#Generate the bindings:
../../../compiler/cpp/thrift --gen js:node user.thrift
+../../../compiler/cpp/thrift --gen js:node --gen py hello.thrift
#To run the user example, first start up the server in one terminal:
NODE_PATH=../lib:../lib/thrift node server.js
#For an example using JavaScript in the browser to connect to
#a node.js server look at hello.html, hello.js and hello.thrift
-
+#HTTP examples are provided also: httpClient.js and httpServer.js
+#You can test HTTP cross platform with the httpServer.py Python server
--- /dev/null
+var thrift = require('thrift');
+var helloSvc = require('./gen-nodejs/HelloSvc.js');
+
+var options = {
+ transport: thrift.TBufferedTransport,
+ protocol: thrift.TJSONProtocol,
+ path: "/hello",
+ headers: {"Connection": "close"},
+ https: false
+};
+
+var connection = thrift.createHttpConnection("localhost", 9090, options);
+var client = thrift.createHttpClient(helloSvc, connection);
+
+connection.on("error", function(err) {
+ console.log("Error: " + err);
+});
+
+client.hello_func(function(error, result) {
+ console.log("Msg from server: " + result);
+});
+
+
--- /dev/null
+var thrift = require('thrift');
+var helloSvc = require('./gen-nodejs/HelloSvc');
+
+//ServiceHandler: Implement the hello service
+var helloHandler = {
+ hello_func: function (result) {
+ console.log("Received Hello call");
+ result(null, "Hello from Node.js");
+ }
+};
+
+//ServiceOptions: The I/O stack for the service
+var helloSvcOpt = {
+ handler: helloHandler,
+ processor: helloSvc,
+ protocol: thrift.TJSONProtocol,
+ transport: thrift.TBufferedTransport
+};
+
+//ServerOptions: Define server features
+var serverOpt = {
+ services: {
+ "/hello": helloSvcOpt
+ }
+}
+
+//Create and start the web server
+var port = 9090;
+thrift.createWebServer(serverOpt).listen(port);
+console.log("Http/Thrift Server running on port: " + port);
+
--- /dev/null
+import sys
+sys.path.append('gen-py')
+
+from hello import HelloSvc
+from thrift.protocol import TJSONProtocol
+from thrift.server import THttpServer
+
+class HelloSvcHandler:
+ def hello_func(self):
+ print "Hello Called"
+ return "hello from Python"
+
+processor = HelloSvc.Processor(HelloSvcHandler())
+protoFactory = TJSONProtocol.TJSONProtocolFactory()
+port = 9090
+server = THttpServer.THttpServer(processor, ("localhost", port), protoFactory)
+print "Python server running on port " + str(port)
+server.serve()
+
client['recv_' + header.fname](message, header.mtype, dummy_seqid);
} else {
delete client._reqs[dummy_seqid];
- throw new thrift.TApplicationException(thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
- "Received a response to an unknown RPC function");
+ self.emit("error",
+ new thrift.TApplicationException(thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
+ "Received a response to an unknown RPC function"));
}
}
}
* specific language governing permissions and limitations
* under the License.
*/
+var util = require('util');
+var http = require('http');
+var https = require('https');
+var EventEmitter = require("events").EventEmitter;
var thrift = require('./thrift');
var ttransport = require('./transport');
var tprotocol = require('./protocol');
-var http = require('http');
+/**
+ * @class
+ * @name ConnectOptions
+ * @property {string} transport - The Thrift layered transport to use (TBufferedTransport, etc).
+ * @property {string} protocol - The Thrift serialization protocol to use (TBinaryProtocol, etc.).
+ * @property {string} path - The URL path to POST to (e.g. "/", "/mySvc", "/thrift/quoteSvc", etc.).
+ * @property {object} headers - A standard Node.js header hash, an object hash containing key/value
+ * pairs where the key is the header name string and the value is the header value string.
+ * @property {boolean} https - True causes the connection to use https, otherwise http is used.
+ * @property {object} nodeOptions - Options passed on to node.
+ * @example
+ * //Use a connection that requires ssl/tls, closes the connection after each request,
+ * // uses the buffered transport layer, uses the JSON protocol and directs RPC traffic
+ * // to https://thrift.example.com:9090/hello
+ * var thrift = require('thrift');
+ * var options = {
+ * transport: thrift.TBufferedTransport,
+ * protocol: thrift.TJSONProtocol,
+ * path: "/hello",
+ * headers: {"Connection": "close"},
+ * https: true
+ * };
+ * var con = thrift.createHttpConnection("thrift.example.com", 9090, options);
+ * var client = thrift.createHttpClient(myService, connection);
+ * client.myServiceFunction();
+ */
+/**
+ * Initializes a Thrift HttpConnection instance (use createHttpConnection() rather than
+ * instantiating directly).
+ * @constructor
+ * @param {string} host - The host name or IP to connect to.
+ * @param {number} port - The TCP port to connect to.
+ * @param {ConnectOptions} options - The configuration options to use.
+ * @throws {error} Exceptions other than ttransport.InputBufferUnderrunError are rethrown
+ * @event {error} The "error" event is fired when a Node.js error event occurs during
+ * request or response processing, in which case the node error is passed on. An "error"
+ * event may also be fired when the connectison can not map a response back to the
+ * appropriate client (an internal error), generating a TApplicationException.
+ * @classdesc HttpConnection objects provide Thrift end point transport
+ * semantics implemented over the Node.js http.request() method.
+ * @see {@link createHttpConnection}
+ */
var HttpConnection = exports.HttpConnection = function(host, port, options) {
+ //Initialize the emitter base object
+ EventEmitter.call(this);
+
//Set configuration
var self = this;
this.options = options || {};
this.host = host;
this.port = port;
+ this.https = this.options.https || false;
this.transport = this.options.transport || ttransport.TBufferedTransport;
this.protocol = this.options.protocol || tprotocol.TBinaryProtocol;
port: this.port || 80,
path: this.options.path || '/',
method: 'POST',
- headers: this.options.headers || {},
- tls: options.tls || {},
+ headers: this.options.headers || {}
};
+ for (var attrname in this.options.nodeOptions) {
+ this.nodeOptions[attrname] = this.options.nodeOptions[attrname];
+ }
+ /*jshint -W069 */
+ if (! this.nodeOptions.headers["Connection"]) {
+ this.nodeOptions.headers["Connection"] = "keep-alive";
+ }
+ /*jshint +W069 */
//The sequence map is used to map seqIDs back to the
// calling client in multiplexed scenarios
client['recv_' + header.fname](proto, header.mtype, dummy_seqid);
} else {
delete client._reqs[dummy_seqid];
- throw new thrift.TApplicationException(thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
- "Received a response to an unknown RPC function");
+ self.emit("error",
+ new thrift.TApplicationException(
+ thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
+ "Received a response to an unknown RPC function"));
}
}
}
throw e;
}
}
- };
+ }
//Response handler
var data = [];
var dataLen = 0;
- response.on('error', function (err) {
- console.log("Error in response: " + err);
+ response.on('error', function (e) {
+ self.emit("error", e);
});
response.on('data', function (chunk) {
});
};
};
+util.inherits(HttpConnection, EventEmitter);
+/**
+ * Writes Thrift message data to the connection
+ * @param {Buffer} data - A Node.js Buffer containing the data to write
+ * @returns {void} No return value.
+ * @event {error} the "error" event is raised upon request failure passing the
+ * Node.js error object to the listener.
+ */
HttpConnection.prototype.write = function(data) {
- var req = http.request(this.nodeOptions, this.responseCallback);
-
- req.on('error', function(e) {
- throw new thrift.TApplicationException(thrift.TApplicationExceptionType.UNKNOWN,
- "Request failed");
- });
-
+ var self = this;
+ self.nodeOptions.headers["Content-length"] = data.length;
+ var req = (self.https) ?
+ https.request(self.nodeOptions, self.responseCallback) :
+ http.request(self.nodeOptions, self.responseCallback);
+ req.on('error', function(err) {
+ self.emit("error", err);
+ });
req.write(data);
req.end();
};
+/**
+ * Creates a new HttpConnection object, used by Thrift clients to connect
+ * to Thrift HTTP based servers.
+ * @param {string} host - The host name or IP to connect to.
+ * @param {number} port - The TCP port to connect to.
+ * @param {ConnectOptions} options - The configuration options to use.
+ * @returns {HttpConnection} The connection object.
+ * @see {@link ConnectOptions}
+ */
exports.createHttpConnection = function(host, port, options) {
return new HttpConnection(host, port, options);
};
+/**
+ * Creates a new client object for the specified Thrift service.
+ * @param {object} cls - The module containing the service client
+ * @param {HttpConnection} httpConnection - The connection to use.
+ * @returns {object} The client object.
+ * @see {@link createHttpConnection}
+ */
exports.createHttpClient = function(cls, httpConnection) {
if (cls.Client) {
cls = cls.Client;
}
- return httpConnection.client =
+ httpConnection.client =
new cls(new httpConnection.transport(undefined, function(buf) {httpConnection.write(buf);}),
httpConnection.protocol);
+ return httpConnection.client;
};
stream.on('end', function() {
stream.end();
});
- };
+ }
if (options.tls) {
return tls.createServer(options.tls, serverImpl);
//Setup all of the services
var services = options.services;
- for (uri in services) {
+ for (var uri in services) {
var svcObj = services[uri];
//Setup the processor
///////////////////////////////////////////////////
function processGet(request, response) {
//Undefined or empty base directory means do not serve static files
- if (!baseDir || "" == baseDir) {
+ if (!baseDir || "" === baseDir) {
response.writeHead(404);
response.end();
return;
if (contentType) {
headers["Content-Type"] = contentType;
}
- for (k in options.headers) {
+ for (var k in options.headers) {
headers[k] = options.headers[k];
}
response.writeHead(200, headers);
}
}
//Perform upgrade
- var hash = crypto.createHash("sha1")
+ 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" +
}
//Prepare next frame for decoding (if any)
frame = result.nextFrame;
- };
+ }
} catch(e) {
console.log("TWebSocketTransport Exception: " + e);
socket.destroy();
protocol: protocol
};
-var connection = undefined;
+var connection;
if (program.ssl) {
options.rejectUnauthorized = false;
path: "/test"
};
-var connection = undefined;
-
if (program.ssl) {
- options.rejectUnauthorized = false;
- connection = thrift.createHttpConnection("localhost", 9090, options);
-} else {
- connection = thrift.createHttpConnection("localhost", 9090, options);
-}
+ options.nodeOptions = { rejectUnauthorized: false };
+ options.https = true;
+}
+
+var connection = thrift.createHttpConnection("localhost", 9090, options);
var client = thrift.createHttpClient(ThriftTest, connection);
-//connection.on('error', function(err) {
-// assert(false, err);
-//});
+connection.on('error', function(err) {
+ assert(false, err);
+});
var testDriver = ThriftTestDriver;
if (program.promise) {
+ console.log(" --Testing promise style client");
testDriver = ThriftTestDriverPromise;
}
testDriver(client, function (status) {
protocol: protocol,
transport: transport
};
-var serverOpt = { services: { "/test": SvcOpt } }
+var serverOpt = { services: { "/test": SvcOpt } };
+if (program.ssl) {
+ serverOpt.tls = {
+ key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
+ cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
+ };
+}
thrift.createWebServer(serverOpt).listen(9090);
protocol: protocol
};
-var connection = undefined;
+var connection;
if (program.ssl) {
- options.rejectUnauthorized = false
+ options.rejectUnauthorized = false;
connection = thrift.createSSLConnection('localhost', 9090, options);
} else {
connection = thrift.createConnection('localhost', 9090, options);
options.tls = {
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
- }
+ };
}
thrift.createMultiplexServer(processor, options).listen(9090);
* under the License.
*/
-//Server test for the following I/O stack:
-// TBinaryProtocol
-// TFramedTransport
-// TSocket
-
var fs = require('fs');
var path = require('path');
var thrift = require('thrift');
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);
testHttpClientServer json framed || TESTOK=1
testHttpClientServer binary buffered || TESTOK=1
testHttpClientServer binary framed || TESTOK=1
+testHttpClientServer json buffered --promise || TESTOK=1
+testHttpClientServer binary framed --ssl || TESTOK=1
exit $TESTOK
});\r
\r
//all Languages in UTF-8\r
+/*jshint -W100 */\r
var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +\r
"Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +\r
"Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " +\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
+/*jshint +W100 */\r
\r
client.testString(stringTest, function(err, response) {\r
assert( ! err);\r
});\r
\r
//all Languages in UTF-8\r
+/*jshint -W100 */\r
var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +\r
"Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +\r
"Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " +\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
+/*jshint +W100 */\r
\r
client.testString(stringTest)\r
.then(function(response) {\r
client.testException('Xception')\r
.then(function(response) {\r
assert.equal(err.errorCode, 1001);\r
- assert.equal('Xception', err.message)\r
+ assert.equal('Xception', err.message);\r
})\r
.fail(function() {\r
assert(false);\r
client.testI32(-1)\r
.then(function(response) {\r
assert.equal(-1, response);\r
- test_complete = true\r
+ test_complete = true;\r
})\r
.fail(function() {\r
assert(false);\r
\r
setTimeout(TestForCompletion, retry_interval);\r
})();\r
-}\r
+};\r