From d60f9789dbbe5e8fb05815eb01cc5213f811ea9b Mon Sep 17 00:00:00 2001 From: Randy Abernethy Date: Fri, 28 Mar 2014 10:36:38 -0700 Subject: [PATCH] THRIFT-2398:Improve Node Server Library\nClient: Node\nPatch: Randy Abernethy\n\nGeneral server parameter harmonization and comments --- lib/js/test/server_http.js | 4 +- lib/js/test/server_https.js | 27 +- lib/nodejs/lib/thrift/index.js | 5 - lib/nodejs/lib/thrift/server.js | 152 ++---------- lib/nodejs/lib/thrift/web_server.js | 199 +++++++++------ lib/nodejs/test/client.js | 35 +-- lib/nodejs/test/multiplex_client.js | 315 ++---------------------- lib/nodejs/test/multiplex_server.js | 36 +-- lib/nodejs/test/server.js | 51 ++-- lib/nodejs/test/test_handler.js | 2 +- lib/nodejs/test/test_handler_promise.js | 2 +- 11 files changed, 220 insertions(+), 608 deletions(-) diff --git a/lib/js/test/server_http.js b/lib/js/test/server_http.js index 01174bc8..ce09afc7 100644 --- a/lib/js/test/server_http.js +++ b/lib/js/test/server_http.js @@ -35,12 +35,12 @@ var ThriftTestHandler = require('./test_handler').ThriftTestHandler; var ThriftTestSvcOpt = { transport: TBufferedTransport, protocol: TJSONProtocol, - cls: ThriftTestSvc, + processor: ThriftTestSvc, handler: ThriftTestHandler }; var ThriftWebServerOptions = { - staticFilePath: ".", + files: ".", services: { "/service": ThriftTestSvcOpt } diff --git a/lib/js/test/server_https.js b/lib/js/test/server_https.js index 28f0585b..69d7e895 100644 --- a/lib/js/test/server_https.js +++ b/lib/js/test/server_https.js @@ -35,21 +35,21 @@ var ThriftTestHandler = require('./test_handler').ThriftTestHandler; //Setup the I/O stack options for the ThriftTest service var ThriftTestSvcOpt = { - transport: TBufferedTransport, - protocol: TJSONProtocol, - cls: ThriftTestSvc, - handler: ThriftTestHandler + transport: TBufferedTransport, + protocol: TJSONProtocol, + processor: ThriftTestSvc, + handler: ThriftTestHandler }; var ThriftWebServerOptions = { - staticFilePath: ".", - tlsOptions: { - key: fs.readFileSync("../../../test/keys/server.key"), - cert: fs.readFileSync("../../../test/keys/server.crt") - }, - services: { - "/service": ThriftTestSvcOpt - } + files: ".", + tls: { + key: fs.readFileSync("../../../test/keys/server.key"), + cert: fs.readFileSync("../../../test/keys/server.crt") + }, + services: { + "/service": ThriftTestSvcOpt + } }; var server = thrift.createWebServer(ThriftWebServerOptions); @@ -57,6 +57,3 @@ var port = 8089; server.listen(port); console.log("Serving files from: " + __dirname); console.log("Http/Thrift Server running on port: " + port); - - - diff --git a/lib/nodejs/lib/thrift/index.js b/lib/nodejs/lib/thrift/index.js index 84874643..dd965d29 100644 --- a/lib/nodejs/lib/thrift/index.js +++ b/lib/nodejs/lib/thrift/index.js @@ -28,12 +28,7 @@ exports.createStdIOConnection = connection.createStdIOConnection; var server = require('./server'); exports.createServer = server.createServer; -exports.createSSLServer = server.createSSLServer; -exports.createHttpServer = server.createHttpServer; -exports.createHttpsServer = server.createHttpsServer; -exports.httpMiddleware = server.httpMiddleware; exports.createMultiplexServer = server.createMultiplexServer; -exports.createMultiplexSSLServer = server.createMultiplexSSLServer; var web_server = require('./web_server'); exports.createWebServer = web_server.createWebServer; diff --git a/lib/nodejs/lib/thrift/server.js b/lib/nodejs/lib/thrift/server.js index 398d3988..06ee90bc 100644 --- a/lib/nodejs/lib/thrift/server.js +++ b/lib/nodejs/lib/thrift/server.js @@ -17,79 +17,24 @@ * under the License. */ var net = require('net'); -var http = require('http'); -var https = require('https'); var tls = require('tls'); -var url = require("url"); -var path = require("path"); -var fs = require("fs"); var ttransport = require('./transport'), TBinaryProtocol = require('./protocol').TBinaryProtocol; +/** + * Create a Thrift server which can serve one or multiple services. + * @param {object} processor - A normal or multiplexedProcessor (must + * be preconstructed with the desired handler). + * @param {ServerOptions} options - Optional additional server configuration. + * @returns {object} - The Apache Thrift Multipled Server. + */ exports.createMultiplexServer = function(processor, options) { - - var transport = (options && options.transport) ? options.transport : ttransport.TBufferedTransport; - var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol; - - return net.createServer(function(stream) { - var self = this; - stream.on('data', transport.receiver(function(transportWithData) { - var input = new protocol(transportWithData); - var output = new protocol(new transport(undefined, function(buf) { - try { - stream.write(buf); - } catch (err) { - self.emit('error', err); - stream.end(); - } - })); - - try { - do { - processStatus = processor.process(input, output); - transportWithData.commitPosition(); - } while (true); - } - catch (err) { - if (err instanceof ttransport.InputBufferUnderrunError) { - //The last data in the buffer was not a complete message, wait for the rest - transportWithData.rollbackPosition(); - } - else if (err.message === "Invalid type: undefined") { - //No more data in the buffer - //This trap is a bit hackish - //The next step to improve the node behavior here is to have - // the compiler generated process method throw a more explicit - // error when the network buffer is empty (regardles of the - // protocol/transport stack in use) and replace this heuristic. - // Also transports should probably not force upper layers to - // manage their buffer positions (i.e. rollbackPosition() and - // commitPosition() should be eliminated in lieu of a transport - // encapsulated buffer management strategy.) - transportWithData.rollbackPosition(); - } - else { - //Unexpected error - self.emit('error', err); - stream.end(); - } - } - })); - - stream.on('end', function() { - stream.end(); - }); - }); -}; - -exports.createMultiplexSSLServer = function(processor, options) { - var transport = (options && options.transport) ? options.transport : ttransport.TBufferedTransport; var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol; - return tls.createServer(options, function(stream) { + function serverImpl(stream) { var self = this; stream.on('data', transport.receiver(function(transportWithData) { var input = new protocol(transportWithData); @@ -107,8 +52,7 @@ exports.createMultiplexSSLServer = function(processor, options) { processStatus = processor.process(input, output); transportWithData.commitPosition(); } while (true); - } - catch (err) { + } catch (err) { if (err instanceof ttransport.InputBufferUnderrunError) { //The last data in the buffer was not a complete message, wait for the rest transportWithData.rollbackPosition(); @@ -137,72 +81,24 @@ exports.createMultiplexSSLServer = function(processor, options) { stream.on('end', function() { stream.end(); }); - }); -}; - - -function httpRequestHandler(cls, handler, options) { - if (cls.Processor) { - cls = cls.Processor; - } - var processor = new cls(handler); - var transport = (options && options.transport) ? options.transport : ttransport.TBufferedTransport; - var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol; - - return function(request, response) { - request.on('data', transport.receiver(function(transportWithData) { - var input = new protocol(transportWithData); - var output = new protocol(new transport(undefined, function(buf) { - try { - response.write(buf); - } catch (err) { - response.writeHead(500); - } - response.end(); - })); - - try { - processor.process(input, output); - transportWithData.commitPosition(); - } - catch (err) { - if (err instanceof ttransport.InputBufferUnderrunError) { - transportWithData.rollbackPosition(); - } else { - response.writeHead(500); - response.end(); - throw err; - } - } - })); }; -} - -exports.httpMiddleware = httpRequestHandler; - -exports.createHttpServer = function(cls, handler, options) { - return http.createServer(options, httpRequestHandler(cls, handler, options)); -}; - -exports.createHttpsServer = function(cls, handler, options) { - return https.createServer(options, httpRequestHandler(cls, handler, options)); -}; - - -exports.createServer = function(cls, handler, options) { - if (cls.Processor) { - cls = cls.Processor; + + if (options.tls) { + return tls.createServer(options.tls, serverImpl); + } else { + return net.createServer(serverImpl); } - var processor = new cls(handler); - - return exports.createMultiplexServer(processor,options); }; -exports.createSSLServer = function(cls, handler, options) { - if (cls.Processor) { - cls = cls.Processor; +/** + * Create a single service Apache Thrift server. + * @param {object} processor - A service class or processor function. + * @param {ServerOptions} options - Optional additional server configuration. + * @returns {object} - The Apache Thrift Multipled Server. + */ +exports.createServer = function(processor, handler, options) { + if (processor.Processor) { + processor = processor.Processor; } - var processor = new cls(handler); - - return exports.createMultiplexSSLServer(processor,options); + return exports.createMultiplexServer(new processor(handler), options); }; diff --git a/lib/nodejs/lib/thrift/web_server.js b/lib/nodejs/lib/thrift/web_server.js index a0403806..fbe4f02a 100644 --- a/lib/nodejs/lib/thrift/web_server.js +++ b/lib/nodejs/lib/thrift/web_server.js @@ -23,6 +23,7 @@ var path = require("path"); var fs = require("fs"); var crypto = require("crypto"); +var MultiplexedProcessor = require('./multiplexed_processor').MultiplexedProcessor; var TTransport = require('./transport'); var TBufferedTransport = require('./transport').TBufferedTransport; var TBinaryProtocol = require('./protocol').TBinaryProtocol; @@ -39,7 +40,7 @@ var TBinaryProtocol = require('./protocol').TBinaryProtocol; * configured for a max of 4GB presently and needs to be adjusted * if Node/Browsers become capabile of > 4GB frames. * - * - FIN is always 1, ATRPC messages are sent in a single frame + * - FIN is 1 if the message is complete * - RSV1/2/3 are always 0 * - Opcode is 1(TEXT) for TJSONProtocol and 2(BIN) for TBinaryProtocol * - Mask Present bit is 1 sending to-server and 0 sending to-client @@ -122,11 +123,13 @@ var wsFrame = { * @property {Buffer} nextFrame - Multiple ATRPC messages may be sent in a * single WebSocket frame, this Buffer contains * any bytes remaining to be decoded + * @property {Boolean} FIN - True is the message is complete */ /** Decodes a WebSocket frame * - * @param {Buffer} frame - The raw inbound frame + * @param {Buffer} frame - The raw inbound frame, if this is a continuation + * frame it must have a mask property with the mask. * @returns {WSDecodeResult} - The decoded payload * * @see {@link WSDecodeResult} @@ -136,11 +139,13 @@ var wsFrame = { data: null, mask: null, binEncoding: false, - nextFrame: null + nextFrame: null, + FIN: true }; + //Byte 0 - FIN & OPCODE if (wsFrame.fin.FIN != (frame[0] & wsFrame.fin.FIN)) { - console.log("WebSocket frame error: Received a frame without fin set."); + result.FIN = false; } result.binEncoding = (wsFrame.frameOpCodes.BIN == (frame[0] & wsFrame.frameOpCodes.BIN)); //Byte 1 or 1-3 or 1-9 - SIZE @@ -159,17 +164,22 @@ var wsFrame = { result.mask = new Buffer(4); frame.copy(result.mask, 0, dataOffset, dataOffset + 4); dataOffset += 4; - } + } //Payload result.data = new Buffer(len); frame.copy(result.data, 0, dataOffset, dataOffset+len); - wsFrame.applyMask(result.data, result.mask); - - //Residual Frames + if (result.mask) { + wsFrame.applyMask(result.data, result.mask); + } + //Next Frame if (frame.length > dataOffset+len) { result.nextFrame = new Buffer(frame.length - (dataOffset+len)); frame.copy(result.nextFrame, 0, dataOffset+len, frame.length); } + //Don't forward control frames + if (frame[0] & wsFrame.frameOpCodes.FINCTRL) { + result.data = null; + } return result; }, @@ -206,7 +216,8 @@ var wsFrame = { frameOpCodes: { CONT: 0x00, TEXT: 0x01, - BIN: 0x02 + BIN: 0x02, + CTRL: 0x80 }, mask: { @@ -226,44 +237,42 @@ var wsFrame = { /** * @class - * @name WebServerOptions - * @property {string} staticFilePath - Path to serve static files from, if absent or "" - * static file service is disabled - * @property {object} tlsOptions - Node.js TLS options (see: nodejs.org/api/tls.html), - * if not present or null regular http is used, - * at least a key and a cert must be defined to use SSL/TLS - * @property {object} services - An object hash mapping service URI strings - * to ThriftServiceOptions objects - * @property {object} headers - An object hash mapping header strings to header value, + * @name ServerOptions + * @property {array} cors - Array of CORS origin strings to permit requests from. + * @property {string} files - Path to serve static files from, if absent or "" + * static file service is disabled. + * @property {object} headers - An object hash mapping header strings to header value * strings, these headers are transmitted in response to * static file GET operations. - * @see {@link ThriftServiceOptions} + * @property {object} services - An object hash mapping service URI strings + * to ServiceOptions objects + * @property {object} tls - Node.js TLS options (see: nodejs.org/api/tls.html), + * if not present or null regular http is used, + * at least a key and a cert must be defined to use SSL/TLS + * @see {@link ServiceOptions} */ /** * @class - * @name ThriftServiceOptions + * @name ServiceOptions * @property {object} transport - The layered transport to use (defaults * to TBufferedTransport). - * @property {object} protocol - The Thrift Protocol to use (defaults to + * @property {object} protocol - The serialization Protocol to use (defaults to * TBinaryProtocol). - * @property {object} processor - The Thrift Service class generated by the IDL - * Compiler for the service (the "cls" key can also - * be used for this attribute). + * @property {object} processor - The Thrift Service class/processor generated + * by the IDL Compiler for the service (the "cls" + * key can also be used for this attribute). * @property {object} handler - The handler methods for the Thrift Service. - * @property {array} corsOrigins - Array of CORS origin strings to permit requests from. */ /** - * Creates a Thrift server which can serve static files and/or one or + * Create a Thrift server which can serve static files and/or one or * more Thrift Services. - * @param {WebServerOptions} options - The server configuration. - * @returns {object} - The Thrift server. - * - * @see {@link WebServerOptions} + * @param {ServerOptions} options - The server configuration. + * @returns {object} - The Apache Thrift Web Server. */ exports.createWebServer = function(options) { - var baseDir = options.staticFilePath; + var baseDir = options.files; var contentTypesByExtension = { '.txt': 'text/plain', '.html': 'text/html', @@ -282,53 +291,62 @@ exports.createWebServer = function(options) { var services = options.services; for (uri in services) { var svcObj = services[uri]; - var processor = (svcObj.processor) ? (svcObj.processor.Processor || svcObj.processor) : - (svcObj.cls.Processor || svcObj.cls); - svcObj.processor = new processor(svcObj.handler); + + //Setup the processor + if (svcObj.processor instanceof MultiplexedProcessor) { + //Multiplex processors have pre embeded processor/handler pairs, save as is + svcObj.processor = svcObj.processor; + } else { + //For historical reasons Node.js supports processors passed in directly or via the + // IDL Compiler generated class housing the processor. Also, the options property + // for a Processor has been called both cls and processor at different times. We + // support any of the four possibilities here. + var processor = (svcObj.processor) ? (svcObj.processor.Processor || svcObj.processor) : + (svcObj.cls.Processor || svcObj.cls); + //Processors can be supplied as constructed objects with handlers already embeded, + // if a handler is provided we construct a new processor, if not we use the processor + // object directly + if (svcObj.handler) { + svcObj.processor = new processor(svcObj.handler); + } else { + svcObj.processor = processor; + } + } svcObj.transport = svcObj.transport ? svcObj.transport : TBufferedTransport; svcObj.protocol = svcObj.protocol ? svcObj.protocol : TBinaryProtocol; } //Verify CORS requirements - function VerifyCORSAndSetHeaders(request, response, svc) { - if (request.headers.origin && svc.corsOrigins) { - if (svcObj.corsOrigins["*"] || svcObj.corsOrigins[request.headers.origin]) { - //Sucess, origin allowed + function VerifyCORSAndSetHeaders(request, response) { + if (request.headers.origin && options.cors) { + if (options.cors["*"] || options.cors[request.headers.origin]) { + //Allow, origin allowed response.setHeader("access-control-allow-origin", request.headers.origin); - response.setHeader("access-control-allow-methods", "POST, OPTIONS"); + response.setHeader("access-control-allow-methods", "GET, POST, OPTIONS"); response.setHeader("access-control-allow-headers", "content-type, accept"); response.setHeader("access-control-max-age", "60"); return true; } else { - //Failure, origin denied + //Disallow, origin denied return false; } } - //Success, CORS is not in use + //Allow, CORS is not in use return true; } - //Handle OPTIONS method (CORS support) + //Handle OPTIONS method (CORS) /////////////////////////////////////////////////// function processOptions(request, response) { - var uri = url.parse(request.url).pathname; - var svc = services[uri]; - if (!svc) { - //Unsupported service - response.writeHead("403", "No Apache Thrift Service at " + uri, {}); - response.end(); - return; - } - - //Verify CORS requirements - if (VerifyCORSAndSetHeaders(request, response, svc)) { + if (VerifyCORSAndSetHeaders(request, response)) { response.writeHead("204", "No Content", {"content-length": 0}); } else { response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {}); } response.end(); } + //Handle POST methods (TXHRTransport) /////////////////////////////////////////////////// @@ -343,7 +361,7 @@ exports.createWebServer = function(options) { } //Verify CORS requirements - if (!VerifyCORSAndSetHeaders(request, response, svc)) { + if (!VerifyCORSAndSetHeaders(request, response)) { response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {}); response.end(); return; @@ -365,12 +383,10 @@ exports.createWebServer = function(options) { try { svc.processor.process(input, output); transportWithData.commitPosition(); - } - catch (err) { + } catch (err) { if (err instanceof TTransport.InputBufferUnderrunError) { transportWithData.rollbackPosition(); - } - else { + } else { response.writeHead(500); response.end(); } @@ -378,6 +394,7 @@ exports.createWebServer = function(options) { })); } + //Handle GET methods (Static Page Server) /////////////////////////////////////////////////// function processGet(request, response) { @@ -387,6 +404,14 @@ exports.createWebServer = function(options) { response.end(); return; } + + //Verify CORS requirements + if (!VerifyCORSAndSetHeaders(request, response)) { + response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {}); + response.end(); + return; + } + //Locate the file requested and send it var uri = url.parse(request.url).pathname; var filename = path.join(baseDir, uri); @@ -422,12 +447,10 @@ exports.createWebServer = function(options) { }); } + //Handle WebSocket calls (TWebSocketTransport) /////////////////////////////////////////////////// - function processWS(data, socket) { - var svc = services[Object.keys(services)[0]]; - //TODO: add multiservice support (maybe multiplexing is the answer for both XHR and WS?) - + function processWS(data, socket, svc) { svc.transport.receiver(function(transportWithData) { var input = new svc.protocol(transportWithData); var output = new svc.protocol(new svc.transport(undefined, function(buf) { @@ -456,8 +479,8 @@ exports.createWebServer = function(options) { //Create the server (HTTP or HTTPS) var server = null; - if (options.tlsOptions) { - server = https.createServer(options.tlsOptions); + if (options.tls) { + server = https.createServer(options.tls); } else { server = http.createServer(); } @@ -478,19 +501,55 @@ exports.createWebServer = function(options) { response.end(); } }).on('upgrade', function(request, socket, head) { + //Lookup service + var svc; + try { + svc = services[Object.keys(services)[0]]; + } catch(e) { + if (!route) { + socket.write("HTTP/1.1 403 No Apache Thrift Service availible\r\n\r\n"); + return; + } + } + //Perform upgrade var hash = crypto.createHash("sha1") hash.update(request.headers['sec-websocket-key'] + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); socket.write("HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-WebSocket-Accept: " + hash.digest("base64") + "\r\n" + - "\r\n"); + "\r\n"); + //Handle WebSocket traffic + var data = null; socket.on('data', function(frame) { - do { - var result = wsFrame.decode(frame); - processWS(result.data, socket); - frame = result.nextFrame; - } while (frame); + try { + while (frame) { + result = wsFrame.decode(frame); + //Prepend any existing decoded data + if (data) { + if (result.data) { + var newData = new Buffer(data.length + result.data.length); + data.copy(newData); + result.data.copy(newData, data.length); + result.data = newData; + } else { + result.data = data; + } + data = null; + } + //If this completes a message process it + if (result.FIN) { + processWS(result.data, socket, svc); + } else { + data = result.data; + } + //Prepare next frame for decoding (if any) + frame = result.nextFrame; + }; + } catch(e) { + console.log("TWebSocketTransport Exception: " + e); + socket.destroy(); + } }); }); diff --git a/lib/nodejs/test/client.js b/lib/nodejs/test/client.js index 2aa22953..a6fabfcb 100644 --- a/lib/nodejs/test/client.js +++ b/lib/nodejs/test/client.js @@ -24,8 +24,6 @@ var fs = require('fs'); var assert = require('assert'); var thrift = require('thrift'); -var ThriftTransports = require('thrift/transport'); -var ThriftProtocols = require('thrift/protocol'); var ThriftTest = require('./gen-nodejs/ThriftTest'); var ThriftTestDriver = require('./thrift_test_driver').ThriftTestDriver; var ThriftTestDriverPromise = require('./thrift_test_driver_promise').ThriftTestDriver; @@ -39,32 +37,15 @@ program .option('--promise', 'test with promise style functions') .parse(process.argv); -var protocol = undefined; -var transport = undefined; -var testDriver = undefined; -if (program.protocol === "binary") { - protocol = ThriftProtocols.TBinaryProtocol; -} else if (program.protocol === "json") { - protocol = ThriftProtocols.TJSONProtocol; -} else { - //default - protocol = ThriftProtocols.TBinaryProtocol; -} +var protocol = thrift.TBinaryProtocol; +if (program.protocol === "json") { + protocol = thrift.TJSONProtocol; +} +var transport = thrift.TBufferedTransport; if (program.transport === "framed") { - transport = ThriftTransports.TFramedTransport; -} else if (program.transport === "buffered") { - transport = ThriftTransports.TBufferedTransport; -} else { - //default - transport = ThriftTransports.TBufferedTransport; -} - -if (program.promise) { - testDriver = ThriftTestDriverPromise; -} else { - testDriver = ThriftTestDriver; + transport = thrift.TFramedTransport; } var options = { @@ -87,6 +68,10 @@ connection.on('error', function(err) { assert(false, err); }); +var testDriver = ThriftTestDriver; +if (program.promise) { + testDriver = ThriftTestDriverPromise; +} testDriver(client, function (status) { console.log(status); connection.end(); diff --git a/lib/nodejs/test/multiplex_client.js b/lib/nodejs/test/multiplex_client.js index 9ef716b7..6580cb57 100644 --- a/lib/nodejs/test/multiplex_client.js +++ b/lib/nodejs/test/multiplex_client.js @@ -17,13 +17,12 @@ * under the License. */ var thrift = require('thrift'); -var ThriftTransports = require('thrift/transport'); -var ThriftProtocols = require('thrift/protocol'); var assert = require('assert'); var ThriftTest = require('./gen-nodejs/ThriftTest'), SecondService = require('./gen-nodejs/SecondService'), ttypes = require('./gen-nodejs/ThriftTest_types'); +var ThriftTestDriver = require('./thrift_test_driver').ThriftTestDriver; var program = require('commander'); @@ -33,27 +32,16 @@ program .option('--ssl', 'use ssl transport') .parse(process.argv); -var protocol = undefined; -var transport = undefined; - -if (program.protocol === "binary") { - protocol = ThriftProtocols.TBinaryProtocol; -} else if (program.protocol === "json") { - protocol = ThriftProtocols.TJSONProtocol; -} else { - //default - protocol = ThriftProtocols.TBinaryProtocol; -} - +var transport = thrift.TBufferedTransport; if (program.transport === "framed") { - transport = ThriftTransports.TFramedTransport; -} else if (program.transport === "buffered") { - transport = ThriftTransports.TBufferedTransport; -} else { - //default - transport = ThriftTransports.TBufferedTransport; + transport = thrift.TFramedTransport; } +var protocol = thrift.TBinaryProtocol; +if (program.protocol === "json") { + protocol = thrift.TJSONProtocol; +} + var options = { transport: transport, protocol: protocol @@ -76,290 +64,17 @@ connection.on('error', function(err) { assert(false, err); }); -// deepEqual doesn't work with fields using node-int64 - -function checkRecursively(map1, map2) { - if (typeof map1 !== 'function' && typeof map2 !== 'function') { - if (!map1 || typeof map1 !== 'object') { - assert.equal(map1, map2); - } else { - for (var key in map1) { - checkRecursively(map1[key], map2[key]); - } - } - } -} - -client.testString("Test", function(err, response) { - assert(!err); - assert.equal("Test", response); -}); -secondclient.secondtestString("Test", function(err, response) { +connection.on('connect', function() { + secondclient.secondtestString("Test", function(err, response) { assert(!err); assert.equal("Test", response); -}); - - -client.testVoid(function(err, response) { - assert(!err); - assert.equal(undefined, response); //void -}); - - -secondclient.secondtestString("Test", function(err, response) { - assert(!err); - assert.equal("Test", response); -}); - -client.testString("", function(err, response) { - assert(!err); - assert.equal("", 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, ‪Norsk (nynorsk)‬, ‪Norsk (" + - "bokmål)‬, 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); -}); - -var specialCharacters = 'quote: \" backslash:' + - ' forwardslash-escaped: \/ ' + - ' backspace: \b formfeed: \f newline: \n return: \r tab: ' + - ' now-all-of-them-together: "\\\/\b\n\r\t' + - ' now-a-bunch-of-junk: !@#$%&()(&%$#{}{}<><><'; -client.testString(specialCharacters, function(err, response) { - assert(!err); - assert.equal(specialCharacters, response); -}); - - -client.testByte(1, function(err, response) { - assert(!err); - assert.equal(1, response); -}); -client.testByte(0, function(err, response) { - assert(!err); - assert.equal(0, response); -}); -client.testByte(-1, function(err, response) { - assert(!err); - assert.equal(-1, response); -}); -client.testByte(-127, function(err, response) { - assert(!err); - assert.equal(-127, response); -}); - -client.testI32(-1, function(err, response) { - assert(!err); - assert.equal(-1, response); -}); - -client.testI64(5, function(err, response) { - assert(!err); - assert.equal(5, response); -}); -client.testI64(-5, function(err, response) { - assert(!err); - assert.equal(-5, response); -}); -client.testI64(-34359738368, function(err, response) { - assert(!err); - assert.equal(-34359738368, response); -}); - -client.testDouble(-5.2098523, function(err, response) { - assert(!err); - assert.equal(-5.2098523, response); -}); -client.testDouble(7.012052175215044, function(err, response) { - assert(!err); - assert.equal(7.012052175215044, response); -}); + }); - -var out = new ttypes.Xtruct({ - string_thing: 'Zero', - byte_thing: 1, - i32_thing: -3, - i64_thing: 1000000 -}); -client.testStruct(out, function(err, response) { - assert(!err); - checkRecursively(out, response); -}); - - -var out2 = new ttypes.Xtruct2(); -out2.byte_thing = 1; -out2.struct_thing = out; -out2.i32_thing = 5; -client.testNest(out2, function(err, response) { - assert(!err); - checkRecursively(out2, response); -}); - - -var mapout = {}; -for (var i = 0; i < 5; ++i) { - mapout[i] = i - 10; -} -client.testMap(mapout, function(err, response) { - assert(!err); - assert.deepEqual(mapout, response); -}); - - -var mapTestInput = { - "a": "123", - "a b": "with spaces ", - "same": "same", - "0": "numeric key", - "longValue": stringTest, - stringTest: "long key" -}; -client.testStringMap(mapTestInput, function(err, response) { - assert(!err); - assert.deepEqual(mapTestInput, response); -}); - - -var setTestInput = [1, 2, 3]; -client.testSet(setTestInput, function(err, response) { - assert(!err); - assert.deepEqual(setTestInput, response); -}); -client.testList(setTestInput, function(err, response) { - assert(!err); - assert.deepEqual(setTestInput, response); -}); - -client.testEnum(ttypes.Numberz.ONE, function(err, response) { - assert(!err); - assert.equal(ttypes.Numberz.ONE, response); -}); - -client.testTypedef(69, function(err, response) { - assert(!err); - assert.equal(69, response); -}); - - -var mapMapTest = { - "4": { - "1": 1, - "2": 2, - "3": 3, - "4": 4 - }, - "-4": { - "-4": -4, - "-3": -3, - "-2": -2, - "-1": -1 - } -}; -client.testMapMap(mapMapTest, function(err, response) { - assert(!err); - assert.deepEqual(mapMapTest, response); -}); - -var crazy = new ttypes.Insanity({ - "userMap": { - "5": 5, - "8": 8 - }, - "xtructs": [new ttypes.Xtruct({ - "string_thing": "Goodbye4", - "byte_thing": 4, - "i32_thing": 4, - "i64_thing": 4 - }), new ttypes.Xtruct({ - "string_thing": "Hello2", - "byte_thing": 2, - "i32_thing": 2, - "i64_thing": 2 - })] -}); -var insanity = { - "1": { - "2": crazy, - "3": crazy - }, - "2": { - "6": { - "userMap": null, - "xtructs": null - } - } -}; -client.testInsanity(crazy, function(err, response) { - assert(!err); - checkRecursively(insanity, response); -}); - - -client.testException('TException', function(err, response) { - //assert(err); //BUG? - assert(!response); -}); -client.testException('Xception', function(err, response) { - assert(!response); - assert.equal(err.errorCode, 1001); - assert.equal('Xception', err.message); -}); -client.testException('no Exception', function(err, response) { - assert(!err); - assert.equal(undefined, response); //void -}); - - -client.testOneway(1, function(err, response) { - assert(!response); //should not answer -}); - -/** - * redo a simple test after the oneway to make sure we aren't "off by one" -- - * if the server treated oneway void like normal void, this next test will - * fail since it will get the void confirmation rather than the correct - * result. In this circumstance, the client will throw the exception: - * - * TApplicationException: Wrong method namea - */ -client.testI32(-1, function(err, response) { - assert(!err); - assert.equal(-1, response); -}); - -setTimeout(function() { - console.log("Server successfully tested!"); + ThriftTestDriver(client, function (status) { + console.log(status); connection.end(); -}, 1500); + }); +}); // to make it also run on expresso exports.expressoTest = function() {}; diff --git a/lib/nodejs/test/multiplex_server.js b/lib/nodejs/test/multiplex_server.js index a2ea535f..6331f6f9 100644 --- a/lib/nodejs/test/multiplex_server.js +++ b/lib/nodejs/test/multiplex_server.js @@ -17,9 +17,6 @@ * under the License. */ var thrift = require('thrift'); -var Thrift = thrift.Thrift; -var ThriftTransports = require('thrift/transport'); -var ThriftProtocols = require('thrift/protocol'); var ThriftTest = require('./gen-nodejs/ThriftTest'), SecondService = require('./gen-nodejs/SecondService'), @@ -36,25 +33,14 @@ program .option('--ssl', 'use ssl transport') .parse(process.argv); -var protocol = undefined; -var transport = undefined; - -if (program.protocol === "binary") { - protocol = ThriftProtocols.TBinaryProtocol; -} else if (program.protocol === "json") { - protocol = ThriftProtocols.TJSONProtocol; -} else { - //default - protocol = ThriftProtocols.TBinaryProtocol; +var protocol = thrift.TBinaryProtocol; +if (program.protocol === "json") { + protocol = thrift.TJSONProtocol; } +var transport = thrift.TBufferedTransport; if (program.transport === "framed") { - transport = ThriftTransports.TFramedTransport; -} else if (program.transport === "buffered") { - transport = ThriftTransports.TBufferedTransport; -} else { - //default - transport = ThriftTransports.TBufferedTransport; + transport = thrift.TFramedTransport; } var ThriftTestHandler = require("./test_handler").ThriftTestHandler; @@ -81,14 +67,12 @@ var options = { protocol: protocol }; -var server = undefined; if (program.ssl) { //ssl options - options.key = fs.readFileSync(path.resolve(__dirname, 'server.key')); - options.cert = fs.readFileSync(path.resolve(__dirname, 'server.crt')); - server = thrift.createMultiplexSSLServer(processor, options); -} else { - server = thrift.createMultiplexServer(processor, options); + options.tls = { + key: fs.readFileSync(path.resolve(__dirname, 'server.key')), + cert: fs.readFileSync(path.resolve(__dirname, 'server.crt')) + } } -server.listen(9090); +thrift.createMultiplexServer(processor, options).listen(9090); diff --git a/lib/nodejs/test/server.js b/lib/nodejs/test/server.js index acc03987..6f5abe6a 100644 --- a/lib/nodejs/test/server.js +++ b/lib/nodejs/test/server.js @@ -25,13 +25,10 @@ var fs = require('fs'); var path = require('path'); var thrift = require('thrift'); -var ThriftTransports = require('thrift/transport'); -var ThriftProtocols = require('thrift/protocol'); var ThriftTest = require('./gen-nodejs/ThriftTest'); var ThriftTestHandler = require('./test_handler').ThriftTestHandler; var ThriftTestHandlerPromise = require('./test_handler_promise').ThriftTestHandler; - var program = require('commander'); program @@ -41,46 +38,30 @@ program .option('--promise', 'test with promise style functions') .parse(process.argv); -var protocol = undefined; -var transport = undefined; -var handler = undefined; - -if (program.protocol === "binary") { - protocol = ThriftProtocols.TBinaryProtocol; -} else if (program.protocol === "json") { - protocol = ThriftProtocols.TJSONProtocol; -} else { - //default - protocol = ThriftProtocols.TBinaryProtocol; -} - +var transport = thrift.TBufferedTransport; if (program.transport === "framed") { - transport = ThriftTransports.TFramedTransport; -} else if (program.transport === "buffered") { - transport = ThriftTransports.TBufferedTransport; -} else { - //default - transport = ThriftTransports.TBufferedTransport; -} + transport = thrift.TFramedTransport; +} +var protocol = thrift.TBinaryProtocol; +if (program.protocol === "json") { + protocol = thrift.TJSONProtocol; +} + +var handler = ThriftTestHandler; if (program.promise) { handler = ThriftTestHandlerPromise; -} else { - handler = ThriftTestHandler; -} +} var options = { protocol: protocol, transport: transport }; - if (program.ssl) { - //ssl options - options.key = fs.readFileSync(path.resolve(__dirname, 'server.key')); - options.cert = fs.readFileSync(path.resolve(__dirname, 'server.crt')); - thrift.createSSLServer(ThriftTest, handler, options).listen(9090); - -} else { - //default - thrift.createServer(ThriftTest, handler, options).listen(9090); + options.tls = { + key: fs.readFileSync(path.resolve(__dirname, 'server.key')), + cert: fs.readFileSync(path.resolve(__dirname, 'server.crt')) + } } +thrift.createServer(ThriftTest, handler, options).listen(9090); + diff --git a/lib/nodejs/test/test_handler.js b/lib/nodejs/test/test_handler.js index 33c8941f..ea1dc1d0 100644 --- a/lib/nodejs/test/test_handler.js +++ b/lib/nodejs/test/test_handler.js @@ -21,7 +21,7 @@ // Apache Thrift test service. var ttypes = require('./gen-nodejs/ThriftTest_types'); -var TException = require('thrift/thrift').TException; +var TException = require('thrift').Thrift.TException; var ThriftTestHandler = exports.ThriftTestHandler = { testVoid: function(result) { diff --git a/lib/nodejs/test/test_handler_promise.js b/lib/nodejs/test/test_handler_promise.js index fc698ebb..c1561bc8 100644 --- a/lib/nodejs/test/test_handler_promise.js +++ b/lib/nodejs/test/test_handler_promise.js @@ -21,7 +21,7 @@ // Apache Thrift test service. var ttypes = require('./gen-nodejs/ThriftTest_types'); -var TException = require('thrift/thrift').TException; +var TException = require('thrift').Thrift.TException; var ThriftTestHandler = exports.ThriftTestHandler = { testVoid: function() { -- 2.17.1