bare-ws 1.3.1 → 2.0.1
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.
- package/lib/constants.js +1 -1
- package/lib/errors.js +155 -65
- package/lib/frame.js +8 -6
- package/lib/server.js +30 -17
- package/lib/socket.js +54 -35
- package/package.json +7 -6
package/lib/constants.js
CHANGED
package/lib/errors.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const { status } = require('./constants')
|
|
2
2
|
|
|
3
3
|
module.exports = class WebSocketError extends Error {
|
|
4
|
-
constructor
|
|
5
|
-
super(`${code}: ${msg}
|
|
4
|
+
constructor(msg, code, status, fn = WebSocketError, cause) {
|
|
5
|
+
super(`${code}: ${msg}`, { cause })
|
|
6
6
|
this.code = code
|
|
7
7
|
this.status = status
|
|
8
8
|
|
|
@@ -11,71 +11,161 @@ module.exports = class WebSocketError extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
get name
|
|
14
|
+
get name() {
|
|
15
15
|
return 'WebSocketError'
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
static
|
|
19
|
-
return new WebSocketError(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
static
|
|
47
|
-
return new WebSocketError(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
18
|
+
static NETWORK_ERROR(msg, cause) {
|
|
19
|
+
return new WebSocketError(
|
|
20
|
+
msg,
|
|
21
|
+
'NETWORK_ERROR',
|
|
22
|
+
0,
|
|
23
|
+
WebSocketError.NETWORK_ERROR,
|
|
24
|
+
cause
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static NOT_CONNECTED(msg = 'Socket is not connected') {
|
|
29
|
+
return new WebSocketError(
|
|
30
|
+
msg,
|
|
31
|
+
'NOT_CONNECTED',
|
|
32
|
+
0,
|
|
33
|
+
WebSocketError.NOT_CONNECTED
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
static UNEXPECTED_RSV1(msg = 'RSV1 must be unset') {
|
|
38
|
+
return new WebSocketError(
|
|
39
|
+
msg,
|
|
40
|
+
'UNEXPECTED_RSV1',
|
|
41
|
+
status.PROTOCOL_ERROR,
|
|
42
|
+
WebSocketError.UNEXPECTED_RSV1
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static UNEXPECTED_RSV2(msg = 'RSV2 must be unset') {
|
|
47
|
+
return new WebSocketError(
|
|
48
|
+
msg,
|
|
49
|
+
'UNEXPECTED_RSV2',
|
|
50
|
+
status.PROTOCOL_ERROR,
|
|
51
|
+
WebSocketError.UNEXPECTED_RSV2
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static UNEXPECTED_RSV3(msg = 'RSV3 must be unset') {
|
|
56
|
+
return new WebSocketError(
|
|
57
|
+
msg,
|
|
58
|
+
'UNEXPECTED_RSV3',
|
|
59
|
+
status.PROTOCOL_ERROR,
|
|
60
|
+
WebSocketError.UNEXPECTED_RSV3
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static EXPECTED_MASK(msg = 'MASK must be set') {
|
|
65
|
+
return new WebSocketError(
|
|
66
|
+
msg,
|
|
67
|
+
'EXPECTED_MASK',
|
|
68
|
+
status.PROTOCOL_ERROR,
|
|
69
|
+
WebSocketError.EXPECTED_MASK
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
static UNEXPECTED_MASK(msg = 'MASK must be unset') {
|
|
74
|
+
return new WebSocketError(
|
|
75
|
+
msg,
|
|
76
|
+
'UNEXPECTED_MASK',
|
|
77
|
+
status.PROTOCOL_ERROR,
|
|
78
|
+
WebSocketError.UNEXPECTED_MASK
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static EXPECTED_CONTINUATION(msg = 'Expected a continuation frame') {
|
|
83
|
+
return new WebSocketError(
|
|
84
|
+
msg,
|
|
85
|
+
'EXPECTED_CONTINUATION',
|
|
86
|
+
status.PROTOCOL_ERROR,
|
|
87
|
+
WebSocketError.EXPECTED_CONTINUATION
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static UNEXPECTED_CONTINUATION(msg = 'Unexpected continuation frame') {
|
|
92
|
+
return new WebSocketError(
|
|
93
|
+
msg,
|
|
94
|
+
'UNEXPECTED_CONTINUATION',
|
|
95
|
+
status.PROTOCOL_ERROR,
|
|
96
|
+
WebSocketError.UNEXPECTED_CONTINUATION
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
static UNEXPECTED_CONTROL(msg = 'Unexpected control frame') {
|
|
101
|
+
return new WebSocketError(
|
|
102
|
+
msg,
|
|
103
|
+
'UNEXPECTED_CONTROL',
|
|
104
|
+
status.PROTOCOL_ERROR,
|
|
105
|
+
WebSocketError.UNEXPECTED_CONTROL
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static INVALID_ENCODING(msg = 'Invalid encoding') {
|
|
110
|
+
return new WebSocketError(
|
|
111
|
+
msg,
|
|
112
|
+
'INVALID_ENCODING',
|
|
113
|
+
status.PROTOCOL_ERROR,
|
|
114
|
+
WebSocketError.INVALID_ENCODING
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static INVALID_UPGRADE_HEADER(msg = 'Invalid Upgrade header') {
|
|
119
|
+
return new WebSocketError(
|
|
120
|
+
msg,
|
|
121
|
+
'INVALID_UPGRADE_HEADER',
|
|
122
|
+
status.PROTOCOL_ERROR,
|
|
123
|
+
WebSocketError.INVALID_UPGRADE_HEADER
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
static INVALID_VERSION_HEADER(msg = 'Invalid Sec-WebSocket-Version header') {
|
|
128
|
+
return new WebSocketError(
|
|
129
|
+
msg,
|
|
130
|
+
'INVALID_VERSION_HEADER',
|
|
131
|
+
status.PROTOCOL_ERROR,
|
|
132
|
+
WebSocketError.INVALID_VERSION_HEADER
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static INVALID_KEY_HEADER(msg = 'Invalid Sec-WebSocket-Key header') {
|
|
137
|
+
return new WebSocketError(
|
|
138
|
+
msg,
|
|
139
|
+
'INVALID_KEY_HEADER',
|
|
140
|
+
status.PROTOCOL_ERROR,
|
|
141
|
+
WebSocketError.INVALID_KEY_HEADER
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
static INVALID_ACCEPT_HEADER(msg = 'Invalid Sec-WebSocket-Accept header') {
|
|
146
|
+
return new WebSocketError(
|
|
147
|
+
msg,
|
|
148
|
+
'INVALID_ACCEPT_HEADER',
|
|
149
|
+
status.PROTOCOL_ERROR,
|
|
150
|
+
WebSocketError.INVALID_ACCEPT_HEADER
|
|
151
|
+
)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static INVALID_OPCODE(msg = 'Invalid opcode') {
|
|
155
|
+
return new WebSocketError(
|
|
156
|
+
msg,
|
|
157
|
+
'INVALID_OPCODE',
|
|
158
|
+
status.PROTOCOL_ERROR,
|
|
159
|
+
WebSocketError.INVALID_OPCODE
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
static INVALID_PAYLOAD_LENGTH(msg = 'Invalid payload length') {
|
|
164
|
+
return new WebSocketError(
|
|
165
|
+
msg,
|
|
166
|
+
'INVALID_PAYLOAD_LENGTH',
|
|
167
|
+
status.MESSAGE_TOO_LARGE,
|
|
168
|
+
WebSocketError.INVALID_PAYLOAD_LENGTH
|
|
169
|
+
)
|
|
80
170
|
}
|
|
81
171
|
}
|
package/lib/frame.js
CHANGED
|
@@ -12,8 +12,8 @@ const MASK = 0b10000000
|
|
|
12
12
|
const LENGTH = 0b01111111
|
|
13
13
|
|
|
14
14
|
// https://datatracker.ietf.org/doc/html/rfc6455#section-5
|
|
15
|
-
|
|
16
|
-
constructor
|
|
15
|
+
module.exports = exports = class Frame {
|
|
16
|
+
constructor(opcode, payload = EMPTY, opts = {}) {
|
|
17
17
|
if (payload && !Buffer.isBuffer(payload)) {
|
|
18
18
|
opts = payload
|
|
19
19
|
payload = EMPTY
|
|
@@ -36,7 +36,7 @@ const Frame = module.exports = exports = class Frame {
|
|
|
36
36
|
this.payload = payload
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
toBuffer
|
|
39
|
+
toBuffer() {
|
|
40
40
|
const state = { start: 0, end: 0, buffer: null }
|
|
41
41
|
|
|
42
42
|
Frame.preencode(state, this)
|
|
@@ -49,7 +49,9 @@ const Frame = module.exports = exports = class Frame {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
const Frame = exports
|
|
53
|
+
|
|
54
|
+
exports.preencode = function preencode(state, f) {
|
|
53
55
|
let i = state.end
|
|
54
56
|
|
|
55
57
|
i++
|
|
@@ -69,7 +71,7 @@ exports.preencode = function preencode (state, f) {
|
|
|
69
71
|
state.end = i
|
|
70
72
|
}
|
|
71
73
|
|
|
72
|
-
exports.encode = function encode
|
|
74
|
+
exports.encode = function encode(state, f) {
|
|
73
75
|
const b = state.buffer
|
|
74
76
|
|
|
75
77
|
let i = state.start
|
|
@@ -133,7 +135,7 @@ exports.encode = function encode (state, f) {
|
|
|
133
135
|
state.start = i
|
|
134
136
|
}
|
|
135
137
|
|
|
136
|
-
exports.decode = function decode
|
|
138
|
+
exports.decode = function decode(state) {
|
|
137
139
|
const b = state.buffer
|
|
138
140
|
|
|
139
141
|
let i = state.start
|
package/lib/server.js
CHANGED
|
@@ -11,7 +11,7 @@ const EMPTY = Buffer.alloc(0)
|
|
|
11
11
|
const KEY = /^[+/0-9A-Za-z]{22}==$/
|
|
12
12
|
|
|
13
13
|
module.exports = exports = class WebSocketServer extends EventEmitter {
|
|
14
|
-
constructor
|
|
14
|
+
constructor(opts = {}, onconnection) {
|
|
15
15
|
if (typeof opts === 'function') {
|
|
16
16
|
onconnection = opts
|
|
17
17
|
opts = {}
|
|
@@ -22,7 +22,10 @@ module.exports = exports = class WebSocketServer extends EventEmitter {
|
|
|
22
22
|
const createServer = opts.secure ? https.createServer : http.createServer
|
|
23
23
|
|
|
24
24
|
const {
|
|
25
|
-
server = createServer(opts, this._onrequest.bind(this)).listen(
|
|
25
|
+
server = createServer(opts, this._onrequest.bind(this)).listen(
|
|
26
|
+
opts,
|
|
27
|
+
this._onlistening.bind(this)
|
|
28
|
+
)
|
|
26
29
|
} = opts
|
|
27
30
|
|
|
28
31
|
this._server = server
|
|
@@ -32,31 +35,31 @@ module.exports = exports = class WebSocketServer extends EventEmitter {
|
|
|
32
35
|
if (onconnection) this.on('connection', onconnection)
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
get listening
|
|
38
|
+
get listening() {
|
|
36
39
|
return this._server.listening
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
address
|
|
42
|
+
address() {
|
|
40
43
|
return this._server.address()
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
close
|
|
46
|
+
close(cb) {
|
|
44
47
|
return this._server.close(cb)
|
|
45
48
|
}
|
|
46
49
|
|
|
47
|
-
ref
|
|
50
|
+
ref() {
|
|
48
51
|
this._server.ref()
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
unref
|
|
54
|
+
unref() {
|
|
52
55
|
this._server.unref()
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
_onlistening
|
|
58
|
+
_onlistening() {
|
|
56
59
|
this.emit('listening')
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
_onrequest
|
|
62
|
+
_onrequest(req, res) {
|
|
60
63
|
const body = http.constants.status[426]
|
|
61
64
|
|
|
62
65
|
res.writeHead(426, {
|
|
@@ -67,7 +70,7 @@ module.exports = exports = class WebSocketServer extends EventEmitter {
|
|
|
67
70
|
res.end(body)
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
_onupgrade
|
|
73
|
+
_onupgrade(req, socket, head) {
|
|
71
74
|
handshake(req, socket, head, (err) => {
|
|
72
75
|
if (err) return socket.destroy(err)
|
|
73
76
|
|
|
@@ -77,7 +80,12 @@ module.exports = exports = class WebSocketServer extends EventEmitter {
|
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
// https://datatracker.ietf.org/doc/html/rfc6455#section-4.2
|
|
80
|
-
const handshake = exports.handshake = function handshake
|
|
83
|
+
const handshake = (exports.handshake = function handshake(
|
|
84
|
+
req,
|
|
85
|
+
socket = req.socket,
|
|
86
|
+
head = EMPTY,
|
|
87
|
+
cb
|
|
88
|
+
) {
|
|
81
89
|
if (typeof socket === 'function') {
|
|
82
90
|
cb = socket
|
|
83
91
|
socket = req.socket
|
|
@@ -103,19 +111,24 @@ const handshake = exports.handshake = function handshake (req, socket = req.sock
|
|
|
103
111
|
return cb(errors.INVALID_KEY_HEADER())
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
const digest = crypto
|
|
114
|
+
const digest = crypto
|
|
115
|
+
.createHash('sha1')
|
|
107
116
|
.update(key)
|
|
108
117
|
.update(GUID)
|
|
109
118
|
.digest('base64')
|
|
110
119
|
|
|
111
120
|
socket.write(
|
|
112
|
-
'HTTP/1.1 101 Web Socket Protocol Handshake' +
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
'HTTP/1.1 101 Web Socket Protocol Handshake' +
|
|
122
|
+
EOL +
|
|
123
|
+
'Upgrade: WebSocket' +
|
|
124
|
+
EOL +
|
|
125
|
+
'Connection: Upgrade' +
|
|
126
|
+
EOL +
|
|
127
|
+
`Sec-WebSocket-Accept: ${digest}` +
|
|
128
|
+
EOF
|
|
116
129
|
)
|
|
117
130
|
|
|
118
131
|
if (head.byteLength) socket.unshift(head)
|
|
119
132
|
|
|
120
133
|
cb(null)
|
|
121
|
-
}
|
|
134
|
+
})
|
package/lib/socket.js
CHANGED
|
@@ -9,7 +9,7 @@ const Frame = require('./frame')
|
|
|
9
9
|
const CLOSE = new Frame(opcode.CLOSE).toBuffer()
|
|
10
10
|
|
|
11
11
|
module.exports = exports = class WebSocket extends Duplex {
|
|
12
|
-
constructor
|
|
12
|
+
constructor(url, opts = {}) {
|
|
13
13
|
if (typeof url === 'string') url = new URL(url)
|
|
14
14
|
|
|
15
15
|
if (isURL(url)) {
|
|
@@ -24,13 +24,11 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
24
24
|
|
|
25
25
|
// For Node.js compatibility
|
|
26
26
|
opts.host = opts.hostname || opts.host
|
|
27
|
-
opts.port =
|
|
27
|
+
opts.port =
|
|
28
|
+
typeof opts.port === 'string' ? parseInt(opts.port, 10) : opts.port
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
const {
|
|
31
|
-
isServer = false,
|
|
32
|
-
socket = null
|
|
33
|
-
} = opts
|
|
31
|
+
const { isServer = false, socket = null } = opts
|
|
34
32
|
|
|
35
33
|
super({ eagerOpen: true })
|
|
36
34
|
|
|
@@ -48,23 +46,27 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
48
46
|
else this._connect(opts)
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
ping
|
|
49
|
+
ping(data) {
|
|
52
50
|
if (this._socket === null) throw errors.NOT_CONNECTED()
|
|
53
51
|
|
|
54
52
|
if (typeof data === 'string') data = Buffer.from(data)
|
|
55
53
|
|
|
56
|
-
this._socket.write(
|
|
54
|
+
this._socket.write(
|
|
55
|
+
new Frame(opcode.PING, data, { mask: this._mask }).toBuffer()
|
|
56
|
+
)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
pong
|
|
59
|
+
pong(data) {
|
|
60
60
|
if (this._socket === null) throw errors.NOT_CONNECTED()
|
|
61
61
|
|
|
62
62
|
if (typeof data === 'string') data = Buffer.from(data)
|
|
63
63
|
|
|
64
|
-
this._socket.write(
|
|
64
|
+
this._socket.write(
|
|
65
|
+
new Frame(opcode.PONG, data, { mask: this._mask }).toBuffer()
|
|
66
|
+
)
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
_attach
|
|
69
|
+
_attach(socket) {
|
|
68
70
|
this._socket = socket
|
|
69
71
|
|
|
70
72
|
this._socket
|
|
@@ -74,7 +76,7 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
74
76
|
.on('drain', this._ondrain.bind(this))
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
_connect
|
|
79
|
+
_connect(opts) {
|
|
78
80
|
const request = opts.secure ? https.request : http.request
|
|
79
81
|
|
|
80
82
|
const req = request(opts)
|
|
@@ -90,15 +92,15 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
90
92
|
})
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
_onerror
|
|
95
|
+
_onerror(err) {
|
|
94
96
|
this.destroy(err)
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
_onclose
|
|
99
|
+
_onclose() {
|
|
98
100
|
this.destroy()
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
_ondata
|
|
103
|
+
_ondata(data) {
|
|
102
104
|
if (this._buffer === null) this._buffer = data
|
|
103
105
|
else this._buffer = Buffer.concat([this._buffer, data])
|
|
104
106
|
|
|
@@ -114,7 +116,8 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
114
116
|
|
|
115
117
|
if (frame === null) return
|
|
116
118
|
|
|
117
|
-
this._buffer =
|
|
119
|
+
this._buffer =
|
|
120
|
+
state.start === state.end ? null : this._buffer.subarray(state.start)
|
|
118
121
|
|
|
119
122
|
try {
|
|
120
123
|
this._onframe(frame)
|
|
@@ -124,7 +127,7 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
124
127
|
}
|
|
125
128
|
}
|
|
126
129
|
|
|
127
|
-
_onframe
|
|
130
|
+
_onframe(frame) {
|
|
128
131
|
if (frame.rsv1) throw errors.UNEXPECTED_RSV1()
|
|
129
132
|
|
|
130
133
|
if (frame.rsv2) throw errors.UNEXPECTED_RSV2()
|
|
@@ -136,15 +139,18 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
if (frame.fin === false) {
|
|
139
|
-
if (this._fragments.push(frame) === 1) {
|
|
140
|
-
|
|
142
|
+
if (this._fragments.push(frame) === 1) {
|
|
143
|
+
// First frame
|
|
144
|
+
if (frame.opcode === opcode.CONTINUATION)
|
|
145
|
+
throw errors.UNEXPECTED_CONTINUATION()
|
|
141
146
|
|
|
142
147
|
if (frame.opcode >= opcode.CLOSE) throw errors.UNEXPECTED_CONTROL()
|
|
143
148
|
|
|
144
149
|
return
|
|
145
150
|
}
|
|
146
151
|
|
|
147
|
-
if (frame.opcode !== opcode.CONTINUATION)
|
|
152
|
+
if (frame.opcode !== opcode.CONTINUATION)
|
|
153
|
+
throw errors.EXPECTED_CONTINUATION()
|
|
148
154
|
|
|
149
155
|
return
|
|
150
156
|
}
|
|
@@ -169,7 +175,7 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
169
175
|
|
|
170
176
|
frame.opcode = this._fragments[0].opcode
|
|
171
177
|
|
|
172
|
-
const payloads = this._fragments.map(frame => frame.payload)
|
|
178
|
+
const payloads = this._fragments.map((frame) => frame.payload)
|
|
173
179
|
|
|
174
180
|
payloads.push(frame.payload)
|
|
175
181
|
|
|
@@ -195,41 +201,46 @@ module.exports = exports = class WebSocket extends Duplex {
|
|
|
195
201
|
}
|
|
196
202
|
}
|
|
197
203
|
|
|
198
|
-
_ondrain
|
|
204
|
+
_ondrain() {
|
|
199
205
|
if (this._pendingWrite === null) return
|
|
200
206
|
const cb = this._pendingWrite
|
|
201
207
|
this._pendingWrite = null
|
|
202
208
|
cb(null)
|
|
203
209
|
}
|
|
204
210
|
|
|
205
|
-
_open
|
|
211
|
+
_open(cb) {
|
|
206
212
|
if (this._socket === null) this._pendingOpen = cb
|
|
207
213
|
else cb(null)
|
|
208
214
|
}
|
|
209
215
|
|
|
210
|
-
_write
|
|
216
|
+
_write(data, encoding, cb) {
|
|
211
217
|
if (encoding !== 'buffer' && encoding !== 'utf8') {
|
|
212
218
|
return cb(errors.INVALID_ENCODING())
|
|
213
219
|
}
|
|
214
220
|
|
|
215
|
-
const frame = new Frame(
|
|
221
|
+
const frame = new Frame(
|
|
222
|
+
encoding === 'buffer' ? opcode.BINARY : opcode.TEXT,
|
|
223
|
+
data,
|
|
224
|
+
{ mask: this._mask }
|
|
225
|
+
)
|
|
216
226
|
|
|
217
227
|
if (this._socket.write(frame.toBuffer())) cb(null)
|
|
218
228
|
else this._pendingWrite = cb
|
|
219
229
|
}
|
|
220
230
|
|
|
221
|
-
_final
|
|
231
|
+
_final(cb) {
|
|
222
232
|
this._socket.end(CLOSE)
|
|
223
233
|
cb(null)
|
|
224
234
|
}
|
|
225
235
|
|
|
226
|
-
_predestroy
|
|
236
|
+
_predestroy() {
|
|
237
|
+
if (!this._socket) return
|
|
227
238
|
this._socket.destroy()
|
|
228
239
|
}
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
// https://datatracker.ietf.org/doc/html/rfc6455#section-4.1
|
|
232
|
-
const handshake = exports.handshake = function handshake
|
|
243
|
+
const handshake = (exports.handshake = function handshake(req, cb) {
|
|
233
244
|
const key = crypto.randomBytes(16).toString('base64')
|
|
234
245
|
|
|
235
246
|
req.headers = {
|
|
@@ -245,7 +256,8 @@ const handshake = exports.handshake = function handshake (req, cb) {
|
|
|
245
256
|
return cb(errors.INVALID_UPGRADE_HEADER())
|
|
246
257
|
}
|
|
247
258
|
|
|
248
|
-
const digest = crypto
|
|
259
|
+
const digest = crypto
|
|
260
|
+
.createHash('sha1')
|
|
249
261
|
.update(key)
|
|
250
262
|
.update(GUID)
|
|
251
263
|
.digest('base64')
|
|
@@ -259,24 +271,31 @@ const handshake = exports.handshake = function handshake (req, cb) {
|
|
|
259
271
|
cb(null)
|
|
260
272
|
})
|
|
261
273
|
|
|
274
|
+
req.on('error', (err) => {
|
|
275
|
+
cb(errors.NETWORK_ERROR('Network error', err))
|
|
276
|
+
})
|
|
277
|
+
|
|
262
278
|
req.end()
|
|
263
|
-
}
|
|
279
|
+
})
|
|
264
280
|
|
|
265
281
|
// https://url.spec.whatwg.org/#default-port
|
|
266
|
-
function defaultPort
|
|
282
|
+
function defaultPort(url) {
|
|
267
283
|
switch (url.protocol) {
|
|
268
|
-
case 'ftp:':
|
|
284
|
+
case 'ftp:':
|
|
285
|
+
return 21
|
|
269
286
|
case 'http:':
|
|
270
|
-
case 'ws:':
|
|
287
|
+
case 'ws:':
|
|
288
|
+
return 80
|
|
271
289
|
case 'https:':
|
|
272
|
-
case 'wss:':
|
|
290
|
+
case 'wss:':
|
|
291
|
+
return 443
|
|
273
292
|
}
|
|
274
293
|
|
|
275
294
|
return null
|
|
276
295
|
}
|
|
277
296
|
|
|
278
297
|
// https://url.spec.whatwg.org/#api
|
|
279
|
-
function isURL
|
|
298
|
+
function isURL(url) {
|
|
280
299
|
return (
|
|
281
300
|
url !== null &&
|
|
282
301
|
typeof url === 'object' &&
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-ws",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "WebSocket library for JavaScript",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"lib"
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
|
-
"test": "
|
|
16
|
+
"test": "prettier . --check && bare test.js"
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
@@ -28,13 +28,14 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"bare-crypto": "^1.2.0",
|
|
30
30
|
"bare-events": "^2.3.1",
|
|
31
|
-
"bare-http1": "^
|
|
32
|
-
"bare-https": "^
|
|
31
|
+
"bare-http1": "^4.0.0",
|
|
32
|
+
"bare-https": "^2.0.0",
|
|
33
33
|
"bare-stream": "^2.1.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"bare-fs": "^
|
|
36
|
+
"bare-fs": "^4.0.0",
|
|
37
37
|
"brittle": "^3.3.0",
|
|
38
|
-
"
|
|
38
|
+
"prettier": "^3.4.1",
|
|
39
|
+
"prettier-config-standard": "^7.0.0"
|
|
39
40
|
}
|
|
40
41
|
}
|