THRIFT-2313 nodejs server crash after processing the first request when using Multipl...
authorhenrique <henrique@apache.org>
Tue, 14 Jan 2014 14:17:04 +0000 (15:17 +0100)
committerhenrique <henrique@apache.org>
Tue, 14 Jan 2014 14:17:04 +0000 (15:17 +0100)
patch: Pierre Lamot

lib/nodejs/lib/thrift/multiplexed_processor.js
lib/nodejs/lib/thrift/transport.js
test/nodejs/Makefile.am
test/nodejs/multiplex_client.js
test/nodejs/thrift_test_driver.js

index 351733e..2931c4f 100644 (file)
 var Thrift = require('./thrift');
 
 var MultiplexedProcessor = exports.MultiplexedProcessor = function(stream, options) {
-    this.services = new Map();
+    this.services = {};
 };
 
 MultiplexedProcessor.prototype.registerProcessor = function(name, handler) {
-    this.services.set(name, handler);
+    this.services[name] = handler;
 };
 
 MultiplexedProcessor.prototype.process = function(inp, out) {
@@ -38,7 +38,7 @@ MultiplexedProcessor.prototype.process = function(inp, out) {
     var sname = p[0];
     var fname = p[1];
 
-    if (!this.services.has(sname)) {
+    if (! sname in this.services) {
         throw new Thrift.TException("TMultiplexedProcessor: Unknown service: " + sname);
     }
 
@@ -56,6 +56,6 @@ MultiplexedProcessor.prototype.process = function(inp, out) {
         };
     };
 
-    this.services.get(sname).process(inpProxy, out);
+    this.services[sname].process(inpProxy, out);
 
 };
index 3adeaf8..2926d00 100644 (file)
@@ -90,7 +90,14 @@ TFramedTransport.prototype = {
   open: function() {},
   close: function() {},
 
+  ensureAvailable: function(len) {
+    if (this.readPos + len > this.inBuf.length) {
+      throw new InputBufferUnderrunError();
+    }
+  },
+
   read: function(len) { // this function will be used for each frames.
+    this.ensureAvailable(len);
     var end = this.readPos + len;
 
     if (this.inBuf.length < end) {
@@ -103,28 +110,33 @@ TFramedTransport.prototype = {
   },
 
   readByte: function() {
+    this.ensureAvailable(1);
     return binary.readByte(this.inBuf[this.readPos++]);
   },
 
   readI16: function() {
+    this.ensureAvailable(2);
     var i16 = binary.readI16(this.inBuf, this.readPos);
     this.readPos += 2;
     return i16;
   },
 
   readI32: function() {
+    this.ensureAvailable(4);
     var i32 = binary.readI32(this.inBuf, this.readPos);
     this.readPos += 4;
     return i32;
   },
 
   readDouble: function() {
+    this.ensureAvailable(8);
     var d = binary.readDouble(this.inBuf, this.readPos);
     this.readPos += 8;
     return d;
   },
 
   readString: function(len) {
+    this.ensureAvailable(len);
     var str = this.inBuf.toString('utf8', this.readPos, this.readPos + len);
     this.readPos += len;
     return str;
index a8c10aa..39a7237 100755 (executable)
@@ -50,7 +50,7 @@ client:
        NODE_PATH=../../lib/nodejs/lib:../../lib/nodejs/lib/thrift:$(NODE_PATH) node client.js
 
 mserver:
-       NODE_PATH=../../lib/nodejs/lib:../../lib/nodejs/lib/thrift:$(NODE_PATH) node --harmony multiplex_server.js
+       NODE_PATH=../../lib/nodejs/lib:../../lib/nodejs/lib/thrift:$(NODE_PATH) node multiplex_server.js
 
 mclient:
-       NODE_PATH=../../lib/nodejs/lib:../../lib/nodejs/lib/thrift:$(NODE_PATH) node --harmony multiplex_client.js
+       NODE_PATH=../../lib/nodejs/lib:../../lib/nodejs/lib/thrift:$(NODE_PATH) node multiplex_client.js
index 3b79b6c..ba0d47b 100644 (file)
@@ -78,7 +78,31 @@ client.testString("", function(err, response) {
 });
 
 // all Languages in UTF-8
-var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, Bahasa Melayu, مازِرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, \202aNorsk (nynorsk)\202c\202aNorsk (bokmål)\202c, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +
+    "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +
+    "Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, বাংলা, " +
+    "Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, Cebuano, ᏣᎳᎩ, " +
+    "Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, Dansk, Zazaki, " +
+    "ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, " +
+    "Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, Français, " +
+    "Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, " +
+    "ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, Kreyòl ayisyen, " +
+    "Magyar, Հայերեն, Interlingua, Bahasa Indonesia, Ilokano, Ido, " +
+    "Íslenska, Italiano, 日本語, Lojban, Basa Jawa, ქართული, Kongo, " +
+    "Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, " +
+    "Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, " +
+    "Lingála, ລາວ, Lietuvių, Latviešu, Basa Banyumasan, Malagasy, " +
+    "Македонски, മലയാളം, मराठी, Bahasa Melayu, مازِرونی, Nnapulitano, " +
+    "Nedersaksisch, नेपाल भाषा, Nederlands, \202aNorsk (nynorsk)\202c\202aNorsk (" +
+    "bokmål)\202c, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, " +
+    "Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa " +
+    "Simi, Rumantsch, Romani, Română, Русский, Саха тыла, Sardu, " +
+    "Sicilianu, Scots, Sámegiella, Simple English, Slovenčina, " +
+    "Slovenščina, Српски / Srpski, Seeltersk, Svenska, Kiswahili, தமிழ், " +
+    "తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, " +
+    "Українська, اردو, Tiếng Việt, Volapük, Walon, Winaray, 吴语, isiXhosa, " +
+    "ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";
+
 client.testString(stringTest, function(err, response) {
     assert(!err);
     assert.equal(stringTest, response);
index 451573d..c61e99d 100644 (file)
@@ -31,244 +31,274 @@ var ttypes = require('./gen-nodejs/ThriftTest_types');
 \r
 var ThriftTestDriver = exports.ThriftTestDriver = function(client, callback) {\r
        \r
-        // deepEqual doesn't work with fields using node-int64\r
-       function checkRecursively(map1, map2) {\r
-         if (typeof map1 !== 'function' && typeof map2 !== 'function') {\r
-           if (!map1 || typeof map1 !== 'object') {\r
-               assert.equal(map1, map2);\r
-           } else {\r
-             for (var key in map1) {\r
-               checkRecursively(map1[key], map2[key]);\r
-             }\r
-           }\r
-         }\r
-       }\r
-\r
-       client.testVoid(function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(undefined, response); //void\r
-       });\r
-\r
-       client.testString("Test", function(err, response) {\r
-         assert( ! err);\r
-         assert.equal("Test", response);\r
-       });\r
-\r
-       client.testString("", function(err, response) {\r
-         assert( ! err);\r
-         assert.equal("", response);\r
-       });\r
-\r
-       //all Languages in UTF-8\r
-       var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, Bahasa Melayu, مازِرونی, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, \202aNorsk (nynorsk)\202c\202aNorsk (bokmål)\202c, Nouormand, Diné bizaad, Occitan, Иронау, Papiamentu, Deitsch, Norfuk / Pitkern, Polski, پنجابی, پښتو, Português, Runa Simi, Rumantsch, Romani, Română, Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, Bân-lâm-gú, 粵語";\r
-       client.testString(stringTest, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(stringTest, response);\r
-       });\r
-\r
-       var specialCharacters = 'quote: \" backslash:' +\r
-           ' forwardslash-escaped: \/ ' +\r
-           ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +\r
-           ' now-all-of-them-together: "\\\/\b\n\r\t' +\r
-           ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><' +
-           ' char-to-test-json-parsing: ]] \"]] \\" }}}{ [[[ ';
-       client.testString(specialCharacters, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(specialCharacters, response);\r
-       });\r
-\r
-\r
-       client.testByte(1, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(1, response);\r
-       });\r
-       client.testByte(0, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(0, response);\r
-       });\r
-       client.testByte(-1, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(-1, response);\r
-       });\r
-       client.testByte(-127, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(-127, response);\r
-       });\r
-\r
-       client.testI32(-1, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(-1, response);\r
-       });\r
-\r
-       client.testI64(5, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(5, response);\r
-       });\r
-       client.testI64(-5, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(-5, response);\r
-       });\r
-       client.testI64(-34359738368, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(-34359738368, response);\r
-       });\r
-\r
-       client.testDouble(-5.2098523, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(-5.2098523, response);\r
-       });\r
-       client.testDouble(7.012052175215044, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(7.012052175215044, response);\r
-       });\r
-\r
-       var out = new ttypes.Xtruct({\r
-         string_thing: 'Zero',\r
-         byte_thing: 1,\r
-         i32_thing: -3,\r
-         i64_thing: 1000000\r
-       });\r
-       client.testStruct(out, function(err, response) {\r
-         assert( ! err);\r
-         checkRecursively(out, response);\r
-       });\r
-\r
-       var out2 = new ttypes.Xtruct2();\r
-       out2.byte_thing = 1;\r
-       out2.struct_thing = out;\r
-       out2.i32_thing = 5;\r
-       client.testNest(out2, function(err, response) {\r
-         assert( ! err);\r
-         checkRecursively(out2, response);\r
-       });\r
-\r
-       var mapout = {};\r
-       for (var i = 0; i < 5; ++i) {\r
-         mapout[i] = i-10;\r
-       }\r
-       client.testMap(mapout, function(err, response) {\r
-         assert( ! err);\r
-         assert.deepEqual(mapout, response);\r
-       });\r
-\r
-       var mapTestInput = {\r
-         "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key",\r
-         "longValue":stringTest, stringTest:"long key"\r
-       };\r
-       client.testStringMap(mapTestInput, function(err, response) {\r
-         assert( ! err);\r
-         assert.deepEqual(mapTestInput, response);\r
-       });\r
-\r
-       var setTestInput = [1,2,3];\r
-       client.testSet(setTestInput, function(err, response) {\r
-         assert( ! err);\r
-         assert.deepEqual(setTestInput, response);\r
-       });\r
-       client.testList(setTestInput, function(err, response) {\r
-         assert( ! err);\r
-         assert.deepEqual(setTestInput, response);\r
-       });\r
-\r
-       client.testEnum(ttypes.Numberz.ONE, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(ttypes.Numberz.ONE, response);\r
-       });\r
-\r
-       client.testTypedef(69, function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(69, response);\r
-       });\r
-\r
-       var mapMapTest = {\r
-         "4": {"1":1, "2":2, "3":3, "4":4},\r
-         "-4": {"-4":-4, "-3":-3, "-2":-2, "-1":-1}\r
-       };\r
-       client.testMapMap(mapMapTest, function(err, response) {\r
-         assert( ! err);\r
-         assert.deepEqual(mapMapTest, response);\r
-       });\r
-\r
-       var crazy = new ttypes.Insanity({\r
-         "userMap":{ "5":5, "8":8 },\r
-         "xtructs":[new ttypes.Xtruct({\r
-             "string_thing":"Goodbye4",\r
-             "byte_thing":4,\r
-             "i32_thing":4,\r
-             "i64_thing":4\r
-           }), new ttypes.Xtruct({\r
-             "string_thing":"Hello2",\r
-             "byte_thing":2,\r
-             "i32_thing":2,\r
-             "i64_thing":2\r
-           })]\r
-       });\r
-       var insanity = {\r
-         "1":{ "2": crazy, "3": crazy },\r
-         "2":{ "6":{ "userMap":null, "xtructs":null } }\r
-       };\r
-       client.testInsanity(crazy, function(err, response) {\r
-         assert( ! err);\r
-         checkRecursively(insanity, response);\r
-       });\r
-\r
-       client.testException('TException', function(err, response) {\r
-         assert( ! response);\r
-       });\r
-\r
-       client.testException('Xception', function(err, response) {\r
-         assert( ! response);\r
-         assert.equal(err.errorCode, 1001);\r
-         assert.equal('Xception', err.message);\r
-       });\r
-\r
-       client.testException('no Exception', function(err, response) {\r
-         assert( ! err);\r
-         assert.equal(undefined, response); //void\r
-       });\r
-\r
-       client.testOneway(0, function(err, response) {\r
-         assert(false); //should not answer\r
-       });\r
-\r
-       (function() {\r
-         var test_complete = false;\r
-         var retrys = 0;\r
-         var retry_limit = 30;\r
-         var retry_interval = 100;\r
-         /**\r
-          * redo a simple test after the oneway to make sure we aren't "off by one" --\r
-          * if the server treated oneway void like normal void, this next test will\r
-          * fail since it will get the void confirmation rather than the correct\r
-          * result. In this circumstance, the client will throw the exception:\r
-          *\r
-          * Because this is the last test against the server, when it completes\r
-          * the entire suite is complete by definition (the tests run serially).\r
-          */\r
-         client.testI32(-1, function(err, response) {\r
-           assert( ! err);\r
-           assert.equal(-1, response);\r
-           test_complete = true;\r
-         });\r
-\r
-      //We wait up to retry_limit * retry_interval for the test suite to complete\r
-         function TestForCompletion() {\r
-           if(test_complete) {\r
-             if (callback) {\r
-               callback("Server successfully tested!");\r
-             }\r
-           } else {\r
-             if (++retrys < retry_limit) {\r
-               setTimeout(TestForCompletion, retry_interval);\r
-             } else {\r
-            if (callback) {\r
-              callback("Server test failed to complete after " +\r
-                (retry_limit*retry_interval/1000) + " seconds");\r
-            }\r
-             }\r
-           }\r
-         }\r
-\r
-         setTimeout(TestForCompletion, retry_interval);\r
-       })();\r
-}
\ No newline at end of file
+// deepEqual doesn't work with fields using node-int64\r
+function checkRecursively(map1, map2) {\r
+  if (typeof map1 !== 'function' && typeof map2 !== 'function') {\r
+    if (!map1 || typeof map1 !== 'object') {\r
+        assert.equal(map1, map2);\r
+    } else {\r
+      for (var key in map1) {\r
+        checkRecursively(map1[key], map2[key]);\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+client.testVoid(function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(undefined, response); //void\r
+});\r
+\r
+client.testString("Test", function(err, response) {\r
+  assert( ! err);\r
+  assert.equal("Test", response);\r
+});\r
+\r
+client.testString("", function(err, response) {\r
+  assert( ! err);\r
+  assert.equal("", response);\r
+});\r
+\r
+//all Languages in UTF-8\r
+var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +\r
+    "Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +\r
+    "Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " +\r
+    "বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн, " +\r
+    "Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg, " +\r
+    "Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English, " +\r
+    "Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt, " +\r
+    "Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego, " +\r
+    "Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski, " +\r
+    "Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia, " +\r
+    "Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa, " +\r
+    "ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар, " +\r
+    "Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino, " +\r
+    "Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa " +\r
+    "Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa " +\r
+    "Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, \202a" +\r
+    "Norsk (nynorsk)\202c\202aNorsk (bokmål)\202c, Nouormand, Diné bizaad, " +\r
+    "Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو, " +\r
+    "Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română, " +\r
+    "Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple " +\r
+    "English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk, " +\r
+    "Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog, " +\r
+    "Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, " +\r
+    "Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " +\r
+    "Bân-lâm-gú, 粵語";\r
+\r
+client.testString(stringTest, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(stringTest, response);\r
+});\r
+\r
+var specialCharacters = 'quote: \" backslash:' +\r
+    ' forwardslash-escaped: \/ ' +\r
+    ' backspace: \b formfeed: \f newline: \n return: \r tab: ' +\r
+    ' now-all-of-them-together: "\\\/\b\n\r\t' +\r
+    ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><' +\r
+    ' char-to-test-json-parsing: ]] \"]] \\" }}}{ [[[ ';\r
+client.testString(specialCharacters, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(specialCharacters, response);\r
+});\r
+\r
+client.testByte(1, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(1, response);\r
+});\r
+\r
+client.testByte(0, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(0, response);\r
+});\r
+\r
+client.testByte(-1, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(-1, response);\r
+});\r
+\r
+client.testByte(-127, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(-127, response);\r
+});\r
+\r
+client.testI32(-1, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(-1, response);\r
+});\r
+\r
+client.testI64(5, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(5, response);\r
+});\r
+\r
+client.testI64(-5, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(-5, response);\r
+});\r
+\r
+client.testI64(-34359738368, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(-34359738368, response);\r
+});\r
+\r
+client.testDouble(-5.2098523, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(-5.2098523, response);\r
+});\r
+\r
+client.testDouble(7.012052175215044, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(7.012052175215044, response);\r
+});\r
+\r
+var out = new ttypes.Xtruct({\r
+  string_thing: 'Zero',\r
+  byte_thing: 1,\r
+  i32_thing: -3,\r
+  i64_thing: 1000000\r
+});\r
+client.testStruct(out, function(err, response) {\r
+  assert( ! err);\r
+  checkRecursively(out, response);\r
+});\r
+\r
+var out2 = new ttypes.Xtruct2();\r
+out2.byte_thing = 1;\r
+out2.struct_thing = out;\r
+out2.i32_thing = 5;\r
+client.testNest(out2, function(err, response) {\r
+  assert( ! err);\r
+  checkRecursively(out2, response);\r
+});\r
+\r
+var mapout = {};\r
+for (var i = 0; i < 5; ++i) {\r
+  mapout[i] = i-10;\r
+}\r
+client.testMap(mapout, function(err, response) {\r
+  assert( ! err);\r
+  assert.deepEqual(mapout, response);\r
+});\r
+\r
+var mapTestInput = {\r
+  "a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key",\r
+  "longValue":stringTest, stringTest:"long key"\r
+};\r
+client.testStringMap(mapTestInput, function(err, response) {\r
+  assert( ! err);\r
+  assert.deepEqual(mapTestInput, response);\r
+});\r
+\r
+var setTestInput = [1,2,3];\r
+client.testSet(setTestInput, function(err, response) {\r
+  assert( ! err);\r
+  assert.deepEqual(setTestInput, response);\r
+});\r
+client.testList(setTestInput, function(err, response) {\r
+  assert( ! err);\r
+  assert.deepEqual(setTestInput, response);\r
+});\r
+\r
+client.testEnum(ttypes.Numberz.ONE, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(ttypes.Numberz.ONE, response);\r
+});\r
+\r
+client.testTypedef(69, function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(69, response);\r
+});\r
+\r
+var mapMapTest = {\r
+  "4": {"1":1, "2":2, "3":3, "4":4},\r
+  "-4": {"-4":-4, "-3":-3, "-2":-2, "-1":-1}\r
+};\r
+client.testMapMap(mapMapTest, function(err, response) {\r
+  assert( ! err);\r
+  assert.deepEqual(mapMapTest, response);\r
+});\r
+\r
+var crazy = new ttypes.Insanity({\r
+  "userMap":{ "5":5, "8":8 },\r
+  "xtructs":[new ttypes.Xtruct({\r
+      "string_thing":"Goodbye4",\r
+      "byte_thing":4,\r
+      "i32_thing":4,\r
+      "i64_thing":4\r
+    }), new ttypes.Xtruct({\r
+      "string_thing":"Hello2",\r
+      "byte_thing":2,\r
+      "i32_thing":2,\r
+      "i64_thing":2\r
+    })]\r
+});\r
+var insanity = {\r
+  "1":{ "2": crazy, "3": crazy },\r
+  "2":{ "6":{ "userMap":null, "xtructs":null } }\r
+};\r
+client.testInsanity(crazy, function(err, response) {\r
+  assert( ! err);\r
+  checkRecursively(insanity, response);\r
+});\r
+\r
+client.testException('TException', function(err, response) {\r
+  assert( ! response);\r
+});\r
+\r
+client.testException('Xception', function(err, response) {\r
+  assert( ! response);\r
+  assert.equal(err.errorCode, 1001);\r
+  assert.equal('Xception', err.message);\r
+});\r
+\r
+client.testException('no Exception', function(err, response) {\r
+  assert( ! err);\r
+  assert.equal(undefined, response); //void\r
+});\r
+\r
+client.testOneway(0, function(err, response) {\r
+  assert(false); //should not answer\r
+});\r
+\r
+(function() {\r
+  var test_complete = false;\r
+  var retrys = 0;\r
+  var retry_limit = 30;\r
+  var retry_interval = 100;\r
+  /**\r
+   * redo a simple test after the oneway to make sure we aren't "off by one" --\r
+   * if the server treated oneway void like normal void, this next test will\r
+   * fail since it will get the void confirmation rather than the correct\r
+   * result. In this circumstance, the client will throw the exception:\r
+   *\r
+   * Because this is the last test against the server, when it completes\r
+   * the entire suite is complete by definition (the tests run serially).\r
+   */\r
+  client.testI32(-1, function(err, response) {\r
+    assert( ! err);\r
+    assert.equal(-1, response);\r
+    test_complete = true;\r
+  });\r
+\r
+//We wait up to retry_limit * retry_interval for the test suite to complete\r
+  function TestForCompletion() {\r
+    if(test_complete) {\r
+      if (callback) {\r
+        callback("Server successfully tested!");\r
+      }\r
+    } else {\r
+      if (++retrys < retry_limit) {\r
+        setTimeout(TestForCompletion, retry_interval);\r
+      } else {\r
+    if (callback) {\r
+      callback("Server test failed to complete after " +\r
+        (retry_limit*retry_interval/1000) + " seconds");\r
+    }\r
+      }\r
+    }\r
+  }\r
+\r
+  setTimeout(TestForCompletion, retry_interval);\r
+})();\r
+}\r