You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@thrift.apache.org by ra...@apache.org on 2014/04/23 11:26:58 UTC

git commit: THRIFT-2493:Node.js lib needs HTTP client Client: node Patch: Randy Abernethy

Repository: thrift
Updated Branches:
  refs/heads/master 751c97c2d -> 8f697cb64


THRIFT-2493:Node.js lib needs HTTP client
Client: node
Patch: Randy Abernethy

Adds http client to node.js along with tests.


Project: http://git-wip-us.apache.org/repos/asf/thrift/repo
Commit: http://git-wip-us.apache.org/repos/asf/thrift/commit/8f697cb6
Tree: http://git-wip-us.apache.org/repos/asf/thrift/tree/8f697cb6
Diff: http://git-wip-us.apache.org/repos/asf/thrift/diff/8f697cb6

Branch: refs/heads/master
Commit: 8f697cb6499997584c82cc03daeda667847e5bfe
Parents: 751c97c
Author: ra <ra...@apache.org>
Authored: Wed Apr 23 02:23:18 2014 -0700
Committer: ra <ra...@apache.org>
Committed: Wed Apr 23 02:23:18 2014 -0700

----------------------------------------------------------------------
 lib/nodejs/lib/thrift/http_connection.js | 153 ++++++++++++++++++++++++++
 lib/nodejs/lib/thrift/index.js           |   5 +
 lib/nodejs/test/http_client.js           |  83 ++++++++++++++
 lib/nodejs/test/http_server.js           |  60 ++++++++++
 lib/nodejs/test/testAll.sh               |  19 ++++
 lib/nodejs/test/thrift_test_driver.js    |  12 +-
 6 files changed, 326 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/thrift/blob/8f697cb6/lib/nodejs/lib/thrift/http_connection.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/lib/thrift/http_connection.js b/lib/nodejs/lib/thrift/http_connection.js
new file mode 100644
index 0000000..7eab320
--- /dev/null
+++ b/lib/nodejs/lib/thrift/http_connection.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 thrift = require('./thrift');
+var ttransport = require('./transport');
+var tprotocol = require('./protocol');
+
+var http = require('http');
+
+var HttpConnection = exports.HttpConnection = function(host, port, options) {
+  //Set configuration
+  var self = this;
+  this.options = options || {};
+  this.host = host;
+  this.port = port;
+  this.transport = this.options.transport || ttransport.TBufferedTransport;
+  this.protocol = this.options.protocol || tprotocol.TBinaryProtocol;
+
+  //Prepare Node.js options
+  this.nodeOptions = {
+    host: this.host,
+    port: this.port || 80,
+    path: this.options.path || '/',
+    method: 'POST',
+    headers: this.options.headers || {},
+    tls: options.tls || {},
+  };
+
+  //The sequence map is used to map seqIDs back to the 
+  //  calling client in multiplexed scenarios
+  this.seqId2Service = {};
+
+  function decodeCallback(transport_with_data) {
+    var proto = new self.protocol(transport_with_data);
+    try {
+      while (true) {
+        var header = proto.readMessageBegin();
+        var dummy_seqid = header.rseqid * -1;
+        var client = self.client;
+        //The Multiplexed Protocol stores a hash of seqid to service names
+        //  in seqId2Service. If the SeqId is found in the hash we need to
+        //  lookup the appropriate client for this call.
+        //  The client var is a single client object when not multiplexing, 
+        //  when using multiplexing it is a service name keyed hash of client
+        //  objects.
+        //NOTE: The 2 way interdependencies between protocols, transports,
+        //  connections and clients in the Node.js implementation are irregular
+        //  and make the implementation difficult to extend and maintain. We 
+        //  should bring this stuff inline with typical thrift I/O stack 
+        //  operation soon.
+        //  --ra
+        var service_name = self.seqId2Service[header.rseqid];
+        if (service_name) {
+          client = self.client[service_name];
+          delete self.seqId2Service[header.rseqid];
+        }
+        /*jshint -W083 */
+        client._reqs[dummy_seqid] = function(err, success){ 
+          transport_with_data.commitPosition();
+          var clientCallback = client._reqs[header.rseqid];
+          delete client._reqs[header.rseqid];
+          if (clientCallback) {
+            clientCallback(err, success);
+          }
+        };
+        /*jshint +W083 */
+        if(client['recv_' + header.fname]) {
+          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");
+        }
+      }
+    }
+    catch (e) {
+      if (e instanceof ttransport.InputBufferUnderrunError) {
+        transport_with_data.rollbackPosition();
+      } else {
+        throw e;
+      }
+    }
+  };
+          
+      
+  //Response handler
+  //////////////////////////////////////////////////
+  this.responseCallback = function(response) {
+    var data = [];
+    var dataLen = 0;
+
+    response.on('error', function (err) {
+      console.log("Error in response: " + err); 
+    });
+
+    response.on('data', function (chunk) {
+      data.push(chunk); 
+      dataLen += chunk.length; 
+    });
+
+    response.on('end', function(){
+      var buf = new Buffer(dataLen); 
+      for (var i=0, len=data.length, pos=0; i<len; i++) { 
+        data[i].copy(buf, pos); 
+        pos += data[i].length; 
+      }
+      //Get thre receiver function for the transport and 
+      //  call it with the buffer
+      self.transport.receiver(decodeCallback)(buf);
+    });
+  };
+};
+
+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");
+  });
+
+  req.write(data);
+  req.end();
+};
+
+exports.createHttpConnection = function(host, port, options) {
+  return new HttpConnection(host, port, options);
+};
+
+exports.createHttpClient = function(cls, httpConnection) {
+  if (cls.Client) {
+    cls = cls.Client;
+  }
+  return httpConnection.client = 
+    new cls(new httpConnection.transport(undefined, function(buf) {httpConnection.write(buf);}), 
+            httpConnection.protocol);
+};
+

http://git-wip-us.apache.org/repos/asf/thrift/blob/8f697cb6/lib/nodejs/lib/thrift/index.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/lib/thrift/index.js b/lib/nodejs/lib/thrift/index.js
index dd965d2..3a865f8 100644
--- a/lib/nodejs/lib/thrift/index.js
+++ b/lib/nodejs/lib/thrift/index.js
@@ -26,6 +26,11 @@ exports.createSSLConnection = connection.createSSLConnection;
 exports.createStdIOClient = connection.createStdIOClient;
 exports.createStdIOConnection = connection.createStdIOConnection;
 
+var httpConnection = require('./http_connection');
+exports.HttpConnection = httpConnection.HttpConnection;
+exports.createHttpConnection = httpConnection.createHttpConnection;
+exports.createHttpClient = httpConnection.createHttpClient;
+
 var server = require('./server');
 exports.createServer = server.createServer;
 exports.createMultiplexServer = server.createMultiplexServer;

http://git-wip-us.apache.org/repos/asf/thrift/blob/8f697cb6/lib/nodejs/test/http_client.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/test/http_client.js b/lib/nodejs/test/http_client.js
new file mode 100644
index 0000000..08a7d27
--- /dev/null
+++ b/lib/nodejs/test/http_client.js
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+//This is the client side test for the standard Apache Thrift
+//"ThriftTest" suite. This client will test any protocol/transport
+//combination specified on the command line.
+
+var fs = require('fs');
+var assert = require('assert');
+var thrift = require('thrift');
+var ThriftTest = require('./gen-nodejs/ThriftTest');
+var ThriftTestDriver = require('./thrift_test_driver').ThriftTestDriver;
+var ThriftTestDriverPromise = require('./thrift_test_driver_promise').ThriftTestDriver;
+
+var program = require('commander');
+
+program
+  .option('-p, --protocol <protocol>', 'Set thrift protocol (binary|json) [protocol]')
+  .option('-t, --transport <transport>', 'Set thrift transport (buffered|framed) [transport]')
+  .option('--ssl', 'use SSL transport')
+  .option('--promise', 'test with promise style functions')
+  .parse(process.argv);
+
+
+var protocol = thrift.TBinaryProtocol;
+if (program.protocol === "json") {
+  protocol = thrift.TJSONProtocol;
+} 
+
+var transport =  thrift.TBufferedTransport;
+if (program.transport === "framed") {
+  transport = thrift.TFramedTransport;
+}
+
+var options = {
+   transport: transport,
+   protocol: protocol,
+   headers: {"Connection": "close"},
+   path: "/test"
+};
+
+var connection = undefined;
+
+if (program.ssl) {
+  options.rejectUnauthorized = false;
+  connection = thrift.createHttpConnection("localhost", 9090, options);
+} else {
+  connection = thrift.createHttpConnection("localhost", 9090, options);
+}
+
+var client = thrift.createHttpClient(ThriftTest, connection);
+
+//connection.on('error', function(err) {
+//  assert(false, err);
+//});
+
+var testDriver = ThriftTestDriver;
+if (program.promise) {
+  testDriver = ThriftTestDriverPromise;
+} 
+testDriver(client, function (status) {
+  console.log(status);
+  process.exit(0);
+});
+
+// to make it also run on expresso
+exports.expressoTest = function() {};

http://git-wip-us.apache.org/repos/asf/thrift/blob/8f697cb6/lib/nodejs/test/http_server.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/test/http_server.js b/lib/nodejs/test/http_server.js
new file mode 100644
index 0000000..d8ef73f
--- /dev/null
+++ b/lib/nodejs/test/http_server.js
@@ -0,0 +1,60 @@
+/*
+ * 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 fs = require('fs');
+var path = require('path');
+var thrift = require('thrift');
+var ThriftTest = require('./gen-nodejs/ThriftTest');
+var ThriftTestHandler = require('./test_handler').ThriftTestHandler;
+var ThriftTestHandlerPromise = require('./test_handler_promise').ThriftTestHandler;
+
+var program = require('commander');
+
+program
+  .option('-p, --protocol <protocol>', 'Set thift protocol (binary|json) [protocol]')
+  .option('-t, --transport <transport>', 'Set thift transport (buffered|framed) [transport]')
+  .option('--ssl', 'use ssl transport')
+  .option('--promise', 'test with promise style functions')
+  .parse(process.argv);
+
+var transport =  thrift.TBufferedTransport;
+if (program.transport === "framed") {
+  transport = thrift.TFramedTransport;
+} 
+
+var protocol = thrift.TBinaryProtocol;
+if (program.protocol === "json") {
+  protocol = thrift.TJSONProtocol;
+} 
+
+var handler = ThriftTestHandler;
+if (program.promise) {
+  handler = ThriftTestHandlerPromise;
+} 
+
+var SvcOpt = {                       		
+    handler: handler,                      	
+    processor: ThriftTest,                         	
+    protocol: protocol,                 
+    transport: transport 		
+};                                  
+var serverOpt = { services: { "/test": SvcOpt } }                                   
+thrift.createWebServer(serverOpt).listen(9090);                        		
+
+

http://git-wip-us.apache.org/repos/asf/thrift/blob/8f697cb6/lib/nodejs/test/testAll.sh
----------------------------------------------------------------------
diff --git a/lib/nodejs/test/testAll.sh b/lib/nodejs/test/testAll.sh
index 96f8a2a..87bbb9d 100755
--- a/lib/nodejs/test/testAll.sh
+++ b/lib/nodejs/test/testAll.sh
@@ -47,6 +47,18 @@ testMultiplexedClientServer()
   return $RET
 }
 
+testHttpClientServer()
+{
+  echo "   Testing HTTP Client/Server with protocol $1 and transport $2 $3";
+  RET=0
+  node ${DIR}/http_server.js -p $1 -t $2 $3 &
+  SERVERPID=$!
+  sleep 1
+  node ${DIR}/http_client.js -p $1 -t $2 $3 || RET=1
+  kill -9 $SERVERPID || RET=1
+  return $RET
+}
+
 
 TESTOK=0
 
@@ -60,6 +72,7 @@ node ${DIR}/binary.test.js || TESTOK=1
 
 #integration tests
 
+#TCP connection tests
 testClientServer binary buffered || TESTOK=1
 testClientServer json buffered || TESTOK=1
 testClientServer binary framed || TESTOK=1
@@ -78,4 +91,10 @@ testMultiplexedClientServer binary framed --ssl || TESTOK=1
 #test promise style
 testClientServer binary framed --promise || TESTOK=1
 
+#HTTP tests
+testHttpClientServer json buffered || TESTOK=1
+testHttpClientServer json framed || TESTOK=1
+testHttpClientServer binary buffered || TESTOK=1
+testHttpClientServer binary framed || TESTOK=1
+
 exit $TESTOK

http://git-wip-us.apache.org/repos/asf/thrift/blob/8f697cb6/lib/nodejs/test/thrift_test_driver.js
----------------------------------------------------------------------
diff --git a/lib/nodejs/test/thrift_test_driver.js b/lib/nodejs/test/thrift_test_driver.js
index ccc105b..a21c9c5 100644
--- a/lib/nodejs/test/thrift_test_driver.js
+++ b/lib/nodejs/test/thrift_test_driver.js
@@ -288,7 +288,7 @@ client.testOneway(0, function(err, response) {
     test_complete = true;
   });
 
-//We wait up to retry_limit * retry_interval for the test suite to complete
+  //We wait up to retry_limit * retry_interval for the test suite to complete
   function TestForCompletion() {
     if(test_complete) {
       if (callback) {
@@ -298,14 +298,14 @@ client.testOneway(0, function(err, response) {
       if (++retrys < retry_limit) {
         setTimeout(TestForCompletion, retry_interval);
       } else {
-    if (callback) {
-      callback("Server test failed to complete after " +
-        (retry_limit*retry_interval/1000) + " seconds");
-    }
+        if (callback) {
+          callback("Server test failed to complete after " +
+                   (retry_limit*retry_interval/1000) + " seconds");
+        }
       }
     }
   }
 
   setTimeout(TestForCompletion, retry_interval);
 })();
-}
+};