[THRIFT-5871] Improve MAX_MESSAGE_SIZE_CHECK and friends
diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c
index 9e80e10..0df2fb0 100644
--- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c
+++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.c
@@ -25,6 +25,14 @@
 #include <thrift/c_glib/protocol/thrift_protocol.h>
 #include <thrift/c_glib/protocol/thrift_binary_protocol.h>
 
+/* object properties */
+enum _ThriftBinaryProtocolProperties
+{
+    PROP_0,
+    PROP_THRIFT_BINARY_PROTOCOL_STRING_LIMIT,
+    PROP_THRIFT_BINARY_PROTOCOL_CONTAINER_LIMIT
+};
+
 G_DEFINE_TYPE(ThriftBinaryProtocol, thrift_binary_protocol, THRIFT_TYPE_PROTOCOL)
 
 static guint64
@@ -561,6 +569,14 @@
     return -1;
   }
 
+  ThriftBinaryProtocol *bp = THRIFT_BINARY_PROTOCOL (protocol);
+  if (bp->container_limit > 0 && sizei > bp->container_limit) {
+    g_set_error (error, THRIFT_PROTOCOL_ERROR,
+                 THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+                 "got size over limit (%d > %d)", sizei, bp->container_limit);
+    return -1;
+  }
+
   if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT(tp->transport), 
                                     sizei * thrift_binary_protocol_get_min_serialized_size(protocol, k, error) + 
                                     sizei * thrift_binary_protocol_get_min_serialized_size(protocol, v, error),
@@ -618,6 +634,14 @@
     return -1;
   }
 
+  ThriftBinaryProtocol *bp = THRIFT_BINARY_PROTOCOL (protocol);
+  if (bp->container_limit > 0 && sizei > bp->container_limit) {
+    g_set_error (error, THRIFT_PROTOCOL_ERROR,
+                 THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+                 "got size over limit (%d > %d)", sizei, bp->container_limit);
+    return -1;
+  }
+
   if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT(tp->transport), 
                                     (sizei * thrift_binary_protocol_get_min_serialized_size(protocol, e, error)),
                                     error))
@@ -814,6 +838,23 @@
     return -1;
   }
 
+  ThriftBinaryProtocol *bp = THRIFT_BINARY_PROTOCOL (protocol);
+  if (bp->string_limit > 0 && read_len > bp->string_limit) {
+    g_set_error (error, THRIFT_PROTOCOL_ERROR,
+                 THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+                 "got size over limit (%d > %d)", read_len, bp->string_limit);
+    *str = NULL;
+    return -1;
+  }
+
+  ThriftProtocol *tp = THRIFT_PROTOCOL (protocol);
+  ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport);
+  if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT(tp->transport), read_len, error))
+  {
+    *str = NULL;
+    return -1;
+  }
+
   /* allocate the memory for the string */
   len = (guint32) read_len + 1; /* space for null terminator */
   *str = g_new0 (gchar, len);
@@ -854,8 +895,36 @@
   }
   xfer += ret;
 
+  if (read_len < 0) {
+    g_set_error (error, THRIFT_PROTOCOL_ERROR,
+                 THRIFT_PROTOCOL_ERROR_NEGATIVE_SIZE,
+                 "got negative size of %d", read_len);
+    *buf = NULL;
+    *len = 0;
+    return -1;
+  }
+
+  ThriftBinaryProtocol *bp = THRIFT_BINARY_PROTOCOL (protocol);
+  if (bp->string_limit > 0 && read_len > bp->string_limit) {
+    g_set_error (error, THRIFT_PROTOCOL_ERROR,
+                 THRIFT_PROTOCOL_ERROR_SIZE_LIMIT,
+                 "got size over limit (%d > %d)", read_len, bp->string_limit);
+    *buf = NULL;
+    *len = 0;
+    return -1;
+  }
+
   if (read_len > 0)
   {
+    ThriftProtocol *tp = THRIFT_PROTOCOL (protocol);
+    ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport);
+    if(!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT(tp->transport), read_len, error))
+    {
+      *buf = NULL;
+      *len = 0;
+      return -1;
+    }
+
     /* allocate the memory as an array of unsigned char for binary data */
     *len = (guint32) read_len;
     *buf = g_new (guchar, *len);
@@ -885,9 +954,9 @@
   switch (type)
   {
     case T_STOP:
-         return 0;
+         return 1;  /* T_STOP needs to count itself */
     case T_VOID:
-         return 0;
+         return 1;  /* T_VOID needs to count itself */
     case T_BOOL:
          return sizeof(gint8);
     case T_BYTE:
@@ -903,7 +972,7 @@
     case T_STRING:
          return sizeof(int);
     case T_STRUCT:
-         return 0;
+         return 1;  /* empty struct needs at least 1 byte for the T_STOP */
     case T_MAP:
          return sizeof(int);
     case T_SET:
@@ -919,6 +988,48 @@
   }
 }
 
+/* property accessor */
+void
+thrift_binary_protocol_get_property (GObject *object, guint property_id,
+                                     GValue *value, GParamSpec *pspec)
+{
+  ThriftBinaryProtocol *tb;
+
+  THRIFT_UNUSED_VAR (pspec);
+
+  tb = THRIFT_BINARY_PROTOCOL (object);
+
+  switch (property_id) {
+    case PROP_THRIFT_BINARY_PROTOCOL_STRING_LIMIT:
+      g_value_set_int (value, tb->string_limit);
+      break;
+    case PROP_THRIFT_BINARY_PROTOCOL_CONTAINER_LIMIT:
+      g_value_set_int (value, tb->container_limit);
+      break;
+  }
+}
+
+/* property mutator */
+void
+thrift_binary_protocol_set_property (GObject *object, guint property_id,
+                                     const GValue *value, GParamSpec *pspec)
+{
+  ThriftBinaryProtocol *tb;
+
+  THRIFT_UNUSED_VAR (pspec);
+
+  tb = THRIFT_BINARY_PROTOCOL (object);
+
+  switch (property_id) {
+    case PROP_THRIFT_BINARY_PROTOCOL_STRING_LIMIT:
+      tb->string_limit = g_value_get_int (value);
+      break;
+    case PROP_THRIFT_BINARY_PROTOCOL_CONTAINER_LIMIT:
+      tb->container_limit = g_value_get_int (value);
+      break;
+  }
+}
+
 static void
 thrift_binary_protocol_init (ThriftBinaryProtocol *protocol)
 {
@@ -929,7 +1040,39 @@
 static void
 thrift_binary_protocol_class_init (ThriftBinaryProtocolClass *klass)
 {
-  ThriftProtocolClass *cls = THRIFT_PROTOCOL_CLASS (klass);
+  ThriftProtocolClass *cls;
+  GObjectClass *gobject_class;
+  GParamSpec *param_spec;
+
+  cls = THRIFT_PROTOCOL_CLASS (klass);
+  gobject_class = G_OBJECT_CLASS (klass);
+  param_spec = NULL;
+
+  /* setup accessors and mutators */
+  gobject_class->get_property = thrift_binary_protocol_get_property;
+  gobject_class->set_property = thrift_binary_protocol_set_property;
+
+  param_spec = g_param_spec_int ("string_limit",
+                                 "Max allowed string size",
+                                 "Set the max string limit",
+                                 0, /* min */
+                                 G_MAXINT32, /* max */
+                                 0, /* default value */
+                                 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_THRIFT_BINARY_PROTOCOL_STRING_LIMIT,
+                                   param_spec);
+
+  param_spec = g_param_spec_int ("container_limit",
+                                 "Max allowed container size",
+                                 "Set the max container limit",
+                                 0, /* min */
+                                 G_MAXINT32, /* max */
+                                 0, /* default value */
+                                 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
+  g_object_class_install_property (gobject_class,
+                                   PROP_THRIFT_BINARY_PROTOCOL_CONTAINER_LIMIT,
+                                   param_spec);
 
   cls->write_message_begin = thrift_binary_protocol_write_message_begin;
   cls->write_message_end = thrift_binary_protocol_write_message_end;
diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h
index bd8b84e..6d2bc3e 100644
--- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h
+++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_binary_protocol.h
@@ -52,6 +52,10 @@
 struct _ThriftBinaryProtocol
 {
   ThriftProtocol parent;
+
+  /* protected */
+  gint32 string_limit;
+  gint32 container_limit;
 };
 
 typedef struct _ThriftBinaryProtocolClass ThriftBinaryProtocolClass;
diff --git a/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c b/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c
index 0aa9a6f..73be027 100644
--- a/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c
+++ b/lib/c_glib/src/thrift/c_glib/protocol/thrift_compact_protocol.c
@@ -1414,6 +1414,13 @@
     return -1;
   }
 
+  ThriftProtocol *tp = THRIFT_PROTOCOL (protocol);
+  ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport);
+  if (read_len > 0 && !ttc->checkReadBytesAvailable (THRIFT_TRANSPORT (tp->transport), read_len, error)) {
+    *str = NULL;
+    return -1;
+  }
+
   /* allocate the memory as an array of unsigned char for binary data */
   *str = g_new0 (gchar, read_len + 1);
   if (read_len > 0) {
@@ -1468,6 +1475,14 @@
   }
 
   if (read_len > 0) {
+    ThriftProtocol *tp = THRIFT_PROTOCOL (protocol);
+    ThriftTransportClass *ttc = THRIFT_TRANSPORT_GET_CLASS (tp->transport);
+    if (!ttc->checkReadBytesAvailable (THRIFT_TRANSPORT (tp->transport), read_len, error)) {
+      *buf = NULL;
+      *len = 0;
+      return -1;
+    }
+
     /* allocate the memory as an array of unsigned char for binary data */
     *len = (guint32) read_len;
     *buf = g_new (guchar, *len);
@@ -1505,9 +1520,9 @@
   switch (type)
   {
     case T_STOP:
-         return 0;
+         return 1;  /* T_STOP needs to count itself */
     case T_VOID:
-         return 0;
+         return 1;  /* T_VOID needs to count itself */
     case T_BOOL:
          return sizeof(gint8);
     case T_DOUBLE:
@@ -1523,7 +1538,7 @@
     case T_STRING:
          return sizeof(gint8);
     case T_STRUCT:
-         return 0;
+         return 1;  /* empty struct needs at least 1 byte for the T_STOP */
     case T_MAP:
          return sizeof(gint8);
     case T_SET:
diff --git a/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c b/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c
index 9d3f25e..a03eb9e 100644
--- a/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c
+++ b/lib/c_glib/src/thrift/c_glib/transport/thrift_transport.c
@@ -164,7 +164,7 @@
 {
   gboolean boolean = TRUE;
   ThriftTransport *tt = THRIFT_TRANSPORT (transport);
-  if(tt->remainingMessageSize_ < numBytes)
+  if(tt->remainingMessageSize_ < numBytes || numBytes < 0)
   {
     g_set_error(error,
                 THRIFT_TRANSPORT_ERROR,
diff --git a/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc b/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
index b227c33..67265cc 100644
--- a/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
+++ b/lib/cpp/src/thrift/protocol/TBinaryProtocol.tcc
@@ -469,6 +469,9 @@
     return size;
   }
 
+  // Check against MaxMessageSize before alloc
+  trans_->checkReadBytesAvailable(size);
+
   str.resize(size);
   this->trans_->readAll(reinterpret_cast<uint8_t*>(&str[0]), size);
   return (uint32_t)size;
@@ -480,8 +483,8 @@
 {
   switch (type)
   {
-      case T_STOP: return 0;
-      case T_VOID: return 0;
+      case T_STOP: return 1;  // T_STOP needs to count itself
+      case T_VOID: return 1;  // T_VOID needs to count itself
       case T_BOOL: return sizeof(int8_t);
       case T_BYTE: return sizeof(int8_t);
       case T_DOUBLE: return sizeof(double);
@@ -489,7 +492,7 @@
       case T_I32: return sizeof(int);
       case T_I64: return sizeof(long);
       case T_STRING: return sizeof(int);  // string length
-      case T_STRUCT: return 0;  // empty struct
+      case T_STRUCT: return 1;  // empty struct needs at least 1 byte for the T_STOP
       case T_MAP: return sizeof(int);  // element count
       case T_SET: return sizeof(int);  // element count
       case T_LIST: return sizeof(int);  // element count
diff --git a/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc b/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc
index b57568f..2a4990c 100644
--- a/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc
+++ b/lib/cpp/src/thrift/protocol/TCompactProtocol.tcc
@@ -859,8 +859,8 @@
 {
   switch (type)
   {
-    case T_STOP:    return 0;
-    case T_VOID:    return 0;
+    case T_STOP:    return 1;  // T_STOP needs to count itself
+    case T_VOID:    return 1;  // T_VOID needs to count itself
     case T_BOOL:   return sizeof(int8_t);
     case T_DOUBLE: return 8;  // uses fixedLongToBytes() which always writes 8 bytes
     case T_BYTE: return sizeof(int8_t);
@@ -868,7 +868,7 @@
     case T_I32:     return sizeof(int8_t);  // zigzag
     case T_I64:     return sizeof(int8_t);  // zigzag
     case T_STRING: return sizeof(int8_t);  // string length
-    case T_STRUCT:  return 0;             // empty struct
+    case T_STRUCT:  return 1;  // empty struct needs at least 1 byte for the T_STOP
     case T_MAP:     return sizeof(int8_t);  // element count
     case T_SET:    return sizeof(int8_t);  // element count
     case T_LIST:    return sizeof(int8_t);  // element count
diff --git a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
index 1218190..a04d7ad 100644
--- a/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
+++ b/lib/cpp/src/thrift/protocol/TJSONProtocol.cpp
@@ -1131,8 +1131,8 @@
 {
   switch (type)
   {
-    case T_STOP: return 0;
-    case T_VOID: return 0;
+    case T_STOP: return 1;  // T_STOP needs to count itself
+    case T_VOID: return 1;  // T_VOID needs to count itself
     case T_BOOL: return 1;  // written as int
     case T_BYTE: return 1;
     case T_DOUBLE: return 1;
diff --git a/lib/cpp/src/thrift/transport/TTransport.h b/lib/cpp/src/thrift/transport/TTransport.h
index 52b3a0a..1158bcf 100644
--- a/lib/cpp/src/thrift/transport/TTransport.h
+++ b/lib/cpp/src/thrift/transport/TTransport.h
@@ -274,7 +274,7 @@
    */
   void checkReadBytesAvailable(long int numBytes)
   {
-    if (remainingMessageSize_ < numBytes)
+    if (remainingMessageSize_ < numBytes || numBytes < 0)
       throw TTransportException(TTransportException::END_OF_FILE, "MaxMessageSize reached");
   }
 
diff --git a/lib/delphi/src/Thrift.Protocol.Compact.pas b/lib/delphi/src/Thrift.Protocol.Compact.pas
index a8ad53a..afb3dd5 100644
--- a/lib/delphi/src/Thrift.Protocol.Compact.pas
+++ b/lib/delphi/src/Thrift.Protocol.Compact.pas
@@ -1011,8 +1011,8 @@
 // Return the minimum number of bytes a type will consume on the wire
 begin
   case aType of
-    TType.Stop:    result := 0;
-    TType.Void:    result := 0;
+    TType.Stop:    result := 1;  // T_STOP needs to count itself
+    TType.Void:    result := 1;  // T_VOID needs to count itself
     TType.Bool_:   result := SizeOf(Byte);
     TType.Byte_:   result := SizeOf(Byte);
     TType.Double_: result := 8;  // uses fixedLongToBytes() which always writes 8 bytes
@@ -1020,7 +1020,7 @@
     TType.I32:     result := SizeOf(Byte);
     TType.I64:     result := SizeOf(Byte);
     TType.String_: result := SizeOf(Byte);  // string length
-    TType.Struct:  result := 0;             // empty struct
+    TType.Struct:  result := 1;  // empty struct needs at least 1 byte for the T_STOP
     TType.Map:     result := SizeOf(Byte);  // element count
     TType.Set_:    result := SizeOf(Byte);  // element count
     TType.List:    result := SizeOf(Byte);  // element count
diff --git a/lib/delphi/src/Thrift.Protocol.JSON.pas b/lib/delphi/src/Thrift.Protocol.JSON.pas
index 2a9682c..5be7ae0 100644
--- a/lib/delphi/src/Thrift.Protocol.JSON.pas
+++ b/lib/delphi/src/Thrift.Protocol.JSON.pas
@@ -1257,8 +1257,8 @@
 // Return the minimum number of bytes a type will consume on the wire
 begin
   case aType of
-    TType.Stop:    result := 0;
-    TType.Void:    result := 0;
+    TType.Stop:    result := 1;  // T_STOP needs to count itself
+    TType.Void:    result := 1;  // T_VOID needs to count itself
     TType.Bool_:   result := 1;
     TType.Byte_:   result := 1;
     TType.Double_: result := 1;
diff --git a/lib/delphi/src/Thrift.Protocol.pas b/lib/delphi/src/Thrift.Protocol.pas
index 969fbcd..ecf2103 100644
--- a/lib/delphi/src/Thrift.Protocol.pas
+++ b/lib/delphi/src/Thrift.Protocol.pas
@@ -1237,8 +1237,8 @@
 // Return the minimum number of bytes a type will consume on the wire
 begin
   case aType of
-    TType.Stop:    result := 0;
-    TType.Void:    result := 0;
+    TType.Stop:    result := 1;  // T_STOP needs to count itself
+    TType.Void:    result := 1;  // T_VOID needs to count itself
     TType.Bool_:   result := SizeOf(Byte);
     TType.Byte_:   result := SizeOf(Byte);
     TType.Double_: result := SizeOf(Double);
@@ -1246,7 +1246,7 @@
     TType.I32:     result := SizeOf(Int32);
     TType.I64:     result := SizeOf(Int64);
     TType.String_: result := SizeOf(Int32);  // string length
-    TType.Struct:  result := 0;  // empty struct
+    TType.Struct:  result := 1;  // empty struct needs at least 1 byte for the T_STOP
     TType.Map:     result := SizeOf(Int32);  // element count
     TType.Set_:    result := SizeOf(Int32);  // element count
     TType.List:    result := SizeOf(Int32);  // element count
diff --git a/lib/go/thrift/binary_protocol.go b/lib/go/thrift/binary_protocol.go
index eded931..431e446 100644
--- a/lib/go/thrift/binary_protocol.go
+++ b/lib/go/thrift/binary_protocol.go
@@ -355,7 +355,9 @@
 		err = NewTProtocolException(e)
 		return
 	}
-	err = checkSizeForProtocol(size32, p.cfg)
+	minElemSize := p.getMinSerializedSize(kType) + p.getMinSerializedSize(vType)
+	totalMinSize := size32 * minElemSize
+	err = checkSizeForProtocol(totalMinSize, p.cfg)
 	if err != nil {
 		return
 	}
@@ -379,7 +381,9 @@
 		err = NewTProtocolException(e)
 		return
 	}
-	err = checkSizeForProtocol(size32, p.cfg)
+	minElemSize := p.getMinSerializedSize(elemType)
+	totalMinSize := size32 * minElemSize
+	err = checkSizeForProtocol(totalMinSize, p.cfg)
 	if err != nil {
 		return
 	}
@@ -404,7 +408,9 @@
 		err = NewTProtocolException(e)
 		return
 	}
-	err = checkSizeForProtocol(size32, p.cfg)
+	minElemSize := p.getMinSerializedSize(elemType)
+	totalMinSize := size32 * minElemSize
+	err = checkSizeForProtocol(totalMinSize, p.cfg)
 	if err != nil {
 		return
 	}
@@ -546,6 +552,44 @@
 	_ TConfigurationSetter = (*TBinaryProtocol)(nil)
 )
 
+// Return the minimum number of bytes a type will consume on the wire
+func (p *TBinaryProtocol) getMinSerializedSize(ttype TType) int32 {
+	switch ttype {
+	case STOP:
+		return 1 // T_STOP needs to count itself
+	case VOID:
+		return 1 // T_VOID needs to count itsel∂
+	case BOOL:
+		return 1 // sizeof(int8)
+	case BYTE:
+		return 1 // sizeof(int8)
+	case DOUBLE:
+		return 8 // sizeof(double)
+	case I16:
+		return 2 // sizeof(short)
+	case I32:
+		return 4 // sizeof(int)
+	case I64:
+		return 8 // sizeof(long)
+	case STRING:
+		return 4 // string length
+	case STRUCT:
+		return 1 // empty struct needs at least 1 byte for the T_STOP
+	case MAP:
+		return 4 // element count
+	case SET:
+		return 4 // element count
+	case LIST:
+		return 4 // element count
+	case UUID:
+		return 16 // 16 bytes
+	default:
+		return 1 // unknown type
+	}
+}
+
+
+
 // This function is shared between TBinaryProtocol and TCompactProtocol.
 //
 // It tries to read size bytes from trans, in a way that prevents large
diff --git a/lib/go/thrift/compact_protocol.go b/lib/go/thrift/compact_protocol.go
index 18915fe..a5223d3 100644
--- a/lib/go/thrift/compact_protocol.go
+++ b/lib/go/thrift/compact_protocol.go
@@ -486,10 +486,6 @@
 		err = NewTProtocolException(e)
 		return
 	}
-	err = checkSizeForProtocol(size32, p.cfg)
-	if err != nil {
-		return
-	}
 	size = int(size32)
 
 	keyAndValueType := byte(STOP)
@@ -501,6 +497,13 @@
 	}
 	keyType, _ = p.getTType(tCompactType(keyAndValueType >> 4))
 	valueType, _ = p.getTType(tCompactType(keyAndValueType & 0xf))
+
+	minElemSize := p.getMinSerializedSize(keyType) + p.getMinSerializedSize(valueType)
+	totalMinSize := size32 * minElemSize
+	err = checkSizeForProtocol(totalMinSize, p.cfg)
+	if err != nil {
+		return
+	}
 	return
 }
 
@@ -524,15 +527,18 @@
 		}
 		size = int(size2)
 	}
-	err = checkSizeForProtocol(int32(size), p.cfg)
-	if err != nil {
-		return
-	}
 	elemType, e := p.getTType(tCompactType(size_and_type))
 	if e != nil {
 		err = NewTProtocolException(e)
 		return
 	}
+
+	minElemSize := p.getMinSerializedSize(elemType)
+	totalMinSize := int32(size) * minElemSize
+	err = checkSizeForProtocol(totalMinSize, p.cfg)
+	if err != nil {
+		return
+	}
 	return
 }
 
@@ -860,6 +866,42 @@
 	p.cfg = conf
 }
 
+// Return the minimum number of bytes a type will consume on the wire
+func (p *TCompactProtocol) getMinSerializedSize(ttype TType) int32 {
+	switch ttype {
+	case STOP:
+		return 1 // T_STOP needs to count itself
+	case VOID:
+		return 1 // T_VOID needs to count itself
+	case BOOL:
+		return 1 // sizeof(int8)
+	case BYTE:
+		return 1 // sizeof(int8)
+	case DOUBLE:
+		return 8 // uses PutUint64() which always writes 8 bytes
+	case I16:
+		return 1 // zigzag
+	case I32:
+		return 1 // zigzag
+	case I64:
+		return 1 // zigzag
+	case STRING:
+		return 1 // string length
+	case STRUCT:
+		return 1 // empty struct needs at least 1 byte for the T_STOP
+	case MAP:
+		return 1 // element count
+	case SET:
+		return 1 // element count
+	case LIST:
+		return 1 // element count
+	case UUID:
+		return 16 // 16 bytes
+	default:
+		return 1 // unknown type
+	}
+}
+
 var (
 	_ TConfigurationSetter = (*TCompactProtocolFactory)(nil)
 	_ TConfigurationSetter = (*TCompactProtocol)(nil)
diff --git a/lib/go/thrift/json_protocol.go b/lib/go/thrift/json_protocol.go
index 6743a7f..3d53b69 100644
--- a/lib/go/thrift/json_protocol.go
+++ b/lib/go/thrift/json_protocol.go
@@ -314,11 +314,14 @@
 	if err != nil {
 		return keyType, valueType, size, err
 	}
-	err = checkSizeForProtocol(int32(iSize), p.cfg)
+	size = int(iSize)
+
+	minElemSize := p.getMinSerializedSize(keyType) + p.getMinSerializedSize(valueType)
+	totalMinSize := int32(iSize) * minElemSize
+	err = checkSizeForProtocol(totalMinSize, p.cfg)
 	if err != nil {
 		return keyType, valueType, 0, err
 	}
-	size = int(iSize)
 
 	_, e = p.ParseObjectStart()
 	return keyType, valueType, size, e
@@ -492,11 +495,14 @@
 	if err != nil {
 		return elemType, 0, err
 	}
-	err = checkSizeForProtocol(int32(nSize), p.cfg)
+	size = int(nSize)
+
+	minElemSize := p.getMinSerializedSize(elemType)
+	totalMinSize := int32(nSize) * minElemSize
+	err = checkSizeForProtocol(totalMinSize, p.cfg)
 	if err != nil {
 		return elemType, 0, err
 	}
-	size = int(nSize)
 	return elemType, size, nil
 }
 
@@ -564,4 +570,40 @@
 	return TType(STOP), NewTProtocolExceptionWithType(INVALID_DATA, e)
 }
 
+// Return the minimum number of bytes a type will consume on the wire
+func (p *TJSONProtocol) getMinSerializedSize(ttype TType) int32 {
+	switch ttype {
+	case STOP:
+		return 1 // T_STOP needs to count itself
+	case VOID:
+		return 1 // T_VOID needs to count itself
+	case BOOL:
+		return 1 // written as int
+	case BYTE:
+		return 1
+	case DOUBLE:
+		return 1
+	case I16:
+		return 1
+	case I32:
+		return 1
+	case I64:
+		return 1
+	case STRING:
+		return 2 // empty string
+	case STRUCT:
+		return 2 // empty struct
+	case MAP:
+		return 2 // empty map
+	case SET:
+		return 2 // empty set
+	case LIST:
+		return 2 // empty list
+	case UUID:
+		return 16 // empty UUID
+	default:
+		return 1 // unknown type
+	}
+}
+
 var _ TConfigurationSetter = (*TJSONProtocol)(nil)
diff --git a/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx b/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx
index 48b8d1e..c510895 100644
--- a/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx
+++ b/lib/haxe/src/org/apache/thrift/protocol/TBinaryProtocol.hx
@@ -321,8 +321,8 @@
 	{
 		switch (type)
 		{
-			case TType.STOP: return 0;
-			case TType.VOID_: return 0;
+			case TType.STOP: return 1;  // T_STOP needs to count itself
+			case TType.VOID_: return 1;  // T_VOID needs to count itself
 			case TType.BOOL: return 1;
 			case TType.BYTE: return 1;
 			case TType.DOUBLE: return 8;
@@ -330,7 +330,7 @@
 			case TType.I32: return 4;
 			case TType.I64: return 8;
 			case TType.STRING: return 4;  // string length
-			case TType.STRUCT: return 0;  // empty struct
+			case TType.STRUCT: return 1;  // empty struct needs at least 1 byte for the T_STOP
 			case TType.MAP: return 4;  // element count
 			case TType.SET: return 4;  // element count
 			case TType.LIST: return 4;  // element count
diff --git a/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx b/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx
index d3577f1..22af9e8 100644
--- a/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx
+++ b/lib/haxe/src/org/apache/thrift/protocol/TCompactProtocol.hx
@@ -725,8 +725,8 @@
 	{
 		switch (type)
 		{
-			case TType.STOP:    return 0;
-			case TType.VOID_:    return 0;
+			case TType.STOP:    return 1;  // T_STOP needs to count itself
+			case TType.VOID_:    return 1;  // T_VOID needs to count itself
 			case TType.BOOL:   return 1;
 			case TType.DOUBLE: return 8;  // uses fixedLongToBytes() which always writes 8 bytes
 			case TType.BYTE: return 1;
@@ -734,7 +734,7 @@
 			case TType.I32:     return 1;  // zigzag
 			case TType.I64:     return 1;  // zigzag
 			case TType.STRING: return 1;  // string length
-			case TType.STRUCT:  return 0;             // empty struct
+			case TType.STRUCT:  return 1;  // empty struct needs at least 1 byte for the T_STOP
 			case TType.MAP:     return 1;  // element count
 			case TType.SET:    return 1;  // element count
 			case TType.LIST:    return 1;  // element count
diff --git a/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx b/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx
index a47479d..4bbc427 100644
--- a/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx
+++ b/lib/haxe/src/org/apache/thrift/protocol/TJSONProtocol.hx
@@ -792,8 +792,8 @@
 	{
 		switch (type)
 		{
-			case TType.STOP: return 0;
-			case TType.VOID_: return 0;
+			case TType.STOP: return 1;  // T_STOP needs to count itself
+			case TType.VOID_: return 1;  // T_VOID needs to count itself
 			case TType.BOOL: return 1;  // written as int  
 			case TType.BYTE: return 1;
 			case TType.DOUBLE: return 1;
diff --git a/lib/haxe/src/org/apache/thrift/transport/TEndpointTransport.hx b/lib/haxe/src/org/apache/thrift/transport/TEndpointTransport.hx
index b20dc70..fad3b54 100644
--- a/lib/haxe/src/org/apache/thrift/transport/TEndpointTransport.hx
+++ b/lib/haxe/src/org/apache/thrift/transport/TEndpointTransport.hx
@@ -78,7 +78,7 @@
 	// Throws if there are not enough bytes in the input stream to satisfy a read of numBytes bytes of data
 	public override function CheckReadBytesAvailable(numBytes : Int64) : Void
 	{
-		if (RemainingMessageSize < numBytes)
+		if (RemainingMessageSize < numBytes || numBytes < 0)
 			throw new TTransportException(TTransportException.END_OF_FILE, 'CheckReadBytesAvailable(${numBytes}): MaxMessageSize reached, only ${RemainingMessageSize} bytes available');
 	}
 
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
index 99c3e93..64d62b1 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TBinaryProtocol.java
@@ -524,9 +524,9 @@
   public int getMinSerializedSize(byte type) throws TTransportException {
     switch (type) {
       case 0:
-        return 0; // Stop
+        return 1; // Stop - T_STOP needs to count itself
       case 1:
-        return 0; // Void
+        return 1; // Void - T_VOID needs to count itself
       case 2:
         return 1; // Bool sizeof(byte)
       case 3:
@@ -542,7 +542,7 @@
       case 11:
         return 4; // string length sizeof(int)
       case 12:
-        return 0; // empty struct
+        return 1; // empty struct needs at least 1 byte for the T_STOP
       case 13:
         return 4; // element count Map sizeof(int)
       case 14:
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
index abb5cca..3407f62 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TCompactProtocol.java
@@ -911,9 +911,9 @@
   public int getMinSerializedSize(byte type) throws TTransportException {
     switch (type) {
       case 0:
-        return 0; // Stop
+        return 1; // Stop - T_STOP needs to count itself
       case 1:
-        return 0; // Void
+        return 1; // Void - T_VOID needs to count itself
       case 2:
         return 1; // Bool sizeof(byte)
       case 3:
@@ -929,7 +929,7 @@
       case 11:
         return 1; // string length sizeof(byte)
       case 12:
-        return 0; // empty struct
+        return 1; // empty struct needs at least 1 byte for the T_STOP
       case 13:
         return 1; // element count Map sizeof(byte)
       case 14:
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java
index 203c016..e32d70b 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TJSONProtocol.java
@@ -972,9 +972,9 @@
   public int getMinSerializedSize(byte type) throws TTransportException {
     switch (type) {
       case 0:
-        return 0; // Stop
+        return 1; // Stop - T_STOP needs to count itself
       case 1:
-        return 0; // Void
+        return 1; // Void - T_VOID needs to count itself
       case 2:
         return 1; // Bool
       case 3:
diff --git a/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java b/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java
index cceb517..7b78a65 100644
--- a/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java
+++ b/lib/java/src/main/java/org/apache/thrift/protocol/TSimpleJSONProtocol.java
@@ -477,9 +477,9 @@
   public int getMinSerializedSize(byte type) throws TException {
     switch (type) {
       case 0:
-        return 0; // Stop
+        return 1; // Stop - T_STOP needs to count itself
       case 1:
-        return 0; // Void
+        return 1; // Void - T_VOID needs to count itself
       case 2:
         return 1; // Bool
       case 3:
diff --git a/lib/java/src/main/java/org/apache/thrift/transport/TEndpointTransport.java b/lib/java/src/main/java/org/apache/thrift/transport/TEndpointTransport.java
index 6026390..99f3192 100644
--- a/lib/java/src/main/java/org/apache/thrift/transport/TEndpointTransport.java
+++ b/lib/java/src/main/java/org/apache/thrift/transport/TEndpointTransport.java
@@ -92,7 +92,7 @@
    * @param numBytes
    */
   public void checkReadBytesAvailable(long numBytes) throws TTransportException {
-    if (remainingMessageSize < numBytes)
+    if (remainingMessageSize < numBytes || numBytes < 0)
       throw new TTransportException(
           TTransportException.MESSAGE_SIZE_LIMIT,
           "Message size exceeds limit: " + getMaxMessageSize());
diff --git a/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs b/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs
index 44fa9f7..fc5be9a 100644
--- a/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs
+++ b/lib/netstd/Thrift/Protocol/TBinaryProtocol.cs
@@ -452,8 +452,8 @@
         {
             switch (type)
             {
-                case TType.Stop: return 0;
-                case TType.Void: return 0;
+                case TType.Stop: return 1;  // T_STOP needs to count itself
+                case TType.Void: return 1;  // T_VOID needs to count itself
                 case TType.Bool: return sizeof(byte);
                 case TType.Byte: return sizeof(byte);
                 case TType.Double: return sizeof(double);
@@ -461,7 +461,7 @@
                 case TType.I32: return sizeof(int);
                 case TType.I64: return sizeof(long);
                 case TType.String: return sizeof(int);  // string length
-                case TType.Struct: return 0;  // empty struct
+                case TType.Struct: return 1;  // empty struct needs at least 1 byte for the T_STOP
                 case TType.Map: return sizeof(int);  // element count
                 case TType.Set: return sizeof(int);  // element count
                 case TType.List: return sizeof(int);  // element count
diff --git a/lib/netstd/Thrift/Protocol/TCompactProtocol.cs b/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
index 1fd7e50..5ca4841 100644
--- a/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
+++ b/lib/netstd/Thrift/Protocol/TCompactProtocol.cs
@@ -816,8 +816,8 @@
         {
             switch (type)
             {
-                case TType.Stop: return 0;
-                case TType.Void: return 0;
+                case TType.Stop: return 1;  // T_STOP needs to count itself
+                case TType.Void: return 1;  // T_VOID needs to count itself
                 case TType.Bool: return sizeof(byte);
                 case TType.Double: return 8;  // uses fixedLongToBytes() which always writes 8 bytes
                 case TType.Byte: return sizeof(byte);
@@ -825,7 +825,7 @@
                 case TType.I32: return sizeof(byte);  // zigzag
                 case TType.I64: return sizeof(byte);  // zigzag
                 case TType.String: return sizeof(byte);  // string length
-                case TType.Struct: return 0;             // empty struct
+                case TType.Struct: return 1;             // empty struct needs at least 1 byte for the T_STOP
                 case TType.Map: return sizeof(byte);  // element count
                 case TType.Set: return sizeof(byte);  // element count
                 case TType.List: return sizeof(byte);  // element count
diff --git a/lib/netstd/Thrift/Protocol/TJSONProtocol.cs b/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
index 170f294..37424a7 100644
--- a/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
+++ b/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
@@ -838,8 +838,8 @@
         {
             switch (type)
             {
-                case TType.Stop: return 0;
-                case TType.Void: return 0;
+                case TType.Stop: return 1;  // T_STOP needs to count itself
+                case TType.Void: return 1;  // T_VOID needs to count itself
                 case TType.Bool: return 1;  // written as int  
                 case TType.Byte: return 1;
                 case TType.Double: return 1;