Commit 7bb309f8 authored by Medicean's avatar Medicean Committed by Medicean

Enhance(Modules/Request): 新增WebSocket连接方式支持,仅RAW模式下可用

parent 3ef6b5ab
......@@ -11,11 +11,11 @@ const fs = require('fs'),
through = require('through'),
CONF = require('./config'),
superagent = require('superagent'),
superagentProxy = require('superagent-proxy');
const {
Readable
} = require("stream");
superagentProxy = require('superagent-proxy'),
ws = require('ws');
const { Readable } = require("stream");
const ProxyAgent = require('proxy-agent');
let wsclients = {};
let logger;
// 请求UA
const USER_AGENT = require('random-fake-useragent');
......@@ -166,6 +166,42 @@ class Request {
}
}
parseWebSocket(tag_s, tag_e, chunkCallBack, sock, callback) {
const tagHexS = Buffer.from(tag_s).toString('hex');
const tagHexE = Buffer.from(tag_e).toString('hex');
let foundTagS = false;
let foundTagE = false;
sock.data = '';
sock.on("message", function(chunk) {
let chunkHex = Buffer.from(chunk).toString('hex');
let temp = '';
if (chunkHex.indexOf(tagHexS) >= 0 && chunkHex.lastIndexOf(tagHexE) >= 0) {
let index_s = chunkHex.indexOf(tagHexS);
let index_e = chunkHex.lastIndexOf(tagHexE);
temp = chunkHex.substr(index_s + tagHexS.length, index_e - index_s - tagHexS.length);
foundTagS = foundTagE = // 如果只包含前截断,则截取后边
true;
} else if (chunkHex.indexOf(tagHexS) >= 0 && chunkHex.lastIndexOf(tagHexE) === -1) {
temp = chunkHex.split(tagHexS)[1];
foundTagS = // 如果只包含后截断,则截取前边
true;
} else if (chunkHex.indexOf(tagHexS) === -1 && chunkHex.lastIndexOf(tagHexE) >= 0) {
temp = chunkHex.split(tagHexE)[0];
foundTagE = // 如果有没有,那就是中途迷路的数据啦 ^.^
true;
} else if (foundTagS && !foundTagE) {
temp = chunkHex;
}
let finalData = Buffer.from(temp, 'hex');
chunkCallBack(finalData);
sock.data += finalData;
});
sock.on("close", function() {
logger.info(`ws onclose data.size=${sock.data.length}`, sock.data);
callback(null, Buffer.from(sock.data, 'binary'));
});
}
/**
* 监听HTTP请求
* @param {Object} event ipcMain事件对象
......@@ -178,6 +214,50 @@ class Request {
if (opts['url'].match(CONF.urlblacklist)) {
return event.sender.send('request-error-' + opts['hash'], "Blacklist URL");
}
// ws
if (opts['useWebSocket'] == 1) {
let wsopts = {
agent: new ProxyAgent(APROXY_CONF['uri']),
}
let sock = new ws(opts['url'], wsopts);
sock.on("open", function() {
let _tmp = encodeURIComponent(opts.data[opts['pwd']]).replace(/asunescape\((.+?)\)/g, function($, $1) {
return unescape($1);
});
_tmp = unescape(_tmp);
logger.debug('onRequest::wsopen-send', _tmp);
sock.send(_tmp);
});
sock.on("error", function(err) {
event.sender.send('request-error-' + opts['hash'], err);
});
self.parseWebSocket(opts['tag_s'], opts['tag_e'], (chunk) => {
event.sender.send('request-chunk-' + opts['hash'], chunk);
sock.close();
}, sock, (err, ret) => {
if (!ret) {
return event.sender.send('request-error-' + opts['hash'], err);
}
let buff = Buffer.from(ret);
let text = '';
let encoding = detectEncoding(buff, {
defaultEncoding: "unknown"
});
logger.debug("detect encoding:", encoding);
encoding = encoding != "unknown" ? encoding : opts['encode'];
text = iconv.decode(buff, encoding);
if (err && text == "") {
return event.sender.send('request-error-' + opts['hash'], err);
};
event.sender.send('request-' + opts['hash'], {
text: text,
buff: buff,
encoding: encoding
});
});
return;
};
// http
self.reqContentType = "form";
let _request = superagent.post(opts['url']);
self.buildHeaders(_request, opts);
......@@ -305,6 +385,38 @@ class Request {
let indexStart = -1;
let indexEnd = -1;
let tempData = [];
// ws
if (opts['useWebSocket'] == 1) {
let wsopts = {
agent: new ProxyAgent(APROXY_CONF['uri']),
}
let sock = new ws(opts['url'], wsopts);
sock.on("open", function() {
let _tmp = encodeURIComponent(opts.data[opts['pwd']]).replace(/asunescape\((.+?)\)/g, function($, $1) {
return unescape($1);
});
_tmp = unescape(_tmp);
logger.debug('onDownlaod::wsopen-send', _tmp);
sock.send(_tmp);
});
sock.on("error", function(err) {
event.sender.send('download-error-' + opts['hash'], err);
});
self.parseWebSocket(opts['tag_s'], opts['tag_e'], (chunk) => {
event.sender.send('download-progress-' + opts['hash'], chunk.length);
sock.close();
}, sock, (err, ret) => {
if (!ret) {
return event.sender.send('download-error-' + opts['hash'], err);
}
rs.write(ret);
rs.close();
event.sender.send('download-' + opts['hash'], ret.length);
});
return;
};
self.reqContentType = "form";
let _request = superagent.post(opts['url']);
// 设置headers
......
../esprima/bin/esparse.js
\ No newline at end of file
../esprima-fb/bin/esparse.js
\ No newline at end of file
../esprima/bin/esvalidate.js
\ No newline at end of file
../esprima-fb/bin/esvalidate.js
\ No newline at end of file
......@@ -138,6 +138,11 @@
"resolved": "https://registry.npm.taobao.org/async/download/async-0.2.10.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
},
"node_modules/async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
......@@ -2166,23 +2171,11 @@
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
},
"node_modules/ws": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz",
"integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"dependencies": {
"async-limiter": "~1.0.0"
}
},
"node_modules/xml-name-validator": {
......
coverage
.nyc_output
\ No newline at end of file
{
"check-coverage": false,
"lines": 99,
"statements": 99,
"functions": 99,
"branches": 99,
"include": [
"index.js"
]
}
\ No newline at end of file
language: node_js
node_js:
- "6"
- "8"
- "10"
- "node"
script: npm run travis
cache:
yarn: true
The MIT License (MIT)
Copyright (c) 2017 Samuel Reed <samuel.trace.reed@gmail.com>
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.
'use strict';
function Queue(options) {
if (!(this instanceof Queue)) {
return new Queue(options);
}
options = options || {};
this.concurrency = options.concurrency || Infinity;
this.pending = 0;
this.jobs = [];
this.cbs = [];
this._done = done.bind(this);
}
var arrayAddMethods = [
'push',
'unshift',
'splice'
];
arrayAddMethods.forEach(function(method) {
Queue.prototype[method] = function() {
var methodResult = Array.prototype[method].apply(this.jobs, arguments);
this._run();
return methodResult;
};
});
Object.defineProperty(Queue.prototype, 'length', {
get: function() {
return this.pending + this.jobs.length;
}
});
Queue.prototype._run = function() {
if (this.pending === this.concurrency) {
return;
}
if (this.jobs.length) {
var job = this.jobs.shift();
this.pending++;
job(this._done);
this._run();
}
if (this.pending === 0) {
while (this.cbs.length !== 0) {
var cb = this.cbs.pop();
process.nextTick(cb);
}
}
};
Queue.prototype.onDone = function(cb) {
if (typeof cb === 'function') {
this.cbs.push(cb);
this._run();
}
};
function done() {
this.pending--;
this._run();
}
module.exports = Queue;
{
"name": "async-limiter",
"version": "1.0.1",
"description": "asynchronous function queue with adjustable concurrency",
"keywords": [
"throttle",
"async",
"limiter",
"asynchronous",
"job",
"task",
"concurrency",
"concurrent"
],
"dependencies": {},
"devDependencies": {
"coveralls": "^3.0.3",
"eslint": "^5.16.0",
"eslint-plugin-mocha": "^5.3.0",
"intelli-espower-loader": "^1.0.1",
"mocha": "^6.1.4",
"nyc": "^14.1.1",
"power-assert": "^1.6.1"
},
"scripts": {
"test": "mocha --require intelli-espower-loader test/",
"travis": "npm run lint && npm run test",
"coverage": "nyc npm test && nyc report --reporter=text-lcov | coveralls",
"example": "node example",
"lint": "eslint ."
},
"repository": "https://github.com/strml/async-limiter.git",
"author": "Samuel Reed <samuel.trace.reed@gmail.com",
"license": "MIT"
}
# Async-Limiter
A module for limiting concurrent asynchronous actions in flight. Forked from [queue](https://github.com/jessetane/queue).
[![npm](http://img.shields.io/npm/v/async-limiter.svg?style=flat-square)](http://www.npmjs.org/async-limiter)
[![tests](https://img.shields.io/travis/STRML/async-limiter.svg?style=flat-square&branch=master)](https://travis-ci.org/STRML/async-limiter)
[![coverage](https://img.shields.io/coveralls/STRML/async-limiter.svg?style=flat-square&branch=master)](https://coveralls.io/r/STRML/async-limiter)
This module exports a class `Limiter` that implements some of the `Array` API.
Pass async functions (ones that accept a callback or return a promise) to an instance's additive array methods.
## Motivation
Certain functions, like `zlib`, have [undesirable behavior](https://github.com/nodejs/node/issues/8871#issuecomment-250915913) when
run at infinite concurrency.
In this case, it is actually faster, and takes far less memory, to limit concurrency.
This module should do the absolute minimum work necessary to queue up functions. PRs are welcome that would
make this module faster or lighter, but new functionality is not desired.
Style should confirm to nodejs/node style.
## Example
``` javascript
var Limiter = require('async-limiter')
var t = new Limiter({concurrency: 2});
var results = []
// add jobs using the familiar Array API
t.push(function (cb) {
results.push('two')
cb()
})
t.push(
function (cb) {
results.push('four')
cb()
},
function (cb) {
results.push('five')
cb()
}
)
t.unshift(function (cb) {
results.push('one')
cb()
})
t.splice(2, 0, function (cb) {
results.push('three')
cb()
})
// Jobs run automatically. If you want a callback when all are done,
// call 'onDone()'.
t.onDone(function () {
console.log('all done:', results)
})
```
## Zlib Example
```js
const zlib = require('zlib');
const Limiter = require('async-limiter');
const message = {some: "data"};
const payload = new Buffer(JSON.stringify(message));
// Try with different concurrency values to see how this actually
// slows significantly with higher concurrency!
//
// 5: 1398.607ms
// 10: 1375.668ms
// Infinity: 4423.300ms
//
const t = new Limiter({concurrency: 5});
function deflate(payload, cb) {
t.push(function(done) {
zlib.deflate(payload, function(err, buffer) {
done();
cb(err, buffer);
});
});
}
console.time('deflate');
for(let i = 0; i < 30000; ++i) {
deflate(payload, function (err, buffer) {});
}
t.onDone(function() {
console.timeEnd('deflate');
});
```
## Install
`npm install async-limiter`
## Test
`npm test`
## API
### `var t = new Limiter([opts])`
Constructor. `opts` may contain inital values for:
* `t.concurrency`
## Instance methods
### `t.onDone(fn)`
`fn` will be called once and only once, when the queue is empty.
## Instance methods mixed in from `Array`
Mozilla has docs on how these methods work [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array).
### `t.push(element1, ..., elementN)`
### `t.unshift(element1, ..., elementN)`
### `t.splice(index , howMany[, element1[, ...[, elementN]]])`
## Properties
### `t.concurrency`
Max number of jobs the queue should process concurrently, defaults to `Infinity`.
### `t.length`
Jobs pending + jobs to process (readonly).
The MIT License (MIT)
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
......
This diff is collapsed.
'use strict';
module.exports = function () {
module.exports = function() {
throw new Error(
'ws does not work in the browser. Browser clients must use the native ' +
'WebSocket object'
......
......@@ -2,12 +2,8 @@
const WebSocket = require('./lib/websocket');
WebSocket.createWebSocketStream = require('./lib/stream');
WebSocket.Server = require('./lib/websocket-server');
WebSocket.Receiver = require('./lib/receiver');
WebSocket.Sender = require('./lib/sender');
WebSocket.WebSocket = WebSocket;
WebSocket.WebSocketServer = WebSocket.Server;
module.exports = WebSocket;
'use strict';
const { EMPTY_BUFFER } = require('./constants');
/**
* Merges an array of buffers into a new buffer.
*
......@@ -11,20 +9,15 @@ const { EMPTY_BUFFER } = require('./constants');
* @public
*/
function concat(list, totalLength) {
if (list.length === 0) return EMPTY_BUFFER;
if (list.length === 1) return list[0];
const target = Buffer.allocUnsafe(totalLength);
let offset = 0;
var offset = 0;
for (let i = 0; i < list.length; i++) {
for (var i = 0; i < list.length; i++) {
const buf = list[i];
target.set(buf, offset);
buf.copy(target, offset);
offset += buf.length;
}
if (offset < totalLength) return target.slice(0, offset);
return target;
}
......@@ -39,7 +32,7 @@ function concat(list, totalLength) {
* @public
*/
function _mask(source, mask, output, offset, length) {
for (let i = 0; i < length; i++) {
for (var i = 0; i < length; i++) {
output[offset + i] = source[i] ^ mask[i & 3];
}
}
......@@ -52,76 +45,28 @@ function _mask(source, mask, output, offset, length) {
* @public
*/
function _unmask(buffer, mask) {
for (let i = 0; i < buffer.length; i++) {
// Required until https://github.com/nodejs/node/issues/9006 is resolved.
const length = buffer.length;
for (var i = 0; i < length; i++) {
buffer[i] ^= mask[i & 3];
}
}
/**
* Converts a buffer to an `ArrayBuffer`.
*
* @param {Buffer} buf The buffer to convert
* @return {ArrayBuffer} Converted buffer
* @public
*/
function toArrayBuffer(buf) {
if (buf.byteLength === buf.buffer.byteLength) {
return buf.buffer;
}
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
}
/**
* Converts `data` to a `Buffer`.
*
* @param {*} data The data to convert
* @return {Buffer} The buffer
* @throws {TypeError}
* @public
*/
function toBuffer(data) {
toBuffer.readOnly = true;
if (Buffer.isBuffer(data)) return data;
let buf;
if (data instanceof ArrayBuffer) {
buf = Buffer.from(data);
} else if (ArrayBuffer.isView(data)) {
buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
} else {
buf = Buffer.from(data);
toBuffer.readOnly = false;
}
return buf;
}
module.exports = {
concat,
mask: _mask,
toArrayBuffer,
toBuffer,
unmask: _unmask
};
/* istanbul ignore else */
if (!process.env.WS_NO_BUFFER_UTIL) {
try {
try {
const bufferUtil = require('bufferutil');
const bu = bufferUtil.BufferUtil || bufferUtil;
module.exports.mask = function (source, mask, output, offset, length) {
module.exports = {
mask(source, mask, output, offset, length) {
if (length < 48) _mask(source, mask, output, offset, length);
else bufferUtil.mask(source, mask, output, offset, length);
};
module.exports.unmask = function (buffer, mask) {
else bu.mask(source, mask, output, offset, length);
},
unmask(buffer, mask) {
if (buffer.length < 32) _unmask(buffer, mask);
else bufferUtil.unmask(buffer, mask);
else bu.unmask(buffer, mask);
},
concat
};
} catch (e) {
// Continue regardless of the error.
}
} catch (e) /* istanbul ignore next */ {
module.exports = { concat, mask: _mask, unmask: _unmask };
}
......@@ -2,11 +2,9 @@
module.exports = {
BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
EMPTY_BUFFER: Buffer.alloc(0),
GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
kForOnEventAttribute: Symbol('kIsForOnEventAttribute'),
kListener: Symbol('kListener'),
kStatusCode: Symbol('status-code'),
kWebSocket: Symbol('websocket'),
EMPTY_BUFFER: Buffer.alloc(0),
NOOP: () => {}
};
'use strict';
const { kForOnEventAttribute, kListener } = require('./constants');
const kCode = Symbol('kCode');
const kData = Symbol('kData');
const kError = Symbol('kError');
const kMessage = Symbol('kMessage');
const kReason = Symbol('kReason');
const kTarget = Symbol('kTarget');
const kType = Symbol('kType');
const kWasClean = Symbol('kWasClean');
/**
* Class representing an event.
*
* @private
*/
class Event {
/**
* Create a new `Event`.
*
* @param {String} type The name of the event
* @throws {TypeError} If the `type` argument is not specified
* @param {Object} target A reference to the target to which the event was dispatched
*/
constructor(type) {
this[kTarget] = null;
this[kType] = type;
constructor(type, target) {
this.target = target;
this.type = type;
}
}
/**
* @type {*}
/**
* Class representing a message event.
*
* @extends Event
* @private
*/
get target() {
return this[kTarget];
}
class MessageEvent extends Event {
/**
* @type {String}
* Create a new `MessageEvent`.
*
* @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
get type() {
return this[kType];
constructor(data, target) {
super('message', target);
this.data = data;
}
}
Object.defineProperty(Event.prototype, 'target', { enumerable: true });
Object.defineProperty(Event.prototype, 'type', { enumerable: true });
/**
* Class representing a close event.
*
* @extends Event
* @private
*/
class CloseEvent extends Event {
/**
* Create a new `CloseEvent`.
*
* @param {String} type The name of the event
* @param {Object} [options] A dictionary object that allows for setting
* attributes via object members of the same name
* @param {Number} [options.code=0] The status code explaining why the
* connection was closed
* @param {String} [options.reason=''] A human-readable string explaining why
* the connection was closed
* @param {Boolean} [options.wasClean=false] Indicates whether or not the
* connection was cleanly closed
*/
constructor(type, options = {}) {
super(type);
this[kCode] = options.code === undefined ? 0 : options.code;
this[kReason] = options.reason === undefined ? '' : options.reason;
this[kWasClean] = options.wasClean === undefined ? false : options.wasClean;
}
/**
* @type {Number}
*/
get code() {
return this[kCode];
}
/**
* @type {String}
* @param {Number} code The status code explaining why the connection is being closed
* @param {String} reason A human-readable string explaining why the connection is closing
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
get reason() {
return this[kReason];
}
constructor(code, reason, target) {
super('close', target);
/**
* @type {Boolean}
*/
get wasClean() {
return this[kWasClean];
this.wasClean = target._closeFrameReceived && target._closeFrameSent;
this.reason = reason;
this.code = code;
}
}
Object.defineProperty(CloseEvent.prototype, 'code', { enumerable: true });
Object.defineProperty(CloseEvent.prototype, 'reason', { enumerable: true });
Object.defineProperty(CloseEvent.prototype, 'wasClean', { enumerable: true });
/**
* Class representing an error event.
* Class representing an open event.
*
* @extends Event
* @private
*/
class ErrorEvent extends Event {
class OpenEvent extends Event {
/**
* Create a new `ErrorEvent`.
* Create a new `OpenEvent`.
*
* @param {String} type The name of the event
* @param {Object} [options] A dictionary object that allows for setting
* attributes via object members of the same name
* @param {*} [options.error=null] The error that generated this event
* @param {String} [options.message=''] The error message
*/
constructor(type, options = {}) {
super(type);
this[kError] = options.error === undefined ? null : options.error;
this[kMessage] = options.message === undefined ? '' : options.message;
}
/**
* @type {*}
*/
get error() {
return this[kError];
}
/**
* @type {String}
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
get message() {
return this[kMessage];
constructor(target) {
super('open', target);
}
}
Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
/**
* Class representing a message event.
* Class representing an error event.
*
* @extends Event
* @private
*/
class MessageEvent extends Event {
class ErrorEvent extends Event {
/**
* Create a new `MessageEvent`.
* Create a new `ErrorEvent`.
*
* @param {String} type The name of the event
* @param {Object} [options] A dictionary object that allows for setting
* attributes via object members of the same name
* @param {*} [options.data=null] The message content
* @param {Object} error The error that generated this event
* @param {WebSocket} target A reference to the target to which the event was dispatched
*/
constructor(type, options = {}) {
super(type);
this[kData] = options.data === undefined ? null : options.data;
}
constructor(error, target) {
super('error', target);
/**
* @type {*}
*/
get data() {
return this[kData];
this.message = error.message;
this.error = error;
}
}
Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
/**
* This provides methods for emulating the `EventTarget` interface. It's not
* meant to be used directly.
......@@ -177,90 +109,62 @@ const EventTarget = {
/**
* Register an event listener.
*
* @param {String} type A string representing the event type to listen for
* @param {String} method A string representing the event type to listen for
* @param {Function} listener The listener to add
* @param {Object} [options] An options object specifies characteristics about
* the event listener
* @param {Boolean} [options.once=false] A `Boolean` indicating that the
* listener should be invoked at most once after being added. If `true`,
* the listener would be automatically removed when invoked.
* @public
*/
addEventListener(type, listener, options = {}) {
let wrapper;
addEventListener(method, listener) {
if (typeof listener !== 'function') return;
if (type === 'message') {
wrapper = function onMessage(data, isBinary) {
const event = new MessageEvent('message', {
data: isBinary ? data : data.toString()
});
event[kTarget] = this;
listener.call(this, event);
};
} else if (type === 'close') {
wrapper = function onClose(code, message) {
const event = new CloseEvent('close', {
code,
reason: message.toString(),
wasClean: this._closeFrameReceived && this._closeFrameSent
});
event[kTarget] = this;
listener.call(this, event);
};
} else if (type === 'error') {
wrapper = function onError(error) {
const event = new ErrorEvent('error', {
error,
message: error.message
});
function onMessage(data) {
listener.call(this, new MessageEvent(data, this));
}
event[kTarget] = this;
listener.call(this, event);
};
} else if (type === 'open') {
wrapper = function onOpen() {
const event = new Event('open');
function onClose(code, message) {
listener.call(this, new CloseEvent(code, message, this));
}
event[kTarget] = this;
listener.call(this, event);
};
} else {
return;
function onError(error) {
listener.call(this, new ErrorEvent(error, this));
}
wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
wrapper[kListener] = listener;
function onOpen() {
listener.call(this, new OpenEvent(this));
}
if (options.once) {
this.once(type, wrapper);
if (method === 'message') {
onMessage._listener = listener;
this.on(method, onMessage);
} else if (method === 'close') {
onClose._listener = listener;
this.on(method, onClose);
} else if (method === 'error') {
onError._listener = listener;
this.on(method, onError);
} else if (method === 'open') {
onOpen._listener = listener;
this.on(method, onOpen);
} else {
this.on(type, wrapper);
this.on(method, listener);
}
},
/**
* Remove an event listener.
*
* @param {String} type A string representing the event type to remove
* @param {Function} handler The listener to remove
* @param {String} method A string representing the event type to remove
* @param {Function} listener The listener to remove
* @public
*/
removeEventListener(type, handler) {
for (const listener of this.listeners(type)) {
if (listener[kListener] === handler && !listener[kForOnEventAttribute]) {
this.removeListener(type, listener);
break;
removeEventListener(method, listener) {
const listeners = this.listeners(method);
for (var i = 0; i < listeners.length; i++) {
if (listeners[i] === listener || listeners[i]._listener === listener) {
this.removeListener(method, listeners[i]);
}
}
}
};
module.exports = {
CloseEvent,
ErrorEvent,
Event,
EventTarget,
MessageEvent
};
module.exports = EventTarget;
'use strict';
const { tokenChars } = require('./validation');
//
// Allowed token characters:
//
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
//
// tokenChars[32] === 0 // ' '
// tokenChars[33] === 1 // '!'
// tokenChars[34] === 0 // '"'
// ...
//
// prettier-ignore
const tokenChars = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
];
/**
* Adds an offer to the map of extension offers or a parameter to the map of
......@@ -13,8 +34,8 @@ const { tokenChars } = require('./validation');
* @private
*/
function push(dest, name, elem) {
if (dest[name] === undefined) dest[name] = [elem];
else dest[name].push(elem);
if (Object.prototype.hasOwnProperty.call(dest, name)) dest[name].push(elem);
else dest[name] = [elem];
}
/**
......@@ -25,28 +46,26 @@ function push(dest, name, elem) {
* @public
*/
function parse(header) {
const offers = Object.create(null);
let params = Object.create(null);
let mustUnescape = false;
let isEscaping = false;
let inQuotes = false;
let extensionName;
let paramName;
let start = -1;
let code = -1;
let end = -1;
let i = 0;
for (; i < header.length; i++) {
code = header.charCodeAt(i);
const offers = {};
if (header === undefined || header === '') return offers;
var params = {};
var mustUnescape = false;
var isEscaping = false;
var inQuotes = false;
var extensionName;
var paramName;
var start = -1;
var end = -1;
for (var i = 0; i < header.length; i++) {
const code = header.charCodeAt(i);
if (extensionName === undefined) {
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (
i !== 0 &&
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
) {
} else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
if (start === -1) {
......@@ -57,7 +76,7 @@ function parse(header) {
const name = header.slice(start, end);
if (code === 0x2c) {
push(offers, name, params);
params = Object.create(null);
params = {};
} else {
extensionName = name;
}
......@@ -80,7 +99,7 @@ function parse(header) {
push(params, header.slice(start, end), true);
if (code === 0x2c) {
push(offers, extensionName, params);
params = Object.create(null);
params = {};
extensionName = undefined;
}
......@@ -127,7 +146,7 @@ function parse(header) {
}
if (end === -1) end = i;
let value = header.slice(start, end);
var value = header.slice(start, end);
if (mustUnescape) {
value = value.replace(/\\/g, '');
mustUnescape = false;
......@@ -135,7 +154,7 @@ function parse(header) {
push(params, paramName, value);
if (code === 0x2c) {
push(offers, extensionName, params);
params = Object.create(null);
params = {};
extensionName = undefined;
}
......@@ -147,14 +166,14 @@ function parse(header) {
}
}
if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
if (start === -1 || inQuotes) {
throw new SyntaxError('Unexpected end of input');
}
if (end === -1) end = i;
const token = header.slice(start, end);
if (extensionName === undefined) {
push(offers, token, params);
push(offers, token, {});
} else {
if (paramName === undefined) {
push(params, token, true);
......@@ -179,14 +198,14 @@ function parse(header) {
function format(extensions) {
return Object.keys(extensions)
.map((extension) => {
let configurations = extensions[extension];
var configurations = extensions[extension];
if (!Array.isArray(configurations)) configurations = [configurations];
return configurations
.map((params) => {
return [extension]
.concat(
Object.keys(params).map((k) => {
let values = params[k];
var values = params[k];
if (!Array.isArray(values)) values = [values];
return values
.map((v) => (v === true ? k : `${k}=${v}`))
......
'use strict';
const kDone = Symbol('kDone');
const kRun = Symbol('kRun');
/**
* A very simple job queue with adjustable concurrency. Adapted from
* https://github.com/STRML/async-limiter
*/
class Limiter {
/**
* Creates a new `Limiter`.
*
* @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
* to run concurrently
*/
constructor(concurrency) {
this[kDone] = () => {
this.pending--;
this[kRun]();
};
this.concurrency = concurrency || Infinity;
this.jobs = [];
this.pending = 0;
}
/**
* Adds a job to the queue.
*
* @param {Function} job The job to run
* @public
*/
add(job) {
this.jobs.push(job);
this[kRun]();
}
/**
* Removes a job from the queue and runs it if possible.
*
* @private
*/
[kRun]() {
if (this.pending === this.concurrency) return;
if (this.jobs.length) {
const job = this.jobs.shift();
this.pending++;
job(this[kDone]);
}
}
}
module.exports = Limiter;
'use strict';
const Limiter = require('async-limiter');
const zlib = require('zlib');
const bufferUtil = require('./buffer-util');
const Limiter = require('./limiter');
const { kStatusCode } = require('./constants');
const { kStatusCode, NOOP } = require('./constants');
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
const EMPTY_BLOCK = Buffer.from([0x00]);
const kPerMessageDeflate = Symbol('permessage-deflate');
const kTotalLength = Symbol('total-length');
const kCallback = Symbol('callback');
......@@ -29,26 +31,24 @@ class PerMessageDeflate {
/**
* Creates a PerMessageDeflate instance.
*
* @param {Object} [options] Configuration options
* @param {(Boolean|Number)} [options.clientMaxWindowBits] Advertise support
* for, or request, a custom client window size
* @param {Boolean} [options.clientNoContextTakeover=false] Advertise/
* acknowledge disabling of client context takeover
* @param {Number} [options.concurrencyLimit=10] The number of concurrent
* calls to zlib
* @param {(Boolean|Number)} [options.serverMaxWindowBits] Request/confirm the
* @param {Object} options Configuration options
* @param {Boolean} options.serverNoContextTakeover Request/accept disabling
* of server context takeover
* @param {Boolean} options.clientNoContextTakeover Advertise/acknowledge
* disabling of client context takeover
* @param {(Boolean|Number)} options.serverMaxWindowBits Request/confirm the
* use of a custom server window size
* @param {Boolean} [options.serverNoContextTakeover=false] Request/accept
* disabling of server context takeover
* @param {Number} [options.threshold=1024] Size (in bytes) below which
* messages should not be compressed if context takeover is disabled
* @param {Object} [options.zlibDeflateOptions] Options to pass to zlib on
* deflate
* @param {Object} [options.zlibInflateOptions] Options to pass to zlib on
* inflate
* @param {Boolean} [isServer=false] Create the instance in either server or
* client mode
* @param {Number} [maxPayload=0] The maximum allowed message length
* @param {(Boolean|Number)} options.clientMaxWindowBits Advertise support
* for, or request, a custom client window size
* @param {Object} options.zlibDeflateOptions Options to pass to zlib on deflate
* @param {Object} options.zlibInflateOptions Options to pass to zlib on inflate
* @param {Number} options.threshold Size (in bytes) below which messages
* should not be compressed
* @param {Number} options.concurrencyLimit The number of concurrent calls to
* zlib
* @param {Boolean} isServer Create the instance in either server or client
* mode
* @param {Number} maxPayload The maximum allowed message length
*/
constructor(options, isServer, maxPayload) {
this._maxPayload = maxPayload | 0;
......@@ -66,7 +66,7 @@ class PerMessageDeflate {
this._options.concurrencyLimit !== undefined
? this._options.concurrencyLimit
: 10;
zlibLimiter = new Limiter(concurrency);
zlibLimiter = new Limiter({ concurrency });
}
}
......@@ -133,18 +133,8 @@ class PerMessageDeflate {
}
if (this._deflate) {
const callback = this._deflate[kCallback];
this._deflate.close();
this._deflate = null;
if (callback) {
callback(
new Error(
'The deflate stream was closed while data was being processed'
)
);
}
}
}
......@@ -243,7 +233,7 @@ class PerMessageDeflate {
normalizeParams(configurations) {
configurations.forEach((params) => {
Object.keys(params).forEach((key) => {
let value = params[key];
var value = params[key];
if (value.length > 1) {
throw new Error(`Parameter "${key}" must have only a single value`);
......@@ -294,7 +284,7 @@ class PerMessageDeflate {
}
/**
* Decompress data. Concurrency limited.
* Decompress data. Concurrency limited by async-limiter.
*
* @param {Buffer} data Compressed data
* @param {Boolean} fin Specifies whether or not this is the last fragment
......@@ -302,7 +292,7 @@ class PerMessageDeflate {
* @public
*/
decompress(data, fin, callback) {
zlibLimiter.add((done) => {
zlibLimiter.push((done) => {
this._decompress(data, fin, (err, result) => {
done();
callback(err, result);
......@@ -311,15 +301,15 @@ class PerMessageDeflate {
}
/**
* Compress data. Concurrency limited.
* Compress data. Concurrency limited by async-limiter.
*
* @param {(Buffer|String)} data Data to compress
* @param {Buffer} data Data to compress
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @public
*/
compress(data, fin, callback) {
zlibLimiter.add((done) => {
zlibLimiter.push((done) => {
this._compress(data, fin, (err, result) => {
done();
callback(err, result);
......@@ -345,10 +335,9 @@ class PerMessageDeflate {
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
this._inflate = zlib.createInflateRaw({
...this._options.zlibInflateOptions,
windowBits
});
this._inflate = zlib.createInflateRaw(
Object.assign({}, this._options.zlibInflateOptions, { windowBits })
);
this._inflate[kPerMessageDeflate] = this;
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
......@@ -376,16 +365,12 @@ class PerMessageDeflate {
this._inflate[kTotalLength]
);
if (this._inflate._readableState.endEmitted) {
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._inflate.close();
this._inflate = null;
} else {
this._inflate[kTotalLength] = 0;
this._inflate[kBuffers] = [];
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._inflate.reset();
}
}
callback(null, data);
......@@ -395,12 +380,17 @@ class PerMessageDeflate {
/**
* Compress data.
*
* @param {(Buffer|String)} data Data to compress
* @param {Buffer} data Data to compress
* @param {Boolean} fin Specifies whether or not this is the last fragment
* @param {Function} callback Callback
* @private
*/
_compress(data, fin, callback) {
if (!data || data.length === 0) {
process.nextTick(callback, null, EMPTY_BLOCK);
return;
}
const endpoint = this._isServer ? 'server' : 'client';
if (!this._deflate) {
......@@ -410,46 +400,48 @@ class PerMessageDeflate {
? zlib.Z_DEFAULT_WINDOWBITS
: this.params[key];
this._deflate = zlib.createDeflateRaw({
...this._options.zlibDeflateOptions,
windowBits
});
this._deflate = zlib.createDeflateRaw(
Object.assign({}, this._options.zlibDeflateOptions, { windowBits })
);
this._deflate[kTotalLength] = 0;
this._deflate[kBuffers] = [];
//
// An `'error'` event is emitted, only on Node.js < 10.0.0, if the
// `zlib.DeflateRaw` instance is closed while data is being processed.
// This can happen if `PerMessageDeflate#cleanup()` is called at the wrong
// time due to an abnormal WebSocket closure.
//
this._deflate.on('error', NOOP);
this._deflate.on('data', deflateOnData);
}
this._deflate[kCallback] = callback;
this._deflate.write(data);
this._deflate.flush(zlib.Z_SYNC_FLUSH, () => {
if (!this._deflate) {
//
// The deflate stream was closed while data was being processed.
// This `if` statement is only needed for Node.js < 10.0.0 because as of
// commit https://github.com/nodejs/node/commit/5e3f5164, the flush
// callback is no longer called if the deflate stream is closed while
// data is being processed.
//
return;
}
let data = bufferUtil.concat(
var data = bufferUtil.concat(
this._deflate[kBuffers],
this._deflate[kTotalLength]
);
if (fin) data = data.slice(0, data.length - 4);
//
// Ensure that the callback will not be called again in
// `PerMessageDeflate#cleanup()`.
//
this._deflate[kCallback] = null;
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._deflate.close();
this._deflate = null;
} else {
this._deflate[kTotalLength] = 0;
this._deflate[kBuffers] = [];
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
this._deflate.reset();
}
callback(null, data);
......@@ -488,7 +480,6 @@ function inflateOnData(chunk) {
}
this[kError] = new RangeError('Max payload size exceeded');
this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
this[kError][kStatusCode] = 1009;
this.removeListener('data', inflateOnData);
this.reset();
......
This diff is collapsed.
This diff is collapsed.
'use strict';
const { Duplex } = require('stream');
/**
* Emits the `'close'` event on a stream.
*
* @param {Duplex} stream The stream.
* @private
*/
function emitClose(stream) {
stream.emit('close');
}
/**
* The listener of the `'end'` event.
*
* @private
*/
function duplexOnEnd() {
if (!this.destroyed && this._writableState.finished) {
this.destroy();
}
}
/**
* The listener of the `'error'` event.
*
* @param {Error} err The error
* @private
*/
function duplexOnError(err) {
this.removeListener('error', duplexOnError);
this.destroy();
if (this.listenerCount('error') === 0) {
// Do not suppress the throwing behavior.
this.emit('error', err);
}
}
/**
* Wraps a `WebSocket` in a duplex stream.
*
* @param {WebSocket} ws The `WebSocket` to wrap
* @param {Object} [options] The options for the `Duplex` constructor
* @return {Duplex} The duplex stream
* @public
*/
function createWebSocketStream(ws, options) {
let terminateOnDestroy = true;
const duplex = new Duplex({
...options,
autoDestroy: false,
emitClose: false,
objectMode: false,
writableObjectMode: false
});
ws.on('message', function message(msg, isBinary) {
const data =
!isBinary && duplex._readableState.objectMode ? msg.toString() : msg;
if (!duplex.push(data)) ws.pause();
});
ws.once('error', function error(err) {
if (duplex.destroyed) return;
// Prevent `ws.terminate()` from being called by `duplex._destroy()`.
//
// - If the `'error'` event is emitted before the `'open'` event, then
// `ws.terminate()` is a noop as no socket is assigned.
// - Otherwise, the error is re-emitted by the listener of the `'error'`
// event of the `Receiver` object. The listener already closes the
// connection by calling `ws.close()`. This allows a close frame to be
// sent to the other peer. If `ws.terminate()` is called right after this,
// then the close frame might not be sent.
terminateOnDestroy = false;
duplex.destroy(err);
});
ws.once('close', function close() {
if (duplex.destroyed) return;
duplex.push(null);
});
duplex._destroy = function (err, callback) {
if (ws.readyState === ws.CLOSED) {
callback(err);
process.nextTick(emitClose, duplex);
return;
}
let called = false;
ws.once('error', function error(err) {
called = true;
callback(err);
});
ws.once('close', function close() {
if (!called) callback(err);
process.nextTick(emitClose, duplex);
});
if (terminateOnDestroy) ws.terminate();
};
duplex._final = function (callback) {
if (ws.readyState === ws.CONNECTING) {
ws.once('open', function open() {
duplex._final(callback);
});
return;
}
// If the value of the `_socket` property is `null` it means that `ws` is a
// client websocket and the handshake failed. In fact, when this happens, a
// socket is never assigned to the websocket. Wait for the `'error'` event
// that will be emitted by the websocket.
if (ws._socket === null) return;
if (ws._socket._writableState.finished) {
callback();
if (duplex._readableState.endEmitted) duplex.destroy();
} else {
ws._socket.once('finish', function finish() {
// `duplex` is not destroyed here because the `'end'` event will be
// emitted on `duplex` after this `'finish'` event. The EOF signaling
// `null` chunk is, in fact, pushed when the websocket emits `'close'`.
callback();
});
ws.close();
}
};
duplex._read = function () {
if (ws.isPaused) ws.resume();
};
duplex._write = function (chunk, encoding, callback) {
if (ws.readyState === ws.CONNECTING) {
ws.once('open', function open() {
duplex._write(chunk, encoding, callback);
});
return;
}
ws.send(chunk, callback);
};
duplex.on('end', duplexOnEnd);
duplex.on('error', duplexOnError);
return duplex;
}
module.exports = createWebSocketStream;
'use strict';
const { tokenChars } = require('./validation');
/**
* Parses the `Sec-WebSocket-Protocol` header into a set of subprotocol names.
*
* @param {String} header The field value of the header
* @return {Set} The subprotocol names
* @public
*/
function parse(header) {
const protocols = new Set();
let start = -1;
let end = -1;
let i = 0;
for (i; i < header.length; i++) {
const code = header.charCodeAt(i);
if (end === -1 && tokenChars[code] === 1) {
if (start === -1) start = i;
} else if (
i !== 0 &&
(code === 0x20 /* ' ' */ || code === 0x09) /* '\t' */
) {
if (end === -1 && start !== -1) end = i;
} else if (code === 0x2c /* ',' */) {
if (start === -1) {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
if (end === -1) end = i;
const protocol = header.slice(start, end);
if (protocols.has(protocol)) {
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
}
protocols.add(protocol);
start = end = -1;
} else {
throw new SyntaxError(`Unexpected character at index ${i}`);
}
}
if (start === -1 || end !== -1) {
throw new SyntaxError('Unexpected end of input');
}
const protocol = header.slice(start, i);
if (protocols.has(protocol)) {
throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`);
}
protocols.add(protocol);
return protocols;
}
module.exports = { parse };
'use strict';
//
// Allowed token characters:
//
// '!', '#', '$', '%', '&', ''', '*', '+', '-',
// '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
//
// tokenChars[32] === 0 // ' '
// tokenChars[33] === 1 // '!'
// tokenChars[34] === 0 // '"'
// ...
//
// prettier-ignore
const tokenChars = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
];
try {
const isValidUTF8 = require('utf-8-validate');
exports.isValidUTF8 =
typeof isValidUTF8 === 'object'
? isValidUTF8.Validation.isValidUTF8 // utf-8-validate@<3.0.0
: isValidUTF8;
} catch (e) /* istanbul ignore next */ {
exports.isValidUTF8 = () => true;
}
/**
* Checks if a status code is allowed in a close frame.
......@@ -30,96 +18,13 @@ const tokenChars = [
* @return {Boolean} `true` if the status code is valid, else `false`
* @public
*/
function isValidStatusCode(code) {
exports.isValidStatusCode = (code) => {
return (
(code >= 1000 &&
code <= 1014 &&
code <= 1013 &&
code !== 1004 &&
code !== 1005 &&
code !== 1006) ||
(code >= 3000 && code <= 4999)
);
}
/**
* Checks if a given buffer contains only correct UTF-8.
* Ported from https://www.cl.cam.ac.uk/%7Emgk25/ucs/utf8_check.c by
* Markus Kuhn.
*
* @param {Buffer} buf The buffer to check
* @return {Boolean} `true` if `buf` contains only correct UTF-8, else `false`
* @public
*/
function _isValidUTF8(buf) {
const len = buf.length;
let i = 0;
while (i < len) {
if ((buf[i] & 0x80) === 0) {
// 0xxxxxxx
i++;
} else if ((buf[i] & 0xe0) === 0xc0) {
// 110xxxxx 10xxxxxx
if (
i + 1 === len ||
(buf[i + 1] & 0xc0) !== 0x80 ||
(buf[i] & 0xfe) === 0xc0 // Overlong
) {
return false;
}
i += 2;
} else if ((buf[i] & 0xf0) === 0xe0) {
// 1110xxxx 10xxxxxx 10xxxxxx
if (
i + 2 >= len ||
(buf[i + 1] & 0xc0) !== 0x80 ||
(buf[i + 2] & 0xc0) !== 0x80 ||
(buf[i] === 0xe0 && (buf[i + 1] & 0xe0) === 0x80) || // Overlong
(buf[i] === 0xed && (buf[i + 1] & 0xe0) === 0xa0) // Surrogate (U+D800 - U+DFFF)
) {
return false;
}
i += 3;
} else if ((buf[i] & 0xf8) === 0xf0) {
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
if (
i + 3 >= len ||
(buf[i + 1] & 0xc0) !== 0x80 ||
(buf[i + 2] & 0xc0) !== 0x80 ||
(buf[i + 3] & 0xc0) !== 0x80 ||
(buf[i] === 0xf0 && (buf[i + 1] & 0xf0) === 0x80) || // Overlong
(buf[i] === 0xf4 && buf[i + 1] > 0x8f) ||
buf[i] > 0xf4 // > U+10FFFF
) {
return false;
}
i += 4;
} else {
return false;
}
}
return true;
}
module.exports = {
isValidStatusCode,
isValidUTF8: _isValidUTF8,
tokenChars
};
/* istanbul ignore else */
if (!process.env.WS_NO_UTF_8_VALIDATE) {
try {
const isValidUTF8 = require('utf-8-validate');
module.exports.isValidUTF8 = function (buf) {
return buf.length < 150 ? _isValidUTF8(buf) : isValidUTF8(buf);
};
} catch (e) {
// Continue regardless of the error.
}
}
This diff is collapsed.
This diff is collapsed.
{
"name": "ws",
"version": "8.8.0",
"version": "6.1.4",
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
"keywords": [
"HyBi",
......@@ -16,46 +16,30 @@
"author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
"license": "MIT",
"main": "index.js",
"exports": {
"import": "./wrapper.mjs",
"require": "./index.js"
},
"browser": "browser.js",
"engines": {
"node": ">=10.0.0"
},
"files": [
"browser.js",
"index.js",
"lib/*.js",
"wrapper.mjs"
"lib/*.js"
],
"scripts": {
"test": "nyc --reporter=lcov --reporter=text mocha --throw-deprecation test/*.test.js",
"integration": "mocha --throw-deprecation test/*.integration.js",
"lint": "eslint --ignore-path .gitignore . && prettier --check --ignore-path .gitignore \"**/*.{json,md,yaml,yml}\""
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
"test": "npm run lint && nyc --reporter=html --reporter=text mocha test/*.test.js",
"integration": "npm run lint && mocha test/*.integration.js",
"lint": "eslint . --ignore-path .gitignore && prettylint '**/*.{json,md}' --ignore-path .gitignore"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
"dependencies": {
"async-limiter": "~1.0.0"
},
"devDependencies": {
"benchmark": "^2.1.4",
"bufferutil": "^4.0.1",
"eslint": "^8.0.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-prettier": "^4.0.0",
"mocha": "^8.4.0",
"nyc": "^15.0.0",
"prettier": "^2.0.5",
"utf-8-validate": "^5.0.2"
"benchmark": "~2.1.4",
"bufferutil": "~4.0.0",
"eslint": "~5.14.0",
"eslint-config-prettier": "~4.0.0",
"eslint-plugin-prettier": "~3.0.0",
"mocha": "~5.2.0",
"nyc": "~13.3.0",
"prettier": "~1.16.1",
"prettylint": "~1.0.0",
"utf-8-validate": "~5.0.0"
}
}
import createWebSocketStream from './lib/stream.js';
import Receiver from './lib/receiver.js';
import Sender from './lib/sender.js';
import WebSocket from './lib/websocket.js';
import WebSocketServer from './lib/websocket-server.js';
export { createWebSocketStream, Receiver, Sender, WebSocket, WebSocketServer };
export default WebSocket;
......@@ -15,6 +15,7 @@
"iconv-lite": "0.4.23",
"jschardet": "1.6.0",
"marked": "0.6.2",
"mime": "2.6.0",
"nedb": "1.5.1",
"node-rsa": "1.0.5",
"random-fake-useragent": "0.1.0",
......@@ -22,7 +23,7 @@
"superagent-proxy": "3.0.0",
"tar": "4.4.18",
"through": "2.3.8",
"ws": "8.8.0",
"ws": "6.1.4",
"xml2js": "0.4.23"
}
},
......@@ -160,6 +161,11 @@
"resolved": "https://registry.npm.taobao.org/async/download/async-0.2.10.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
},
"node_modules/async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
......@@ -2188,23 +2194,11 @@
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
},
"node_modules/ws": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz",
"integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"dependencies": {
"async-limiter": "~1.0.0"
}
},
"node_modules/xml-name-validator": {
......@@ -2364,6 +2358,11 @@
"resolved": "https://registry.npm.taobao.org/async/download/async-0.2.10.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
},
"async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "http://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
......@@ -4006,10 +4005,12 @@
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
},
"ws": {
"version": "8.8.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.8.0.tgz",
"integrity": "sha512-JDAgSYQ1ksuwqfChJusw1LSJ8BizJ2e/vVu5Lxjq3YvNJNlROv1ui4i+c/kUUrPheBvQl4c5UbERhTwKa6QBJQ==",
"requires": {}
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
"integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
"requires": {
"async-limiter": "~1.0.0"
}
},
"xml-name-validator": {
"version": "2.0.1",
......
......@@ -17,14 +17,15 @@
"superagent-proxy": "3.0.0",
"tar": "4.4.18",
"through": "2.3.8",
"ws": "8.8.0",
"ws": "6.1.4",
"mime": "2.6.0",
"xml2js": "0.4.23"
},
"scripts": {
"start": "AntSword app.js",
"build": "npm start"
},
"author": "antoor <u@uyu.us>",
"author": "antoor",
"license": "MIT",
"repository": {
"type": "git",
......
......@@ -316,12 +316,16 @@ class Base {
opts: this.__opts__,
rsa: this.rsaEncrypt()
}
let useRaw = this.__opts__['type'].endsWith("raw") || (this.constructor.supportRawBody && (this.__opts__['otherConf'] || {})['use-raw-body'] === 1);
let useWebSocket = (this.__opts__['url'].startsWith('ws://') || this.__opts__['url'].startsWith('wss://'));
return new Promise((res, rej) => {
console.log(this.__opts__['type'].endsWith("raw") || (this.constructor.supportRawBody && (this.__opts__['otherConf'] || {})['use-raw-body'] === 1))
// 随机ID(用于监听数据来源)
const hash = (String(+new Date) + String(Math.random()))
.substr(10, 10)
.replace('.', '_');
console.log(hash);
// 监听数据返回
antSword['ipcRenderer']
// 请求完毕返回数据{text,buff}
......@@ -369,7 +373,8 @@ class Base {
addMassData: (this.__opts__['otherConf'] || {})['add-MassData'] === 1,
randomPrefix: parseInt((this.__opts__['otherConf'] || {})['random-Prefix']),
useRandomVariable: (this.__opts__['otherConf'] || {})['use-random-variable'] === 1,
useRaw: this.__opts__['type'].endsWith("raw") || (this.constructor.supportRawBody && (this.__opts__['otherConf'] || {})['use-raw-body'] === 1),
useRaw: useRaw,
useWebSocket: useWebSocket,
timeout: parseInt((this.__opts__['otherConf'] || {})['request-timeout']),
headers: (this.__opts__['httpConf'] || {})['headers'] || {},
body: (this.__opts__['httpConf'] || {})['body'] || {}
......@@ -386,6 +391,9 @@ class Base {
*/
download(savePath, postCode, progressCallback) {
const opt = this.complete(postCode, true);
let useRaw = this.__opts__['type'].endsWith("raw") || (this.constructor.supportRawBody && (this.__opts__['otherConf'] || {})['use-raw-body'] === 1);
let useWebSocket = (this.__opts__['url'].startsWith('ws://') || this.__opts__['url'].startsWith('wss://'));
return new Promise((ret, rej) => {
// 随机ID(用于监听数据来源)
const hash = (String(+new Date) + String(Math.random()))
......@@ -410,6 +418,7 @@ class Base {
// 发送请求数据
.send('download', {
url: this.__opts__['url'],
pwd: this.__opts__['pwd'],
hash: hash,
path: savePath,
data: opt['data'],
......@@ -424,6 +433,8 @@ class Base {
addMassData: (this.__opts__['otherConf'] || {})['add-MassData'] === 1,
randomPrefix: parseInt((this.__opts__['otherConf'] || {})['random-Prefix']),
useRandomVariable: (this.__opts__['otherConf'] || {})['use-random-variable'] === 1,
useRaw: useRaw,
useWebSocket: useWebSocket,
timeout: parseInt((this.__opts__['otherConf'] || {})['request-timeout']),
headers: (this.__opts__['httpConf'] || {})['headers'] || {},
body: (this.__opts__['httpConf'] || {})['body'] || {}
......
......@@ -83,17 +83,17 @@ module.exports = (arg1, arg2, arg3) => ({
_: `command_exists() { command -v "$@" > /dev/null 2>&1; };
if command_exists md5sum; then
asmd5=$(md5sum #{path}|awk '{print $1}');
echo -n "MD5\\t$asmd5\\n";
echo -n "MD5\t$asmd5\n";
elif command_exists busybox && busybox --list-modules | grep -q md5sum; then
asmd5=$(busybox md5sum #{path}|awk '{print $1}');
echo -n "MD5\\t$asmd5\\n";
echo -n "MD5\t$asmd5\n";
fi;
if command_exists sha1sum; then
assha1=$(sha1sum #{path}|awk '{print $1}');
echo -n "SHA1\\t$assha1\\n";
echo -n "SHA1\t$assha1\n";
elif command_exists busybox && busybox --list-modules | grep -q sha1sum; then
assha1=$(busybox sha1sum #{path}|awk '{print $1}');
echo -n "SHA1\\t$assha1\\n";
echo -n "SHA1\t$assha1\n";
fi;
`.replace(/\n\s+/g, ''),
},
......
......@@ -544,7 +544,7 @@ class FileManager {
width: 800,
height: 600,
});
var filemime = mime.lookup(name);
var filemime = mime.getType(name);
let savepath = PATH.join(process.env.AS_WORKDIR, `antData/.temp/`, Buffer.from(name).toString("hex"));
win.cell.lastChild['style']['overflow'] = 'scroll';
win.cell.lastChild['style']['textAlign'] = 'center';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment