Moving from govendor to dep, updated dependencies (#48)

* Moving from govendor to dep.

* Making the pull request template more friendly.

* Fixing akward space in PR template.

* goimports run on whole project using ` goimports -w $(find . -type f -name '*.go' -not -path "./vendor/*" -not -path "./gen-go/*")`

source of command: https://gist.github.com/bgentry/fd1ffef7dbde01857f66
This commit is contained in:
Renan DelValle 2018-01-07 13:13:47 -08:00 committed by GitHub
parent 9631aa3aab
commit 8d445c1c77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2186 changed files with 400410 additions and 352 deletions

43
vendor/git.apache.org/thrift.git/lib/nodejs/Makefile.am generated vendored Executable file
View file

@ -0,0 +1,43 @@
# 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.
THRIFT = $(top_builddir)/compiler/cpp/thrift
stubs: $(top_srcdir)/test/ThriftTest.thrift
$(THRIFT) --gen js:node -o test/ $(top_srcdir)/test/ThriftTest.thrift
deps: $(top_srcdir)/package.json
$(NPM) install --no-bin-links $(top_srcdir)/
all-local: deps
precross: deps stubs
check: deps
cd $(top_srcdir) && $(NPM) test && cd lib/nodejs
clean-local:
$(RM) -r test/gen-nodejs
$(RM) -r $(top_srcdir)/node_modules
EXTRA_DIST = \
examples \
lib \
test \
coding_standards.md \
README.md

65
vendor/git.apache.org/thrift.git/lib/nodejs/README.md generated vendored Normal file
View file

@ -0,0 +1,65 @@
Thrift Node.js Library
=========================
License
-------
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.
## Install
npm install thrift
## Thrift Compiler
You can compile IDL sources for Node.js with the following command:
thrift --gen js:node thrift_file
## Cassandra Client Example:
Here is a Cassandra example:
var thrift = require('thrift'),
Cassandra = require('./gen-nodejs/Cassandra')
ttypes = require('./gen-nodejs/cassandra_types');
var connection = thrift.createConnection("localhost", 9160),
client = thrift.createClient(Cassandra, connection);
connection.on('error', function(err) {
console.error(err);
});
client.get_slice("Keyspace", "key", new ttypes.ColumnParent({column_family: "ExampleCF"}), new ttypes.SlicePredicate({slice_range: new ttypes.SliceRange({start: '', finish: ''})}), ttypes.ConsistencyLevel.ONE, function(err, data) {
if (err) {
// handle err
} else {
// data == [ttypes.ColumnOrSuperColumn, ...]
}
connection.end();
});
<a name="int64"></a>
## Int64
Since JavaScript represents all numbers as doubles, int64 values cannot be accurately represented naturally. To solve this, int64 values in responses will be wrapped with Thirft.Int64 objects. The Int64 implementation used is [broofa/node-int64](https://github.com/broofa/node-int64).
## Client and server examples
Several example clients and servers are included in the thrift/lib/nodejs/examples folder and the cross language tutorial thrift/tutorial/nodejs folder.

View file

@ -0,0 +1 @@
Please follow [General Coding Standards](/doc/coding_standards.md)

View file

@ -0,0 +1,40 @@
# Thrift Node.js Examples
## License
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.
## Running the user example
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
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
HTTP examples are provided also: httpClient.js and httpServer.js
You can test HTTP cross platform with the httpServer.py Python server

View file

@ -0,0 +1,49 @@
/*
* 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 UserStorage = require('./gen-nodejs/UserStorage.js'),
ttypes = require('./gen-nodejs/user_types');
var connection = thrift.createConnection('localhost', 9090),
client = thrift.createClient(UserStorage, connection);
var user = new ttypes.UserProfile({uid: 1,
name: "Mark Slee",
blurb: "I'll find something to put here."});
connection.on('error', function(err) {
console.error(err);
});
client.store(user, function(err, response) {
if (err) {
console.error(err);
} else {
console.log("client stored:", user.uid);
client.retrieve(user.uid, function(err, responseUser) {
if (err) {
console.error(err);
} else {
console.log("client retrieved:", responseUser.uid);
connection.end();
}
});
}
});

View file

@ -0,0 +1,58 @@
/*
* 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'),
ttransport = require('thrift/transport');
var UserStorage = require('./gen-nodejs/UserStorage'),
ttypes = require('./gen-nodejs/user_types');
var f_conn = thrift.createConnection('localhost', 9090), // default: framed
f_client = thrift.createClient(UserStorage, f_conn);
var b_conn = thrift.createConnection('localhost', 9091, {transport: ttransport.TBufferedTransport}),
b_client = thrift.createClient(UserStorage, b_conn);
var user1 = new ttypes.UserProfile({uid: 1,
name: "Mark Slee",
blurb: "I'll find something to put here."});
var user2 = new ttypes.UserProfile({uid: 2,
name: "Satoshi Tagomori",
blurb: "ok, let's test with buffered transport."});
f_conn.on('error', function(err) {
console.error("framed:", err);
});
f_client.store(user1, function(err, response) {
if (err) { console.error(err); return; }
console.log("stored:", user1.uid, " as ", user1.name);
b_client.retrieve(user1.uid, function(err, responseUser) {
if (err) { console.error(err); return; }
console.log("retrieved:", responseUser.uid, " as ", responseUser.name);
});
});
b_client.store(user2, function(err, response) {
if (err) { console.error(err); return; }
console.log("stored:", user2.uid, " as ", user2.name);
f_client.retrieve(user2.uid, function(err, responseUser) {
if (err) { console.error(err); return; }
console.log("retrieved:", responseUser.uid, " as ", responseUser.name);
});
});

View file

@ -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.
-->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Apache Thrift JavaScript Browser Client Demo</title>
<script src="thrift.js" type="text/javascript"></script>
<script src="gen-js/HelloSvc.js" type="text/javascript"></script>
<script src="gen-js/TimesTwo.js" type="text/javascript"></script>
</head>
<body>
<h1>Apache Thrift JavaScript Browser Client Demo</h1>
<p>This html file demonstrates Apache Thrift JavaScrpt RPC between a browser client to a node.js server. Clicking the buttons below will call the RPC functions 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:</p>
<ol>
<li>Install Node.js <pre><a href="http://nodejs.org">nodejs.org</a></pre></li>
<li>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+) <pre>$ npm install thrift</pre></li>
<li>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.)<pre>$ thrift -gen js -gen js:node hello.thrift</pre></li>
<li>Run the node server in the directory with the hello.html file<pre>$ node hello.js</pre></li>
<li>Copy the Apache Thrift JavaScript library, thrift.js, into the directory with this html file.<pre>$ cp ...../thrift.js . (you should be able to use Bower to install the browser based Apache Thrift library in the near future.)</pre>
<li>Reload this page in a browser through the node server using using the URL: <pre>http://localhost:8585/hello.html</pre>then click a button below to make an RPC call</li>
</ol>
<button id="btn">Get Message from Node Server</button>
<button id="btnDbl">Double 25</button>
<script type="text/javascript">
document.getElementById("btn").addEventListener("click", getMessage, false);
function getMessage() {
var transport = new Thrift.TXHRTransport("http://localhost:8585/hello");
var protocol = new Thrift.TJSONProtocol(transport);
var client = new HelloSvcClient(protocol);
var msg = client.hello_func();
document.getElementById("output").innerHTML = msg;
}
document.getElementById("btnDbl").addEventListener("click", dblMessage, false);
function dblMessage() {
var transport = new Thrift.TXHRTransport("http://localhost:8585/dbl");
var protocol = new Thrift.TJSONProtocol(transport);
var client = new TimesTwoClient(protocol);
var val = client.dbl(25);
document.getElementById("output2").innerHTML = val;
}
</script>
<h2>Server Response: <div id="output"></div></h2>
<h2>Server Dbl: <div id="output2"></div></h2>
</body>
</html>

View file

@ -0,0 +1,63 @@
/*
* 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 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: thrift.TBufferedTransport,
protocol: thrift.TJSONProtocol,
processor: HelloSvc,
handler: helloHandler
};
var dblService = {
transport: thrift.TBufferedTransport,
protocol: thrift.TJSONProtocol,
processor: TimesTwoSvc,
handler: timesTwoHandler
};
var ServerOptions = {
files: ".",
services: {
"/hello": helloService,
"/dbl": dblService,
}
}
var server = thrift.createWebServer(ServerOptions);
var port = 8585;
server.listen(port);
console.log("Http/Thrift Server running on port: " + port);

View file

@ -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),
}

View 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);
});

View 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);

View 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()

View file

@ -0,0 +1,46 @@
/*
* 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 a standalone deserialize/parse example if you just want to deserialize
thrift decoupled from cassandra server
1. acquire thrift template specification files from who ever built it (eg: EXAMPLE.thrift)
2. Install thrift on local machine
3. generate thrift clients for nodejs using template specification files (#1)
thrift --gen js:node schema/EXAMPLE.thrift
This creates creates gen-node.js directory containing a new file, GENERATED.js
4. Inside GENERATED.js is a class you will want to instanciate. Find this class name and plug
it into the example code below (ie, "YOUR_CLASS_NAME")
*/
function parseThrift(thriftEncodedData, callback) {
var thrift = require('thrift');
var transport = new thrift.TFramedTransport(thriftEncodedData);
var protocol = new thrift.TBinaryProtocol(transport);
var clientClass = require('../gen-nodejs/GENERATED').YOUR_CLASS_NAME;
var client = new clientClass();
client.read(protocol);
callback(null, client);
}

View file

@ -0,0 +1,39 @@
/*
* 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 UserStorage = require('./gen-nodejs/UserStorage.js'),
ttypes = require('./gen-nodejs/user_types');
var users = {};
var server = thrift.createServer(UserStorage, {
store: function(user, result) {
console.log("server stored:", user.uid);
users[user.uid] = user;
result(null);
},
retrieve: function(uid, result) {
console.log("server retrieved:", uid);
result(null, users[uid]);
},
});
server.listen(9090);

View file

@ -0,0 +1,53 @@
/*
* 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 connect = require('connect');
var thrift = require('thrift');
var UserStorage = require('./gen-nodejs/UserStorage'),
ttypes = require('./gen-nodejs/user_types');
var users = {};
var store = function(user, result) {
console.log("stored:", user.uid);
users[user.uid] = user;
result(null);
};
var retrieve = function(uid, result) {
console.log("retrieved:", uid);
result(null, users[uid]);
};
var server_http = thrift.createHttpServer(UserStorage, {
store: store,
retrieve: retrieve
});
server_http.listen(9090);
var server_connect = connect(thrift.httpMiddleware(UserStorage, {
store: store,
retrieve: retrieve
}));
server_http.listen(9091);
var server_connect_json = connect(thrift.httpMiddleware(UserStorage, {
store: store,
retrieve: retrieve
}, {protocol: thrift.TJSONProtocol}));
server_connect_json.listen(9092);

View file

@ -0,0 +1,46 @@
/*
* 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'),
ttransport = require('thrift/transport');
var UserStorage = require('./gen-nodejs/UserStorage'),
ttypes = require('./gen-nodejs/user_types');
var users = {};
var store = function(user, result) {
console.log("stored:", user.uid);
users[user.uid] = user;
result(null);
};
var retrieve = function(uid, result) {
console.log("retrieved:", uid);
result(null, users[uid]);
};
var server_framed = thrift.createServer(UserStorage, {
store: store,
retrieve: retrieve
});
server_framed.listen(9090);
var server_buffered = thrift.createServer(UserStorage, {
store: store,
retrieve: retrieve
}, {transport: ttransport.TBufferedTransport});
server_buffered.listen(9091);

View file

@ -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.
struct UserProfile {
1: i32 uid,
2: string name,
3: string blurb
}
service UserStorage {
void store(1: UserProfile user),
UserProfile retrieve(1: i32 uid)
}

View file

@ -0,0 +1,168 @@
/*
* 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 POW_8 = Math.pow(2, 8);
var POW_16 = Math.pow(2, 16);
var POW_24 = Math.pow(2, 24);
var POW_32 = Math.pow(2, 32);
var POW_40 = Math.pow(2, 40);
var POW_48 = Math.pow(2, 48);
var POW_52 = Math.pow(2, 52);
var POW_1022 = Math.pow(2, 1022);
exports.readByte = function(b){
return b > 127 ? b-256 : b;
};
exports.readI16 = function(buff, off) {
off = off || 0;
var v = buff[off + 1];
v += buff[off] << 8;
if (buff[off] & 128) {
v -= POW_16;
}
return v;
};
exports.readI32 = function(buff, off) {
off = off || 0;
var v = buff[off + 3];
v += buff[off + 2] << 8;
v += buff[off + 1] << 16;
v += buff[off] * POW_24;
if (buff[off] & 0x80) {
v -= POW_32;
}
return v;
};
exports.writeI16 = function(buff, v) {
buff[1] = v & 0xff;
v >>= 8;
buff[0] = v & 0xff;
return buff;
};
exports.writeI32 = function(buff, v) {
buff[3] = v & 0xff;
v >>= 8;
buff[2] = v & 0xff;
v >>= 8;
buff[1] = v & 0xff;
v >>= 8;
buff[0] = v & 0xff;
return buff;
};
exports.readDouble = function(buff, off) {
off = off || 0;
var signed = buff[off] & 0x80;
var e = (buff[off+1] & 0xF0) >> 4;
e += (buff[off] & 0x7F) << 4;
var m = buff[off+7];
m += buff[off+6] << 8;
m += buff[off+5] << 16;
m += buff[off+4] * POW_24;
m += buff[off+3] * POW_32;
m += buff[off+2] * POW_40;
m += (buff[off+1] & 0x0F) * POW_48;
switch (e) {
case 0:
e = -1022;
break;
case 2047:
return m ? NaN : (signed ? -Infinity : Infinity);
default:
m += POW_52;
e -= 1023;
}
if (signed) {
m *= -1;
}
return m * Math.pow(2, e - 52);
};
/*
* Based on code from the jspack module:
* http://code.google.com/p/jspack/
*/
exports.writeDouble = function(buff, v) {
var m, e, c;
buff[0] = (v < 0 ? 0x80 : 0x00);
v = Math.abs(v);
if (v !== v) {
// NaN, use QNaN IEEE format
m = 2251799813685248;
e = 2047;
} else if (v === Infinity) {
m = 0;
e = 2047;
} else {
e = Math.floor(Math.log(v) / Math.LN2);
c = Math.pow(2, -e);
if (v * c < 1) {
e--;
c *= 2;
}
if (e + 1023 >= 2047)
{
// Overflow
m = 0;
e = 2047;
}
else if (e + 1023 >= 1)
{
// Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
m = (v*c-1) * POW_52;
e += 1023;
}
else
{
// Denormalized - also catches the '0' case, somewhat by chance
m = (v * POW_1022) * POW_52;
e = 0;
}
}
buff[1] = (e << 4) & 0xf0;
buff[0] |= (e >> 4) & 0x7f;
buff[7] = m & 0xff;
m = Math.floor(m / POW_8);
buff[6] = m & 0xff;
m = Math.floor(m / POW_8);
buff[5] = m & 0xff;
m = Math.floor(m / POW_8);
buff[4] = m & 0xff;
m >>= 8;
buff[3] = m & 0xff;
m >>= 8;
buff[2] = m & 0xff;
m >>= 8;
buff[1] |= m & 0x0f;
return buff;
};

View file

@ -0,0 +1,366 @@
/*
* 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 log = require('./log');
var binary = require('./binary');
var Int64 = require('node-int64');
var Thrift = require('./thrift');
var Type = Thrift.Type;
module.exports = TBinaryProtocol;
// JavaScript supports only numeric doubles, therefore even hex values are always signed.
// The largest integer value which can be represented in JavaScript is +/-2^53.
// Bitwise operations convert numbers to 32 bit integers but perform sign extension
// upon assigning values back to variables.
var VERSION_MASK = -65536, // 0xffff0000
VERSION_1 = -2147418112, // 0x80010000
TYPE_MASK = 0x000000ff;
function TBinaryProtocol(trans, strictRead, strictWrite) {
this.trans = trans;
this.strictRead = (strictRead !== undefined ? strictRead : false);
this.strictWrite = (strictWrite !== undefined ? strictWrite : true);
};
TBinaryProtocol.prototype.flush = function() {
return this.trans.flush();
};
TBinaryProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
if (this.strictWrite) {
this.writeI32(VERSION_1 | type);
this.writeString(name);
this.writeI32(seqid);
} else {
this.writeString(name);
this.writeByte(type);
this.writeI32(seqid);
}
// Record client seqid to find callback again
if (this._seqid) {
// TODO better logging log warning
log.warning('SeqId already set', { 'name': name });
} else {
this._seqid = seqid;
this.trans.setCurrSeqId(seqid);
}
};
TBinaryProtocol.prototype.writeMessageEnd = function() {
if (this._seqid) {
this._seqid = null;
} else {
log.warning('No seqid to unset');
}
};
TBinaryProtocol.prototype.writeStructBegin = function(name) {
};
TBinaryProtocol.prototype.writeStructEnd = function() {
};
TBinaryProtocol.prototype.writeFieldBegin = function(name, type, id) {
this.writeByte(type);
this.writeI16(id);
};
TBinaryProtocol.prototype.writeFieldEnd = function() {
};
TBinaryProtocol.prototype.writeFieldStop = function() {
this.writeByte(Type.STOP);
};
TBinaryProtocol.prototype.writeMapBegin = function(ktype, vtype, size) {
this.writeByte(ktype);
this.writeByte(vtype);
this.writeI32(size);
};
TBinaryProtocol.prototype.writeMapEnd = function() {
};
TBinaryProtocol.prototype.writeListBegin = function(etype, size) {
this.writeByte(etype);
this.writeI32(size);
};
TBinaryProtocol.prototype.writeListEnd = function() {
};
TBinaryProtocol.prototype.writeSetBegin = function(etype, size) {
this.writeByte(etype);
this.writeI32(size);
};
TBinaryProtocol.prototype.writeSetEnd = function() {
};
TBinaryProtocol.prototype.writeBool = function(bool) {
if (bool) {
this.writeByte(1);
} else {
this.writeByte(0);
}
};
TBinaryProtocol.prototype.writeByte = function(b) {
this.trans.write(new Buffer([b]));
};
TBinaryProtocol.prototype.writeI16 = function(i16) {
this.trans.write(binary.writeI16(new Buffer(2), i16));
};
TBinaryProtocol.prototype.writeI32 = function(i32) {
this.trans.write(binary.writeI32(new Buffer(4), i32));
};
TBinaryProtocol.prototype.writeI64 = function(i64) {
if (i64.buffer) {
this.trans.write(i64.buffer);
} else {
this.trans.write(new Int64(i64).buffer);
}
};
TBinaryProtocol.prototype.writeDouble = function(dub) {
this.trans.write(binary.writeDouble(new Buffer(8), dub));
};
TBinaryProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) {
if (typeof(arg) === 'string') {
this.writeI32(Buffer.byteLength(arg, encoding));
this.trans.write(new Buffer(arg, encoding));
} else if ((arg instanceof Buffer) ||
(Object.prototype.toString.call(arg) == '[object Uint8Array]')) {
// Buffers in Node.js under Browserify may extend UInt8Array instead of
// defining a new object. We detect them here so we can write them
// correctly
this.writeI32(arg.length);
this.trans.write(arg);
} else {
throw new Error(name + ' called without a string/Buffer argument: ' + arg);
}
};
TBinaryProtocol.prototype.writeString = function(arg) {
this.writeStringOrBinary('writeString', 'utf8', arg);
};
TBinaryProtocol.prototype.writeBinary = function(arg) {
this.writeStringOrBinary('writeBinary', 'binary', arg);
};
TBinaryProtocol.prototype.readMessageBegin = function() {
var sz = this.readI32();
var type, name, seqid;
if (sz < 0) {
var version = sz & VERSION_MASK;
if (version != VERSION_1) {
console.log("BAD: " + version);
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad version in readMessageBegin: " + sz);
}
type = sz & TYPE_MASK;
name = this.readString();
seqid = this.readI32();
} else {
if (this.strictRead) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "No protocol version header");
}
name = this.trans.read(sz);
type = this.readByte();
seqid = this.readI32();
}
return {fname: name, mtype: type, rseqid: seqid};
};
TBinaryProtocol.prototype.readMessageEnd = function() {
};
TBinaryProtocol.prototype.readStructBegin = function() {
return {fname: ''};
};
TBinaryProtocol.prototype.readStructEnd = function() {
};
TBinaryProtocol.prototype.readFieldBegin = function() {
var type = this.readByte();
if (type == Type.STOP) {
return {fname: null, ftype: type, fid: 0};
}
var id = this.readI16();
return {fname: null, ftype: type, fid: id};
};
TBinaryProtocol.prototype.readFieldEnd = function() {
};
TBinaryProtocol.prototype.readMapBegin = function() {
var ktype = this.readByte();
var vtype = this.readByte();
var size = this.readI32();
return {ktype: ktype, vtype: vtype, size: size};
};
TBinaryProtocol.prototype.readMapEnd = function() {
};
TBinaryProtocol.prototype.readListBegin = function() {
var etype = this.readByte();
var size = this.readI32();
return {etype: etype, size: size};
};
TBinaryProtocol.prototype.readListEnd = function() {
};
TBinaryProtocol.prototype.readSetBegin = function() {
var etype = this.readByte();
var size = this.readI32();
return {etype: etype, size: size};
};
TBinaryProtocol.prototype.readSetEnd = function() {
};
TBinaryProtocol.prototype.readBool = function() {
var b = this.readByte();
if (b === 0) {
return false;
}
return true;
};
TBinaryProtocol.prototype.readByte = function() {
return this.trans.readByte();
};
TBinaryProtocol.prototype.readI16 = function() {
return this.trans.readI16();
};
TBinaryProtocol.prototype.readI32 = function() {
return this.trans.readI32();
};
TBinaryProtocol.prototype.readI64 = function() {
var buff = this.trans.read(8);
return new Int64(buff);
};
TBinaryProtocol.prototype.readDouble = function() {
return this.trans.readDouble();
};
TBinaryProtocol.prototype.readBinary = function() {
var len = this.readI32();
if (len === 0) {
return new Buffer(0);
}
if (len < 0) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size");
}
return this.trans.read(len);
};
TBinaryProtocol.prototype.readString = function() {
var len = this.readI32();
if (len === 0) {
return "";
}
if (len < 0) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size");
}
return this.trans.readString(len);
};
TBinaryProtocol.prototype.getTransport = function() {
return this.trans;
};
TBinaryProtocol.prototype.skip = function(type) {
switch (type) {
case Type.STOP:
return;
case Type.BOOL:
this.readBool();
break;
case Type.BYTE:
this.readByte();
break;
case Type.I16:
this.readI16();
break;
case Type.I32:
this.readI32();
break;
case Type.I64:
this.readI64();
break;
case Type.DOUBLE:
this.readDouble();
break;
case Type.STRING:
this.readString();
break;
case Type.STRUCT:
this.readStructBegin();
while (true) {
var r = this.readFieldBegin();
if (r.ftype === Type.STOP) {
break;
}
this.skip(r.ftype);
this.readFieldEnd();
}
this.readStructEnd();
break;
case Type.MAP:
var mapBegin = this.readMapBegin();
for (var i = 0; i < mapBegin.size; ++i) {
this.skip(mapBegin.ktype);
this.skip(mapBegin.vtype);
}
this.readMapEnd();
break;
case Type.SET:
var setBegin = this.readSetBegin();
for (var i2 = 0; i2 < setBegin.size; ++i2) {
this.skip(setBegin.etype);
}
this.readSetEnd();
break;
case Type.LIST:
var listBegin = this.readListBegin();
for (var i3 = 0; i3 < listBegin.size; ++i3) {
this.skip(listBegin.etype);
}
this.readListEnd();
break;
default:
throw new Error("Invalid type: " + type);
}
};

View file

@ -0,0 +1,34 @@
/*
* 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.
*/
exports.Thrift = require('./thrift');
var xhrConnection = require('./xhr_connection');
exports.XHRConnection = xhrConnection.XHRConnection;
exports.createXHRConnection = xhrConnection.createXHRConnection;
exports.createXHRClient = xhrConnection.createXHRClient;
exports.Multiplexer = require('./multiplexed_protocol').Multiplexer;
exports.TWebSocketTransport = require('./ws_transport');
exports.TBufferedTransport = require('./buffered_transport');
exports.TFramedTransport = require('./framed_transport');
exports.Protocol = exports.TJSONProtocol = require('./json_protocol');
exports.TBinaryProtocol = require('./binary_protocol');
exports.TCompactProtocol = require('./compact_protocol');

View file

@ -0,0 +1,175 @@
/*
* 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 binary = require('./binary');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
module.exports = TBufferedTransport;
function TBufferedTransport(buffer, callback) {
this.defaultReadBufferSize = 1024;
this.writeBufferSize = 512; // Soft Limit
this.inBuf = new Buffer(this.defaultReadBufferSize);
this.readCursor = 0;
this.writeCursor = 0; // for input buffer
this.outBuffers = [];
this.outCount = 0;
this.onFlush = callback;
};
TBufferedTransport.receiver = function(callback, seqid) {
var reader = new TBufferedTransport();
return function(data) {
if (reader.writeCursor + data.length > reader.inBuf.length) {
var buf = new Buffer(reader.writeCursor + data.length);
reader.inBuf.copy(buf, 0, 0, reader.writeCursor);
reader.inBuf = buf;
}
data.copy(reader.inBuf, reader.writeCursor, 0);
reader.writeCursor += data.length;
callback(reader, seqid);
};
};
TBufferedTransport.prototype.commitPosition = function(){
var unreadSize = this.writeCursor - this.readCursor;
var bufSize = (unreadSize * 2 > this.defaultReadBufferSize) ?
unreadSize * 2 : this.defaultReadBufferSize;
var buf = new Buffer(bufSize);
if (unreadSize > 0) {
this.inBuf.copy(buf, 0, this.readCursor, this.writeCursor);
}
this.readCursor = 0;
this.writeCursor = unreadSize;
this.inBuf = buf;
};
TBufferedTransport.prototype.rollbackPosition = function(){
this.readCursor = 0;
}
// TODO: Implement open/close support
TBufferedTransport.prototype.isOpen = function() {
return true;
};
TBufferedTransport.prototype.open = function() {
};
TBufferedTransport.prototype.close = function() {
};
// Set the seqid of the message in the client
// So that callbacks can be found
TBufferedTransport.prototype.setCurrSeqId = function(seqid) {
this._seqid = seqid;
};
TBufferedTransport.prototype.ensureAvailable = function(len) {
if (this.readCursor + len > this.writeCursor) {
throw new InputBufferUnderrunError();
}
};
TBufferedTransport.prototype.read = function(len) {
this.ensureAvailable(len);
var buf = new Buffer(len);
this.inBuf.copy(buf, 0, this.readCursor, this.readCursor + len);
this.readCursor += len;
return buf;
};
TBufferedTransport.prototype.readByte = function() {
this.ensureAvailable(1);
return binary.readByte(this.inBuf[this.readCursor++]);
};
TBufferedTransport.prototype.readI16 = function() {
this.ensureAvailable(2);
var i16 = binary.readI16(this.inBuf, this.readCursor);
this.readCursor += 2;
return i16;
};
TBufferedTransport.prototype.readI32 = function() {
this.ensureAvailable(4);
var i32 = binary.readI32(this.inBuf, this.readCursor);
this.readCursor += 4;
return i32;
};
TBufferedTransport.prototype.readDouble = function() {
this.ensureAvailable(8);
var d = binary.readDouble(this.inBuf, this.readCursor);
this.readCursor += 8;
return d;
};
TBufferedTransport.prototype.readString = function(len) {
this.ensureAvailable(len);
var str = this.inBuf.toString('utf8', this.readCursor, this.readCursor + len);
this.readCursor += len;
return str;
};
TBufferedTransport.prototype.borrow = function() {
var obj = {buf: this.inBuf, readIndex: this.readCursor, writeIndex: this.writeCursor};
return obj;
};
TBufferedTransport.prototype.consume = function(bytesConsumed) {
this.readCursor += bytesConsumed;
};
TBufferedTransport.prototype.write = function(buf) {
if (typeof(buf) === "string") {
buf = new Buffer(buf, 'utf8');
}
this.outBuffers.push(buf);
this.outCount += buf.length;
};
TBufferedTransport.prototype.flush = function() {
// If the seqid of the callback is available pass it to the onFlush
// Then remove the current seqid
var seqid = this._seqid;
this._seqid = null;
if (this.outCount < 1) {
return;
}
var msg = new Buffer(this.outCount),
pos = 0;
this.outBuffers.forEach(function(buf) {
buf.copy(msg, pos, 0);
pos += buf.length;
});
if (this.onFlush) {
// Passing seqid through this call to get it to the connection
this.onFlush(msg, seqid);
}
this.outBuffers = [];
this.outCount = 0;
}

View file

@ -0,0 +1,918 @@
/*
* 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 log = require('./log');
var Int64 = require('node-int64');
var Thrift = require('./thrift');
var Type = Thrift.Type;
module.exports = TCompactProtocol;
var POW_8 = Math.pow(2, 8);
var POW_24 = Math.pow(2, 24);
var POW_32 = Math.pow(2, 32);
var POW_40 = Math.pow(2, 40);
var POW_48 = Math.pow(2, 48);
var POW_52 = Math.pow(2, 52);
var POW_1022 = Math.pow(2, 1022);
/**
* Constructor Function for the Compact Protocol.
* @constructor
* @param {object} [trans] - The underlying transport to read/write.
* @classdesc The Apache Thrift Protocol layer performs serialization
* of base types, the compact protocol serializes data in binary
* form with minimal space used for scalar values.
*/
function TCompactProtocol(trans) {
this.trans = trans;
this.lastField_ = [];
this.lastFieldId_ = 0;
this.string_limit_ = 0;
this.string_buf_ = null;
this.string_buf_size_ = 0;
this.container_limit_ = 0;
this.booleanField_ = {
name: null,
hasBoolValue: false
};
this.boolValue_ = {
hasBoolValue: false,
boolValue: false
};
};
//
// Compact Protocol Constants
//
/**
* Compact Protocol ID number.
* @readonly
* @const {number} PROTOCOL_ID
*/
TCompactProtocol.PROTOCOL_ID = -126; //1000 0010
/**
* Compact Protocol version number.
* @readonly
* @const {number} VERSION_N
*/
TCompactProtocol.VERSION_N = 1;
/**
* Compact Protocol version mask for combining protocol version and message type in one byte.
* @readonly
* @const {number} VERSION_MASK
*/
TCompactProtocol.VERSION_MASK = 0x1f; //0001 1111
/**
* Compact Protocol message type mask for combining protocol version and message type in one byte.
* @readonly
* @const {number} TYPE_MASK
*/
TCompactProtocol.TYPE_MASK = -32; //1110 0000
/**
* Compact Protocol message type bits for ensuring message type bit size.
* @readonly
* @const {number} TYPE_BITS
*/
TCompactProtocol.TYPE_BITS = 7; //0000 0111
/**
* Compact Protocol message type shift amount for combining protocol version and message type in one byte.
* @readonly
* @const {number} TYPE_SHIFT_AMOUNT
*/
TCompactProtocol.TYPE_SHIFT_AMOUNT = 5;
/**
* Compact Protocol type IDs used to keep type data within one nibble.
* @readonly
* @property {number} CT_STOP - End of a set of fields.
* @property {number} CT_BOOLEAN_TRUE - Flag for Boolean field with true value (packed field and value).
* @property {number} CT_BOOLEAN_FALSE - Flag for Boolean field with false value (packed field and value).
* @property {number} CT_BYTE - Signed 8 bit integer.
* @property {number} CT_I16 - Signed 16 bit integer.
* @property {number} CT_I32 - Signed 32 bit integer.
* @property {number} CT_I64 - Signed 64 bit integer (2^53 max in JavaScript).
* @property {number} CT_DOUBLE - 64 bit IEEE 854 floating point.
* @property {number} CT_BINARY - Array of bytes (used for strings also).
* @property {number} CT_LIST - A collection type (unordered).
* @property {number} CT_SET - A collection type (unordered and without repeated values).
* @property {number} CT_MAP - A collection type (map/associative-array/dictionary).
* @property {number} CT_STRUCT - A multifield type.
*/
TCompactProtocol.Types = {
CT_STOP: 0x00,
CT_BOOLEAN_TRUE: 0x01,
CT_BOOLEAN_FALSE: 0x02,
CT_BYTE: 0x03,
CT_I16: 0x04,
CT_I32: 0x05,
CT_I64: 0x06,
CT_DOUBLE: 0x07,
CT_BINARY: 0x08,
CT_LIST: 0x09,
CT_SET: 0x0A,
CT_MAP: 0x0B,
CT_STRUCT: 0x0C
};
/**
* Array mapping Compact type IDs to standard Thrift type IDs.
* @readonly
*/
TCompactProtocol.TTypeToCType = [
TCompactProtocol.Types.CT_STOP, // T_STOP
0, // unused
TCompactProtocol.Types.CT_BOOLEAN_TRUE, // T_BOOL
TCompactProtocol.Types.CT_BYTE, // T_BYTE
TCompactProtocol.Types.CT_DOUBLE, // T_DOUBLE
0, // unused
TCompactProtocol.Types.CT_I16, // T_I16
0, // unused
TCompactProtocol.Types.CT_I32, // T_I32
0, // unused
TCompactProtocol.Types.CT_I64, // T_I64
TCompactProtocol.Types.CT_BINARY, // T_STRING
TCompactProtocol.Types.CT_STRUCT, // T_STRUCT
TCompactProtocol.Types.CT_MAP, // T_MAP
TCompactProtocol.Types.CT_SET, // T_SET
TCompactProtocol.Types.CT_LIST, // T_LIST
];
//
// Compact Protocol Utilities
//
/**
* Returns the underlying transport layer.
* @return {object} The underlying transport layer.
*/TCompactProtocol.prototype.getTransport = function() {
return this.trans;
};
/**
* Lookup a Compact Protocol Type value for a given Thrift Type value.
* N.B. Used only internally.
* @param {number} ttype - Thrift type value
* @returns {number} Compact protocol type value
*/
TCompactProtocol.prototype.getCompactType = function(ttype) {
return TCompactProtocol.TTypeToCType[ttype];
};
/**
* Lookup a Thrift Type value for a given Compact Protocol Type value.
* N.B. Used only internally.
* @param {number} type - Compact Protocol type value
* @returns {number} Thrift Type value
*/
TCompactProtocol.prototype.getTType = function(type) {
switch (type) {
case Type.STOP:
return Type.STOP;
case TCompactProtocol.Types.CT_BOOLEAN_FALSE:
case TCompactProtocol.Types.CT_BOOLEAN_TRUE:
return Type.BOOL;
case TCompactProtocol.Types.CT_BYTE:
return Type.BYTE;
case TCompactProtocol.Types.CT_I16:
return Type.I16;
case TCompactProtocol.Types.CT_I32:
return Type.I32;
case TCompactProtocol.Types.CT_I64:
return Type.I64;
case TCompactProtocol.Types.CT_DOUBLE:
return Type.DOUBLE;
case TCompactProtocol.Types.CT_BINARY:
return Type.STRING;
case TCompactProtocol.Types.CT_LIST:
return Type.LIST;
case TCompactProtocol.Types.CT_SET:
return Type.SET;
case TCompactProtocol.Types.CT_MAP:
return Type.MAP;
case TCompactProtocol.Types.CT_STRUCT:
return Type.STRUCT;
default:
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Unknown type: " + type);
}
return Type.STOP;
};
//
// Compact Protocol write operations
//
/**
* Send any buffered bytes to the end point.
*/
TCompactProtocol.prototype.flush = function() {
return this.trans.flush();
};
/**
* Writes an RPC message header
* @param {string} name - The method name for the message.
* @param {number} type - The type of message (CALL, REPLY, EXCEPTION, ONEWAY).
* @param {number} seqid - The call sequence number (if any).
*/
TCompactProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
this.writeByte(TCompactProtocol.PROTOCOL_ID);
this.writeByte((TCompactProtocol.VERSION_N & TCompactProtocol.VERSION_MASK) |
((type << TCompactProtocol.TYPE_SHIFT_AMOUNT) & TCompactProtocol.TYPE_MASK));
this.writeVarint32(seqid);
this.writeString(name);
// Record client seqid to find callback again
if (this._seqid) {
// TODO better logging log warning
log.warning('SeqId already set', { 'name': name });
} else {
this._seqid = seqid;
this.trans.setCurrSeqId(seqid);
}
};
TCompactProtocol.prototype.writeMessageEnd = function() {
};
TCompactProtocol.prototype.writeStructBegin = function(name) {
this.lastField_.push(this.lastFieldId_);
this.lastFieldId_ = 0;
};
TCompactProtocol.prototype.writeStructEnd = function() {
this.lastFieldId_ = this.lastField_.pop();
};
/**
* Writes a struct field header
* @param {string} name - The field name (not written with the compact protocol).
* @param {number} type - The field data type (a normal Thrift field Type).
* @param {number} id - The IDL field Id.
*/
TCompactProtocol.prototype.writeFieldBegin = function(name, type, id) {
if (type != Type.BOOL) {
return this.writeFieldBeginInternal(name, type, id, -1);
}
this.booleanField_.name = name;
this.booleanField_.fieldType = type;
this.booleanField_.fieldId = id;
};
TCompactProtocol.prototype.writeFieldEnd = function() {
};
TCompactProtocol.prototype.writeFieldStop = function() {
this.writeByte(TCompactProtocol.Types.CT_STOP);
};
/**
* Writes a map collection header
* @param {number} keyType - The Thrift type of the map keys.
* @param {number} valType - The Thrift type of the map values.
* @param {number} size - The number of k/v pairs in the map.
*/
TCompactProtocol.prototype.writeMapBegin = function(keyType, valType, size) {
if (size === 0) {
this.writeByte(0);
} else {
this.writeVarint32(size);
this.writeByte(this.getCompactType(keyType) << 4 | this.getCompactType(valType));
}
};
TCompactProtocol.prototype.writeMapEnd = function() {
};
/**
* Writes a list collection header
* @param {number} elemType - The Thrift type of the list elements.
* @param {number} size - The number of elements in the list.
*/
TCompactProtocol.prototype.writeListBegin = function(elemType, size) {
this.writeCollectionBegin(elemType, size);
};
TCompactProtocol.prototype.writeListEnd = function() {
};
/**
* Writes a set collection header
* @param {number} elemType - The Thrift type of the set elements.
* @param {number} size - The number of elements in the set.
*/
TCompactProtocol.prototype.writeSetBegin = function(elemType, size) {
this.writeCollectionBegin(elemType, size);
};
TCompactProtocol.prototype.writeSetEnd = function() {
};
TCompactProtocol.prototype.writeBool = function(value) {
if (this.booleanField_.name !== null) {
// we haven't written the field header yet
this.writeFieldBeginInternal(this.booleanField_.name,
this.booleanField_.fieldType,
this.booleanField_.fieldId,
(value ? TCompactProtocol.Types.CT_BOOLEAN_TRUE
: TCompactProtocol.Types.CT_BOOLEAN_FALSE));
this.booleanField_.name = null;
} else {
// we're not part of a field, so just write the value
this.writeByte((value ? TCompactProtocol.Types.CT_BOOLEAN_TRUE
: TCompactProtocol.Types.CT_BOOLEAN_FALSE));
}
};
TCompactProtocol.prototype.writeByte = function(b) {
this.trans.write(new Buffer([b]));
};
TCompactProtocol.prototype.writeI16 = function(i16) {
this.writeVarint32(this.i32ToZigzag(i16));
};
TCompactProtocol.prototype.writeI32 = function(i32) {
this.writeVarint32(this.i32ToZigzag(i32));
};
TCompactProtocol.prototype.writeI64 = function(i64) {
this.writeVarint64(this.i64ToZigzag(i64));
};
// Little-endian, unlike TBinaryProtocol
TCompactProtocol.prototype.writeDouble = function(v) {
var buff = new Buffer(8);
var m, e, c;
buff[7] = (v < 0 ? 0x80 : 0x00);
v = Math.abs(v);
if (v !== v) {
// NaN, use QNaN IEEE format
m = 2251799813685248;
e = 2047;
} else if (v === Infinity) {
m = 0;
e = 2047;
} else {
e = Math.floor(Math.log(v) / Math.LN2);
c = Math.pow(2, -e);
if (v * c < 1) {
e--;
c *= 2;
}
if (e + 1023 >= 2047)
{
// Overflow
m = 0;
e = 2047;
}
else if (e + 1023 >= 1)
{
// Normalized - term order matters, as Math.pow(2, 52-e) and v*Math.pow(2, 52) can overflow
m = (v*c-1) * POW_52;
e += 1023;
}
else
{
// Denormalized - also catches the '0' case, somewhat by chance
m = (v * POW_1022) * POW_52;
e = 0;
}
}
buff[6] = (e << 4) & 0xf0;
buff[7] |= (e >> 4) & 0x7f;
buff[0] = m & 0xff;
m = Math.floor(m / POW_8);
buff[1] = m & 0xff;
m = Math.floor(m / POW_8);
buff[2] = m & 0xff;
m = Math.floor(m / POW_8);
buff[3] = m & 0xff;
m >>= 8;
buff[4] = m & 0xff;
m >>= 8;
buff[5] = m & 0xff;
m >>= 8;
buff[6] |= m & 0x0f;
this.trans.write(buff);
};
TCompactProtocol.prototype.writeStringOrBinary = function(name, encoding, arg) {
if (typeof arg === 'string') {
this.writeVarint32(Buffer.byteLength(arg, encoding)) ;
this.trans.write(new Buffer(arg, encoding));
} else if (arg instanceof Buffer ||
Object.prototype.toString.call(arg) == '[object Uint8Array]') {
// Buffers in Node.js under Browserify may extend UInt8Array instead of
// defining a new object. We detect them here so we can write them
// correctly
this.writeVarint32(arg.length);
this.trans.write(arg);
} else {
throw new Error(name + ' called without a string/Buffer argument: ' + arg);
}
};
TCompactProtocol.prototype.writeString = function(arg) {
this.writeStringOrBinary('writeString', 'utf8', arg);
};
TCompactProtocol.prototype.writeBinary = function(arg) {
this.writeStringOrBinary('writeBinary', 'binary', arg);
};
//
// Compact Protocol internal write methods
//
TCompactProtocol.prototype.writeFieldBeginInternal = function(name,
fieldType,
fieldId,
typeOverride) {
//If there's a type override, use that.
var typeToWrite = (typeOverride == -1 ? this.getCompactType(fieldType) : typeOverride);
//Check if we can delta encode the field id
if (fieldId > this.lastFieldId_ && fieldId - this.lastFieldId_ <= 15) {
//Include the type delta with the field ID
this.writeByte((fieldId - this.lastFieldId_) << 4 | typeToWrite);
} else {
//Write separate type and ID values
this.writeByte(typeToWrite);
this.writeI16(fieldId);
}
this.lastFieldId_ = fieldId;
};
TCompactProtocol.prototype.writeCollectionBegin = function(elemType, size) {
if (size <= 14) {
//Combine size and type in one byte if possible
this.writeByte(size << 4 | this.getCompactType(elemType));
} else {
this.writeByte(0xf0 | this.getCompactType(elemType));
this.writeVarint32(size);
}
};
/**
* Write an i32 as a varint. Results in 1-5 bytes on the wire.
*/
TCompactProtocol.prototype.writeVarint32 = function(n) {
var buf = new Buffer(5);
var wsize = 0;
while (true) {
if ((n & ~0x7F) === 0) {
buf[wsize++] = n;
break;
} else {
buf[wsize++] = ((n & 0x7F) | 0x80);
n = n >>> 7;
}
}
var wbuf = new Buffer(wsize);
buf.copy(wbuf,0,0,wsize);
this.trans.write(wbuf);
};
/**
* Write an i64 as a varint. Results in 1-10 bytes on the wire.
* N.B. node-int64 is always big endian
*/
TCompactProtocol.prototype.writeVarint64 = function(n) {
if (typeof n === "number"){
n = new Int64(n);
}
if (! (n instanceof Int64)) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Expected Int64 or Number, found: " + n);
}
var buf = new Buffer(10);
var wsize = 0;
var hi = n.buffer.readUInt32BE(0, true);
var lo = n.buffer.readUInt32BE(4, true);
var mask = 0;
while (true) {
if (((lo & ~0x7F) === 0) && (hi === 0)) {
buf[wsize++] = lo;
break;
} else {
buf[wsize++] = ((lo & 0x7F) | 0x80);
mask = hi << 25;
lo = lo >>> 7;
hi = hi >>> 7;
lo = lo | mask;
}
}
var wbuf = new Buffer(wsize);
buf.copy(wbuf,0,0,wsize);
this.trans.write(wbuf);
};
/**
* Convert l into a zigzag long. This allows negative numbers to be
* represented compactly as a varint.
*/
TCompactProtocol.prototype.i64ToZigzag = function(l) {
if (typeof l === 'string') {
l = new Int64(parseInt(l, 10));
} else if (typeof l === 'number') {
l = new Int64(l);
}
if (! (l instanceof Int64)) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Expected Int64 or Number, found: " + l);
}
var hi = l.buffer.readUInt32BE(0, true);
var lo = l.buffer.readUInt32BE(4, true);
var sign = hi >>> 31;
hi = ((hi << 1) | (lo >>> 31)) ^ ((!!sign) ? 0xFFFFFFFF : 0);
lo = (lo << 1) ^ ((!!sign) ? 0xFFFFFFFF : 0);
return new Int64(hi, lo);
};
/**
* Convert n into a zigzag int. This allows negative numbers to be
* represented compactly as a varint.
*/
TCompactProtocol.prototype.i32ToZigzag = function(n) {
return (n << 1) ^ ((n & 0x80000000) ? 0xFFFFFFFF : 0);
};
//
// Compact Protocol read operations
//
TCompactProtocol.prototype.readMessageBegin = function() {
//Read protocol ID
var protocolId = this.trans.readByte();
if (protocolId != TCompactProtocol.PROTOCOL_ID) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad protocol identifier " + protocolId);
}
//Read Version and Type
var versionAndType = this.trans.readByte();
var version = (versionAndType & TCompactProtocol.VERSION_MASK);
if (version != TCompactProtocol.VERSION_N) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.BAD_VERSION, "Bad protocol version " + version);
}
var type = ((versionAndType >> TCompactProtocol.TYPE_SHIFT_AMOUNT) & TCompactProtocol.TYPE_BITS);
//Read SeqId
var seqid = this.readVarint32();
//Read name
var name = this.readString();
return {fname: name, mtype: type, rseqid: seqid};
};
TCompactProtocol.prototype.readMessageEnd = function() {
};
TCompactProtocol.prototype.readStructBegin = function() {
this.lastField_.push(this.lastFieldId_);
this.lastFieldId_ = 0;
return {fname: ''};
};
TCompactProtocol.prototype.readStructEnd = function() {
this.lastFieldId_ = this.lastField_.pop();
};
TCompactProtocol.prototype.readFieldBegin = function() {
var fieldId = 0;
var b = this.trans.readByte(b);
var type = (b & 0x0f);
if (type == TCompactProtocol.Types.CT_STOP) {
return {fname: null, ftype: Thrift.Type.STOP, fid: 0};
}
//Mask off the 4 MSB of the type header to check for field id delta.
var modifier = ((b & 0x000000f0) >>> 4);
if (modifier === 0) {
//If not a delta read the field id.
fieldId = this.readI16();
} else {
//Recover the field id from the delta
fieldId = (this.lastFieldId_ + modifier);
}
var fieldType = this.getTType(type);
//Boolean are encoded with the type
if (type == TCompactProtocol.Types.CT_BOOLEAN_TRUE ||
type == TCompactProtocol.Types.CT_BOOLEAN_FALSE) {
this.boolValue_.hasBoolValue = true;
this.boolValue_.boolValue =
(type == TCompactProtocol.Types.CT_BOOLEAN_TRUE ? true : false);
}
//Save the new field for the next delta computation.
this.lastFieldId_ = fieldId;
return {fname: null, ftype: fieldType, fid: fieldId};
};
TCompactProtocol.prototype.readFieldEnd = function() {
};
TCompactProtocol.prototype.readMapBegin = function() {
var msize = this.readVarint32();
if (msize < 0) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative map size");
}
var kvType = 0;
if (msize !== 0) {
kvType = this.trans.readByte();
}
var keyType = this.getTType((kvType & 0xf0) >>> 4);
var valType = this.getTType(kvType & 0xf);
return {ktype: keyType, vtype: valType, size: msize};
};
TCompactProtocol.prototype.readMapEnd = function() {
};
TCompactProtocol.prototype.readListBegin = function() {
var size_and_type = this.trans.readByte();
var lsize = (size_and_type >>> 4) & 0x0000000f;
if (lsize == 15) {
lsize = this.readVarint32();
}
if (lsize < 0) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative list size");
}
var elemType = this.getTType(size_and_type & 0x0000000f);
return {etype: elemType, size: lsize};
};
TCompactProtocol.prototype.readListEnd = function() {
};
TCompactProtocol.prototype.readSetBegin = function() {
return this.readListBegin();
};
TCompactProtocol.prototype.readSetEnd = function() {
};
TCompactProtocol.prototype.readBool = function() {
var value = false;
var rsize = 0;
if (this.boolValue_.hasBoolValue === true) {
value = this.boolValue_.boolValue;
this.boolValue_.hasBoolValue = false;
} else {
var res = this.trans.readByte();
rsize = res.rsize;
value = (res.value == TCompactProtocol.Types.CT_BOOLEAN_TRUE);
}
return value;
};
TCompactProtocol.prototype.readByte = function() {
return this.trans.readByte();
};
TCompactProtocol.prototype.readI16 = function() {
return this.readI32();
};
TCompactProtocol.prototype.readI32 = function() {
return this.zigzagToI32(this.readVarint32());
};
TCompactProtocol.prototype.readI64 = function() {
return this.zigzagToI64(this.readVarint64());
};
// Little-endian, unlike TBinaryProtocol
TCompactProtocol.prototype.readDouble = function() {
var buff = this.trans.read(8);
var off = 0;
var signed = buff[off + 7] & 0x80;
var e = (buff[off+6] & 0xF0) >> 4;
e += (buff[off+7] & 0x7F) << 4;
var m = buff[off];
m += buff[off+1] << 8;
m += buff[off+2] << 16;
m += buff[off+3] * POW_24;
m += buff[off+4] * POW_32;
m += buff[off+5] * POW_40;
m += (buff[off+6] & 0x0F) * POW_48;
switch (e) {
case 0:
e = -1022;
break;
case 2047:
return m ? NaN : (signed ? -Infinity : Infinity);
default:
m += POW_52;
e -= 1023;
}
if (signed) {
m *= -1;
}
return m * Math.pow(2, e - 52);
};
TCompactProtocol.prototype.readBinary = function() {
var size = this.readVarint32();
if (size === 0) {
return new Buffer(0);
}
if (size < 0) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative binary size");
}
return this.trans.read(size);
};
TCompactProtocol.prototype.readString = function() {
var size = this.readVarint32();
// Catch empty string case
if (size === 0) {
return "";
}
// Catch error cases
if (size < 0) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.NEGATIVE_SIZE, "Negative string size");
}
return this.trans.readString(size);
};
//
// Compact Protocol internal read operations
//
/**
* Read an i32 from the wire as a varint. The MSB of each byte is set
* if there is another byte to follow. This can read up to 5 bytes.
*/
TCompactProtocol.prototype.readVarint32 = function() {
return this.readVarint64().toNumber();
};
/**
* Read an i64 from the wire as a proper varint. The MSB of each byte is set
* if there is another byte to follow. This can read up to 10 bytes.
*/
TCompactProtocol.prototype.readVarint64 = function() {
var rsize = 0;
var lo = 0;
var hi = 0;
var shift = 0;
while (true) {
var b = this.trans.readByte();
rsize ++;
if (shift <= 25) {
lo = lo | ((b & 0x7f) << shift);
} else if (25 < shift && shift < 32) {
lo = lo | ((b & 0x7f) << shift);
hi = hi | ((b & 0x7f) >>> (32-shift));
} else {
hi = hi | ((b & 0x7f) << (shift-32));
}
shift += 7;
if (!(b & 0x80)) {
break;
}
if (rsize >= 10) {
throw new Thrift.TProtocolException(Thrift.TProtocolExceptionType.INVALID_DATA, "Variable-length int over 10 bytes.");
}
}
return new Int64(hi, lo);
};
/**
* Convert from zigzag int to int.
*/
TCompactProtocol.prototype.zigzagToI32 = function(n) {
return (n >>> 1) ^ (-1 * (n & 1));
};
/**
* Convert from zigzag long to long.
*/
TCompactProtocol.prototype.zigzagToI64 = function(n) {
var hi = n.buffer.readUInt32BE(0, true);
var lo = n.buffer.readUInt32BE(4, true);
var neg = new Int64(hi & 0, lo & 1);
neg._2scomp();
var hi_neg = neg.buffer.readUInt32BE(0, true);
var lo_neg = neg.buffer.readUInt32BE(4, true);
var hi_lo = (hi << 31);
hi = (hi >>> 1) ^ (hi_neg);
lo = ((lo >>> 1) | hi_lo) ^ (lo_neg);
return new Int64(hi, lo);
};
TCompactProtocol.prototype.skip = function(type) {
switch (type) {
case Type.STOP:
return;
case Type.BOOL:
this.readBool();
break;
case Type.BYTE:
this.readByte();
break;
case Type.I16:
this.readI16();
break;
case Type.I32:
this.readI32();
break;
case Type.I64:
this.readI64();
break;
case Type.DOUBLE:
this.readDouble();
break;
case Type.STRING:
this.readString();
break;
case Type.STRUCT:
this.readStructBegin();
while (true) {
var r = this.readFieldBegin();
if (r.ftype === Type.STOP) {
break;
}
this.skip(r.ftype);
this.readFieldEnd();
}
this.readStructEnd();
break;
case Type.MAP:
var mapBegin = this.readMapBegin();
for (var i = 0; i < mapBegin.size; ++i) {
this.skip(mapBegin.ktype);
this.skip(mapBegin.vtype);
}
this.readMapEnd();
break;
case Type.SET:
var setBegin = this.readSetBegin();
for (var i2 = 0; i2 < setBegin.size; ++i2) {
this.skip(setBegin.etype);
}
this.readSetEnd();
break;
case Type.LIST:
var listBegin = this.readListBegin();
for (var i3 = 0; i3 < listBegin.size; ++i3) {
this.skip(listBegin.etype);
}
this.readListEnd();
break;
default:
throw new Error("Invalid type: " + type);
}
};

View file

@ -0,0 +1,353 @@
/*
* 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 util = require('util');
var EventEmitter = require("events").EventEmitter;
var net = require('net');
var tls = require('tls');
var thrift = require('./thrift');
var TBufferedTransport = require('./buffered_transport');
var TBinaryProtocol = require('./binary_protocol');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
var createClient = require('./create_client');
var binary = require('./binary');
var Connection = exports.Connection = function(stream, options) {
var self = this;
EventEmitter.call(this);
this.seqId2Service = {};
this.connection = stream;
this.ssl = (stream.encrypted);
this.options = options || {};
this.transport = this.options.transport || TBufferedTransport;
this.protocol = this.options.protocol || TBinaryProtocol;
this.offline_queue = [];
this.connected = false;
this.initialize_retry_vars();
this._debug = this.options.debug || false;
if (this.options.max_attempts &&
!isNaN(this.options.max_attempts) &&
this.options.max_attempts > 0) {
this.max_attempts = +this.options.max_attempts;
}
this.retry_max_delay = null;
if (this.options.retry_max_delay !== undefined &&
!isNaN(this.options.retry_max_delay) &&
this.options.retry_max_delay > 0) {
this.retry_max_delay = this.options.retry_max_delay;
}
this.connect_timeout = false;
if (this.options.connect_timeout &&
!isNaN(this.options.connect_timeout) &&
this.options.connect_timeout > 0) {
this.connect_timeout = +this.options.connect_timeout;
}
this.connection.addListener(this.ssl ? "secureConnect" : "connect", function() {
self.connected = true;
this.setTimeout(self.options.timeout || 0);
this.setNoDelay();
this.frameLeft = 0;
this.framePos = 0;
this.frame = null;
self.initialize_retry_vars();
self.offline_queue.forEach(function(data) {
self.connection.write(data);
});
self.emit("connect");
});
this.connection.addListener("error", function(err) {
// Only emit the error if no-one else is listening on the connection
// or if someone is listening on us, because Node turns unhandled
// 'error' events into exceptions.
if (self.connection.listeners('error').length === 1 ||
self.listeners('error').length > 0) {
self.emit("error", err);
}
});
// Add a close listener
this.connection.addListener("close", function() {
self.connection_gone(); // handle close event. try to reconnect
});
this.connection.addListener("timeout", function() {
self.emit("timeout");
});
this.connection.addListener("data", self.transport.receiver(function(transport_with_data) {
var message = new self.protocol(transport_with_data);
try {
while (true) {
var header = message.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 connection.client object 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 callback = client._reqs[header.rseqid];
delete client._reqs[header.rseqid];
if (callback) {
callback(err, success);
}
};
/*jshint +W083 */
if(client['recv_' + header.fname]) {
client['recv_' + header.fname](message, header.mtype, dummy_seqid);
} else {
delete client._reqs[dummy_seqid];
self.emit("error",
new thrift.TApplicationException(thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
"Received a response to an unknown RPC function"));
}
}
}
catch (e) {
if (e instanceof InputBufferUnderrunError) {
transport_with_data.rollbackPosition();
}
else {
self.emit('error', e);
}
}
}));
};
util.inherits(Connection, EventEmitter);
Connection.prototype.end = function() {
this.connection.end();
};
Connection.prototype.destroy = function() {
this.connection.destroy();
};
Connection.prototype.initialize_retry_vars = function () {
this.retry_timer = null;
this.retry_totaltime = 0;
this.retry_delay = 150;
this.retry_backoff = 1.7;
this.attempts = 0;
};
Connection.prototype.write = function(data) {
if (!this.connected) {
this.offline_queue.push(data);
return;
}
this.connection.write(data);
};
Connection.prototype.connection_gone = function () {
var self = this;
this.connected = false;
// If a retry is already in progress, just let that happen
if (this.retry_timer) {
return;
}
// We cannot reconnect a secure socket.
if (!this.max_attempts || this.ssl) {
self.emit("close");
return;
}
if (this.retry_max_delay !== null && this.retry_delay >= this.retry_max_delay) {
this.retry_delay = this.retry_max_delay;
} else {
this.retry_delay = Math.floor(this.retry_delay * this.retry_backoff);
}
if (self._debug) {
console.log("Retry connection in " + this.retry_delay + " ms");
}
if (this.max_attempts && this.attempts >= this.max_attempts) {
this.retry_timer = null;
console.error("thrift: Couldn't get thrift connection after " + this.max_attempts + " attempts.");
self.emit("close");
return;
}
this.attempts += 1;
this.emit("reconnecting", {
delay: self.retry_delay,
attempt: self.attempts
});
this.retry_timer = setTimeout(function () {
if (self._debug) {
console.log("Retrying connection...");
}
self.retry_totaltime += self.retry_delay;
if (self.connect_timeout && self.retry_totaltime >= self.connect_timeout) {
self.retry_timer = null;
console.error("thrift: Couldn't get thrift connection after " + self.retry_totaltime + "ms.");
self.emit("close");
return;
}
self.connection.connect(self.port, self.host);
self.retry_timer = null;
}, this.retry_delay);
};
exports.createConnection = function(host, port, options) {
var stream = net.createConnection(port, host);
var connection = new Connection(stream, options);
connection.host = host;
connection.port = port;
return connection;
};
exports.createSSLConnection = function(host, port, options) {
var stream = tls.connect(port, host, options);
var connection = new Connection(stream, options);
connection.host = host;
connection.port = port;
return connection;
};
exports.createClient = createClient;
var child_process = require('child_process');
var StdIOConnection = exports.StdIOConnection = function(command, options) {
var command_parts = command.split(' ');
command = command_parts[0];
var args = command_parts.splice(1,command_parts.length -1);
var child = this.child = child_process.spawn(command,args);
var self = this;
EventEmitter.call(this);
this._debug = options.debug || false;
this.connection = child.stdin;
this.options = options || {};
this.transport = this.options.transport || TBufferedTransport;
this.protocol = this.options.protocol || TBinaryProtocol;
this.offline_queue = [];
if(this._debug === true){
this.child.stderr.on('data',function(err){
console.log(err.toString(),'CHILD ERROR');
});
this.child.on('exit',function(code,signal){
console.log(code+':'+signal,'CHILD EXITED');
});
}
this.frameLeft = 0;
this.framePos = 0;
this.frame = null;
this.connected = true;
self.offline_queue.forEach(function(data) {
self.connection.write(data);
});
this.connection.addListener("error", function(err) {
self.emit("error", err);
});
// Add a close listener
this.connection.addListener("close", function() {
self.emit("close");
});
child.stdout.addListener("data", self.transport.receiver(function(transport_with_data) {
var message = new self.protocol(transport_with_data);
try {
var header = message.readMessageBegin();
var dummy_seqid = header.rseqid * -1;
var client = self.client;
client._reqs[dummy_seqid] = function(err, success){
transport_with_data.commitPosition();
var callback = client._reqs[header.rseqid];
delete client._reqs[header.rseqid];
if (callback) {
callback(err, success);
}
};
client['recv_' + header.fname](message, header.mtype, dummy_seqid);
}
catch (e) {
if (e instanceof InputBufferUnderrunError) {
transport_with_data.rollbackPosition();
}
else {
throw e;
}
}
}));
};
util.inherits(StdIOConnection, EventEmitter);
StdIOConnection.prototype.end = function() {
this.connection.end();
};
StdIOConnection.prototype.write = function(data) {
if (!this.connected) {
this.offline_queue.push(data);
return;
}
this.connection.write(data);
};
exports.createStdIOConnection = function(command,options){
return new StdIOConnection(command,options);
};
exports.createStdIOClient = createClient;

View file

@ -0,0 +1,54 @@
/*
* 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.
*/
module.exports = createClient;
/**
* Creates a new client object for the specified Thrift service.
* @param {object} ServiceClient - The module containing the generated service client
* @param {Connection} Connection - The connection to use.
* @returns {object} The client object.
*/
function createClient(ServiceClient, connection) {
// TODO validate required options and throw otherwise
if (ServiceClient.Client) {
ServiceClient = ServiceClient.Client;
}
// TODO detangle these initialization calls
// creating "client" requires
// - new service client instance
//
// New service client instance requires
// - new transport instance
// - protocol class reference
//
// New transport instance requires
// - Buffer to use (or none)
// - Callback to call on flush
// Wrap the write method
var writeCb = function(buf, seqid) {
connection.write(buf, seqid);
};
var transport = new connection.transport(undefined, writeCb);
var client = new ServiceClient(transport, connection.protocol);
transport.client = client;
connection.client = client;
return client;
};

View file

@ -0,0 +1,182 @@
/*
* 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 binary = require('./binary');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
module.exports = TFramedTransport;
function TFramedTransport(buffer, callback) {
this.inBuf = buffer || new Buffer(0);
this.outBuffers = [];
this.outCount = 0;
this.readPos = 0;
this.onFlush = callback;
};
TFramedTransport.receiver = function(callback, seqid) {
var residual = null;
return function(data) {
// Prepend any residual data from our previous read
if (residual) {
data = Buffer.concat([residual, data]);
residual = null;
}
// framed transport
while (data.length) {
if (data.length < 4) {
// Not enough bytes to continue, save and resume on next packet
residual = data;
return;
}
var frameSize = binary.readI32(data, 0);
if (data.length < 4 + frameSize) {
// Not enough bytes to continue, save and resume on next packet
residual = data;
return;
}
var frame = data.slice(4, 4 + frameSize);
residual = data.slice(4 + frameSize);
callback(new TFramedTransport(frame), seqid);
data = residual;
residual = null;
}
};
};
TFramedTransport.prototype.commitPosition = function(){},
TFramedTransport.prototype.rollbackPosition = function(){},
// TODO: Implement open/close support
TFramedTransport.prototype.isOpen = function() {
return true;
};
TFramedTransport.prototype.open = function() {};
TFramedTransport.prototype.close = function() {};
// Set the seqid of the message in the client
// So that callbacks can be found
TFramedTransport.prototype.setCurrSeqId = function(seqid) {
this._seqid = seqid;
};
TFramedTransport.prototype.ensureAvailable = function(len) {
if (this.readPos + len > this.inBuf.length) {
throw new InputBufferUnderrunError();
}
};
TFramedTransport.prototype.read = function(len) { // this function will be used for each frames.
this.ensureAvailable(len);
var end = this.readPos + len;
if (this.inBuf.length < end) {
throw new Error('read(' + len + ') failed - not enough data');
}
var buf = this.inBuf.slice(this.readPos, end);
this.readPos = end;
return buf;
};
TFramedTransport.prototype.readByte = function() {
this.ensureAvailable(1);
return binary.readByte(this.inBuf[this.readPos++]);
};
TFramedTransport.prototype.readI16 = function() {
this.ensureAvailable(2);
var i16 = binary.readI16(this.inBuf, this.readPos);
this.readPos += 2;
return i16;
};
TFramedTransport.prototype.readI32 = function() {
this.ensureAvailable(4);
var i32 = binary.readI32(this.inBuf, this.readPos);
this.readPos += 4;
return i32;
};
TFramedTransport.prototype.readDouble = function() {
this.ensureAvailable(8);
var d = binary.readDouble(this.inBuf, this.readPos);
this.readPos += 8;
return d;
};
TFramedTransport.prototype.readString = function(len) {
this.ensureAvailable(len);
var str = this.inBuf.toString('utf8', this.readPos, this.readPos + len);
this.readPos += len;
return str;
};
TFramedTransport.prototype.borrow = function() {
return {
buf: this.inBuf,
readIndex: this.readPos,
writeIndex: this.inBuf.length
};
};
TFramedTransport.prototype.consume = function(bytesConsumed) {
this.readPos += bytesConsumed;
};
TFramedTransport.prototype.write = function(buf, encoding) {
if (typeof(buf) === "string") {
buf = new Buffer(buf, encoding || 'utf8');
}
this.outBuffers.push(buf);
this.outCount += buf.length;
};
TFramedTransport.prototype.flush = function() {
// If the seqid of the callback is available pass it to the onFlush
// Then remove the current seqid
var seqid = this._seqid;
this._seqid = null;
var out = new Buffer(this.outCount),
pos = 0;
this.outBuffers.forEach(function(buf) {
buf.copy(out, pos, 0);
pos += buf.length;
});
if (this.onFlush) {
// TODO: optimize this better, allocate one buffer instead of both:
var msg = new Buffer(out.length + 4);
binary.writeI32(msg, out.length);
out.copy(msg, 4, 0, out.length);
if (this.onFlush) {
// Passing seqid through this call to get it to the connection
this.onFlush(msg, seqid);
}
}
this.outBuffers = [];
this.outCount = 0;
};

View file

@ -0,0 +1,238 @@
/*
* 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 util = require('util');
var http = require('http');
var https = require('https');
var EventEmitter = require('events').EventEmitter;
var thrift = require('./thrift');
var TBufferedTransport = require('./buffered_transport');
var TBinaryProtocol = require('./binary_protocol');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
var createClient = require('./create_client');
/**
* @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 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 connection 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 || TBufferedTransport;
this.protocol = this.options.protocol || TBinaryProtocol;
//Prepare Node.js options
this.nodeOptions = {
host: this.host,
port: this.port || 80,
path: this.options.path || '/',
method: 'POST',
headers: this.options.headers || {},
responseType: this.options.responseType || null
};
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
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) {
process.nextTick(function() {
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];
self.emit("error",
new thrift.TApplicationException(
thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
"Received a response to an unknown RPC function"));
}
}
}
catch (e) {
if (e instanceof InputBufferUnderrunError) {
transport_with_data.rollbackPosition();
} else {
self.emit('error', e);
}
}
}
//Response handler
//////////////////////////////////////////////////
this.responseCallback = function(response) {
var data = [];
var dataLen = 0;
response.on('error', function (e) {
self.emit("error", e);
});
// When running directly under node, chunk will be a buffer,
// however, when running in a Browser (e.g. Browserify), chunk
// will be a string or an ArrayBuffer.
response.on('data', function (chunk) {
if ((typeof chunk == 'string') ||
(Object.prototype.toString.call(chunk) == '[object Uint8Array]')) {
// Wrap ArrayBuffer/string in a Buffer so data[i].copy will work
data.push(new Buffer(chunk));
} else {
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 the receiver function for the transport and
// call it with the buffer
self.transport.receiver(decodeCallback)(buf);
});
};
};
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 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);
};
exports.createHttpClient = createClient

View file

@ -0,0 +1,67 @@
/*
* 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.
*/
exports.Thrift = require('./thrift');
var connection = require('./connection');
exports.Connection = connection.Connection;
exports.createClient = connection.createClient;
exports.createConnection = connection.createConnection;
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 wsConnection = require('./ws_connection');
exports.WSConnection = wsConnection.WSConnection;
exports.createWSConnection = wsConnection.createWSConnection;
exports.createWSClient = wsConnection.createWSClient;
var xhrConnection = require('./xhr_connection');
exports.XHRConnection = xhrConnection.XHRConnection;
exports.createXHRConnection = xhrConnection.createXHRConnection;
exports.createXHRClient = xhrConnection.createXHRClient;
var server = require('./server');
exports.createServer = server.createServer;
exports.createMultiplexServer = server.createMultiplexServer;
var web_server = require('./web_server');
exports.createWebServer = web_server.createWebServer;
exports.Int64 = require('node-int64');
exports.Q = require('q');
var mprocessor = require('./multiplexed_processor');
var mprotocol = require('./multiplexed_protocol');
exports.Multiplexer = mprotocol.Multiplexer;
exports.MultiplexedProcessor = mprocessor.MultiplexedProcessor;
/*
* Export transport and protocol so they can be used outside of a
* cassandra/server context
*/
exports.TFramedTransport = require('./framed_transport');
exports.TBufferedTransport = require('./buffered_transport');
exports.TBinaryProtocol = require('./binary_protocol');
exports.TJSONProtocol = require('./json_protocol');
exports.TCompactProtocol = require('./compact_protocol');

View file

@ -0,0 +1,30 @@
/*
* 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 util = require("util");
module.exports = InputBufferUnderrunError;
function InputBufferUnderrunError(message) {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = message;
};
util.inherits(InputBufferUnderrunError, Error);

View file

@ -0,0 +1,91 @@
/*
* 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 Int64 = require('node-int64');
var Int64Util = module.exports = {};
var POW2_24 = Math.pow(2, 24);
var POW2_31 = Math.pow(2, 31);
var POW2_32 = Math.pow(2, 32);
var POW10_11 = Math.pow(10, 11);
Int64Util.toDecimalString = function(i64) {
var b = i64.buffer;
var o = i64.offset;
if ((!b[o] && !(b[o + 1] & 0xe0)) ||
(!~b[o] && !~(b[o + 1] & 0xe0))) {
// The magnitude is small enough.
return i64.toString();
} else {
var negative = b[o] & 0x80;
if (negative) {
// 2's complement
var incremented = false;
var buffer = new Buffer(8);
for (var i = 7; i >= 0; --i) {
buffer[i] = (~b[o + i] + (incremented ? 0 : 1)) & 0xff;
incremented |= b[o + i];
}
b = buffer;
}
var high2 = b[o + 1] + (b[o] << 8);
// Lesser 11 digits with exceeding values but is under 53 bits capacity.
var low = b[o + 7] + (b[o + 6] << 8) + (b[o + 5] << 16)
+ b[o + 4] * POW2_24 // Bit shift renders 32th bit as sign, so use multiplication
+ (b[o + 3] + (b[o + 2] << 8)) * POW2_32 + high2 * 74976710656; // The literal is 2^48 % 10^11
// 12th digit and greater.
var high = Math.floor(low / POW10_11) + high2 * 2814; // The literal is 2^48 / 10^11
// Make it exactly 11 with leading zeros.
low = ('00000000000' + String(low % POW10_11)).slice(-11);
return (negative ? '-' : '') + String(high) + low;
}
};
Int64Util.fromDecimalString = function(text) {
var negative = text.charAt(0) === '-';
if (text.length < (negative ? 17 : 16)) {
// The magnitude is smaller than 2^53.
return new Int64(+text);
} else if (text.length > (negative ? 20 : 19)) {
throw new RangeError('Too many digits for Int64: ' + text);
} else {
// Most significant (up to 5) digits
var high5 = +text.slice(negative ? 1 : 0, -15);
var low = +text.slice(-15) + high5 * 2764472320; // The literal is 10^15 % 2^32
var high = Math.floor(low / POW2_32) + high5 * 232830; // The literal is 10^15 / 2^&32
low = low % POW2_32;
if (high >= POW2_31 &&
!(negative && high == POW2_31 && low == 0) // Allow minimum Int64
) {
throw new RangeError('The magnitude is too large for Int64.');
}
if (negative) {
// 2's complement
high = ~high;
if (low === 0) {
high = (high + 1) & 0xffffffff;
} else {
low = ~low + 1;
}
high = 0x80000000 | high;
}
return new Int64(high, low);
}
};

View file

@ -0,0 +1,299 @@
/*
* Imported from Douglas Crockford's reference implementation with minimum modification
* to handle Int64.
*
* https://github.com/douglascrockford/JSON-js/blob/c98948ae1944a28e2e8ebc3717894e580aeaaa05/json_parse.js
*
* Original license header:
*
* json_parse.js
* 2015-05-02
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
/*jslint for */
/*property
at, b, call, charAt, f, fromCharCode, hasOwnProperty, message, n, name,
prototype, push, r, t, text
*/
var Int64 = require('node-int64');
var Int64Util = require('./int64_util');
var json_parse = module.exports = (function () {
"use strict";
// This is a function that can parse a JSON text, producing a JavaScript
// data structure. It is a simple, recursive descent parser. It does not use
// eval or regular expressions, so it can be used as a model for implementing
// a JSON parser in other languages.
// We are defining the function inside of another function to avoid creating
// global variables.
var at, // The index of the current character
ch, // The current character
escapee = {
'"': '"',
'\\': '\\',
'/': '/',
b: '\b',
f: '\f',
n: '\n',
r: '\r',
t: '\t'
},
text,
error = function (m) {
// Call error when something is wrong.
throw new SyntaxError(m);
},
next = function (c) {
// If a c parameter is provided, verify that it matches the current character.
if (c && c !== ch) {
error("Expected '" + c + "' instead of '" + ch + "'");
}
// Get the next character. When there are no more characters,
// return the empty string.
ch = text.charAt(at);
at += 1;
return ch;
},
number = function () {
// Parse a number value.
var number,
string = '';
if (ch === '-') {
string = '-';
next('-');
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
if (ch === '.') {
string += '.';
while (next() && ch >= '0' && ch <= '9') {
string += ch;
}
}
if (ch === 'e' || ch === 'E') {
string += ch;
next();
if (ch === '-' || ch === '+') {
string += ch;
next();
}
while (ch >= '0' && ch <= '9') {
string += ch;
next();
}
}
number = +string;
if (!isFinite(number)) {
error("Bad number");
} else if (number >= Int64.MAX_INT || number <= Int64.MIN_INT) {
// Return raw string for further process in TJSONProtocol
return string;
} else {
return number;
}
},
string = function () {
// Parse a string value.
var hex,
i,
string = '',
uffff;
// When parsing for string values, we must look for " and \ characters.
if (ch === '"') {
while (next()) {
if (ch === '"') {
next();
return string;
}
if (ch === '\\') {
next();
if (ch === 'u') {
uffff = 0;
for (i = 0; i < 4; i += 1) {
hex = parseInt(next(), 16);
if (!isFinite(hex)) {
break;
}
uffff = uffff * 16 + hex;
}
string += String.fromCharCode(uffff);
} else if (typeof escapee[ch] === 'string') {
string += escapee[ch];
} else {
break;
}
} else {
string += ch;
}
}
}
error("Bad string");
},
white = function () {
// Skip whitespace.
while (ch && ch <= ' ') {
next();
}
},
word = function () {
// true, false, or null.
switch (ch) {
case 't':
next('t');
next('r');
next('u');
next('e');
return true;
case 'f':
next('f');
next('a');
next('l');
next('s');
next('e');
return false;
case 'n':
next('n');
next('u');
next('l');
next('l');
return null;
}
error("Unexpected '" + ch + "'");
},
value, // Place holder for the value function.
array = function () {
// Parse an array value.
var array = [];
if (ch === '[') {
next('[');
white();
if (ch === ']') {
next(']');
return array; // empty array
}
while (ch) {
array.push(value());
white();
if (ch === ']') {
next(']');
return array;
}
next(',');
white();
}
}
error("Bad array");
},
object = function () {
// Parse an object value.
var key,
object = {};
if (ch === '{') {
next('{');
white();
if (ch === '}') {
next('}');
return object; // empty object
}
while (ch) {
key = string();
white();
next(':');
if (Object.hasOwnProperty.call(object, key)) {
error('Duplicate key "' + key + '"');
}
object[key] = value();
white();
if (ch === '}') {
next('}');
return object;
}
next(',');
white();
}
}
error("Bad object");
};
value = function () {
// Parse a JSON value. It could be an object, an array, a string, a number,
// or a word.
white();
switch (ch) {
case '{':
return object();
case '[':
return array();
case '"':
return string();
case '-':
return number();
default:
return ch >= '0' && ch <= '9'
? number()
: word();
}
};
// Return the json_parse function. It will have access to all of the above
// functions and variables.
return function (source) {
var result;
text = source;
at = 0;
ch = ' ';
result = value();
white();
if (ch) {
error("Syntax error");
}
return result;
};
}());

View file

@ -0,0 +1,743 @@
/*
* 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 log = require('./log');
var Int64 = require('node-int64');
var InputBufferUnderrunError = require('./transport').InputBufferUnderrunError;
var Thrift = require('./thrift');
var Type = Thrift.Type;
var util = require("util");
var Int64Util = require('./int64_util');
var json_parse = require('./json_parse');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
module.exports = TJSONProtocol;
/**
* Initializes a Thrift JSON protocol instance.
* @constructor
* @param {Thrift.Transport} trans - The transport to serialize to/from.
* @classdesc Apache Thrift Protocols perform serialization which enables cross
* language RPC. The Protocol type is the JavaScript browser implementation
* of the Apache Thrift TJSONProtocol.
* @example
* var protocol = new Thrift.Protocol(transport);
*/
function TJSONProtocol(trans) {
this.tstack = [];
this.tpos = [];
this.trans = trans;
};
/**
* Thrift IDL type Id to string mapping.
* @readonly
* @see {@link Thrift.Type}
*/
TJSONProtocol.Type = {};
TJSONProtocol.Type[Type.BOOL] = '"tf"';
TJSONProtocol.Type[Type.BYTE] = '"i8"';
TJSONProtocol.Type[Type.I16] = '"i16"';
TJSONProtocol.Type[Type.I32] = '"i32"';
TJSONProtocol.Type[Type.I64] = '"i64"';
TJSONProtocol.Type[Type.DOUBLE] = '"dbl"';
TJSONProtocol.Type[Type.STRUCT] = '"rec"';
TJSONProtocol.Type[Type.STRING] = '"str"';
TJSONProtocol.Type[Type.MAP] = '"map"';
TJSONProtocol.Type[Type.LIST] = '"lst"';
TJSONProtocol.Type[Type.SET] = '"set"';
/**
* Thrift IDL type string to Id mapping.
* @readonly
* @see {@link Thrift.Type}
*/
TJSONProtocol.RType = {};
TJSONProtocol.RType.tf = Type.BOOL;
TJSONProtocol.RType.i8 = Type.BYTE;
TJSONProtocol.RType.i16 = Type.I16;
TJSONProtocol.RType.i32 = Type.I32;
TJSONProtocol.RType.i64 = Type.I64;
TJSONProtocol.RType.dbl = Type.DOUBLE;
TJSONProtocol.RType.rec = Type.STRUCT;
TJSONProtocol.RType.str = Type.STRING;
TJSONProtocol.RType.map = Type.MAP;
TJSONProtocol.RType.lst = Type.LIST;
TJSONProtocol.RType.set = Type.SET;
/**
* The TJSONProtocol version number.
* @readonly
* @const {number} Version
* @memberof Thrift.Protocol
*/
TJSONProtocol.Version = 1;
TJSONProtocol.prototype.flush = function() {
this.writeToTransportIfStackIsFlushable();
return this.trans.flush();
};
TJSONProtocol.prototype.writeToTransportIfStackIsFlushable = function() {
if (this.tstack.length === 1) {
this.trans.write(this.tstack.pop());
}
};
/**
* Serializes the beginning of a Thrift RPC message.
* @param {string} name - The service method to call.
* @param {Thrift.MessageType} messageType - The type of method call.
* @param {number} seqid - The sequence number of this call (always 0 in Apache Thrift).
*/
TJSONProtocol.prototype.writeMessageBegin = function(name, messageType, seqid) {
this.tstack.push([TJSONProtocol.Version, '"' + name + '"', messageType, seqid]);
};
/**
* Serializes the end of a Thrift RPC message.
*/
TJSONProtocol.prototype.writeMessageEnd = function() {
var obj = this.tstack.pop();
this.wobj = this.tstack.pop();
this.wobj.push(obj);
this.wbuf = '[' + this.wobj.join(',') + ']';
// we assume there is nothing more to come so we write
this.trans.write(this.wbuf);
};
/**
* Serializes the beginning of a struct.
* @param {string} name - The name of the struct.
*/
TJSONProtocol.prototype.writeStructBegin = function(name) {
this.tpos.push(this.tstack.length);
this.tstack.push({});
};
/**
* Serializes the end of a struct.
*/
TJSONProtocol.prototype.writeStructEnd = function() {
var p = this.tpos.pop();
var struct = this.tstack[p];
var str = '{';
var first = true;
for (var key in struct) {
if (first) {
first = false;
} else {
str += ',';
}
str += key + ':' + struct[key];
}
str += '}';
this.tstack[p] = str;
this.writeToTransportIfStackIsFlushable();
};
/**
* Serializes the beginning of a struct field.
* @param {string} name - The name of the field.
* @param {Thrift.Protocol.Type} fieldType - The data type of the field.
* @param {number} fieldId - The field's unique identifier.
*/
TJSONProtocol.prototype.writeFieldBegin = function(name, fieldType, fieldId) {
this.tpos.push(this.tstack.length);
this.tstack.push({ 'fieldId': '"' +
fieldId + '"', 'fieldType': TJSONProtocol.Type[fieldType]
});
};
/**
* Serializes the end of a field.
*/
TJSONProtocol.prototype.writeFieldEnd = function() {
var value = this.tstack.pop();
var fieldInfo = this.tstack.pop();
if (':' + value === ":[object Object]") {
this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
fieldInfo.fieldType + ':' + JSON.stringify(value) + '}';
} else {
this.tstack[this.tstack.length - 1][fieldInfo.fieldId] = '{' +
fieldInfo.fieldType + ':' + value + '}';
}
this.tpos.pop();
this.writeToTransportIfStackIsFlushable();
};
/**
* Serializes the end of the set of fields for a struct.
*/
TJSONProtocol.prototype.writeFieldStop = function() {
};
/**
* Serializes the beginning of a map collection.
* @param {Thrift.Type} keyType - The data type of the key.
* @param {Thrift.Type} valType - The data type of the value.
* @param {number} [size] - The number of elements in the map (ignored).
*/
TJSONProtocol.prototype.writeMapBegin = function(keyType, valType, size) {
//size is invalid, we'll set it on end.
this.tpos.push(this.tstack.length);
this.tstack.push([TJSONProtocol.Type[keyType], TJSONProtocol.Type[valType], 0]);
};
/**
* Serializes the end of a map.
*/
TJSONProtocol.prototype.writeMapEnd = function() {
var p = this.tpos.pop();
if (p == this.tstack.length) {
return;
}
if ((this.tstack.length - p - 1) % 2 !== 0) {
this.tstack.push('');
}
var size = (this.tstack.length - p - 1) / 2;
this.tstack[p][this.tstack[p].length - 1] = size;
var map = '}';
var first = true;
while (this.tstack.length > p + 1) {
var v = this.tstack.pop();
var k = this.tstack.pop();
if (first) {
first = false;
} else {
map = ',' + map;
}
if (! isNaN(k)) { k = '"' + k + '"'; } //json "keys" need to be strings
map = k + ':' + v + map;
}
map = '{' + map;
this.tstack[p].push(map);
this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
this.writeToTransportIfStackIsFlushable();
};
/**
* Serializes the beginning of a list collection.
* @param {Thrift.Type} elemType - The data type of the elements.
* @param {number} size - The number of elements in the list.
*/
TJSONProtocol.prototype.writeListBegin = function(elemType, size) {
this.tpos.push(this.tstack.length);
this.tstack.push([TJSONProtocol.Type[elemType], size]);
};
/**
* Serializes the end of a list.
*/
TJSONProtocol.prototype.writeListEnd = function() {
var p = this.tpos.pop();
while (this.tstack.length > p + 1) {
var tmpVal = this.tstack[p + 1];
this.tstack.splice(p + 1, 1);
this.tstack[p].push(tmpVal);
}
this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
this.writeToTransportIfStackIsFlushable();
};
/**
* Serializes the beginning of a set collection.
* @param {Thrift.Type} elemType - The data type of the elements.
* @param {number} size - The number of elements in the list.
*/
TJSONProtocol.prototype.writeSetBegin = function(elemType, size) {
this.tpos.push(this.tstack.length);
this.tstack.push([TJSONProtocol.Type[elemType], size]);
};
/**
* Serializes the end of a set.
*/
TJSONProtocol.prototype.writeSetEnd = function() {
var p = this.tpos.pop();
while (this.tstack.length > p + 1) {
var tmpVal = this.tstack[p + 1];
this.tstack.splice(p + 1, 1);
this.tstack[p].push(tmpVal);
}
this.tstack[p] = '[' + this.tstack[p].join(',') + ']';
this.writeToTransportIfStackIsFlushable();
};
/** Serializes a boolean */
TJSONProtocol.prototype.writeBool = function(bool) {
this.tstack.push(bool ? 1 : 0);
};
/** Serializes a number */
TJSONProtocol.prototype.writeByte = function(byte) {
this.tstack.push(byte);
};
/** Serializes a number */
TJSONProtocol.prototype.writeI16 = function(i16) {
this.tstack.push(i16);
};
/** Serializes a number */
TJSONProtocol.prototype.writeI32 = function(i32) {
this.tstack.push(i32);
};
/** Serializes a number */
TJSONProtocol.prototype.writeI64 = function(i64) {
if (i64 instanceof Int64) {
this.tstack.push(Int64Util.toDecimalString(i64));
} else {
this.tstack.push(i64);
}
};
/** Serializes a number */
TJSONProtocol.prototype.writeDouble = function(dub) {
this.tstack.push(dub);
};
/** Serializes a string */
TJSONProtocol.prototype.writeString = function(arg) {
// We do not encode uri components for wire transfer:
if (arg === null) {
this.tstack.push(null);
} else {
if (typeof arg === 'string') {
var str = arg;
} else if (arg instanceof Buffer) {
var str = arg.toString('utf8');
} else {
throw new Error('writeString called without a string/Buffer argument: ' + arg);
}
// concat may be slower than building a byte buffer
var escapedString = '';
for (var i = 0; i < str.length; i++) {
var ch = str.charAt(i); // a single double quote: "
if (ch === '\"') {
escapedString += '\\\"'; // write out as: \"
} else if (ch === '\\') { // a single backslash: \
escapedString += '\\\\'; // write out as: \\
/* Currently escaped forward slashes break TJSONProtocol.
* As it stands, we can simply pass forward slashes into
* our strings across the wire without being escaped.
* I think this is the protocol's bug, not thrift.js
* } else if(ch === '/') { // a single forward slash: /
* escapedString += '\\/'; // write out as \/
* }
*/
} else if (ch === '\b') { // a single backspace: invisible
escapedString += '\\b'; // write out as: \b"
} else if (ch === '\f') { // a single formfeed: invisible
escapedString += '\\f'; // write out as: \f"
} else if (ch === '\n') { // a single newline: invisible
escapedString += '\\n'; // write out as: \n"
} else if (ch === '\r') { // a single return: invisible
escapedString += '\\r'; // write out as: \r"
} else if (ch === '\t') { // a single tab: invisible
escapedString += '\\t'; // write out as: \t"
} else {
escapedString += ch; // Else it need not be escaped
}
}
this.tstack.push('"' + escapedString + '"');
}
};
/** Serializes a string */
TJSONProtocol.prototype.writeBinary = function(arg) {
if (typeof arg === 'string') {
var buf = new Buffer(arg, 'binary');
} else if (arg instanceof Buffer ||
Object.prototype.toString.call(arg) == '[object Uint8Array]') {
var buf = arg;
} else {
throw new Error('writeBinary called without a string/Buffer argument: ' + arg);
}
this.tstack.push('"' + buf.toString('base64') + '"');
};
/**
* @class
* @name AnonReadMessageBeginReturn
* @property {string} fname - The name of the service method.
* @property {Thrift.MessageType} mtype - The type of message call.
* @property {number} rseqid - The sequence number of the message (0 in Thrift RPC).
*/
/**
* Deserializes the beginning of a message.
* @returns {AnonReadMessageBeginReturn}
*/
TJSONProtocol.prototype.readMessageBegin = function() {
this.rstack = [];
this.rpos = [];
//Borrow the inbound transport buffer and ensure data is present/consistent
var transBuf = this.trans.borrow();
if (transBuf.readIndex >= transBuf.writeIndex) {
throw new InputBufferUnderrunError();
}
var cursor = transBuf.readIndex;
if (transBuf.buf[cursor] !== 0x5B) { //[
throw new Error("Malformed JSON input, no opening bracket");
}
//Parse a single message (there may be several in the buffer)
// TODO: Handle characters using multiple code units
cursor++;
var openBracketCount = 1;
var inString = false;
for (; cursor < transBuf.writeIndex; cursor++) {
var chr = transBuf.buf[cursor];
//we use hexa charcode here because data[i] returns an int and not a char
if (inString) {
if (chr === 0x22) { //"
inString = false;
} else if (chr === 0x5C) { //\
//escaped character, skip
cursor += 1;
}
} else {
if (chr === 0x5B) { //[
openBracketCount += 1;
} else if (chr === 0x5D) { //]
openBracketCount -= 1;
if (openBracketCount === 0) {
//end of json message detected
break;
}
} else if (chr === 0x22) { //"
inString = true;
}
}
}
if (openBracketCount !== 0) {
// Missing closing bracket. Can be buffer underrun.
throw new InputBufferUnderrunError();
}
//Reconstitute the JSON object and conume the necessary bytes
this.robj = json_parse(transBuf.buf.slice(transBuf.readIndex, cursor+1).toString());
this.trans.consume(cursor + 1 - transBuf.readIndex);
//Verify the protocol version
var version = this.robj.shift();
if (version != TJSONProtocol.Version) {
throw new Error('Wrong thrift protocol version: ' + version);
}
//Objectify the thrift message {name/type/sequence-number} for return
// and then save the JSON object in rstack
var r = {};
r.fname = this.robj.shift();
r.mtype = this.robj.shift();
r.rseqid = this.robj.shift();
this.rstack.push(this.robj.shift());
return r;
};
/** Deserializes the end of a message. */
TJSONProtocol.prototype.readMessageEnd = function() {
};
/**
* Deserializes the beginning of a struct.
* @param {string} [name] - The name of the struct (ignored)
* @returns {object} - An object with an empty string fname property
*/
TJSONProtocol.prototype.readStructBegin = function() {
var r = {};
r.fname = '';
//incase this is an array of structs
if (this.rstack[this.rstack.length - 1] instanceof Array) {
this.rstack.push(this.rstack[this.rstack.length - 1].shift());
}
return r;
};
/** Deserializes the end of a struct. */
TJSONProtocol.prototype.readStructEnd = function() {
this.rstack.pop();
};
/**
* @class
* @name AnonReadFieldBeginReturn
* @property {string} fname - The name of the field (always '').
* @property {Thrift.Type} ftype - The data type of the field.
* @property {number} fid - The unique identifier of the field.
*/
/**
* Deserializes the beginning of a field.
* @returns {AnonReadFieldBeginReturn}
*/
TJSONProtocol.prototype.readFieldBegin = function() {
var r = {};
var fid = -1;
var ftype = Type.STOP;
//get a fieldId
for (var f in (this.rstack[this.rstack.length - 1])) {
if (f === null) {
continue;
}
fid = parseInt(f, 10);
this.rpos.push(this.rstack.length);
var field = this.rstack[this.rstack.length - 1][fid];
//remove so we don't see it again
delete this.rstack[this.rstack.length - 1][fid];
this.rstack.push(field);
break;
}
if (fid != -1) {
//should only be 1 of these but this is the only
//way to match a key
for (var i in (this.rstack[this.rstack.length - 1])) {
if (TJSONProtocol.RType[i] === null) {
continue;
}
ftype = TJSONProtocol.RType[i];
this.rstack[this.rstack.length - 1] = this.rstack[this.rstack.length - 1][i];
}
}
r.fname = '';
r.ftype = ftype;
r.fid = fid;
return r;
};
/** Deserializes the end of a field. */
TJSONProtocol.prototype.readFieldEnd = function() {
var pos = this.rpos.pop();
//get back to the right place in the stack
while (this.rstack.length > pos) {
this.rstack.pop();
}
};
/**
* @class
* @name AnonReadMapBeginReturn
* @property {Thrift.Type} ktype - The data type of the key.
* @property {Thrift.Type} vtype - The data type of the value.
* @property {number} size - The number of elements in the map.
*/
/**
* Deserializes the beginning of a map.
* @returns {AnonReadMapBeginReturn}
*/
TJSONProtocol.prototype.readMapBegin = function() {
var map = this.rstack.pop();
var first = map.shift();
if (first instanceof Array) {
this.rstack.push(map);
map = first;
first = map.shift();
}
var r = {};
r.ktype = TJSONProtocol.RType[first];
r.vtype = TJSONProtocol.RType[map.shift()];
r.size = map.shift();
this.rpos.push(this.rstack.length);
this.rstack.push(map.shift());
return r;
};
/** Deserializes the end of a map. */
TJSONProtocol.prototype.readMapEnd = function() {
this.readFieldEnd();
};
/**
* @class
* @name AnonReadColBeginReturn
* @property {Thrift.Type} etype - The data type of the element.
* @property {number} size - The number of elements in the collection.
*/
/**
* Deserializes the beginning of a list.
* @returns {AnonReadColBeginReturn}
*/
TJSONProtocol.prototype.readListBegin = function() {
var list = this.rstack[this.rstack.length - 1];
var r = {};
r.etype = TJSONProtocol.RType[list.shift()];
r.size = list.shift();
this.rpos.push(this.rstack.length);
this.rstack.push(list.shift());
return r;
};
/** Deserializes the end of a list. */
TJSONProtocol.prototype.readListEnd = function() {
var pos = this.rpos.pop() - 2;
var st = this.rstack;
st.pop();
if (st instanceof Array && st.length > pos && st[pos].length > 0) {
st.push(st[pos].shift());
}
};
/**
* Deserializes the beginning of a set.
* @returns {AnonReadColBeginReturn}
*/
TJSONProtocol.prototype.readSetBegin = function() {
return this.readListBegin();
};
/** Deserializes the end of a set. */
TJSONProtocol.prototype.readSetEnd = function() {
return this.readListEnd();
};
TJSONProtocol.prototype.readBool = function() {
return this.readValue() == '1';
};
TJSONProtocol.prototype.readByte = function() {
return this.readI32();
};
TJSONProtocol.prototype.readI16 = function() {
return this.readI32();
};
TJSONProtocol.prototype.readI32 = function(f) {
return +this.readValue();
}
/** Returns the next value found in the protocol buffer */
TJSONProtocol.prototype.readValue = function(f) {
if (f === undefined) {
f = this.rstack[this.rstack.length - 1];
}
var r = {};
if (f instanceof Array) {
if (f.length === 0) {
r.value = undefined;
} else {
r.value = f.shift();
}
} else if (!(f instanceof Int64) && f instanceof Object) {
for (var i in f) {
if (i === null) {
continue;
}
this.rstack.push(f[i]);
delete f[i];
r.value = i;
break;
}
} else {
r.value = f;
this.rstack.pop();
}
return r.value;
};
TJSONProtocol.prototype.readI64 = function() {
var n = this.readValue()
if (typeof n === 'string') {
// Assuming no one is sending in 1.11111e+33 format
return Int64Util.fromDecimalString(n);
} else {
return new Int64(n);
}
};
TJSONProtocol.prototype.readDouble = function() {
return this.readI32();
};
TJSONProtocol.prototype.readBinary = function() {
return new Buffer(this.readValue(), 'base64');
};
TJSONProtocol.prototype.readString = function() {
return this.readValue();
};
/**
* Returns the underlying transport.
* @readonly
* @returns {Thrift.Transport} The underlying transport.
*/
TJSONProtocol.prototype.getTransport = function() {
return this.trans;
};
/**
* Method to arbitrarily skip over data
*/
TJSONProtocol.prototype.skip = function(type) {
throw new Error('skip not supported yet');
};

View file

@ -0,0 +1,26 @@
/*
* 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.
*/
module.exports = {
'info' : function logInfo() {},
'warning' : function logWarning() {},
'error' : function logError() {},
'debug' : function logDebug() {},
'trace' : function logTrace() {}
};

View file

@ -0,0 +1,63 @@
/*
* 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');
exports.MultiplexedProcessor = MultiplexedProcessor;
function MultiplexedProcessor(stream, options) {
this.services = {};
};
MultiplexedProcessor.prototype.registerProcessor = function(name, handler) {
this.services[name] = handler;
};
MultiplexedProcessor.prototype.process = function(inp, out) {
var begin = inp.readMessageBegin();
if (begin.mtype != Thrift.MessageType.CALL && begin.mtype != Thrift.MessageType.ONEWAY) {
throw new Thrift.TException('TMultiplexedProcessor: Unexpected message type');
}
var p = begin.fname.split(':');
var sname = p[0];
var fname = p[1];
if (! (sname in this.services)) {
throw new Thrift.TException('TMultiplexedProcessor: Unknown service: ' + sname);
}
//construct a proxy object which stubs the readMessageBegin
//for the service
var inpProxy = {};
for (var attr in inp) {
inpProxy[attr] = inp[attr];
}
inpProxy.readMessageBegin = function() {
return {
fname: fname,
mtype: begin.mtype,
rseqid: begin.rseqid
};
};
this.services[sname].process(inpProxy, out);
};

View file

@ -0,0 +1,73 @@
/*
* 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 util = require('util');
var Thrift = require('./thrift');
exports.Multiplexer = Multiplexer;
function Wrapper(serviceName, protocol, connection) {
function MultiplexProtocol(trans, strictRead, strictWrite) {
protocol.call(this, trans, strictRead, strictWrite);
};
util.inherits(MultiplexProtocol, protocol);
MultiplexProtocol.prototype.writeMessageBegin = function(name, type, seqid) {
if (type == Thrift.MessageType.CALL || type == Thrift.MessageType.ONEWAY) {
connection.seqId2Service[seqid] = serviceName;
MultiplexProtocol.super_.prototype.writeMessageBegin.call(this,
serviceName + ":" + name,
type,
seqid);
} else {
MultiplexProtocol.super_.prototype.writeMessageBegin.call(this, name, type, seqid);
}
};
return MultiplexProtocol;
};
function Multiplexer() {
this.seqid = 0;
};
Multiplexer.prototype.createClient = function(serviceName, ServiceClient, connection) {
if (ServiceClient.Client) {
ServiceClient = ServiceClient.Client;
}
var self = this;
ServiceClient.prototype.new_seqid = function() {
self.seqid += 1;
return self.seqid;
};
var writeCb = function(buf, seqid) {
connection.write(buf,seqid);
};
var transport = new connection.transport(undefined, writeCb);
var protocolWrapper = new Wrapper(serviceName, connection.protocol, connection);
var client = new ServiceClient(transport, protocolWrapper);
if (typeof connection.client !== 'object') {
connection.client = {};
}
connection.client[serviceName] = client;
return client;
};

View file

@ -0,0 +1,22 @@
/*
* 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.
*/
module.exports.TBinaryProtocol = require('./binary_protocol');
module.exports.TCompactProtocol = require('./compact_protocol');
module.exports.TJSONProtocol = require('./json_protocol');

View file

@ -0,0 +1,107 @@
/*
* 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 net = require('net');
var tls = require('tls');
var TBufferedTransport = require('./buffered_transport');
var TBinaryProtocol = require('./binary_protocol');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
/**
* 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 Multiplex Server.
*/
exports.createMultiplexServer = function(processor, options) {
var transport = (options && options.transport) ? options.transport : TBufferedTransport;
var protocol = (options && options.protocol) ? options.protocol : TBinaryProtocol;
function serverImpl(stream) {
var self = this;
stream.on('error', function(err) {
self.emit('error', err);
});
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 {
processor.process(input, output);
transportWithData.commitPosition();
} while (true);
} catch (err) {
if (err instanceof 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();
});
}
if (options && options.tls) {
return tls.createServer(options.tls, serverImpl);
} else {
return net.createServer(serverImpl);
}
};
/**
* 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 Multiplex Server.
*/
exports.createServer = function(processor, handler, options) {
if (processor.Processor) {
processor = processor.Processor;
}
return exports.createMultiplexServer(new processor(handler), options);
};

View file

@ -0,0 +1,232 @@
/*
* 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 util = require('util');
var Type = exports.Type = {
STOP: 0,
VOID: 1,
BOOL: 2,
BYTE: 3,
I08: 3,
DOUBLE: 4,
I16: 6,
I32: 8,
I64: 10,
STRING: 11,
UTF7: 11,
STRUCT: 12,
MAP: 13,
SET: 14,
LIST: 15,
UTF8: 16,
UTF16: 17
};
exports.MessageType = {
CALL: 1,
REPLY: 2,
EXCEPTION: 3,
ONEWAY: 4
};
exports.TException = TException;
function TException(message) {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = message;
};
util.inherits(TException, Error);
var TApplicationExceptionType = exports.TApplicationExceptionType = {
UNKNOWN: 0,
UNKNOWN_METHOD: 1,
INVALID_MESSAGE_TYPE: 2,
WRONG_METHOD_NAME: 3,
BAD_SEQUENCE_ID: 4,
MISSING_RESULT: 5,
INTERNAL_ERROR: 6,
PROTOCOL_ERROR: 7,
INVALID_TRANSFORM: 8,
INVALID_PROTOCOL: 9,
UNSUPPORTED_CLIENT_TYPE: 10
};
exports.TApplicationException = TApplicationException;
function TApplicationException(type, message) {
TException.call(this);
Error.captureStackTrace(this, this.constructor);
this.type = type || TApplicationExceptionType.UNKNOWN;
this.name = this.constructor.name;
this.message = message;
};
util.inherits(TApplicationException, TException);
TApplicationException.prototype.read = function(input) {
var ftype;
var ret = input.readStructBegin('TApplicationException');
while(1){
ret = input.readFieldBegin();
if(ret.ftype == Type.STOP)
break;
switch(ret.fid){
case 1:
if( ret.ftype == Type.STRING ){
ret = input.readString();
this.message = ret;
} else {
ret = input.skip(ret.ftype);
}
break;
case 2:
if( ret.ftype == Type.I32 ){
ret = input.readI32();
this.type = ret;
} else {
ret = input.skip(ret.ftype);
}
break;
default:
ret = input.skip(ret.ftype);
break;
}
input.readFieldEnd();
}
input.readStructEnd();
};
TApplicationException.prototype.write = function(output){
output.writeStructBegin('TApplicationException');
if (this.message) {
output.writeFieldBegin('message', Type.STRING, 1);
output.writeString(this.message);
output.writeFieldEnd();
}
if (this.code) {
output.writeFieldBegin('type', Type.I32, 2);
output.writeI32(this.code);
output.writeFieldEnd();
}
output.writeFieldStop();
output.writeStructEnd();
};
var TProtocolExceptionType = exports.TProtocolExceptionType = {
UNKNOWN: 0,
INVALID_DATA: 1,
NEGATIVE_SIZE: 2,
SIZE_LIMIT: 3,
BAD_VERSION: 4,
NOT_IMPLEMENTED: 5,
DEPTH_LIMIT: 6
};
exports.TProtocolException = TProtocolException;
function TProtocolException(type, message) {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.type = type;
this.message = message;
};
util.inherits(TProtocolException, Error);
exports.objectLength = function(obj) {
return Object.keys(obj).length;
};
exports.inherits = function(constructor, superConstructor) {
util.inherits(constructor, superConstructor);
};
var copyList, copyMap;
copyList = function(lst, types) {
if (!lst) {return lst; }
var type;
if (types.shift === undefined) {
type = types;
}
else {
type = types[0];
}
var Type = type;
var len = lst.length, result = [], i, val;
for (i = 0; i < len; i++) {
val = lst[i];
if (type === null) {
result.push(val);
}
else if (type === copyMap || type === copyList) {
result.push(type(val, types.slice(1)));
}
else {
result.push(new Type(val));
}
}
return result;
};
copyMap = function(obj, types){
if (!obj) {return obj; }
var type;
if (types.shift === undefined) {
type = types;
}
else {
type = types[0];
}
var Type = type;
var result = {}, val;
for(var prop in obj) {
if(obj.hasOwnProperty(prop)) {
val = obj[prop];
if (type === null) {
result[prop] = val;
}
else if (type === copyMap || type === copyList) {
result[prop] = type(val, types.slice(1));
}
else {
result[prop] = new Type(val);
}
}
}
return result;
};
module.exports.copyMap = copyMap;
module.exports.copyList = copyList;

View file

@ -0,0 +1,22 @@
/*
* 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.
*/
module.exports.TBufferedTransport = require('./buffered_transport');
module.exports.TFramedTransport = require('./framed_transport');
module.exports.InputBufferUnderrunError = require('./input_buffer_underrun_error');

View file

@ -0,0 +1,564 @@
/*
* 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 https = require('https');
var url = require("url");
var path = require("path");
var fs = require("fs");
var crypto = require("crypto");
var MultiplexedProcessor = require('./multiplexed_processor').MultiplexedProcessor;
var TBufferedTransport = require('./buffered_transport');
var TBinaryProtocol = require('./binary_protocol');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
// WSFrame constructor and prototype
/////////////////////////////////////////////////////////////////////
/** Apache Thrift RPC Web Socket Transport
* Frame layout conforming to RFC 6455 circa 12/2011
*
* Theoretical frame size limit is 4GB*4GB, however the Node Buffer
* limit is 1GB as of v0.10. The frame length encoding is also
* configured for a max of 4GB presently and needs to be adjusted
* if Node/Browsers become capabile of > 4GB frames.
*
* - 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
* - Payload Len:
* + If < 126: then represented directly
* + If >=126: but within range of an unsigned 16 bit integer
* then Payload Len is 126 and the two following bytes store
* the length
* + Else: Payload Len is 127 and the following 8 bytes store the
* length as an unsigned 64 bit integer
* - Masking key is a 32 bit key only present when sending to the server
* - Payload follows the masking key or length
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-------+-+-------------+-------------------------------+
* |F|R|R|R| opcode|M| Payload len | Extended payload length |
* |I|S|S|S| (4) |A| (7) | (16/64) |
* |N|V|V|V| |S| | (if payload len==126/127) |
* | |1|2|3| |K| | |
* +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
* | Extended payload length continued, if payload len == 127 |
* + - - - - - - - - - - - - - - - +-------------------------------+
* | |Masking-key, if MASK set to 1 |
* +-------------------------------+-------------------------------+
* | Masking-key (continued) | Payload Data |
* +-------------------------------- - - - - - - - - - - - - - - - +
* : Payload Data continued ... :
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | Payload Data continued ... |
* +---------------------------------------------------------------+
*/
var wsFrame = {
/** Encodes a WebSocket frame
*
* @param {Buffer} data - The raw data to encode
* @param {Buffer} mask - The mask to apply when sending to server, null for no mask
* @param {Boolean} binEncoding - True for binary encoding, false for text encoding
* @returns {Buffer} - The WebSocket frame, ready to send
*/
encode: function(data, mask, binEncoding) {
var frame = new Buffer(wsFrame.frameSizeFromData(data, mask));
//Byte 0 - FIN & OPCODE
frame[0] = wsFrame.fin.FIN +
(binEncoding ? wsFrame.frameOpCodes.BIN : wsFrame.frameOpCodes.TEXT);
//Byte 1 or 1-3 or 1-9 - MASK FLAG & SIZE
var payloadOffset = 2;
if (data.length < 0x7E) {
frame[1] = data.length + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT);
} else if (data.length < 0xFFFF) {
frame[1] = 0x7E + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT);
frame.writeUInt16BE(data.length, 2, true);
payloadOffset = 4;
} else {
frame[1] = 0x7F + (mask ? wsFrame.mask.TO_SERVER : wsFrame.mask.TO_CLIENT);
frame.writeUInt32BE(0, 2, true);
frame.writeUInt32BE(data.length, 6, true);
payloadOffset = 10;
}
//MASK
if (mask) {
mask.copy(frame, payloadOffset, 0, 4);
payloadOffset += 4;
}
//Payload
data.copy(frame, payloadOffset);
if (mask) {
wsFrame.applyMask(frame.slice(payloadOffset), frame.slice(payloadOffset-4,payloadOffset));
}
return frame;
},
/**
* @class
* @name WSDecodeResult
* @property {Buffer} data - The decoded data for the first ATRPC message
* @property {Buffer} mask - The frame mask
* @property {Boolean} binEncoding - True if binary (TBinaryProtocol),
* False if text (TJSONProtocol)
* @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, if this is a continuation
* frame it must have a mask property with the mask.
* @returns {WSDecodeResult} - The decoded payload
*
* @see {@link WSDecodeResult}
*/
decode: function(frame) {
var result = {
data: null,
mask: null,
binEncoding: false,
nextFrame: null,
FIN: true
};
//Byte 0 - FIN & OPCODE
if (wsFrame.fin.FIN != (frame[0] & wsFrame.fin.FIN)) {
result.FIN = false;
}
result.binEncoding = (wsFrame.frameOpCodes.BIN == (frame[0] & wsFrame.frameOpCodes.BIN));
//Byte 1 or 1-3 or 1-9 - SIZE
var lenByte = (frame[1] & 0x0000007F);
var len = lenByte;
var dataOffset = 2;
if (lenByte == 0x7E) {
len = frame.readUInt16BE(2);
dataOffset = 4;
} else if (lenByte == 0x7F) {
len = frame.readUInt32BE(6);
dataOffset = 10;
}
//MASK
if (wsFrame.mask.TO_SERVER == (frame[1] & wsFrame.mask.TO_SERVER)) {
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);
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;
},
/** Masks/Unmasks data
*
* @param {Buffer} data - data to mask/unmask in place
* @param {Buffer} mask - the mask
*/
applyMask: function(data, mask){
//TODO: look into xoring words at a time
var dataLen = data.length;
var maskLen = mask.length;
for (var i = 0; i < dataLen; i++) {
data[i] = data[i] ^ mask[i%maskLen];
}
},
/** Computes frame size on the wire from data to be sent
*
* @param {Buffer} data - data.length is the assumed payload size
* @param {Boolean} mask - true if a mask will be sent (TO_SERVER)
*/
frameSizeFromData: function(data, mask) {
var headerSize = 10;
if (data.length < 0x7E) {
headerSize = 2;
} else if (data.length < 0xFFFF) {
headerSize = 4;
}
return headerSize + data.length + (mask ? 4 : 0);
},
frameOpCodes: {
CONT: 0x00,
TEXT: 0x01,
BIN: 0x02,
CTRL: 0x80
},
mask: {
TO_SERVER: 0x80,
TO_CLIENT: 0x00
},
fin: {
CONT: 0x00,
FIN: 0x80
}
};
// createWebServer constructor and options
/////////////////////////////////////////////////////////////////////
/**
* @class
* @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.
* @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 ServiceOptions
* @property {object} transport - The layered transport to use (defaults
* to TBufferedTransport).
* @property {object} protocol - The serialization Protocol to use (defaults to
* TBinaryProtocol).
* @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.
*/
/**
* Create a Thrift server which can serve static files and/or one or
* more Thrift Services.
* @param {ServerOptions} options - The server configuration.
* @returns {object} - The Apache Thrift Web Server.
*/
exports.createWebServer = function(options) {
var baseDir = options.files;
var contentTypesByExtension = {
'.txt': 'text/plain',
'.html': 'text/html',
'.css': 'text/css',
'.xml': 'application/xml',
'.json': 'application/json',
'.js': 'application/javascript',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.png': 'image/png',
  '.svg': 'image/svg+xml'
};
//Setup all of the services
var services = options.services;
for (var uri in services) {
var svcObj = services[uri];
//Setup the processor
if (svcObj.processor instanceof MultiplexedProcessor) {
//Multiplex processors have pre embedded 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 embedded,
// 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) {
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", "GET, POST, OPTIONS");
response.setHeader("access-control-allow-headers", "content-type, accept");
response.setHeader("access-control-max-age", "60");
return true;
} else {
//Disallow, origin denied
return false;
}
}
//Allow, CORS is not in use
return true;
}
//Handle OPTIONS method (CORS)
///////////////////////////////////////////////////
function processOptions(request, response) {
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)
///////////////////////////////////////////////////
function processPost(request, response) {
//Lookup service
var uri = url.parse(request.url).pathname;
var svc = services[uri];
if (!svc) {
response.writeHead("403", "No Apache Thrift Service at " + uri, {});
response.end();
return;
}
//Verify CORS requirements
if (!VerifyCORSAndSetHeaders(request, response)) {
response.writeHead("403", "Origin " + request.headers.origin + " not allowed", {});
response.end();
return;
}
//Process XHR payload
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 InputBufferUnderrunError) {
transportWithData.rollbackPosition();
} else {
response.writeHead(500);
response.end();
}
}
}));
}
//Handle GET methods (Static Page Server)
///////////////////////////////////////////////////
function processGet(request, response) {
//Undefined or empty base directory means do not serve static files
if (!baseDir || "" === baseDir) {
response.writeHead(404);
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);
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;
}
var headers = {};
var contentType = contentTypesByExtension[path.extname(filename)];
if (contentType) {
headers["Content-Type"] = contentType;
}
for (var k in options.headers) {
headers[k] = options.headers[k];
}
response.writeHead(200, headers);
response.write(file, "binary");
response.end();
});
});
}
//Handle WebSocket calls (TWebSocketTransport)
///////////////////////////////////////////////////
function processWS(data, socket, svc, binEncoding) {
svc.transport.receiver(function(transportWithData) {
var input = new svc.protocol(transportWithData);
var output = new svc.protocol(new svc.transport(undefined, function(buf) {
try {
var frame = wsFrame.encode(buf, null, binEncoding);
socket.write(frame);
} catch (err) {
//TODO: Add better error processing
}
}));
try {
svc.processor.process(input, output);
transportWithData.commitPosition();
}
catch (err) {
if (err instanceof InputBufferUnderrunError) {
transportWithData.rollbackPosition();
}
else {
//TODO: Add better error processing
}
}
})(data);
}
//Create the server (HTTP or HTTPS)
var server = null;
if (options.tls) {
server = https.createServer(options.tls);
} else {
server = http.createServer();
}
//Wire up listeners for upgrade(to WebSocket) & request methods for:
// - GET static files,
// - POST XHR Thrift services
// - OPTIONS CORS requests
server.on('request', function(request, response) {
if (request.method === 'POST') {
processPost(request, response);
} else if (request.method === 'GET') {
processGet(request, response);
} else if (request.method === 'OPTIONS') {
processOptions(request, response);
} else {
response.writeHead(500);
response.end();
}
}).on('upgrade', function(request, socket, head) {
//Lookup service
var svc;
try {
svc = services[Object.keys(services)[0]];
} catch(e) {
socket.write("HTTP/1.1 403 No Apache Thrift Service available\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" +
"Sec-WebSocket-Origin: " + request.headers.origin + "\r\n" +
"Sec-WebSocket-Location: ws://" + request.headers.host + request.url + "\r\n" +
"\r\n");
//Handle WebSocket traffic
var data = null;
socket.on('data', function(frame) {
try {
while (frame) {
var 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, result.binEncoding);
} else {
data = result.data;
}
//Prepare next frame for decoding (if any)
frame = result.nextFrame;
}
} catch(e) {
console.log("TWebSocketTransport Exception: " + e);
socket.destroy();
}
});
});
//Return the server
return server;
};

View file

@ -0,0 +1,286 @@
/*
* 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 util = require('util');
var WebSocket = require('ws');
var EventEmitter = require("events").EventEmitter;
var thrift = require('./thrift');
var ttransport = require('./transport');
var tprotocol = require('./protocol');
var TBufferedTransport = require('./buffered_transport');
var TJSONProtocol = require('./json_protocol');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
var createClient = require('./create_client');
exports.WSConnection = WSConnection;
/**
* @class
* @name WSConnectOptions
* @property {string} transport - The Thrift layered transport to use (TBufferedTransport, etc).
* @property {string} protocol - The Thrift serialization protocol to use (TJSONProtocol, etc.).
* @property {string} path - The URL path to connect 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} secure - True causes the connection to use wss, otherwise ws is used.
* @property {object} wsOptions - Options passed on to WebSocket.
* @example
* //Use a secured websocket connection
* // uses the buffered transport layer, uses the JSON protocol and directs RPC traffic
* // to wss://thrift.example.com:9090/hello
* var thrift = require('thrift');
* var options = {
* transport: thrift.TBufferedTransport,
* protocol: thrift.TJSONProtocol,
* path: "/hello",
* secure: true
* };
* var con = thrift.createWSConnection("thrift.example.com", 9090, options);
* con.open()
* var client = thrift.createWSClient(myService, connection);
* client.myServiceFunction();
* con.close()
*/
/**
* Initializes a Thrift WSConnection instance (use createWSConnection() 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 {WSConnectOptions} 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 WSConnection objects provide Thrift end point transport
* semantics implemented using Websockets.
* @see {@link createWSConnection}
*/
function WSConnection(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.secure = this.options.secure || false;
this.transport = this.options.transport || TBufferedTransport;
this.protocol = this.options.protocol || TJSONProtocol;
this.path = this.options.path;
this.send_pending = [];
//The sequence map is used to map seqIDs back to the
// calling client in multiplexed scenarios
this.seqId2Service = {};
//Prepare WebSocket options
this.wsOptions = {
host: this.host,
port: this.port || 80,
path: this.options.path || '/',
headers: this.options.headers || {}
};
for (var attrname in this.options.wsOptions) {
this.wsOptions[attrname] = this.options.wsOptions[attrname];
}
};
util.inherits(WSConnection, EventEmitter);
WSConnection.prototype.__reset = function() {
this.socket = null; //The web socket
this.send_pending = []; //Buffers/Callback pairs waiting to be sent
};
WSConnection.prototype.__onOpen = function() {
var self = this;
this.emit("open");
if (this.send_pending.length > 0) {
//If the user made calls before the connection was fully
//open, send them now
this.send_pending.forEach(function(data) {
self.socket.send(data);
});
this.send_pending = [];
}
};
WSConnection.prototype.__onClose = function(evt) {
this.emit("close");
this.__reset();
};
WSConnection.prototype.__decodeCallback = function(transport_with_data) {
var proto = new this.protocol(transport_with_data);
try {
while (true) {
var header = proto.readMessageBegin();
var dummy_seqid = header.rseqid * -1;
var client = this.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 = this.seqId2Service[header.rseqid];
if (service_name) {
client = this.client[service_name];
delete this.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];
this.emit("error",
new thrift.TApplicationException(
thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
"Received a response to an unknown RPC function"));
}
}
} catch (e) {
if (e instanceof InputBufferUnderrunError) {
transport_with_data.rollbackPosition();
} else {
throw e;
}
}
};
WSConnection.prototype.__onData = function(data) {
if (Object.prototype.toString.call(data) == "[object ArrayBuffer]") {
data = new Uint8Array(data);
}
var buf = new Buffer(data);
this.transport.receiver(this.__decodeCallback.bind(this))(buf);
};
WSConnection.prototype.__onMessage = function(evt) {
this.__onData(evt.data);
};
WSConnection.prototype.__onError = function(evt) {
this.emit("error", evt);
this.socket.close();
};
/**
* Returns true if the transport is open
* @readonly
* @returns {boolean}
*/
WSConnection.prototype.isOpen = function() {
return this.socket && this.socket.readyState == this.socket.OPEN;
};
/**
* Opens the transport connection
*/
WSConnection.prototype.open = function() {
//If OPEN/CONNECTING/CLOSING ignore additional opens
if (this.socket && this.socket.readyState != this.socket.CLOSED) {
return;
}
//If there is no socket or the socket is closed:
this.socket = new WebSocket(this.uri(), "", this.wsOptions);
this.socket.binaryType = 'arraybuffer';
this.socket.onopen = this.__onOpen.bind(this);
this.socket.onmessage = this.__onMessage.bind(this);
this.socket.onerror = this.__onError.bind(this);
this.socket.onclose = this.__onClose.bind(this);
};
/**
* Closes the transport connection
*/
WSConnection.prototype.close = function() {
this.socket.close();
};
/**
* Return URI for the connection
* @returns {string} URI
*/
WSConnection.prototype.uri = function() {
var schema = this.secure ? 'wss' : 'ws';
var port = '';
var path = this.path || '/';
var host = this.host;
// avoid port if default for schema
if (this.port && (('wss' == schema && this.port != 443) ||
('ws' == schema && this.port != 80))) {
port = ':' + this.port;
}
return schema + '://' + host + port + path;
};
/**
* 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.
*/
WSConnection.prototype.write = function(data) {
if (this.isOpen()) {
//Send data and register a callback to invoke the client callback
this.socket.send(data);
} else {
//Queue the send to go out __onOpen
this.send_pending.push(data);
}
};
/**
* Creates a new WSConnection 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 {WSConnectOptions} options - The configuration options to use.
* @returns {WSConnection} The connection object.
* @see {@link WSConnectOptions}
*/
exports.createWSConnection = function(host, port, options) {
return new WSConnection(host, port, options);
};
exports.createWSClient = createClient;

View file

@ -0,0 +1,204 @@
/*
* 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.
*/
module.exports = TWebSocketTransport;
/**
* Constructor Function for the WebSocket transport.
* @constructor
* @param {string} [url] - The URL to connect to.
* @classdesc The Apache Thrift Transport layer performs byte level I/O
* between RPC clients and servers. The JavaScript TWebSocketTransport object
* uses the WebSocket protocol. Target servers must implement WebSocket.
* (see: node.js example server_http.js).
* @example
* var transport = new Thrift.TWebSocketTransport("http://localhost:8585");
*/
function TWebSocketTransport(url) {
this.__reset(url);
};
TWebSocketTransport.prototype.__reset = function(url) {
this.url = url; //Where to connect
this.socket = null; //The web socket
this.callbacks = []; //Pending callbacks
this.send_pending = []; //Buffers/Callback pairs waiting to be sent
this.send_buf = ''; //Outbound data, immutable until sent
this.recv_buf = ''; //Inbound data
this.rb_wpos = 0; //Network write position in receive buffer
this.rb_rpos = 0; //Client read position in receive buffer
};
/**
* Sends the current WS request and registers callback. The async
* parameter is ignored (WS flush is always async) and the callback
* function parameter is required.
* @param {object} async - Ignored.
* @param {object} callback - The client completion callback.
* @returns {undefined|string} Nothing (undefined)
*/
TWebSocketTransport.prototype.flush = function(async, callback) {
var self = this;
if (this.isOpen()) {
//Send data and register a callback to invoke the client callback
this.socket.send(this.send_buf);
this.callbacks.push((function() {
var clientCallback = callback;
return function(msg) {
self.setRecvBuffer(msg);
clientCallback();
};
}()));
} else {
//Queue the send to go out __onOpen
this.send_pending.push({
buf: this.send_buf,
cb: callback
});
}
};
TWebSocketTransport.prototype.__onOpen = function() {
var self = this;
if (this.send_pending.length > 0) {
//If the user made calls before the connection was fully
//open, send them now
this.send_pending.forEach(function(elem) {
this.socket.send(elem.buf);
this.callbacks.push((function() {
var clientCallback = elem.cb;
return function(msg) {
self.setRecvBuffer(msg);
clientCallback();
};
}()));
});
this.send_pending = [];
}
};
TWebSocketTransport.prototype.__onClose = function(evt) {
this.__reset(this.url);
};
TWebSocketTransport.prototype.__onMessage = function(evt) {
if (this.callbacks.length) {
this.callbacks.shift()(evt.data);
}
};
TWebSocketTransport.prototype.__onError = function(evt) {
console.log("Thrift WebSocket Error: " + evt.toString());
this.socket.close();
};
/**
* Sets the buffer to use when receiving server responses.
* @param {string} buf - The buffer to receive server responses.
*/
TWebSocketTransport.prototype.setRecvBuffer = function(buf) {
this.recv_buf = buf;
this.recv_buf_sz = this.recv_buf.length;
this.wpos = this.recv_buf.length;
this.rpos = 0;
};
/**
* Returns true if the transport is open
* @readonly
* @returns {boolean}
*/
TWebSocketTransport.prototype.isOpen = function() {
return this.socket && this.socket.readyState == this.socket.OPEN;
};
/**
* Opens the transport connection
*/
TWebSocketTransport.prototype.open = function() {
//If OPEN/CONNECTING/CLOSING ignore additional opens
if (this.socket && this.socket.readyState != this.socket.CLOSED) {
return;
}
//If there is no socket or the socket is closed:
this.socket = new WebSocket(this.url);
this.socket.onopen = this.__onOpen.bind(this);
this.socket.onmessage = this.__onMessage.bind(this);
this.socket.onerror = this.__onError.bind(this);
this.socket.onclose = this.__onClose.bind(this);
};
/**
* Closes the transport connection
*/
TWebSocketTransport.prototype.close = function() {
this.socket.close();
};
/**
* Returns the specified number of characters from the response
* buffer.
* @param {number} len - The number of characters to return.
* @returns {string} Characters sent by the server.
*/
TWebSocketTransport.prototype.read = function(len) {
var avail = this.wpos - this.rpos;
if (avail === 0) {
return '';
}
var give = len;
if (avail < len) {
give = avail;
}
var ret = this.read_buf.substr(this.rpos, give);
this.rpos += give;
//clear buf when complete?
return ret;
};
/**
* Returns the entire response buffer.
* @returns {string} Characters sent by the server.
*/
TWebSocketTransport.prototype.readAll = function() {
return this.recv_buf;
};
/**
* Sets the send buffer to buf.
* @param {string} buf - The buffer to send.
*/
TWebSocketTransport.prototype.write = function(buf) {
this.send_buf = buf;
};
/**
* Returns the send buffer.
* @readonly
* @returns {string} The send buffer.
*/
TWebSocketTransport.prototype.getSendBuffer = function() {
return this.send_buf;
};

View file

@ -0,0 +1,280 @@
/*
* 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 util = require('util');
var EventEmitter = require("events").EventEmitter;
var thrift = require('./thrift');
var TBufferedTransport = require('./buffered_transport');
var TJSONProtocol = require('./json_protocol');
var InputBufferUnderrunError = require('./input_buffer_underrun_error');
var createClient = require('./create_client');
exports.XHRConnection = XHRConnection;
/**
* Constructor Function for the XHR Connection.
* If you do not specify a host and port then XHRConnection will default to the
* host and port of the page from which this javascript is served.
* @constructor
* @param {string} [url] - The URL to connect to.
* @classdesc TXHRConnection objects provide Thrift end point transport
* semantics implemented using XHR.
* @example
* var transport = new Thrift.TXHRConnection('localhost', 9099, {});
*/
function XHRConnection(host, port, options) {
this.options = options || {};
this.wpos = 0;
this.rpos = 0;
this.useCORS = (options && options.useCORS);
this.send_buf = '';
this.recv_buf = '';
this.transport = options.transport || TBufferedTransport;
this.protocol = options.protocol || TJSONProtocol;
this.headers = options.headers || {};
host = host || window.location.host;
port = port || window.location.port;
var prefix = options.https ? 'https://' : 'http://';
var path = options.path || '/';
if (port === '') {
port = undefined;
}
if (!port || port === 80 || port === '80') {
this.url = prefix + host + path;
} else {
this.url = prefix + host + ':' + port + path;
}
//The sequence map is used to map seqIDs back to the
// calling client in multiplexed scenarios
this.seqId2Service = {};
};
util.inherits(XHRConnection, EventEmitter);
/**
* Gets the browser specific XmlHttpRequest Object.
* @returns {object} the browser XHR interface object
*/
XHRConnection.prototype.getXmlHttpRequestObject = function() {
try { return new XMLHttpRequest(); } catch (e1) { }
try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch (e2) { }
try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch (e3) { }
throw "Your browser doesn't support XHR.";
};
/**
* Sends the current XRH request if the transport was created with a URL
* and the async parameter is false. If the transport was not created with
* a URL, or the async parameter is True and no callback is provided, or
* the URL is an empty string, the current send buffer is returned.
* @param {object} async - If true the current send buffer is returned.
* @param {object} callback - Optional async completion callback
* @returns {undefined|string} Nothing or the current send buffer.
* @throws {string} If XHR fails.
*/
XHRConnection.prototype.flush = function() {
var self = this;
if (this.url === undefined || this.url === '') {
return this.send_buf;
}
var xreq = this.getXmlHttpRequestObject();
if (xreq.overrideMimeType) {
xreq.overrideMimeType('application/json');
}
xreq.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
self.setRecvBuffer(this.responseText);
}
};
xreq.open('POST', this.url, true);
Object.keys(this.headers).forEach(function(headerKey) {
xreq.setRequestHeader(headerKey, self.headers[headerKey]);
});
xreq.send(this.send_buf);
};
/**
* Sets the buffer to provide the protocol when deserializing.
* @param {string} buf - The buffer to supply the protocol.
*/
XHRConnection.prototype.setRecvBuffer = function(buf) {
this.recv_buf = buf;
this.recv_buf_sz = this.recv_buf.length;
this.wpos = this.recv_buf.length;
this.rpos = 0;
if (Object.prototype.toString.call(buf) == "[object ArrayBuffer]") {
var data = new Uint8Array(buf);
}
var thing = new Buffer(data || buf);
this.transport.receiver(this.__decodeCallback.bind(this))(thing);
};
XHRConnection.prototype.__decodeCallback = function(transport_with_data) {
var proto = new this.protocol(transport_with_data);
try {
while (true) {
var header = proto.readMessageBegin();
var dummy_seqid = header.rseqid * -1;
var client = this.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 = this.seqId2Service[header.rseqid];
if (service_name) {
client = this.client[service_name];
delete this.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];
this.emit("error",
new thrift.TApplicationException(
thrift.TApplicationExceptionType.WRONG_METHOD_NAME,
"Received a response to an unknown RPC function"));
}
}
} catch (e) {
if (e instanceof InputBufferUnderrunError) {
transport_with_data.rollbackPosition();
} else {
throw e;
}
}
};
/**
* Returns true if the transport is open, XHR always returns true.
* @readonly
* @returns {boolean} Always True.
*/
XHRConnection.prototype.isOpen = function() {
return true;
};
/**
* Opens the transport connection, with XHR this is a nop.
*/
XHRConnection.prototype.open = function() {};
/**
* Closes the transport connection, with XHR this is a nop.
*/
XHRConnection.prototype.close = function() {};
/**
* Returns the specified number of characters from the response
* buffer.
* @param {number} len - The number of characters to return.
* @returns {string} Characters sent by the server.
*/
XHRConnection.prototype.read = function(len) {
var avail = this.wpos - this.rpos;
if (avail === 0) {
return '';
}
var give = len;
if (avail < len) {
give = avail;
}
var ret = this.read_buf.substr(this.rpos, give);
this.rpos += give;
//clear buf when complete?
return ret;
};
/**
* Returns the entire response buffer.
* @returns {string} Characters sent by the server.
*/
XHRConnection.prototype.readAll = function() {
return this.recv_buf;
};
/**
* Sets the send buffer to buf.
* @param {string} buf - The buffer to send.
*/
XHRConnection.prototype.write = function(buf) {
this.send_buf = buf;
this.flush();
};
/**
* Returns the send buffer.
* @readonly
* @returns {string} The send buffer.
*/
XHRConnection.prototype.getSendBuffer = function() {
return this.send_buf;
};
/**
* Creates a new TXHRTransport 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 {XHRConnectOptions} options - The configuration options to use.
* @returns {XHRConnection} The connection object.
* @see {@link XHRConnectOptions}
*/
exports.createXHRConnection = function(host, port, options) {
return new XHRConnection(host, port, options);
};
exports.createXHRClient = createClient;

View file

@ -0,0 +1,137 @@
/*
* 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 test = require('tape');
var binary = require('thrift/binary');
var cases = {
"Should read signed byte": function(assert){
assert.equal(1, binary.readByte(0x01));
assert.equal(-1, binary.readByte(0xFF));
assert.equal(127, binary.readByte(0x7F));
assert.equal(-128, binary.readByte(0x80));
assert.end();
},
"Should write byte": function(assert){
//Protocol simply writes to the buffer. Nothing to test.. yet.
assert.ok(true);
assert.end();
},
"Should read I16": function(assert) {
assert.equal(0, binary.readI16([0x00, 0x00]));
assert.equal(1, binary.readI16([0x00, 0x01]));
assert.equal(-1, binary.readI16([0xff, 0xff]));
// Min I16
assert.equal(-32768, binary.readI16([0x80, 0x00]));
// Max I16
assert.equal(32767, binary.readI16([0x7f, 0xff]));
assert.end();
},
"Should write I16": function(assert) {
assert.deepEqual([0x00, 0x00], binary.writeI16([], 0));
assert.deepEqual([0x00, 0x01], binary.writeI16([], 1));
assert.deepEqual([0xff, 0xff], binary.writeI16([], -1));
// Min I16
assert.deepEqual([0x80, 0x00], binary.writeI16([], -32768));
// Max I16
assert.deepEqual([0x7f, 0xff], binary.writeI16([], 32767));
assert.end();
},
"Should read I32": function(assert) {
assert.equal(0, binary.readI32([0x00, 0x00, 0x00, 0x00]));
assert.equal(1, binary.readI32([0x00, 0x00, 0x00, 0x01]));
assert.equal(-1, binary.readI32([0xff, 0xff, 0xff, 0xff]));
// Min I32
assert.equal(-2147483648, binary.readI32([0x80, 0x00, 0x00, 0x00]));
// Max I32
assert.equal(2147483647, binary.readI32([0x7f, 0xff, 0xff, 0xff]));
assert.end();
},
"Should write I32": function(assert) {
assert.deepEqual([0x00, 0x00, 0x00, 0x00], binary.writeI32([], 0));
assert.deepEqual([0x00, 0x00, 0x00, 0x01], binary.writeI32([], 1));
assert.deepEqual([0xff, 0xff, 0xff, 0xff], binary.writeI32([], -1));
// Min I32
assert.deepEqual([0x80, 0x00, 0x00, 0x00], binary.writeI32([], -2147483648));
// Max I32
assert.deepEqual([0x7f, 0xff, 0xff, 0xff], binary.writeI32([], 2147483647));
assert.end();
},
"Should read doubles": function(assert) {
assert.equal(0, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
assert.equal(0, binary.readDouble([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
assert.equal(1, binary.readDouble([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
assert.equal(2, binary.readDouble([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
assert.equal(-2, binary.readDouble([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
assert.equal(Math.PI, binary.readDouble([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18]))
assert.equal(Infinity, binary.readDouble([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
assert.equal(-Infinity, binary.readDouble([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
assert.ok(isNaN(binary.readDouble([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])))
assert.equal(1/3, binary.readDouble([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55]))
// Min subnormal positive double
assert.equal(4.9406564584124654e-324, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]))
// Min normal positive double
assert.equal(2.2250738585072014e-308, binary.readDouble([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
// Max positive double
assert.equal(1.7976931348623157e308, binary.readDouble([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]))
assert.end();
},
"Should write doubles": function(assert) {
assert.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 0));
assert.deepEqual([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 1));
assert.deepEqual([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2));
assert.deepEqual([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -2));
assert.deepEqual([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], binary.writeDouble([], Math.PI));
assert.deepEqual([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], Infinity));
assert.deepEqual([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -Infinity));
assert.deepEqual([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], NaN));
assert.deepEqual([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55], binary.writeDouble([], 1/3));
// Min subnormal positive double
assert.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], binary.writeDouble([], 4.9406564584124654e-324));
// Min normal positive double
assert.deepEqual([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2.2250738585072014e-308));
// Max positive double
assert.deepEqual([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], binary.writeDouble([], 1.7976931348623157e308));
assert.end();
}
};
Object.keys(cases).forEach(function(caseName) {
test(caseName, cases[caseName]);
});

View file

@ -0,0 +1,27 @@
var assert = require('assert');
var thrift = require('thrift');
var helpers = require('./helpers');
var ThriftTest = require('./gen-nodejs/ThriftTest');
var ThriftTestDriver = require('./test_driver').ThriftTestDriver;
// createXHRConnection createWSConnection
var connection = thrift.createXHRConnection("localhost", 9090, {
transport: helpers.transports['buffered'],
protocol: helpers.protocols['json'],
path: '/test'
});
connection.on('error', function(err) {
assert(false, err);
});
// Uncomment the following line to start a websockets connection
// connection.open();
// createWSClient createXHRClient
var client = thrift.createXHRClient(ThriftTest, connection);
ThriftTestDriver(client, function (status) {
console.log('Browser:', status);
});

View file

@ -0,0 +1,7 @@
server.crt AND server.key ARE PROVIDED FOR TEST PURPOSE AND SHOULD *NEVER* BE USED IN PRODUCTION
Origin of the test key and cert is the folder test/keys of Apache Thrift source code distribution
We need copies for npm deployment

View file

@ -0,0 +1,130 @@
#!/usr/bin/env node
/*
* 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 assert = require('assert');
var thrift = require('thrift');
var helpers = require('./helpers');
var ThriftTest = require('./gen-nodejs/ThriftTest');
var ThriftTestDriver = require('./test_driver').ThriftTestDriver;
var ThriftTestDriverPromise = require('./test_driver').ThriftTestDriverPromise;
var SecondService = require('./gen-nodejs/SecondService');
var ttypes = require('./gen-nodejs/ThriftTest_types');
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('--port <port>', 'Set thrift server port number to connect', 9090)
.option('--host <host>', 'Set thrift server host to connect', 'localhost')
.option('--ssl', 'use SSL transport')
.option('--promise', 'test with promise style functions')
.option('-t, --type <type>', 'Select server type (tcp|multiplex|http)', 'tcp')
.parse(process.argv);
var host = program.host;
var port = program.port;
var type = program.type;
var ssl = program.ssl;
var promise = program.promise;
var options = {
transport: helpers.transports[program.transport],
protocol: helpers.protocols[program.protocol]
};
if (type === 'http' || type === 'websocket') {
options.path = '/test';
}
if (type === 'http') {
options.headers = {"Connection": "close"};
}
if (ssl) {
if (type === 'tcp' || type === 'multiplex') {
options.rejectUnauthorized = false;
} else if (type === 'http') {
options.nodeOptions = { rejectUnauthorized: false };
options.https = true;
} else if (type === 'websocket') {
options.wsOptions = { rejectUnauthorized: false };
options.secure = true;
}
}
var connection;
var client;
var testDriver = promise ? ThriftTestDriverPromise : ThriftTestDriver;
if (type === 'tcp' || type === 'multiplex') {
connection = ssl ?
thrift.createSSLConnection(host, port, options) :
thrift.createConnection(host, port, options);
} else if (type === 'http') {
connection = thrift.createHttpConnection(host, port, options);
} else if (type === 'websocket') {
connection = thrift.createWSConnection(host, port, options);
connection.open();
}
connection.on('error', function(err) {
assert(false, err);
});
if (type === 'tcp') {
client = thrift.createClient(ThriftTest, connection);
runTests();
} else if (type === 'multiplex') {
var mp = new thrift.Multiplexer();
client = mp.createClient("ThriftTest", ThriftTest, connection);
secondclient = mp.createClient("SecondService", SecondService, connection);
connection.on('connect', function() {
secondclient.secondtestString("Test", function(err, response) {
assert(!err);
assert.equal("Test", response);
});
runTests();
});
} else if (type === 'http') {
client = thrift.createHttpClient(ThriftTest, connection);
runTests();
} else if (type === 'websocket') {
client = thrift.createWSClient(ThriftTest, connection);
runTests();
}
function runTests() {
testDriver(client, function (status) {
console.log(status);
if (type !== 'http' && type !== 'websocket') {
connection.end();
}
if (type !== 'multiplex') {
process.exit(0);
}
});
}
exports.expressoTest = function() {};

View file

@ -0,0 +1,314 @@
var ttypes = require('./gen-nodejs/JsDeepConstructorTest_types');
var thrift = require('thrift');
var test = require('tape');
var bufferEquals = require('buffer-equals');
function serializeBinary(data) {
var buff;
var transport = new thrift.TBufferedTransport(null, function(msg){
buff = msg;
});
var prot = new thrift.TBinaryProtocol(transport);
data.write(prot);
prot.flush();
return buff;
}
function deserializeBinary(serialized, type) {
var t = new thrift.TFramedTransport(serialized);
var p = new thrift.TBinaryProtocol(t);
var data = new type();
data.read(p);
return data;
}
function serializeJSON(data) {
var buff;
var transport = new thrift.TBufferedTransport(null, function(msg){
buff = msg;
});
var protocol = new thrift.TJSONProtocol(transport);
protocol.writeMessageBegin("", 0, 0);
data.write(protocol);
protocol.writeMessageEnd();
protocol.flush();
return buff;
}
function deserializeJSON(serialized, type) {
var transport = new thrift.TFramedTransport(serialized);
var protocol = new thrift.TJSONProtocol(transport);
protocol.readMessageBegin();
var data = new type();
data.read(protocol);
protocol.readMessageEnd();
return data;
}
function createThriftObj() {
return new ttypes.Complex({
struct_field: new ttypes.Simple({value: 'a'}),
struct_list_field: [
new ttypes.Simple({value: 'b'}),
new ttypes.Simple({value: 'c'}),
],
struct_set_field: [
new ttypes.Simple({value: 'd'}),
new ttypes.Simple({value: 'e'}),
],
struct_map_field: {
A: new ttypes.Simple({value: 'f'}),
B: new ttypes.Simple({value: 'g'})
},
struct_nested_containers_field: [
[
{
C: [
new ttypes.Simple({value: 'h'}),
new ttypes.Simple({value: 'i'})
]
}
]
],
struct_nested_containers_field2: {
D: [
{
DA: new ttypes.Simple({value: 'j'})
},
{
DB: new ttypes.Simple({value: 'k'})
}
]
},
list_of_list_field: [
['l00', 'l01', 'l02'],
['l10', 'l11', 'l12'],
['l20', 'l21', 'l22'],
],
list_of_list_of_list_field: [
[['m000', 'm001', 'm002'], ['m010', 'm011', 'm012'], ['m020', 'm021', 'm022']],
[['m100', 'm101', 'm102'], ['m110', 'm111', 'm112'], ['m120', 'm121', 'm122']],
[['m200', 'm201', 'm202'], ['m210', 'm211', 'm212'], ['m220', 'm221', 'm222']],
],
});
}
function createJsObj() {
return {
struct_field: {value: 'a'},
struct_list_field: [
{value: 'b'},
{value: 'c'},
],
struct_set_field: [
{value: 'd'},
{value: 'e'},
],
struct_map_field: {
A: {value: 'f'},
B: {value: 'g'}
},
struct_nested_containers_field: [
[
{
C: [
{value: 'h'},
{value: 'i'}
]
}
]
],
struct_nested_containers_field2: {
D: [
{
DA: {value: 'j'}
},
{
DB: {value: 'k'}
}
]
},
list_of_list_field: [
['l00', 'l01', 'l02'],
['l10', 'l11', 'l12'],
['l20', 'l21', 'l22'],
],
list_of_list_of_list_field: [
[['m000', 'm001', 'm002'], ['m010', 'm011', 'm012'], ['m020', 'm021', 'm022']],
[['m100', 'm101', 'm102'], ['m110', 'm111', 'm112'], ['m120', 'm121', 'm122']],
[['m200', 'm201', 'm202'], ['m210', 'm211', 'm212'], ['m220', 'm221', 'm222']],
],
};
}
function assertValues(obj, assert) {
assert.equals(obj.struct_field.value, 'a');
assert.equals(obj.struct_list_field[0].value, 'b');
assert.equals(obj.struct_list_field[1].value, 'c');
assert.equals(obj.struct_set_field[0].value, 'd');
assert.equals(obj.struct_set_field[1].value, 'e');
assert.equals(obj.struct_map_field.A.value, 'f');
assert.equals(obj.struct_map_field.B.value, 'g');
assert.equals(obj.struct_nested_containers_field[0][0].C[0].value, 'h');
assert.equals(obj.struct_nested_containers_field[0][0].C[1].value, 'i');
assert.equals(obj.struct_nested_containers_field2.D[0].DA.value, 'j');
assert.equals(obj.struct_nested_containers_field2.D[1].DB.value, 'k');
assert.equals(obj.list_of_list_field[0][0], 'l00');
assert.equals(obj.list_of_list_field[0][1], 'l01');
assert.equals(obj.list_of_list_field[0][2], 'l02');
assert.equals(obj.list_of_list_field[1][0], 'l10');
assert.equals(obj.list_of_list_field[1][1], 'l11');
assert.equals(obj.list_of_list_field[1][2], 'l12');
assert.equals(obj.list_of_list_field[2][0], 'l20');
assert.equals(obj.list_of_list_field[2][1], 'l21');
assert.equals(obj.list_of_list_field[2][2], 'l22');
assert.equals(obj.list_of_list_of_list_field[0][0][0], 'm000');
assert.equals(obj.list_of_list_of_list_field[0][0][1], 'm001');
assert.equals(obj.list_of_list_of_list_field[0][0][2], 'm002');
assert.equals(obj.list_of_list_of_list_field[0][1][0], 'm010');
assert.equals(obj.list_of_list_of_list_field[0][1][1], 'm011');
assert.equals(obj.list_of_list_of_list_field[0][1][2], 'm012');
assert.equals(obj.list_of_list_of_list_field[0][2][0], 'm020');
assert.equals(obj.list_of_list_of_list_field[0][2][1], 'm021');
assert.equals(obj.list_of_list_of_list_field[0][2][2], 'm022');
assert.equals(obj.list_of_list_of_list_field[1][0][0], 'm100');
assert.equals(obj.list_of_list_of_list_field[1][0][1], 'm101');
assert.equals(obj.list_of_list_of_list_field[1][0][2], 'm102');
assert.equals(obj.list_of_list_of_list_field[1][1][0], 'm110');
assert.equals(obj.list_of_list_of_list_field[1][1][1], 'm111');
assert.equals(obj.list_of_list_of_list_field[1][1][2], 'm112');
assert.equals(obj.list_of_list_of_list_field[1][2][0], 'm120');
assert.equals(obj.list_of_list_of_list_field[1][2][1], 'm121');
assert.equals(obj.list_of_list_of_list_field[1][2][2], 'm122');
assert.equals(obj.list_of_list_of_list_field[2][0][0], 'm200');
assert.equals(obj.list_of_list_of_list_field[2][0][1], 'm201');
assert.equals(obj.list_of_list_of_list_field[2][0][2], 'm202');
assert.equals(obj.list_of_list_of_list_field[2][1][0], 'm210');
assert.equals(obj.list_of_list_of_list_field[2][1][1], 'm211');
assert.equals(obj.list_of_list_of_list_field[2][1][2], 'm212');
assert.equals(obj.list_of_list_of_list_field[2][2][0], 'm220');
assert.equals(obj.list_of_list_of_list_field[2][2][1], 'm221');
assert.equals(obj.list_of_list_of_list_field[2][2][2], 'm222');
}
function createTestCases(serialize, deserialize) {
var cases = {
"Serialize/deserialize should return equal object": function(assert){
var tObj = createThriftObj();
var received = deserialize(serialize(tObj), ttypes.Complex);
assert.ok(tObj !== received, 'not the same object');
assert.deepEqual(tObj, received);
assert.end();
},
"Nested structs and containers initialized from plain js objects should serialize same as if initialized from thrift objects": function(assert) {
var tObj1 = createThriftObj();
var tObj2 = new ttypes.Complex(createJsObj());
assertValues(tObj2, assert);
var s1 = serialize(tObj1);
var s2 = serialize(tObj2);
assert.ok(bufferEquals(s1, s2));
assert.end();
},
"Modifications to args object should not affect constructed Thrift object": function (assert) {
var args = createJsObj();
assertValues(args, assert);
var tObj = new ttypes.Complex(args);
assertValues(tObj, assert);
args.struct_field.value = 'ZZZ';
args.struct_list_field[0].value = 'ZZZ';
args.struct_list_field[1].value = 'ZZZ';
args.struct_set_field[0].value = 'ZZZ';
args.struct_set_field[1].value = 'ZZZ';
args.struct_map_field.A.value = 'ZZZ';
args.struct_map_field.B.value = 'ZZZ';
args.struct_nested_containers_field[0][0].C[0] = 'ZZZ';
args.struct_nested_containers_field[0][0].C[1] = 'ZZZ';
args.struct_nested_containers_field2.D[0].DA = 'ZZZ';
args.struct_nested_containers_field2.D[0].DB = 'ZZZ';
assertValues(tObj, assert);
assert.end();
},
"nulls are ok": function(assert) {
var tObj = new ttypes.Complex({
struct_field: null,
struct_list_field: null,
struct_set_field: null,
struct_map_field: null,
struct_nested_containers_field: null,
struct_nested_containers_field2: null
});
var received = deserialize(serialize(tObj), ttypes.Complex);
assert.strictEqual(tObj.struct_field, null);
assert.ok(tObj !== received);
assert.deepEqual(tObj, received);
assert.end();
},
"Can make list with objects": function(assert) {
var tObj = new ttypes.ComplexList({
"struct_list_field": [new ttypes.Complex({})]
});
var innerObj = tObj.struct_list_field[0];
assert.ok(innerObj instanceof ttypes.Complex)
assert.strictEqual(innerObj.struct_field, null);
assert.strictEqual(innerObj.struct_list_field, null);
assert.strictEqual(innerObj.struct_set_field, null);
assert.strictEqual(innerObj.struct_map_field, null);
assert.strictEqual(innerObj.struct_nested_containers_field, null);
assert.strictEqual(innerObj.struct_nested_containers_field2, null);
assert.end();
}
};
return cases;
}
function run(name, cases){
Object.keys(cases).forEach(function(caseName) {
test(name + ': ' + caseName, cases[caseName]);
});
}
run('binary', createTestCases(serializeBinary, deserializeBinary));
run('json', createTestCases(serializeJSON, deserializeJSON));

View file

@ -0,0 +1,55 @@
'use strict';
var test = require('tape');
var thrift = require('../lib/thrift/thrift.js');
var InputBufferUnderrunError = require('../lib/thrift/input_buffer_underrun_error');
test('TApplicationException', function t(assert) {
var e = new thrift.TApplicationException(1, 'foo');
assert.ok(e instanceof thrift.TApplicationException, 'is instanceof TApplicationException');
assert.ok(e instanceof thrift.TException, 'is instanceof TException');
assert.ok(e instanceof Error, 'is instanceof Error');
assert.equal(typeof e.stack, 'string', 'has stack trace');
assert.ok(/^TApplicationException: foo/.test(e.stack), 'Stack trace has correct error name and message');
assert.ok(e.stack.indexOf('test/exceptions.js:7:11') !== -1, 'stack trace starts on correct line and column');
assert.equal(e.name, 'TApplicationException', 'has function name TApplicationException');
assert.equal(e.message, 'foo', 'has error message "foo"');
assert.equal(e.type, 1, 'has type 1');
assert.end();
});
test('TException', function t(assert) {
var e = new thrift.TException('foo');
assert.ok(e instanceof thrift.TException, 'is instanceof TException');
assert.ok(e instanceof Error, 'is instanceof Error');
assert.equal(typeof e.stack, 'string', 'has stack trace');
assert.ok(/^TException: foo/.test(e.stack), 'Stack trace has correct error name and message');
assert.ok(e.stack.indexOf('test/exceptions.js:21:11') !== -1, 'stack trace starts on correct line and column');
assert.equal(e.name, 'TException', 'has function name TException');
assert.equal(e.message, 'foo', 'has error message "foo"');
assert.end();
});
test('TProtocolException', function t(assert) {
var e = new thrift.TProtocolException(1, 'foo');
assert.ok(e instanceof thrift.TProtocolException, 'is instanceof TProtocolException');
assert.ok(e instanceof Error, 'is instanceof Error');
assert.equal(typeof e.stack, 'string', 'has stack trace');
assert.ok(/^TProtocolException: foo/.test(e.stack), 'Stack trace has correct error name and message');
assert.ok(e.stack.indexOf('test/exceptions.js:33:11') !== -1, 'stack trace starts on correct line and column');
assert.equal(e.name, 'TProtocolException', 'has function name TProtocolException');
assert.equal(e.message, 'foo', 'has error message "foo"');
assert.equal(e.type, 1, 'has type 1');
assert.end();
});
test('InputBufferUnderrunError', function t(assert) {
var e = new InputBufferUnderrunError('foo');
assert.ok(e instanceof InputBufferUnderrunError, 'is instanceof InputBufferUnderrunError');
assert.ok(e instanceof Error, 'is instanceof Error');
assert.equal(typeof e.stack, 'string', 'has stack trace');
assert.ok(/^InputBufferUnderrunError: foo/.test(e.stack), 'Stack trace has correct error name and message');
assert.ok(e.stack.indexOf('test/exceptions.js:46:11') !== -1, 'stack trace starts on correct line and column');
assert.equal(e.name, 'InputBufferUnderrunError', 'has function name InputBufferUnderrunError');
assert.equal(e.message, 'foo', 'has error message "foo"');
assert.end();
});

View file

@ -0,0 +1,13 @@
'use strict';
var thrift = require('../lib/thrift');
module.exports.transports = {
'buffered': thrift.TBufferedTransport,
'framed': thrift.TFramedTransport
};
module.exports.protocols = {
'json': thrift.TJSONProtocol,
'binary': thrift.TBinaryProtocol,
'compact': thrift.TCompactProtocol
};

View file

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIENzCCAx+gAwIBAgIJAOYfYfw7NCOcMA0GCSqGSIb3DQEBBQUAMIGxMQswCQYD
VQQGEwJVUzERMA8GA1UECAwITWFyeWxhbmQxFDASBgNVBAcMC0ZvcmVzdCBIaWxs
MScwJQYDVQQKDB5UaGUgQXBhY2hlIFNvZnR3YXJlIEZvdW5kYXRpb24xFjAUBgNV
BAsMDUFwYWNoZSBUaHJpZnQxEjAQBgNVBAMMCWxvY2FsaG9zdDEkMCIGCSqGSIb3
DQEJARYVZGV2QHRocmlmdC5hcGFjaGUub3JnMB4XDTE0MDQwNzE4NTgwMFoXDTIy
MDYyNDE4NTgwMFowgbExCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhNYXJ5bGFuZDEU
MBIGA1UEBwwLRm9yZXN0IEhpbGwxJzAlBgNVBAoMHlRoZSBBcGFjaGUgU29mdHdh
cmUgRm91bmRhdGlvbjEWMBQGA1UECwwNQXBhY2hlIFRocmlmdDESMBAGA1UEAwwJ
bG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVkZXZAdGhyaWZ0LmFwYWNoZS5vcmcw
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqE9TE9wEXp5LRtLQVDSGQ
GV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCySN8I2Xw6
L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/HjKNg6ZKg
2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQBGmZmMIUw
AinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xku62LipkX
wCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDmtrhVQF4n
AgMBAAGjUDBOMB0GA1UdDgQWBBQo8v0wzQPx3EEexJPGlxPK1PpgKjAfBgNVHSME
GDAWgBQo8v0wzQPx3EEexJPGlxPK1PpgKjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
DQEBBQUAA4IBAQBGFRiJslcX0aJkwZpzTwSUdgcfKbpvNEbCNtVohfQVTI4a/oN5
U+yqDZJg3vOaOuiAZqyHcIlZ8qyesCgRN314Tl4/JQ++CW8mKj1meTgo5YFxcZYm
T9vsI3C+Nzn84DINgI9mx6yktIt3QOKZRDpzyPkUzxsyJ8J427DaimDrjTR+fTwD
1Dh09xeeMnSa5zeV1HEDyJTqCXutLetwQ/IyfmMBhIx+nvB5f67pz/m+Dv6V0r3I
p4HCcdnDUDGJbfqtoqsAATQQWO+WWuswB6mOhDbvPTxhRpZq6AkgWqv4S+u3M2GO
r5p9FrBgavAw5bKO54C0oQKpN/5fta5l6Ws0
-----END CERTIFICATE-----

View file

@ -0,0 +1,101 @@
#!/usr/bin/env node
/*
* 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('../lib/thrift');
var program = require('commander');
var helpers = require('./helpers');
var ThriftTest = require('./gen-nodejs/ThriftTest');
var SecondService = require('./gen-nodejs/SecondService');
var ThriftTestHandler = require('./test_handler').AsyncThriftTestHandler;
var ThriftTestHandlerPromise = require('./test_handler').SyncThriftTestHandler;
var ttypes = require('./gen-nodejs/ThriftTest_types');
program
.option('-p, --protocol <protocol>', 'Set thrift protocol (binary|json|compact)', 'binary')
.option('-t, --transport <transport>', 'Set thrift transport (buffered|framed)', 'buffered')
.option('--ssl', 'use ssl transport')
.option('--port <port>', 'Set thrift server port', 9090)
.option('--promise', 'test with promise style functions')
.option('-t, --type <type>', 'Select server type (tcp|multiplex|http)', 'tcp')
.parse(process.argv);
var port = program.port;
var type = program.type;
var ssl = program.ssl;
var promise = program.promise;
var handler = program.promise ? ThriftTestHandler : ThriftTestHandlerPromise;
var options = {
transport: helpers.transports[program.transport],
protocol: helpers.protocols[program.protocol]
};
if (type === 'http' || type ==='websocket') {
options.handler = handler;
options.processor = ThriftTest;
options = {
services: { "/test": options },
cors: {
'*': true
}
}
}
if (type === 'multiplex') {
var SecondServiceHandler = {
secondtestString: function(thing, result) {
console.log('testString(\'' + thing + '\')');
result(null, thing);
}
};
var processor = new thrift.MultiplexedProcessor();
processor.registerProcessor("ThriftTest",
new ThriftTest.Processor(ThriftTestHandler));
processor.registerProcessor("SecondService",
new SecondService.Processor(SecondServiceHandler));
}
if (ssl) {
options.tls = {
key: fs.readFileSync(path.resolve(__dirname, 'server.key')),
cert: fs.readFileSync(path.resolve(__dirname, 'server.crt'))
};
}
var server;
if (type === 'tcp') {
server = thrift.createServer(ThriftTest, handler, options);
} else if (type === 'multiplex') {
server = thrift.createMultiplexServer(processor, options);
} else if (type === 'http' || type === 'websocket') {
server = thrift.createWebServer(options);
}
server.listen(port);

View file

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCqE9TE9wEXp5LR
tLQVDSGQGV78+7ZtP/I/ZaJ6Q6ZGlfxDFvZjFF73seNhAvlKlYm/jflIHYLnNOCy
SN8I2Xw6L9MbC+jvwkEKfQo4eDoxZnOZjNF5J1/lZtBeOowMkhhzBMH1Rds351/H
jKNg6ZKg2Cldd0j7HbDtEixOLgLbPRpBcaYrLrNMasf3Hal+x8/b8ue28x93HSQB
GmZmMIUwAinEu/fNP4lLGl/0kZb76TnyRpYSPYojtS6CnkH+QLYnsRREXJYwD1Xk
u62LipkXwCkRTnZ5nUsDMX6FPKgjQFQCWDXG/N096+PRUQAChhrXsJ+gF3NqWtDm
trhVQF4nAgMBAAECggEAW/y52YYW6ypROGbZ94DQpFV0kLO7qT8q0Ksxw5sPNaIt
fEPRIymDa8ikyHWJS5Oxmw84wo5jnJV26jaLmwe2Lupq7Xf1lqej8f5LJtuv7cQR
xfzp1vM65KJFFJHp6WqjGqJ6HSSZOpVDsnQYcXQjQCdpyAmaSWd3p+FqYSZ1mQmD
bFNI7jqpczWSZhTdotQ7p7Hn9TVCehflP3yGIB3bQ+wCcCB85dOBz201L+YgaIck
Sz43A4NvWaQIRLRDw7s9GW4jY5T0Jv282WIeAlVpVxLIwu48r4R4yGTIx9Ydowvq
57+Y5iPPjAXxu0V9t00oS3bYxDaKh2DUfc/5zowq8QKBgQDYNVPXmaG0aIH4vjQ9
7fRdw/UDkYcQbn6CnglQOu77/S8ogQzpKCVJgJgkZNqOVtQMEPzekGEcLTbje1gU
8Bky2k+PL9UwbFy0emnOVh4rqrNXHsRvJcehNT/PRb5hjF3MUMFV/0iD4b+naFaE
jrSWiZ2ZXj2qfwAK52GFbtOuBQKBgQDJYQuGiY0r22E4waJmCSKczoBT3cwlVzWj
V2ljgA9RHLNTVkvNNYQLGu2qngFrtwpeaSnsMDerVG4wKAQWyCnYzxVrlnC4uDrJ
HXuFEltBWi9Ffbgfsnd3749AT0oBP1NT2tMleguyf5DFgjCR3VRJLdrVaaZ8row/
LqKcFMqnOwKBgB+OIO99l7E584Y3VG6ZdSneOLtNmRXX2pT7tcZE465ZdHGH7Dd3
SYHhx9K/+Xn+yDH+pLli/xlarAEldmSP6k2WuTfftlC78AfTOfAId5zN7CDR9791
Fx67I9X/itq33tS8EIuZl57P6uXm/4GXRloWOa8xpvRkVsBApuYPl8t1AoGATQDS
y2sllDObBXzlgGbV2WgNIgSZ311toTv3jJiXQsjauW8yJRHln+l4H9mzaWDgkiFc
ang1kUoDqF5k0eFQPxtQcYdhKwEnWWfwp33RbzfxA32DPnubuzzbZhfrkHaKgnIW
cyor9uFYlm2l7ODZLfJez2RKyTplXnOSsmQw6akCgYAz3dj9Hskyj+HVJ+ht1OcE
c7ai/ESkSA7Vajp0tjJp0EKjW/zq8DvUSXOtcdnJgkKycFluLwbmnaN4txBds1C1
Qr8Rt2sUCCBNZe1L6DHe3XBdbkJe9sgZVNTjtUSQrzy8UhvsCqG4YWeCu07Szcbc
rdPUV9/uQkdx8VrShxlD8A==
-----END PRIVATE KEY-----

View file

@ -0,0 +1,144 @@
'use strict';
var ttypes = require('./gen-nodejs/ThriftTest_types');
var Int64 = require('node-int64');
//all Languages in UTF-8
/*jshint -W100 */
var stringTest = module.exports.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, Polski, پنجابی, پښتو, " +
"Norfuk / Pitkern, 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ú, 粵語";
/*jshint +W100 */
var specialCharacters = module.exports.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: !@#$%&()(&%$#{}{}<><><' +
' char-to-test-json-parsing: ]] \"]] \\" }}}{ [[[ ';
var mapTestInput = module.exports.mapTestInput = {
"a":"123", "a b":"with spaces ", "same":"same", "0":"numeric key",
"longValue":stringTest, stringTest:"long key"
};
var simple = [
['testVoid', undefined],
['testString', 'Test'],
['testString', ''],
['testString', stringTest],
['testString', specialCharacters],
['testBool', true],
['testBool', false],
['testByte', 1],
['testByte', 0],
['testByte', -1],
['testByte', -127],
['testI32', -1],
['testDouble', -5.2098523],
['testDouble', 7.012052175215044],
['testEnum', ttypes.Numberz.ONE],
['testI64', 5],
['testI64', -5],
['testI64', 734359738368],
['testI64', -734359738368],
['testI64', new Int64(new Buffer([0, 0x20, 0, 0, 0, 0, 0, 1]))], // 2^53+1
['testI64', new Int64(
new Buffer([0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]))], // -2^53-1
['testTypedef', 69]
]
var mapout = {};
for (var i = 0; i < 5; ++i) {
mapout[i] = i-10;
}
var deep = [
['testList', [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]],
];
var deepUnordered = [
['testMap', mapout],
['testSet', [1,2,3]],
['testStringMap', mapTestInput]
];
var out = new ttypes.Xtruct({
string_thing: 'Zero',
byte_thing: 1,
i32_thing: -3,
i64_thing: 1000000
});
var out2 = new ttypes.Xtruct2();
out2.byte_thing = 1;
out2.struct_thing = out;
out2.i32_thing = 5;
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 crazy2 = new ttypes.Insanity({
"userMap":{ "5":5, "8":8 },
"xtructs":[{
"string_thing":"Goodbye4",
"byte_thing":4,
"i32_thing":4,
"i64_thing":4
}, {
"string_thing":"Hello2",
"byte_thing":2,
"i32_thing":2,
"i64_thing":2
}]
});
var insanity = {
"1":{ "2": crazy, "3": crazy },
"2":{ "6":{ "userMap":{}, "xtructs":[] } }
};
module.exports.simple = simple;
module.exports.deep = deep;
module.exports.deepUnordered = deepUnordered;
module.exports.out = out;
module.exports.out2 = out2;
module.exports.crazy = crazy;
module.exports.crazy2 = crazy2;
module.exports.insanity = insanity;

107
vendor/git.apache.org/thrift.git/lib/nodejs/test/testAll.sh generated vendored Executable file
View file

@ -0,0 +1,107 @@
#! /bin/sh
# 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.
if [ -n "${1}" ]; then
COVER=${1};
fi
DIR="$( cd "$( dirname "$0" )" && pwd )"
ISTANBUL="$DIR/../../../node_modules/istanbul/lib/cli.js"
RUNBROWSER="$DIR/../../../node_modules/run-browser/bin/cli.js"
REPORT_PREFIX="${DIR}/../coverage/report"
COUNT=0
export NODE_PATH="${DIR}:${DIR}/../lib:${NODE_PATH}"
testServer()
{
echo " Testing $1 Client/Server with protocol $2 and transport $3 $4";
RET=0
if [ -n "${COVER}" ]; then
${ISTANBUL} cover ${DIR}/server.js --dir ${REPORT_PREFIX}${COUNT} --handle-sigint -- --type $1 -p $2 -t $3 $4 &
COUNT=$((COUNT+1))
else
node ${DIR}/server.js --type $1 -p $2 -t $3 $4 &
fi
SERVERPID=$!
sleep 1
if [ -n "${COVER}" ]; then
${ISTANBUL} cover ${DIR}/client.js --dir ${REPORT_PREFIX}${COUNT} -- --type $1 -p $2 -t $3 $4 || RET=1
COUNT=$((COUNT+1))
else
node ${DIR}/client.js --type $1 -p $2 -t $3 $4 || RET=1
fi
kill -2 $SERVERPID || RET=1
return $RET
}
testBrowser()
{
echo " Testing browser client with http server with json protocol and buffered transport";
RET=0
node ${DIR}/server.js --type http -p json -t buffered &
SERVERPID=$!
sleep 1
${RUNBROWSER} ${DIR}/browser_client.js --phantom || RET=1
kill -2 $SERVERPID || RET=1
return $RET
}
TESTOK=0
#generating thrift code
${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/ThriftTest.thrift
${DIR}/../../../compiler/cpp/thrift -o ${DIR} --gen js:node ${DIR}/../../../test/JsDeepConstructorTest.thrift
#unit tests
node ${DIR}/binary.test.js || TESTOK=1
node ${DIR}/deep-constructor.test.js || TESTOK=1
#integration tests
for type in tcp multiplex websocket http
do
for protocol in compact binary json
do
for transport in buffered framed
do
testServer $type $protocol $transport || TESTOK=1
testServer $type $protocol $transport --ssl || TESTOK=1
testServer $type $protocol $transport --promise || TESTOK=1
done
done
done
# XHR only until phantomjs 2 is released.
testBrowser
if [ -n "${COVER}" ]; then
${ISTANBUL} report --dir "${DIR}/../coverage" --include "${DIR}/../coverage/report*/coverage.json" lcov cobertura html
rm -r ${DIR}/../coverage/report*/*
rmdir ${DIR}/../coverage/report*
fi
exit $TESTOK

View file

@ -0,0 +1,327 @@
/*
* 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 Node.js test driver for the standard Apache Thrift
// test service. The driver invokes every function defined in the
// Thrift Test service with a representative range of parameters.
//
// The ThriftTestDriver function requires a client object
// connected to a server hosting the Thrift Test service and
// supports an optional callback function which is called with
// a status message when the test is complete.
var test = require('tape');
//var assert = require('assert');
var ttypes = require('./gen-nodejs/ThriftTest_types');
var TException = require('thrift').Thrift.TException;
var Int64 = require('node-int64');
var testCases = require('./test-cases');
exports.ThriftTestDriver = function(client, callback) {
test('NodeJS Style Callback Client Tests', function(assert) {
var checkRecursively = makeRecursiveCheck(assert);
function makeAsserter(assertionFn) {
return function(c) {
var fnName = c[0];
var expected = c[1];
client[fnName](expected, function(err, actual) {
assert.error(err, fnName + ': no callback error');
assertionFn(actual, expected, fnName);
})
};
}
testCases.simple.forEach(makeAsserter(function(a, e, m){
if (a instanceof Int64) {
var e64 = e instanceof Int64 ? e : new Int64(e);
assert.deepEqual(a.buffer, e64.buffer, m);
} else {
assert.equal(a, e, m);
}
}));
testCases.deep.forEach(makeAsserter(assert.deepEqual));
testCases.deepUnordered.forEach(makeAsserter(makeUnorderedDeepEqual(assert)));
var arr = [];
for (var i = 0; i < 256; ++i) {
arr[i] = 255 - i;
}
var buf = new Buffer(arr);
client.testBinary(buf, function(err, response) {
assert.error(err, 'testBinary: no callback error');
assert.equal(response.length, 256, 'testBinary');
assert.deepEqual(response, buf, 'testBinary(Buffer)');
});
var buf = new Buffer(arr);
client.testBinary(buf.toString('binary'), function(err, response) {
assert.error(err, 'testBinary: no callback error');
assert.equal(response.length, 256, 'testBinary');
assert.deepEqual(response, buf, 'testBinary(string)');
});
client.testMapMap(42, function(err, response) {
var expected = {
"4": {"1":1, "2":2, "3":3, "4":4},
"-4": {"-4":-4, "-3":-3, "-2":-2, "-1":-1}
};
assert.error(err, 'testMapMap: no callback error');
assert.deepEqual(expected, response, 'testMapMap');
});
client.testStruct(testCases.out, function(err, response) {
assert.error(err, 'testStruct: no callback error');
checkRecursively(testCases.out, response, 'testStruct');
});
client.testNest(testCases.out2, function(err, response) {
assert.error(err, 'testNest: no callback error');
checkRecursively(testCases.out2, response, 'testNest');
});
client.testInsanity(testCases.crazy, function(err, response) {
assert.error(err, 'testInsanity: no callback error');
checkRecursively(testCases.insanity, response, 'testInsanity');
});
client.testInsanity(testCases.crazy2, function(err, response) {
assert.error(err, 'testInsanity2: no callback error');
checkRecursively(testCases.insanity, response, 'testInsanity2');
});
client.testException('TException', function(err, response) {
assert.ok(err instanceof TException, 'testException: correct error type');
assert.ok(!response, 'testException: no response');
});
client.testException('Xception', function(err, response) {
assert.ok(err instanceof ttypes.Xception, 'testException: correct error type');
assert.ok(!response, 'testException: no response');
assert.equal(err.errorCode, 1001, 'testException: correct error code');
assert.equal('Xception', err.message, 'testException: correct error message');
});
client.testException('no Exception', function(err, response) {
assert.error(err, 'testException: no callback error');
assert.ok(!response, 'testException: no response');
});
client.testOneway(0, function(err, response) {
assert.fail('testOneway should not answer');
});
checkOffByOne(function(done) {
client.testI32(-1, function(err, response) {
assert.error(err, 'checkOffByOne: no callback error');
assert.equal(-1, response);
assert.end();
done();
});
}, callback);
});
};
exports.ThriftTestDriverPromise = function(client, callback) {
test('Q Promise Client Tests', function(assert) {
var checkRecursively = makeRecursiveCheck(assert);
function fail(msg) {
return function() {
assert.fail(msg);
}
}
function makeAsserter(assertionFn) {
return function(c) {
var fnName = c[0];
var expected = c[1];
client[fnName](expected)
.then(function(actual) {
assertionFn(actual, expected, fnName);
})
.fail(fail('fnName'));
};
}
testCases.simple.forEach(makeAsserter(function(a, e, m){
if (a instanceof Int64) {
var e64 = e instanceof Int64 ? e : new Int64(e);
assert.deepEqual(a.buffer, e64.buffer, m);
} else {
assert.equal(a, e, m);
}
}));
testCases.deep.forEach(makeAsserter(assert.deepEqual));
testCases.deepUnordered.forEach(makeAsserter(makeUnorderedDeepEqual(assert)));
client.testStruct(testCases.out)
.then(function(response) {
checkRecursively(testCases.out, response, 'testStruct');
})
.fail(fail('testStruct'));
client.testNest(testCases.out2)
.then(function(response) {
checkRecursively(testCases.out2, response, 'testNest');
})
.fail(fail('testNest'));
client.testInsanity(testCases.crazy)
.then(function(response) {
checkRecursively(testCases.insanity, response, 'testInsanity');
})
.fail(fail('testInsanity'));
client.testInsanity(testCases.crazy2)
.then(function(response) {
checkRecursively(testCases.insanity, response, 'testInsanity2');
})
.fail(fail('testInsanity2'));
client.testException('TException')
.then(function(response) {
fail('testException: TException');
})
.fail(function(err) {
assert.ok(err instanceof TException);
});
client.testException('Xception')
.then(function(response) {
fail('testException: Xception');
})
.fail(function(err) {
assert.ok(err instanceof ttypes.Xception);
assert.equal(err.errorCode, 1001);
assert.equal('Xception', err.message);
});
client.testException('no Exception')
.then(function(response) {
assert.equal(undefined, response); //void
})
.fail(fail('testException'));
client.testOneway(0, fail('testOneway: should not answer'));
checkOffByOne(function(done) {
client.testI32(-1)
.then(function(response) {
assert.equal(-1, response);
assert.end();
done();
})
.fail(fail('checkOffByOne'));
}, callback);
});
};
// Helper Functions
// =========================================================
function makeRecursiveCheck(assert) {
return function (map1, map2, msg) {
var equal = true;
var equal = checkRecursively(map1, map2);
assert.ok(equal, msg);
// 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') {
//Handle int64 types (which use node-int64 in Node.js JavaScript)
if ((typeof map1 === "number") && (typeof map2 === "object") &&
(map2.buffer) && (map2.buffer instanceof Buffer) && (map2.buffer.length === 8)) {
var n = new Int64(map2.buffer);
return map1 === n.toNumber();
} else {
return map1 == map2;
}
} else {
return Object.keys(map1).every(function(key) {
return checkRecursively(map1[key], map2[key]);
});
}
}
}
}
}
function checkOffByOne(done, callback) {
var retry_limit = 30;
var retry_interval = 100;
var test_complete = false;
var retrys = 0;
/**
* 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:
*
* Because this is the last test against the server, when it completes
* the entire suite is complete by definition (the tests run serially).
*/
done(function() {
test_complete = true;
});
//We wait up to retry_limit * retry_interval for the test suite to complete
function TestForCompletion() {
if(test_complete && callback) {
callback("Server successfully tested!");
} else {
if (++retrys < retry_limit) {
setTimeout(TestForCompletion, retry_interval);
} else if (callback) {
callback("Server test failed to complete after " +
(retry_limit * retry_interval / 1000) + " seconds");
}
}
}
setTimeout(TestForCompletion, retry_interval);
}
function makeUnorderedDeepEqual(assert) {
return function(actual, expected, name) {
assert.equal(actual.length, expected.length, name);
for (var k in actual) {
var found = false;
for (var k2 in expected) {
if (actual[k] === expected[k2]) {
found = true;
}
}
if (!found) {
assert.fail('Unexpected value ' + actual[k] + ' with key ' + k);
}
}
};
}

View file

@ -0,0 +1,227 @@
/*
* 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 server side Node test handler for the standard
// Apache Thrift test service.
var ttypes = require('./gen-nodejs/ThriftTest_types');
var TException = require('thrift').Thrift.TException;
function makeSyncHandler(label) {
return function(thing) {
//console.log(label + '(\'' + thing + '\')');
return thing;
}
}
var syncHandlers = {
testVoid: testVoid,
testMapMap: testMapMap,
testInsanity: testInsanity,
testMulti: testMulti,
testException: testException,
testMultiException: testMultiException,
testOneway: testOneway
};
function makeAsyncHandler(label) {
return function(thing, result) {
thing = syncHandlers[label](thing);
result(null, thing);
}
}
var asyncHandlers = {
testVoid: testVoidAsync,
testMulti: testMultiAsync,
testException: testExceptionAsync,
testMultiException: testMultiExceptionAsync,
testOneway: testOnewayAsync
};
var identityHandlers = [
'testString',
'testBool',
'testByte',
'testI32',
'testI64',
'testDouble',
'testBinary',
'testStruct',
'testNest',
'testMap',
'testStringMap',
'testSet',
'testList',
'testEnum',
'testTypedef'
];
function testVoid() {
//console.log('testVoid()');
}
function testVoidAsync(result) {
result(testVoid());
}
function testMapMap(hello) {
//console.log('testMapMap(' + hello + ')');
var mapmap = [];
var pos = [];
var neg = [];
for (var i = 1; i < 5; i++) {
pos[i] = i;
neg[-i] = -i;
}
mapmap[4] = pos;
mapmap[-4] = neg;
return mapmap;
}
function testInsanity(argument) {
//console.log('testInsanity(');
//console.log(argument);
//console.log(')');
var first_map = [];
var second_map = [];
first_map[ttypes.Numberz.TWO] = argument;
first_map[ttypes.Numberz.THREE] = argument;
var looney = new ttypes.Insanity();
second_map[ttypes.Numberz.SIX] = looney;
var insane = [];
insane[1] = first_map;
insane[2] = second_map;
//console.log('insane result:');
//console.log(insane);
return insane;
}
function testMulti(arg0, arg1, arg2, arg3, arg4, arg5) {
//console.log('testMulti()');
var hello = new ttypes.Xtruct();
hello.string_thing = 'Hello2';
hello.byte_thing = arg0;
hello.i32_thing = arg1;
hello.i64_thing = arg2;
return hello;
}
function testMultiAsync(arg0, arg1, arg2, arg3, arg4, arg5, result) {
var hello = testMulti(arg0, arg1, arg2, arg3, arg4, arg5);
result(null, hello);
}
function testException(arg) {
//console.log('testException('+arg+')');
if (arg === 'Xception') {
var x = new ttypes.Xception();
x.errorCode = 1001;
x.message = arg;
throw x;
} else if (arg === 'TException') {
throw new TException(arg);
} else {
return;
}
}
function testExceptionAsync(arg, result) {
//console.log('testException('+arg+')');
if (arg === 'Xception') {
var x = new ttypes.Xception();
x.errorCode = 1001;
x.message = arg;
result(x);
} else if (arg === 'TException') {
result(new TException(arg));
} else {
result(null);
}
}
function testMultiException(arg0, arg1) {
//console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
if (arg0 === ('Xception')) {
var x = new ttypes.Xception();
x.errorCode = 1001;
x.message = 'This is an Xception';
throw x;
} else if (arg0 === ('Xception2')) {
var x2 = new ttypes.Xception2();
x2.errorCode = 2002;
x2.struct_thing = new ttypes.Xtruct();
x2.struct_thing.string_thing = 'This is an Xception2';
throw x2;
}
var res = new ttypes.Xtruct();
res.string_thing = arg1;
return res;
}
function testMultiExceptionAsync(arg0, arg1, result) {
//console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
if (arg0 === ('Xception')) {
var x = new ttypes.Xception();
x.errorCode = 1001;
x.message = 'This is an Xception';
result(x);
} else if (arg0 === ('Xception2')) {
var x2 = new ttypes.Xception2();
x2.errorCode = 2002;
x2.struct_thing = new ttypes.Xtruct();
x2.struct_thing.string_thing = 'This is an Xception2';
result(x2);
} else {
var res = new ttypes.Xtruct();
res.string_thing = arg1;
result(null, res);
}
}
function testOneway(sleepFor) {
//console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!');
}
function testOnewayAsync(sleepFor, result) {
testOneway(sleepFor);
}
identityHandlers.forEach(function(label) {
syncHandlers[label] = makeSyncHandler(label);
asyncHandlers[label] = makeAsyncHandler(label);
});
['testMapMap', 'testInsanity'].forEach(function(label) {
asyncHandlers[label] = makeAsyncHandler(label);
});
exports.ThriftTestHandler = asyncHandlers;
exports.AsyncThriftTestHandler = asyncHandlers;
exports.SyncThriftTestHandler = asyncHandlers;