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