Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TINKERPOP-2143 JavaScript GLV: Support browsers #1291

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -23,7 +23,6 @@
'use strict';

const EventEmitter = require('events');
const WebSocket = require('ws');
const util = require('util');
const utils = require('../utils');
const serializer = require('../structure/io/graph-serializer');
Expand Down Expand Up @@ -120,38 +119,33 @@ class Connection extends EventEmitter {

this.emit('log', `ws open`);

this._ws = new WebSocket(this.url, {
headers: this.options.headers,
ca: this.options.ca,
cert: this.options.cert,
pfx: this.options.pfx,
rejectUnauthorized: this.options.rejectUnauthorized
});
this._ws = new WebSocket(this.url);

this._ws.on('message', (data) => this._handleMessage(data));
this._ws.on('close', (code, message) => this._handleClose(code, message));
this._ws.onmessage = (data => this._handleMessage(data));
this._ws.onerror = (err => this._handleError(err));
this._ws.onclose = ((code, message) => this._handleClose(code, message));

this._ws.on('pong', () => {
this._ws.pong = (() => {
this.emit('log', 'ws pong received');
if (this._pongTimeout) {
clearTimeout(this._pongTimeout);
this._pongTimeout = null;
}
});
this._ws.on('ping', () => {
if (this._pongTimeout) {
clearTimeout(this._pongTimeout);
this._pongTimeout = null;
}
});
this._ws.ping = (() => {
this.emit('log', 'ws ping received');
this._ws.pong();
});
this._ws.pong();
});

return this._openPromise = new Promise((resolve, reject) => {
this._ws.on('open', () => {
this.isOpen = true;
if (this._pingEnabled) {
this._pingHeartbeat();
}
resolve();
});
this._ws.on('error', (err) => {
this._ws.onopen = (() => {
this.isOpen = true;
if (this._pingEnabled) {
this._pingHeartbeat();
}
resolve();
});
this._ws.on('error', (err) => {
this._handleError(err);
reject(err);
});
Expand All @@ -163,14 +157,20 @@ class Connection extends EventEmitter {
const rid = requestId || utils.getUuid();
return this.open().then(() => new Promise((resolve, reject) => {
if (op !== 'authentication') {
this._responseHandlers[rid] = {
callback: (err, result) => err ? reject(err) : resolve(result),
this._responseHandlers[rid] = {
callback: (err, result) => err ? reject(err) : resolve(result),
result: null
};
}
};
}

const message = Buffer.from(this._header + JSON.stringify(this._getRequest(rid, op, args, processor)));
this._ws.send(message);
const message = this._header + JSON.stringify(this._getRequest(requestId, bytecode, op, args, processor));
var buf = new ArrayBuffer(message.length); // 2 bytes for each char
var bufView = new Uint8Array(buf);
for (var i=0, strLen=message.length; i < strLen; i++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably this needs a TextEncoder or something like that for fast access: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder

There are buffer libraries that use built-in Buffer in Node.js and polyfills for browsers, we should use one of those.

bufView[i] = message.charCodeAt(i);
}
this._ws.binaryType = 'arraybuffer';
this._ws.send(bufView);
}));
}

Expand All @@ -197,6 +197,7 @@ class Connection extends EventEmitter {
args['gremlin'] = this._writer.adaptObject(args['gremlin']);
}


return ({
'requestId': { '@type': 'g:UUID', '@value': id },
'op': op || 'bytecode',
Expand All @@ -215,20 +216,20 @@ class Connection extends EventEmitter {

this._pingInterval = setInterval(() => {
if (this.isOpen === false) {
// in case of if not open..
if (this._pingInterval) {
clearInterval(this._pingInterval);
this._pingInterval = null;
}
// in case of if not open..
if (this._pingInterval) {
clearInterval(this._pingInterval);
this._pingInterval = null;
}
}

this._pongTimeout = setTimeout(() => {
this._ws.terminate();
}, this._pongTimeoutDelay);
this._pongTimeout = setTimeout(() => {
this._ws.terminate();
}, this._pongTimeoutDelay);

this._ws.ping();
this._ws.ping();

}, this._pingIntervalDelay);
}, this._pingIntervalDelay);
}

_handleError(err) {
Expand All @@ -246,8 +247,14 @@ class Connection extends EventEmitter {
this.emit('close', code, message);
}

_handleMessage(data) {
const response = this._reader.read(JSON.parse(data.toString()));
_handleMessage(msg) {
if(msg.data instanceof ArrayBuffer ) {
//if in browser javascript, the data are sent as Uint8
var data = String.fromCharCode.apply(null, new Uint8Array(msg.data));
}else{
data = msg;
}
const response = this._reader.read(JSON.parse(data));
if (response.requestId === null || response.requestId === undefined) {
// There was a serialization issue on the server that prevented the parsing of the request id
// We invoke any of the pending handlers with an error
Expand Down Expand Up @@ -278,7 +285,7 @@ class Connection extends EventEmitter {
if (response.status.code === responseStatusCode.authenticationChallenge && this._authenticator) {
this._authenticator.evaluateChallenge(response.result.data).then(res => {
return this.submit(undefined, 'authentication', res, response.requestId);
}).catch(handler.callback);
}).catch(handler.callback);

return;
}
Expand Down Expand Up @@ -351,10 +358,10 @@ class Connection extends EventEmitter {
// in another map for types like EnumValue. Could be a nicer way to do this but for now it's solving the
// problem with script submission of non JSON native types
if (protocolLevel && key === 'bindings')
newObj[key] = this._adaptArgs(args[key], false);
else
newObj[key] = this._writer.adaptObject(args[key]);
});
newObj[key] = this._adaptArgs(args[key], false);
else
newObj[key] = this._writer.adaptObject(args[key]);
});

return newObj;
}
Expand All @@ -373,8 +380,8 @@ class Connection extends EventEmitter {
if (!this._closePromise) {
this._closePromise = new Promise(resolve => {
this._closeCallback = resolve;
this._ws.close();
});
this._ws.close();
});
}
return this._closePromise;
}
Expand Down
Expand Up @@ -23,7 +23,7 @@
*/
'use strict';

const crypto = require('crypto');
const uuidv4 = require('uuid/v4');

exports.toLong = function toLong(value) {
return new Long(value);
Expand All @@ -37,22 +37,7 @@ const Long = exports.Long = function Long(value) {
};

exports.getUuid = function getUuid() {
const buffer = crypto.randomBytes(16);
//clear the version
buffer[6] &= 0x0f;
//set the version 4
buffer[6] |= 0x40;
//clear the variant
buffer[8] &= 0x3f;
//set the IETF variant
buffer[8] |= 0x80;
const hex = buffer.toString('hex');
return (
hex.substr(0, 8) + '-' +
hex.substr(8, 4) + '-' +
hex.substr(12, 4) + '-' +
hex.substr(16, 4) + '-' +
hex.substr(20, 12));
return uuidv4();
};

exports.emptyArray = Object.freeze([]);
Expand Down
Expand Up @@ -14,15 +14,19 @@
],
"license": "Apache-2.0",
"dependencies": {
"ws": "^6.2.2"
"ws": "^6.2.2",
"util": "^0.11.1",
"events": "^3.0.0",
"uuid": "^3.3.2"
},
"devDependencies": {
"chai": "~4.1.2",
"cucumber": "~4.2.1",
"cucumber": "~5.1.0",
"chai": "~4.2.0",
"grunt": "~1.2.0",
"grunt-cli": "~1.3.2",
"grunt-jsdoc": "~2.4.1",
"mocha": "~5.2.0"
"mocha": "~5.2.0",
"uuid": "^3.3.2"
},
"repository": {
"type": "git",
Expand Down