From 7cf085e6c5bd1ca574cff0a1d209b13395d0d449 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Henrique=20Mendon=C3=A7a?= Date: Fri, 20 Sep 2013 16:47:02 +0200 Subject: [PATCH] THRIFT-2164 Add a Get/Post Http Server to Node along with examples Client: nodejs Patch: Randy Abernethy small change to run on 0.6.x --- lib/nodejs/examples/README.md | 3 + lib/nodejs/examples/hello.html | 65 +++++++++++ lib/nodejs/examples/hello.js | 65 +++++++++++ lib/nodejs/examples/hello.thrift | 27 +++++ lib/nodejs/lib/thrift/index.js | 3 + lib/nodejs/lib/thrift/server.js | 8 +- lib/nodejs/lib/thrift/static_server.js | 153 +++++++++++++++++++++++++ 7 files changed, 319 insertions(+), 5 deletions(-) create mode 100644 lib/nodejs/examples/hello.html create mode 100644 lib/nodejs/examples/hello.js create mode 100644 lib/nodejs/examples/hello.thrift create mode 100644 lib/nodejs/lib/thrift/static_server.js diff --git a/lib/nodejs/examples/README.md b/lib/nodejs/examples/README.md index a87581fb..0773e606 100644 --- a/lib/nodejs/examples/README.md +++ b/lib/nodejs/examples/README.md @@ -25,5 +25,8 @@ NODE_PATH=../lib:../lib/thrift node server.js #Now run the client: 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 + diff --git a/lib/nodejs/examples/hello.html b/lib/nodejs/examples/hello.html new file mode 100644 index 00000000..0897ea9a --- /dev/null +++ b/lib/nodejs/examples/hello.html @@ -0,0 +1,65 @@ + + + + + Apache Thrift JavaScript Browser Client Demo + + + + + +

Apache Thrift JavaScript Browser Client Demo

+

This html file demonstrates Apache Thrift JavaScrpt RPC between a browser client to a node.js server. Clicking the button below will call the hello_func() hosted by the Apache Thrift server at localhost:8585. The file hello.js contains the JavaScript node.js server required. Here are the steps to get the example running:

+
    +
  1. Install Node.js
    nodejs.org
  2. +
  3. Install Apache Thrift for node (note that the node package manager will create the node_modules folder in the current directory so make sure to run npm from the same directory as hello.js so that the server can find the Thrift libraries. This example requires Apache Thrift 0.9.2+)
    $ npm install thrift
  4. +
  5. Compile the hello.idl for JavaScript and Node.js (you'll need to have the Apache Thrift compiler installed for this step. This also needs to be executed in the same directory as hello.js because hello.js and hello.html look for the gen-nodejs and gen-js directories here.)
    $ thrift -gen js -gen js:node hello.thrift
  6. +
  7. Run the node server in the directory with the hello.html file
    $ node hello.js
  8. +
  9. Copy the Apache Thrift JavaScript library, thrift.js, into the directory with this html file.
    $ cp ...../thrift.js .
    +
  10. Reload this page in a browser through the node server using using the URL:
    http://localhost:8585/hello.html
    then click a button below to make an RPC call
  11. +
+ + + +

Server Response:

+

Server Dbl:

+ + + diff --git a/lib/nodejs/examples/hello.js b/lib/nodejs/examples/hello.js new file mode 100644 index 00000000..90634c95 --- /dev/null +++ b/lib/nodejs/examples/hello.js @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var thrift = require('thrift'); +var TBufferedTransport = require('thrift/transport').TBufferedTransport; +var TJSONProtocol = require('thrift/protocol').TJSONProtocol; +var HelloSvc = require('./gen-nodejs/HelloSvc.js'); +var TimesTwoSvc = require('./gen-nodejs/TimesTwo.js'); + +var helloHandler = { + hello_func: function(result) { + this.call_counter = this.call_counter || 0; + console.log("Client call: " + (++this.call_counter)); + result(null, "Hello Apache Thrift for JavaScript " + this.call_counter); + } +} + +var timesTwoHandler = { + dbl: function(val, result) { + console.log("Client call: " + val); + result(null, val * 2); + } +} + +var helloService = { + transport: TBufferedTransport, + protocol: TJSONProtocol, + cls: HelloSvc, + handler: helloHandler +}; + +var dblService = { + transport: TBufferedTransport, + protocol: TJSONProtocol, + cls: TimesTwoSvc, + handler: timesTwoHandler +}; + +var StaticHttpThriftServerOptions = { + staticFilePath: ".", + services: { + "/hello": helloService, + "/dbl": dblService, + } +} + +var server = thrift.createStaticHttpThriftServer(StaticHttpThriftServerOptions); +var port = 8585; +server.listen(port); +console.log("Http/Thrift Server running on port: " + port); diff --git a/lib/nodejs/examples/hello.thrift b/lib/nodejs/examples/hello.thrift new file mode 100644 index 00000000..deaf5a5f --- /dev/null +++ b/lib/nodejs/examples/hello.thrift @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +service HelloSvc { + string hello_func(), +} + +service TimesTwo { + i64 dbl(1: i64 val), +} + + diff --git a/lib/nodejs/lib/thrift/index.js b/lib/nodejs/lib/thrift/index.js index 92014fd3..4992bedb 100644 --- a/lib/nodejs/lib/thrift/index.js +++ b/lib/nodejs/lib/thrift/index.js @@ -31,6 +31,9 @@ exports.createHttpServer = server.createHttpServer; exports.httpMiddleware = server.httpMiddleware; exports.createMultiplexServer = server.createMultiplexServer; +var static_server = require('./static_server') +exports.createStaticHttpThriftServer = static_server.createStaticHttpThriftServer; + exports.Int64 = require('node-int64') var mprocessor = require('./multiplexed_processor'); diff --git a/lib/nodejs/lib/thrift/server.js b/lib/nodejs/lib/thrift/server.js index e9a76233..fee07d49 100644 --- a/lib/nodejs/lib/thrift/server.js +++ b/lib/nodejs/lib/thrift/server.js @@ -19,8 +19,8 @@ var net = require('net'); var http = require('http'); -var ttransport = require('./transport') - , TBinaryProtocol = require('./protocol').TBinaryProtocol; +var ttransport = require('./transport'), + TBinaryProtocol = require('./protocol').TBinaryProtocol; exports.createMultiplexServer = function(processor, options) { @@ -71,8 +71,6 @@ function httpRequestHandler(cls, handler, options) { var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol; return function(request, response) { - var self = this; - request.on('data', transport.receiver(function(transportWithData) { var input = new protocol(transportWithData); var output = new protocol(new transport(undefined, function(buf) { @@ -99,7 +97,7 @@ function httpRequestHandler(cls, handler, options) { } })); }; -}; +} exports.httpMiddleware = httpRequestHandler; diff --git a/lib/nodejs/lib/thrift/static_server.js b/lib/nodejs/lib/thrift/static_server.js new file mode 100644 index 00000000..16273283 --- /dev/null +++ b/lib/nodejs/lib/thrift/static_server.js @@ -0,0 +1,153 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +var http = require('http'); +var url = require("url"); +var path = require("path"); +var fs = require("fs"); + +var ttransport = require('./transport'); +var TBinaryProtocol = require('./protocol').TBinaryProtocol; + +/** + * @class + * @name StaticHttpThriftServerOptions + * @property {string} staticFilePath - Path to serve static files from, default + * is ".", use "" to disable static file service. + * @property {object} services - An object hash mapping service URIs to + * ThriftServiceOptions objects. + * @see {@link ThriftServiceOptions} + */ + +/** + * @class + * @name ThriftServiceOptions + * @property {object} transport - The layered transport to use (defaults to none). + * @property {object} protocol - The Thrift Protocol to use (defaults to TBinaryProtocol). + * @property {object} cls - The Thrift Service class generated by the IDL Compiler for the service. + * @property {object} handler - The handler methods for the Thrift Service. + */ + +/** + * Creates a Thrift server which can serve static files and/or one or + * more Thrift Services. + * @param {StaticHttpThriftServerOptions} - The server configuration. + * @returns {object} - The Thrift server. + */ +exports.createStaticHttpThriftServer = function(options) { + //Set the static dir to serve files from + var baseDir = typeof options.staticFilePath != "string" ? process.cwd() : options.staticFilePath; + + //Setup all of the services + var services = options.services; + for (svc in services) { + var svcObj = services[svc]; + var processor = svcObj.cls.Processor || svcObj.cls; + svcObj.processor = new processor(svcObj.handler); + svcObj.transport = svcObj.transport ? svcObj.transport : ttransport.TBufferedTransport; + svcObj.protocol = svcObj.protocol ? svcObj.protocol : TBinaryProtocol; + } + + //Handle POST methods + function processPost(request, response) { + var uri = url.parse(request.url).pathname; + var svc = services[uri]; + if (!svc) { + //Todo: add support for non Thrift posts + response.writeHead(500); + response.end(); + return; + } + + request.on('data', svc.transport.receiver(function(transportWithData) { + var input = new svc.protocol(transportWithData); + var output = new svc.protocol(new svc.transport(undefined, function(buf) { + try { + response.writeHead(200); + response.end(buf); + } catch (err) { + response.writeHead(500); + response.end(); + } + })); + + try { + svc.processor.process(input, output); + transportWithData.commitPosition(); + } + catch (err) { + if (err instanceof ttransport.InputBufferUnderrunError) { + transportWithData.rollbackPosition(); + } + else { + response.writeHead(500); + response.end(); + } + } + })); + } + + //Handle GET methods + function processGet(request, response) { + //An empty string base directory means do not serve static files + if ("" == baseDir) { + response.writeHead(404); + response.end(); + return; + } + //Locate the file requested + var uri = url.parse(request.url).pathname; + var filename = path.join(baseDir, uri); + fs.exists(filename, function(exists) { + if(!exists) { + response.writeHead(404); + response.end(); + return; + } + + if (fs.statSync(filename).isDirectory()) { + filename += '/index.html'; + } + + fs.readFile(filename, "binary", function(err, file) { + if(err) { + response.writeHead(500); + response.end(err + "\n"); + return; + } + + response.writeHead(200); + response.write(file, "binary"); + response.end(); + }); + }); + } + + //Return the server + return http.createServer(function(request, response) { + if (request.method === 'POST') { + processPost(request, response); + } else if (request.method === 'GET') { + processGet(request, response); + } else { + response.writeHead(500); + response.end(); + } + }); +}; + -- 2.17.1