@fails-components/webtransport 1.0.10 → 1.1.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/dist/lib/http2/browser/browser.d.ts.map +1 -1
- package/dist/lib/http2/browser/browserparser.d.ts +4 -3
- package/dist/lib/http2/browser/browserparser.d.ts.map +1 -1
- package/dist/lib/http2/node/capsuleparser.d.ts +3 -2
- package/dist/lib/http2/node/capsuleparser.d.ts.map +1 -1
- package/dist/lib/http2/node/client.d.ts.map +1 -1
- package/dist/lib/http2/node/server.d.ts +4 -4
- package/dist/lib/http2/node/server.d.ts.map +1 -1
- package/dist/lib/http2/node/websocketparser.d.ts +7 -6
- package/dist/lib/http2/node/websocketparser.d.ts.map +1 -1
- package/dist/lib/http2/parserbase.d.ts +16 -4
- package/dist/lib/http2/parserbase.d.ts.map +1 -1
- package/dist/lib/http2/parserbasehttp2.d.ts +9 -1
- package/dist/lib/http2/parserbasehttp2.d.ts.map +1 -1
- package/dist/lib/http2/session.d.ts.map +1 -1
- package/dist/lib/index.node.d.ts +1 -1
- package/dist/lib/index.node.d.ts.map +1 -1
- package/dist/lib/index.types.d.ts +1 -1
- package/dist/lib/index.types.d.ts.map +1 -1
- package/dist/lib/server.d.ts.map +1 -1
- package/dist/lib/session.d.ts +8 -4
- package/dist/lib/session.d.ts.map +1 -1
- package/dist/lib/types.d.ts +18 -6
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/webtransport.browser.d.ts.map +1 -1
- package/dist/lib/webtransport.node.d.ts +1 -0
- package/dist/lib/webtransport.node.d.ts.map +1 -1
- package/lib/http2/browser/browser.js +2 -1
- package/lib/http2/browser/browserparser.js +43 -7
- package/lib/http2/node/capsuleparser.js +41 -10
- package/lib/http2/node/client.js +28 -7
- package/lib/http2/node/server.js +117 -55
- package/lib/http2/node/websocketparser.js +42 -20
- package/lib/http2/parserbase.js +44 -7
- package/lib/http2/parserbasehttp2.js +15 -2
- package/lib/http2/session.js +1 -5
- package/lib/index.node.js +1 -1
- package/lib/index.types.js +1 -1
- package/lib/server.js +7 -4
- package/lib/session.js +5 -2
- package/lib/types.ts +19 -6
- package/lib/webtransport.browser.js +3 -2
- package/lib/webtransport.node.js +16 -5
- package/lib/webtransportbase.js +1 -1
- package/old_test/test.js +13 -1
- package/package.json +3 -2
- package/readme.md +4 -1
- package/test/bidirectional-streams.spec.js +31 -0
- package/test/datagrams.spec.js +12 -0
- package/test/fixtures/quiche.browser.js +2 -0
- package/test/fixtures/quiche.js +1 -0
- package/test/fixtures/read-stream.js +20 -7
- package/test/fixtures/reader-value.js +0 -5
- package/test/fixtures/server.js +70 -0
- package/test/session.spec.js +29 -3
- package/test/stream-limits.spec.js +12 -0
- package/test/unidirectional-streams.spec.js +12 -0
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ParserBaseHttp2,
|
|
3
|
+
readVarInt,
|
|
4
|
+
readUint32,
|
|
5
|
+
writeVarInt
|
|
6
|
+
} from '../parserbasehttp2.js'
|
|
2
7
|
import { lengthVarInt } from '../parserbase.js'
|
|
3
8
|
|
|
4
9
|
export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
@@ -9,7 +14,8 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
9
14
|
stream,
|
|
10
15
|
nativesession,
|
|
11
16
|
isclient,
|
|
12
|
-
|
|
17
|
+
initialStreamSendWindowOffsetBidi,
|
|
18
|
+
initialStreamSendWindowOffsetUnidi,
|
|
13
19
|
initialStreamReceiveWindowOffset,
|
|
14
20
|
streamShouldAutoTuneReceiveWindow,
|
|
15
21
|
streamReceiveWindowSizeLimit
|
|
@@ -18,7 +24,8 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
18
24
|
stream,
|
|
19
25
|
nativesession,
|
|
20
26
|
isclient,
|
|
21
|
-
|
|
27
|
+
initialStreamSendWindowOffsetBidi,
|
|
28
|
+
initialStreamSendWindowOffsetUnidi,
|
|
22
29
|
initialStreamReceiveWindowOffset,
|
|
23
30
|
streamShouldAutoTuneReceiveWindow,
|
|
24
31
|
streamReceiveWindowSizeLimit
|
|
@@ -211,6 +218,23 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
211
218
|
case Http2CapsuleParser.WT_STREAMS_BLOCKED_BIDI:
|
|
212
219
|
this.onStreamsBlockedBidi(readVarInt(bufferstate))
|
|
213
220
|
break
|
|
221
|
+
case Http2CapsuleParser.CLOSE_WEBTRANSPORT_SESSION:
|
|
222
|
+
{
|
|
223
|
+
const code = readUint32(bufferstate) || 0
|
|
224
|
+
const decoder = new TextDecoder()
|
|
225
|
+
const reason = decoder.decode(
|
|
226
|
+
new Uint8Array(
|
|
227
|
+
bufferstate.buffer.buffer,
|
|
228
|
+
bufferstate.buffer.byteOffset + bufferstate.offset,
|
|
229
|
+
offsetend - bufferstate.offset
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
this.onCloseWebTransportSession({ code, reason })
|
|
233
|
+
}
|
|
234
|
+
break
|
|
235
|
+
case Http2CapsuleParser.DRAIN_WEBTRANSPORT_SESSION:
|
|
236
|
+
this.onDrain()
|
|
237
|
+
break
|
|
214
238
|
case Http2CapsuleParser.DATAGRAM:
|
|
215
239
|
this.session.jsobj.onDatagramReceived({
|
|
216
240
|
datagram: new Uint8Array(
|
|
@@ -275,9 +299,9 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
275
299
|
}
|
|
276
300
|
|
|
277
301
|
/**
|
|
278
|
-
* @param{{type: Number, headerVints: Array<Number>, payload: Uint8Array|undefined}} bs
|
|
302
|
+
* @param{{type: Number, headerVints: Array<Number>, payload: Uint8Array|undefined, end?: () => void}} bs
|
|
279
303
|
*/
|
|
280
|
-
writeCapsule({ type, headerVints, payload }) {
|
|
304
|
+
writeCapsule({ type, headerVints, payload, end }) {
|
|
281
305
|
let length = 0
|
|
282
306
|
for (const ind in headerVints) length += lengthVarInt(headerVints[ind])
|
|
283
307
|
let headlength = length
|
|
@@ -289,11 +313,18 @@ export class Http2CapsuleParser extends ParserBaseHttp2 {
|
|
|
289
313
|
writeVarInt(bufferstate, length)
|
|
290
314
|
for (const ind in headerVints) writeVarInt(bufferstate, headerVints[ind])
|
|
291
315
|
if (!this.stream) return
|
|
292
|
-
|
|
293
|
-
if (
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
316
|
+
// note it might be illegal to split the write
|
|
317
|
+
if (!end) {
|
|
318
|
+
let blocked = !this.stream.write(cdata)
|
|
319
|
+
if (payload) blocked = !this.stream.write(payload) || blocked
|
|
320
|
+
// do something if blocked
|
|
321
|
+
if (blocked) this.blocked = true
|
|
322
|
+
return blocked
|
|
323
|
+
} else {
|
|
324
|
+
if (!payload) this.stream.end(cdata, end)
|
|
325
|
+
else this.stream.write(cdata)
|
|
326
|
+
if (payload) this.stream.end(payload, end)
|
|
327
|
+
}
|
|
297
328
|
}
|
|
298
329
|
|
|
299
330
|
/**
|
package/lib/http2/node/client.js
CHANGED
|
@@ -44,9 +44,10 @@ export class Http2WebTransportClient {
|
|
|
44
44
|
|
|
45
45
|
createTransport() {
|
|
46
46
|
/**
|
|
47
|
+
* @param {string} hostname
|
|
47
48
|
* @param {import('node:tls').PeerCertificate} cert
|
|
48
49
|
* */
|
|
49
|
-
const webTransportVerifier = (cert) => {
|
|
50
|
+
const webTransportVerifier = (hostname, cert) => {
|
|
50
51
|
if (
|
|
51
52
|
this.serverCertificateHashes &&
|
|
52
53
|
this.serverCertificateHashes.some((el) => {
|
|
@@ -91,7 +92,8 @@ export class Http2WebTransportClient {
|
|
|
91
92
|
0x2b63: this.initialStreamFlowControlWindow, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAM_DATA_BIDI
|
|
92
93
|
0x2b64: this.initialUnidirectionalStreams, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_UNI
|
|
93
94
|
0x2b65: this.initialBidirectionalStreams // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_BIDI
|
|
94
|
-
}
|
|
95
|
+
},
|
|
96
|
+
remoteCustomSettings: [0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65]
|
|
95
97
|
},
|
|
96
98
|
localPort: this.localPort,
|
|
97
99
|
// TODO: REMOVE BEFORE RELEASE; UNSAFE SETTING
|
|
@@ -141,7 +143,9 @@ export class Http2WebTransportClient {
|
|
|
141
143
|
if (!oursocket.authorized) {
|
|
142
144
|
// ok last hope we have hashes
|
|
143
145
|
if (this.serverCertificateHashes) {
|
|
144
|
-
if (
|
|
146
|
+
if (
|
|
147
|
+
!webTransportVerifier(this.hostname, oursocket.getPeerCertificate())
|
|
148
|
+
) {
|
|
145
149
|
this.clientInt?.destroy(
|
|
146
150
|
undefined,
|
|
147
151
|
http2constants.NGHTTP2_REFUSED_STREAM
|
|
@@ -210,6 +214,14 @@ export class Http2WebTransportClient {
|
|
|
210
214
|
authority: this.hostname,
|
|
211
215
|
origin: this.hostname
|
|
212
216
|
})
|
|
217
|
+
const {
|
|
218
|
+
0x2b65: remoteBidirectionalStreams = undefined,
|
|
219
|
+
0x2b64: remoteUnidirectionalStreams = undefined,
|
|
220
|
+
0x2b63: remoteBidirectionalStreamFlowControlWindow = undefined,
|
|
221
|
+
0x2b62: remoteUnidirectionalStreamFlowControlWindow = undefined,
|
|
222
|
+
0x2b61: remoteSessionFlowControlWindow = undefined
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
} = this.clientInt.remoteSettings?.customSettings || {}
|
|
213
225
|
|
|
214
226
|
const retObj = {
|
|
215
227
|
header: stream.sentHeaders,
|
|
@@ -221,18 +233,27 @@ export class Http2WebTransportClient {
|
|
|
221
233
|
stream,
|
|
222
234
|
nativesession,
|
|
223
235
|
isclient: true,
|
|
224
|
-
|
|
236
|
+
initialStreamSendWindowOffsetBidi:
|
|
237
|
+
remoteBidirectionalStreamFlowControlWindow ||
|
|
238
|
+
this.initialStreamFlowControlWindow,
|
|
239
|
+
initialStreamSendWindowOffsetUnidi:
|
|
240
|
+
remoteUnidirectionalStreamFlowControlWindow ||
|
|
241
|
+
this.initialStreamFlowControlWindow,
|
|
225
242
|
initialStreamReceiveWindowOffset:
|
|
226
243
|
this.initialStreamFlowControlWindow,
|
|
227
244
|
streamShouldAutoTuneReceiveWindow:
|
|
228
245
|
this.streamShouldAutoTuneReceiveWindow,
|
|
229
246
|
streamReceiveWindowSizeLimit: this.streamFlowControlWindowSizeLimit
|
|
230
247
|
}),
|
|
231
|
-
initialBidirectionalSendStreams:
|
|
248
|
+
initialBidirectionalSendStreams:
|
|
249
|
+
remoteBidirectionalStreams || this.initialBidirectionalStreams,
|
|
232
250
|
initialBidirectionalReceiveStreams: this.initialBidirectionalStreams,
|
|
233
|
-
initialUnidirectionalSendStreams:
|
|
251
|
+
initialUnidirectionalSendStreams:
|
|
252
|
+
remoteUnidirectionalStreams || this.initialUnidirectionalStreams,
|
|
234
253
|
initialUnidirectionalReceiveStreams: this.initialUnidirectionalStreams,
|
|
235
|
-
sendWindowOffset:
|
|
254
|
+
sendWindowOffset:
|
|
255
|
+
remoteSessionFlowControlWindow ||
|
|
256
|
+
this.sessionFlowControlWindowSizeLimit,
|
|
236
257
|
receiveWindowOffset: this.sessionFlowControlWindowSizeLimit,
|
|
237
258
|
shouldAutoTuneReceiveWindow: this.sessionShouldAutoTuneReceiveWindow,
|
|
238
259
|
receiveWindowSizeLimit: this.sessionFlowControlWindowSizeLimit
|
package/lib/http2/node/server.js
CHANGED
|
@@ -65,7 +65,8 @@ export class Http2WebTransportServer {
|
|
|
65
65
|
0x2b63: this.initialStreamFlowControlWindow, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAM_DATA_BIDI
|
|
66
66
|
0x2b64: this.initialUnidirectionalStreams, // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_UNI
|
|
67
67
|
0x2b65: this.initialBidirectionalStreams // SETTINGS_WEBTRANSPORT_INITIAL_MAX_STREAMS_BIDI
|
|
68
|
-
}
|
|
68
|
+
},
|
|
69
|
+
remoteCustomSettings: [0x2b60, 0x2b61, 0x2b62, 0x2b63, 0x2b64, 0x2b65]
|
|
69
70
|
}
|
|
70
71
|
})
|
|
71
72
|
|
|
@@ -126,20 +127,31 @@ export class Http2WebTransportServer {
|
|
|
126
127
|
'upgrade',
|
|
127
128
|
(request, stream /* actually a socket */, head) => {
|
|
128
129
|
let path = request.url
|
|
129
|
-
const header = request.headers
|
|
130
130
|
if (!path) {
|
|
131
131
|
stream.destroy()
|
|
132
132
|
return
|
|
133
133
|
}
|
|
134
|
+
while (path.length > 1 && path[0] === '/' && path[1] === '/') {
|
|
135
|
+
path = path?.slice(1)
|
|
136
|
+
}
|
|
137
|
+
const header = { ...request.headers, ':path': path }
|
|
134
138
|
const websocketProt = this.checkProtocolHeader(header)
|
|
135
139
|
if (!websocketProt) {
|
|
136
140
|
stream.destroy()
|
|
137
141
|
return
|
|
138
142
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
+
if (this.hasrequesthandler) {
|
|
144
|
+
stream.head = head
|
|
145
|
+
|
|
146
|
+
const retObj = {
|
|
147
|
+
header,
|
|
148
|
+
session: stream,
|
|
149
|
+
protocol: 'websocketoverhttp1',
|
|
150
|
+
head,
|
|
151
|
+
transportPrivate: { websocketProt }
|
|
152
|
+
}
|
|
153
|
+
this.jsobj.onSessionRequest(retObj)
|
|
154
|
+
} else if (this.paths[path]) {
|
|
143
155
|
this.sendHttp1Headers({ stream, header, protocol: websocketProt })
|
|
144
156
|
.then(() => {
|
|
145
157
|
const retObj = {
|
|
@@ -153,7 +165,8 @@ export class Http2WebTransportServer {
|
|
|
153
165
|
stream,
|
|
154
166
|
nativesession,
|
|
155
167
|
isclient: false,
|
|
156
|
-
|
|
168
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
169
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
157
170
|
initialStreamReceiveWindowOffset:
|
|
158
171
|
this.initialStreamFlowControlWindow,
|
|
159
172
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -187,16 +200,6 @@ export class Http2WebTransportServer {
|
|
|
187
200
|
log('Problem sendHttp1Header', error)
|
|
188
201
|
stream.destroy()
|
|
189
202
|
})
|
|
190
|
-
} else if (this.hasrequesthandler) {
|
|
191
|
-
stream.head = head
|
|
192
|
-
|
|
193
|
-
const retObj = {
|
|
194
|
-
header,
|
|
195
|
-
session: stream,
|
|
196
|
-
protocol: 'websocketoverhttp1',
|
|
197
|
-
head
|
|
198
|
-
}
|
|
199
|
-
this.jsobj.onSessionRequest(retObj)
|
|
200
203
|
} else {
|
|
201
204
|
stream.destroy()
|
|
202
205
|
}
|
|
@@ -243,7 +246,24 @@ export class Http2WebTransportServer {
|
|
|
243
246
|
while (path.length > 1 && path[0] === '/' && path[1] === '/') {
|
|
244
247
|
path = path?.slice(1)
|
|
245
248
|
}
|
|
246
|
-
|
|
249
|
+
header[':path'] = path // also adapt it for the middleware
|
|
250
|
+
if (this.hasrequesthandler) {
|
|
251
|
+
const retObj = {
|
|
252
|
+
header,
|
|
253
|
+
session: stream,
|
|
254
|
+
protocol: websocketProt ? 'websocket' : 'capsule',
|
|
255
|
+
transportPrivate: { websocketProt }
|
|
256
|
+
}
|
|
257
|
+
this.jsobj.onSessionRequest(retObj)
|
|
258
|
+
} else if (this.paths[path]) {
|
|
259
|
+
const {
|
|
260
|
+
0x2b65: remoteBidirectionalStreams = undefined,
|
|
261
|
+
0x2b64: remoteUnidirectionalStreams = undefined,
|
|
262
|
+
0x2b63: remoteBidirectionalStreamFlowControlWindow = undefined,
|
|
263
|
+
0x2b62: remoteUnidirectionalStreamFlowControlWindow = undefined,
|
|
264
|
+
0x2b61: remoteSessionFlowControlWindow = undefined
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
} = stream?.session?.remoteSettings?.customSettings || {}
|
|
247
267
|
const retObj = {
|
|
248
268
|
session: new Http2WebTransportSession({
|
|
249
269
|
stream,
|
|
@@ -256,8 +276,12 @@ export class Http2WebTransportServer {
|
|
|
256
276
|
stream,
|
|
257
277
|
nativesession,
|
|
258
278
|
isclient: false,
|
|
259
|
-
|
|
260
|
-
|
|
279
|
+
initialStreamSendWindowOffsetBidi:
|
|
280
|
+
remoteBidirectionalStreamFlowControlWindow ||
|
|
281
|
+
this.initialStreamFlowControlWindow,
|
|
282
|
+
initialStreamSendWindowOffsetUnidi:
|
|
283
|
+
remoteUnidirectionalStreamFlowControlWindow ||
|
|
284
|
+
this.initialStreamFlowControlWindow,
|
|
261
285
|
initialStreamReceiveWindowOffset:
|
|
262
286
|
this.initialStreamFlowControlWindow,
|
|
263
287
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -270,7 +294,8 @@ export class Http2WebTransportServer {
|
|
|
270
294
|
stream,
|
|
271
295
|
nativesession,
|
|
272
296
|
isclient: false,
|
|
273
|
-
|
|
297
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
298
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
274
299
|
initialStreamReceiveWindowOffset:
|
|
275
300
|
this.initialStreamFlowControlWindow,
|
|
276
301
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -280,18 +305,20 @@ export class Http2WebTransportServer {
|
|
|
280
305
|
}))
|
|
281
306
|
}
|
|
282
307
|
},
|
|
283
|
-
initialBidirectionalSendStreams: websocketProt
|
|
308
|
+
initialBidirectionalSendStreams: websocketProt
|
|
284
309
|
? 0
|
|
285
|
-
: this.initialBidirectionalStreams,
|
|
310
|
+
: remoteBidirectionalStreams || this.initialBidirectionalStreams,
|
|
286
311
|
initialBidirectionalReceiveStreams:
|
|
287
312
|
this.initialBidirectionalStreams,
|
|
288
313
|
initialUnidirectionalSendStreams: websocketProt
|
|
289
314
|
? 0
|
|
290
|
-
:
|
|
315
|
+
: remoteUnidirectionalStreams ||
|
|
316
|
+
this.initialUnidirectionalStreams,
|
|
291
317
|
initialUnidirectionalReceiveStreams:
|
|
292
318
|
this.initialUnidirectionalStreams,
|
|
293
319
|
sendWindowOffset: !websocketProt
|
|
294
|
-
?
|
|
320
|
+
? remoteSessionFlowControlWindow ||
|
|
321
|
+
this.sessionFlowControlWindowSizeLimit
|
|
295
322
|
: 0, // TODO, once supported by node, use initial settings
|
|
296
323
|
receiveWindowOffset: this.sessionFlowControlWindowSizeLimit,
|
|
297
324
|
shouldAutoTuneReceiveWindow:
|
|
@@ -310,19 +337,6 @@ export class Http2WebTransportServer {
|
|
|
310
337
|
if (websocketProt) resp['sec-websocket-protocol'] = websocketProt
|
|
311
338
|
stream.respond(resp)
|
|
312
339
|
this.jsobj.onHttpWTSessionVisitor(retObj)
|
|
313
|
-
} else if (this.hasrequesthandler) {
|
|
314
|
-
const retObj = {
|
|
315
|
-
header,
|
|
316
|
-
session: stream,
|
|
317
|
-
protocol: websocketProt ? 'websocket' : 'capsule'
|
|
318
|
-
}
|
|
319
|
-
const resp = {
|
|
320
|
-
':status': '200'
|
|
321
|
-
}
|
|
322
|
-
// @ts-ignore
|
|
323
|
-
if (websocketProt) resp['sec-websocket-protocol'] = websocketProt
|
|
324
|
-
stream.respond(resp)
|
|
325
|
-
this.jsobj.onSessionRequest(retObj)
|
|
326
340
|
} else {
|
|
327
341
|
stream.respond({
|
|
328
342
|
':status': '404'
|
|
@@ -364,8 +378,8 @@ export class Http2WebTransportServer {
|
|
|
364
378
|
}
|
|
365
379
|
|
|
366
380
|
/**
|
|
367
|
-
* @param {string} cert
|
|
368
|
-
* @param {string} privKey
|
|
381
|
+
* @param {string|string[]} cert
|
|
382
|
+
* @param {string|string[]} privKey
|
|
369
383
|
* @param {boolean} http2only
|
|
370
384
|
* */
|
|
371
385
|
updateCert(cert, privKey, http2only) {
|
|
@@ -420,17 +434,35 @@ export class Http2WebTransportServer {
|
|
|
420
434
|
/**
|
|
421
435
|
* @param {import('../../types.js').NativeFinishSessionRequest} args
|
|
422
436
|
*/
|
|
423
|
-
finishSessionRequest({
|
|
437
|
+
finishSessionRequest({
|
|
438
|
+
header,
|
|
439
|
+
userData,
|
|
440
|
+
session: stream,
|
|
441
|
+
status,
|
|
442
|
+
protocol,
|
|
443
|
+
head,
|
|
444
|
+
path,
|
|
445
|
+
transportPrivate
|
|
446
|
+
}) {
|
|
424
447
|
if (status !== 200) {
|
|
425
448
|
if (protocol === 'websocketoverhttp1') {
|
|
426
449
|
stream.destroy()
|
|
427
450
|
} else {
|
|
428
|
-
stream.
|
|
451
|
+
stream.respond({
|
|
452
|
+
':status': status.toString()
|
|
453
|
+
})
|
|
454
|
+
stream.end()
|
|
455
|
+
stream.close()
|
|
429
456
|
}
|
|
430
457
|
} else {
|
|
431
458
|
if (protocol === 'websocketoverhttp1') {
|
|
432
|
-
|
|
433
|
-
|
|
459
|
+
this.sendHttp1Headers({
|
|
460
|
+
// @ts-ignore
|
|
461
|
+
stream,
|
|
462
|
+
header,
|
|
463
|
+
// @ts-ignore
|
|
464
|
+
protocol: transportPrivate.websocketProt
|
|
465
|
+
})
|
|
434
466
|
.then(() => {
|
|
435
467
|
const retObj = {
|
|
436
468
|
session: new Http2WebTransportSession({
|
|
@@ -443,7 +475,8 @@ export class Http2WebTransportServer {
|
|
|
443
475
|
stream,
|
|
444
476
|
nativesession,
|
|
445
477
|
isclient: false,
|
|
446
|
-
|
|
478
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
479
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
447
480
|
initialStreamReceiveWindowOffset:
|
|
448
481
|
this.initialStreamFlowControlWindow,
|
|
449
482
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -466,8 +499,9 @@ export class Http2WebTransportServer {
|
|
|
466
499
|
this.sessionShouldAutoTuneReceiveWindow,
|
|
467
500
|
receiveWindowSizeLimit: this.sessionFlowControlWindowSizeLimit
|
|
468
501
|
}),
|
|
469
|
-
path
|
|
470
|
-
header
|
|
502
|
+
path,
|
|
503
|
+
header,
|
|
504
|
+
userData
|
|
471
505
|
}
|
|
472
506
|
// @ts-ignore
|
|
473
507
|
this.jsobj.onHttpWTSessionVisitor(retObj)
|
|
@@ -476,6 +510,22 @@ export class Http2WebTransportServer {
|
|
|
476
510
|
log('sendHttp1Headers error', error)
|
|
477
511
|
})
|
|
478
512
|
} else {
|
|
513
|
+
const resp = {
|
|
514
|
+
':status': '200'
|
|
515
|
+
}
|
|
516
|
+
// @ts-ignore
|
|
517
|
+
if (transportPrivate?.websocketProt)
|
|
518
|
+
// @ts-ignore
|
|
519
|
+
resp['sec-websocket-protocol'] = transportPrivate.websocketProt
|
|
520
|
+
stream.respond(resp)
|
|
521
|
+
const {
|
|
522
|
+
0x2b65: remoteBidirectionalStreams = undefined,
|
|
523
|
+
0x2b64: remoteUnidirectionalStreams = undefined,
|
|
524
|
+
0x2b63: remoteBidirectionalStreamFlowControlWindow = undefined,
|
|
525
|
+
0x2b62: remoteUnidirectionalStreamFlowControlWindow = undefined,
|
|
526
|
+
0x2b61: remoteSessionFlowControlWindow = undefined
|
|
527
|
+
// @ts-ignore
|
|
528
|
+
} = stream?.session?.remoteSettings?.customSettings || {}
|
|
479
529
|
const retObj = {
|
|
480
530
|
session: new Http2WebTransportSession({
|
|
481
531
|
stream,
|
|
@@ -488,8 +538,12 @@ export class Http2WebTransportServer {
|
|
|
488
538
|
stream,
|
|
489
539
|
nativesession,
|
|
490
540
|
isclient: false,
|
|
491
|
-
|
|
492
|
-
|
|
541
|
+
initialStreamSendWindowOffsetBidi:
|
|
542
|
+
remoteBidirectionalStreamFlowControlWindow ||
|
|
543
|
+
this.initialStreamFlowControlWindow,
|
|
544
|
+
initialStreamSendWindowOffsetUnidi:
|
|
545
|
+
remoteUnidirectionalStreamFlowControlWindow ||
|
|
546
|
+
this.initialStreamFlowControlWindow,
|
|
493
547
|
initialStreamReceiveWindowOffset:
|
|
494
548
|
this.initialStreamFlowControlWindow,
|
|
495
549
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -502,7 +556,8 @@ export class Http2WebTransportServer {
|
|
|
502
556
|
stream,
|
|
503
557
|
nativesession,
|
|
504
558
|
isclient: false,
|
|
505
|
-
|
|
559
|
+
initialStreamSendWindowOffsetBidi: 0,
|
|
560
|
+
initialStreamSendWindowOffsetUnidi: 0,
|
|
506
561
|
initialStreamReceiveWindowOffset:
|
|
507
562
|
this.initialStreamFlowControlWindow,
|
|
508
563
|
streamShouldAutoTuneReceiveWindow:
|
|
@@ -513,24 +568,31 @@ export class Http2WebTransportServer {
|
|
|
513
568
|
}
|
|
514
569
|
},
|
|
515
570
|
initialBidirectionalSendStreams:
|
|
516
|
-
protocol !== 'websocket'
|
|
571
|
+
protocol !== 'websocket'
|
|
572
|
+
? remoteBidirectionalStreams || this.initialBidirectionalStreams
|
|
573
|
+
: 0, // TODO, once supported by node, use initial settings
|
|
517
574
|
initialBidirectionalReceiveStreams:
|
|
518
575
|
this.initialBidirectionalStreams,
|
|
519
576
|
initialUnidirectionalSendStreams:
|
|
520
|
-
protocol !== 'websocket'
|
|
577
|
+
protocol !== 'websocket'
|
|
578
|
+
? remoteUnidirectionalStreams ||
|
|
579
|
+
this.initialUnidirectionalStreams
|
|
580
|
+
: 0, // TODO, once supported by node, use initial settings
|
|
521
581
|
initialUnidirectionalReceiveStreams:
|
|
522
582
|
this.initialUnidirectionalStreams,
|
|
523
583
|
sendWindowOffset:
|
|
524
584
|
protocol !== 'websocket'
|
|
525
|
-
?
|
|
585
|
+
? remoteSessionFlowControlWindow ||
|
|
586
|
+
this.sessionFlowControlWindowSizeLimit
|
|
526
587
|
: 0, // TODO, once supported by node, use initial settings
|
|
527
588
|
receiveWindowOffset: this.sessionFlowControlWindowSizeLimit,
|
|
528
589
|
shouldAutoTuneReceiveWindow:
|
|
529
590
|
this.sessionShouldAutoTuneReceiveWindow,
|
|
530
591
|
receiveWindowSizeLimit: this.sessionFlowControlWindowSizeLimit
|
|
531
592
|
}),
|
|
532
|
-
path
|
|
533
|
-
header
|
|
593
|
+
path,
|
|
594
|
+
header,
|
|
595
|
+
userData
|
|
534
596
|
}
|
|
535
597
|
// @ts-ignore
|
|
536
598
|
this.jsobj.onHttpWTSessionVisitor(retObj)
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto'
|
|
2
2
|
import { ParserBase, lengthVarInt } from '../parserbase.js'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ParserBaseHttp2,
|
|
5
|
+
readVarInt,
|
|
6
|
+
writeVarInt,
|
|
7
|
+
readUint32
|
|
8
|
+
} from '../parserbasehttp2.js'
|
|
4
9
|
import { logger } from '../../utils.js'
|
|
5
10
|
|
|
6
11
|
const log = logger(`webtransport:http2:node:websocketparser(${process?.pid})`)
|
|
@@ -174,7 +179,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
174
179
|
stream,
|
|
175
180
|
nativesession,
|
|
176
181
|
isclient,
|
|
177
|
-
|
|
182
|
+
initialStreamSendWindowOffsetUnidi,
|
|
183
|
+
initialStreamSendWindowOffsetBidi,
|
|
178
184
|
initialStreamReceiveWindowOffset,
|
|
179
185
|
streamShouldAutoTuneReceiveWindow,
|
|
180
186
|
streamReceiveWindowSizeLimit
|
|
@@ -183,7 +189,8 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
183
189
|
stream,
|
|
184
190
|
nativesession,
|
|
185
191
|
isclient,
|
|
186
|
-
|
|
192
|
+
initialStreamSendWindowOffsetUnidi,
|
|
193
|
+
initialStreamSendWindowOffsetBidi,
|
|
187
194
|
initialStreamReceiveWindowOffset,
|
|
188
195
|
streamShouldAutoTuneReceiveWindow,
|
|
189
196
|
streamReceiveWindowSizeLimit
|
|
@@ -598,6 +605,23 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
598
605
|
case ParserBase.WT_STREAMS_BLOCKED_BIDI:
|
|
599
606
|
this.onStreamsBlockedBidi(readVarInt(bufferstate))
|
|
600
607
|
break
|
|
608
|
+
case ParserBase.CLOSE_WEBTRANSPORT_SESSION:
|
|
609
|
+
{
|
|
610
|
+
const code = readUint32(bufferstate) || 0
|
|
611
|
+
const decoder = new TextDecoder()
|
|
612
|
+
const reason = decoder.decode(
|
|
613
|
+
new Uint8Array(
|
|
614
|
+
bufferstate.buffer.buffer,
|
|
615
|
+
bufferstate.buffer.byteOffset + bufferstate.offset,
|
|
616
|
+
offsetend - bufferstate.offset
|
|
617
|
+
)
|
|
618
|
+
)
|
|
619
|
+
this.onCloseWebTransportSession({ code, reason })
|
|
620
|
+
}
|
|
621
|
+
break
|
|
622
|
+
case ParserBase.DRAIN_WEBTRANSPORT_SESSION:
|
|
623
|
+
this.onDrain()
|
|
624
|
+
break
|
|
601
625
|
case ParserBase.DATAGRAM:
|
|
602
626
|
if (wbufferstate) {
|
|
603
627
|
this.session.jsobj.onDatagramReceived({
|
|
@@ -688,19 +712,12 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
688
712
|
* @param{{code: Number, reason: string}}arg
|
|
689
713
|
*/
|
|
690
714
|
sendClose({ code, reason }) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
'utf8'
|
|
694
|
-
)
|
|
695
|
-
const wbuf = Buffer.allocUnsafe(2 + reasBuf.byteLength)
|
|
696
|
-
const errorcode = code === 0 ? 1000 : 1003
|
|
697
|
-
wbuf.writeUint16BE(errorcode, 0)
|
|
698
|
-
reasBuf.copy(wbuf, 2)
|
|
699
|
-
this.sendCloseInt(wbuf)
|
|
715
|
+
super.sendClose({ code, reason })
|
|
716
|
+
this.sendCloseInt()
|
|
700
717
|
}
|
|
701
718
|
|
|
702
719
|
/**
|
|
703
|
-
* @param {Uint8Array} payload
|
|
720
|
+
* @param {Uint8Array} [payload]
|
|
704
721
|
*/
|
|
705
722
|
sendCloseInt(payload) {
|
|
706
723
|
this.writeWSFrame({ opcode: WebSocketParser.WS_CLOSE, payload })
|
|
@@ -715,9 +732,9 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
715
732
|
}
|
|
716
733
|
|
|
717
734
|
/**
|
|
718
|
-
* @param{{type: Number, headerVints: Array<Number|bigint>, payload: Uint8Array|undefined}} bs
|
|
735
|
+
* @param{{type: Number, headerVints: Array<Number|bigint>, payload: Uint8Array|undefined, end?: () => void}} bs
|
|
719
736
|
*/
|
|
720
|
-
writeCapsule({ type, headerVints, payload }) {
|
|
737
|
+
writeCapsule({ type, headerVints, payload, end }) {
|
|
721
738
|
let plength = 0
|
|
722
739
|
for (const ind in headerVints) plength += lengthVarInt(headerVints[ind])
|
|
723
740
|
plength += lengthVarInt(type)
|
|
@@ -762,8 +779,13 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
762
779
|
payload.byteLength
|
|
763
780
|
)
|
|
764
781
|
}
|
|
765
|
-
let blocked =
|
|
766
|
-
if (payload)
|
|
782
|
+
let blocked = false
|
|
783
|
+
if (payload) {
|
|
784
|
+
blocked = !this.stream.write(cdata)
|
|
785
|
+
blocked = !this.stream.write(payload, end) || blocked
|
|
786
|
+
} else {
|
|
787
|
+
blocked = !this.stream.write(cdata, end)
|
|
788
|
+
}
|
|
767
789
|
// do something if blocked
|
|
768
790
|
if (blocked) this.blocked = true
|
|
769
791
|
return blocked
|
|
@@ -802,10 +824,10 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
802
824
|
}
|
|
803
825
|
|
|
804
826
|
/**
|
|
805
|
-
* @param {{ opcode: number; payload: Uint8Array; }} args
|
|
827
|
+
* @param {{ opcode: number; payload: Uint8Array|undefined; }} args
|
|
806
828
|
*/
|
|
807
829
|
writeWSFrame({ opcode, payload }) {
|
|
808
|
-
const plength = payload
|
|
830
|
+
const plength = payload?.byteLength || 0
|
|
809
831
|
let headlength = 2
|
|
810
832
|
let mask = 0
|
|
811
833
|
if (this.isclient) {
|
|
@@ -825,7 +847,7 @@ export class WebSocketParser extends ParserBaseHttp2 {
|
|
|
825
847
|
throw new Error('Headlength does not match pos')
|
|
826
848
|
|
|
827
849
|
let blocked = !this.stream.write(cdata)
|
|
828
|
-
if (maskstate)
|
|
850
|
+
if (maskstate && payload)
|
|
829
851
|
applyMask(
|
|
830
852
|
maskstate,
|
|
831
853
|
Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength),
|