mirror of
https://github.com/valitydev/thrift.git
synced 2024-11-07 02:45:22 +00:00
THRIFT-2493:Node.js lib needs HTTP client
Client: node Patch: Randy Abernethy Clean up of many jshint warnings/errors, jsdoc for HttpConnect, added support for https and Python to HttpConnect, added tests for HttpConnect with https and promises.
This commit is contained in:
parent
8f697cb649
commit
779b9ac2ef
@ -18,6 +18,7 @@
|
||||
|
||||
#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
|
||||
@ -28,5 +29,6 @@ NODE_PATH=../lib:../lib/thrift node client.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
|
||||
|
||||
|
23
lib/nodejs/examples/httpClient.js
Normal file
23
lib/nodejs/examples/httpClient.js
Normal file
@ -0,0 +1,23 @@
|
||||
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);
|
||||
});
|
||||
|
||||
|
31
lib/nodejs/examples/httpServer.js
Normal file
31
lib/nodejs/examples/httpServer.js
Normal file
@ -0,0 +1,31 @@
|
||||
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);
|
||||
|
19
lib/nodejs/examples/httpServer.py
Normal file
19
lib/nodejs/examples/httpServer.py
Normal file
@ -0,0 +1,19 @@
|
||||
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()
|
||||
|
@ -152,8 +152,9 @@ var Connection = exports.Connection = function(stream, options) {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,18 +16,67 @@
|
||||
* 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;
|
||||
|
||||
@ -37,9 +86,16 @@ var HttpConnection = exports.HttpConnection = function(host, port, options) {
|
||||
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
|
||||
@ -83,8 +139,10 @@ var HttpConnection = exports.HttpConnection = function(host, port, options) {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,7 +153,7 @@ var HttpConnection = exports.HttpConnection = function(host, port, options) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//Response handler
|
||||
@ -104,8 +162,8 @@ var HttpConnection = exports.HttpConnection = function(host, port, options) {
|
||||
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) {
|
||||
@ -125,29 +183,55 @@ var HttpConnection = exports.HttpConnection = function(host, port, options) {
|
||||
});
|
||||
};
|
||||
};
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -84,7 +84,7 @@ exports.createMultiplexServer = function(processor, options) {
|
||||
stream.on('end', function() {
|
||||
stream.end();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
if (options.tls) {
|
||||
return tls.createServer(options.tls, serverImpl);
|
||||
|
@ -289,7 +289,7 @@ exports.createWebServer = function(options) {
|
||||
|
||||
//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
|
||||
@ -399,7 +399,7 @@ exports.createWebServer = function(options) {
|
||||
///////////////////////////////////////////////////
|
||||
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;
|
||||
@ -437,7 +437,7 @@ exports.createWebServer = function(options) {
|
||||
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);
|
||||
@ -512,7 +512,7 @@ exports.createWebServer = function(options) {
|
||||
}
|
||||
}
|
||||
//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" +
|
||||
@ -545,7 +545,7 @@ exports.createWebServer = function(options) {
|
||||
}
|
||||
//Prepare next frame for decoding (if any)
|
||||
frame = result.nextFrame;
|
||||
};
|
||||
}
|
||||
} catch(e) {
|
||||
console.log("TWebSocketTransport Exception: " + e);
|
||||
socket.destroy();
|
||||
|
@ -53,7 +53,7 @@ var options = {
|
||||
protocol: protocol
|
||||
};
|
||||
|
||||
var connection = undefined;
|
||||
var connection;
|
||||
|
||||
if (program.ssl) {
|
||||
options.rejectUnauthorized = false;
|
||||
|
@ -55,23 +55,22 @@ var options = {
|
||||
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) {
|
||||
|
@ -54,7 +54,13 @@ var SvcOpt = {
|
||||
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);
|
||||
|
||||
|
||||
|
@ -47,9 +47,9 @@ var options = {
|
||||
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);
|
||||
|
@ -72,7 +72,7 @@ if (program.ssl) {
|
||||
options.tls = {
|
||||
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
|
||||
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
thrift.createMultiplexServer(processor, options).listen(9090);
|
||||
|
@ -17,11 +17,6 @@
|
||||
* 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');
|
||||
@ -61,7 +56,7 @@ if (program.ssl) {
|
||||
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);
|
||||
|
||||
|
@ -96,5 +96,7 @@ testHttpClientServer json buffered || TESTOK=1
|
||||
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
|
||||
|
@ -67,6 +67,7 @@ client.testString("", function(err, response) {
|
||||
});
|
||||
|
||||
//all Languages in UTF-8
|
||||
/*jshint -W100 */
|
||||
var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +
|
||||
"Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +
|
||||
"Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " +
|
||||
@ -92,6 +93,7 @@ var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, "
|
||||
"Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, " +
|
||||
"Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " +
|
||||
"Bân-lâm-gú, 粵語";
|
||||
/*jshint +W100 */
|
||||
|
||||
client.testString(stringTest, function(err, response) {
|
||||
assert( ! err);
|
||||
|
@ -70,6 +70,7 @@ client.testString("")
|
||||
});
|
||||
|
||||
//all Languages in UTF-8
|
||||
/*jshint -W100 */
|
||||
var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, " +
|
||||
"Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška, " +
|
||||
"Беларуская, Беларуская (тарашкевіца), Български, Bamanankan, " +
|
||||
@ -95,6 +96,7 @@ var stringTest = "Afrikaans, Alemannisch, Aragonés, العربية, مصرى, "
|
||||
"Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük, " +
|
||||
"Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文, " +
|
||||
"Bân-lâm-gú, 粵語";
|
||||
/*jshint +W100 */
|
||||
|
||||
client.testString(stringTest)
|
||||
.then(function(response) {
|
||||
@ -330,7 +332,7 @@ client.testException('TException')
|
||||
client.testException('Xception')
|
||||
.then(function(response) {
|
||||
assert.equal(err.errorCode, 1001);
|
||||
assert.equal('Xception', err.message)
|
||||
assert.equal('Xception', err.message);
|
||||
})
|
||||
.fail(function() {
|
||||
assert(false);
|
||||
@ -365,7 +367,7 @@ client.testOneway(0, function(error, response) {
|
||||
client.testI32(-1)
|
||||
.then(function(response) {
|
||||
assert.equal(-1, response);
|
||||
test_complete = true
|
||||
test_complete = true;
|
||||
})
|
||||
.fail(function() {
|
||||
assert(false);
|
||||
@ -391,4 +393,4 @@ client.testOneway(0, function(error, response) {
|
||||
|
||||
setTimeout(TestForCompletion, retry_interval);
|
||||
})();
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user