THRIFT-4386 Add Lua 5.3/5.4 support
Clint: lua
Patch: Thomas Bruggink

This closes #3012
diff --git a/build/docker/ubuntu-jammy/Dockerfile b/build/docker/ubuntu-jammy/Dockerfile
index c6176a8..a37b7c6 100644
--- a/build/docker/ubuntu-jammy/Dockerfile
+++ b/build/docker/ubuntu-jammy/Dockerfile
@@ -206,8 +206,8 @@
 
 RUN apt-get install -y --no-install-recommends \
   `# Lua dependencies` \
-  lua5.2 \
-  lua5.2-dev
+  lua5.4 \
+  liblua5.4-dev
 # https://bugs.launchpad.net/ubuntu/+source/lua5.3/+bug/1707212
 # lua5.3 does not install alternatives!
 # need to update our luasocket code, lua doesn't have luaL_openlib any more
diff --git a/compiler/cpp/src/thrift/generate/t_lua_generator.cc b/compiler/cpp/src/thrift/generate/t_lua_generator.cc
index 642dd9c..f7f8f05 100644
--- a/compiler/cpp/src/thrift/generate/t_lua_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_lua_generator.cc
@@ -273,6 +273,9 @@
         out << value->get_double();
       }
       break;
+    case t_base_type::TYPE_UUID:
+      out << "TUUIDfromString(" << value->get_string() << ")";
+      break;
     default:
       throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
     }
@@ -727,6 +730,8 @@
   if (!tfunction->is_oneway()) {
       out << indent() << "local result = " << resultname
           << ":new{}" << '\n';
+  } else {
+    out << indent() << "oprot.trans:flushOneway()" << '\n';
   }
 
   out <<  indent() << "local status, res = pcall(self.handler." << fn_name
@@ -845,6 +850,9 @@
       case t_base_type::TYPE_DOUBLE:
         out << "readDouble()";
         break;
+      case t_base_type::TYPE_UUID:
+        out << "readUuid()";
+        break;
       default:
         throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
       }
@@ -1000,6 +1008,9 @@
       case t_base_type::TYPE_DOUBLE:
         out << "writeDouble(" << name << ")";
         break;
+      case t_base_type::TYPE_UUID:
+        out << "writeUuid(" << name << ")";
+        break;
       default:
         throw "compiler error: no PHP name for base type " + t_base_type::t_base_name(tbase);
       }
@@ -1151,6 +1162,8 @@
       return "TType.I64";
     case t_base_type::TYPE_DOUBLE:
       return "TType.DOUBLE";
+    case t_base_type::TYPE_UUID:
+      return "TType.UUID";
     default:
       throw "compiler error: unhandled type";
     }
diff --git a/lib/lua/TBinaryProtocol.lua b/lib/lua/TBinaryProtocol.lua
index 4b8e98a..9eacc4a 100644
--- a/lib/lua/TBinaryProtocol.lua
+++ b/lib/lua/TBinaryProtocol.lua
@@ -18,8 +18,8 @@
 --
 
 require 'TProtocol'
-require 'libluabpack'
-require 'libluabitwise'
+local libluabpack = require 'libluabpack'
+local libluabitwise = require 'libluabitwise'
 
 TBinaryProtocol = __TObject.new(TProtocolBase, {
   __type = 'TBinaryProtocol',
@@ -111,6 +111,11 @@
   self.trans:write(buff)
 end
 
+function TBinaryProtocol:writeUI32(i32)
+  local buff = libluabpack.bpack('I', i32)
+  self.trans:write(buff)
+end
+
 function TBinaryProtocol:writeI64(i64)
   local buff = libluabpack.bpack('l', i64)
   self.trans:write(buff)
@@ -127,6 +132,13 @@
   self.trans:write(str)
 end
 
+function TBinaryProtocol:writeUuid(uuid)
+  self:writeUI32(uuid.two)
+  self:writeUI32(uuid.three)
+  self:writeUI32(uuid.zero)
+  self:writeUI32(uuid.one)
+end
+
 function TBinaryProtocol:readMessageBegin()
   local sz, ttype, name, seqid = self:readI32()
   if sz < 0 then
@@ -226,6 +238,12 @@
   return val
 end
 
+function TBinaryProtocol:readUI32()
+  local buff = self.trans:readAll(4)
+  local val = libluabpack.bunpack('I', buff)
+  return val
+end
+
 function TBinaryProtocol:readI64()
   local buff = self.trans:readAll(8)
   local val = libluabpack.bunpack('l', buff)
@@ -244,6 +262,19 @@
   return str
 end
 
+function TBinaryProtocol:readUuid()
+  local a = self:readUI32()
+  local b = self:readUI32()
+  local c = self:readUI32()
+  local d = self:readUI32()
+  return TUUID:new {
+    zero = c,
+    one = d,
+    two = a,
+    three = b
+  }
+end
+
 TBinaryProtocolFactory = TProtocolFactory:new{
   __type = 'TBinaryProtocolFactory',
   strictRead = false
diff --git a/lib/lua/TCompactProtocol.lua b/lib/lua/TCompactProtocol.lua
index 8ec7b3a..f01c56f 100644
--- a/lib/lua/TCompactProtocol.lua
+++ b/lib/lua/TCompactProtocol.lua
@@ -18,9 +18,9 @@
 --
 
 require 'TProtocol'
-require 'libluabpack'
-require 'libluabitwise'
-require 'liblualongnumber'
+local libluabpack = require 'libluabpack'
+local libluabitwise = require 'libluabitwise'
+local liblualongnumber = require 'liblualongnumber'
 
 TCompactProtocol = __TObject.new(TProtocolBase, {
   __type = 'TCompactProtocol',
@@ -61,7 +61,8 @@
   COMPACT_LIST          = 0x09,
   COMPACT_SET           = 0x0A,
   COMPACT_MAP           = 0x0B,
-  COMPACT_STRUCT        = 0x0C
+  COMPACT_STRUCT        = 0x0C,
+  COMPACT_UUID          = 0x0D,
 }
 
 TTypeToCompactType = {}
@@ -77,21 +78,23 @@
 TTypeToCompactType[TType.SET]    = TCompactType.COMPACT_SET
 TTypeToCompactType[TType.MAP]    = TCompactType.COMPACT_MAP
 TTypeToCompactType[TType.STRUCT] = TCompactType.COMPACT_STRUCT
+TTypeToCompactType[TType.UUID]   = TCompactType.COMPACT_UUID
 
 CompactTypeToTType = {}
-CompactTypeToTType[TType.STOP]                        = TType.STOP
-CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_TRUE] = TType.BOOL
+CompactTypeToTType[TType.STOP]                         = TType.STOP
+CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_TRUE]  = TType.BOOL
 CompactTypeToTType[TCompactType.COMPACT_BOOLEAN_FALSE] = TType.BOOL
-CompactTypeToTType[TCompactType.COMPACT_BYTE]         = TType.BYTE
-CompactTypeToTType[TCompactType.COMPACT_I16]          = TType.I16
-CompactTypeToTType[TCompactType.COMPACT_I32]          = TType.I32
-CompactTypeToTType[TCompactType.COMPACT_I64]          = TType.I64
-CompactTypeToTType[TCompactType.COMPACT_DOUBLE]       = TType.DOUBLE
-CompactTypeToTType[TCompactType.COMPACT_BINARY]       = TType.STRING
-CompactTypeToTType[TCompactType.COMPACT_LIST]         = TType.LIST
-CompactTypeToTType[TCompactType.COMPACT_SET]          = TType.SET
-CompactTypeToTType[TCompactType.COMPACT_MAP]          = TType.MAP
-CompactTypeToTType[TCompactType.COMPACT_STRUCT]       = TType.STRUCT
+CompactTypeToTType[TCompactType.COMPACT_BYTE]          = TType.BYTE
+CompactTypeToTType[TCompactType.COMPACT_I16]           = TType.I16
+CompactTypeToTType[TCompactType.COMPACT_I32]           = TType.I32
+CompactTypeToTType[TCompactType.COMPACT_I64]           = TType.I64
+CompactTypeToTType[TCompactType.COMPACT_DOUBLE]        = TType.DOUBLE
+CompactTypeToTType[TCompactType.COMPACT_BINARY]        = TType.STRING
+CompactTypeToTType[TCompactType.COMPACT_LIST]          = TType.LIST
+CompactTypeToTType[TCompactType.COMPACT_SET]           = TType.SET
+CompactTypeToTType[TCompactType.COMPACT_MAP]           = TType.MAP
+CompactTypeToTType[TCompactType.COMPACT_STRUCT]        = TType.STRUCT
+CompactTypeToTType[TCompactType.COMPACT_UUID]          = TType.UUID
 
 function TCompactProtocol:resetLastField()
   self.lastField = {}
@@ -197,6 +200,11 @@
   self:writeVarint32(libluabpack.i32ToZigzag(i32))
 end
 
+function TCompactProtocol:writeUI32(i32)
+  local buff = libluabpack.bpack('I', i32)
+  self.trans:write(buff)
+end
+
 function TCompactProtocol:writeI64(i64)
   self:writeVarint64(libluabpack.i64ToZigzag(i64))
 end
@@ -211,6 +219,13 @@
   self:writeBinary(str)
 end
 
+function TCompactProtocol:writeUuid(uuid)
+  self:writeUI32(uuid.two)
+  self:writeUI32(uuid.three)
+  self:writeUI32(uuid.zero)
+  self:writeUI32(uuid.one)
+end
+
 function TCompactProtocol:writeBinary(str)
   -- Should be utf-8
   self:writeVarint32(string.len(str))
@@ -385,6 +400,12 @@
   return value
 end
 
+function TCompactProtocol:readUI32()
+  local buff = self.trans:readAll(4)
+  local val = libluabpack.bunpack('I', buff)
+  return val
+end
+
 function TCompactProtocol:readI64()
   local value = self:readVarint64()
   return value
@@ -400,6 +421,19 @@
   return self:readBinary()
 end
 
+function TCompactProtocol:readUuid()
+  local a = self:readUI32()
+  local b = self:readUI32()
+  local c = self:readUI32()
+  local d = self:readUI32()
+  return TUUID:new {
+    zero = c,
+    one = d,
+    two = a,
+    three = b
+  }
+end
+
 function TCompactProtocol:readBinary()
   local size = self:readVarint32()
   if size <= 0 then
diff --git a/lib/lua/TFramedTransport.lua b/lib/lua/TFramedTransport.lua
index 768e2d9..4f41e83 100644
--- a/lib/lua/TFramedTransport.lua
+++ b/lib/lua/TFramedTransport.lua
@@ -18,7 +18,7 @@
 --
 
 require 'TTransport'
-require 'libluabpack'
+local libluabpack = require 'libluabpack'
 
 TFramedTransport = TTransportBase:new{
   __type = 'TFramedTransport',
diff --git a/lib/lua/THttpTransport.lua b/lib/lua/THttpTransport.lua
index 2951db7..e1318c3 100644
--- a/lib/lua/THttpTransport.lua
+++ b/lib/lua/THttpTransport.lua
@@ -160,12 +160,21 @@
   end
 end
 
+function THttpTransport:flushOneway()
+  self.wBuf = ''
+  self:writeHttpHeader(0)
+  self.trans:flush()
+end
+
 function THttpTransport:flush()
   -- If the write fails we still want wBuf to be clear
   local tmp = self.wBuf
   self.wBuf = ''
-  self:writeHttpHeader(string.len(tmp))
-  self.trans:write(tmp)
+  local dataLen = string.len(tmp)
+  self:writeHttpHeader(dataLen)
+  if dataLen > 0 then
+    self.trans:write(tmp)
+  end
   self.trans:flush()
 end
 
diff --git a/lib/lua/TJsonProtocol.lua b/lib/lua/TJsonProtocol.lua
index db08eec..cec8e26 100644
--- a/lib/lua/TJsonProtocol.lua
+++ b/lib/lua/TJsonProtocol.lua
@@ -18,8 +18,9 @@
 --
 
 require 'TProtocol'
-require 'libluabpack'
-require 'libluabitwise'
+local libluabpack = require 'libluabpack'
+local libluabitwise = require 'libluabitwise'
+local liblualongnumber = require 'liblualongnumber'
 
 TJSONProtocol = __TObject.new(TProtocolBase, {
   __type = 'TJSONProtocol',
@@ -42,6 +43,7 @@
 TTypeToString[TType.LIST]   = "lst"
 TTypeToString[TType.SET]    = "set"
 TTypeToString[TType.MAP]    = "map"
+TTypeToString[TType.UUID]   = "uid"
 
 StringToTType = {
   tf  = TType.BOOL,
@@ -54,7 +56,8 @@
   rec = TType.STRUCT,
   map = TType.MAP,
   set = TType.SET,
-  lst = TType.LIST
+  lst = TType.LIST,
+  uid = TType.UUID,
 }
 
 JSONNode = {
@@ -402,13 +405,17 @@
 end
 
 function TJSONProtocol:writeDouble(dub)
-  self:writeJSONDouble(string.format("%.16f", dub))
+  self:writeJSONDouble(string.format("%.20f", dub))
 end
 
 function TJSONProtocol:writeString(str)
   self:writeJSONString(str)
 end
 
+function TJSONProtocol:writeUuid(uuid)
+  self:writeJSONString(uuid:getString())
+end
+
 function TJSONProtocol:writeBinary(str)
   -- Should be utf-8
   self:writeJSONBase64(str)
@@ -706,6 +713,10 @@
   return self:readJSONString()
 end
 
+function TJSONProtocol:readUuid()
+  return TUUIDfromString(self:readJSONString())
+end
+
 function TJSONProtocol:readBinary()
   return self:readJSONBase64()
 end
diff --git a/lib/lua/TProtocol.lua b/lib/lua/TProtocol.lua
index 1306fb3..f7a993f 100644
--- a/lib/lua/TProtocol.lua
+++ b/lib/lua/TProtocol.lua
@@ -86,6 +86,7 @@
 function TProtocolBase:writeI64(i64) end
 function TProtocolBase:writeDouble(dub) end
 function TProtocolBase:writeString(str) end
+function TProtocolBase:writeUuid(uuid) end
 function TProtocolBase:readMessageBegin() end
 function TProtocolBase:readMessageEnd() end
 function TProtocolBase:readStructBegin() end
@@ -105,6 +106,7 @@
 function TProtocolBase:readI64() end
 function TProtocolBase:readDouble() end
 function TProtocolBase:readString() end
+function TProtocolBase:readUuid() end
 
 function TProtocolBase:skip(ttype)
   if ttype == TType.BOOL then
@@ -151,6 +153,8 @@
       self:skip(ettype)
     end
     self:readListEnd()
+  elseif ttype == TType.UUID then
+    self:readUuid()
   else
     terror(TProtocolException:new{
       message = 'Invalid data'
diff --git a/lib/lua/TSocket.lua b/lib/lua/TSocket.lua
index d71fc1f..47ca6f0 100644
--- a/lib/lua/TSocket.lua
+++ b/lib/lua/TSocket.lua
@@ -17,7 +17,7 @@
 --
 
 require 'TTransport'
-require 'libluasocket'
+local luasocket = require 'libluasocket'
 
 -- TSocketBase
 TSocketBase = TTransportBase:new{
diff --git a/lib/lua/TTransport.lua b/lib/lua/TTransport.lua
index 01c7e59..b7f6b83 100644
--- a/lib/lua/TTransport.lua
+++ b/lib/lua/TTransport.lua
@@ -76,6 +76,8 @@
   return buf
 end
 function TTransportBase:write(buf) end
+-- flushOneway is a NOOP for most transport types.
+function TTransportBase:flushOneway() end
 function TTransportBase:flush() end
 
 TServerTransportBase = __TObject:new{
diff --git a/lib/lua/Thrift.lua b/lib/lua/Thrift.lua
index 58daa22..b6e3628 100644
--- a/lib/lua/Thrift.lua
+++ b/lib/lua/Thrift.lua
@@ -23,6 +23,9 @@
 --setfenv(1, thrift)
 
 package.cpath = package.cpath .. ';bin/?.so' -- TODO FIX
+
+local libluabitwise = require 'libluabitwise'
+
 function ttype(obj)
   if type(obj) == 'table' and
     obj.__type and
@@ -66,8 +69,7 @@
   MAP    = 13,
   SET    = 14,
   LIST   = 15,
-  UTF8   = 16,
-  UTF16  = 17
+  UUID   = 16
 }
 
 TMessageType = {
@@ -233,6 +235,35 @@
   oprot:writeStructEnd()
 end
 
+TUUID = {
+  zero,
+  one,
+  two,
+  three
+}
+
+TUUID = __TObject:new{
+  __type = 'TUUID'
+}
+
+function TUUIDfromString(str)
+  local iterator = string.gmatch(str, "[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]")
+  return TUUID:new {
+    zero = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16)),
+    one = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16)),
+    two = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16)),
+    three = libluabitwise.buor(libluabitwise.ushiftl(tonumber(iterator(), 16), 16), tonumber(iterator(), 16))
+  }
+end
+
+function TUUID:getString()
+  return string.format("%08x-%04x-%04x-%04x-%04x%08x", self.zero, libluabitwise.ushiftr(self.one, 16), libluabitwise.buand(self.one, 0xFFFF), libluabitwise.ushiftr(self.two, 16), libluabitwise.buand(self.two, 0xFFFF), self.three)
+end
+
+function TUUID:__tostring()
+  return "<TUUID: " .. self:getString() .. ">"
+end
+
 -- Basic Client (used in generated lua code)
 __TClient = __TObject:new{
   __type = '__TClient',
diff --git a/lib/lua/src/luabitwise.c b/lib/lua/src/luabitwise.c
index 2e07e17..ea9110a 100644
--- a/lib/lua/src/luabitwise.c
+++ b/lib/lua/src/luabitwise.c
@@ -27,6 +27,13 @@
   return 1;
 }
 
+static int l_unot(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  a = ~a;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_xor(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -35,6 +42,15 @@
   return 1;
 }
 
+static int l_uxor(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a ^= b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
+
 static int l_and(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -43,6 +59,14 @@
   return 1;
 }
 
+static int l_uand(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a &= b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_or(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -51,6 +75,14 @@
   return 1;
 }
 
+static int l_uor(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a |= b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_shiftr(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -59,6 +91,14 @@
   return 1;
 }
 
+static int l_ushiftr(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a = a >> b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static int l_shiftl(lua_State *L) {
   int a = luaL_checkinteger(L, 1);
   int b = luaL_checkinteger(L, 2);
@@ -67,17 +107,36 @@
   return 1;
 }
 
+static int l_ushiftl(lua_State *L) {
+  unsigned int a = luaL_checkinteger(L, 1);
+  unsigned int b = luaL_checkinteger(L, 2);
+  a = a << b;
+  lua_pushnumber(L, a);
+  return 1;
+}
+
 static const struct luaL_Reg funcs[] = {
   {"band", l_and},
+  {"buand", l_uand},
   {"bor", l_or},
+  {"buor", l_uor},
   {"bxor", l_xor},
+  {"buxor", l_uxor},
   {"bnot", l_not},
+  {"bunot", l_unot},
   {"shiftl", l_shiftl},
+  {"ushiftl", l_ushiftl},
   {"shiftr", l_shiftr},
+  {"ushiftr", l_ushiftr},
   {NULL, NULL}
 };
 
 int luaopen_libluabitwise(lua_State *L) {
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, funcs, 0);
+#else
   luaL_register(L, "libluabitwise", funcs);
+#endif
   return 1;
 }
diff --git a/lib/lua/src/luabpack.c b/lib/lua/src/luabpack.c
index 077b6aa..cdfb72a 100644
--- a/lib/lua/src/luabpack.c
+++ b/lib/lua/src/luabpack.c
@@ -45,6 +45,7 @@
  *  c - Signed Byte
  *  s - Signed Short
  *  i - Signed Int
+ *  I - Unsigned Int
  *  l - Signed Long
  *  d - Double
  */
@@ -72,6 +73,12 @@
       luaL_addlstring(&buf, (void*)&data, sizeof(data));
       break;
     }
+    case 'I': {
+      uint32_t data = luaL_checkinteger(L, 2);
+      data = (uint32_t)htonl(data);
+      luaL_addlstring(&buf, (void*)&data, sizeof(data));
+      break;
+    }
     case 'l': {
       int64_t data = lualongnumber_checklong(L, 2);
       data = (int64_t)T_htonll(data);
@@ -97,6 +104,7 @@
  *  C - Unsigned Byte
  *  s - Signed Short
  *  i - Signed Int
+ *  I - Unsigned Int
  *  l - Signed Long
  *  d - Double
  */
@@ -144,6 +152,17 @@
       lua_pushnumber(L, val);
       break;
     }
+    /**
+     * unpack unsigned Int.
+     */
+    case 'I': {
+      uint32_t val;
+      luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
+      memcpy(&val, data, sizeof(val));
+      val = (uint32_t)ntohl(val);
+      lua_pushnumber(L, val);
+      break;
+    }
     case 'l': {
       int64_t val;
       luaL_argcheck(L, len == sizeof(val), 1, "Invalid input string size.");
@@ -303,6 +322,11 @@
 };
 
 int luaopen_libluabpack(lua_State *L) {
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, lua_bpack, 0);
+#else
   luaL_register(L, "libluabpack", lua_bpack);
+#endif
   return 1;
 }
diff --git a/lib/lua/src/lualongnumber.c b/lib/lua/src/lualongnumber.c
index 9001e4a..91df70a 100644
--- a/lib/lua/src/lualongnumber.c
+++ b/lib/lua/src/lualongnumber.c
@@ -223,6 +223,11 @@
   lua_pop(L, 1);
   set_methods(L, LONG_NUM_TYPE, methods);
 
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, funcs, 0);
+#else
   luaL_register(L, "liblualongnumber", funcs);
+#endif
   return 1;
 }
diff --git a/lib/lua/src/luasocket.c b/lib/lua/src/luasocket.c
index 6f63d3d..07524ab 100644
--- a/lib/lua/src/luasocket.c
+++ b/lib/lua/src/luasocket.c
@@ -185,8 +185,12 @@
   set_methods(L, SOCKET_GENERIC, methods_generic);
   set_methods(L, SOCKET_CLIENT, methods_client);
   set_methods(L, SOCKET_SERVER, methods_server);
-
+#if LUA_VERSION_NUM >= 502
+    lua_newtable(L);
+    luaL_setfuncs(L, funcs_luasocket, 0);
+#else
   luaL_register(L, "luasocket", funcs_luasocket);
+#endif
   return 1;
 }
 
diff --git a/lib/lua/src/usocket.c b/lib/lua/src/usocket.c
index 21c0bac..27103a0 100644
--- a/lib/lua/src/usocket.c
+++ b/lib/lua/src/usocket.c
@@ -205,21 +205,35 @@
   return socket_wait(sock, WAIT_MODE_C, timeout);
 }
 
+#define SEND_RETRY_COUNT 5
 T_ERRCODE socket_send(
   p_socket sock, const char *data, size_t len, int timeout) {
   int err, put = 0;
   if (*sock < 0) {
     return CLOSED;
   }
-  do {
-    put = send(*sock, data, len, 0);
-    if (put > 0) {
-      return SUCCESS;
-    }
-  } while ((err = errno) == EINTR);
+  for(int i = 0; i < SEND_RETRY_COUNT; i++) {
+    do {
+      size_t l = len - put;
+      put = send(*sock, data + put, l, 0);
+      if (put > 0) {
+        if(put == l) {
+          return SUCCESS;
+        }
+        // Not all data was delivered, we need to try again.
+        err = EAGAIN;
+        break;
+      }
+    } while ((err = errno) == EINTR);
 
-  if (err == EAGAIN) {
-    return socket_wait(sock, WAIT_MODE_W, timeout);
+    if (err == EAGAIN) {
+      err = socket_wait(sock, WAIT_MODE_W, timeout);
+      // Check if the socket is available again and try to resend.
+      if(err == SUCCESS) {
+        continue;
+      }
+    }
+    break;
   }
 
   return err;
diff --git a/test/lua/Makefile.am b/test/lua/Makefile.am
index 00bdf3d..7cdbe04 100644
--- a/test/lua/Makefile.am
+++ b/test/lua/Makefile.am
@@ -19,11 +19,8 @@
 
 THRIFT = $(top_builddir)/compiler/cpp/thrift
 
-# Remove "MapType =" line to ignore some map bug for now
-stubs: ../v0.16/ThriftTest.thrift $(THRIFT)
-	$(THRIFT) --gen lua $<
-	$(SED) -i.bak 's/MapType =//g' gen-lua/ThriftTest_ttypes.lua
-	$(RM) gen-lua/ThriftTest_ttypes.lua.bak
+stubs: ../ThriftTest.thrift
+	$(THRIFT) --gen lua ../ThriftTest.thrift
 
 precross: stubs
 
diff --git a/test/lua/test_basic_client.lua b/test/lua/test_basic_client.lua
index 11567d9..1932dd8 100644
--- a/test/lua/test_basic_client.lua
+++ b/test/lua/test_basic_client.lua
@@ -24,7 +24,7 @@
 require('TJsonProtocol')
 require('TBinaryProtocol')
 require('ThriftTest_ThriftTest')
-require('liblualongnumber')
+local liblualongnumber = require('liblualongnumber')
 
 local client
 
@@ -98,6 +98,9 @@
   assertEqual(client:testString('lala'),  'lala',  'Failed testString')
   assertEqual(client:testString('wahoo'), 'wahoo', 'Failed testString')
 
+  -- UUID
+  assertEqual(client:testUuid(TUUIDfromString('00112233-4455-6677-8899-aabbccddeeff')):getString(),  '00112233-4455-6677-8899-aabbccddeeff',  'Failed testUuid')
+
   -- Bool
   assertEqual(client:testBool(true), true, 'Failed testBool true')
   assertEqual(client:testBool(false), false, 'Failed testBool false')
diff --git a/test/lua/test_basic_server.lua b/test/lua/test_basic_server.lua
index 20ac407..0b421d1 100644
--- a/test/lua/test_basic_server.lua
+++ b/test/lua/test_basic_server.lua
@@ -24,7 +24,7 @@
 require('TJsonProtocol')
 require('TBinaryProtocol')
 require('TServer')
-require('liblualongnumber')
+local liblualongnumber = require('liblualongnumber')
 
 --------------------------------------------------------------------------------
 -- Handler
@@ -62,10 +62,113 @@
   return by
 end
 
+function TestHandler:testUuid(uuid)
+  return uuid
+end
+
+function TestHandler:testNest(thing)
+  return thing
+end
+
 function TestHandler:testStruct(thing)
   return thing
 end
 
+function TestHandler:testMap(thing)
+  return thing
+end
+
+function TestHandler:testStringMap(thing)
+  return thing
+end
+
+function TestHandler:testSet(thing)
+  return thing
+end
+
+function TestHandler:testList(thing)
+  return thing
+end
+
+function TestHandler:testEnum(thing)
+  return thing
+end
+
+function TestHandler:testTypedef(thing)
+  return thing
+end
+
+function TestHandler:testMapMap(hello)
+  return {
+    ["-4"] = {
+      ["-4"] = -4,
+      ["-3"] = -3,
+      ["-2"] = -2,
+      ["-1"] = -1
+    },
+    ["4"] = {
+      ["1"] = 1,
+      ["2"] = 2,
+      ["3"] = 3,
+      ["4"] = 4
+    }
+  }
+end
+
+function TestHandler:testInsanity(argument)
+  local first_map = {
+    [Numberz.TWO] = argument,
+    [Numberz.THREE] = argument
+  };
+  local second_map = {
+    [Numberz.SIX] = Insanity:new {
+      userMap = {},
+      xtructs = {}
+    }
+  }
+
+  return {
+    ["1"] = first_map,
+    ["2"] = second_map
+  };
+end
+
+function TestHandler:testMulti(arg0, arg1, arg2, arg3, arg4, arg5)
+  return Xtruct:new {}
+end
+
+function TestHandler:testException(arg)
+  if arg == "Xception" then
+    return Xception:new {
+      errorCode = 1001,
+      message = arg
+    }
+  elseif arg == "TException" then
+    error("")
+  end
+end
+
+function TestHandler:testMultiException(arg0, arg1)
+  if arg0 == "Xception" then
+    return Xception:new {
+      errorCode = 1001,
+      message = "This is an Xception"
+    }
+  elseif arg0 == "Xception2" then
+    return Xception2:new {
+      errorCode = 2002,
+      struct_thing = Xtruct:new {
+        string_thing = "This is an Xception2"
+      }
+    }
+  elseif arg0 == "TException" then
+    error("")
+  end
+  return Xtruct:new {
+    string_thing = arg1
+  }
+end
+
 function TestHandler:testOneway(secondsToSleep)
   print("testOneway secondsToSleep:", secondsToSleep)
 end
diff --git a/test/tests.json b/test/tests.json
index 91aa767..015a559 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -695,24 +695,31 @@
     },
     "client": {
       "timeout": 5,
-      "transports": [
-        "buffered",
-        "framed",
-        "http"
-      ],
-      "sockets": [
-        "ip"
-      ],
-      "protocols": [
-        "binary",
-        "compact",
-        "json"
-      ],
       "command": [
         "lua",
         "test_basic_client.lua"
       ]
     },
+    "server": {
+      "delay": 5,
+      "command": [
+        "lua",
+        "test_basic_server.lua"
+      ]
+    },
+    "transports": [
+      "buffered",
+      "framed",
+      "http"
+    ],
+    "sockets": [
+      "ip"
+    ],
+    "protocols": [
+      "binary",
+      "compact",
+      "json"
+    ],
     "workdir": "lua"
   },
   {