Skip to content

Commit

Permalink
THRIFT-5003: Websocket Connection in Browsers with nodejs code
Browse files Browse the repository at this point in the history
* changed this to self in forEach callback

* updated minimum node version to 8.16.2 (Maintenance LTS until December 2019)
changed ws_connection.js to work in the browser, with isomorphic-ws
added exports for `wsConnection`, `createWSConnection`, `createWSClient`

* added exports for WSConnection to browser.js

* extended the sample of nodejs code in the browser with webpack

* tested and updated node version to LTS 10.18.0 Dubnium
discussion based: apache#1927 (comment)
  • Loading branch information
alabama authored and dcelasun committed Jan 7, 2020
1 parent 6e44378 commit 30ac259
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 112 deletions.
37 changes: 34 additions & 3 deletions lib/nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@ You can use code generated with js:node on browsers with Webpack. Here is an exa

thrift --gen js:node,ts,es6,with_ns

```
import * as thrift from 'thrift/browser';
```javascript
import * as thrift from 'thrift';
import { MyServiceClient } from '../gen-nodejs/MyService';

let host = window.location.hostname;
Expand Down Expand Up @@ -108,4 +108,35 @@ thriftClient.myService(param)
});
```

Note that thrift/index.js must be renamed or skipped for browsers.
Bundlers, like webpack, will use thrift/browser.js by default because of the
`"browser": "./lib/nodejs/lib/thrift/browser.js"` field in package.json.

### Browser example with WebSocket, BufferedTransport and BinaryProtocol
```javascript
import thrift from 'thrift';
import { MyServiceClient } from '../gen-nodejs/MyService';

const host = window.location.hostname;
const port = 9090;
const opts = {
transport: thrift.TBufferedTransport,
protocol: thrift.TBinaryProtocol
}
const connection = thrift.createWSConnection(host, port, opts);
connection.open();
const thriftClient = thrift.createWSClient(MyServiceClient, connection);

connection.on('error', (err) => {
console.error(err);
});

thriftClient.myService(param)
.then((result) => {
console.log(result);
})
.catch((err) => {
....
});
```


5 changes: 5 additions & 0 deletions lib/nodejs/lib/thrift/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ exports.XHRConnection = xhrConnection.XHRConnection;
exports.createXHRConnection = xhrConnection.createXHRConnection;
exports.createXHRClient = xhrConnection.createXHRClient;

var wsConnection = require('./ws_connection');
exports.WSConnection = wsConnection.WSConnection;
exports.createWSConnection = wsConnection.createWSConnection;
exports.createWSClient = wsConnection.createWSClient;

exports.Multiplexer = require('./multiplexed_protocol').Multiplexer;

exports.TWebSocketTransport = require('./ws_transport');
Expand Down
30 changes: 15 additions & 15 deletions lib/nodejs/lib/thrift/ws_connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,16 @@
* under the License.
*/
var util = require('util');
var WebSocket = require('ws');
var WebSocket = require('isomorphic-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');

var jsEnv = require('browser-or-node');
exports.WSConnection = WSConnection;

/**
Expand Down Expand Up @@ -69,7 +67,7 @@ exports.WSConnection = WSConnection;
* @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
* 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 WSConnection objects provide Thrift end point transport
* semantics implemented using Websockets.
Expand All @@ -80,7 +78,6 @@ function WSConnection(host, port, options) {
EventEmitter.call(this);

//Set configuration
var self = this;
this.options = options || {};
this.host = host;
this.port = port;
Expand Down Expand Up @@ -113,14 +110,13 @@ WSConnection.prototype.__reset = function() {
};

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.socket.send(data);
}, this);
this.send_pending = [];
}
};
Expand Down Expand Up @@ -184,7 +180,7 @@ WSConnection.prototype.__decodeCallback = function(transport_with_data) {
};

WSConnection.prototype.__onData = function(data) {
if (Object.prototype.toString.call(data) == "[object ArrayBuffer]") {
if (Object.prototype.toString.call(data) === "[object ArrayBuffer]") {
data = new Uint8Array(data);
}
var buf = new Buffer(data);
Expand All @@ -207,19 +203,23 @@ WSConnection.prototype.__onError = function(evt) {
* @returns {boolean}
*/
WSConnection.prototype.isOpen = function() {
return this.socket && this.socket.readyState == this.socket.OPEN;
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) {
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);
if (jsEnv.isBrowser) {
this.socket = new WebSocket(this.uri());
} else {
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);
Expand All @@ -245,8 +245,8 @@ WSConnection.prototype.uri = function() {
var host = this.host;

// avoid port if default for schema
if (this.port && (('wss' == schema && this.port != 443) ||
('ws' == schema && this.port != 80))) {
if (this.port && (('wss' === schema && this.port !== 443) ||
('ws' === schema && this.port !== 80))) {
port = ':' + this.port;
}

Expand Down
Loading

0 comments on commit 30ac259

Please sign in to comment.