Add files via upload
This commit is contained in:
parent
119d0275bd
commit
10f4531375
20
node_modules/opusscript/LICENSE
generated
vendored
Normal file
20
node_modules/opusscript/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016-2021 abalabahaha
|
||||||
|
|
||||||
|
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.
|
53
node_modules/opusscript/README.md
generated
vendored
Normal file
53
node_modules/opusscript/README.md
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# OpusScript
|
||||||
|
|
||||||
|
JS bindings for libopus 1.3.1, ported with Emscripten.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
var OpusScript = require("opusscript");
|
||||||
|
|
||||||
|
// 48kHz sampling rate, 20ms frame duration, stereo audio (2 channels)
|
||||||
|
var samplingRate = 48000;
|
||||||
|
var frameDuration = 20;
|
||||||
|
var channels = 2;
|
||||||
|
|
||||||
|
// Optimize encoding for audio. Available applications are VOIP, AUDIO, and RESTRICTED_LOWDELAY
|
||||||
|
var encoder = new OpusScript(samplingRate, channels, OpusScript.Application.AUDIO);
|
||||||
|
|
||||||
|
var frameSize = samplingRate * frameDuration / 1000;
|
||||||
|
|
||||||
|
// Get PCM data from somewhere and encode it into opus
|
||||||
|
var pcmData = new Buffer(pcmSource);
|
||||||
|
var encodedPacket = encoder.encode(pcmData, frameSize);
|
||||||
|
|
||||||
|
// Decode the opus packet back into PCM
|
||||||
|
var decodedPacket = encoder.decode(encodedPacket);
|
||||||
|
|
||||||
|
// Delete the encoder when finished with it (Emscripten does not automatically call C++ object destructors)
|
||||||
|
encoder.delete();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Note: WASM
|
||||||
|
|
||||||
|
If your environment doesn't support WASM, you can try the JS-only module. Do note that the JS-only version barely has optimizations due to compiler/toolchain limitations, and should only be used as a last resort.
|
||||||
|
|
||||||
|
```js
|
||||||
|
var encoder = new OpusScript(samplingRate, channels, OpusScript.Application.AUDIO, {
|
||||||
|
wasm: false
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Note: TypeScript
|
||||||
|
|
||||||
|
Since this module wasn't written for TypeScript, you need to use `import = require` syntax.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Import using:
|
||||||
|
import OpusScript = require("opusscript");
|
||||||
|
|
||||||
|
// and NOT:
|
||||||
|
import OpusScript from "opusscript";
|
||||||
|
```
|
44
node_modules/opusscript/build/COPYING.libopus
generated
vendored
Normal file
44
node_modules/opusscript/build/COPYING.libopus
generated
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
|
||||||
|
Jean-Marc Valin, Timothy B. Terriberry,
|
||||||
|
CSIRO, Gregory Maxwell, Mark Borgerding,
|
||||||
|
Erik de Castro Lopo
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||||
|
names of specific contributors, may be used to endorse or promote
|
||||||
|
products derived from this software without specific prior written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||||
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
Opus is subject to the royalty-free patent licenses which are
|
||||||
|
specified at:
|
||||||
|
|
||||||
|
Xiph.Org Foundation:
|
||||||
|
https://datatracker.ietf.org/ipr/1524/
|
||||||
|
|
||||||
|
Microsoft Corporation:
|
||||||
|
https://datatracker.ietf.org/ipr/1914/
|
||||||
|
|
||||||
|
Broadcom Corporation:
|
||||||
|
https://datatracker.ietf.org/ipr/1526/
|
110
node_modules/opusscript/build/opusscript_native_nasm.js
generated
vendored
Normal file
110
node_modules/opusscript/build/opusscript_native_nasm.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
77
node_modules/opusscript/build/opusscript_native_wasm.js
generated
vendored
Normal file
77
node_modules/opusscript/build/opusscript_native_wasm.js
generated
vendored
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
|
||||||
|
var Module = (function() {
|
||||||
|
var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
|
||||||
|
if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
|
||||||
|
return (
|
||||||
|
function(Module) {
|
||||||
|
Module = Module || {};
|
||||||
|
|
||||||
|
|
||||||
|
var g;g||(g=typeof Module !== 'undefined' ? Module : {});var aa,ba;g.ready=new Promise(function(b,a){aa=b;ba=a});var r={},t;for(t in g)g.hasOwnProperty(t)&&(r[t]=g[t]);var ca=!1,v=!1,ea=!1,fa=!1;ca="object"===typeof window;v="function"===typeof importScripts;ea="object"===typeof process&&"object"===typeof process.versions&&"string"===typeof process.versions.node;fa=!ca&&!ea&&!v;var w="",ha,y,ia,ja;
|
||||||
|
if(ea)w=v?require("path").dirname(w)+"/":__dirname+"/",ha=function(b,a){ia||(ia=require("fs"));ja||(ja=require("path"));b=ja.normalize(b);return ia.readFileSync(b,a?null:"utf8")},y=function(b){b=ha(b,!0);b.buffer||(b=new Uint8Array(b));assert(b.buffer);return b},1<process.argv.length&&process.argv[1].replace(/\\/g,"/"),process.argv.slice(2),g.inspect=function(){return"[Emscripten Module object]"};else if(fa)"undefined"!=typeof read&&(ha=function(b){return read(b)}),y=function(b){if("function"===typeof readbuffer)return new Uint8Array(readbuffer(b));
|
||||||
|
b=read(b,"binary");assert("object"===typeof b);return b},"undefined"!==typeof print&&("undefined"===typeof console&&(console={}),console.log=print,console.warn=console.error="undefined"!==typeof printErr?printErr:print);else if(ca||v)v?w=self.location.href:"undefined"!==typeof document&&document.currentScript&&(w=document.currentScript.src),_scriptDir&&(w=_scriptDir),0!==w.indexOf("blob:")?w=w.substr(0,w.lastIndexOf("/")+1):w="",ha=function(b){var a=new XMLHttpRequest;a.open("GET",b,!1);a.send(null);
|
||||||
|
return a.responseText},v&&(y=function(b){var a=new XMLHttpRequest;a.open("GET",b,!1);a.responseType="arraybuffer";a.send(null);return new Uint8Array(a.response)});var ka=g.print||console.log.bind(console),z=g.printErr||console.warn.bind(console);for(t in r)r.hasOwnProperty(t)&&(g[t]=r[t]);r=null;var la;g.wasmBinary&&(la=g.wasmBinary);var noExitRuntime;g.noExitRuntime&&(noExitRuntime=g.noExitRuntime);"object"!==typeof WebAssembly&&A("no native wasm support detected");var ma,na=!1;
|
||||||
|
function assert(b,a){b||A("Assertion failed: "+a)}var oa="undefined"!==typeof TextDecoder?new TextDecoder("utf8"):void 0;
|
||||||
|
function pa(b,a,c){var d=a+c;for(c=a;b[c]&&!(c>=d);)++c;if(16<c-a&&b.subarray&&oa)return oa.decode(b.subarray(a,c));for(d="";a<c;){var e=b[a++];if(e&128){var f=b[a++]&63;if(192==(e&224))d+=String.fromCharCode((e&31)<<6|f);else{var l=b[a++]&63;e=224==(e&240)?(e&15)<<12|f<<6|l:(e&7)<<18|f<<12|l<<6|b[a++]&63;65536>e?d+=String.fromCharCode(e):(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023))}}else d+=String.fromCharCode(e)}return d}
|
||||||
|
function qa(b,a,c){var d=B;if(0<c){c=a+c-1;for(var e=0;e<b.length;++e){var f=b.charCodeAt(e);if(55296<=f&&57343>=f){var l=b.charCodeAt(++e);f=65536+((f&1023)<<10)|l&1023}if(127>=f){if(a>=c)break;d[a++]=f}else{if(2047>=f){if(a+1>=c)break;d[a++]=192|f>>6}else{if(65535>=f){if(a+2>=c)break;d[a++]=224|f>>12}else{if(a+3>=c)break;d[a++]=240|f>>18;d[a++]=128|f>>12&63}d[a++]=128|f>>6&63}d[a++]=128|f&63}}d[a]=0}}var sa="undefined"!==typeof TextDecoder?new TextDecoder("utf-16le"):void 0;
|
||||||
|
function ta(b,a){var c=b>>1;for(var d=c+a/2;!(c>=d)&&ua[c];)++c;c<<=1;if(32<c-b&&sa)return sa.decode(B.subarray(b,c));c="";for(d=0;!(d>=a/2);++d){var e=C[b+2*d>>1];if(0==e)break;c+=String.fromCharCode(e)}return c}function va(b,a,c){void 0===c&&(c=2147483647);if(2>c)return 0;c-=2;var d=a;c=c<2*b.length?c/2:b.length;for(var e=0;e<c;++e)C[a>>1]=b.charCodeAt(e),a+=2;C[a>>1]=0;return a-d}function wa(b){return 2*b.length}
|
||||||
|
function xa(b,a){for(var c=0,d="";!(c>=a/4);){var e=D[b+4*c>>2];if(0==e)break;++c;65536<=e?(e-=65536,d+=String.fromCharCode(55296|e>>10,56320|e&1023)):d+=String.fromCharCode(e)}return d}function ya(b,a,c){void 0===c&&(c=2147483647);if(4>c)return 0;var d=a;c=d+c-4;for(var e=0;e<b.length;++e){var f=b.charCodeAt(e);if(55296<=f&&57343>=f){var l=b.charCodeAt(++e);f=65536+((f&1023)<<10)|l&1023}D[a>>2]=f;a+=4;if(a+4>c)break}D[a>>2]=0;return a-d}
|
||||||
|
function za(b){for(var a=0,c=0;c<b.length;++c){var d=b.charCodeAt(c);55296<=d&&57343>=d&&++c;a+=4}return a}var Aa,E,B,C,ua,D,F,Ba,Ca;function Da(){var b=ma.buffer;Aa=b;g.HEAP8=E=new Int8Array(b);g.HEAP16=C=new Int16Array(b);g.HEAP32=D=new Int32Array(b);g.HEAPU8=B=new Uint8Array(b);g.HEAPU16=ua=new Uint16Array(b);g.HEAPU32=F=new Uint32Array(b);g.HEAPF32=Ba=new Float32Array(b);g.HEAPF64=Ca=new Float64Array(b);g.HEAP64=new BigInt64Array(b)}var Ea,Fa=[],Ga=[],Ha=[],Ia=[];Ga.push({ba:function(){Ja()}});
|
||||||
|
function Ka(){var b=g.preRun.shift();Fa.unshift(b)}var G=0,La=null,H=null;g.preloadedImages={};g.preloadedAudios={};function A(b){if(g.onAbort)g.onAbort(b);z(b);na=!0;b=new WebAssembly.RuntimeError("abort("+b+"). Build with -s ASSERTIONS=1 for more info.");ba(b);throw b;}var I="opusscript_native_wasm.wasm";if(String.prototype.startsWith?!I.startsWith("data:application/octet-stream;base64,"):0!==I.indexOf("data:application/octet-stream;base64,")){var Ma=I;I=g.locateFile?g.locateFile(Ma,w):w+Ma}
|
||||||
|
function Na(b){var a=I;try{a:{try{if(a==I&&la){var c=new Uint8Array(la);break a}if(y){c=y(a);break a}throw"sync fetching of the wasm failed: you can preload it to Module['wasmBinary'] manually, or emcc.py will do that for you when generating HTML (but not JS)";}catch(f){A(f)}c=void 0}var d=new WebAssembly.Module(c);var e=new WebAssembly.Instance(d,b)}catch(f){throw b=f.toString(),z("failed to compile wasm module: "+b),(0<=b.indexOf("imported Memory")||0<=b.indexOf("memory import"))&&z("Memory size incompatibility issues may be due to changing INITIAL_MEMORY at runtime to something too large. Use ALLOW_MEMORY_GROWTH to allow any size memory (and also make sure not to set INITIAL_MEMORY at runtime to something smaller than it was at compile time)."),
|
||||||
|
f;}return[e,d]}var J,Pa;function Qa(b){for(;0<b.length;){var a=b.shift();if("function"==typeof a)a(g);else{var c=a.ba;"number"===typeof c?void 0===a.W?Ea.get(c)():Ea.get(c)(a.W):c(void 0===a.W?null:a.W)}}}function Ra(b){this.D=b-16;this.qa=function(a){D[this.D+8>>2]=a};this.na=function(a){D[this.D+0>>2]=a};this.oa=function(){D[this.D+4>>2]=0};this.ma=function(){E[this.D+12>>0]=0};this.pa=function(){E[this.D+13>>0]=0};this.ga=function(a,c){this.qa(a);this.na(c);this.oa();this.ma();this.pa()}}
|
||||||
|
var Sa=0;function Ta(b){switch(b){case 1:return 0;case 2:return 1;case 4:return 2;case 8:return 3;default:throw new TypeError("Unknown type size: "+b);}}var Ua=void 0;function K(b){for(var a="";B[b];)a+=Ua[B[b++]];return a}var L={},M={},Va={};function Wa(b){if(void 0===b)return"_unknown";b=b.replace(/[^a-zA-Z0-9_]/g,"$");var a=b.charCodeAt(0);return 48<=a&&57>=a?"_"+b:b}
|
||||||
|
function Xa(b,a){b=Wa(b);return(new Function("body","return function "+b+'() {\n "use strict"; return body.apply(this, arguments);\n};\n'))(a)}function Ya(b){var a=Error,c=Xa(b,function(d){this.name=b;this.message=d;d=Error(d).stack;void 0!==d&&(this.stack=this.toString()+"\n"+d.replace(/^Error(:[^\n]*)?\n/,""))});c.prototype=Object.create(a.prototype);c.prototype.constructor=c;c.prototype.toString=function(){return void 0===this.message?this.name:this.name+": "+this.message};return c}
|
||||||
|
var N=void 0;function O(b){throw new N(b);}var Za=void 0;function $a(b){throw new Za(b);}function P(b,a,c){function d(h){h=c(h);h.length!==b.length&&$a("Mismatched type converter count");for(var n=0;n<b.length;++n)Q(b[n],h[n])}b.forEach(function(h){Va[h]=a});var e=Array(a.length),f=[],l=0;a.forEach(function(h,n){M.hasOwnProperty(h)?e[n]=M[h]:(f.push(h),L.hasOwnProperty(h)||(L[h]=[]),L[h].push(function(){e[n]=M[h];++l;l===f.length&&d(e)}))});0===f.length&&d(e)}
|
||||||
|
function Q(b,a,c){c=c||{};if(!("argPackAdvance"in a))throw new TypeError("registerType registeredInstance requires argPackAdvance");var d=a.name;b||O('type "'+d+'" must have a positive integer typeid pointer');if(M.hasOwnProperty(b)){if(c.fa)return;O("Cannot register type '"+d+"' twice")}M[b]=a;delete Va[b];L.hasOwnProperty(b)&&(a=L[b],delete L[b],a.forEach(function(e){e()}))}function ab(b){return{count:b.count,P:b.P,S:b.S,D:b.D,G:b.G,I:b.I,J:b.J}}
|
||||||
|
function bb(b){O(b.C.G.F.name+" instance already deleted")}var cb=!1;function db(){}function eb(b){--b.count.value;0===b.count.value&&(b.I?b.J.N(b.I):b.G.F.N(b.D))}function R(b){if("undefined"===typeof FinalizationGroup)return R=function(a){return a},b;cb=new FinalizationGroup(function(a){for(var c=a.next();!c.done;c=a.next())c=c.value,c.D?eb(c):console.warn("object already deleted: "+c.D)});R=function(a){cb.register(a,a.C,a.C);return a};db=function(a){cb.unregister(a.C)};return R(b)}
|
||||||
|
var fb=void 0,gb=[];function hb(){for(;gb.length;){var b=gb.pop();b.C.P=!1;b["delete"]()}}function S(){}var ib={};function jb(b,a,c){if(void 0===b[a].H){var d=b[a];b[a]=function(){b[a].H.hasOwnProperty(arguments.length)||O("Function '"+c+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+b[a].H+")!");return b[a].H[arguments.length].apply(this,arguments)};b[a].H=[];b[a].H[d.O]=d}}
|
||||||
|
function kb(b,a){g.hasOwnProperty(b)?(O("Cannot register public name '"+b+"' twice"),jb(g,b,b),g.hasOwnProperty(void 0)&&O("Cannot register multiple overloads of a function with the same number of arguments (undefined)!"),g[b].H[void 0]=a):g[b]=a}function lb(b,a,c,d,e,f,l,h){this.name=b;this.constructor=a;this.R=c;this.N=d;this.K=e;this.da=f;this.T=l;this.aa=h;this.ia=[]}
|
||||||
|
function ob(b,a,c){for(;a!==c;)a.T||O("Expected null or instance of "+c.name+", got an instance of "+a.name),b=a.T(b),a=a.K;return b}function pb(b,a){if(null===a)return this.X&&O("null is not a valid "+this.name),0;a.C||O('Cannot pass "'+U(a)+'" as a '+this.name);a.C.D||O("Cannot pass deleted object as a pointer of type "+this.name);return ob(a.C.D,a.C.G.F,this.F)}
|
||||||
|
function qb(b,a){if(null===a){this.X&&O("null is not a valid "+this.name);if(this.V){var c=this.ja();null!==b&&b.push(this.N,c);return c}return 0}a.C||O('Cannot pass "'+U(a)+'" as a '+this.name);a.C.D||O("Cannot pass deleted object as a pointer of type "+this.name);!this.U&&a.C.G.U&&O("Cannot convert argument of type "+(a.C.J?a.C.J.name:a.C.G.name)+" to parameter type "+this.name);c=ob(a.C.D,a.C.G.F,this.F);if(this.V)switch(void 0===a.C.I&&O("Passing raw pointer to smart pointer is illegal"),this.ra){case 0:a.C.J===
|
||||||
|
this?c=a.C.I:O("Cannot convert argument of type "+(a.C.J?a.C.J.name:a.C.G.name)+" to parameter type "+this.name);break;case 1:c=a.C.I;break;case 2:if(a.C.J===this)c=a.C.I;else{var d=a.clone();c=this.ka(c,rb(function(){d["delete"]()}));null!==b&&b.push(this.N,c)}break;default:O("Unsupporting sharing policy")}return c}
|
||||||
|
function sb(b,a){if(null===a)return this.X&&O("null is not a valid "+this.name),0;a.C||O('Cannot pass "'+U(a)+'" as a '+this.name);a.C.D||O("Cannot pass deleted object as a pointer of type "+this.name);a.C.G.U&&O("Cannot convert argument of type "+a.C.G.name+" to parameter type "+this.name);return ob(a.C.D,a.C.G.F,this.F)}function tb(b){return this.fromWireType(F[b>>2])}function ub(b,a,c){if(a===c)return b;if(void 0===c.K)return null;b=ub(b,a,c.K);return null===b?null:c.aa(b)}var vb={};
|
||||||
|
function wb(b,a){for(void 0===a&&O("ptr should not be undefined");b.K;)a=b.T(a),b=b.K;return vb[a]}function xb(b,a){a.G&&a.D||$a("makeClassHandle requires ptr and ptrType");!!a.J!==!!a.I&&$a("Both smartPtrType and smartPtr must be specified");a.count={value:1};return R(Object.create(b,{C:{value:a}}))}function V(b,a,c,d){this.name=b;this.F=a;this.X=c;this.U=d;this.V=!1;this.N=this.ka=this.ja=this.Z=this.ra=this.ha=void 0;void 0!==a.K?this.toWireType=qb:(this.toWireType=d?pb:sb,this.L=null)}
|
||||||
|
function yb(b,a){g.hasOwnProperty(b)||$a("Replacing nonexistant public symbol");g[b]=a;g[b].O=void 0}function W(b,a){b=K(b);var c=Ea.get(a);"function"!==typeof c&&O("unknown function pointer with signature "+b+": "+a);return c}var zb=void 0;function Ab(b){b=Bb(b);var a=K(b);X(b);return a}function Cb(b,a){function c(f){e[f]||M[f]||(Va[f]?Va[f].forEach(c):(d.push(f),e[f]=!0))}var d=[],e={};a.forEach(c);throw new zb(b+": "+d.map(Ab).join([", "]));}
|
||||||
|
function Db(b){var a=Function;if(!(a instanceof Function))throw new TypeError("new_ called with constructor type "+typeof a+" which is not a function");var c=Xa(a.name||"unknownFunctionName",function(){});c.prototype=a.prototype;c=new c;b=a.apply(c,b);return b instanceof Object?b:c}function Eb(b){for(;b.length;){var a=b.pop();b.pop()(a)}}
|
||||||
|
function Fb(b,a,c,d,e){var f=a.length;2>f&&O("argTypes array size mismatch! Must at least get return value and 'this' types!");var l=null!==a[1]&&null!==c,h=!1;for(c=1;c<a.length;++c)if(null!==a[c]&&void 0===a[c].L){h=!0;break}var n="void"!==a[0].name,k="",m="";for(c=0;c<f-2;++c)k+=(0!==c?", ":"")+"arg"+c,m+=(0!==c?", ":"")+"arg"+c+"Wired";b="return function "+Wa(b)+"("+k+") {\nif (arguments.length !== "+(f-2)+") {\nthrowBindingError('function "+b+" called with ' + arguments.length + ' arguments, expected "+
|
||||||
|
(f-2)+" args!');\n}\n";h&&(b+="var destructors = [];\n");var p=h?"destructors":"null";k="throwBindingError invoker fn runDestructors retType classParam".split(" ");d=[O,d,e,Eb,a[0],a[1]];l&&(b+="var thisWired = classParam.toWireType("+p+", this);\n");for(c=0;c<f-2;++c)b+="var arg"+c+"Wired = argType"+c+".toWireType("+p+", arg"+c+"); // "+a[c+2].name+"\n",k.push("argType"+c),d.push(a[c+2]);l&&(m="thisWired"+(0<m.length?", ":"")+m);b+=(n?"var rv = ":"")+"invoker(fn"+(0<m.length?", ":"")+m+");\n";if(h)b+=
|
||||||
|
"runDestructors(destructors);\n";else for(c=l?1:2;c<a.length;++c)f=1===c?"thisWired":"arg"+(c-2)+"Wired",null!==a[c].L&&(b+=f+"_dtor("+f+"); // "+a[c].name+"\n",k.push(f+"_dtor"),d.push(a[c].L));n&&(b+="var ret = retType.fromWireType(rv);\nreturn ret;\n");k.push(b+"}\n");return Db(k).apply(null,d)}function Gb(b,a){for(var c=[],d=0;d<b;d++)c.push(D[(a>>2)+d]);return c}var Hb=[],Y=[{},{value:void 0},{value:null},{value:!0},{value:!1}];
|
||||||
|
function rb(b){switch(b){case void 0:return 1;case null:return 2;case !0:return 3;case !1:return 4;default:var a=Hb.length?Hb.pop():Y.length;Y[a]={la:1,value:b};return a}}function U(b){if(null===b)return"null";var a=typeof b;return"object"===a||"array"===a||"function"===a?b.toString():""+b}function Ib(b,a){switch(a){case 2:return function(c){return this.fromWireType(Ba[c>>2])};case 3:return function(c){return this.fromWireType(Ca[c>>3])};default:throw new TypeError("Unknown float type: "+b);}}
|
||||||
|
function Jb(b,a,c){switch(a){case 0:return c?function(d){return E[d]}:function(d){return B[d]};case 1:return c?function(d){return C[d>>1]}:function(d){return ua[d>>1]};case 2:return c?function(d){return D[d>>2]}:function(d){return F[d>>2]};default:throw new TypeError("Unknown integer type: "+b);}}for(var Kb=[null,[],[]],Lb=Array(256),Mb=0;256>Mb;++Mb)Lb[Mb]=String.fromCharCode(Mb);Ua=Lb;N=g.BindingError=Ya("BindingError");Za=g.InternalError=Ya("InternalError");
|
||||||
|
S.prototype.isAliasOf=function(b){if(!(this instanceof S&&b instanceof S))return!1;var a=this.C.G.F,c=this.C.D,d=b.C.G.F;for(b=b.C.D;a.K;)c=a.T(c),a=a.K;for(;d.K;)b=d.T(b),d=d.K;return a===d&&c===b};S.prototype.clone=function(){this.C.D||bb(this);if(this.C.S)return this.C.count.value+=1,this;var b=R(Object.create(Object.getPrototypeOf(this),{C:{value:ab(this.C)}}));b.C.count.value+=1;b.C.P=!1;return b};
|
||||||
|
S.prototype["delete"]=function(){this.C.D||bb(this);this.C.P&&!this.C.S&&O("Object already scheduled for deletion");db(this);eb(this.C);this.C.S||(this.C.I=void 0,this.C.D=void 0)};S.prototype.isDeleted=function(){return!this.C.D};S.prototype.deleteLater=function(){this.C.D||bb(this);this.C.P&&!this.C.S&&O("Object already scheduled for deletion");gb.push(this);1===gb.length&&fb&&fb(hb);this.C.P=!0;return this};V.prototype.ea=function(b){this.Z&&(b=this.Z(b));return b};
|
||||||
|
V.prototype.Y=function(b){this.N&&this.N(b)};V.prototype.argPackAdvance=8;V.prototype.readValueFromPointer=tb;V.prototype.deleteObject=function(b){if(null!==b)b["delete"]()};
|
||||||
|
V.prototype.fromWireType=function(b){function a(){return this.V?xb(this.F.R,{G:this.ha,D:c,J:this,I:b}):xb(this.F.R,{G:this,D:b})}var c=this.ea(b);if(!c)return this.Y(b),null;var d=wb(this.F,c);if(void 0!==d){if(0===d.C.count.value)return d.C.D=c,d.C.I=b,d.clone();d=d.clone();this.Y(b);return d}d=this.F.da(c);d=ib[d];if(!d)return a.call(this);d=this.U?d.$:d.pointerType;var e=ub(c,this.F,d.F);return null===e?a.call(this):this.V?xb(d.F.R,{G:d,D:e,J:this,I:b}):xb(d.F.R,{G:d,D:e})};
|
||||||
|
g.getInheritedInstanceCount=function(){return Object.keys(vb).length};g.getLiveInheritedInstances=function(){var b=[],a;for(a in vb)vb.hasOwnProperty(a)&&b.push(vb[a]);return b};g.flushPendingDeletes=hb;g.setDelayFunction=function(b){fb=b;gb.length&&fb&&fb(hb)};zb=g.UnboundTypeError=Ya("UnboundTypeError");g.count_emval_handles=function(){for(var b=0,a=5;a<Y.length;++a)void 0!==Y[a]&&++b;return b};g.get_first_emval=function(){for(var b=5;b<Y.length;++b)if(void 0!==Y[b])return Y[b];return null};
|
||||||
|
var Ob={i:function(b){return Nb(b+16)+16},h:function(b,a,c){(new Ra(b)).ga(a,c);Sa++;throw b;},k:function(b,a,c,d,e){var f=Ta(c);a=K(a);Q(b,{name:a,fromWireType:function(l){return!!l},toWireType:function(l,h){return h?d:e},argPackAdvance:8,readValueFromPointer:function(l){if(1===c)var h=E;else if(2===c)h=C;else if(4===c)h=D;else throw new TypeError("Unknown boolean type size: "+a);return this.fromWireType(h[l>>f])},L:null})},t:function(b,a,c,d,e,f,l,h,n,k,m,p,q){m=K(m);f=W(e,f);h&&(h=W(l,h));k&&(k=
|
||||||
|
W(n,k));q=W(p,q);var x=Wa(m);kb(x,function(){Cb("Cannot construct "+m+" due to unbound types",[d])});P([b,a,c],d?[d]:[],function(u){u=u[0];if(d){var ra=u.F;var da=ra.R}else da=S.prototype;u=Xa(x,function(){if(Object.getPrototypeOf(this)!==Oa)throw new N("Use 'new' to construct "+m);if(void 0===T.M)throw new N(m+" has no accessible constructor");var mb=T.M[arguments.length];if(void 0===mb)throw new N("Tried to invoke ctor of "+m+" with invalid number of parameters ("+arguments.length+") - expected ("+
|
||||||
|
Object.keys(T.M).toString()+") parameters instead!");return mb.apply(this,arguments)});var Oa=Object.create(da,{constructor:{value:u}});u.prototype=Oa;var T=new lb(m,u,Oa,q,ra,f,h,k);ra=new V(m,T,!0,!1);da=new V(m+"*",T,!1,!1);var nb=new V(m+" const*",T,!1,!0);ib[b]={pointerType:da,$:nb};yb(x,u);return[ra,da,nb]})},m:function(b,a,c,d,e,f,l){var h=Gb(c,d);a=K(a);f=W(e,f);P([],[b],function(n){function k(){Cb("Cannot call "+m+" due to unbound types",h)}n=n[0];var m=n.name+"."+a,p=n.F.constructor;void 0===
|
||||||
|
p[a]?(k.O=c-1,p[a]=k):(jb(p,a,m),p[a].H[c-1]=k);P([],h,function(q){q=[q[0],null].concat(q.slice(1));q=Fb(m,q,null,f,l);void 0===p[a].H?(q.O=c-1,p[a]=q):p[a].H[c-1]=q;return[]});return[]})},s:function(b,a,c,d,e,f){assert(0<a);var l=Gb(a,c);e=W(d,e);var h=[f],n=[];P([],[b],function(k){k=k[0];var m="constructor "+k.name;void 0===k.F.M&&(k.F.M=[]);if(void 0!==k.F.M[a-1])throw new N("Cannot register multiple constructors with identical number of parameters ("+(a-1)+") for class '"+k.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!");
|
||||||
|
k.F.M[a-1]=function(){Cb("Cannot construct "+k.name+" due to unbound types",l)};P([],l,function(p){k.F.M[a-1]=function(){arguments.length!==a-1&&O(m+" called with "+arguments.length+" arguments, expected "+(a-1));n.length=0;h.length=a;for(var q=1;q<a;++q)h[q]=p[q].toWireType(n,arguments[q-1]);q=e.apply(null,h);Eb(n);return p[0].fromWireType(q)};return[]});return[]})},d:function(b,a,c,d,e,f,l,h){var n=Gb(c,d);a=K(a);f=W(e,f);P([],[b],function(k){function m(){Cb("Cannot call "+p+" due to unbound types",
|
||||||
|
n)}k=k[0];var p=k.name+"."+a;h&&k.F.ia.push(a);var q=k.F.R,x=q[a];void 0===x||void 0===x.H&&x.className!==k.name&&x.O===c-2?(m.O=c-2,m.className=k.name,q[a]=m):(jb(q,a,p),q[a].H[c-2]=m);P([],n,function(u){u=Fb(p,u,k,f,l);void 0===q[a].H?(u.O=c-2,q[a]=u):q[a].H[c-2]=u;return[]});return[]})},r:function(b,a){a=K(a);Q(b,{name:a,fromWireType:function(c){var d=Y[c].value;4<c&&0===--Y[c].la&&(Y[c]=void 0,Hb.push(c));return d},toWireType:function(c,d){return rb(d)},argPackAdvance:8,readValueFromPointer:tb,
|
||||||
|
L:null})},j:function(b,a,c){c=Ta(c);a=K(a);Q(b,{name:a,fromWireType:function(d){return d},toWireType:function(d,e){if("number"!==typeof e&&"boolean"!==typeof e)throw new TypeError('Cannot convert "'+U(e)+'" to '+this.name);return e},argPackAdvance:8,readValueFromPointer:Ib(a,c),L:null})},b:function(b,a,c,d,e){function f(k){return k}a=K(a);-1===e&&(e=4294967295);var l=Ta(c);if(0===d){var h=32-8*c;f=function(k){return k<<h>>>h}}var n=-1!=a.indexOf("unsigned");Q(b,{name:a,fromWireType:f,toWireType:function(k,
|
||||||
|
m){if("number"!==typeof m&&"boolean"!==typeof m)throw new TypeError('Cannot convert "'+U(m)+'" to '+this.name);if(m<d||m>e)throw new TypeError('Passing a number "'+U(m)+'" from JS side to C/C++ side to an argument of type "'+a+'", which is outside the valid range ['+d+", "+e+"]!");return n?m>>>0:m|0},argPackAdvance:8,readValueFromPointer:Jb(a,l,0!==d),L:null})},a:function(b,a,c){function d(f){f>>=2;var l=F;return new e(Aa,l[f+1],l[f])}var e=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,
|
||||||
|
Uint32Array,Float32Array,Float64Array][a];c=K(c);Q(b,{name:c,fromWireType:d,argPackAdvance:8,readValueFromPointer:d},{fa:!0})},f:function(b,a){a=K(a);var c="std::string"===a;Q(b,{name:a,fromWireType:function(d){var e=F[d>>2];if(c)for(var f=d+4,l=0;l<=e;++l){var h=d+4+l;if(l==e||0==B[h]){f=f?pa(B,f,h-f):"";if(void 0===n)var n=f;else n+=String.fromCharCode(0),n+=f;f=h+1}}else{n=Array(e);for(l=0;l<e;++l)n[l]=String.fromCharCode(B[d+4+l]);n=n.join("")}X(d);return n},toWireType:function(d,e){e instanceof
|
||||||
|
ArrayBuffer&&(e=new Uint8Array(e));var f="string"===typeof e;f||e instanceof Uint8Array||e instanceof Uint8ClampedArray||e instanceof Int8Array||O("Cannot pass non-string to std::string");var l=(c&&f?function(){for(var k=0,m=0;m<e.length;++m){var p=e.charCodeAt(m);55296<=p&&57343>=p&&(p=65536+((p&1023)<<10)|e.charCodeAt(++m)&1023);127>=p?++k:k=2047>=p?k+2:65535>=p?k+3:k+4}return k}:function(){return e.length})(),h=Nb(4+l+1);F[h>>2]=l;if(c&&f)qa(e,h+4,l+1);else if(f)for(f=0;f<l;++f){var n=e.charCodeAt(f);
|
||||||
|
255<n&&(X(h),O("String has UTF-16 code units that do not fit in 8 bits"));B[h+4+f]=n}else for(f=0;f<l;++f)B[h+4+f]=e[f];null!==d&&d.push(X,h);return h},argPackAdvance:8,readValueFromPointer:tb,L:function(d){X(d)}})},e:function(b,a,c){c=K(c);if(2===a){var d=ta;var e=va;var f=wa;var l=function(){return ua};var h=1}else 4===a&&(d=xa,e=ya,f=za,l=function(){return F},h=2);Q(b,{name:c,fromWireType:function(n){for(var k=F[n>>2],m=l(),p,q=n+4,x=0;x<=k;++x){var u=n+4+x*a;if(x==k||0==m[u>>h])q=d(q,u-q),void 0===
|
||||||
|
p?p=q:(p+=String.fromCharCode(0),p+=q),q=u+a}X(n);return p},toWireType:function(n,k){"string"!==typeof k&&O("Cannot pass non-string to C++ string type "+c);var m=f(k),p=Nb(4+m+a);F[p>>2]=m>>h;e(k,p+4,m+a);null!==n&&n.push(X,p);return p},argPackAdvance:8,readValueFromPointer:tb,L:function(n){X(n)}})},l:function(b,a){a=K(a);Q(b,{sa:!0,name:a,argPackAdvance:0,fromWireType:function(){},toWireType:function(){}})},g:function(){A()},n:function(b,a,c){B.copyWithin(b,a,a+c)},c:function(b){b>>>=0;var a=B.length;
|
||||||
|
if(2147483648<b)return!1;for(var c=1;4>=c;c*=2){var d=a*(1+.2/c);d=Math.min(d,b+100663296);d=Math.max(16777216,b,d);0<d%65536&&(d+=65536-d%65536);a:{try{ma.grow(Math.min(2147483648,d)-Aa.byteLength+65535>>>16);Da();var e=1;break a}catch(f){}e=void 0}if(e)return!0}return!1},p:function(){return 0},q:function(){},o:function(b,a,c,d){for(var e=0,f=0;f<c;f++){for(var l=D[a+8*f>>2],h=D[a+(8*f+4)>>2],n=0;n<h;n++){var k=B[l+n],m=Kb[b];0===k||10===k?((1===b?ka:z)(pa(m,0)),m.length=0):m.push(k)}e+=h}D[d>>2]=
|
||||||
|
e;return 0}},Z=function(){function b(c){g.asm=c.exports;ma=g.asm.u;Da();Ea=g.asm.z;G--;g.monitorRunDependencies&&g.monitorRunDependencies(G);0==G&&(null!==La&&(clearInterval(La),La=null),H&&(c=H,H=null,c()))}var a={a:Ob};G++;g.monitorRunDependencies&&g.monitorRunDependencies(G);if(g.instantiateWasm)try{return g.instantiateWasm(a,b)}catch(c){return z("Module.instantiateWasm callback failed with error: "+c),!1}a=Na(a);b(a[0],a[1]);return g.asm}(),Ja=g.___wasm_call_ctors=Z.v;g._opus_strerror=Z.w;
|
||||||
|
var Nb=g._malloc=Z.x,X=g._free=Z.y,Bb=g.___getTypeName=Z.A;g.___embind_register_native_and_builtin_types=Z.B;
|
||||||
|
g.setValue=function(b,a,c){c=c||"i8";"*"===c.charAt(c.length-1)&&(c="i32");switch(c){case "i1":E[b>>0]=a;break;case "i8":E[b>>0]=a;break;case "i16":C[b>>1]=a;break;case "i32":D[b>>2]=a;break;case "i64":Pa=[a>>>0,(J=a,1<=+Math.abs(J)?0<J?(Math.min(+Math.floor(J/4294967296),4294967295)|0)>>>0:~~+Math.ceil((J-+(~~J>>>0))/4294967296)>>>0:0)];D[b>>2]=Pa[0];D[b+4>>2]=Pa[1];break;case "float":Ba[b>>2]=a;break;case "double":Ca[b>>3]=a;break;default:A("invalid type for setValue: "+c)}};
|
||||||
|
g.getValue=function(b,a){a=a||"i8";"*"===a.charAt(a.length-1)&&(a="i32");switch(a){case "i1":return E[b>>0];case "i8":return E[b>>0];case "i16":return C[b>>1];case "i32":return D[b>>2];case "i64":return D[b>>2];case "float":return Ba[b>>2];case "double":return Ca[b>>3];default:A("invalid type for getValue: "+a)}return null};var Pb;H=function Qb(){Pb||Rb();Pb||(H=Qb)};
|
||||||
|
function Rb(){function b(){if(!Pb&&(Pb=!0,g.calledRun=!0,!na)){Qa(Ga);Qa(Ha);aa(g);if(g.onRuntimeInitialized)g.onRuntimeInitialized();if(g.postRun)for("function"==typeof g.postRun&&(g.postRun=[g.postRun]);g.postRun.length;){var a=g.postRun.shift();Ia.unshift(a)}Qa(Ia)}}if(!(0<G)){if(g.preRun)for("function"==typeof g.preRun&&(g.preRun=[g.preRun]);g.preRun.length;)Ka();Qa(Fa);0<G||(g.setStatus?(g.setStatus("Running..."),setTimeout(function(){setTimeout(function(){g.setStatus("")},1);b()},1)):b())}}
|
||||||
|
g.run=Rb;if(g.preInit)for("function"==typeof g.preInit&&(g.preInit=[g.preInit]);0<g.preInit.length;)g.preInit.pop()();noExitRuntime=!0;Rb();
|
||||||
|
|
||||||
|
|
||||||
|
return Module
|
||||||
|
}
|
||||||
|
);
|
||||||
|
})();
|
||||||
|
if (typeof exports === 'object' && typeof module === 'object')
|
||||||
|
module.exports = Module;
|
||||||
|
else if (typeof define === 'function' && define['amd'])
|
||||||
|
define([], function() { return Module; });
|
||||||
|
else if (typeof exports === 'object')
|
||||||
|
exports["Module"] = Module;
|
BIN
node_modules/opusscript/build/opusscript_native_wasm.wasm
generated
vendored
Normal file
BIN
node_modules/opusscript/build/opusscript_native_wasm.wasm
generated
vendored
Normal file
Binary file not shown.
93
node_modules/opusscript/index.d.ts
generated
vendored
Normal file
93
node_modules/opusscript/index.d.ts
generated
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
declare module 'opusscript' {
|
||||||
|
/**
|
||||||
|
* Opus application type
|
||||||
|
*/
|
||||||
|
enum OpusApplication {
|
||||||
|
/**
|
||||||
|
* Voice Over IP
|
||||||
|
*/
|
||||||
|
VOIP = 2048,
|
||||||
|
/**
|
||||||
|
* Audio
|
||||||
|
*/
|
||||||
|
AUDIO = 2049,
|
||||||
|
/**
|
||||||
|
* Restricted Low-Delay
|
||||||
|
*/
|
||||||
|
RESTRICTED_LOWDELAY = 2051
|
||||||
|
}
|
||||||
|
enum OpusError {
|
||||||
|
"OK" = 0,
|
||||||
|
"Bad argument" = -1,
|
||||||
|
"Buffer too small" = -2,
|
||||||
|
"Internal error" = -3,
|
||||||
|
"Invalid packet" = -4,
|
||||||
|
"Unimplemented" = -5,
|
||||||
|
"Invalid state" = -6,
|
||||||
|
"Memory allocation fail" = -7
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Valid audio sampling rates
|
||||||
|
*/
|
||||||
|
type VALID_SAMPLING_RATES = 8000 | 12000 | 16000 | 24000 | 48000;
|
||||||
|
/**
|
||||||
|
* Maximum bytes in a frame
|
||||||
|
*/
|
||||||
|
type MAX_FRAME_SIZE = 2880;
|
||||||
|
/**
|
||||||
|
* Maximum bytes in a packet
|
||||||
|
*/
|
||||||
|
type MAX_PACKET_SIZE = 3828;
|
||||||
|
/**
|
||||||
|
* Constructor options for OpusScript
|
||||||
|
*/
|
||||||
|
interface OpusScriptOptions {
|
||||||
|
/**
|
||||||
|
* Whether or not to use the WASM-compiled version of OpusScript. This is true by default.
|
||||||
|
*/
|
||||||
|
wasm?: boolean;
|
||||||
|
}
|
||||||
|
class OpusScript {
|
||||||
|
/**
|
||||||
|
* Different Opus application types
|
||||||
|
*/
|
||||||
|
static Application: typeof OpusApplication;
|
||||||
|
/**
|
||||||
|
* Opus Error codes
|
||||||
|
*/
|
||||||
|
static Error: typeof OpusError;
|
||||||
|
/**
|
||||||
|
* Array of sampling rates that Opus can use
|
||||||
|
*/
|
||||||
|
static VALID_SAMPLING_RATES: [8000, 12000, 16000, 24000, 48000];
|
||||||
|
/**
|
||||||
|
* The maximum size (in bytes) to send in a packet
|
||||||
|
*/
|
||||||
|
static MAX_PACKET_SIZE: MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpusScript options being used
|
||||||
|
*/
|
||||||
|
options: OpusScriptOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Opus en/decoder
|
||||||
|
*/
|
||||||
|
constructor(samplingRate: VALID_SAMPLING_RATES, channels?: number, application?: OpusApplication, options?: OpusScriptOptions);
|
||||||
|
/**
|
||||||
|
* Encode a buffer into Opus
|
||||||
|
*/
|
||||||
|
encode(buffer: Buffer, frameSize: number): Buffer;
|
||||||
|
/**
|
||||||
|
* Decode an opus buffer
|
||||||
|
*/
|
||||||
|
decode(buffer: Buffer): Buffer;
|
||||||
|
encoderCTL(ctl: number, arg: number): void;
|
||||||
|
decoderCTL(ctl: number, arg: number): void;
|
||||||
|
/**
|
||||||
|
* Delete the opus object
|
||||||
|
*/
|
||||||
|
delete(): void;
|
||||||
|
}
|
||||||
|
export = OpusScript;
|
||||||
|
}
|
116
node_modules/opusscript/index.js
generated
vendored
Normal file
116
node_modules/opusscript/index.js
generated
vendored
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
let opusscript_native_nasm = null;
|
||||||
|
let opusscript_native_wasm = null;
|
||||||
|
|
||||||
|
var OpusApplication = {
|
||||||
|
VOIP: 2048,
|
||||||
|
AUDIO: 2049,
|
||||||
|
RESTRICTED_LOWDELAY: 2051
|
||||||
|
};
|
||||||
|
var OpusError = {
|
||||||
|
"0": "OK",
|
||||||
|
"-1": "Bad argument",
|
||||||
|
"-2": "Buffer too small",
|
||||||
|
"-3": "Internal error",
|
||||||
|
"-4": "Invalid packet",
|
||||||
|
"-5": "Unimplemented",
|
||||||
|
"-6": "Invalid state",
|
||||||
|
"-7": "Memory allocation fail"
|
||||||
|
};
|
||||||
|
var VALID_SAMPLING_RATES = [8000, 12000, 16000, 24000, 48000];
|
||||||
|
var MAX_FRAME_SIZE = 48000 * 60 / 1000;
|
||||||
|
var MAX_PACKET_SIZE = 1276 * 3;
|
||||||
|
|
||||||
|
function OpusScript(samplingRate, channels, application, options) {
|
||||||
|
if(!~VALID_SAMPLING_RATES.indexOf(samplingRate)) {
|
||||||
|
throw new RangeError(`${samplingRate} is an invalid sampling rate.`);
|
||||||
|
}
|
||||||
|
this.options = Object.assign({
|
||||||
|
wasm: true
|
||||||
|
}, options);
|
||||||
|
|
||||||
|
this.samplingRate = samplingRate;
|
||||||
|
this.channels = channels || 1;
|
||||||
|
this.application = application || OpusApplication.AUDIO;
|
||||||
|
|
||||||
|
let opusscript_native = null;
|
||||||
|
if(this.options.wasm) {
|
||||||
|
if(!opusscript_native_wasm) {
|
||||||
|
opusscript_native_wasm = require("./build/opusscript_native_wasm.js")();
|
||||||
|
}
|
||||||
|
opusscript_native = opusscript_native_wasm;
|
||||||
|
} else {
|
||||||
|
if(!opusscript_native_nasm) {
|
||||||
|
opusscript_native_nasm = require("./build/opusscript_native_nasm.js")();
|
||||||
|
}
|
||||||
|
opusscript_native = opusscript_native_nasm;
|
||||||
|
}
|
||||||
|
this.handler = new opusscript_native.OpusScriptHandler(this.samplingRate, this.channels, this.application);
|
||||||
|
|
||||||
|
this.inPCMLength = MAX_FRAME_SIZE * this.channels * 2;
|
||||||
|
this.inPCMPointer = opusscript_native._malloc(this.inPCMLength);
|
||||||
|
this.inPCM = opusscript_native.HEAPU16.subarray(this.inPCMPointer, this.inPCMPointer + this.inPCMLength);
|
||||||
|
|
||||||
|
this.inOpusPointer = opusscript_native._malloc(MAX_PACKET_SIZE);
|
||||||
|
this.inOpus = opusscript_native.HEAPU8.subarray(this.inOpusPointer, this.inOpusPointer + MAX_PACKET_SIZE);
|
||||||
|
|
||||||
|
this.outOpusPointer = opusscript_native._malloc(MAX_PACKET_SIZE);
|
||||||
|
this.outOpus = opusscript_native.HEAPU8.subarray(this.outOpusPointer, this.outOpusPointer + MAX_PACKET_SIZE);
|
||||||
|
|
||||||
|
this.outPCMLength = MAX_FRAME_SIZE * this.channels * 2;
|
||||||
|
this.outPCMPointer = opusscript_native._malloc(this.outPCMLength);
|
||||||
|
this.outPCM = opusscript_native.HEAPU16.subarray(this.outPCMPointer, this.outPCMPointer + this.outPCMLength);
|
||||||
|
};
|
||||||
|
|
||||||
|
OpusScript.prototype.encode = function encode(buffer, frameSize) {
|
||||||
|
this.inPCM.set(buffer);
|
||||||
|
|
||||||
|
var len = this.handler._encode(this.inPCM.byteOffset, buffer.length, this.outOpusPointer, frameSize);
|
||||||
|
if(len < 0) {
|
||||||
|
throw new Error("Encode error: " + OpusError["" + len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Buffer.from(this.outOpus.subarray(0, len));
|
||||||
|
};
|
||||||
|
|
||||||
|
OpusScript.prototype.decode = function decode(buffer) {
|
||||||
|
this.inOpus.set(buffer);
|
||||||
|
|
||||||
|
var len = this.handler._decode(this.inOpusPointer, buffer.length, this.outPCM.byteOffset);
|
||||||
|
if(len < 0) {
|
||||||
|
throw new Error("Decode error: " + OpusError["" + len]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Buffer.from(this.outPCM.subarray(0, len * this.channels * 2));
|
||||||
|
};
|
||||||
|
|
||||||
|
OpusScript.prototype.encoderCTL = function encoderCTL(ctl, arg) {
|
||||||
|
var len = this.handler._encoder_ctl(ctl, arg);
|
||||||
|
if(len < 0) {
|
||||||
|
throw new Error("Encoder CTL error: " + OpusError["" + len]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OpusScript.prototype.decoderCTL = function decoderCTL(ctl, arg) {
|
||||||
|
var len = this.handler._decoder_ctl(ctl, arg);
|
||||||
|
if(len < 0) {
|
||||||
|
throw new Error("Decoder CTL error: " + OpusError["" + len]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OpusScript.prototype.delete = function del() {
|
||||||
|
let opusscript_native = this.options.wasm ? opusscript_native_wasm : opusscript_native_nasm;
|
||||||
|
opusscript_native.OpusScriptHandler.destroy_handler(this.handler);
|
||||||
|
opusscript_native._free(this.inPCMPointer);
|
||||||
|
opusscript_native._free(this.inOpusPointer);
|
||||||
|
opusscript_native._free(this.outOpusPointer);
|
||||||
|
opusscript_native._free(this.outPCMPointer);
|
||||||
|
};
|
||||||
|
|
||||||
|
OpusScript.Application = OpusApplication;
|
||||||
|
OpusScript.Error = OpusError;
|
||||||
|
OpusScript.VALID_SAMPLING_RATES = VALID_SAMPLING_RATES;
|
||||||
|
OpusScript.MAX_PACKET_SIZE = MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
module.exports = OpusScript;
|
26
node_modules/opusscript/package.json
generated
vendored
Normal file
26
node_modules/opusscript/package.json
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "opusscript",
|
||||||
|
"version": "0.0.8",
|
||||||
|
"description": "JS bindings for libopus 1.3.1, ported with emscripten",
|
||||||
|
"main": "index.js",
|
||||||
|
"types": "index.d.ts",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/abalabahaha/opusscript.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"libopus",
|
||||||
|
"encoder",
|
||||||
|
"emscripten"
|
||||||
|
],
|
||||||
|
"author": "abalabahaha",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/abalabahaha/opusscript/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/abalabahaha/opusscript#readme",
|
||||||
|
"browser": {
|
||||||
|
"fs": false,
|
||||||
|
"path": false
|
||||||
|
}
|
||||||
|
}
|
27
node_modules/tweetnacl/AUTHORS.md
generated
vendored
Normal file
27
node_modules/tweetnacl/AUTHORS.md
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
List of TweetNaCl.js authors
|
||||||
|
============================
|
||||||
|
|
||||||
|
Format: Name (GitHub username or URL)
|
||||||
|
|
||||||
|
* Dmitry Chestnykh (@dchest)
|
||||||
|
* Devi Mandiri (@devi)
|
||||||
|
* AndSDev (@AndSDev)
|
||||||
|
|
||||||
|
List of authors of third-party public domain code from which TweetNaCl.js code was derived
|
||||||
|
==========================================================================================
|
||||||
|
|
||||||
|
[TweetNaCl](http://tweetnacl.cr.yp.to/)
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
* Bernard van Gastel
|
||||||
|
* Daniel J. Bernstein <http://cr.yp.to/djb.html>
|
||||||
|
* Peter Schwabe <http://www.cryptojedi.org/users/peter/>
|
||||||
|
* Sjaak Smetsers <http://www.cs.ru.nl/~sjakie/>
|
||||||
|
* Tanja Lange <http://hyperelliptic.org/tanja>
|
||||||
|
* Wesley Janssen
|
||||||
|
|
||||||
|
|
||||||
|
[Poly1305-donna](https://github.com/floodyberry/poly1305-donna)
|
||||||
|
--------------------------------------------------------------
|
||||||
|
|
||||||
|
* Andrew Moon (@floodyberry)
|
283
node_modules/tweetnacl/CHANGELOG.md
generated
vendored
Normal file
283
node_modules/tweetnacl/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
|||||||
|
TweetNaCl.js Changelog
|
||||||
|
======================
|
||||||
|
|
||||||
|
v1.0.3
|
||||||
|
------
|
||||||
|
|
||||||
|
***IMPORTANT BUG FIX***. Due to a bug in calculating carry in
|
||||||
|
modulo reduction that used bit operations on integers larger than
|
||||||
|
32 bits, `nacl.sign` or `nacl.sign.detached` could have created
|
||||||
|
incorrect signatures.
|
||||||
|
|
||||||
|
This only affects signing, not verification.
|
||||||
|
|
||||||
|
Thanks to @valerini on GitHub for finding and reporting the bug.
|
||||||
|
|
||||||
|
|
||||||
|
v1.0.2
|
||||||
|
------
|
||||||
|
|
||||||
|
Exported more internal undocumented functions for
|
||||||
|
third-party projects that rely on low-level interface,
|
||||||
|
(something users of TweetNaCl shouldn't care about).
|
||||||
|
|
||||||
|
|
||||||
|
v1.0.1
|
||||||
|
------
|
||||||
|
|
||||||
|
Updated documentation and typings.
|
||||||
|
|
||||||
|
|
||||||
|
v1.0.0
|
||||||
|
------
|
||||||
|
|
||||||
|
No code changes from v1.0.0-rc.1.
|
||||||
|
|
||||||
|
|
||||||
|
v1.0.0-rc.1
|
||||||
|
-----------
|
||||||
|
|
||||||
|
* **IMPORTANT!** In previous versions, `nacl.secretbox.open`, `nacl.box.open`,
|
||||||
|
and `nacl.box.after` returned `false` when opening failed (for example, when
|
||||||
|
using incorrect key, nonce, or when input was maliciously or accidentally
|
||||||
|
modified after encryption). This version instead returns `null`.
|
||||||
|
|
||||||
|
The usual way to check for this condition:
|
||||||
|
|
||||||
|
`if (!result) { ... }`
|
||||||
|
|
||||||
|
is correct and will continue to work.
|
||||||
|
|
||||||
|
However, direct comparison with `false`:
|
||||||
|
|
||||||
|
`if (result == false) { ... }`
|
||||||
|
|
||||||
|
it will no longer work and **will not detect failure**. Please check
|
||||||
|
your code for this condition.
|
||||||
|
|
||||||
|
(`nacl.sign.open` always returned `null`, so it is not affected.)
|
||||||
|
|
||||||
|
|
||||||
|
* Arguments type check now uses `instanceof Uint8Array` instead of `Object.prototype.toString`.
|
||||||
|
* Removed deprecation checks for `nacl.util` (moved to a
|
||||||
|
[separate package](https://github.com/dchest/tweetnacl-util-js) in v0.14.0).
|
||||||
|
* Removed deprecation checks for the old signature API (changed in v0.10.0).
|
||||||
|
* Improved benchmarking.
|
||||||
|
|
||||||
|
v0.14.5
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Fixed incomplete return types in TypeScript typings.
|
||||||
|
* Replaced COPYING.txt with LICENSE file, which now has public domain dedication
|
||||||
|
text from The Unlicense. License fields in package.json and bower.json have
|
||||||
|
been set to "Unlicense". The project was and will be in the public domain --
|
||||||
|
this change just makes it easier for automated tools to know about this fact by
|
||||||
|
using the widely recognized and SPDX-compatible template for public domain
|
||||||
|
dedication.
|
||||||
|
|
||||||
|
|
||||||
|
v0.14.4
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Added TypeScript type definitions (contributed by @AndSDev).
|
||||||
|
* Improved benchmarking code.
|
||||||
|
|
||||||
|
|
||||||
|
v0.14.3
|
||||||
|
-------
|
||||||
|
|
||||||
|
Fixed a bug in the fast version of Poly1305 and brought it back.
|
||||||
|
|
||||||
|
Thanks to @floodyberry for promptly responding and fixing the original C code:
|
||||||
|
|
||||||
|
> "The issue was not properly detecting if st->h was >= 2^130 - 5, coupled with
|
||||||
|
> [testing mistake] not catching the failure. The chance of the bug affecting
|
||||||
|
> anything in the real world is essentially zero luckily, but it's good to have
|
||||||
|
> it fixed."
|
||||||
|
|
||||||
|
https://github.com/floodyberry/poly1305-donna/issues/2#issuecomment-202698577
|
||||||
|
|
||||||
|
|
||||||
|
v0.14.2
|
||||||
|
-------
|
||||||
|
|
||||||
|
Switched Poly1305 fast version back to original (slow) version due to a bug.
|
||||||
|
|
||||||
|
|
||||||
|
v0.14.1
|
||||||
|
-------
|
||||||
|
|
||||||
|
No code changes, just tweaked packaging and added COPYING.txt.
|
||||||
|
|
||||||
|
|
||||||
|
v0.14.0
|
||||||
|
-------
|
||||||
|
|
||||||
|
* **Breaking change!** All functions from `nacl.util` have been removed. These
|
||||||
|
functions are no longer available:
|
||||||
|
|
||||||
|
nacl.util.decodeUTF8
|
||||||
|
nacl.util.encodeUTF8
|
||||||
|
nacl.util.decodeBase64
|
||||||
|
nacl.util.encodeBase64
|
||||||
|
|
||||||
|
If want to continue using them, you can include
|
||||||
|
<https://github.com/dchest/tweetnacl-util-js> package:
|
||||||
|
|
||||||
|
<script src="nacl.min.js"></script>
|
||||||
|
<script src="nacl-util.min.js"></script>
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
var nacl = require('tweetnacl');
|
||||||
|
nacl.util = require('tweetnacl-util');
|
||||||
|
|
||||||
|
However it is recommended to use better packages that have wider
|
||||||
|
compatibility and better performance. Functions from `nacl.util` were never
|
||||||
|
intended to be robust solution for string conversion and were included for
|
||||||
|
convenience: cryptography library is not the right place for them.
|
||||||
|
|
||||||
|
Currently calling these functions will throw error pointing to
|
||||||
|
`tweetnacl-util-js` (in the next version this error message will be removed).
|
||||||
|
|
||||||
|
* Improved detection of available random number generators, making it possible
|
||||||
|
to use `nacl.randomBytes` and related functions in Web Workers without
|
||||||
|
changes.
|
||||||
|
|
||||||
|
* Changes to testing (see README).
|
||||||
|
|
||||||
|
|
||||||
|
v0.13.3
|
||||||
|
-------
|
||||||
|
|
||||||
|
No code changes.
|
||||||
|
|
||||||
|
* Reverted license field in package.json to "Public domain".
|
||||||
|
|
||||||
|
* Fixed typo in README.
|
||||||
|
|
||||||
|
|
||||||
|
v0.13.2
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Fixed undefined variable bug in fast version of Poly1305. No worries, this
|
||||||
|
bug was *never* triggered.
|
||||||
|
|
||||||
|
* Specified CC0 public domain dedication.
|
||||||
|
|
||||||
|
* Updated development dependencies.
|
||||||
|
|
||||||
|
|
||||||
|
v0.13.1
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Exclude `crypto` and `buffer` modules from browserify builds.
|
||||||
|
|
||||||
|
|
||||||
|
v0.13.0
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Made `nacl-fast` the default version in NPM package. Now
|
||||||
|
`require("tweetnacl")` will use fast version; to get the original version,
|
||||||
|
use `require("tweetnacl/nacl.js")`.
|
||||||
|
|
||||||
|
* Cleanup temporary array after generating random bytes.
|
||||||
|
|
||||||
|
|
||||||
|
v0.12.2
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Improved performance of curve operations, making `nacl.scalarMult`, `nacl.box`,
|
||||||
|
`nacl.sign` and related functions up to 3x faster in `nacl-fast` version.
|
||||||
|
|
||||||
|
|
||||||
|
v0.12.1
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Significantly improved performance of Salsa20 (~1.5x faster) and
|
||||||
|
Poly1305 (~3.5x faster) in `nacl-fast` version.
|
||||||
|
|
||||||
|
|
||||||
|
v0.12.0
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Instead of using the given secret key directly, TweetNaCl.js now copies it to
|
||||||
|
a new array in `nacl.box.keyPair.fromSecretKey` and
|
||||||
|
`nacl.sign.keyPair.fromSecretKey`.
|
||||||
|
|
||||||
|
|
||||||
|
v0.11.2
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Added new constant: `nacl.sign.seedLength`.
|
||||||
|
|
||||||
|
|
||||||
|
v0.11.1
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Even faster hash for both short and long inputs (in `nacl-fast`).
|
||||||
|
|
||||||
|
|
||||||
|
v0.11.0
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Implement `nacl.sign.keyPair.fromSeed` to enable creation of sign key pairs
|
||||||
|
deterministically from a 32-byte seed. (It behaves like
|
||||||
|
[libsodium's](http://doc.libsodium.org/public-key_cryptography/public-key_signatures.html)
|
||||||
|
`crypto_sign_seed_keypair`: the seed becomes a secret part of the secret key.)
|
||||||
|
|
||||||
|
* Fast version now has an improved hash implementation that is 2x-5x faster.
|
||||||
|
|
||||||
|
* Fixed benchmarks, which may have produced incorrect measurements.
|
||||||
|
|
||||||
|
|
||||||
|
v0.10.1
|
||||||
|
-------
|
||||||
|
|
||||||
|
* Exported undocumented `nacl.lowlevel.crypto_core_hsalsa20`.
|
||||||
|
|
||||||
|
|
||||||
|
v0.10.0
|
||||||
|
-------
|
||||||
|
|
||||||
|
* **Signature API breaking change!** `nacl.sign` and `nacl.sign.open` now deal
|
||||||
|
with signed messages, and new `nacl.sign.detached` and
|
||||||
|
`nacl.sign.detached.verify` are available.
|
||||||
|
|
||||||
|
Previously, `nacl.sign` returned a signature, and `nacl.sign.open` accepted a
|
||||||
|
message and "detached" signature. This was unlike NaCl's API, which dealt with
|
||||||
|
signed messages (concatenation of signature and message).
|
||||||
|
|
||||||
|
The new API is:
|
||||||
|
|
||||||
|
nacl.sign(message, secretKey) -> signedMessage
|
||||||
|
nacl.sign.open(signedMessage, publicKey) -> message | null
|
||||||
|
|
||||||
|
Since detached signatures are common, two new API functions were introduced:
|
||||||
|
|
||||||
|
nacl.sign.detached(message, secretKey) -> signature
|
||||||
|
nacl.sign.detached.verify(message, signature, publicKey) -> true | false
|
||||||
|
|
||||||
|
(Note that it's `verify`, not `open`, and it returns a boolean value, unlike
|
||||||
|
`open`, which returns an "unsigned" message.)
|
||||||
|
|
||||||
|
* NPM package now comes without `test` directory to keep it small.
|
||||||
|
|
||||||
|
|
||||||
|
v0.9.2
|
||||||
|
------
|
||||||
|
|
||||||
|
* Improved documentation.
|
||||||
|
* Fast version: increased theoretical message size limit from 2^32-1 to 2^52
|
||||||
|
bytes in Poly1305 (and thus, secretbox and box). However this has no impact
|
||||||
|
in practice since JavaScript arrays or ArrayBuffers are limited to 32-bit
|
||||||
|
indexes, and most implementations won't allocate more than a gigabyte or so.
|
||||||
|
(Obviously, there are no tests for the correctness of implementation.) Also,
|
||||||
|
it's not recommended to use messages that large without splitting them into
|
||||||
|
smaller packets anyway.
|
||||||
|
|
||||||
|
|
||||||
|
v0.9.1
|
||||||
|
------
|
||||||
|
|
||||||
|
* Initial release
|
24
node_modules/tweetnacl/LICENSE
generated
vendored
Normal file
24
node_modules/tweetnacl/LICENSE
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
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 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.
|
||||||
|
|
||||||
|
For more information, please refer to <http://unlicense.org>
|
20
node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
20
node_modules/tweetnacl/PULL_REQUEST_TEMPLATE.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Important!
|
||||||
|
|
||||||
|
If your contribution is not trivial (not a typo fix, etc.), we can only accept
|
||||||
|
it if you dedicate your copyright for the contribution to the public domain.
|
||||||
|
Make sure you understand what it means (see http://unlicense.org/)! If you
|
||||||
|
agree, please add yourself to AUTHORS.md file, and include the following text
|
||||||
|
to your pull request description or a comment in it:
|
||||||
|
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
I dedicate any and all copyright interest in this software to the
|
||||||
|
public domain. I make this dedication for the benefit of the public at
|
||||||
|
large and to the detriment of my heirs and successors. I intend this
|
||||||
|
dedication to be an overt act of relinquishment in perpetuity of all
|
||||||
|
present and future rights to this software under copyright law.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
494
node_modules/tweetnacl/README.md
generated
vendored
Normal file
494
node_modules/tweetnacl/README.md
generated
vendored
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
TweetNaCl.js
|
||||||
|
============
|
||||||
|
|
||||||
|
Port of [TweetNaCl](http://tweetnacl.cr.yp.to) / [NaCl](http://nacl.cr.yp.to/)
|
||||||
|
to JavaScript for modern browsers and Node.js. Public domain.
|
||||||
|
|
||||||
|
[
|
||||||
|
](https://travis-ci.org/dchest/tweetnacl-js)
|
||||||
|
|
||||||
|
Demo: <https://dchest.github.io/tweetnacl-js/>
|
||||||
|
|
||||||
|
Documentation
|
||||||
|
=============
|
||||||
|
|
||||||
|
* [Overview](#overview)
|
||||||
|
* [Audits](#audits)
|
||||||
|
* [Installation](#installation)
|
||||||
|
* [Examples](#examples)
|
||||||
|
* [Usage](#usage)
|
||||||
|
* [Public-key authenticated encryption (box)](#public-key-authenticated-encryption-box)
|
||||||
|
* [Secret-key authenticated encryption (secretbox)](#secret-key-authenticated-encryption-secretbox)
|
||||||
|
* [Scalar multiplication](#scalar-multiplication)
|
||||||
|
* [Signatures](#signatures)
|
||||||
|
* [Hashing](#hashing)
|
||||||
|
* [Random bytes generation](#random-bytes-generation)
|
||||||
|
* [Constant-time comparison](#constant-time-comparison)
|
||||||
|
* [System requirements](#system-requirements)
|
||||||
|
* [Development and testing](#development-and-testing)
|
||||||
|
* [Benchmarks](#benchmarks)
|
||||||
|
* [Contributors](#contributors)
|
||||||
|
* [Who uses it](#who-uses-it)
|
||||||
|
|
||||||
|
|
||||||
|
Overview
|
||||||
|
--------
|
||||||
|
|
||||||
|
The primary goal of this project is to produce a translation of TweetNaCl to
|
||||||
|
JavaScript which is as close as possible to the original C implementation, plus
|
||||||
|
a thin layer of idiomatic high-level API on top of it.
|
||||||
|
|
||||||
|
There are two versions, you can use either of them:
|
||||||
|
|
||||||
|
* `nacl.js` is the port of TweetNaCl with minimum differences from the
|
||||||
|
original + high-level API.
|
||||||
|
|
||||||
|
* `nacl-fast.js` is like `nacl.js`, but with some functions replaced with
|
||||||
|
faster versions. (Used by default when importing NPM package.)
|
||||||
|
|
||||||
|
|
||||||
|
Audits
|
||||||
|
------
|
||||||
|
|
||||||
|
TweetNaCl.js has been audited by [Cure53](https://cure53.de/) in January-February
|
||||||
|
2017 (audit was sponsored by [Deletype](https://deletype.com)):
|
||||||
|
|
||||||
|
> The overall outcome of this audit signals a particularly positive assessment
|
||||||
|
> for TweetNaCl-js, as the testing team was unable to find any security
|
||||||
|
> problems in the library. It has to be noted that this is an exceptionally
|
||||||
|
> rare result of a source code audit for any project and must be seen as a true
|
||||||
|
> testament to a development proceeding with security at its core.
|
||||||
|
>
|
||||||
|
> To reiterate, the TweetNaCl-js project, the source code was found to be
|
||||||
|
> bug-free at this point.
|
||||||
|
>
|
||||||
|
> [...]
|
||||||
|
>
|
||||||
|
> In sum, the testing team is happy to recommend the TweetNaCl-js project as
|
||||||
|
> likely one of the safer and more secure cryptographic tools among its
|
||||||
|
> competition.
|
||||||
|
|
||||||
|
[Read full audit report](https://cure53.de/tweetnacl.pdf)
|
||||||
|
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
You can install TweetNaCl.js via a package manager:
|
||||||
|
|
||||||
|
[Yarn](https://yarnpkg.com/):
|
||||||
|
|
||||||
|
$ yarn add tweetnacl
|
||||||
|
|
||||||
|
[NPM](https://www.npmjs.org/):
|
||||||
|
|
||||||
|
$ npm install tweetnacl
|
||||||
|
|
||||||
|
or [download source code](https://github.com/dchest/tweetnacl-js/releases).
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
You can find usage examples in our [wiki](https://github.com/dchest/tweetnacl-js/wiki/Examples).
|
||||||
|
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
All API functions accept and return bytes as `Uint8Array`s. If you need to
|
||||||
|
encode or decode strings, use functions from
|
||||||
|
<https://github.com/dchest/tweetnacl-util-js> or one of the more robust codec
|
||||||
|
packages.
|
||||||
|
|
||||||
|
In Node.js v4 and later `Buffer` objects are backed by `Uint8Array`s, so you
|
||||||
|
can freely pass them to TweetNaCl.js functions as arguments. The returned
|
||||||
|
objects are still `Uint8Array`s, so if you need `Buffer`s, you'll have to
|
||||||
|
convert them manually; make sure to convert using copying: `Buffer.from(array)`
|
||||||
|
(or `new Buffer(array)` in Node.js v4 or earlier), instead of sharing:
|
||||||
|
`Buffer.from(array.buffer)` (or `new Buffer(array.buffer)` Node 4 or earlier),
|
||||||
|
because some functions return subarrays of their buffers.
|
||||||
|
|
||||||
|
|
||||||
|
### Public-key authenticated encryption (box)
|
||||||
|
|
||||||
|
Implements *x25519-xsalsa20-poly1305*.
|
||||||
|
|
||||||
|
#### nacl.box.keyPair()
|
||||||
|
|
||||||
|
Generates a new random key pair for box and returns it as an object with
|
||||||
|
`publicKey` and `secretKey` members:
|
||||||
|
|
||||||
|
{
|
||||||
|
publicKey: ..., // Uint8Array with 32-byte public key
|
||||||
|
secretKey: ... // Uint8Array with 32-byte secret key
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#### nacl.box.keyPair.fromSecretKey(secretKey)
|
||||||
|
|
||||||
|
Returns a key pair for box with public key corresponding to the given secret
|
||||||
|
key.
|
||||||
|
|
||||||
|
#### nacl.box(message, nonce, theirPublicKey, mySecretKey)
|
||||||
|
|
||||||
|
Encrypts and authenticates message using peer's public key, our secret key, and
|
||||||
|
the given nonce, which must be unique for each distinct message for a key pair.
|
||||||
|
|
||||||
|
Returns an encrypted and authenticated message, which is
|
||||||
|
`nacl.box.overheadLength` longer than the original message.
|
||||||
|
|
||||||
|
#### nacl.box.open(box, nonce, theirPublicKey, mySecretKey)
|
||||||
|
|
||||||
|
Authenticates and decrypts the given box with peer's public key, our secret
|
||||||
|
key, and the given nonce.
|
||||||
|
|
||||||
|
Returns the original message, or `null` if authentication fails.
|
||||||
|
|
||||||
|
#### nacl.box.before(theirPublicKey, mySecretKey)
|
||||||
|
|
||||||
|
Returns a precomputed shared key which can be used in `nacl.box.after` and
|
||||||
|
`nacl.box.open.after`.
|
||||||
|
|
||||||
|
#### nacl.box.after(message, nonce, sharedKey)
|
||||||
|
|
||||||
|
Same as `nacl.box`, but uses a shared key precomputed with `nacl.box.before`.
|
||||||
|
|
||||||
|
#### nacl.box.open.after(box, nonce, sharedKey)
|
||||||
|
|
||||||
|
Same as `nacl.box.open`, but uses a shared key precomputed with `nacl.box.before`.
|
||||||
|
|
||||||
|
#### Constants
|
||||||
|
|
||||||
|
##### nacl.box.publicKeyLength = 32
|
||||||
|
|
||||||
|
Length of public key in bytes.
|
||||||
|
|
||||||
|
##### nacl.box.secretKeyLength = 32
|
||||||
|
|
||||||
|
Length of secret key in bytes.
|
||||||
|
|
||||||
|
##### nacl.box.sharedKeyLength = 32
|
||||||
|
|
||||||
|
Length of precomputed shared key in bytes.
|
||||||
|
|
||||||
|
##### nacl.box.nonceLength = 24
|
||||||
|
|
||||||
|
Length of nonce in bytes.
|
||||||
|
|
||||||
|
##### nacl.box.overheadLength = 16
|
||||||
|
|
||||||
|
Length of overhead added to box compared to original message.
|
||||||
|
|
||||||
|
|
||||||
|
### Secret-key authenticated encryption (secretbox)
|
||||||
|
|
||||||
|
Implements *xsalsa20-poly1305*.
|
||||||
|
|
||||||
|
#### nacl.secretbox(message, nonce, key)
|
||||||
|
|
||||||
|
Encrypts and authenticates message using the key and the nonce. The nonce must
|
||||||
|
be unique for each distinct message for this key.
|
||||||
|
|
||||||
|
Returns an encrypted and authenticated message, which is
|
||||||
|
`nacl.secretbox.overheadLength` longer than the original message.
|
||||||
|
|
||||||
|
#### nacl.secretbox.open(box, nonce, key)
|
||||||
|
|
||||||
|
Authenticates and decrypts the given secret box using the key and the nonce.
|
||||||
|
|
||||||
|
Returns the original message, or `null` if authentication fails.
|
||||||
|
|
||||||
|
#### Constants
|
||||||
|
|
||||||
|
##### nacl.secretbox.keyLength = 32
|
||||||
|
|
||||||
|
Length of key in bytes.
|
||||||
|
|
||||||
|
##### nacl.secretbox.nonceLength = 24
|
||||||
|
|
||||||
|
Length of nonce in bytes.
|
||||||
|
|
||||||
|
##### nacl.secretbox.overheadLength = 16
|
||||||
|
|
||||||
|
Length of overhead added to secret box compared to original message.
|
||||||
|
|
||||||
|
|
||||||
|
### Scalar multiplication
|
||||||
|
|
||||||
|
Implements *x25519*.
|
||||||
|
|
||||||
|
#### nacl.scalarMult(n, p)
|
||||||
|
|
||||||
|
Multiplies an integer `n` by a group element `p` and returns the resulting
|
||||||
|
group element.
|
||||||
|
|
||||||
|
#### nacl.scalarMult.base(n)
|
||||||
|
|
||||||
|
Multiplies an integer `n` by a standard group element and returns the resulting
|
||||||
|
group element.
|
||||||
|
|
||||||
|
#### Constants
|
||||||
|
|
||||||
|
##### nacl.scalarMult.scalarLength = 32
|
||||||
|
|
||||||
|
Length of scalar in bytes.
|
||||||
|
|
||||||
|
##### nacl.scalarMult.groupElementLength = 32
|
||||||
|
|
||||||
|
Length of group element in bytes.
|
||||||
|
|
||||||
|
|
||||||
|
### Signatures
|
||||||
|
|
||||||
|
Implements [ed25519](http://ed25519.cr.yp.to).
|
||||||
|
|
||||||
|
#### nacl.sign.keyPair()
|
||||||
|
|
||||||
|
Generates new random key pair for signing and returns it as an object with
|
||||||
|
`publicKey` and `secretKey` members:
|
||||||
|
|
||||||
|
{
|
||||||
|
publicKey: ..., // Uint8Array with 32-byte public key
|
||||||
|
secretKey: ... // Uint8Array with 64-byte secret key
|
||||||
|
}
|
||||||
|
|
||||||
|
#### nacl.sign.keyPair.fromSecretKey(secretKey)
|
||||||
|
|
||||||
|
Returns a signing key pair with public key corresponding to the given
|
||||||
|
64-byte secret key. The secret key must have been generated by
|
||||||
|
`nacl.sign.keyPair` or `nacl.sign.keyPair.fromSeed`.
|
||||||
|
|
||||||
|
#### nacl.sign.keyPair.fromSeed(seed)
|
||||||
|
|
||||||
|
Returns a new signing key pair generated deterministically from a 32-byte seed.
|
||||||
|
The seed must contain enough entropy to be secure. This method is not
|
||||||
|
recommended for general use: instead, use `nacl.sign.keyPair` to generate a new
|
||||||
|
key pair from a random seed.
|
||||||
|
|
||||||
|
#### nacl.sign(message, secretKey)
|
||||||
|
|
||||||
|
Signs the message using the secret key and returns a signed message.
|
||||||
|
|
||||||
|
#### nacl.sign.open(signedMessage, publicKey)
|
||||||
|
|
||||||
|
Verifies the signed message and returns the message without signature.
|
||||||
|
|
||||||
|
Returns `null` if verification failed.
|
||||||
|
|
||||||
|
#### nacl.sign.detached(message, secretKey)
|
||||||
|
|
||||||
|
Signs the message using the secret key and returns a signature.
|
||||||
|
|
||||||
|
#### nacl.sign.detached.verify(message, signature, publicKey)
|
||||||
|
|
||||||
|
Verifies the signature for the message and returns `true` if verification
|
||||||
|
succeeded or `false` if it failed.
|
||||||
|
|
||||||
|
#### Constants
|
||||||
|
|
||||||
|
##### nacl.sign.publicKeyLength = 32
|
||||||
|
|
||||||
|
Length of signing public key in bytes.
|
||||||
|
|
||||||
|
##### nacl.sign.secretKeyLength = 64
|
||||||
|
|
||||||
|
Length of signing secret key in bytes.
|
||||||
|
|
||||||
|
##### nacl.sign.seedLength = 32
|
||||||
|
|
||||||
|
Length of seed for `nacl.sign.keyPair.fromSeed` in bytes.
|
||||||
|
|
||||||
|
##### nacl.sign.signatureLength = 64
|
||||||
|
|
||||||
|
Length of signature in bytes.
|
||||||
|
|
||||||
|
|
||||||
|
### Hashing
|
||||||
|
|
||||||
|
Implements *SHA-512*.
|
||||||
|
|
||||||
|
#### nacl.hash(message)
|
||||||
|
|
||||||
|
Returns SHA-512 hash of the message.
|
||||||
|
|
||||||
|
#### Constants
|
||||||
|
|
||||||
|
##### nacl.hash.hashLength = 64
|
||||||
|
|
||||||
|
Length of hash in bytes.
|
||||||
|
|
||||||
|
|
||||||
|
### Random bytes generation
|
||||||
|
|
||||||
|
#### nacl.randomBytes(length)
|
||||||
|
|
||||||
|
Returns a `Uint8Array` of the given length containing random bytes of
|
||||||
|
cryptographic quality.
|
||||||
|
|
||||||
|
**Implementation note**
|
||||||
|
|
||||||
|
TweetNaCl.js uses the following methods to generate random bytes,
|
||||||
|
depending on the platform it runs on:
|
||||||
|
|
||||||
|
* `window.crypto.getRandomValues` (WebCrypto standard)
|
||||||
|
* `window.msCrypto.getRandomValues` (Internet Explorer 11)
|
||||||
|
* `crypto.randomBytes` (Node.js)
|
||||||
|
|
||||||
|
If the platform doesn't provide a suitable PRNG, the following functions,
|
||||||
|
which require random numbers, will throw exception:
|
||||||
|
|
||||||
|
* `nacl.randomBytes`
|
||||||
|
* `nacl.box.keyPair`
|
||||||
|
* `nacl.sign.keyPair`
|
||||||
|
|
||||||
|
Other functions are deterministic and will continue working.
|
||||||
|
|
||||||
|
If a platform you are targeting doesn't implement secure random number
|
||||||
|
generator, but you somehow have a cryptographically-strong source of entropy
|
||||||
|
(not `Math.random`!), and you know what you are doing, you can plug it into
|
||||||
|
TweetNaCl.js like this:
|
||||||
|
|
||||||
|
nacl.setPRNG(function(x, n) {
|
||||||
|
// ... copy n random bytes into x ...
|
||||||
|
});
|
||||||
|
|
||||||
|
Note that `nacl.setPRNG` *completely replaces* internal random byte generator
|
||||||
|
with the one provided.
|
||||||
|
|
||||||
|
|
||||||
|
### Constant-time comparison
|
||||||
|
|
||||||
|
#### nacl.verify(x, y)
|
||||||
|
|
||||||
|
Compares `x` and `y` in constant time and returns `true` if their lengths are
|
||||||
|
non-zero and equal, and their contents are equal.
|
||||||
|
|
||||||
|
Returns `false` if either of the arguments has zero length, or arguments have
|
||||||
|
different lengths, or their contents differ.
|
||||||
|
|
||||||
|
|
||||||
|
System requirements
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
TweetNaCl.js supports modern browsers that have a cryptographically secure
|
||||||
|
pseudorandom number generator and typed arrays, including the latest versions
|
||||||
|
of:
|
||||||
|
|
||||||
|
* Chrome
|
||||||
|
* Firefox
|
||||||
|
* Safari (Mac, iOS)
|
||||||
|
* Internet Explorer 11
|
||||||
|
|
||||||
|
Other systems:
|
||||||
|
|
||||||
|
* Node.js
|
||||||
|
|
||||||
|
|
||||||
|
Development and testing
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
Install NPM modules needed for development:
|
||||||
|
|
||||||
|
$ npm install
|
||||||
|
|
||||||
|
To build minified versions:
|
||||||
|
|
||||||
|
$ npm run build
|
||||||
|
|
||||||
|
Tests use minified version, so make sure to rebuild it every time you change
|
||||||
|
`nacl.js` or `nacl-fast.js`.
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
To run tests in Node.js:
|
||||||
|
|
||||||
|
$ npm run test-node
|
||||||
|
|
||||||
|
By default all tests described here work on `nacl.min.js`. To test other
|
||||||
|
versions, set environment variable `NACL_SRC` to the file name you want to test.
|
||||||
|
For example, the following command will test fast minified version:
|
||||||
|
|
||||||
|
$ NACL_SRC=nacl-fast.min.js npm run test-node
|
||||||
|
|
||||||
|
To run full suite of tests in Node.js, including comparing outputs of
|
||||||
|
JavaScript port to outputs of the original C version:
|
||||||
|
|
||||||
|
$ npm run test-node-all
|
||||||
|
|
||||||
|
To prepare tests for browsers:
|
||||||
|
|
||||||
|
$ npm run build-test-browser
|
||||||
|
|
||||||
|
and then open `test/browser/test.html` (or `test/browser/test-fast.html`) to
|
||||||
|
run them.
|
||||||
|
|
||||||
|
To run tests in both Node and Electron:
|
||||||
|
|
||||||
|
$ npm test
|
||||||
|
|
||||||
|
### Benchmarking
|
||||||
|
|
||||||
|
To run benchmarks in Node.js:
|
||||||
|
|
||||||
|
$ npm run bench
|
||||||
|
$ NACL_SRC=nacl-fast.min.js npm run bench
|
||||||
|
|
||||||
|
To run benchmarks in a browser, open `test/benchmark/bench.html` (or
|
||||||
|
`test/benchmark/bench-fast.html`).
|
||||||
|
|
||||||
|
|
||||||
|
Benchmarks
|
||||||
|
----------
|
||||||
|
|
||||||
|
For reference, here are benchmarks from MacBook Pro (Retina, 13-inch, Mid 2014)
|
||||||
|
laptop with 2.6 GHz Intel Core i5 CPU (Intel) in Chrome 53/OS X and Xiaomi Redmi
|
||||||
|
Note 3 smartphone with 1.8 GHz Qualcomm Snapdragon 650 64-bit CPU (ARM) in
|
||||||
|
Chrome 52/Android:
|
||||||
|
|
||||||
|
| | nacl.js Intel | nacl-fast.js Intel | nacl.js ARM | nacl-fast.js ARM |
|
||||||
|
| ------------- |:-------------:|:-------------------:|:-------------:|:-----------------:|
|
||||||
|
| salsa20 | 1.3 MB/s | 128 MB/s | 0.4 MB/s | 43 MB/s |
|
||||||
|
| poly1305 | 13 MB/s | 171 MB/s | 4 MB/s | 52 MB/s |
|
||||||
|
| hash | 4 MB/s | 34 MB/s | 0.9 MB/s | 12 MB/s |
|
||||||
|
| secretbox 1K | 1113 op/s | 57583 op/s | 334 op/s | 14227 op/s |
|
||||||
|
| box 1K | 145 op/s | 718 op/s | 37 op/s | 368 op/s |
|
||||||
|
| scalarMult | 171 op/s | 733 op/s | 56 op/s | 380 op/s |
|
||||||
|
| sign | 77 op/s | 200 op/s | 20 op/s | 61 op/s |
|
||||||
|
| sign.open | 39 op/s | 102 op/s | 11 op/s | 31 op/s |
|
||||||
|
|
||||||
|
(You can run benchmarks on your devices by clicking on the links at the bottom
|
||||||
|
of the [home page](https://tweetnacl.js.org)).
|
||||||
|
|
||||||
|
In short, with *nacl-fast.js* and 1024-byte messages you can expect to encrypt and
|
||||||
|
authenticate more than 57000 messages per second on a typical laptop or more than
|
||||||
|
14000 messages per second on a $170 smartphone, sign about 200 and verify 100
|
||||||
|
messages per second on a laptop or 60 and 30 messages per second on a smartphone,
|
||||||
|
per CPU core (with Web Workers you can do these operations in parallel),
|
||||||
|
which is good enough for most applications.
|
||||||
|
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
See AUTHORS.md file.
|
||||||
|
|
||||||
|
|
||||||
|
Third-party libraries based on TweetNaCl.js
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
* [forward-secrecy](https://github.com/alax/forward-secrecy) — Axolotl ratchet implementation
|
||||||
|
* [nacl-stream](https://github.com/dchest/nacl-stream-js) - streaming encryption
|
||||||
|
* [tweetnacl-auth-js](https://github.com/dchest/tweetnacl-auth-js) — implementation of [`crypto_auth`](http://nacl.cr.yp.to/auth.html)
|
||||||
|
* [tweetnacl-sealed-box](https://github.com/whs/tweetnacl-sealed-box) — implementation of [`sealed boxes`](https://download.libsodium.org/doc/public-key_cryptography/sealed_boxes.html)
|
||||||
|
* [chloride](https://github.com/dominictarr/chloride) - unified API for various NaCl modules
|
||||||
|
|
||||||
|
|
||||||
|
Who uses it
|
||||||
|
-----------
|
||||||
|
|
||||||
|
Some notable users of TweetNaCl.js:
|
||||||
|
|
||||||
|
* [GitHub](https://github.com)
|
||||||
|
* [MEGA](https://github.com/meganz/webclient)
|
||||||
|
* [Stellar](https://www.stellar.org/)
|
||||||
|
* [miniLock](https://github.com/kaepora/miniLock)
|
2391
node_modules/tweetnacl/nacl-fast.js
generated
vendored
Normal file
2391
node_modules/tweetnacl/nacl-fast.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/tweetnacl/nacl-fast.min.js
generated
vendored
Normal file
1
node_modules/tweetnacl/nacl-fast.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
98
node_modules/tweetnacl/nacl.d.ts
generated
vendored
Normal file
98
node_modules/tweetnacl/nacl.d.ts
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
// Type definitions for TweetNaCl.js
|
||||||
|
|
||||||
|
export as namespace nacl;
|
||||||
|
|
||||||
|
declare var nacl: nacl;
|
||||||
|
export = nacl;
|
||||||
|
|
||||||
|
declare namespace nacl {
|
||||||
|
export interface BoxKeyPair {
|
||||||
|
publicKey: Uint8Array;
|
||||||
|
secretKey: Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SignKeyPair {
|
||||||
|
publicKey: Uint8Array;
|
||||||
|
secretKey: Uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface secretbox {
|
||||||
|
(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
||||||
|
open(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | null;
|
||||||
|
readonly keyLength: number;
|
||||||
|
readonly nonceLength: number;
|
||||||
|
readonly overheadLength: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface scalarMult {
|
||||||
|
(n: Uint8Array, p: Uint8Array): Uint8Array;
|
||||||
|
base(n: Uint8Array): Uint8Array;
|
||||||
|
readonly scalarLength: number;
|
||||||
|
readonly groupElementLength: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace boxProps {
|
||||||
|
export interface open {
|
||||||
|
(msg: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array | null;
|
||||||
|
after(box: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface keyPair {
|
||||||
|
(): BoxKeyPair;
|
||||||
|
fromSecretKey(secretKey: Uint8Array): BoxKeyPair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface box {
|
||||||
|
(msg: Uint8Array, nonce: Uint8Array, publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||||
|
before(publicKey: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||||
|
after(msg: Uint8Array, nonce: Uint8Array, key: Uint8Array): Uint8Array;
|
||||||
|
open: boxProps.open;
|
||||||
|
keyPair: boxProps.keyPair;
|
||||||
|
readonly publicKeyLength: number;
|
||||||
|
readonly secretKeyLength: number;
|
||||||
|
readonly sharedKeyLength: number;
|
||||||
|
readonly nonceLength: number;
|
||||||
|
readonly overheadLength: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace signProps {
|
||||||
|
export interface detached {
|
||||||
|
(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||||
|
verify(msg: Uint8Array, sig: Uint8Array, publicKey: Uint8Array): boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface keyPair {
|
||||||
|
(): SignKeyPair;
|
||||||
|
fromSecretKey(secretKey: Uint8Array): SignKeyPair;
|
||||||
|
fromSeed(secretKey: Uint8Array): SignKeyPair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface sign {
|
||||||
|
(msg: Uint8Array, secretKey: Uint8Array): Uint8Array;
|
||||||
|
open(signedMsg: Uint8Array, publicKey: Uint8Array): Uint8Array | null;
|
||||||
|
detached: signProps.detached;
|
||||||
|
keyPair: signProps.keyPair;
|
||||||
|
readonly publicKeyLength: number;
|
||||||
|
readonly secretKeyLength: number;
|
||||||
|
readonly seedLength: number;
|
||||||
|
readonly signatureLength: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface hash {
|
||||||
|
(msg: Uint8Array): Uint8Array;
|
||||||
|
readonly hashLength: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare interface nacl {
|
||||||
|
randomBytes(n: number): Uint8Array;
|
||||||
|
secretbox: nacl.secretbox;
|
||||||
|
scalarMult: nacl.scalarMult;
|
||||||
|
box: nacl.box;
|
||||||
|
sign: nacl.sign;
|
||||||
|
hash: nacl.hash;
|
||||||
|
verify(x: Uint8Array, y: Uint8Array): boolean;
|
||||||
|
setPRNG(fn: (x: Uint8Array, n: number) => void): void;
|
||||||
|
}
|
1178
node_modules/tweetnacl/nacl.js
generated
vendored
Normal file
1178
node_modules/tweetnacl/nacl.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
node_modules/tweetnacl/nacl.min.js
generated
vendored
Normal file
1
node_modules/tweetnacl/nacl.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
56
node_modules/tweetnacl/package.json
generated
vendored
Normal file
56
node_modules/tweetnacl/package.json
generated
vendored
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"name": "tweetnacl",
|
||||||
|
"version": "1.0.3",
|
||||||
|
"description": "Port of TweetNaCl cryptographic library to JavaScript",
|
||||||
|
"main": "nacl-fast.js",
|
||||||
|
"types": "nacl.d.ts",
|
||||||
|
"directories": {
|
||||||
|
"test": "test"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "uglifyjs nacl.js -c -m -o nacl.min.js && uglifyjs nacl-fast.js -c -m -o nacl-fast.min.js",
|
||||||
|
"test-node": "tape test/*.js | faucet",
|
||||||
|
"test-node-all": "make -C test/c && tape test/*.js test/c/*.js | faucet",
|
||||||
|
"build-test-browser": "browserify test/browser/init.js test/*.js | uglifyjs -c -m -o test/browser/_bundle.js 2>/dev/null && browserify test/browser/init.js test/*.quick.js | uglifyjs -c -m -o test/browser/_bundle-quick.js 2>/dev/null",
|
||||||
|
"test": "npm run test-node-all",
|
||||||
|
"bench": "node test/benchmark/bench.js",
|
||||||
|
"lint": "eslint nacl.js nacl-fast.js test/*.js test/benchmark/*.js"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/dchest/tweetnacl-js.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"crypto",
|
||||||
|
"cryptography",
|
||||||
|
"curve25519",
|
||||||
|
"ed25519",
|
||||||
|
"encrypt",
|
||||||
|
"hash",
|
||||||
|
"key",
|
||||||
|
"nacl",
|
||||||
|
"poly1305",
|
||||||
|
"public",
|
||||||
|
"salsa20",
|
||||||
|
"signatures"
|
||||||
|
],
|
||||||
|
"author": "TweetNaCl-js contributors",
|
||||||
|
"license": "Unlicense",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/dchest/tweetnacl-js/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://tweetnacl.js.org",
|
||||||
|
"devDependencies": {
|
||||||
|
"browserify": "^16.2.3",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"faucet": "^0.0.1",
|
||||||
|
"tap-browser-color": "^0.1.2",
|
||||||
|
"tape": "^4.13.0",
|
||||||
|
"tweetnacl-util": "^0.15.0",
|
||||||
|
"uglify-js": "^3.7.5"
|
||||||
|
},
|
||||||
|
"browser": {
|
||||||
|
"buffer": false,
|
||||||
|
"crypto": false
|
||||||
|
}
|
||||||
|
}
|
20
node_modules/ws/LICENSE
generated
vendored
Normal file
20
node_modules/ws/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
||||||
|
Copyright (c) 2013 Arnout Kazemier and contributors
|
||||||
|
Copyright (c) 2016 Luigi Pinca and contributors
|
||||||
|
|
||||||
|
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.
|
536
node_modules/ws/README.md
generated
vendored
Normal file
536
node_modules/ws/README.md
generated
vendored
Normal file
@ -0,0 +1,536 @@
|
|||||||
|
# ws: a Node.js WebSocket library
|
||||||
|
|
||||||
|
[](https://www.npmjs.com/package/ws)
|
||||||
|
[](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
||||||
|
[](https://coveralls.io/github/websockets/ws)
|
||||||
|
|
||||||
|
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
|
||||||
|
server implementation.
|
||||||
|
|
||||||
|
Passes the quite extensive Autobahn test suite: [server][server-report],
|
||||||
|
[client][client-report].
|
||||||
|
|
||||||
|
**Note**: This module does not work in the browser. The client in the docs is a
|
||||||
|
reference to a back end with the role of a client in the WebSocket
|
||||||
|
communication. Browser clients must use the native
|
||||||
|
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
||||||
|
object. To make the same code work seamlessly on Node.js and the browser, you
|
||||||
|
can use one of the many wrappers available on npm, like
|
||||||
|
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
- [Protocol support](#protocol-support)
|
||||||
|
- [Installing](#installing)
|
||||||
|
- [Opt-in for performance](#opt-in-for-performance)
|
||||||
|
- [API docs](#api-docs)
|
||||||
|
- [WebSocket compression](#websocket-compression)
|
||||||
|
- [Usage examples](#usage-examples)
|
||||||
|
- [Sending and receiving text data](#sending-and-receiving-text-data)
|
||||||
|
- [Sending binary data](#sending-binary-data)
|
||||||
|
- [Simple server](#simple-server)
|
||||||
|
- [External HTTP/S server](#external-https-server)
|
||||||
|
- [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
|
||||||
|
- [Client authentication](#client-authentication)
|
||||||
|
- [Server broadcast](#server-broadcast)
|
||||||
|
- [Round-trip time](#round-trip-time)
|
||||||
|
- [Use the Node.js streams API](#use-the-nodejs-streams-api)
|
||||||
|
- [Other examples](#other-examples)
|
||||||
|
- [FAQ](#faq)
|
||||||
|
- [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
|
||||||
|
- [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
|
||||||
|
- [How to connect via a proxy?](#how-to-connect-via-a-proxy)
|
||||||
|
- [Changelog](#changelog)
|
||||||
|
- [License](#license)
|
||||||
|
|
||||||
|
## Protocol support
|
||||||
|
|
||||||
|
- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
|
||||||
|
- **HyBi drafts 13-17** (Current default, alternatively option
|
||||||
|
`protocolVersion: 13`)
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
```
|
||||||
|
npm install ws
|
||||||
|
```
|
||||||
|
|
||||||
|
### Opt-in for performance
|
||||||
|
|
||||||
|
There are 2 optional modules that can be installed along side with the ws
|
||||||
|
module. These modules are binary addons that improve the performance of certain
|
||||||
|
operations. Prebuilt binaries are available for the most popular platforms so
|
||||||
|
you don't necessarily need to have a C++ compiler installed on your machine.
|
||||||
|
|
||||||
|
- `npm install --save-optional bufferutil`: Allows to efficiently perform
|
||||||
|
operations such as masking and unmasking the data payload of the WebSocket
|
||||||
|
frames.
|
||||||
|
- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
|
||||||
|
message contains valid UTF-8.
|
||||||
|
|
||||||
|
To not even try to require and use these modules, use the
|
||||||
|
[`WS_NO_BUFFER_UTIL`](./doc/ws.md#ws_no_buffer_util) and
|
||||||
|
[`WS_NO_UTF_8_VALIDATE`](./doc/ws.md#ws_no_utf_8_validate) environment
|
||||||
|
variables. These might be useful to enhance security in systems where a user can
|
||||||
|
put a package in the package search path of an application of another user, due
|
||||||
|
to how the Node.js resolver algorithm works.
|
||||||
|
|
||||||
|
The `utf-8-validate` module is not needed and is not required, even if it is
|
||||||
|
already installed, regardless of the value of the `WS_NO_UTF_8_VALIDATE`
|
||||||
|
environment variable, if [`buffer.isUtf8()`][] is available.
|
||||||
|
|
||||||
|
## API docs
|
||||||
|
|
||||||
|
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
|
||||||
|
utility functions.
|
||||||
|
|
||||||
|
## WebSocket compression
|
||||||
|
|
||||||
|
ws supports the [permessage-deflate extension][permessage-deflate] which enables
|
||||||
|
the client and server to negotiate a compression algorithm and its parameters,
|
||||||
|
and then selectively apply it to the data payloads of each WebSocket message.
|
||||||
|
|
||||||
|
The extension is disabled by default on the server and enabled by default on the
|
||||||
|
client. It adds a significant overhead in terms of performance and memory
|
||||||
|
consumption so we suggest to enable it only if it is really needed.
|
||||||
|
|
||||||
|
Note that Node.js has a variety of issues with high-performance compression,
|
||||||
|
where increased concurrency, especially on Linux, can lead to [catastrophic
|
||||||
|
memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
|
||||||
|
permessage-deflate in production, it is worthwhile to set up a test
|
||||||
|
representative of your workload and ensure Node.js/zlib will handle it with
|
||||||
|
acceptable performance and memory usage.
|
||||||
|
|
||||||
|
Tuning of permessage-deflate can be done via the options defined below. You can
|
||||||
|
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
|
||||||
|
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
|
||||||
|
|
||||||
|
See [the docs][ws-server-options] for more options.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket, { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({
|
||||||
|
port: 8080,
|
||||||
|
perMessageDeflate: {
|
||||||
|
zlibDeflateOptions: {
|
||||||
|
// See zlib defaults.
|
||||||
|
chunkSize: 1024,
|
||||||
|
memLevel: 7,
|
||||||
|
level: 3
|
||||||
|
},
|
||||||
|
zlibInflateOptions: {
|
||||||
|
chunkSize: 10 * 1024
|
||||||
|
},
|
||||||
|
// Other options settable:
|
||||||
|
clientNoContextTakeover: true, // Defaults to negotiated value.
|
||||||
|
serverNoContextTakeover: true, // Defaults to negotiated value.
|
||||||
|
serverMaxWindowBits: 10, // Defaults to negotiated value.
|
||||||
|
// Below options specified as default values.
|
||||||
|
concurrencyLimit: 10, // Limits zlib concurrency for perf.
|
||||||
|
threshold: 1024 // Size (in bytes) below which messages
|
||||||
|
// should not be compressed if context takeover is disabled.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The client will only use the extension if it is supported and enabled on the
|
||||||
|
server. To always disable the extension on the client set the
|
||||||
|
`perMessageDeflate` option to `false`.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
|
const ws = new WebSocket('ws://www.host.com/path', {
|
||||||
|
perMessageDeflate: false
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage examples
|
||||||
|
|
||||||
|
### Sending and receiving text data
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
|
const ws = new WebSocket('ws://www.host.com/path');
|
||||||
|
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('open', function open() {
|
||||||
|
ws.send('something');
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('message', function message(data) {
|
||||||
|
console.log('received: %s', data);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sending binary data
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
|
const ws = new WebSocket('ws://www.host.com/path');
|
||||||
|
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('open', function open() {
|
||||||
|
const array = new Float32Array(5);
|
||||||
|
|
||||||
|
for (var i = 0; i < array.length; ++i) {
|
||||||
|
array[i] = i / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ws.send(array);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Simple server
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({ port: 8080 });
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws) {
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('message', function message(data) {
|
||||||
|
console.log('received: %s', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.send('something');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### External HTTP/S server
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createServer } from 'https';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
const server = createServer({
|
||||||
|
cert: readFileSync('/path/to/cert.pem'),
|
||||||
|
key: readFileSync('/path/to/key.pem')
|
||||||
|
});
|
||||||
|
const wss = new WebSocketServer({ server });
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws) {
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('message', function message(data) {
|
||||||
|
console.log('received: %s', data);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.send('something');
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(8080);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple servers sharing a single HTTP/S server
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createServer } from 'http';
|
||||||
|
import { parse } from 'url';
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
const server = createServer();
|
||||||
|
const wss1 = new WebSocketServer({ noServer: true });
|
||||||
|
const wss2 = new WebSocketServer({ noServer: true });
|
||||||
|
|
||||||
|
wss1.on('connection', function connection(ws) {
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
wss2.on('connection', function connection(ws) {
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('upgrade', function upgrade(request, socket, head) {
|
||||||
|
const { pathname } = parse(request.url);
|
||||||
|
|
||||||
|
if (pathname === '/foo') {
|
||||||
|
wss1.handleUpgrade(request, socket, head, function done(ws) {
|
||||||
|
wss1.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
} else if (pathname === '/bar') {
|
||||||
|
wss2.handleUpgrade(request, socket, head, function done(ws) {
|
||||||
|
wss2.emit('connection', ws, request);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
socket.destroy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(8080);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client authentication
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { createServer } from 'http';
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
function onSocketError(err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = createServer();
|
||||||
|
const wss = new WebSocketServer({ noServer: true });
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws, request, client) {
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('message', function message(data) {
|
||||||
|
console.log(`Received message ${data} from user ${client}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('upgrade', function upgrade(request, socket, head) {
|
||||||
|
socket.on('error', onSocketError);
|
||||||
|
|
||||||
|
// This function is not defined on purpose. Implement it with your own logic.
|
||||||
|
authenticate(request, function next(err, client) {
|
||||||
|
if (err || !client) {
|
||||||
|
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
||||||
|
socket.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.removeListener('error', onSocketError);
|
||||||
|
|
||||||
|
wss.handleUpgrade(request, socket, head, function done(ws) {
|
||||||
|
wss.emit('connection', ws, request, client);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
server.listen(8080);
|
||||||
|
```
|
||||||
|
|
||||||
|
Also see the provided [example][session-parse-example] using `express-session`.
|
||||||
|
|
||||||
|
### Server broadcast
|
||||||
|
|
||||||
|
A client WebSocket broadcasting to all connected WebSocket clients, including
|
||||||
|
itself.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket, { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({ port: 8080 });
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws) {
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('message', function message(data, isBinary) {
|
||||||
|
wss.clients.forEach(function each(client) {
|
||||||
|
if (client.readyState === WebSocket.OPEN) {
|
||||||
|
client.send(data, { binary: isBinary });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
A client WebSocket broadcasting to every other connected WebSocket clients,
|
||||||
|
excluding itself.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket, { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({ port: 8080 });
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws) {
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('message', function message(data, isBinary) {
|
||||||
|
wss.clients.forEach(function each(client) {
|
||||||
|
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
||||||
|
client.send(data, { binary: isBinary });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Round-trip time
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
|
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||||
|
|
||||||
|
ws.on('error', console.error);
|
||||||
|
|
||||||
|
ws.on('open', function open() {
|
||||||
|
console.log('connected');
|
||||||
|
ws.send(Date.now());
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('close', function close() {
|
||||||
|
console.log('disconnected');
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.on('message', function message(data) {
|
||||||
|
console.log(`Round-trip time: ${Date.now() - data} ms`);
|
||||||
|
|
||||||
|
setTimeout(function timeout() {
|
||||||
|
ws.send(Date.now());
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Use the Node.js streams API
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket, { createWebSocketStream } from 'ws';
|
||||||
|
|
||||||
|
const ws = new WebSocket('wss://websocket-echo.com/');
|
||||||
|
|
||||||
|
const duplex = createWebSocketStream(ws, { encoding: 'utf8' });
|
||||||
|
|
||||||
|
duplex.on('error', console.error);
|
||||||
|
|
||||||
|
duplex.pipe(process.stdout);
|
||||||
|
process.stdin.pipe(duplex);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other examples
|
||||||
|
|
||||||
|
For a full example with a browser client communicating with a ws server, see the
|
||||||
|
examples folder.
|
||||||
|
|
||||||
|
Otherwise, see the test cases.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### How to get the IP address of the client?
|
||||||
|
|
||||||
|
The remote IP address can be obtained from the raw socket.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({ port: 8080 });
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws, req) {
|
||||||
|
const ip = req.socket.remoteAddress;
|
||||||
|
|
||||||
|
ws.on('error', console.error);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
When the server runs behind a proxy like NGINX, the de-facto standard is to use
|
||||||
|
the `X-Forwarded-For` header.
|
||||||
|
|
||||||
|
```js
|
||||||
|
wss.on('connection', function connection(ws, req) {
|
||||||
|
const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
|
||||||
|
|
||||||
|
ws.on('error', console.error);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to detect and close broken connections?
|
||||||
|
|
||||||
|
Sometimes the link between the server and the client can be interrupted in a way
|
||||||
|
that keeps both the server and the client unaware of the broken state of the
|
||||||
|
connection (e.g. when pulling the cord).
|
||||||
|
|
||||||
|
In these cases ping messages can be used as a means to verify that the remote
|
||||||
|
endpoint is still responsive.
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { WebSocketServer } from 'ws';
|
||||||
|
|
||||||
|
function heartbeat() {
|
||||||
|
this.isAlive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wss = new WebSocketServer({ port: 8080 });
|
||||||
|
|
||||||
|
wss.on('connection', function connection(ws) {
|
||||||
|
ws.isAlive = true;
|
||||||
|
ws.on('error', console.error);
|
||||||
|
ws.on('pong', heartbeat);
|
||||||
|
});
|
||||||
|
|
||||||
|
const interval = setInterval(function ping() {
|
||||||
|
wss.clients.forEach(function each(ws) {
|
||||||
|
if (ws.isAlive === false) return ws.terminate();
|
||||||
|
|
||||||
|
ws.isAlive = false;
|
||||||
|
ws.ping();
|
||||||
|
});
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
wss.on('close', function close() {
|
||||||
|
clearInterval(interval);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Pong messages are automatically sent in response to ping messages as required by
|
||||||
|
the spec.
|
||||||
|
|
||||||
|
Just like the server example above your clients might as well lose connection
|
||||||
|
without knowing it. You might want to add a ping listener on your clients to
|
||||||
|
prevent that. A simple implementation would be:
|
||||||
|
|
||||||
|
```js
|
||||||
|
import WebSocket from 'ws';
|
||||||
|
|
||||||
|
function heartbeat() {
|
||||||
|
clearTimeout(this.pingTimeout);
|
||||||
|
|
||||||
|
// Use `WebSocket#terminate()`, which immediately destroys the connection,
|
||||||
|
// instead of `WebSocket#close()`, which waits for the close timer.
|
||||||
|
// Delay should be equal to the interval at which your server
|
||||||
|
// sends out pings plus a conservative assumption of the latency.
|
||||||
|
this.pingTimeout = setTimeout(() => {
|
||||||
|
this.terminate();
|
||||||
|
}, 30000 + 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = new WebSocket('wss://websocket-echo.com/');
|
||||||
|
|
||||||
|
client.on('error', console.error);
|
||||||
|
client.on('open', heartbeat);
|
||||||
|
client.on('ping', heartbeat);
|
||||||
|
client.on('close', function clear() {
|
||||||
|
clearTimeout(this.pingTimeout);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### How to connect via a proxy?
|
||||||
|
|
||||||
|
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
|
||||||
|
[socks-proxy-agent][].
|
||||||
|
|
||||||
|
## Changelog
|
||||||
|
|
||||||
|
We're using the GitHub [releases][changelog] for changelog entries.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](LICENSE)
|
||||||
|
|
||||||
|
[`buffer.isutf8()`]: https://nodejs.org/api/buffer.html#bufferisutf8input
|
||||||
|
[changelog]: https://github.com/websockets/ws/releases
|
||||||
|
[client-report]: http://websockets.github.io/ws/autobahn/clients/
|
||||||
|
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
|
||||||
|
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
|
||||||
|
[node-zlib-deflaterawdocs]:
|
||||||
|
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
|
||||||
|
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
|
||||||
|
[server-report]: http://websockets.github.io/ws/autobahn/servers/
|
||||||
|
[session-parse-example]: ./examples/express-session-parse
|
||||||
|
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
|
||||||
|
[ws-server-options]: ./doc/ws.md#new-websocketserveroptions-callback
|
8
node_modules/ws/browser.js
generated
vendored
Normal file
8
node_modules/ws/browser.js
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = function () {
|
||||||
|
throw new Error(
|
||||||
|
'ws does not work in the browser. Browser clients must use the native ' +
|
||||||
|
'WebSocket object'
|
||||||
|
);
|
||||||
|
};
|
13
node_modules/ws/index.js
generated
vendored
Normal file
13
node_modules/ws/index.js
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
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;
|
131
node_modules/ws/lib/buffer-util.js
generated
vendored
Normal file
131
node_modules/ws/lib/buffer-util.js
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { EMPTY_BUFFER } = require('./constants');
|
||||||
|
|
||||||
|
const FastBuffer = Buffer[Symbol.species];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges an array of buffers into a new buffer.
|
||||||
|
*
|
||||||
|
* @param {Buffer[]} list The array of buffers to concat
|
||||||
|
* @param {Number} totalLength The total length of buffers in the list
|
||||||
|
* @return {Buffer} The resulting buffer
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const buf = list[i];
|
||||||
|
target.set(buf, offset);
|
||||||
|
offset += buf.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offset < totalLength) {
|
||||||
|
return new FastBuffer(target.buffer, target.byteOffset, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Masks a buffer using the given mask.
|
||||||
|
*
|
||||||
|
* @param {Buffer} source The buffer to mask
|
||||||
|
* @param {Buffer} mask The mask to use
|
||||||
|
* @param {Buffer} output The buffer where to store the result
|
||||||
|
* @param {Number} offset The offset at which to start writing
|
||||||
|
* @param {Number} length The number of bytes to mask.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
function _mask(source, mask, output, offset, length) {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
output[offset + i] = source[i] ^ mask[i & 3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unmasks a buffer using the given mask.
|
||||||
|
*
|
||||||
|
* @param {Buffer} buffer The buffer to unmask
|
||||||
|
* @param {Buffer} mask The mask to use
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
function _unmask(buffer, mask) {
|
||||||
|
for (let i = 0; i < buffer.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.length === buf.buffer.byteLength) {
|
||||||
|
return buf.buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 = new FastBuffer(data);
|
||||||
|
} else if (ArrayBuffer.isView(data)) {
|
||||||
|
buf = new FastBuffer(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 {
|
||||||
|
const bufferUtil = require('bufferutil');
|
||||||
|
|
||||||
|
module.exports.mask = function (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) {
|
||||||
|
if (buffer.length < 32) _unmask(buffer, mask);
|
||||||
|
else bufferUtil.unmask(buffer, mask);
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
// Continue regardless of the error.
|
||||||
|
}
|
||||||
|
}
|
12
node_modules/ws/lib/constants.js
generated
vendored
Normal file
12
node_modules/ws/lib/constants.js
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
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'),
|
||||||
|
NOOP: () => {}
|
||||||
|
};
|
292
node_modules/ws/lib/event-target.js
generated
vendored
Normal file
292
node_modules/ws/lib/event-target.js
generated
vendored
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
'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.
|
||||||
|
*/
|
||||||
|
class Event {
|
||||||
|
/**
|
||||||
|
* Create a new `Event`.
|
||||||
|
*
|
||||||
|
* @param {String} type The name of the event
|
||||||
|
* @throws {TypeError} If the `type` argument is not specified
|
||||||
|
*/
|
||||||
|
constructor(type) {
|
||||||
|
this[kTarget] = null;
|
||||||
|
this[kType] = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {*}
|
||||||
|
*/
|
||||||
|
get target() {
|
||||||
|
return this[kTarget];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {String}
|
||||||
|
*/
|
||||||
|
get type() {
|
||||||
|
return this[kType];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(Event.prototype, 'target', { enumerable: true });
|
||||||
|
Object.defineProperty(Event.prototype, 'type', { enumerable: true });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a close event.
|
||||||
|
*
|
||||||
|
* @extends Event
|
||||||
|
*/
|
||||||
|
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}
|
||||||
|
*/
|
||||||
|
get reason() {
|
||||||
|
return this[kReason];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Boolean}
|
||||||
|
*/
|
||||||
|
get wasClean() {
|
||||||
|
return this[kWasClean];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* @extends Event
|
||||||
|
*/
|
||||||
|
class ErrorEvent extends Event {
|
||||||
|
/**
|
||||||
|
* 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.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}
|
||||||
|
*/
|
||||||
|
get message() {
|
||||||
|
return this[kMessage];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(ErrorEvent.prototype, 'error', { enumerable: true });
|
||||||
|
Object.defineProperty(ErrorEvent.prototype, 'message', { enumerable: true });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a message event.
|
||||||
|
*
|
||||||
|
* @extends Event
|
||||||
|
*/
|
||||||
|
class MessageEvent extends Event {
|
||||||
|
/**
|
||||||
|
* Create a new `MessageEvent`.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
constructor(type, options = {}) {
|
||||||
|
super(type);
|
||||||
|
|
||||||
|
this[kData] = options.data === undefined ? null : options.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {*}
|
||||||
|
*/
|
||||||
|
get data() {
|
||||||
|
return this[kData];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.defineProperty(MessageEvent.prototype, 'data', { enumerable: true });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This provides methods for emulating the `EventTarget` interface. It's not
|
||||||
|
* meant to be used directly.
|
||||||
|
*
|
||||||
|
* @mixin
|
||||||
|
*/
|
||||||
|
const EventTarget = {
|
||||||
|
/**
|
||||||
|
* Register an event listener.
|
||||||
|
*
|
||||||
|
* @param {String} type A string representing the event type to listen for
|
||||||
|
* @param {(Function|Object)} handler 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, handler, options = {}) {
|
||||||
|
for (const listener of this.listeners(type)) {
|
||||||
|
if (
|
||||||
|
!options[kForOnEventAttribute] &&
|
||||||
|
listener[kListener] === handler &&
|
||||||
|
!listener[kForOnEventAttribute]
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let wrapper;
|
||||||
|
|
||||||
|
if (type === 'message') {
|
||||||
|
wrapper = function onMessage(data, isBinary) {
|
||||||
|
const event = new MessageEvent('message', {
|
||||||
|
data: isBinary ? data : data.toString()
|
||||||
|
});
|
||||||
|
|
||||||
|
event[kTarget] = this;
|
||||||
|
callListener(handler, 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;
|
||||||
|
callListener(handler, this, event);
|
||||||
|
};
|
||||||
|
} else if (type === 'error') {
|
||||||
|
wrapper = function onError(error) {
|
||||||
|
const event = new ErrorEvent('error', {
|
||||||
|
error,
|
||||||
|
message: error.message
|
||||||
|
});
|
||||||
|
|
||||||
|
event[kTarget] = this;
|
||||||
|
callListener(handler, this, event);
|
||||||
|
};
|
||||||
|
} else if (type === 'open') {
|
||||||
|
wrapper = function onOpen() {
|
||||||
|
const event = new Event('open');
|
||||||
|
|
||||||
|
event[kTarget] = this;
|
||||||
|
callListener(handler, this, event);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper[kForOnEventAttribute] = !!options[kForOnEventAttribute];
|
||||||
|
wrapper[kListener] = handler;
|
||||||
|
|
||||||
|
if (options.once) {
|
||||||
|
this.once(type, wrapper);
|
||||||
|
} else {
|
||||||
|
this.on(type, wrapper);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an event listener.
|
||||||
|
*
|
||||||
|
* @param {String} type A string representing the event type to remove
|
||||||
|
* @param {(Function|Object)} handler 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
CloseEvent,
|
||||||
|
ErrorEvent,
|
||||||
|
Event,
|
||||||
|
EventTarget,
|
||||||
|
MessageEvent
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call an event listener
|
||||||
|
*
|
||||||
|
* @param {(Function|Object)} listener The listener to call
|
||||||
|
* @param {*} thisArg The value to use as `this`` when calling the listener
|
||||||
|
* @param {Event} event The event to pass to the listener
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function callListener(listener, thisArg, event) {
|
||||||
|
if (typeof listener === 'object' && listener.handleEvent) {
|
||||||
|
listener.handleEvent.call(listener, event);
|
||||||
|
} else {
|
||||||
|
listener.call(thisArg, event);
|
||||||
|
}
|
||||||
|
}
|
203
node_modules/ws/lib/extension.js
generated
vendored
Normal file
203
node_modules/ws/lib/extension.js
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { tokenChars } = require('./validation');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an offer to the map of extension offers or a parameter to the map of
|
||||||
|
* parameters.
|
||||||
|
*
|
||||||
|
* @param {Object} dest The map of extension offers or parameters
|
||||||
|
* @param {String} name The extension or parameter name
|
||||||
|
* @param {(Object|Boolean|String)} elem The extension parameters or the
|
||||||
|
* parameter value
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function push(dest, name, elem) {
|
||||||
|
if (dest[name] === undefined) dest[name] = [elem];
|
||||||
|
else dest[name].push(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the `Sec-WebSocket-Extensions` header into an object.
|
||||||
|
*
|
||||||
|
* @param {String} header The field value of the header
|
||||||
|
* @return {Object} The parsed object
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
if (extensionName === undefined) {
|
||||||
|
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 === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
|
||||||
|
if (start === -1) {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end === -1) end = i;
|
||||||
|
const name = header.slice(start, end);
|
||||||
|
if (code === 0x2c) {
|
||||||
|
push(offers, name, params);
|
||||||
|
params = Object.create(null);
|
||||||
|
} else {
|
||||||
|
extensionName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = end = -1;
|
||||||
|
} else {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
} else if (paramName === undefined) {
|
||||||
|
if (end === -1 && tokenChars[code] === 1) {
|
||||||
|
if (start === -1) start = i;
|
||||||
|
} else if (code === 0x20 || code === 0x09) {
|
||||||
|
if (end === -1 && start !== -1) end = i;
|
||||||
|
} else if (code === 0x3b || code === 0x2c) {
|
||||||
|
if (start === -1) {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end === -1) end = i;
|
||||||
|
push(params, header.slice(start, end), true);
|
||||||
|
if (code === 0x2c) {
|
||||||
|
push(offers, extensionName, params);
|
||||||
|
params = Object.create(null);
|
||||||
|
extensionName = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = end = -1;
|
||||||
|
} else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {
|
||||||
|
paramName = header.slice(start, i);
|
||||||
|
start = end = -1;
|
||||||
|
} else {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//
|
||||||
|
// The value of a quoted-string after unescaping must conform to the
|
||||||
|
// token ABNF, so only token characters are valid.
|
||||||
|
// Ref: https://tools.ietf.org/html/rfc6455#section-9.1
|
||||||
|
//
|
||||||
|
if (isEscaping) {
|
||||||
|
if (tokenChars[code] !== 1) {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
if (start === -1) start = i;
|
||||||
|
else if (!mustUnescape) mustUnescape = true;
|
||||||
|
isEscaping = false;
|
||||||
|
} else if (inQuotes) {
|
||||||
|
if (tokenChars[code] === 1) {
|
||||||
|
if (start === -1) start = i;
|
||||||
|
} else if (code === 0x22 /* '"' */ && start !== -1) {
|
||||||
|
inQuotes = false;
|
||||||
|
end = i;
|
||||||
|
} else if (code === 0x5c /* '\' */) {
|
||||||
|
isEscaping = true;
|
||||||
|
} else {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
} else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
|
||||||
|
inQuotes = true;
|
||||||
|
} else if (end === -1 && tokenChars[code] === 1) {
|
||||||
|
if (start === -1) start = i;
|
||||||
|
} else if (start !== -1 && (code === 0x20 || code === 0x09)) {
|
||||||
|
if (end === -1) end = i;
|
||||||
|
} else if (code === 0x3b || code === 0x2c) {
|
||||||
|
if (start === -1) {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end === -1) end = i;
|
||||||
|
let value = header.slice(start, end);
|
||||||
|
if (mustUnescape) {
|
||||||
|
value = value.replace(/\\/g, '');
|
||||||
|
mustUnescape = false;
|
||||||
|
}
|
||||||
|
push(params, paramName, value);
|
||||||
|
if (code === 0x2c) {
|
||||||
|
push(offers, extensionName, params);
|
||||||
|
params = Object.create(null);
|
||||||
|
extensionName = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
paramName = undefined;
|
||||||
|
start = end = -1;
|
||||||
|
} else {
|
||||||
|
throw new SyntaxError(`Unexpected character at index ${i}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (start === -1 || inQuotes || code === 0x20 || code === 0x09) {
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
if (paramName === undefined) {
|
||||||
|
push(params, token, true);
|
||||||
|
} else if (mustUnescape) {
|
||||||
|
push(params, paramName, token.replace(/\\/g, ''));
|
||||||
|
} else {
|
||||||
|
push(params, paramName, token);
|
||||||
|
}
|
||||||
|
push(offers, extensionName, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return offers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the `Sec-WebSocket-Extensions` header field value.
|
||||||
|
*
|
||||||
|
* @param {Object} extensions The map of extensions and parameters to format
|
||||||
|
* @return {String} A string representing the given object
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
function format(extensions) {
|
||||||
|
return Object.keys(extensions)
|
||||||
|
.map((extension) => {
|
||||||
|
let 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];
|
||||||
|
if (!Array.isArray(values)) values = [values];
|
||||||
|
return values
|
||||||
|
.map((v) => (v === true ? k : `${k}=${v}`))
|
||||||
|
.join('; ');
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.join('; ');
|
||||||
|
})
|
||||||
|
.join(', ');
|
||||||
|
})
|
||||||
|
.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { format, parse };
|
55
node_modules/ws/lib/limiter.js
generated
vendored
Normal file
55
node_modules/ws/lib/limiter.js
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
'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;
|
514
node_modules/ws/lib/permessage-deflate.js
generated
vendored
Normal file
514
node_modules/ws/lib/permessage-deflate.js
generated
vendored
Normal file
@ -0,0 +1,514 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const zlib = require('zlib');
|
||||||
|
|
||||||
|
const bufferUtil = require('./buffer-util');
|
||||||
|
const Limiter = require('./limiter');
|
||||||
|
const { kStatusCode } = require('./constants');
|
||||||
|
|
||||||
|
const FastBuffer = Buffer[Symbol.species];
|
||||||
|
const TRAILER = Buffer.from([0x00, 0x00, 0xff, 0xff]);
|
||||||
|
const kPerMessageDeflate = Symbol('permessage-deflate');
|
||||||
|
const kTotalLength = Symbol('total-length');
|
||||||
|
const kCallback = Symbol('callback');
|
||||||
|
const kBuffers = Symbol('buffers');
|
||||||
|
const kError = Symbol('error');
|
||||||
|
|
||||||
|
//
|
||||||
|
// We limit zlib concurrency, which prevents severe memory fragmentation
|
||||||
|
// as documented in https://github.com/nodejs/node/issues/8871#issuecomment-250915913
|
||||||
|
// and https://github.com/websockets/ws/issues/1202
|
||||||
|
//
|
||||||
|
// Intentionally global; it's the global thread pool that's an issue.
|
||||||
|
//
|
||||||
|
let zlibLimiter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* permessage-deflate implementation.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
constructor(options, isServer, maxPayload) {
|
||||||
|
this._maxPayload = maxPayload | 0;
|
||||||
|
this._options = options || {};
|
||||||
|
this._threshold =
|
||||||
|
this._options.threshold !== undefined ? this._options.threshold : 1024;
|
||||||
|
this._isServer = !!isServer;
|
||||||
|
this._deflate = null;
|
||||||
|
this._inflate = null;
|
||||||
|
|
||||||
|
this.params = null;
|
||||||
|
|
||||||
|
if (!zlibLimiter) {
|
||||||
|
const concurrency =
|
||||||
|
this._options.concurrencyLimit !== undefined
|
||||||
|
? this._options.concurrencyLimit
|
||||||
|
: 10;
|
||||||
|
zlibLimiter = new Limiter(concurrency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {String}
|
||||||
|
*/
|
||||||
|
static get extensionName() {
|
||||||
|
return 'permessage-deflate';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an extension negotiation offer.
|
||||||
|
*
|
||||||
|
* @return {Object} Extension parameters
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
offer() {
|
||||||
|
const params = {};
|
||||||
|
|
||||||
|
if (this._options.serverNoContextTakeover) {
|
||||||
|
params.server_no_context_takeover = true;
|
||||||
|
}
|
||||||
|
if (this._options.clientNoContextTakeover) {
|
||||||
|
params.client_no_context_takeover = true;
|
||||||
|
}
|
||||||
|
if (this._options.serverMaxWindowBits) {
|
||||||
|
params.server_max_window_bits = this._options.serverMaxWindowBits;
|
||||||
|
}
|
||||||
|
if (this._options.clientMaxWindowBits) {
|
||||||
|
params.client_max_window_bits = this._options.clientMaxWindowBits;
|
||||||
|
} else if (this._options.clientMaxWindowBits == null) {
|
||||||
|
params.client_max_window_bits = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept an extension negotiation offer/response.
|
||||||
|
*
|
||||||
|
* @param {Array} configurations The extension negotiation offers/reponse
|
||||||
|
* @return {Object} Accepted configuration
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
accept(configurations) {
|
||||||
|
configurations = this.normalizeParams(configurations);
|
||||||
|
|
||||||
|
this.params = this._isServer
|
||||||
|
? this.acceptAsServer(configurations)
|
||||||
|
: this.acceptAsClient(configurations);
|
||||||
|
|
||||||
|
return this.params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases all resources used by the extension.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
cleanup() {
|
||||||
|
if (this._inflate) {
|
||||||
|
this._inflate.close();
|
||||||
|
this._inflate = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept an extension negotiation offer.
|
||||||
|
*
|
||||||
|
* @param {Array} offers The extension negotiation offers
|
||||||
|
* @return {Object} Accepted configuration
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
acceptAsServer(offers) {
|
||||||
|
const opts = this._options;
|
||||||
|
const accepted = offers.find((params) => {
|
||||||
|
if (
|
||||||
|
(opts.serverNoContextTakeover === false &&
|
||||||
|
params.server_no_context_takeover) ||
|
||||||
|
(params.server_max_window_bits &&
|
||||||
|
(opts.serverMaxWindowBits === false ||
|
||||||
|
(typeof opts.serverMaxWindowBits === 'number' &&
|
||||||
|
opts.serverMaxWindowBits > params.server_max_window_bits))) ||
|
||||||
|
(typeof opts.clientMaxWindowBits === 'number' &&
|
||||||
|
!params.client_max_window_bits)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!accepted) {
|
||||||
|
throw new Error('None of the extension offers can be accepted');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts.serverNoContextTakeover) {
|
||||||
|
accepted.server_no_context_takeover = true;
|
||||||
|
}
|
||||||
|
if (opts.clientNoContextTakeover) {
|
||||||
|
accepted.client_no_context_takeover = true;
|
||||||
|
}
|
||||||
|
if (typeof opts.serverMaxWindowBits === 'number') {
|
||||||
|
accepted.server_max_window_bits = opts.serverMaxWindowBits;
|
||||||
|
}
|
||||||
|
if (typeof opts.clientMaxWindowBits === 'number') {
|
||||||
|
accepted.client_max_window_bits = opts.clientMaxWindowBits;
|
||||||
|
} else if (
|
||||||
|
accepted.client_max_window_bits === true ||
|
||||||
|
opts.clientMaxWindowBits === false
|
||||||
|
) {
|
||||||
|
delete accepted.client_max_window_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
return accepted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accept the extension negotiation response.
|
||||||
|
*
|
||||||
|
* @param {Array} response The extension negotiation response
|
||||||
|
* @return {Object} Accepted configuration
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
acceptAsClient(response) {
|
||||||
|
const params = response[0];
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._options.clientNoContextTakeover === false &&
|
||||||
|
params.client_no_context_takeover
|
||||||
|
) {
|
||||||
|
throw new Error('Unexpected parameter "client_no_context_takeover"');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.client_max_window_bits) {
|
||||||
|
if (typeof this._options.clientMaxWindowBits === 'number') {
|
||||||
|
params.client_max_window_bits = this._options.clientMaxWindowBits;
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
this._options.clientMaxWindowBits === false ||
|
||||||
|
(typeof this._options.clientMaxWindowBits === 'number' &&
|
||||||
|
params.client_max_window_bits > this._options.clientMaxWindowBits)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
'Unexpected or invalid parameter "client_max_window_bits"'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize parameters.
|
||||||
|
*
|
||||||
|
* @param {Array} configurations The extension negotiation offers/reponse
|
||||||
|
* @return {Array} The offers/response with normalized parameters
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
normalizeParams(configurations) {
|
||||||
|
configurations.forEach((params) => {
|
||||||
|
Object.keys(params).forEach((key) => {
|
||||||
|
let value = params[key];
|
||||||
|
|
||||||
|
if (value.length > 1) {
|
||||||
|
throw new Error(`Parameter "${key}" must have only a single value`);
|
||||||
|
}
|
||||||
|
|
||||||
|
value = value[0];
|
||||||
|
|
||||||
|
if (key === 'client_max_window_bits') {
|
||||||
|
if (value !== true) {
|
||||||
|
const num = +value;
|
||||||
|
if (!Number.isInteger(num) || num < 8 || num > 15) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Invalid value for parameter "${key}": ${value}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value = num;
|
||||||
|
} else if (!this._isServer) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Invalid value for parameter "${key}": ${value}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (key === 'server_max_window_bits') {
|
||||||
|
const num = +value;
|
||||||
|
if (!Number.isInteger(num) || num < 8 || num > 15) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Invalid value for parameter "${key}": ${value}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
value = num;
|
||||||
|
} else if (
|
||||||
|
key === 'client_no_context_takeover' ||
|
||||||
|
key === 'server_no_context_takeover'
|
||||||
|
) {
|
||||||
|
if (value !== true) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Invalid value for parameter "${key}": ${value}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unknown parameter "${key}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
params[key] = value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return configurations;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompress data. Concurrency limited.
|
||||||
|
*
|
||||||
|
* @param {Buffer} data Compressed data
|
||||||
|
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||||
|
* @param {Function} callback Callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
decompress(data, fin, callback) {
|
||||||
|
zlibLimiter.add((done) => {
|
||||||
|
this._decompress(data, fin, (err, result) => {
|
||||||
|
done();
|
||||||
|
callback(err, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress data. Concurrency limited.
|
||||||
|
*
|
||||||
|
* @param {(Buffer|String)} 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) => {
|
||||||
|
this._compress(data, fin, (err, result) => {
|
||||||
|
done();
|
||||||
|
callback(err, result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompress data.
|
||||||
|
*
|
||||||
|
* @param {Buffer} data Compressed data
|
||||||
|
* @param {Boolean} fin Specifies whether or not this is the last fragment
|
||||||
|
* @param {Function} callback Callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_decompress(data, fin, callback) {
|
||||||
|
const endpoint = this._isServer ? 'client' : 'server';
|
||||||
|
|
||||||
|
if (!this._inflate) {
|
||||||
|
const key = `${endpoint}_max_window_bits`;
|
||||||
|
const windowBits =
|
||||||
|
typeof this.params[key] !== 'number'
|
||||||
|
? zlib.Z_DEFAULT_WINDOWBITS
|
||||||
|
: this.params[key];
|
||||||
|
|
||||||
|
this._inflate = zlib.createInflateRaw({
|
||||||
|
...this._options.zlibInflateOptions,
|
||||||
|
windowBits
|
||||||
|
});
|
||||||
|
this._inflate[kPerMessageDeflate] = this;
|
||||||
|
this._inflate[kTotalLength] = 0;
|
||||||
|
this._inflate[kBuffers] = [];
|
||||||
|
this._inflate.on('error', inflateOnError);
|
||||||
|
this._inflate.on('data', inflateOnData);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._inflate[kCallback] = callback;
|
||||||
|
|
||||||
|
this._inflate.write(data);
|
||||||
|
if (fin) this._inflate.write(TRAILER);
|
||||||
|
|
||||||
|
this._inflate.flush(() => {
|
||||||
|
const err = this._inflate[kError];
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
this._inflate.close();
|
||||||
|
this._inflate = null;
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = bufferUtil.concat(
|
||||||
|
this._inflate[kBuffers],
|
||||||
|
this._inflate[kTotalLength]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (this._inflate._readableState.endEmitted) {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compress data.
|
||||||
|
*
|
||||||
|
* @param {(Buffer|String)} 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) {
|
||||||
|
const endpoint = this._isServer ? 'server' : 'client';
|
||||||
|
|
||||||
|
if (!this._deflate) {
|
||||||
|
const key = `${endpoint}_max_window_bits`;
|
||||||
|
const windowBits =
|
||||||
|
typeof this.params[key] !== 'number'
|
||||||
|
? zlib.Z_DEFAULT_WINDOWBITS
|
||||||
|
: this.params[key];
|
||||||
|
|
||||||
|
this._deflate = zlib.createDeflateRaw({
|
||||||
|
...this._options.zlibDeflateOptions,
|
||||||
|
windowBits
|
||||||
|
});
|
||||||
|
|
||||||
|
this._deflate[kTotalLength] = 0;
|
||||||
|
this._deflate[kBuffers] = [];
|
||||||
|
|
||||||
|
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.
|
||||||
|
//
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = bufferUtil.concat(
|
||||||
|
this._deflate[kBuffers],
|
||||||
|
this._deflate[kTotalLength]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (fin) {
|
||||||
|
data = new FastBuffer(data.buffer, data.byteOffset, data.length - 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ensure that the callback will not be called again in
|
||||||
|
// `PerMessageDeflate#cleanup()`.
|
||||||
|
//
|
||||||
|
this._deflate[kCallback] = null;
|
||||||
|
|
||||||
|
this._deflate[kTotalLength] = 0;
|
||||||
|
this._deflate[kBuffers] = [];
|
||||||
|
|
||||||
|
if (fin && this.params[`${endpoint}_no_context_takeover`]) {
|
||||||
|
this._deflate.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = PerMessageDeflate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener of the `zlib.DeflateRaw` stream `'data'` event.
|
||||||
|
*
|
||||||
|
* @param {Buffer} chunk A chunk of data
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function deflateOnData(chunk) {
|
||||||
|
this[kBuffers].push(chunk);
|
||||||
|
this[kTotalLength] += chunk.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener of the `zlib.InflateRaw` stream `'data'` event.
|
||||||
|
*
|
||||||
|
* @param {Buffer} chunk A chunk of data
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function inflateOnData(chunk) {
|
||||||
|
this[kTotalLength] += chunk.length;
|
||||||
|
|
||||||
|
if (
|
||||||
|
this[kPerMessageDeflate]._maxPayload < 1 ||
|
||||||
|
this[kTotalLength] <= this[kPerMessageDeflate]._maxPayload
|
||||||
|
) {
|
||||||
|
this[kBuffers].push(chunk);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The listener of the `zlib.InflateRaw` stream `'error'` event.
|
||||||
|
*
|
||||||
|
* @param {Error} err The emitted error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function inflateOnError(err) {
|
||||||
|
//
|
||||||
|
// There is no need to call `Zlib#close()` as the handle is automatically
|
||||||
|
// closed when an error is emitted.
|
||||||
|
//
|
||||||
|
this[kPerMessageDeflate]._inflate = null;
|
||||||
|
err[kStatusCode] = 1007;
|
||||||
|
this[kCallback](err);
|
||||||
|
}
|
679
node_modules/ws/lib/receiver.js
generated
vendored
Normal file
679
node_modules/ws/lib/receiver.js
generated
vendored
Normal file
@ -0,0 +1,679 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Writable } = require('stream');
|
||||||
|
|
||||||
|
const PerMessageDeflate = require('./permessage-deflate');
|
||||||
|
const {
|
||||||
|
BINARY_TYPES,
|
||||||
|
EMPTY_BUFFER,
|
||||||
|
kStatusCode,
|
||||||
|
kWebSocket
|
||||||
|
} = require('./constants');
|
||||||
|
const { concat, toArrayBuffer, unmask } = require('./buffer-util');
|
||||||
|
const { isValidStatusCode, isValidUTF8 } = require('./validation');
|
||||||
|
|
||||||
|
const FastBuffer = Buffer[Symbol.species];
|
||||||
|
const promise = Promise.resolve();
|
||||||
|
|
||||||
|
//
|
||||||
|
// `queueMicrotask()` is not available in Node.js < 11.
|
||||||
|
//
|
||||||
|
const queueTask =
|
||||||
|
typeof queueMicrotask === 'function' ? queueMicrotask : queueMicrotaskShim;
|
||||||
|
|
||||||
|
const GET_INFO = 0;
|
||||||
|
const GET_PAYLOAD_LENGTH_16 = 1;
|
||||||
|
const GET_PAYLOAD_LENGTH_64 = 2;
|
||||||
|
const GET_MASK = 3;
|
||||||
|
const GET_DATA = 4;
|
||||||
|
const INFLATING = 5;
|
||||||
|
const WAIT_MICROTASK = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HyBi Receiver implementation.
|
||||||
|
*
|
||||||
|
* @extends Writable
|
||||||
|
*/
|
||||||
|
class Receiver extends Writable {
|
||||||
|
/**
|
||||||
|
* Creates a Receiver instance.
|
||||||
|
*
|
||||||
|
* @param {Object} [options] Options object
|
||||||
|
* @param {String} [options.binaryType=nodebuffer] The type for binary data
|
||||||
|
* @param {Object} [options.extensions] An object containing the negotiated
|
||||||
|
* extensions
|
||||||
|
* @param {Boolean} [options.isServer=false] Specifies whether to operate in
|
||||||
|
* client or server mode
|
||||||
|
* @param {Number} [options.maxPayload=0] The maximum allowed message length
|
||||||
|
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||||
|
* not to skip UTF-8 validation for text and close messages
|
||||||
|
*/
|
||||||
|
constructor(options = {}) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this._binaryType = options.binaryType || BINARY_TYPES[0];
|
||||||
|
this._extensions = options.extensions || {};
|
||||||
|
this._isServer = !!options.isServer;
|
||||||
|
this._maxPayload = options.maxPayload | 0;
|
||||||
|
this._skipUTF8Validation = !!options.skipUTF8Validation;
|
||||||
|
this[kWebSocket] = undefined;
|
||||||
|
|
||||||
|
this._bufferedBytes = 0;
|
||||||
|
this._buffers = [];
|
||||||
|
|
||||||
|
this._compressed = false;
|
||||||
|
this._payloadLength = 0;
|
||||||
|
this._mask = undefined;
|
||||||
|
this._fragmented = 0;
|
||||||
|
this._masked = false;
|
||||||
|
this._fin = false;
|
||||||
|
this._opcode = 0;
|
||||||
|
|
||||||
|
this._totalPayloadLength = 0;
|
||||||
|
this._messageLength = 0;
|
||||||
|
this._fragments = [];
|
||||||
|
|
||||||
|
this._state = GET_INFO;
|
||||||
|
this._loop = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements `Writable.prototype._write()`.
|
||||||
|
*
|
||||||
|
* @param {Buffer} chunk The chunk of data to write
|
||||||
|
* @param {String} encoding The character encoding of `chunk`
|
||||||
|
* @param {Function} cb Callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
_write(chunk, encoding, cb) {
|
||||||
|
if (this._opcode === 0x08 && this._state == GET_INFO) return cb();
|
||||||
|
|
||||||
|
this._bufferedBytes += chunk.length;
|
||||||
|
this._buffers.push(chunk);
|
||||||
|
this.startLoop(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes `n` bytes from the buffered data.
|
||||||
|
*
|
||||||
|
* @param {Number} n The number of bytes to consume
|
||||||
|
* @return {Buffer} The consumed bytes
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
consume(n) {
|
||||||
|
this._bufferedBytes -= n;
|
||||||
|
|
||||||
|
if (n === this._buffers[0].length) return this._buffers.shift();
|
||||||
|
|
||||||
|
if (n < this._buffers[0].length) {
|
||||||
|
const buf = this._buffers[0];
|
||||||
|
this._buffers[0] = new FastBuffer(
|
||||||
|
buf.buffer,
|
||||||
|
buf.byteOffset + n,
|
||||||
|
buf.length - n
|
||||||
|
);
|
||||||
|
|
||||||
|
return new FastBuffer(buf.buffer, buf.byteOffset, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dst = Buffer.allocUnsafe(n);
|
||||||
|
|
||||||
|
do {
|
||||||
|
const buf = this._buffers[0];
|
||||||
|
const offset = dst.length - n;
|
||||||
|
|
||||||
|
if (n >= buf.length) {
|
||||||
|
dst.set(this._buffers.shift(), offset);
|
||||||
|
} else {
|
||||||
|
dst.set(new Uint8Array(buf.buffer, buf.byteOffset, n), offset);
|
||||||
|
this._buffers[0] = new FastBuffer(
|
||||||
|
buf.buffer,
|
||||||
|
buf.byteOffset + n,
|
||||||
|
buf.length - n
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
n -= buf.length;
|
||||||
|
} while (n > 0);
|
||||||
|
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the parsing loop.
|
||||||
|
*
|
||||||
|
* @param {Function} cb Callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
startLoop(cb) {
|
||||||
|
let err;
|
||||||
|
this._loop = true;
|
||||||
|
|
||||||
|
do {
|
||||||
|
switch (this._state) {
|
||||||
|
case GET_INFO:
|
||||||
|
err = this.getInfo();
|
||||||
|
break;
|
||||||
|
case GET_PAYLOAD_LENGTH_16:
|
||||||
|
err = this.getPayloadLength16();
|
||||||
|
break;
|
||||||
|
case GET_PAYLOAD_LENGTH_64:
|
||||||
|
err = this.getPayloadLength64();
|
||||||
|
break;
|
||||||
|
case GET_MASK:
|
||||||
|
this.getMask();
|
||||||
|
break;
|
||||||
|
case GET_DATA:
|
||||||
|
err = this.getData(cb);
|
||||||
|
break;
|
||||||
|
case INFLATING:
|
||||||
|
this._loop = false;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
//
|
||||||
|
// `WAIT_MICROTASK`.
|
||||||
|
//
|
||||||
|
this._loop = false;
|
||||||
|
|
||||||
|
queueTask(() => {
|
||||||
|
this._state = GET_INFO;
|
||||||
|
this.startLoop(cb);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} while (this._loop);
|
||||||
|
|
||||||
|
cb(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the first two bytes of a frame.
|
||||||
|
*
|
||||||
|
* @return {(RangeError|undefined)} A possible error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
getInfo() {
|
||||||
|
if (this._bufferedBytes < 2) {
|
||||||
|
this._loop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = this.consume(2);
|
||||||
|
|
||||||
|
if ((buf[0] & 0x30) !== 0x00) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'RSV2 and RSV3 must be clear',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_UNEXPECTED_RSV_2_3'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const compressed = (buf[0] & 0x40) === 0x40;
|
||||||
|
|
||||||
|
if (compressed && !this._extensions[PerMessageDeflate.extensionName]) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'RSV1 must be clear',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_UNEXPECTED_RSV_1'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fin = (buf[0] & 0x80) === 0x80;
|
||||||
|
this._opcode = buf[0] & 0x0f;
|
||||||
|
this._payloadLength = buf[1] & 0x7f;
|
||||||
|
|
||||||
|
if (this._opcode === 0x00) {
|
||||||
|
if (compressed) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'RSV1 must be clear',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_UNEXPECTED_RSV_1'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._fragmented) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'invalid opcode 0',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_INVALID_OPCODE'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._opcode = this._fragmented;
|
||||||
|
} else if (this._opcode === 0x01 || this._opcode === 0x02) {
|
||||||
|
if (this._fragmented) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
`invalid opcode ${this._opcode}`,
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_INVALID_OPCODE'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._compressed = compressed;
|
||||||
|
} else if (this._opcode > 0x07 && this._opcode < 0x0b) {
|
||||||
|
if (!this._fin) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'FIN must be set',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_EXPECTED_FIN'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compressed) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'RSV1 must be clear',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_UNEXPECTED_RSV_1'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._payloadLength > 0x7d ||
|
||||||
|
(this._opcode === 0x08 && this._payloadLength === 1)
|
||||||
|
) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
`invalid payload length ${this._payloadLength}`,
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
`invalid opcode ${this._opcode}`,
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_INVALID_OPCODE'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._fin && !this._fragmented) this._fragmented = this._opcode;
|
||||||
|
this._masked = (buf[1] & 0x80) === 0x80;
|
||||||
|
|
||||||
|
if (this._isServer) {
|
||||||
|
if (!this._masked) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'MASK must be set',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_EXPECTED_MASK'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (this._masked) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'MASK must be clear',
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_UNEXPECTED_MASK'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._payloadLength === 126) this._state = GET_PAYLOAD_LENGTH_16;
|
||||||
|
else if (this._payloadLength === 127) this._state = GET_PAYLOAD_LENGTH_64;
|
||||||
|
else return this.haveLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets extended payload length (7+16).
|
||||||
|
*
|
||||||
|
* @return {(RangeError|undefined)} A possible error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
getPayloadLength16() {
|
||||||
|
if (this._bufferedBytes < 2) {
|
||||||
|
this._loop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._payloadLength = this.consume(2).readUInt16BE(0);
|
||||||
|
return this.haveLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets extended payload length (7+64).
|
||||||
|
*
|
||||||
|
* @return {(RangeError|undefined)} A possible error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
getPayloadLength64() {
|
||||||
|
if (this._bufferedBytes < 8) {
|
||||||
|
this._loop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = this.consume(8);
|
||||||
|
const num = buf.readUInt32BE(0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// The maximum safe integer in JavaScript is 2^53 - 1. An error is returned
|
||||||
|
// if payload length is greater than this number.
|
||||||
|
//
|
||||||
|
if (num > Math.pow(2, 53 - 32) - 1) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'Unsupported WebSocket frame: payload length > 2^53 - 1',
|
||||||
|
false,
|
||||||
|
1009,
|
||||||
|
'WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._payloadLength = num * Math.pow(2, 32) + buf.readUInt32BE(4);
|
||||||
|
return this.haveLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Payload length has been read.
|
||||||
|
*
|
||||||
|
* @return {(RangeError|undefined)} A possible error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
haveLength() {
|
||||||
|
if (this._payloadLength && this._opcode < 0x08) {
|
||||||
|
this._totalPayloadLength += this._payloadLength;
|
||||||
|
if (this._totalPayloadLength > this._maxPayload && this._maxPayload > 0) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
'Max payload size exceeded',
|
||||||
|
false,
|
||||||
|
1009,
|
||||||
|
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._masked) this._state = GET_MASK;
|
||||||
|
else this._state = GET_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads mask bytes.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
getMask() {
|
||||||
|
if (this._bufferedBytes < 4) {
|
||||||
|
this._loop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._mask = this.consume(4);
|
||||||
|
this._state = GET_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads data bytes.
|
||||||
|
*
|
||||||
|
* @param {Function} cb Callback
|
||||||
|
* @return {(Error|RangeError|undefined)} A possible error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
getData(cb) {
|
||||||
|
let data = EMPTY_BUFFER;
|
||||||
|
|
||||||
|
if (this._payloadLength) {
|
||||||
|
if (this._bufferedBytes < this._payloadLength) {
|
||||||
|
this._loop = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = this.consume(this._payloadLength);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this._masked &&
|
||||||
|
(this._mask[0] | this._mask[1] | this._mask[2] | this._mask[3]) !== 0
|
||||||
|
) {
|
||||||
|
unmask(data, this._mask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._opcode > 0x07) return this.controlMessage(data);
|
||||||
|
|
||||||
|
if (this._compressed) {
|
||||||
|
this._state = INFLATING;
|
||||||
|
this.decompress(data, cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.length) {
|
||||||
|
//
|
||||||
|
// This message is not compressed so its length is the sum of the payload
|
||||||
|
// length of all fragments.
|
||||||
|
//
|
||||||
|
this._messageLength = this._totalPayloadLength;
|
||||||
|
this._fragments.push(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dataMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompresses data.
|
||||||
|
*
|
||||||
|
* @param {Buffer} data Compressed data
|
||||||
|
* @param {Function} cb Callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
decompress(data, cb) {
|
||||||
|
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||||
|
|
||||||
|
perMessageDeflate.decompress(data, this._fin, (err, buf) => {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
if (buf.length) {
|
||||||
|
this._messageLength += buf.length;
|
||||||
|
if (this._messageLength > this._maxPayload && this._maxPayload > 0) {
|
||||||
|
return cb(
|
||||||
|
error(
|
||||||
|
RangeError,
|
||||||
|
'Max payload size exceeded',
|
||||||
|
false,
|
||||||
|
1009,
|
||||||
|
'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._fragments.push(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
const er = this.dataMessage();
|
||||||
|
if (er) return cb(er);
|
||||||
|
|
||||||
|
this.startLoop(cb);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a data message.
|
||||||
|
*
|
||||||
|
* @return {(Error|undefined)} A possible error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
dataMessage() {
|
||||||
|
if (this._fin) {
|
||||||
|
const messageLength = this._messageLength;
|
||||||
|
const fragments = this._fragments;
|
||||||
|
|
||||||
|
this._totalPayloadLength = 0;
|
||||||
|
this._messageLength = 0;
|
||||||
|
this._fragmented = 0;
|
||||||
|
this._fragments = [];
|
||||||
|
|
||||||
|
if (this._opcode === 2) {
|
||||||
|
let data;
|
||||||
|
|
||||||
|
if (this._binaryType === 'nodebuffer') {
|
||||||
|
data = concat(fragments, messageLength);
|
||||||
|
} else if (this._binaryType === 'arraybuffer') {
|
||||||
|
data = toArrayBuffer(concat(fragments, messageLength));
|
||||||
|
} else {
|
||||||
|
data = fragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('message', data, true);
|
||||||
|
} else {
|
||||||
|
const buf = concat(fragments, messageLength);
|
||||||
|
|
||||||
|
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||||
|
this._loop = false;
|
||||||
|
return error(
|
||||||
|
Error,
|
||||||
|
'invalid UTF-8 sequence',
|
||||||
|
true,
|
||||||
|
1007,
|
||||||
|
'WS_ERR_INVALID_UTF8'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('message', buf, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._state = WAIT_MICROTASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a control message.
|
||||||
|
*
|
||||||
|
* @param {Buffer} data Data to handle
|
||||||
|
* @return {(Error|RangeError|undefined)} A possible error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
controlMessage(data) {
|
||||||
|
if (this._opcode === 0x08) {
|
||||||
|
this._loop = false;
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
this.emit('conclude', 1005, EMPTY_BUFFER);
|
||||||
|
this.end();
|
||||||
|
|
||||||
|
this._state = GET_INFO;
|
||||||
|
} else {
|
||||||
|
const code = data.readUInt16BE(0);
|
||||||
|
|
||||||
|
if (!isValidStatusCode(code)) {
|
||||||
|
return error(
|
||||||
|
RangeError,
|
||||||
|
`invalid status code ${code}`,
|
||||||
|
true,
|
||||||
|
1002,
|
||||||
|
'WS_ERR_INVALID_CLOSE_CODE'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = new FastBuffer(
|
||||||
|
data.buffer,
|
||||||
|
data.byteOffset + 2,
|
||||||
|
data.length - 2
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!this._skipUTF8Validation && !isValidUTF8(buf)) {
|
||||||
|
return error(
|
||||||
|
Error,
|
||||||
|
'invalid UTF-8 sequence',
|
||||||
|
true,
|
||||||
|
1007,
|
||||||
|
'WS_ERR_INVALID_UTF8'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('conclude', code, buf);
|
||||||
|
this.end();
|
||||||
|
|
||||||
|
this._state = GET_INFO;
|
||||||
|
}
|
||||||
|
} else if (this._opcode === 0x09) {
|
||||||
|
this.emit('ping', data);
|
||||||
|
this._state = WAIT_MICROTASK;
|
||||||
|
} else {
|
||||||
|
this.emit('pong', data);
|
||||||
|
this._state = WAIT_MICROTASK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Receiver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds an error object.
|
||||||
|
*
|
||||||
|
* @param {function(new:Error|RangeError)} ErrorCtor The error constructor
|
||||||
|
* @param {String} message The error message
|
||||||
|
* @param {Boolean} prefix Specifies whether or not to add a default prefix to
|
||||||
|
* `message`
|
||||||
|
* @param {Number} statusCode The status code
|
||||||
|
* @param {String} errorCode The exposed error code
|
||||||
|
* @return {(Error|RangeError)} The error
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function error(ErrorCtor, message, prefix, statusCode, errorCode) {
|
||||||
|
const err = new ErrorCtor(
|
||||||
|
prefix ? `Invalid WebSocket frame: ${message}` : message
|
||||||
|
);
|
||||||
|
|
||||||
|
Error.captureStackTrace(err, error);
|
||||||
|
err.code = errorCode;
|
||||||
|
err[kStatusCode] = statusCode;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shim for `queueMicrotask()`.
|
||||||
|
*
|
||||||
|
* @param {Function} cb Callback
|
||||||
|
*/
|
||||||
|
function queueMicrotaskShim(cb) {
|
||||||
|
promise.then(cb).catch(throwErrorNextTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error.
|
||||||
|
*
|
||||||
|
* @param {Error} err The error to throw
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function throwError(err) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws an error in the next tick.
|
||||||
|
*
|
||||||
|
* @param {Error} err The error to throw
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function throwErrorNextTick(err) {
|
||||||
|
process.nextTick(throwError, err);
|
||||||
|
}
|
477
node_modules/ws/lib/sender.js
generated
vendored
Normal file
477
node_modules/ws/lib/sender.js
generated
vendored
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex" }] */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Duplex } = require('stream');
|
||||||
|
const { randomFillSync } = require('crypto');
|
||||||
|
|
||||||
|
const PerMessageDeflate = require('./permessage-deflate');
|
||||||
|
const { EMPTY_BUFFER } = require('./constants');
|
||||||
|
const { isValidStatusCode } = require('./validation');
|
||||||
|
const { mask: applyMask, toBuffer } = require('./buffer-util');
|
||||||
|
|
||||||
|
const kByteLength = Symbol('kByteLength');
|
||||||
|
const maskBuffer = Buffer.alloc(4);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HyBi Sender implementation.
|
||||||
|
*/
|
||||||
|
class Sender {
|
||||||
|
/**
|
||||||
|
* Creates a Sender instance.
|
||||||
|
*
|
||||||
|
* @param {Duplex} socket The connection socket
|
||||||
|
* @param {Object} [extensions] An object containing the negotiated extensions
|
||||||
|
* @param {Function} [generateMask] The function used to generate the masking
|
||||||
|
* key
|
||||||
|
*/
|
||||||
|
constructor(socket, extensions, generateMask) {
|
||||||
|
this._extensions = extensions || {};
|
||||||
|
|
||||||
|
if (generateMask) {
|
||||||
|
this._generateMask = generateMask;
|
||||||
|
this._maskBuffer = Buffer.alloc(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._socket = socket;
|
||||||
|
|
||||||
|
this._firstFragment = true;
|
||||||
|
this._compress = false;
|
||||||
|
|
||||||
|
this._bufferedBytes = 0;
|
||||||
|
this._deflating = false;
|
||||||
|
this._queue = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frames a piece of data according to the HyBi WebSocket protocol.
|
||||||
|
*
|
||||||
|
* @param {(Buffer|String)} data The data to frame
|
||||||
|
* @param {Object} options Options object
|
||||||
|
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||||
|
* FIN bit
|
||||||
|
* @param {Function} [options.generateMask] The function used to generate the
|
||||||
|
* masking key
|
||||||
|
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||||
|
* `data`
|
||||||
|
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
||||||
|
* key
|
||||||
|
* @param {Number} options.opcode The opcode
|
||||||
|
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||||
|
* modified
|
||||||
|
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
||||||
|
* RSV1 bit
|
||||||
|
* @return {(Buffer|String)[]} The framed data
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
static frame(data, options) {
|
||||||
|
let mask;
|
||||||
|
let merge = false;
|
||||||
|
let offset = 2;
|
||||||
|
let skipMasking = false;
|
||||||
|
|
||||||
|
if (options.mask) {
|
||||||
|
mask = options.maskBuffer || maskBuffer;
|
||||||
|
|
||||||
|
if (options.generateMask) {
|
||||||
|
options.generateMask(mask);
|
||||||
|
} else {
|
||||||
|
randomFillSync(mask, 0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
skipMasking = (mask[0] | mask[1] | mask[2] | mask[3]) === 0;
|
||||||
|
offset = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dataLength;
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
if (
|
||||||
|
(!options.mask || skipMasking) &&
|
||||||
|
options[kByteLength] !== undefined
|
||||||
|
) {
|
||||||
|
dataLength = options[kByteLength];
|
||||||
|
} else {
|
||||||
|
data = Buffer.from(data);
|
||||||
|
dataLength = data.length;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dataLength = data.length;
|
||||||
|
merge = options.mask && options.readOnly && !skipMasking;
|
||||||
|
}
|
||||||
|
|
||||||
|
let payloadLength = dataLength;
|
||||||
|
|
||||||
|
if (dataLength >= 65536) {
|
||||||
|
offset += 8;
|
||||||
|
payloadLength = 127;
|
||||||
|
} else if (dataLength > 125) {
|
||||||
|
offset += 2;
|
||||||
|
payloadLength = 126;
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = Buffer.allocUnsafe(merge ? dataLength + offset : offset);
|
||||||
|
|
||||||
|
target[0] = options.fin ? options.opcode | 0x80 : options.opcode;
|
||||||
|
if (options.rsv1) target[0] |= 0x40;
|
||||||
|
|
||||||
|
target[1] = payloadLength;
|
||||||
|
|
||||||
|
if (payloadLength === 126) {
|
||||||
|
target.writeUInt16BE(dataLength, 2);
|
||||||
|
} else if (payloadLength === 127) {
|
||||||
|
target[2] = target[3] = 0;
|
||||||
|
target.writeUIntBE(dataLength, 4, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.mask) return [target, data];
|
||||||
|
|
||||||
|
target[1] |= 0x80;
|
||||||
|
target[offset - 4] = mask[0];
|
||||||
|
target[offset - 3] = mask[1];
|
||||||
|
target[offset - 2] = mask[2];
|
||||||
|
target[offset - 1] = mask[3];
|
||||||
|
|
||||||
|
if (skipMasking) return [target, data];
|
||||||
|
|
||||||
|
if (merge) {
|
||||||
|
applyMask(data, mask, target, offset, dataLength);
|
||||||
|
return [target];
|
||||||
|
}
|
||||||
|
|
||||||
|
applyMask(data, mask, data, 0, dataLength);
|
||||||
|
return [target, data];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a close message to the other peer.
|
||||||
|
*
|
||||||
|
* @param {Number} [code] The status code component of the body
|
||||||
|
* @param {(String|Buffer)} [data] The message component of the body
|
||||||
|
* @param {Boolean} [mask=false] Specifies whether or not to mask the message
|
||||||
|
* @param {Function} [cb] Callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
close(code, data, mask, cb) {
|
||||||
|
let buf;
|
||||||
|
|
||||||
|
if (code === undefined) {
|
||||||
|
buf = EMPTY_BUFFER;
|
||||||
|
} else if (typeof code !== 'number' || !isValidStatusCode(code)) {
|
||||||
|
throw new TypeError('First argument must be a valid error code number');
|
||||||
|
} else if (data === undefined || !data.length) {
|
||||||
|
buf = Buffer.allocUnsafe(2);
|
||||||
|
buf.writeUInt16BE(code, 0);
|
||||||
|
} else {
|
||||||
|
const length = Buffer.byteLength(data);
|
||||||
|
|
||||||
|
if (length > 123) {
|
||||||
|
throw new RangeError('The message must not be greater than 123 bytes');
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = Buffer.allocUnsafe(2 + length);
|
||||||
|
buf.writeUInt16BE(code, 0);
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
buf.write(data, 2);
|
||||||
|
} else {
|
||||||
|
buf.set(data, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
[kByteLength]: buf.length,
|
||||||
|
fin: true,
|
||||||
|
generateMask: this._generateMask,
|
||||||
|
mask,
|
||||||
|
maskBuffer: this._maskBuffer,
|
||||||
|
opcode: 0x08,
|
||||||
|
readOnly: false,
|
||||||
|
rsv1: false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._deflating) {
|
||||||
|
this.enqueue([this.dispatch, buf, false, options, cb]);
|
||||||
|
} else {
|
||||||
|
this.sendFrame(Sender.frame(buf, options), cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a ping message to the other peer.
|
||||||
|
*
|
||||||
|
* @param {*} data The message to send
|
||||||
|
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||||
|
* @param {Function} [cb] Callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
ping(data, mask, cb) {
|
||||||
|
let byteLength;
|
||||||
|
let readOnly;
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
byteLength = Buffer.byteLength(data);
|
||||||
|
readOnly = false;
|
||||||
|
} else {
|
||||||
|
data = toBuffer(data);
|
||||||
|
byteLength = data.length;
|
||||||
|
readOnly = toBuffer.readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byteLength > 125) {
|
||||||
|
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
[kByteLength]: byteLength,
|
||||||
|
fin: true,
|
||||||
|
generateMask: this._generateMask,
|
||||||
|
mask,
|
||||||
|
maskBuffer: this._maskBuffer,
|
||||||
|
opcode: 0x09,
|
||||||
|
readOnly,
|
||||||
|
rsv1: false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._deflating) {
|
||||||
|
this.enqueue([this.dispatch, data, false, options, cb]);
|
||||||
|
} else {
|
||||||
|
this.sendFrame(Sender.frame(data, options), cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a pong message to the other peer.
|
||||||
|
*
|
||||||
|
* @param {*} data The message to send
|
||||||
|
* @param {Boolean} [mask=false] Specifies whether or not to mask `data`
|
||||||
|
* @param {Function} [cb] Callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
pong(data, mask, cb) {
|
||||||
|
let byteLength;
|
||||||
|
let readOnly;
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
byteLength = Buffer.byteLength(data);
|
||||||
|
readOnly = false;
|
||||||
|
} else {
|
||||||
|
data = toBuffer(data);
|
||||||
|
byteLength = data.length;
|
||||||
|
readOnly = toBuffer.readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (byteLength > 125) {
|
||||||
|
throw new RangeError('The data size must not be greater than 125 bytes');
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
[kByteLength]: byteLength,
|
||||||
|
fin: true,
|
||||||
|
generateMask: this._generateMask,
|
||||||
|
mask,
|
||||||
|
maskBuffer: this._maskBuffer,
|
||||||
|
opcode: 0x0a,
|
||||||
|
readOnly,
|
||||||
|
rsv1: false
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._deflating) {
|
||||||
|
this.enqueue([this.dispatch, data, false, options, cb]);
|
||||||
|
} else {
|
||||||
|
this.sendFrame(Sender.frame(data, options), cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a data message to the other peer.
|
||||||
|
*
|
||||||
|
* @param {*} data The message to send
|
||||||
|
* @param {Object} options Options object
|
||||||
|
* @param {Boolean} [options.binary=false] Specifies whether `data` is binary
|
||||||
|
* or text
|
||||||
|
* @param {Boolean} [options.compress=false] Specifies whether or not to
|
||||||
|
* compress `data`
|
||||||
|
* @param {Boolean} [options.fin=false] Specifies whether the fragment is the
|
||||||
|
* last one
|
||||||
|
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||||
|
* `data`
|
||||||
|
* @param {Function} [cb] Callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
send(data, options, cb) {
|
||||||
|
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||||
|
let opcode = options.binary ? 2 : 1;
|
||||||
|
let rsv1 = options.compress;
|
||||||
|
|
||||||
|
let byteLength;
|
||||||
|
let readOnly;
|
||||||
|
|
||||||
|
if (typeof data === 'string') {
|
||||||
|
byteLength = Buffer.byteLength(data);
|
||||||
|
readOnly = false;
|
||||||
|
} else {
|
||||||
|
data = toBuffer(data);
|
||||||
|
byteLength = data.length;
|
||||||
|
readOnly = toBuffer.readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._firstFragment) {
|
||||||
|
this._firstFragment = false;
|
||||||
|
if (
|
||||||
|
rsv1 &&
|
||||||
|
perMessageDeflate &&
|
||||||
|
perMessageDeflate.params[
|
||||||
|
perMessageDeflate._isServer
|
||||||
|
? 'server_no_context_takeover'
|
||||||
|
: 'client_no_context_takeover'
|
||||||
|
]
|
||||||
|
) {
|
||||||
|
rsv1 = byteLength >= perMessageDeflate._threshold;
|
||||||
|
}
|
||||||
|
this._compress = rsv1;
|
||||||
|
} else {
|
||||||
|
rsv1 = false;
|
||||||
|
opcode = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.fin) this._firstFragment = true;
|
||||||
|
|
||||||
|
if (perMessageDeflate) {
|
||||||
|
const opts = {
|
||||||
|
[kByteLength]: byteLength,
|
||||||
|
fin: options.fin,
|
||||||
|
generateMask: this._generateMask,
|
||||||
|
mask: options.mask,
|
||||||
|
maskBuffer: this._maskBuffer,
|
||||||
|
opcode,
|
||||||
|
readOnly,
|
||||||
|
rsv1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this._deflating) {
|
||||||
|
this.enqueue([this.dispatch, data, this._compress, opts, cb]);
|
||||||
|
} else {
|
||||||
|
this.dispatch(data, this._compress, opts, cb);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.sendFrame(
|
||||||
|
Sender.frame(data, {
|
||||||
|
[kByteLength]: byteLength,
|
||||||
|
fin: options.fin,
|
||||||
|
generateMask: this._generateMask,
|
||||||
|
mask: options.mask,
|
||||||
|
maskBuffer: this._maskBuffer,
|
||||||
|
opcode,
|
||||||
|
readOnly,
|
||||||
|
rsv1: false
|
||||||
|
}),
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a message.
|
||||||
|
*
|
||||||
|
* @param {(Buffer|String)} data The message to send
|
||||||
|
* @param {Boolean} [compress=false] Specifies whether or not to compress
|
||||||
|
* `data`
|
||||||
|
* @param {Object} options Options object
|
||||||
|
* @param {Boolean} [options.fin=false] Specifies whether or not to set the
|
||||||
|
* FIN bit
|
||||||
|
* @param {Function} [options.generateMask] The function used to generate the
|
||||||
|
* masking key
|
||||||
|
* @param {Boolean} [options.mask=false] Specifies whether or not to mask
|
||||||
|
* `data`
|
||||||
|
* @param {Buffer} [options.maskBuffer] The buffer used to store the masking
|
||||||
|
* key
|
||||||
|
* @param {Number} options.opcode The opcode
|
||||||
|
* @param {Boolean} [options.readOnly=false] Specifies whether `data` can be
|
||||||
|
* modified
|
||||||
|
* @param {Boolean} [options.rsv1=false] Specifies whether or not to set the
|
||||||
|
* RSV1 bit
|
||||||
|
* @param {Function} [cb] Callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
dispatch(data, compress, options, cb) {
|
||||||
|
if (!compress) {
|
||||||
|
this.sendFrame(Sender.frame(data, options), cb);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const perMessageDeflate = this._extensions[PerMessageDeflate.extensionName];
|
||||||
|
|
||||||
|
this._bufferedBytes += options[kByteLength];
|
||||||
|
this._deflating = true;
|
||||||
|
perMessageDeflate.compress(data, options.fin, (_, buf) => {
|
||||||
|
if (this._socket.destroyed) {
|
||||||
|
const err = new Error(
|
||||||
|
'The socket was closed while data was being compressed'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (typeof cb === 'function') cb(err);
|
||||||
|
|
||||||
|
for (let i = 0; i < this._queue.length; i++) {
|
||||||
|
const params = this._queue[i];
|
||||||
|
const callback = params[params.length - 1];
|
||||||
|
|
||||||
|
if (typeof callback === 'function') callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._bufferedBytes -= options[kByteLength];
|
||||||
|
this._deflating = false;
|
||||||
|
options.readOnly = false;
|
||||||
|
this.sendFrame(Sender.frame(buf, options), cb);
|
||||||
|
this.dequeue();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes queued send operations.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
dequeue() {
|
||||||
|
while (!this._deflating && this._queue.length) {
|
||||||
|
const params = this._queue.shift();
|
||||||
|
|
||||||
|
this._bufferedBytes -= params[3][kByteLength];
|
||||||
|
Reflect.apply(params[0], this, params.slice(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueues a send operation.
|
||||||
|
*
|
||||||
|
* @param {Array} params Send operation parameters.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
enqueue(params) {
|
||||||
|
this._bufferedBytes += params[3][kByteLength];
|
||||||
|
this._queue.push(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a frame.
|
||||||
|
*
|
||||||
|
* @param {Buffer[]} list The frame to send
|
||||||
|
* @param {Function} [cb] Callback
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
sendFrame(list, cb) {
|
||||||
|
if (list.length === 2) {
|
||||||
|
this._socket.cork();
|
||||||
|
this._socket.write(list[0]);
|
||||||
|
this._socket.write(list[1], cb);
|
||||||
|
this._socket.uncork();
|
||||||
|
} else {
|
||||||
|
this._socket.write(list[0], cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Sender;
|
159
node_modules/ws/lib/stream.js
generated
vendored
Normal file
159
node_modules/ws/lib/stream.js
generated
vendored
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
'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;
|
62
node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
62
node_modules/ws/lib/subprotocol.js
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
'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 };
|
130
node_modules/ws/lib/validation.js
generated
vendored
Normal file
130
node_modules/ws/lib/validation.js
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { isUtf8 } = require('buffer');
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a status code is allowed in a close frame.
|
||||||
|
*
|
||||||
|
* @param {Number} code The status code
|
||||||
|
* @return {Boolean} `true` if the status code is valid, else `false`
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
function isValidStatusCode(code) {
|
||||||
|
return (
|
||||||
|
(code >= 1000 &&
|
||||||
|
code <= 1014 &&
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isUtf8) {
|
||||||
|
module.exports.isValidUTF8 = function (buf) {
|
||||||
|
return buf.length < 24 ? _isValidUTF8(buf) : isUtf8(buf);
|
||||||
|
};
|
||||||
|
} /* istanbul ignore else */ else if (!process.env.WS_NO_UTF_8_VALIDATE) {
|
||||||
|
try {
|
||||||
|
const isValidUTF8 = require('utf-8-validate');
|
||||||
|
|
||||||
|
module.exports.isValidUTF8 = function (buf) {
|
||||||
|
return buf.length < 32 ? _isValidUTF8(buf) : isValidUTF8(buf);
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
// Continue regardless of the error.
|
||||||
|
}
|
||||||
|
}
|
531
node_modules/ws/lib/websocket-server.js
generated
vendored
Normal file
531
node_modules/ws/lib/websocket-server.js
generated
vendored
Normal file
@ -0,0 +1,531 @@
|
|||||||
|
/* eslint no-unused-vars: ["error", { "varsIgnorePattern": "^Duplex$" }] */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const EventEmitter = require('events');
|
||||||
|
const http = require('http');
|
||||||
|
const { Duplex } = require('stream');
|
||||||
|
const { createHash } = require('crypto');
|
||||||
|
|
||||||
|
const extension = require('./extension');
|
||||||
|
const PerMessageDeflate = require('./permessage-deflate');
|
||||||
|
const subprotocol = require('./subprotocol');
|
||||||
|
const WebSocket = require('./websocket');
|
||||||
|
const { GUID, kWebSocket } = require('./constants');
|
||||||
|
|
||||||
|
const keyRegex = /^[+/0-9A-Za-z]{22}==$/;
|
||||||
|
|
||||||
|
const RUNNING = 0;
|
||||||
|
const CLOSING = 1;
|
||||||
|
const CLOSED = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a WebSocket server.
|
||||||
|
*
|
||||||
|
* @extends EventEmitter
|
||||||
|
*/
|
||||||
|
class WebSocketServer extends EventEmitter {
|
||||||
|
/**
|
||||||
|
* Create a `WebSocketServer` instance.
|
||||||
|
*
|
||||||
|
* @param {Object} options Configuration options
|
||||||
|
* @param {Number} [options.backlog=511] The maximum length of the queue of
|
||||||
|
* pending connections
|
||||||
|
* @param {Boolean} [options.clientTracking=true] Specifies whether or not to
|
||||||
|
* track clients
|
||||||
|
* @param {Function} [options.handleProtocols] A hook to handle protocols
|
||||||
|
* @param {String} [options.host] The hostname where to bind the server
|
||||||
|
* @param {Number} [options.maxPayload=104857600] The maximum allowed message
|
||||||
|
* size
|
||||||
|
* @param {Boolean} [options.noServer=false] Enable no server mode
|
||||||
|
* @param {String} [options.path] Accept only connections matching this path
|
||||||
|
* @param {(Boolean|Object)} [options.perMessageDeflate=false] Enable/disable
|
||||||
|
* permessage-deflate
|
||||||
|
* @param {Number} [options.port] The port where to bind the server
|
||||||
|
* @param {(http.Server|https.Server)} [options.server] A pre-created HTTP/S
|
||||||
|
* server to use
|
||||||
|
* @param {Boolean} [options.skipUTF8Validation=false] Specifies whether or
|
||||||
|
* not to skip UTF-8 validation for text and close messages
|
||||||
|
* @param {Function} [options.verifyClient] A hook to reject connections
|
||||||
|
* @param {Function} [options.WebSocket=WebSocket] Specifies the `WebSocket`
|
||||||
|
* class to use. It must be the `WebSocket` class or class that extends it
|
||||||
|
* @param {Function} [callback] A listener for the `listening` event
|
||||||
|
*/
|
||||||
|
constructor(options, callback) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
options = {
|
||||||
|
maxPayload: 100 * 1024 * 1024,
|
||||||
|
skipUTF8Validation: false,
|
||||||
|
perMessageDeflate: false,
|
||||||
|
handleProtocols: null,
|
||||||
|
clientTracking: true,
|
||||||
|
verifyClient: null,
|
||||||
|
noServer: false,
|
||||||
|
backlog: null, // use default (511 as implemented in net.js)
|
||||||
|
server: null,
|
||||||
|
host: null,
|
||||||
|
path: null,
|
||||||
|
port: null,
|
||||||
|
WebSocket,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
|
||||||
|
if (
|
||||||
|
(options.port == null && !options.server && !options.noServer) ||
|
||||||
|
(options.port != null && (options.server || options.noServer)) ||
|
||||||
|
(options.server && options.noServer)
|
||||||
|
) {
|
||||||
|
throw new TypeError(
|
||||||
|
'One and only one of the "port", "server", or "noServer" options ' +
|
||||||
|
'must be specified'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.port != null) {
|
||||||
|
this._server = http.createServer((req, res) => {
|
||||||
|
const body = http.STATUS_CODES[426];
|
||||||
|
|
||||||
|
res.writeHead(426, {
|
||||||
|
'Content-Length': body.length,
|
||||||
|
'Content-Type': 'text/plain'
|
||||||
|
});
|
||||||
|
res.end(body);
|
||||||
|
});
|
||||||
|
this._server.listen(
|
||||||
|
options.port,
|
||||||
|
options.host,
|
||||||
|
options.backlog,
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
} else if (options.server) {
|
||||||
|
this._server = options.server;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._server) {
|
||||||
|
const emitConnection = this.emit.bind(this, 'connection');
|
||||||
|
|
||||||
|
this._removeListeners = addListeners(this._server, {
|
||||||
|
listening: this.emit.bind(this, 'listening'),
|
||||||
|
error: this.emit.bind(this, 'error'),
|
||||||
|
upgrade: (req, socket, head) => {
|
||||||
|
this.handleUpgrade(req, socket, head, emitConnection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.perMessageDeflate === true) options.perMessageDeflate = {};
|
||||||
|
if (options.clientTracking) {
|
||||||
|
this.clients = new Set();
|
||||||
|
this._shouldEmitClose = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.options = options;
|
||||||
|
this._state = RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bound address, the address family name, and port of the server
|
||||||
|
* as reported by the operating system if listening on an IP socket.
|
||||||
|
* If the server is listening on a pipe or UNIX domain socket, the name is
|
||||||
|
* returned as a string.
|
||||||
|
*
|
||||||
|
* @return {(Object|String|null)} The address of the server
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
address() {
|
||||||
|
if (this.options.noServer) {
|
||||||
|
throw new Error('The server is operating in "noServer" mode');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._server) return null;
|
||||||
|
return this._server.address();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop the server from accepting new connections and emit the `'close'` event
|
||||||
|
* when all existing connections are closed.
|
||||||
|
*
|
||||||
|
* @param {Function} [cb] A one-time listener for the `'close'` event
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
close(cb) {
|
||||||
|
if (this._state === CLOSED) {
|
||||||
|
if (cb) {
|
||||||
|
this.once('close', () => {
|
||||||
|
cb(new Error('The server is not running'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
process.nextTick(emitClose, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cb) this.once('close', cb);
|
||||||
|
|
||||||
|
if (this._state === CLOSING) return;
|
||||||
|
this._state = CLOSING;
|
||||||
|
|
||||||
|
if (this.options.noServer || this.options.server) {
|
||||||
|
if (this._server) {
|
||||||
|
this._removeListeners();
|
||||||
|
this._removeListeners = this._server = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.clients) {
|
||||||
|
if (!this.clients.size) {
|
||||||
|
process.nextTick(emitClose, this);
|
||||||
|
} else {
|
||||||
|
this._shouldEmitClose = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
process.nextTick(emitClose, this);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const server = this._server;
|
||||||
|
|
||||||
|
this._removeListeners();
|
||||||
|
this._removeListeners = this._server = null;
|
||||||
|
|
||||||
|
//
|
||||||
|
// The HTTP/S server was created internally. Close it, and rely on its
|
||||||
|
// `'close'` event.
|
||||||
|
//
|
||||||
|
server.close(() => {
|
||||||
|
emitClose(this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See if a given request should be handled by this server instance.
|
||||||
|
*
|
||||||
|
* @param {http.IncomingMessage} req Request object to inspect
|
||||||
|
* @return {Boolean} `true` if the request is valid, else `false`
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
shouldHandle(req) {
|
||||||
|
if (this.options.path) {
|
||||||
|
const index = req.url.indexOf('?');
|
||||||
|
const pathname = index !== -1 ? req.url.slice(0, index) : req.url;
|
||||||
|
|
||||||
|
if (pathname !== this.options.path) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle a HTTP Upgrade request.
|
||||||
|
*
|
||||||
|
* @param {http.IncomingMessage} req The request object
|
||||||
|
* @param {Duplex} socket The network socket between the server and client
|
||||||
|
* @param {Buffer} head The first packet of the upgraded stream
|
||||||
|
* @param {Function} cb Callback
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
handleUpgrade(req, socket, head, cb) {
|
||||||
|
socket.on('error', socketOnError);
|
||||||
|
|
||||||
|
const key = req.headers['sec-websocket-key'];
|
||||||
|
const version = +req.headers['sec-websocket-version'];
|
||||||
|
|
||||||
|
if (req.method !== 'GET') {
|
||||||
|
const message = 'Invalid HTTP method';
|
||||||
|
abortHandshakeOrEmitwsClientError(this, req, socket, 405, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.headers.upgrade.toLowerCase() !== 'websocket') {
|
||||||
|
const message = 'Invalid Upgrade header';
|
||||||
|
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!key || !keyRegex.test(key)) {
|
||||||
|
const message = 'Missing or invalid Sec-WebSocket-Key header';
|
||||||
|
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version !== 8 && version !== 13) {
|
||||||
|
const message = 'Missing or invalid Sec-WebSocket-Version header';
|
||||||
|
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.shouldHandle(req)) {
|
||||||
|
abortHandshake(socket, 400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const secWebSocketProtocol = req.headers['sec-websocket-protocol'];
|
||||||
|
let protocols = new Set();
|
||||||
|
|
||||||
|
if (secWebSocketProtocol !== undefined) {
|
||||||
|
try {
|
||||||
|
protocols = subprotocol.parse(secWebSocketProtocol);
|
||||||
|
} catch (err) {
|
||||||
|
const message = 'Invalid Sec-WebSocket-Protocol header';
|
||||||
|
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const secWebSocketExtensions = req.headers['sec-websocket-extensions'];
|
||||||
|
const extensions = {};
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.options.perMessageDeflate &&
|
||||||
|
secWebSocketExtensions !== undefined
|
||||||
|
) {
|
||||||
|
const perMessageDeflate = new PerMessageDeflate(
|
||||||
|
this.options.perMessageDeflate,
|
||||||
|
true,
|
||||||
|
this.options.maxPayload
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const offers = extension.parse(secWebSocketExtensions);
|
||||||
|
|
||||||
|
if (offers[PerMessageDeflate.extensionName]) {
|
||||||
|
perMessageDeflate.accept(offers[PerMessageDeflate.extensionName]);
|
||||||
|
extensions[PerMessageDeflate.extensionName] = perMessageDeflate;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const message =
|
||||||
|
'Invalid or unacceptable Sec-WebSocket-Extensions header';
|
||||||
|
abortHandshakeOrEmitwsClientError(this, req, socket, 400, message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Optionally call external client verification handler.
|
||||||
|
//
|
||||||
|
if (this.options.verifyClient) {
|
||||||
|
const info = {
|
||||||
|
origin:
|
||||||
|
req.headers[`${version === 8 ? 'sec-websocket-origin' : 'origin'}`],
|
||||||
|
secure: !!(req.socket.authorized || req.socket.encrypted),
|
||||||
|
req
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.options.verifyClient.length === 2) {
|
||||||
|
this.options.verifyClient(info, (verified, code, message, headers) => {
|
||||||
|
if (!verified) {
|
||||||
|
return abortHandshake(socket, code || 401, message, headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.completeUpgrade(
|
||||||
|
extensions,
|
||||||
|
key,
|
||||||
|
protocols,
|
||||||
|
req,
|
||||||
|
socket,
|
||||||
|
head,
|
||||||
|
cb
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.options.verifyClient(info)) return abortHandshake(socket, 401);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.completeUpgrade(extensions, key, protocols, req, socket, head, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upgrade the connection to WebSocket.
|
||||||
|
*
|
||||||
|
* @param {Object} extensions The accepted extensions
|
||||||
|
* @param {String} key The value of the `Sec-WebSocket-Key` header
|
||||||
|
* @param {Set} protocols The subprotocols
|
||||||
|
* @param {http.IncomingMessage} req The request object
|
||||||
|
* @param {Duplex} socket The network socket between the server and client
|
||||||
|
* @param {Buffer} head The first packet of the upgraded stream
|
||||||
|
* @param {Function} cb Callback
|
||||||
|
* @throws {Error} If called more than once with the same socket
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
completeUpgrade(extensions, key, protocols, req, socket, head, cb) {
|
||||||
|
//
|
||||||
|
// Destroy the socket if the client has already sent a FIN packet.
|
||||||
|
//
|
||||||
|
if (!socket.readable || !socket.writable) return socket.destroy();
|
||||||
|
|
||||||
|
if (socket[kWebSocket]) {
|
||||||
|
throw new Error(
|
||||||
|
'server.handleUpgrade() was called more than once with the same ' +
|
||||||
|
'socket, possibly due to a misconfiguration'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
||||||
|
|
||||||
|
const digest = createHash('sha1')
|
||||||
|
.update(key + GUID)
|
||||||
|
.digest('base64');
|
||||||
|
|
||||||
|
const headers = [
|
||||||
|
'HTTP/1.1 101 Switching Protocols',
|
||||||
|
'Upgrade: websocket',
|
||||||
|
'Connection: Upgrade',
|
||||||
|
`Sec-WebSocket-Accept: ${digest}`
|
||||||
|
];
|
||||||
|
|
||||||
|
const ws = new this.options.WebSocket(null);
|
||||||
|
|
||||||
|
if (protocols.size) {
|
||||||
|
//
|
||||||
|
// Optionally call external protocol selection handler.
|
||||||
|
//
|
||||||
|
const protocol = this.options.handleProtocols
|
||||||
|
? this.options.handleProtocols(protocols, req)
|
||||||
|
: protocols.values().next().value;
|
||||||
|
|
||||||
|
if (protocol) {
|
||||||
|
headers.push(`Sec-WebSocket-Protocol: ${protocol}`);
|
||||||
|
ws._protocol = protocol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extensions[PerMessageDeflate.extensionName]) {
|
||||||
|
const params = extensions[PerMessageDeflate.extensionName].params;
|
||||||
|
const value = extension.format({
|
||||||
|
[PerMessageDeflate.extensionName]: [params]
|
||||||
|
});
|
||||||
|
headers.push(`Sec-WebSocket-Extensions: ${value}`);
|
||||||
|
ws._extensions = extensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allow external modification/inspection of handshake headers.
|
||||||
|
//
|
||||||
|
this.emit('headers', headers, req);
|
||||||
|
|
||||||
|
socket.write(headers.concat('\r\n').join('\r\n'));
|
||||||
|
socket.removeListener('error', socketOnError);
|
||||||
|
|
||||||
|
ws.setSocket(socket, head, {
|
||||||
|
maxPayload: this.options.maxPayload,
|
||||||
|
skipUTF8Validation: this.options.skipUTF8Validation
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.clients) {
|
||||||
|
this.clients.add(ws);
|
||||||
|
ws.on('close', () => {
|
||||||
|
this.clients.delete(ws);
|
||||||
|
|
||||||
|
if (this._shouldEmitClose && !this.clients.size) {
|
||||||
|
process.nextTick(emitClose, this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
cb(ws, req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = WebSocketServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add event listeners on an `EventEmitter` using a map of <event, listener>
|
||||||
|
* pairs.
|
||||||
|
*
|
||||||
|
* @param {EventEmitter} server The event emitter
|
||||||
|
* @param {Object.<String, Function>} map The listeners to add
|
||||||
|
* @return {Function} A function that will remove the added listeners when
|
||||||
|
* called
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function addListeners(server, map) {
|
||||||
|
for (const event of Object.keys(map)) server.on(event, map[event]);
|
||||||
|
|
||||||
|
return function removeListeners() {
|
||||||
|
for (const event of Object.keys(map)) {
|
||||||
|
server.removeListener(event, map[event]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a `'close'` event on an `EventEmitter`.
|
||||||
|
*
|
||||||
|
* @param {EventEmitter} server The event emitter
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function emitClose(server) {
|
||||||
|
server._state = CLOSED;
|
||||||
|
server.emit('close');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle socket errors.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function socketOnError() {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the connection when preconditions are not fulfilled.
|
||||||
|
*
|
||||||
|
* @param {Duplex} socket The socket of the upgrade request
|
||||||
|
* @param {Number} code The HTTP response status code
|
||||||
|
* @param {String} [message] The HTTP response body
|
||||||
|
* @param {Object} [headers] Additional HTTP response headers
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function abortHandshake(socket, code, message, headers) {
|
||||||
|
//
|
||||||
|
// The socket is writable unless the user destroyed or ended it before calling
|
||||||
|
// `server.handleUpgrade()` or in the `verifyClient` function, which is a user
|
||||||
|
// error. Handling this does not make much sense as the worst that can happen
|
||||||
|
// is that some of the data written by the user might be discarded due to the
|
||||||
|
// call to `socket.end()` below, which triggers an `'error'` event that in
|
||||||
|
// turn causes the socket to be destroyed.
|
||||||
|
//
|
||||||
|
message = message || http.STATUS_CODES[code];
|
||||||
|
headers = {
|
||||||
|
Connection: 'close',
|
||||||
|
'Content-Type': 'text/html',
|
||||||
|
'Content-Length': Buffer.byteLength(message),
|
||||||
|
...headers
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.once('finish', socket.destroy);
|
||||||
|
|
||||||
|
socket.end(
|
||||||
|
`HTTP/1.1 ${code} ${http.STATUS_CODES[code]}\r\n` +
|
||||||
|
Object.keys(headers)
|
||||||
|
.map((h) => `${h}: ${headers[h]}`)
|
||||||
|
.join('\r\n') +
|
||||||
|
'\r\n\r\n' +
|
||||||
|
message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit a `'wsClientError'` event on a `WebSocketServer` if there is at least
|
||||||
|
* one listener for it, otherwise call `abortHandshake()`.
|
||||||
|
*
|
||||||
|
* @param {WebSocketServer} server The WebSocket server
|
||||||
|
* @param {http.IncomingMessage} req The request object
|
||||||
|
* @param {Duplex} socket The socket of the upgrade request
|
||||||
|
* @param {Number} code The HTTP response status code
|
||||||
|
* @param {String} message The HTTP response body
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
function abortHandshakeOrEmitwsClientError(server, req, socket, code, message) {
|
||||||
|
if (server.listenerCount('wsClientError')) {
|
||||||
|
const err = new Error(message);
|
||||||
|
Error.captureStackTrace(err, abortHandshakeOrEmitwsClientError);
|
||||||
|
|
||||||
|
server.emit('wsClientError', err, socket, req);
|
||||||
|
} else {
|
||||||
|
abortHandshake(socket, code, message);
|
||||||
|
}
|
||||||
|
}
|
1319
node_modules/ws/lib/websocket.js
generated
vendored
Normal file
1319
node_modules/ws/lib/websocket.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
68
node_modules/ws/package.json
generated
vendored
Normal file
68
node_modules/ws/package.json
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{
|
||||||
|
"name": "ws",
|
||||||
|
"version": "8.14.2",
|
||||||
|
"description": "Simple to use, blazing fast and thoroughly tested websocket client and server for Node.js",
|
||||||
|
"keywords": [
|
||||||
|
"HyBi",
|
||||||
|
"Push",
|
||||||
|
"RFC-6455",
|
||||||
|
"WebSocket",
|
||||||
|
"WebSockets",
|
||||||
|
"real-time"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/websockets/ws",
|
||||||
|
"bugs": "https://github.com/websockets/ws/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/websockets/ws.git"
|
||||||
|
},
|
||||||
|
"author": "Einar Otto Stangvik <einaros@gmail.com> (http://2x.io)",
|
||||||
|
"license": "MIT",
|
||||||
|
"main": "index.js",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"browser": "./browser.js",
|
||||||
|
"import": "./wrapper.mjs",
|
||||||
|
"require": "./index.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
|
"browser": "browser.js",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"browser.js",
|
||||||
|
"index.js",
|
||||||
|
"lib/*.js",
|
||||||
|
"wrapper.mjs"
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"benchmark": "^2.1.4",
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"eslint": "^8.0.0",
|
||||||
|
"eslint-config-prettier": "^9.0.0",
|
||||||
|
"eslint-plugin-prettier": "^5.0.0",
|
||||||
|
"mocha": "^8.4.0",
|
||||||
|
"nyc": "^15.0.0",
|
||||||
|
"prettier": "^3.0.0",
|
||||||
|
"utf-8-validate": "^6.0.0"
|
||||||
|
}
|
||||||
|
}
|
8
node_modules/ws/wrapper.mjs
generated
vendored
Normal file
8
node_modules/ws/wrapper.mjs
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
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;
|
Loading…
x
Reference in New Issue
Block a user