start werk

This commit is contained in:
Daniel Hübleitner
2019-09-17 23:18:00 +02:00
commit 615619e6e7
3598 changed files with 1238825 additions and 0 deletions
Generated Vendored
+21
View File
@@ -0,0 +1,21 @@
This software is licensed under the MIT License.
Copyright (c) 2017, Christopher Jeffrey (https://github.com/chjj)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+52
View File
@@ -0,0 +1,52 @@
# bsock
A minimal websocket-only implementation of the socket.io protocol, complete
with ES6/ES7 features.
## Usage
``` js
const http = require('http');
const bsock = require('bsock');
const io = bsock.createServer();
const server = http.createServer();
io.attach(server);
io.on('socket', (socket) => {
// Bind = listen for event
socket.bind('bar', (data) => {
console.log('Received bar: %s.', data.toString('ascii'));
});
// Hook = listen for call (event + ack)
socket.hook('foo', async () => {
return Buffer.from('bar');
});
});
server.listen(8000);
const socket = bsock.connect(8000);
socket.on('connect', async () => {
console.log('Calling foo...');
// Call = emit event and wait for ack
const data = await socket.call('foo');
console.log('Response for foo: %s.', data.toString('ascii'));
console.log('Sending bar...');
// Fire = emit event
socket.fire('bar', Buffer.from('baz'));
});
```
## Contribution and License Agreement
If you contribute code to this project, you are implicitly allowing your code
to be distributed under the MIT license. You are also implicitly verifying that
all code is your original work. `</legalese>`
## License
- Copyright (c) 2017, Christopher Jeffrey (MIT License).
See LICENSE for more info.
+6
View File
@@ -0,0 +1,6 @@
'use strict';
module.exports = {
Client: global.WebSocket || global.MozWebSocket,
EventSource: global.EventSource
};
+3
View File
@@ -0,0 +1,3 @@
'use strict';
module.exports = require('../vendor/faye-websocket');
+17
View File
@@ -0,0 +1,17 @@
'use strict';
module.exports = {
connect: true,
connect_error: true,
connect_timeout: true,
connecting: true,
disconnect: true,
error: true,
reconnect: true,
reconnect_attempt: true,
reconnect_failed: true,
reconnect_error: true,
reconnecting: true,
ping: true,
pong: true
};
+14
View File
@@ -0,0 +1,14 @@
'use strict';
const WebSocket = require('./backend');
const Server = require('./server');
const Socket = require('./socket');
exports.WebSocket = WebSocket;
exports.Server = Server;
exports.server = () => new Server();
exports.createServer = Server.createServer.bind(Server);
exports.attach = Server.attach.bind(Server);
exports.Socket = Socket;
exports.socket = () => new Socket();
exports.connect = Socket.connect.bind(Socket);
+21
View File
@@ -0,0 +1,21 @@
'use strict';
// https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
module.exports = {
1000: 'NORMAL_CLOSURE',
1001: 'GOING_AWAY',
1002: 'PROTOCOL_ERROR',
1003: 'UNSUPPORTED_DATA',
1004: 'RESERVED',
1005: 'NO_STATUS_RECVD',
1006: 'ABNORMAL_CLOSURE',
1007: 'INVALID_FRAME_PAYLOAD_DATA',
1008: 'POLICY_VIOLATION',
1009: 'MESSAGE_TOO_BIG',
1010: 'MISSING_EXTENSION',
1011: 'INTERNAL_ERROR',
1012: 'SERVICE_RESTART',
1013: 'TRY_AGAIN_LATER',
1014: 'BAD_GATEWAY',
1015: 'TLS_HANDSHAKE'
};
+127
View File
@@ -0,0 +1,127 @@
'use strict';
const assert = require('bsert');
const DUMMY = Buffer.alloc(0);
const types = {
OPEN: 0,
CLOSE: 1,
PING: 2,
PONG: 3,
MESSAGE: 4,
UPGRADE: 5,
NOOP: 6
};
const table = [
'open',
'close',
'ping',
'pong',
'message',
'upgrade',
'noop'
];
class Frame {
constructor(type, data, binary) {
assert(typeof type === 'number');
assert((type >>> 0) === type);
assert(type <= types.NOOP);
assert(typeof binary === 'boolean');
if (binary) {
if (data == null)
data = DUMMY;
assert(Buffer.isBuffer(data));
} else {
if (data == null)
data = '';
assert(typeof data === 'string');
}
this.type = type;
this.data = data;
this.binary = binary;
}
toString() {
let str = '';
if (this.binary) {
str += 'b';
str += this.type.toString(10);
str += this.data.toString('base64');
} else {
str += this.type.toString(10);
str += this.data;
}
return str;
}
static fromString(str) {
assert(typeof str === 'string');
let type = str.charCodeAt(0);
let binary = false;
let data;
// 'b' - base64
if (type === 0x62) {
assert(str.length > 1);
type = str.charCodeAt(1);
data = Buffer.from(str.substring(2), 'base64');
binary = true;
} else {
data = str.substring(1);
}
type -= 0x30;
assert(type >= 0 && type <= 9);
assert(type <= types.NOOP);
return new this(type, data, binary);
}
size() {
let len = 1;
if (this.binary)
len += this.data.length;
else
len += Buffer.byteLength(this.data, 'utf8');
return len;
}
toRaw() {
const data = Buffer.allocUnsafe(this.size());
data[0] = this.type;
if (this.binary) {
this.data.copy(data, 1);
} else {
if (this.data.length > 0)
data.write(this.data, 1, 'utf8');
}
return data;
}
static fromRaw(data) {
assert(Buffer.isBuffer(data));
assert(data.length > 0);
const type = data[0];
assert(type <= types.NOOP);
return new this(type, data.slice(1), true);
}
}
Frame.types = types;
Frame.table = table;
module.exports = Frame;
+253
View File
@@ -0,0 +1,253 @@
'use strict';
const assert = require('bsert');
const types = {
CONNECT: 0,
DISCONNECT: 1,
EVENT: 2,
ACK: 3,
ERROR: 4,
BINARY_EVENT: 5,
BINARY_ACK: 6
};
class Packet {
constructor(type) {
this.type = type || 0;
this.attachments = 0;
this.nsp = '/';
this.id = -1;
this.data = '';
this.buffers = [];
}
setData(data) {
assert(data !== undefined);
assert(typeof data !== 'number');
assert(typeof data !== 'function');
const [str, buffers] = deconstruct(data);
this.data = str;
this.buffers = buffers;
this.attachments = buffers.length;
if (this.attachments > 0) {
switch (this.type) {
case types.EVENT:
this.type = types.BINARY_EVENT;
break;
case types.ACK:
this.type = types.BINARY_ACK;
break;
}
}
return this;
}
getData() {
if (this.data.length === 0)
return null;
return reconstruct(this.data, this.buffers);
}
toString() {
let str = this.type.toString(10);
switch (this.type) {
case types.BINARY_EVENT:
case types.BINARY_ACK:
str += this.attachments.toString(10) + '-';
break;
}
if (this.nsp !== '/')
str += this.nsp + ',';
if (this.id !== -1)
str += this.id.toString(10);
str += this.data;
return str;
}
static fromString(str) {
assert(typeof str === 'string');
assert(str.length > 0);
let i = 0;
let type = 0;
let attachments = 0;
let nsp = '/';
let id = -1;
let data = '';
[i, type] = readChar(str, i);
assert(type !== -1);
assert(type <= types.BINARY_ACK);
switch (type) {
case types.BINARY_EVENT:
case types.BINARY_ACK: {
[i, attachments] = readInt(str, i);
assert(attachments !== -1);
assert(i < str.length);
assert(str[i] === '-');
i += 1;
break;
}
}
if (i < str.length && str[i] === '/')
[i, nsp] = readTo(str, i, ',');
[i, id] = readInt(str, i);
if (i < str.length)
data = str.substring(i);
const packet = new this();
packet.type = type;
packet.attachments = attachments;
packet.nsp = nsp;
packet.id = id;
packet.data = data;
return packet;
}
}
Packet.types = types;
function isPlaceholder(obj) {
return obj !== null
&& typeof obj === 'object'
&& obj._placeholder === true
&& (obj.num >>> 0) === obj.num;
}
function deconstruct(obj) {
const buffers = [];
const out = replace('', obj, buffers, new Map());
const str = JSON.stringify(out);
return [str, buffers];
}
function replace(key, value, buffers, seen) {
if (value === null || typeof value !== 'object')
return value;
if (Buffer.isBuffer(value)) {
const placeholder = seen.get(value);
// De-duplicate.
if (placeholder != null)
return placeholder;
const out = { _placeholder: true, num: buffers.length };
seen.set(value, out);
buffers.push(value);
return out;
}
if (seen.has(value))
throw new TypeError('Converting circular structure to JSON.');
if (Array.isArray(value)) {
const out = [];
seen.set(value, null);
for (let i = 0; i < value.length; i++)
out.push(replace(i, value[i], buffers, seen));
seen.delete(value);
return out;
}
const out = Object.create(null);
const json = typeof value.toJSON === 'function'
? value.toJSON(key)
: value;
seen.set(value, null);
for (const key of Object.keys(json))
out[key] = replace(key, json[key], buffers, seen);
seen.delete(value);
return out;
}
function reconstruct(str, buffers) {
return JSON.parse(str, (key, value) => {
if (isPlaceholder(value)) {
if (value.num < buffers.length)
return buffers[value.num];
}
return value;
});
}
function readChar(str, i) {
const ch = str.charCodeAt(i) - 0x30;
if (ch < 0 || ch > 9)
return -1;
return [i + 1, ch];
}
function readInt(str, i) {
let len = 0;
let num = 0;
for (; i < str.length; i++) {
const ch = str.charCodeAt(i) - 0x30;
if (ch < 0 || ch > 9)
break;
num *= 10;
num += ch;
len += 1;
assert(len <= 10);
}
assert(num <= 0xffffffff);
if (len === 0)
num = -1;
return [i, num];
}
function readTo(str, i, ch) {
let j = i;
for (; j < str.length; j++) {
if (str[j] === ch)
break;
}
assert(j < str.length);
return [j + 1, str.substring(i, j)];
}
/*
* Expose
*/
module.exports = Packet;
+67
View File
@@ -0,0 +1,67 @@
/*!
* parser.js - packet parser
* Copyright (c) 2017, Christopher Jeffrey (MIT License).
* https://github.com/chjj
*/
'use strict';
const assert = require('bsert');
const EventEmitter = require('events');
const Frame = require('./frame');
const MAX_MESSAGE = 100000000;
class Parser extends EventEmitter {
constructor() {
super();
}
error(msg) {
this.emit('error', new Error(msg));
}
feedBinary(data) {
assert(Buffer.isBuffer(data));
if (data.length > MAX_MESSAGE) {
this.error('Frame too large.');
return;
}
let frame;
try {
frame = Frame.fromRaw(data);
} catch (e) {
this.emit('error', e);
return;
}
this.emit('frame', frame);
}
feedString(data) {
assert(typeof data === 'string');
if (Buffer.byteLength(data, 'utf8') > MAX_MESSAGE) {
this.error('Frame too large.');
return;
}
let frame;
try {
frame = Frame.fromString(data);
} catch (e) {
this.emit('error', e);
return;
}
this.emit('frame', frame);
}
}
/*
* Expose
*/
module.exports = Parser;
+50
View File
@@ -0,0 +1,50 @@
'use strict';
const EventEmitter = require('events');
class Server extends EventEmitter {
constructor(options) {
super();
this.sockets = new Set();
this.channels = new Map();
this.mounts = [];
}
attach() {
return this;
}
mount() {}
async open() {}
async close() {}
join() {
return true;
}
leave() {
return true;
}
channel() {
return null;
}
to() {}
all() {}
static attach(parent, options) {
const server = new this(options);
return server.attach(parent);
}
static createServer(options) {
return new this(options);
}
}
module.exports = Server;
+180
View File
@@ -0,0 +1,180 @@
'use strict';
const assert = require('bsert');
const EventEmitter = require('events');
const Packet = require('./packet');
const WebSocket = require('./backend');
const Socket = require('./socket');
class Server extends EventEmitter {
constructor(options = {}) {
super();
assert(!options.protocols || Array.isArray(options.protocols));
this.protocols = options.protocols || undefined;
this.sockets = new Set();
this.channels = new Map();
this.mounts = [];
this.mounted = false;
}
handleSocket(socket) {
this.add(socket);
socket.on('close', () => {
this.remove(socket);
});
this.emit('socket', socket);
for (const server of this.mounts)
server.emit('socket', socket);
}
mount(server) {
assert(!server.mounted);
server.mounted = true;
server.sockets = this.sockets;
server.channels = this.channels;
this.mounts.push(server);
}
async open() {
;
}
async close() {
if (this.mounted)
return;
for (const socket of this.sockets)
socket.destroy();
}
attach(server) {
const onUpgrade = (req, socket, body) => {
if (!socket.remoteAddress) {
socket.destroy();
return;
}
if (!WebSocket.isWebSocket(req)) {
socket.destroy();
return;
}
const ws = new WebSocket(req, socket, body, this.protocols);
const sock = Socket.accept(this, req, socket, ws);
this.handleSocket(sock);
};
server.on('upgrade', (req, socket, body) => {
try {
onUpgrade(req, socket, body);
} catch (e) {
this.emit('error', e);
}
});
return this;
}
add(socket) {
this.sockets.add(socket);
}
remove(socket) {
for (const name of socket.channels)
this.leave(socket, name);
assert(this.sockets.delete(socket));
}
join(socket, name) {
if (socket.channels.has(name))
return false;
if (!this.channels.has(name))
this.channels.set(name, new Set());
const sockets = this.channels.get(name);
sockets.add(socket);
socket.channels.add(name);
return true;
}
leave(socket, name) {
if (!socket.channels.has(name))
return false;
const sockets = this.channels.get(name);
assert(sockets);
assert(sockets.delete(socket));
if (sockets.size === 0)
this.channels.delete(name);
socket.channels.delete(name);
return true;
}
channel(name) {
const sockets = this.channels.get(name);
if (!sockets)
return null;
assert(sockets.size > 0);
return sockets;
}
event(args) {
assert(args.length > 0, 'Event must be present.');
assert(typeof args[0] === 'string', 'Event must be a string.');
const packet = new Packet();
packet.type = Packet.types.EVENT;
packet.setData(args);
return packet;
}
to(name, ...args) {
const sockets = this.channels.get(name);
if (!sockets)
return;
assert(sockets.size > 0);
// Pre-serialize for speed.
const packet = this.event(args);
for (const socket of sockets)
socket.sendPacket(packet);
}
all(...args) {
// Pre-serialize for speed.
const packet = this.event(args);
for (const socket of this.sockets)
socket.sendPacket(packet);
}
static attach(parent, options) {
const server = new this(options);
return server.attach(parent);
}
static createServer(options) {
return new this(options);
}
}
module.exports = Server;
+1007
View File
File diff suppressed because it is too large Load Diff
+39
View File
@@ -0,0 +1,39 @@
'use strict';
const assert = require('bsert');
const URL = require('url');
exports.parseURL = function parseURL(url) {
if (url.indexOf('://') === -1)
url = `ws://${url}`;
const data = URL.parse(url);
if (data.protocol !== 'http:'
&& data.protocol !== 'https:'
&& data.protocol !== 'ws:'
&& data.protocol !== 'wss:') {
throw new Error('Invalid protocol for websocket URL.');
}
if (!data.hostname)
throw new Error('Malformed URL.');
const host = data.hostname;
let port = 80;
let ssl = false;
if (data.protocol === 'https:' || data.protocol === 'wss:') {
port = 443;
ssl = true;
}
if (data.port) {
port = parseInt(data.port, 10);
assert((port & 0xffff) === port);
assert(port !== 0);
}
return [port, host, ssl];
};
+237
View File
@@ -0,0 +1,237 @@
'use strict';
const assert = require('bsert');
const EventEmitter = require('events');
const UWS = require('uws');
const UWSClient = UWS;
const UWSServer = UWS.Server;
const noop = () => {};
let server = null;
// Make UWS look like Faye.
class API extends EventEmitter {
constructor() {
super();
this.ws = null;
this.readable = true;
this.writable = true;
this.url = '';
this.binaryType = 'arraybuffer';
this.version = 'hybi-13';
this.protocol = '';
this.extensions = '';
this.bufferedAmount = 0;
this.onopen = noop;
this.onclose = noop;
this.onerror = noop;
this.onmessage = noop;
this.on('error', noop);
}
_open(ws, outbound) {
assert(ws);
this.ws = ws;
if (outbound) {
ws.onopen = () => {
this.onopen();
this.emit('open');
};
}
ws.onclose = ({code, reason}) => {
const event = {
code: code >>> 0,
reason: String(reason)
};
this.onclose(event);
this.emit('close', event);
};
ws.onerror = ({message}) => {
if (message === 'uWs client connection error')
message = `Network error: ${this.url}: connect ECONNREFUSED`;
const event = {
message: String(message)
};
this.onerror(event);
this.emit('error', event);
};
ws.onmessage = ({data}) => {
// UWS is zero copy.
if (typeof data !== 'string') {
assert(data instanceof ArrayBuffer);
const ab = Buffer.from(data);
const raw = Buffer.allocUnsafe(ab.length);
ab.copy(raw, 0);
data = raw;
}
const event = { data };
this.onmessage(event);
this.emit('message', event);
};
}
write(data) {
return this.send(data);
}
end(data) {
if (data !== undefined)
this.write(data);
this.close();
}
pause() {
;
}
resume() {
;
}
send(data) {
if (!this.ws)
return true;
this.ws.send(data);
return true;
}
get readyState() {
if (!this.ws)
return API.CONNECTING;
return this.ws.readyState;
}
ping(msg, callback) {
if (!this.ws)
return false;
if (this.readyState > API.OPEN)
return false;
this.ws.ping(msg);
if (callback)
callback();
return true;
}
close() {
if (!this.ws)
return;
this.ws.close();
}
static isWebSocket(req, socket) {
if (socket) {
if (socket._isNative && (!server || server.serverGroup))
return true;
}
if (req.method !== 'GET')
return false;
const connection = req.headers.connection;
if (!connection)
return false;
const conn = connection.toLowerCase().split(/ *, */);
if (conn.indexOf('upgrade') === -1)
return false;
const upgrade = req.headers.upgrade;
if (!upgrade)
return false;
if (upgrade.toLowerCase() !== 'websocket')
return false;
const key = req.headers['sec-websocket-key'];
if (!key)
return false;
if (key.length !== 24)
return false;
if (socket && (!socket.ssl || socket._parent)) {
const {ssl, _handle, _parent} = socket;
const handle = ssl ? _parent._handle : _handle;
if (!handle)
return false;
}
return true;
}
}
API.CONNECTING = 0;
API.OPEN = 1;
API.CLOSING = 2;
API.CLOSED = 3;
API.CLOSE_TIMEOUT = 3000;
class Client extends API {
constructor(url) {
super();
assert(typeof url === 'string');
url = url.replace(/^http:/, 'ws:');
url = url.replace(/^https:/, 'wss:');
if (url.indexOf('://') === -1)
url = `ws://${url}`;
url = url.replace('://localhost', '://127.0.0.1');
this.url = url;
this._open(new UWSClient(url), true);
}
}
class WebSocket extends API {
constructor(req, socket, body) {
super();
assert(req);
this.url = req.url;
if (!server)
server = new UWSServer({ noServer: true });
server.handleUpgrade(req, socket, body, (ws) => {
setImmediate(() => {
this._open(ws, false);
this.onopen();
this.emit('open');
});
});
}
}
WebSocket.Client = Client;
module.exports = WebSocket;
+68
View File
@@ -0,0 +1,68 @@
{
"_from": "bsock",
"_id": "bsock@0.1.9",
"_inBundle": false,
"_integrity": "sha512-/l9Kg/c5o+n/0AqreMxh2jpzDMl1ikl4gUxT7RFNe3A3YRIyZkiREhwcjmqxiymJSRI/Qhew357xGn1SLw/xEw==",
"_location": "/bsock",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "bsock",
"name": "bsock",
"escapedName": "bsock",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/bsock/-/bsock-0.1.9.tgz",
"_shasum": "6aa14b8e4bda730e0f60ec73eb52a8a888ac22c8",
"_spec": "bsock",
"_where": "/home/daniel/development/repos/fw-vendor/rpclib",
"author": {
"name": "Christopher Jeffrey",
"email": "chjjeffrey@gmail.com"
},
"browser": {
"./lib/backend": "./lib/backend-browser.js",
"./lib/server": "./lib/server-browser.js"
},
"bugs": {
"url": "https://github.com/bcoin-org/bsock/issues"
},
"bundleDependencies": false,
"dependencies": {
"bsert": "~0.0.10"
},
"deprecated": false,
"description": "Websocket bike-shed",
"devDependencies": {
"bmocha": "^2.1.0"
},
"engines": {
"node": ">=8.0.0"
},
"homepage": "https://github.com/bcoin-org/bsock",
"keywords": [
"tcp",
"http",
"socket.io",
"websockets"
],
"license": "MIT",
"main": "./lib/bsock.js",
"name": "bsock",
"repository": {
"type": "git",
"url": "git://github.com/bcoin-org/bsock.git"
},
"scripts": {
"lint": "eslint lib/ test/socket-test.js || exit 0",
"test": "bmocha --reporter spec test/*-test.js"
},
"version": "0.1.9"
}
+3494
View File
File diff suppressed because it is too large Load Diff