opal 1.6.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/build.yml +17 -0
- data/CHANGELOG.md +35 -1
- data/Gemfile +1 -0
- data/HACKING.md +47 -26
- data/benchmark/benchmarks +415 -103
- data/benchmark/bm_call_overhead.yml +28 -0
- data/benchmark/run.rb +61 -40
- data/docs/cdp_common.json +3364 -0
- data/docs/cdp_common.md +18 -0
- data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
- data/lib/opal/ast/builder.rb +1 -1
- data/lib/opal/builder.rb +6 -1
- data/lib/opal/builder_processors.rb +5 -3
- data/lib/opal/cache.rb +1 -7
- data/lib/opal/cli_options.rb +72 -58
- data/lib/opal/cli_runners/chrome.rb +47 -9
- data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
- data/lib/opal/cli_runners/compiler.rb +146 -13
- data/lib/opal/cli_runners/deno.rb +32 -0
- data/lib/opal/cli_runners/firefox.rb +350 -0
- data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
- data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
- data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
- data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
- data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
- data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
- data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
- data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
- data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
- data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
- data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
- data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
- data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
- data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
- data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
- data/lib/opal/cli_runners/package-lock.json +62 -0
- data/lib/opal/cli_runners/package.json +1 -1
- data/lib/opal/cli_runners.rb +26 -4
- data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
- data/lib/opal/nodes/def.rb +8 -8
- data/lib/opal/nodes/iter.rb +12 -12
- data/lib/opal/nodes/logic.rb +1 -1
- data/lib/opal/nodes/masgn.rb +2 -2
- data/lib/opal/parser/with_ruby_lexer.rb +1 -1
- data/lib/opal/paths.rb +14 -0
- data/lib/opal/rewriter.rb +2 -0
- data/lib/opal/rewriters/forward_args.rb +52 -4
- data/lib/opal/rewriters/targeted_patches.rb +94 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/basic_object.rb +1 -1
- data/opal/corelib/boolean.rb +2 -2
- data/opal/corelib/class.rb +11 -0
- data/opal/corelib/constants.rb +3 -3
- data/opal/corelib/enumerable.rb +4 -0
- data/opal/corelib/enumerator.rb +1 -1
- data/opal/corelib/hash.rb +2 -2
- data/opal/corelib/helpers.rb +1 -1
- data/opal/corelib/kernel.rb +3 -3
- data/opal/corelib/method.rb +1 -1
- data/opal/corelib/module.rb +29 -8
- data/opal/corelib/proc.rb +7 -5
- data/opal/corelib/runtime.js +141 -78
- data/opal/corelib/set.rb +252 -0
- data/opal/corelib/string.rb +2 -1
- data/opal/corelib/time.rb +2 -2
- data/opal/opal.rb +1 -0
- data/opal.gemspec +1 -0
- data/spec/filters/bugs/array.rb +22 -13
- data/spec/filters/bugs/base64.rb +5 -5
- data/spec/filters/bugs/basicobject.rb +16 -8
- data/spec/filters/bugs/bigdecimal.rb +161 -160
- data/spec/filters/bugs/binding.rb +10 -10
- data/spec/filters/bugs/class.rb +8 -8
- data/spec/filters/bugs/complex.rb +2 -1
- data/spec/filters/bugs/date.rb +79 -81
- data/spec/filters/bugs/datetime.rb +29 -29
- data/spec/filters/bugs/delegate.rb +1 -3
- data/spec/filters/bugs/encoding.rb +69 -69
- data/spec/filters/bugs/enumerable.rb +22 -20
- data/spec/filters/bugs/enumerator.rb +88 -85
- data/spec/filters/bugs/exception.rb +46 -40
- data/spec/filters/bugs/file.rb +32 -32
- data/spec/filters/bugs/float.rb +26 -21
- data/spec/filters/bugs/freeze.rb +88 -0
- data/spec/filters/bugs/hash.rb +39 -38
- data/spec/filters/bugs/integer.rb +57 -44
- data/spec/filters/bugs/io.rb +1 -1
- data/spec/filters/bugs/kernel.rb +349 -269
- data/spec/filters/bugs/language.rb +220 -188
- data/spec/filters/bugs/main.rb +5 -3
- data/spec/filters/bugs/marshal.rb +38 -38
- data/spec/filters/bugs/math.rb +2 -1
- data/spec/filters/bugs/method.rb +73 -62
- data/spec/filters/bugs/module.rb +163 -143
- data/spec/filters/bugs/numeric.rb +6 -6
- data/spec/filters/bugs/objectspace.rb +16 -16
- data/spec/filters/bugs/openstruct.rb +1 -1
- data/spec/filters/bugs/pack_unpack.rb +51 -51
- data/spec/filters/bugs/pathname.rb +7 -7
- data/spec/filters/bugs/proc.rb +63 -63
- data/spec/filters/bugs/random.rb +7 -6
- data/spec/filters/bugs/range.rb +12 -9
- data/spec/filters/bugs/rational.rb +8 -7
- data/spec/filters/bugs/regexp.rb +49 -48
- data/spec/filters/bugs/ruby-32.rb +56 -0
- data/spec/filters/bugs/set.rb +30 -30
- data/spec/filters/bugs/singleton.rb +4 -4
- data/spec/filters/bugs/string.rb +187 -99
- data/spec/filters/bugs/stringio.rb +7 -0
- data/spec/filters/bugs/stringscanner.rb +68 -68
- data/spec/filters/bugs/struct.rb +11 -9
- data/spec/filters/bugs/symbol.rb +1 -1
- data/spec/filters/bugs/time.rb +78 -63
- data/spec/filters/bugs/trace_point.rb +4 -4
- data/spec/filters/bugs/unboundmethod.rb +32 -17
- data/spec/filters/bugs/warnings.rb +8 -12
- data/spec/filters/unsupported/array.rb +24 -107
- data/spec/filters/unsupported/basicobject.rb +12 -12
- data/spec/filters/unsupported/bignum.rb +27 -52
- data/spec/filters/unsupported/class.rb +1 -2
- data/spec/filters/unsupported/delegator.rb +3 -3
- data/spec/filters/unsupported/enumerable.rb +2 -9
- data/spec/filters/unsupported/enumerator.rb +2 -11
- data/spec/filters/unsupported/file.rb +1 -1
- data/spec/filters/unsupported/float.rb +28 -47
- data/spec/filters/unsupported/hash.rb +8 -14
- data/spec/filters/unsupported/integer.rb +75 -91
- data/spec/filters/unsupported/kernel.rb +17 -35
- data/spec/filters/unsupported/language.rb +11 -19
- data/spec/filters/unsupported/marshal.rb +22 -41
- data/spec/filters/unsupported/matchdata.rb +28 -52
- data/spec/filters/unsupported/math.rb +1 -1
- data/spec/filters/unsupported/privacy.rb +229 -285
- data/spec/filters/unsupported/range.rb +1 -5
- data/spec/filters/unsupported/regexp.rb +40 -66
- data/spec/filters/unsupported/set.rb +2 -2
- data/spec/filters/unsupported/singleton.rb +4 -4
- data/spec/filters/unsupported/string.rb +305 -508
- data/spec/filters/unsupported/struct.rb +3 -4
- data/spec/filters/unsupported/symbol.rb +15 -18
- data/spec/filters/unsupported/thread.rb +1 -7
- data/spec/filters/unsupported/time.rb +159 -202
- data/spec/filters/unsupported/usage_of_files.rb +170 -259
- data/spec/lib/builder_spec.rb +4 -4
- data/spec/lib/rewriters/forward_args_spec.rb +32 -12
- data/spec/mspec-opal/runner.rb +2 -0
- data/spec/ruby_specs +4 -0
- data/stdlib/deno/base.rb +28 -0
- data/stdlib/deno/file.rb +340 -0
- data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
- data/stdlib/headless_browser/file.rb +15 -0
- data/stdlib/headless_browser.rb +4 -0
- data/stdlib/native.rb +1 -1
- data/stdlib/nodejs/file.rb +5 -0
- data/stdlib/opal/platform.rb +8 -6
- data/stdlib/opal-platform.rb +14 -8
- data/stdlib/set.rb +1 -258
- data/tasks/benchmarking.rake +62 -19
- data/tasks/performance.rake +1 -1
- data/tasks/testing.rake +5 -3
- data/test/nodejs/test_file.rb +29 -10
- data/test/opal/http_server.rb +28 -11
- data/test/opal/unsupported_and_bugs.rb +2 -1
- metadata +89 -50
- data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
- data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
- data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
- data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
- data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
- data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
- data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
- data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
- data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
- data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
- data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
- data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
- data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
- data/spec/filters/bugs/boolean.rb +0 -3
- data/spec/filters/bugs/matrix.rb +0 -3
- data/spec/filters/unsupported/fixnum.rb +0 -15
- data/spec/filters/unsupported/freeze.rb +0 -102
- data/spec/filters/unsupported/pathname.rb +0 -4
- data/spec/filters/unsupported/proc.rb +0 -4
- data/spec/filters/unsupported/random.rb +0 -5
- data/spec/filters/unsupported/taint.rb +0 -162
@@ -1,92 +1,29 @@
|
|
1
1
|
{
|
2
|
-
"
|
3
|
-
|
4
|
-
{
|
5
|
-
"raw": "commander@2.1.x",
|
6
|
-
"scope": null,
|
7
|
-
"escapedName": "commander",
|
8
|
-
"name": "commander",
|
9
|
-
"rawSpec": "2.1.x",
|
10
|
-
"spec": ">=2.1.0 <2.2.0",
|
11
|
-
"type": "range"
|
12
|
-
},
|
13
|
-
"/Users/ilya/Work/opal/lib/opal/cli_runners/node_modules/chrome-remote-interface"
|
14
|
-
]
|
15
|
-
],
|
16
|
-
"_from": "commander@>=2.1.0 <2.2.0",
|
17
|
-
"_id": "commander@2.1.0",
|
18
|
-
"_inCache": true,
|
19
|
-
"_location": "/commander",
|
20
|
-
"_npmUser": {
|
21
|
-
"name": "tjholowaychuk",
|
22
|
-
"email": "tj@vision-media.ca"
|
23
|
-
},
|
24
|
-
"_npmVersion": "1.3.14",
|
25
|
-
"_phantomChildren": {},
|
26
|
-
"_requested": {
|
27
|
-
"raw": "commander@2.1.x",
|
28
|
-
"scope": null,
|
29
|
-
"escapedName": "commander",
|
30
|
-
"name": "commander",
|
31
|
-
"rawSpec": "2.1.x",
|
32
|
-
"spec": ">=2.1.0 <2.2.0",
|
33
|
-
"type": "range"
|
34
|
-
},
|
35
|
-
"_requiredBy": [
|
36
|
-
"/chrome-remote-interface"
|
37
|
-
],
|
38
|
-
"_resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz",
|
39
|
-
"_shasum": "d121bbae860d9992a3d517ba96f56588e47c6781",
|
40
|
-
"_shrinkwrap": null,
|
41
|
-
"_spec": "commander@2.1.x",
|
42
|
-
"_where": "/Users/ilya/Work/opal/lib/opal/cli_runners/node_modules/chrome-remote-interface",
|
43
|
-
"author": {
|
44
|
-
"name": "TJ Holowaychuk",
|
45
|
-
"email": "tj@vision-media.ca"
|
46
|
-
},
|
47
|
-
"bugs": {
|
48
|
-
"url": "https://github.com/visionmedia/commander.js/issues"
|
49
|
-
},
|
50
|
-
"dependencies": {},
|
2
|
+
"name": "commander",
|
3
|
+
"version": "2.11.0",
|
51
4
|
"description": "the complete solution for node.js command-line programs",
|
52
|
-
"devDependencies": {
|
53
|
-
"should": ">= 0.0.1"
|
54
|
-
},
|
55
|
-
"directories": {},
|
56
|
-
"dist": {
|
57
|
-
"shasum": "d121bbae860d9992a3d517ba96f56588e47c6781",
|
58
|
-
"tarball": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz"
|
59
|
-
},
|
60
|
-
"engines": {
|
61
|
-
"node": ">= 0.6.x"
|
62
|
-
},
|
63
|
-
"files": [
|
64
|
-
"index.js"
|
65
|
-
],
|
66
|
-
"homepage": "https://github.com/visionmedia/commander.js",
|
67
5
|
"keywords": [
|
6
|
+
"commander",
|
68
7
|
"command",
|
69
8
|
"option",
|
70
|
-
"parser"
|
71
|
-
"prompt",
|
72
|
-
"stdin"
|
73
|
-
],
|
74
|
-
"main": "index",
|
75
|
-
"maintainers": [
|
76
|
-
{
|
77
|
-
"name": "tjholowaychuk",
|
78
|
-
"email": "tj@vision-media.ca"
|
79
|
-
}
|
9
|
+
"parser"
|
80
10
|
],
|
81
|
-
"
|
82
|
-
"
|
83
|
-
"readme": "ERROR: No README data found!",
|
11
|
+
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
12
|
+
"license": "MIT",
|
84
13
|
"repository": {
|
85
14
|
"type": "git",
|
86
|
-
"url": "
|
15
|
+
"url": "https://github.com/tj/commander.js.git"
|
16
|
+
},
|
17
|
+
"devDependencies": {
|
18
|
+
"should": "^11.2.1",
|
19
|
+
"sinon": "^2.3.5"
|
87
20
|
},
|
88
21
|
"scripts": {
|
89
22
|
"test": "make test"
|
90
23
|
},
|
91
|
-
"
|
24
|
+
"main": "index",
|
25
|
+
"files": [
|
26
|
+
"index.js"
|
27
|
+
],
|
28
|
+
"dependencies": {}
|
92
29
|
}
|
@@ -1,73 +1,131 @@
|
|
1
1
|
# ws: a Node.js WebSocket library
|
2
2
|
|
3
|
-
[](https://www.npmjs.com/package/ws)
|
4
|
-
[](https://www.npmjs.com/package/ws)
|
4
|
+
[](https://github.com/websockets/ws/actions?query=workflow%3ACI+branch%3Amaster)
|
5
|
+
[](https://coveralls.io/github/websockets/ws)
|
6
|
+
|
7
|
+
ws is a simple to use, blazing fast, and thoroughly tested WebSocket client and
|
8
|
+
server implementation.
|
9
|
+
|
10
|
+
Passes the quite extensive Autobahn test suite: [server][server-report],
|
11
|
+
[client][client-report].
|
12
|
+
|
13
|
+
**Note**: This module does not work in the browser. The client in the docs is a
|
14
|
+
reference to a back end with the role of a client in the WebSocket
|
15
|
+
communication. Browser clients must use the native
|
16
|
+
[`WebSocket`](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
17
|
+
object. To make the same code work seamlessly on Node.js and the browser, you
|
18
|
+
can use one of the many wrappers available on npm, like
|
19
|
+
[isomorphic-ws](https://github.com/heineiuo/isomorphic-ws).
|
20
|
+
|
21
|
+
## Table of Contents
|
22
|
+
|
23
|
+
- [Protocol support](#protocol-support)
|
24
|
+
- [Installing](#installing)
|
25
|
+
- [Opt-in for performance](#opt-in-for-performance)
|
26
|
+
- [API docs](#api-docs)
|
27
|
+
- [WebSocket compression](#websocket-compression)
|
28
|
+
- [Usage examples](#usage-examples)
|
29
|
+
- [Sending and receiving text data](#sending-and-receiving-text-data)
|
30
|
+
- [Sending binary data](#sending-binary-data)
|
31
|
+
- [Simple server](#simple-server)
|
32
|
+
- [External HTTP/S server](#external-https-server)
|
33
|
+
- [Multiple servers sharing a single HTTP/S server](#multiple-servers-sharing-a-single-https-server)
|
34
|
+
- [Client authentication](#client-authentication)
|
35
|
+
- [Server broadcast](#server-broadcast)
|
36
|
+
- [echo.websocket.org demo](#echowebsocketorg-demo)
|
37
|
+
- [Use the Node.js streams API](#use-the-nodejs-streams-api)
|
38
|
+
- [Other examples](#other-examples)
|
39
|
+
- [FAQ](#faq)
|
40
|
+
- [How to get the IP address of the client?](#how-to-get-the-ip-address-of-the-client)
|
41
|
+
- [How to detect and close broken connections?](#how-to-detect-and-close-broken-connections)
|
42
|
+
- [How to connect via a proxy?](#how-to-connect-via-a-proxy)
|
43
|
+
- [Changelog](#changelog)
|
44
|
+
- [License](#license)
|
13
45
|
|
14
46
|
## Protocol support
|
15
47
|
|
16
|
-
|
17
|
-
|
48
|
+
- **HyBi drafts 07-12** (Use the option `protocolVersion: 8`)
|
49
|
+
- **HyBi drafts 13-17** (Current default, alternatively option
|
50
|
+
`protocolVersion: 13`)
|
18
51
|
|
19
52
|
## Installing
|
20
53
|
|
21
54
|
```
|
22
|
-
npm install
|
55
|
+
npm install ws
|
23
56
|
```
|
24
57
|
|
25
58
|
### Opt-in for performance
|
26
59
|
|
27
|
-
There are 2 optional modules that can be installed along side with the
|
28
|
-
module. These modules are binary addons which improve certain operations
|
29
|
-
|
30
|
-
compiler
|
60
|
+
There are 2 optional modules that can be installed along side with the ws
|
61
|
+
module. These modules are binary addons which improve certain operations.
|
62
|
+
Prebuilt binaries are available for the most popular platforms so you don't
|
63
|
+
necessarily need to have a C++ compiler installed on your machine.
|
31
64
|
|
32
|
-
- `npm install --save bufferutil`:
|
33
|
-
|
34
|
-
|
35
|
-
- `npm install --save utf-8-validate`:
|
36
|
-
|
37
|
-
hence the need for a binary addon. In most cases you will already be
|
38
|
-
validating the input that you receive for security purposes leading to double
|
39
|
-
validation. But if you want to be 100% spec-conforming and have fast
|
40
|
-
validation of UTF-8 then this module is a must.
|
65
|
+
- `npm install --save-optional bufferutil`: Allows to efficiently perform
|
66
|
+
operations such as masking and unmasking the data payload of the WebSocket
|
67
|
+
frames.
|
68
|
+
- `npm install --save-optional utf-8-validate`: Allows to efficiently check if a
|
69
|
+
message contains valid UTF-8.
|
41
70
|
|
42
|
-
## API
|
71
|
+
## API docs
|
43
72
|
|
44
|
-
See [`/doc/ws.md`](
|
45
|
-
|
73
|
+
See [`/doc/ws.md`](./doc/ws.md) for Node.js-like documentation of ws classes and
|
74
|
+
utility functions.
|
46
75
|
|
47
76
|
## WebSocket compression
|
48
77
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
78
|
+
ws supports the [permessage-deflate extension][permessage-deflate] which enables
|
79
|
+
the client and server to negotiate a compression algorithm and its parameters,
|
80
|
+
and then selectively apply it to the data payloads of each WebSocket message.
|
81
|
+
|
82
|
+
The extension is disabled by default on the server and enabled by default on the
|
83
|
+
client. It adds a significant overhead in terms of performance and memory
|
84
|
+
consumption so we suggest to enable it only if it is really needed.
|
53
85
|
|
54
|
-
|
55
|
-
|
56
|
-
|
86
|
+
Note that Node.js has a variety of issues with high-performance compression,
|
87
|
+
where increased concurrency, especially on Linux, can lead to [catastrophic
|
88
|
+
memory fragmentation][node-zlib-bug] and slow performance. If you intend to use
|
89
|
+
permessage-deflate in production, it is worthwhile to set up a test
|
90
|
+
representative of your workload and ensure Node.js/zlib will handle it with
|
91
|
+
acceptable performance and memory usage.
|
57
92
|
|
58
|
-
|
59
|
-
|
93
|
+
Tuning of permessage-deflate can be done via the options defined below. You can
|
94
|
+
also use `zlibDeflateOptions` and `zlibInflateOptions`, which is passed directly
|
95
|
+
into the creation of [raw deflate/inflate streams][node-zlib-deflaterawdocs].
|
96
|
+
|
97
|
+
See [the docs][ws-server-options] for more options.
|
60
98
|
|
61
99
|
```js
|
62
100
|
const WebSocket = require('ws');
|
63
101
|
|
64
102
|
const wss = new WebSocket.Server({
|
65
|
-
|
66
|
-
|
103
|
+
port: 8080,
|
104
|
+
perMessageDeflate: {
|
105
|
+
zlibDeflateOptions: {
|
106
|
+
// See zlib defaults.
|
107
|
+
chunkSize: 1024,
|
108
|
+
memLevel: 7,
|
109
|
+
level: 3
|
110
|
+
},
|
111
|
+
zlibInflateOptions: {
|
112
|
+
chunkSize: 10 * 1024
|
113
|
+
},
|
114
|
+
// Other options settable:
|
115
|
+
clientNoContextTakeover: true, // Defaults to negotiated value.
|
116
|
+
serverNoContextTakeover: true, // Defaults to negotiated value.
|
117
|
+
serverMaxWindowBits: 10, // Defaults to negotiated value.
|
118
|
+
// Below options specified as default values.
|
119
|
+
concurrencyLimit: 10, // Limits zlib concurrency for perf.
|
120
|
+
threshold: 1024 // Size (in bytes) below which messages
|
121
|
+
// should not be compressed.
|
122
|
+
}
|
67
123
|
});
|
68
124
|
```
|
69
125
|
|
70
|
-
|
126
|
+
The client will only use the extension if it is supported and enabled on the
|
127
|
+
server. To always disable the extension on the client set the
|
128
|
+
`perMessageDeflate` option to `false`.
|
71
129
|
|
72
130
|
```js
|
73
131
|
const WebSocket = require('ws');
|
@@ -90,9 +148,8 @@ ws.on('open', function open() {
|
|
90
148
|
ws.send('something');
|
91
149
|
});
|
92
150
|
|
93
|
-
ws.on('message', function incoming(data
|
94
|
-
|
95
|
-
// flags.masked will be set if the data was masked.
|
151
|
+
ws.on('message', function incoming(data) {
|
152
|
+
console.log(data);
|
96
153
|
});
|
97
154
|
```
|
98
155
|
|
@@ -114,7 +171,7 @@ ws.on('open', function open() {
|
|
114
171
|
});
|
115
172
|
```
|
116
173
|
|
117
|
-
###
|
174
|
+
### Simple server
|
118
175
|
|
119
176
|
```js
|
120
177
|
const WebSocket = require('ws');
|
@@ -130,27 +187,117 @@ wss.on('connection', function connection(ws) {
|
|
130
187
|
});
|
131
188
|
```
|
132
189
|
|
133
|
-
###
|
190
|
+
### External HTTP/S server
|
134
191
|
|
135
192
|
```js
|
193
|
+
const fs = require('fs');
|
194
|
+
const https = require('https');
|
136
195
|
const WebSocket = require('ws');
|
137
196
|
|
138
|
-
const
|
197
|
+
const server = https.createServer({
|
198
|
+
cert: fs.readFileSync('/path/to/cert.pem'),
|
199
|
+
key: fs.readFileSync('/path/to/key.pem')
|
200
|
+
});
|
201
|
+
const wss = new WebSocket.Server({ server });
|
202
|
+
|
203
|
+
wss.on('connection', function connection(ws) {
|
204
|
+
ws.on('message', function incoming(message) {
|
205
|
+
console.log('received: %s', message);
|
206
|
+
});
|
207
|
+
|
208
|
+
ws.send('something');
|
209
|
+
});
|
210
|
+
|
211
|
+
server.listen(8080);
|
212
|
+
```
|
213
|
+
|
214
|
+
### Multiple servers sharing a single HTTP/S server
|
139
215
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
216
|
+
```js
|
217
|
+
const http = require('http');
|
218
|
+
const WebSocket = require('ws');
|
219
|
+
const url = require('url');
|
220
|
+
|
221
|
+
const server = http.createServer();
|
222
|
+
const wss1 = new WebSocket.Server({ noServer: true });
|
223
|
+
const wss2 = new WebSocket.Server({ noServer: true });
|
224
|
+
|
225
|
+
wss1.on('connection', function connection(ws) {
|
226
|
+
// ...
|
227
|
+
});
|
228
|
+
|
229
|
+
wss2.on('connection', function connection(ws) {
|
230
|
+
// ...
|
231
|
+
});
|
232
|
+
|
233
|
+
server.on('upgrade', function upgrade(request, socket, head) {
|
234
|
+
const pathname = url.parse(request.url).pathname;
|
235
|
+
|
236
|
+
if (pathname === '/foo') {
|
237
|
+
wss1.handleUpgrade(request, socket, head, function done(ws) {
|
238
|
+
wss1.emit('connection', ws, request);
|
239
|
+
});
|
240
|
+
} else if (pathname === '/bar') {
|
241
|
+
wss2.handleUpgrade(request, socket, head, function done(ws) {
|
242
|
+
wss2.emit('connection', ws, request);
|
243
|
+
});
|
244
|
+
} else {
|
245
|
+
socket.destroy();
|
246
|
+
}
|
247
|
+
});
|
248
|
+
|
249
|
+
server.listen(8080);
|
250
|
+
```
|
251
|
+
|
252
|
+
### Client authentication
|
253
|
+
|
254
|
+
```js
|
255
|
+
const http = require('http');
|
256
|
+
const WebSocket = require('ws');
|
257
|
+
|
258
|
+
const server = http.createServer();
|
259
|
+
const wss = new WebSocket.Server({ noServer: true });
|
260
|
+
|
261
|
+
wss.on('connection', function connection(ws, request, client) {
|
262
|
+
ws.on('message', function message(msg) {
|
263
|
+
console.log(`Received message ${msg} from user ${client}`);
|
264
|
+
});
|
265
|
+
});
|
266
|
+
|
267
|
+
server.on('upgrade', function upgrade(request, socket, head) {
|
268
|
+
// This function is not defined on purpose. Implement it with your own logic.
|
269
|
+
authenticate(request, (err, client) => {
|
270
|
+
if (err || !client) {
|
271
|
+
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
|
272
|
+
socket.destroy();
|
273
|
+
return;
|
145
274
|
}
|
275
|
+
|
276
|
+
wss.handleUpgrade(request, socket, head, function done(ws) {
|
277
|
+
wss.emit('connection', ws, request, client);
|
278
|
+
});
|
146
279
|
});
|
147
|
-
};
|
280
|
+
});
|
281
|
+
|
282
|
+
server.listen(8080);
|
283
|
+
```
|
284
|
+
|
285
|
+
Also see the provided [example][session-parse-example] using `express-session`.
|
286
|
+
|
287
|
+
### Server broadcast
|
288
|
+
|
289
|
+
A client WebSocket broadcasting to all connected WebSocket clients, including
|
290
|
+
itself.
|
291
|
+
|
292
|
+
```js
|
293
|
+
const WebSocket = require('ws');
|
294
|
+
|
295
|
+
const wss = new WebSocket.Server({ port: 8080 });
|
148
296
|
|
149
297
|
wss.on('connection', function connection(ws) {
|
150
298
|
ws.on('message', function incoming(data) {
|
151
|
-
// Broadcast to everyone else.
|
152
299
|
wss.clients.forEach(function each(client) {
|
153
|
-
if (client
|
300
|
+
if (client.readyState === WebSocket.OPEN) {
|
154
301
|
client.send(data);
|
155
302
|
}
|
156
303
|
});
|
@@ -158,37 +305,22 @@ wss.on('connection', function connection(ws) {
|
|
158
305
|
});
|
159
306
|
```
|
160
307
|
|
161
|
-
|
308
|
+
A client WebSocket broadcasting to every other connected WebSocket clients,
|
309
|
+
excluding itself.
|
162
310
|
|
163
311
|
```js
|
164
|
-
const express = require('express');
|
165
|
-
const http = require('http');
|
166
|
-
const url = require('url');
|
167
312
|
const WebSocket = require('ws');
|
168
313
|
|
169
|
-
const
|
170
|
-
|
171
|
-
app.use(function (req, res) {
|
172
|
-
res.send({ msg: "hello" });
|
173
|
-
});
|
174
|
-
|
175
|
-
const server = http.createServer(app);
|
176
|
-
const wss = new WebSocket.Server({ server });
|
314
|
+
const wss = new WebSocket.Server({ port: 8080 });
|
177
315
|
|
178
316
|
wss.on('connection', function connection(ws) {
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
317
|
+
ws.on('message', function incoming(data) {
|
318
|
+
wss.clients.forEach(function each(client) {
|
319
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
320
|
+
client.send(data);
|
321
|
+
}
|
322
|
+
});
|
185
323
|
});
|
186
|
-
|
187
|
-
ws.send('something');
|
188
|
-
});
|
189
|
-
|
190
|
-
server.listen(8080, function listening() {
|
191
|
-
console.log('Listening on %d', server.address().port);
|
192
324
|
});
|
193
325
|
```
|
194
326
|
|
@@ -210,8 +342,8 @@ ws.on('close', function close() {
|
|
210
342
|
console.log('disconnected');
|
211
343
|
});
|
212
344
|
|
213
|
-
ws.on('message', function incoming(data
|
214
|
-
console.log(`Roundtrip time: ${Date.now() - data} ms
|
345
|
+
ws.on('message', function incoming(data) {
|
346
|
+
console.log(`Roundtrip time: ${Date.now() - data} ms`);
|
215
347
|
|
216
348
|
setTimeout(function timeout() {
|
217
349
|
ws.send(Date.now());
|
@@ -219,6 +351,21 @@ ws.on('message', function incoming(data, flags) {
|
|
219
351
|
});
|
220
352
|
```
|
221
353
|
|
354
|
+
### Use the Node.js streams API
|
355
|
+
|
356
|
+
```js
|
357
|
+
const WebSocket = require('ws');
|
358
|
+
|
359
|
+
const ws = new WebSocket('wss://echo.websocket.org/', {
|
360
|
+
origin: 'https://websocket.org'
|
361
|
+
});
|
362
|
+
|
363
|
+
const duplex = WebSocket.createWebSocketStream(ws, { encoding: 'utf8' });
|
364
|
+
|
365
|
+
duplex.pipe(process.stdout);
|
366
|
+
process.stdin.pipe(duplex);
|
367
|
+
```
|
368
|
+
|
222
369
|
### Other examples
|
223
370
|
|
224
371
|
For a full example with a browser client communicating with a ws server, see the
|
@@ -226,34 +373,123 @@ examples folder.
|
|
226
373
|
|
227
374
|
Otherwise, see the test cases.
|
228
375
|
|
229
|
-
##
|
376
|
+
## FAQ
|
377
|
+
|
378
|
+
### How to get the IP address of the client?
|
379
|
+
|
380
|
+
The remote IP address can be obtained from the raw socket.
|
381
|
+
|
382
|
+
```js
|
383
|
+
const WebSocket = require('ws');
|
384
|
+
|
385
|
+
const wss = new WebSocket.Server({ port: 8080 });
|
386
|
+
|
387
|
+
wss.on('connection', function connection(ws, req) {
|
388
|
+
const ip = req.socket.remoteAddress;
|
389
|
+
});
|
390
|
+
```
|
391
|
+
|
392
|
+
When the server runs behind a proxy like NGINX, the de-facto standard is to use
|
393
|
+
the `X-Forwarded-For` header.
|
230
394
|
|
231
395
|
```js
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
// Errors (both immediate and async write errors) can be detected in an optional
|
236
|
-
// callback. The callback is also the only way of being notified that data has
|
237
|
-
// actually been sent.
|
238
|
-
ws.send('something', function ack(error) {
|
239
|
-
// If error is not defined, the send has been completed, otherwise the error
|
240
|
-
// object will indicate what failed.
|
396
|
+
wss.on('connection', function connection(ws, req) {
|
397
|
+
const ip = req.headers['x-forwarded-for'].split(',')[0].trim();
|
241
398
|
});
|
399
|
+
```
|
400
|
+
|
401
|
+
### How to detect and close broken connections?
|
402
|
+
|
403
|
+
Sometimes the link between the server and the client can be interrupted in a way
|
404
|
+
that keeps both the server and the client unaware of the broken state of the
|
405
|
+
connection (e.g. when pulling the cord).
|
406
|
+
|
407
|
+
In these cases ping messages can be used as a means to verify that the remote
|
408
|
+
endpoint is still responsive.
|
409
|
+
|
410
|
+
```js
|
411
|
+
const WebSocket = require('ws');
|
412
|
+
|
413
|
+
function noop() {}
|
414
|
+
|
415
|
+
function heartbeat() {
|
416
|
+
this.isAlive = true;
|
417
|
+
}
|
418
|
+
|
419
|
+
const wss = new WebSocket.Server({ port: 8080 });
|
420
|
+
|
421
|
+
wss.on('connection', function connection(ws) {
|
422
|
+
ws.isAlive = true;
|
423
|
+
ws.on('pong', heartbeat);
|
424
|
+
});
|
425
|
+
|
426
|
+
const interval = setInterval(function ping() {
|
427
|
+
wss.clients.forEach(function each(ws) {
|
428
|
+
if (ws.isAlive === false) return ws.terminate();
|
242
429
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
430
|
+
ws.isAlive = false;
|
431
|
+
ws.ping(noop);
|
432
|
+
});
|
433
|
+
}, 30000);
|
434
|
+
|
435
|
+
wss.on('close', function close() {
|
436
|
+
clearInterval(interval);
|
437
|
+
});
|
438
|
+
```
|
439
|
+
|
440
|
+
Pong messages are automatically sent in response to ping messages as required by
|
441
|
+
the spec.
|
442
|
+
|
443
|
+
Just like the server example above your clients might as well lose connection
|
444
|
+
without knowing it. You might want to add a ping listener on your clients to
|
445
|
+
prevent that. A simple implementation would be:
|
446
|
+
|
447
|
+
```js
|
448
|
+
const WebSocket = require('ws');
|
449
|
+
|
450
|
+
function heartbeat() {
|
451
|
+
clearTimeout(this.pingTimeout);
|
452
|
+
|
453
|
+
// Use `WebSocket#terminate()`, which immediately destroys the connection,
|
454
|
+
// instead of `WebSocket#close()`, which waits for the close timer.
|
455
|
+
// Delay should be equal to the interval at which your server
|
456
|
+
// sends out pings plus a conservative assumption of the latency.
|
457
|
+
this.pingTimeout = setTimeout(() => {
|
458
|
+
this.terminate();
|
459
|
+
}, 30000 + 1000);
|
460
|
+
}
|
461
|
+
|
462
|
+
const client = new WebSocket('wss://echo.websocket.org/');
|
463
|
+
|
464
|
+
client.on('open', heartbeat);
|
465
|
+
client.on('ping', heartbeat);
|
466
|
+
client.on('close', function clear() {
|
467
|
+
clearTimeout(this.pingTimeout);
|
468
|
+
});
|
248
469
|
```
|
249
470
|
|
471
|
+
### How to connect via a proxy?
|
472
|
+
|
473
|
+
Use a custom `http.Agent` implementation like [https-proxy-agent][] or
|
474
|
+
[socks-proxy-agent][].
|
475
|
+
|
250
476
|
## Changelog
|
251
477
|
|
252
|
-
We're using the GitHub [
|
253
|
-
for changelog entries.
|
478
|
+
We're using the GitHub [releases][changelog] for changelog entries.
|
254
479
|
|
255
480
|
## License
|
256
481
|
|
257
482
|
[MIT](LICENSE)
|
258
483
|
|
484
|
+
[changelog]: https://github.com/websockets/ws/releases
|
485
|
+
[client-report]: http://websockets.github.io/ws/autobahn/clients/
|
486
|
+
[https-proxy-agent]: https://github.com/TooTallNate/node-https-proxy-agent
|
487
|
+
[node-zlib-bug]: https://github.com/nodejs/node/issues/8871
|
488
|
+
[node-zlib-deflaterawdocs]:
|
489
|
+
https://nodejs.org/api/zlib.html#zlib_zlib_createdeflateraw_options
|
259
490
|
[permessage-deflate]: https://tools.ietf.org/html/rfc7692
|
491
|
+
[server-report]: http://websockets.github.io/ws/autobahn/servers/
|
492
|
+
[session-parse-example]: ./examples/express-session-parse
|
493
|
+
[socks-proxy-agent]: https://github.com/TooTallNate/node-socks-proxy-agent
|
494
|
+
[ws-server-options]:
|
495
|
+
https://github.com/websockets/ws/blob/master/doc/ws.md#new-websocketserveroptions-callback
|
@@ -1,15 +1,10 @@
|
|
1
|
-
/*!
|
2
|
-
* ws: a node.js websocket client
|
3
|
-
* Copyright(c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|
4
|
-
* MIT Licensed
|
5
|
-
*/
|
6
|
-
|
7
1
|
'use strict';
|
8
2
|
|
9
|
-
const WebSocket = require('./lib/
|
3
|
+
const WebSocket = require('./lib/websocket');
|
10
4
|
|
11
|
-
WebSocket.
|
12
|
-
WebSocket.
|
13
|
-
WebSocket.
|
5
|
+
WebSocket.createWebSocketStream = require('./lib/stream');
|
6
|
+
WebSocket.Server = require('./lib/websocket-server');
|
7
|
+
WebSocket.Receiver = require('./lib/receiver');
|
8
|
+
WebSocket.Sender = require('./lib/sender');
|
14
9
|
|
15
10
|
module.exports = WebSocket;
|