@fails-components/webtransport 1.0.8 → 1.0.9
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/client.d.ts.map +1 -0
- package/dist/{main/lib → lib}/dom.d.ts +10 -2
- package/dist/lib/dom.d.ts.map +1 -0
- package/dist/lib/error.d.ts.map +1 -0
- package/dist/{main/lib → lib}/features.d.ts.map +1 -1
- package/dist/{main/lib → lib}/http2/browser/browser.d.ts +2 -0
- package/dist/lib/http2/browser/browser.d.ts.map +1 -0
- package/dist/lib/http2/browser/browserparser.d.ts.map +1 -0
- package/dist/{main/lib → lib}/http2/flowcontroller.d.ts +1 -1
- package/dist/lib/http2/flowcontroller.d.ts.map +1 -0
- package/dist/{main/lib → lib}/http2/node/capsuleparser.d.ts +1 -1
- package/dist/lib/http2/node/capsuleparser.d.ts.map +1 -0
- package/dist/{main/lib → lib}/http2/node/client.d.ts +2 -0
- package/dist/lib/http2/node/client.d.ts.map +1 -0
- package/dist/{main/test → lib/http2/node}/index.d.ts.map +1 -1
- package/dist/{main/lib → lib}/http2/node/server.d.ts +3 -0
- package/dist/lib/http2/node/server.d.ts.map +1 -0
- package/dist/{main/lib → lib}/http2/node/websocketparser.d.ts +4 -2
- package/dist/lib/http2/node/websocketparser.d.ts.map +1 -0
- package/dist/{main/lib → lib}/http2/parserbase.d.ts +26 -9
- package/dist/lib/http2/parserbase.d.ts.map +1 -0
- package/dist/lib/http2/parserbasehttp2.d.ts.map +1 -0
- package/dist/{main/lib → lib}/http2/session.d.ts +43 -6
- package/dist/lib/http2/session.d.ts.map +1 -0
- package/dist/{main/lib → lib}/http2/stream.d.ts +29 -7
- package/dist/lib/http2/stream.d.ts.map +1 -0
- package/dist/lib/http2/streamidmanager.d.ts +72 -0
- package/dist/lib/http2/streamidmanager.d.ts.map +1 -0
- package/dist/lib/http2/websocketcommon.d.ts.map +1 -0
- package/dist/lib/index.browser.d.ts.map +1 -0
- package/dist/lib/index.node.d.ts.map +1 -0
- package/dist/lib/server.d.ts.map +1 -0
- package/dist/{main/lib → lib}/session.d.ts +10 -3
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/stream.d.ts.map +1 -0
- package/dist/{main/lib → lib}/types.d.ts +26 -4
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/webstreams.browser.d.ts.map +1 -0
- package/dist/lib/webstreams.d.ts.map +1 -0
- package/dist/{main/lib → lib}/webtransport.browser.d.ts +12 -2
- package/dist/lib/webtransport.browser.d.ts.map +1 -0
- package/dist/lib/webtransport.node.d.ts.map +1 -0
- package/dist/{main/lib → lib}/webtransportbase.d.ts +14 -2
- package/dist/lib/webtransportbase.d.ts.map +1 -0
- package/lib/dom.ts +12 -2
- package/lib/http2/browser/browser.js +12 -3
- package/lib/http2/browser/browserparser.js +36 -36
- package/lib/http2/flowcontroller.js +2 -2
- package/lib/http2/node/capsuleparser.js +58 -51
- package/lib/http2/node/client.js +12 -3
- package/lib/http2/node/server.js +53 -16
- package/lib/http2/node/websocketparser.js +69 -59
- package/lib/http2/parserbase.js +87 -12
- package/lib/http2/session.js +109 -34
- package/lib/http2/stream.js +65 -10
- package/lib/http2/streamidmanager.js +316 -0
- package/lib/session.js +31 -5
- package/lib/types.ts +20 -4
- package/lib/webtransport.browser.js +14 -4
- package/lib/webtransportbase.js +14 -4
- package/package.json +1 -1
- package/readme.md +4 -3
- package/test/fixtures/certificate.js +1 -2
- package/test/fixtures/server.js +112 -7
- package/test/stream-limits.spec.js +247 -0
- package/tsconfig.json +2 -7
- package/dist/main/lib/client.d.ts.map +0 -1
- package/dist/main/lib/dom.d.ts.map +0 -1
- package/dist/main/lib/error.d.ts.map +0 -1
- package/dist/main/lib/http2/browser/browser.d.ts.map +0 -1
- package/dist/main/lib/http2/browser/browserparser.d.ts.map +0 -1
- package/dist/main/lib/http2/flowcontroller.d.ts.map +0 -1
- package/dist/main/lib/http2/node/capsuleparser.d.ts.map +0 -1
- package/dist/main/lib/http2/node/client.d.ts.map +0 -1
- package/dist/main/lib/http2/node/index.d.ts.map +0 -1
- package/dist/main/lib/http2/node/server.d.ts.map +0 -1
- package/dist/main/lib/http2/node/websocketparser.d.ts.map +0 -1
- package/dist/main/lib/http2/parserbase.d.ts.map +0 -1
- package/dist/main/lib/http2/parserbasehttp2.d.ts.map +0 -1
- package/dist/main/lib/http2/session.d.ts.map +0 -1
- package/dist/main/lib/http2/stream.d.ts.map +0 -1
- package/dist/main/lib/http2/websocketcommon.d.ts.map +0 -1
- package/dist/main/lib/index.browser.d.ts.map +0 -1
- package/dist/main/lib/index.node.d.ts.map +0 -1
- package/dist/main/lib/server.d.ts.map +0 -1
- package/dist/main/lib/session.d.ts.map +0 -1
- package/dist/main/lib/stream.d.ts.map +0 -1
- package/dist/main/lib/types.d.ts.map +0 -1
- package/dist/main/lib/utils.d.ts.map +0 -1
- package/dist/main/lib/webstreams.browser.d.ts.map +0 -1
- package/dist/main/lib/webstreams.d.ts.map +0 -1
- package/dist/main/lib/webtransport.browser.d.ts.map +0 -1
- package/dist/main/lib/webtransport.node.d.ts.map +0 -1
- package/dist/main/lib/webtransportbase.d.ts.map +0 -1
- package/dist/main/test/bidirectional-streams.spec.d.ts +0 -5
- package/dist/main/test/bidirectional-streams.spec.d.ts.map +0 -1
- package/dist/main/test/datagrams.spec.d.ts +0 -5
- package/dist/main/test/datagrams.spec.d.ts.map +0 -1
- package/dist/main/test/fixtures/certificate.d.ts +0 -21
- package/dist/main/test/fixtures/certificate.d.ts.map +0 -1
- package/dist/main/test/fixtures/chai.d.ts +0 -2
- package/dist/main/test/fixtures/chai.d.ts.map +0 -1
- package/dist/main/test/fixtures/known-bytes.d.ts +0 -8
- package/dist/main/test/fixtures/known-bytes.d.ts.map +0 -1
- package/dist/main/test/fixtures/p-timeout.d.ts +0 -8
- package/dist/main/test/fixtures/p-timeout.d.ts.map +0 -1
- package/dist/main/test/fixtures/read-cert-hash.d.ts +0 -6
- package/dist/main/test/fixtures/read-cert-hash.d.ts.map +0 -1
- package/dist/main/test/fixtures/read-stream.d.ts +0 -18
- package/dist/main/test/fixtures/read-stream.d.ts.map +0 -1
- package/dist/main/test/fixtures/reader-value.d.ts +0 -21
- package/dist/main/test/fixtures/reader-value.d.ts.map +0 -1
- package/dist/main/test/fixtures/server.d.ts +0 -5
- package/dist/main/test/fixtures/server.d.ts.map +0 -1
- package/dist/main/test/fixtures/webtransport.browser.d.ts +0 -4
- package/dist/main/test/fixtures/webtransport.browser.d.ts.map +0 -1
- package/dist/main/test/fixtures/webtransport.d.ts +0 -3
- package/dist/main/test/fixtures/webtransport.d.ts.map +0 -1
- package/dist/main/test/fixtures/write-stream.d.ts +0 -10
- package/dist/main/test/fixtures/write-stream.d.ts.map +0 -1
- package/dist/main/test/index.d.ts +0 -2
- package/dist/main/test/session.spec.d.ts +0 -5
- package/dist/main/test/session.spec.d.ts.map +0 -1
- package/dist/main/test/unidirectional-streams.spec.d.ts +0 -2
- package/dist/main/test/unidirectional-streams.spec.d.ts.map +0 -1
- package/dist/main/tsconfig.tsbuildinfo +0 -1
- package/dist/transports/http3-quiche/lib/clientsocket.d.ts +0 -19
- package/dist/transports/http3-quiche/lib/clientsocket.d.ts.map +0 -1
- package/dist/transports/http3-quiche/lib/index.d.ts +0 -11
- package/dist/transports/http3-quiche/lib/index.d.ts.map +0 -1
- package/dist/transports/http3-quiche/lib/serversocket.d.ts +0 -12
- package/dist/transports/http3-quiche/lib/serversocket.d.ts.map +0 -1
- package/dist/transports/http3-quiche/lib/socket.d.ts +0 -19
- package/dist/transports/http3-quiche/lib/socket.d.ts.map +0 -1
- package/dist/transports/http3-quiche/lib/types.d.ts +0 -21
- package/dist/transports/http3-quiche/lib/types.d.ts.map +0 -1
- package/dist/transports/http3-quiche/lib/utils.d.ts +0 -6
- package/dist/transports/http3-quiche/lib/utils.d.ts.map +0 -1
- /package/dist/{main/lib → lib}/client.d.ts +0 -0
- /package/dist/{main/lib → lib}/error.d.ts +0 -0
- /package/dist/{main/lib → lib}/features.d.ts +0 -0
- /package/dist/{main/lib → lib}/http2/browser/browserparser.d.ts +0 -0
- /package/dist/{main/lib → lib}/http2/node/index.d.ts +0 -0
- /package/dist/{main/lib → lib}/http2/parserbasehttp2.d.ts +0 -0
- /package/dist/{main/lib → lib}/http2/websocketcommon.d.ts +0 -0
- /package/dist/{main/lib → lib}/index.browser.d.ts +0 -0
- /package/dist/{main/lib → lib}/index.node.d.ts +0 -0
- /package/dist/{main/lib → lib}/server.d.ts +0 -0
- /package/dist/{main/lib → lib}/stream.d.ts +0 -0
- /package/dist/{main/lib → lib}/utils.d.ts +0 -0
- /package/dist/{main/lib → lib}/webstreams.browser.d.ts +0 -0
- /package/dist/{main/lib → lib}/webstreams.d.ts +0 -0
- /package/dist/{main/lib → lib}/webtransport.node.d.ts +0 -0
package/lib/http2/session.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import('http2').Http2Stream} Http2Stream
|
|
3
|
+
* @typedef {import('../dom.js').WebTransportSendGroup} WebTransportSendGroup
|
|
3
4
|
*/
|
|
4
5
|
import { ParserBase } from './parserbase.js'
|
|
5
6
|
import { FlowController } from './flowcontroller.js'
|
|
6
7
|
import { logger } from '../utils.js'
|
|
8
|
+
import { StreamIdManager } from './streamidmanager.js'
|
|
7
9
|
|
|
8
10
|
const pid = typeof process !== 'undefined' ? process.pid : 0
|
|
9
11
|
const log = logger(`webtransport:http2webtransportsession(${pid})`)
|
|
@@ -20,7 +22,11 @@ export class Http2WebTransportSession {
|
|
|
20
22
|
* sendWindowOffset: Number,
|
|
21
23
|
* receiveWindowOffset: Number,
|
|
22
24
|
* shouldAutoTuneReceiveWindow: boolean
|
|
23
|
-
* receiveWindowSizeLimit: Number
|
|
25
|
+
* receiveWindowSizeLimit: Number,
|
|
26
|
+
* initialBidirectionalSendStreams: Number,
|
|
27
|
+
* initialBidirectionalReceiveStreams: Number,
|
|
28
|
+
* initialUnidirectionalSendStreams: Number,
|
|
29
|
+
* initialUnidirectionalReceiveStreams: Number}} args
|
|
24
30
|
* */
|
|
25
31
|
constructor({
|
|
26
32
|
stream,
|
|
@@ -30,7 +36,11 @@ export class Http2WebTransportSession {
|
|
|
30
36
|
sendWindowOffset,
|
|
31
37
|
receiveWindowOffset,
|
|
32
38
|
shouldAutoTuneReceiveWindow,
|
|
33
|
-
receiveWindowSizeLimit
|
|
39
|
+
receiveWindowSizeLimit,
|
|
40
|
+
initialBidirectionalSendStreams,
|
|
41
|
+
initialBidirectionalReceiveStreams,
|
|
42
|
+
initialUnidirectionalSendStreams,
|
|
43
|
+
initialUnidirectionalReceiveStreams
|
|
34
44
|
}) {
|
|
35
45
|
// @ts-ignore
|
|
36
46
|
this.jsobj = undefined // the creator will set this
|
|
@@ -40,8 +50,6 @@ export class Http2WebTransportSession {
|
|
|
40
50
|
this.ws = ws
|
|
41
51
|
} else throw new Error('Neither stream or websocket supplied')
|
|
42
52
|
this.capsParser = createParser(this)
|
|
43
|
-
this.unidiId = 0
|
|
44
|
-
this.bidiId = 0
|
|
45
53
|
this.isclient = isclient
|
|
46
54
|
this.flowController = new FlowController({
|
|
47
55
|
tocontrol: this,
|
|
@@ -50,6 +58,22 @@ export class Http2WebTransportSession {
|
|
|
50
58
|
shouldAutoTuneReceiveWindow,
|
|
51
59
|
receiveWindowSizeLimit
|
|
52
60
|
})
|
|
61
|
+
this.streamIdMngrUni = new StreamIdManager({
|
|
62
|
+
delegate: this,
|
|
63
|
+
unidirectional: true,
|
|
64
|
+
isclient,
|
|
65
|
+
maxAllowedIncomingStreams: initialUnidirectionalReceiveStreams,
|
|
66
|
+
maxAllowedOutgoingStreams: initialUnidirectionalSendStreams
|
|
67
|
+
})
|
|
68
|
+
this.streamIdMngrBi = new StreamIdManager({
|
|
69
|
+
delegate: this,
|
|
70
|
+
unidirectional: false,
|
|
71
|
+
isclient,
|
|
72
|
+
maxAllowedIncomingStreams: initialBidirectionalReceiveStreams,
|
|
73
|
+
maxAllowedOutgoingStreams: initialBidirectionalSendStreams
|
|
74
|
+
})
|
|
75
|
+
this.orderUniStreams = 0
|
|
76
|
+
this.orderBiStreams = 0
|
|
53
77
|
if (stream) {
|
|
54
78
|
if (isclient) {
|
|
55
79
|
stream.on('response', (headers) => {
|
|
@@ -75,16 +99,8 @@ export class Http2WebTransportSession {
|
|
|
75
99
|
|
|
76
100
|
sendInitialParameters() {
|
|
77
101
|
this.flowController.sendWindowUpdate()
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
headerVints: [0xffffff],
|
|
81
|
-
payload: undefined
|
|
82
|
-
})
|
|
83
|
-
this.capsParser.writeCapsule({
|
|
84
|
-
type: ParserBase.WT_MAX_STREAMS_UNIDI,
|
|
85
|
-
headerVints: [0xffffff],
|
|
86
|
-
payload: undefined
|
|
87
|
-
})
|
|
102
|
+
this.streamIdMngrBi.sendMaxStreamsFrameInitial()
|
|
103
|
+
this.streamIdMngrUni.sendMaxStreamsFrameInitial()
|
|
88
104
|
}
|
|
89
105
|
|
|
90
106
|
/**
|
|
@@ -101,28 +117,66 @@ export class Http2WebTransportSession {
|
|
|
101
117
|
})
|
|
102
118
|
}
|
|
103
119
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
120
|
+
trySendingUnidirectionalStreams() {
|
|
121
|
+
while (
|
|
122
|
+
this.orderUniStreams > 0 &&
|
|
123
|
+
this.streamIdMngrUni.canOpenNextOutgoingStream()
|
|
124
|
+
) {
|
|
125
|
+
const streamid = this.streamIdMngrUni.getNextOutgoingStreamId()
|
|
126
|
+
this.capsParser.writeCapsule({
|
|
127
|
+
type: ParserBase.WT_STREAM_WOFIN,
|
|
128
|
+
headerVints: [streamid],
|
|
129
|
+
payload: undefined
|
|
130
|
+
})
|
|
131
|
+
this.capsParser.newStream(streamid)
|
|
132
|
+
this.orderUniStreams--
|
|
133
|
+
}
|
|
114
134
|
}
|
|
115
135
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
/**
|
|
137
|
+
* @param {{sendGroup: WebTransportSendGroup|null, sendOrder: number, waitUntilAvailable: boolean}} opts
|
|
138
|
+
*/
|
|
139
|
+
orderUnidiStream({ sendGroup, sendOrder, waitUntilAvailable }) {
|
|
140
|
+
const canopen = this.streamIdMngrUni.canOpenNextOutgoingStream()
|
|
141
|
+
const maxset = this.streamIdMngrUni.isMaxStreamSet() // we block if the maxsetting did not arrive
|
|
142
|
+
|
|
143
|
+
if (canopen || waitUntilAvailable || !maxset) {
|
|
144
|
+
this.orderUniStreams++
|
|
145
|
+
this.trySendingUnidirectionalStreams()
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
trySendingBidirectionalStreams() {
|
|
152
|
+
while (
|
|
153
|
+
this.orderBiStreams > 0 &&
|
|
154
|
+
this.streamIdMngrBi.canOpenNextOutgoingStream()
|
|
155
|
+
) {
|
|
156
|
+
const streamid = this.streamIdMngrBi.getNextOutgoingStreamId()
|
|
157
|
+
this.capsParser.writeCapsule({
|
|
158
|
+
type: ParserBase.WT_STREAM_WOFIN,
|
|
159
|
+
headerVints: [streamid],
|
|
160
|
+
payload: undefined
|
|
161
|
+
})
|
|
162
|
+
this.capsParser.newStream(streamid)
|
|
163
|
+
this.orderBiStreams--
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {{sendGroup: WebTransportSendGroup|null, sendOrder: number, waitUntilAvailable: boolean}} opts
|
|
169
|
+
*/
|
|
170
|
+
orderBidiStream({ sendGroup, sendOrder, waitUntilAvailable }) {
|
|
171
|
+
const canopen = this.streamIdMngrBi.canOpenNextOutgoingStream()
|
|
172
|
+
const maxset = this.streamIdMngrBi.isMaxStreamSet() // we block if the maxsetting did not arrive
|
|
173
|
+
|
|
174
|
+
if (canopen || waitUntilAvailable || !maxset) {
|
|
175
|
+
this.orderBiStreams++
|
|
176
|
+
this.trySendingBidirectionalStreams()
|
|
177
|
+
return true
|
|
178
|
+
}
|
|
179
|
+
return false
|
|
126
180
|
}
|
|
127
181
|
|
|
128
182
|
orderSessionStats() {
|
|
@@ -218,4 +272,25 @@ export class Http2WebTransportSession {
|
|
|
218
272
|
return navigator?.connection?.rtt || 26
|
|
219
273
|
}
|
|
220
274
|
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @returns {boolean}
|
|
278
|
+
*/
|
|
279
|
+
canSendMaxStreams() {
|
|
280
|
+
return true
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @param {bigint} maxStreams
|
|
285
|
+
* @param {boolean} unidirectional
|
|
286
|
+
*/
|
|
287
|
+
sendMaxStreams(maxStreams, unidirectional) {
|
|
288
|
+
this.capsParser.writeCapsule({
|
|
289
|
+
type: unidirectional
|
|
290
|
+
? ParserBase.WT_MAX_STREAMS_UNIDI
|
|
291
|
+
: ParserBase.WT_MAX_STREAMS_BIDI,
|
|
292
|
+
headerVints: [maxStreams],
|
|
293
|
+
payload: undefined
|
|
294
|
+
})
|
|
295
|
+
}
|
|
221
296
|
}
|
package/lib/http2/stream.js
CHANGED
|
@@ -12,7 +12,8 @@ const log = logger(`webtransport:http2webtransportstream(${pid})`)
|
|
|
12
12
|
* @typedef {import('../types').StreamWriteEvent} StreamWriteEvent
|
|
13
13
|
* @typedef {import('../types').StreamNetworkFinishEvent} StreamNetworkFinishEvent
|
|
14
14
|
*
|
|
15
|
-
*
|
|
15
|
+
* @typedef {import('../types').ReadDataInt} ReadDataInt
|
|
16
|
+
* @typedef {import('./streamidmanager.js').StreamIdManager} StreamIdManager
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
19
|
let processnextTick = (/** @type {{ (): void; }} */ func) =>
|
|
@@ -22,21 +23,28 @@ if (typeof process !== 'undefined') processnextTick = process.nextTick
|
|
|
22
23
|
|
|
23
24
|
export class Http2WebTransportStream {
|
|
24
25
|
/**
|
|
25
|
-
* @param {{streamid:
|
|
26
|
+
* @param {{streamid: bigint,
|
|
27
|
+
* unidirectional: boolean,
|
|
28
|
+
* incoming: boolean,
|
|
29
|
+
* capsuleParser: ParserBase
|
|
26
30
|
* sendWindowOffset: Number,
|
|
27
31
|
* receiveWindowOffset: Number,
|
|
28
32
|
* shouldAutoTuneReceiveWindow: boolean
|
|
29
33
|
* receiveWindowSizeLimit: Number,
|
|
30
|
-
* sessionFlowController: FlowController
|
|
34
|
+
* sessionFlowController: FlowController,
|
|
35
|
+
* streamIdManager: StreamIdManager}} args
|
|
31
36
|
* */
|
|
32
37
|
constructor({
|
|
33
38
|
streamid,
|
|
39
|
+
unidirectional,
|
|
40
|
+
incoming,
|
|
34
41
|
capsuleParser,
|
|
35
42
|
sendWindowOffset,
|
|
36
43
|
receiveWindowOffset,
|
|
37
44
|
shouldAutoTuneReceiveWindow,
|
|
38
45
|
receiveWindowSizeLimit,
|
|
39
|
-
sessionFlowController
|
|
46
|
+
sessionFlowController,
|
|
47
|
+
streamIdManager
|
|
40
48
|
}) {
|
|
41
49
|
/** @type {import('../stream').HttpWTStream} */
|
|
42
50
|
// @ts-ignore
|
|
@@ -58,11 +66,22 @@ export class Http2WebTransportStream {
|
|
|
58
66
|
sessionFlowController
|
|
59
67
|
})
|
|
60
68
|
this.sessionFlowController = sessionFlowController
|
|
69
|
+
this.streamIdManager = streamIdManager
|
|
61
70
|
|
|
62
71
|
this.final = false
|
|
63
72
|
this.stopReading_ = true
|
|
64
73
|
this.drainReads_ = true
|
|
65
74
|
this.recvBytes = 0
|
|
75
|
+
|
|
76
|
+
this.outgoingClosed_ = false
|
|
77
|
+
this.incomingClosed_ = false
|
|
78
|
+
if (unidirectional) {
|
|
79
|
+
if (incoming) {
|
|
80
|
+
this.outgoingClosed_ = true
|
|
81
|
+
} else {
|
|
82
|
+
this.incomingClosed_ = true
|
|
83
|
+
}
|
|
84
|
+
}
|
|
66
85
|
}
|
|
67
86
|
|
|
68
87
|
sendInitialParameters() {
|
|
@@ -71,12 +90,12 @@ export class Http2WebTransportStream {
|
|
|
71
90
|
|
|
72
91
|
/**
|
|
73
92
|
* @param {Object} obj
|
|
74
|
-
* @param {Uint8Array} obj.data
|
|
93
|
+
* @param {Uint8Array|undefined} obj.data
|
|
75
94
|
* @param {Boolean} obj.fin
|
|
76
95
|
*/
|
|
77
96
|
recvData({ data, fin }) {
|
|
78
97
|
this.incomdata.push({ data, fin })
|
|
79
|
-
if (data?.byteLength > 0) {
|
|
98
|
+
if (data && data?.byteLength > 0) {
|
|
80
99
|
const checkstream = this.flowController.updateHighestReceivedOffset(
|
|
81
100
|
data?.byteLength
|
|
82
101
|
)
|
|
@@ -115,13 +134,13 @@ export class Http2WebTransportStream {
|
|
|
115
134
|
) {
|
|
116
135
|
if (!buffer) {
|
|
117
136
|
const bytes = this.incomdata.reduce(
|
|
118
|
-
(prevVal, val) => prevVal + val.data?.byteLength,
|
|
137
|
+
(prevVal, val) => prevVal + ((val && val.data?.byteLength) || 0),
|
|
119
138
|
0
|
|
120
139
|
)
|
|
121
140
|
if (bytes > 0) {
|
|
122
141
|
buffer = this.jsobj.getReadBuffer({
|
|
123
142
|
byteSize: this.incomdata.reduce(
|
|
124
|
-
(prevVal, val) => prevVal + val.data?.byteLength,
|
|
143
|
+
(prevVal, val) => prevVal + ((val && val.data?.byteLength) || 0),
|
|
125
144
|
0
|
|
126
145
|
)
|
|
127
146
|
})
|
|
@@ -194,10 +213,40 @@ export class Http2WebTransportStream {
|
|
|
194
213
|
this.stopReading_ = true
|
|
195
214
|
}
|
|
196
215
|
|
|
216
|
+
/**
|
|
217
|
+
* @param {'resetStream'|'stopSending'} type
|
|
218
|
+
*/
|
|
219
|
+
onStreamSignal(type) {
|
|
220
|
+
switch (type) {
|
|
221
|
+
case 'resetStream':
|
|
222
|
+
this.closeIncoming()
|
|
223
|
+
break
|
|
224
|
+
case 'stopSending':
|
|
225
|
+
this.closeOutgoing()
|
|
226
|
+
break
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
closeIncoming() {
|
|
231
|
+
this.incomingClosed_ = true
|
|
232
|
+
if (this.outgoingClosed_) this.onClose()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
closeOutgoing() {
|
|
236
|
+
this.outgoingClosed_ = true
|
|
237
|
+
if (this.incomingClosed_) this.onClose()
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
onClose() {
|
|
241
|
+
// ok, inform our session id manager
|
|
242
|
+
this.streamIdManager.onStreamClosed(this.streamid)
|
|
243
|
+
}
|
|
244
|
+
|
|
197
245
|
/**
|
|
198
246
|
* @param {Number} code
|
|
199
247
|
*/
|
|
200
248
|
stopSending(code) {
|
|
249
|
+
this.closeIncoming()
|
|
201
250
|
this.capsuleParser.writeCapsule({
|
|
202
251
|
type: ParserBase.WT_STOP_SENDING,
|
|
203
252
|
headerVints: [this.streamid, code],
|
|
@@ -210,10 +259,15 @@ export class Http2WebTransportStream {
|
|
|
210
259
|
)
|
|
211
260
|
}
|
|
212
261
|
|
|
262
|
+
onFin() {
|
|
263
|
+
this.closeIncoming()
|
|
264
|
+
}
|
|
265
|
+
|
|
213
266
|
/**
|
|
214
267
|
* @param {Number} code
|
|
215
268
|
*/
|
|
216
269
|
resetStream(code) {
|
|
270
|
+
this.closeOutgoing()
|
|
217
271
|
this.capsuleParser.writeCapsule({
|
|
218
272
|
type: ParserBase.WT_RESET_STREAM,
|
|
219
273
|
headerVints: [this.streamid, code],
|
|
@@ -238,8 +292,8 @@ export class Http2WebTransportStream {
|
|
|
238
292
|
while (
|
|
239
293
|
this.outgochunks.length > 0 &&
|
|
240
294
|
(!this.capsuleParser.blocked || this.final) &&
|
|
241
|
-
this.flowController.sendWindowSize() >
|
|
242
|
-
this.sessionFlowController.sendWindowSize() >
|
|
295
|
+
this.flowController.sendWindowSize() > 0n &&
|
|
296
|
+
this.sessionFlowController.sendWindowSize() > 0n
|
|
243
297
|
) {
|
|
244
298
|
const cur = this.outgochunks.shift()
|
|
245
299
|
if (cur) {
|
|
@@ -308,6 +362,7 @@ export class Http2WebTransportStream {
|
|
|
308
362
|
* @param {bigint} windowOffset
|
|
309
363
|
*/
|
|
310
364
|
sendWindowUpdate(windowOffset) {
|
|
365
|
+
log('sendwindow offset stream:', windowOffset)
|
|
311
366
|
this.capsuleParser.writeCapsule({
|
|
312
367
|
type: ParserBase.WT_MAX_STREAM_DATA,
|
|
313
368
|
headerVints: [this.streamid, windowOffset],
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import { logger } from '../utils.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('../types').StreamIdClient} StreamIdClient
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
function GetMaxStreamCount() {
|
|
8
|
+
return (0xffffffffn >> 2n) + 1n
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const pid = typeof process !== 'undefined' ? process.pid : 0
|
|
12
|
+
const log = logger(`webtransport:streamidmanager(${pid})`)
|
|
13
|
+
// Ported from libquiche, QuicStreamIdManager so their license applies to the original in C++ and this javascript translation
|
|
14
|
+
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
|
|
15
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
16
|
+
// found in the LICENSE file.
|
|
17
|
+
|
|
18
|
+
export class StreamIdManager {
|
|
19
|
+
/**
|
|
20
|
+
* @param {{delegate: StreamIdClient
|
|
21
|
+
* unidirectional: boolean,
|
|
22
|
+
* isclient: boolean,
|
|
23
|
+
* maxAllowedOutgoingStreams: number,
|
|
24
|
+
* maxAllowedIncomingStreams: number}} arg
|
|
25
|
+
*/
|
|
26
|
+
constructor({
|
|
27
|
+
delegate,
|
|
28
|
+
unidirectional,
|
|
29
|
+
isclient,
|
|
30
|
+
maxAllowedOutgoingStreams,
|
|
31
|
+
maxAllowedIncomingStreams
|
|
32
|
+
}) {
|
|
33
|
+
this.delegate = delegate
|
|
34
|
+
this.unidirectional = unidirectional
|
|
35
|
+
this.isclient = isclient
|
|
36
|
+
|
|
37
|
+
this.outgoingMaxStreams = BigInt(maxAllowedOutgoingStreams)
|
|
38
|
+
|
|
39
|
+
// The ID to use for the next outgoing stream.
|
|
40
|
+
this.nextOutgoingStreamId = this.getFirstOutgoingStreamId()
|
|
41
|
+
|
|
42
|
+
// The number of outgoing streams that have ever been opened, including those
|
|
43
|
+
// that have been closed. This number must never be larger than
|
|
44
|
+
// outgoing_max_streams_.
|
|
45
|
+
this.outgoingStreamCount = 0n
|
|
46
|
+
|
|
47
|
+
// FOR INCOMING STREAMS
|
|
48
|
+
|
|
49
|
+
// The actual maximum number of streams that can be opened by the peer.
|
|
50
|
+
this.incomingActualMaxStreams = BigInt(maxAllowedIncomingStreams)
|
|
51
|
+
// Max incoming stream number that has been advertised to the peer and is <=
|
|
52
|
+
// incoming_actual_max_streams_. It is set to incoming_actual_max_streams_
|
|
53
|
+
// when a MAX_STREAMS is sent.
|
|
54
|
+
this.incomingAdvertisedMaxStreams = BigInt(maxAllowedIncomingStreams)
|
|
55
|
+
|
|
56
|
+
// Initial maximum on the number of open streams allowed.
|
|
57
|
+
this.incomingInitialMaxOpenStreams = BigInt(maxAllowedIncomingStreams)
|
|
58
|
+
|
|
59
|
+
// The number of streams that have been created, including open ones and
|
|
60
|
+
// closed ones.
|
|
61
|
+
this.incomingStreamCount = 0n
|
|
62
|
+
|
|
63
|
+
// Set of stream ids that are less than the largest stream id that has been
|
|
64
|
+
// received, but are nonetheless available to be created.
|
|
65
|
+
this.availableStreams = new Set()
|
|
66
|
+
|
|
67
|
+
this.largestPeerCreatedStreamId = BigInt(Number.MAX_SAFE_INTEGER)
|
|
68
|
+
|
|
69
|
+
// If true, then the stream limit will never be increased.
|
|
70
|
+
this.stopIncreasingIncomingMaxStreams = false
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {bigint} streamCount
|
|
75
|
+
*/
|
|
76
|
+
onStreamsBlockedFrame(streamCount) {
|
|
77
|
+
if (streamCount > this.incomingAdvertisedMaxStreams) {
|
|
78
|
+
// Peer thinks it can send more streams that we've told it.
|
|
79
|
+
return {
|
|
80
|
+
error:
|
|
81
|
+
"StreamsBlockedFrame's stream count " +
|
|
82
|
+
streamCount +
|
|
83
|
+
' exceeds incoming max stream ' +
|
|
84
|
+
this.incomingAdvertisedMaxStreams
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (this.incomingAdvertisedMaxStreams === this.incomingActualMaxStreams) {
|
|
88
|
+
// We have told peer about current max.
|
|
89
|
+
return { success: true }
|
|
90
|
+
}
|
|
91
|
+
if (
|
|
92
|
+
streamCount < this.incomingActualMaxStreams &&
|
|
93
|
+
this.delegate.canSendMaxStreams()
|
|
94
|
+
) {
|
|
95
|
+
// Peer thinks it's blocked on a stream count that is less than our current
|
|
96
|
+
// max. Inform the peer of the correct stream count.
|
|
97
|
+
this.sendMaxStreamsFrame()
|
|
98
|
+
}
|
|
99
|
+
return { success: true }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {bigint} maxOpenStreams
|
|
104
|
+
*/
|
|
105
|
+
maybeAllowNewOutgoingStreams(maxOpenStreams) {
|
|
106
|
+
if (maxOpenStreams <= this.outgoingMaxStreams) {
|
|
107
|
+
// Only update the stream count if it would increase the limit.
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// This implementation only supports 32 bit Stream IDs, so limit max streams
|
|
112
|
+
// if it would exceed the max 32 bits can express.
|
|
113
|
+
const maxStreamCount = GetMaxStreamCount()
|
|
114
|
+
if (maxOpenStreams < maxStreamCount)
|
|
115
|
+
this.outgoingMaxStreams = maxOpenStreams
|
|
116
|
+
else this.outgoingMaxStreams = maxStreamCount
|
|
117
|
+
|
|
118
|
+
return true
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* @param {bigint} maxOpenStreams
|
|
123
|
+
*/
|
|
124
|
+
setMaxOpenIncomingStreams(maxOpenStreams) {
|
|
125
|
+
if (this.incomingStreamCount > 0)
|
|
126
|
+
throw new Error(
|
|
127
|
+
'non-zero incoming stream count ' +
|
|
128
|
+
this.incomingStreamCount +
|
|
129
|
+
+' when setting max incoming stream to ' +
|
|
130
|
+
maxOpenStreams
|
|
131
|
+
)
|
|
132
|
+
if (this.incomingInitialMaxOpenStreams !== maxOpenStreams)
|
|
133
|
+
log(
|
|
134
|
+
this.unidirectional ? 'unidirectional ' : 'bidirectional: ',
|
|
135
|
+
'incoming stream limit changed from ',
|
|
136
|
+
this.incomingInitialMaxOpenStreams,
|
|
137
|
+
' to ',
|
|
138
|
+
maxOpenStreams
|
|
139
|
+
)
|
|
140
|
+
this.incomingActualMaxStreams = maxOpenStreams
|
|
141
|
+
this.incomingAdvertisedMaxStreams = maxOpenStreams
|
|
142
|
+
this.incomingInitialMaxOpenStreams = maxOpenStreams
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
maybeSendMaxStreamsFrame() {
|
|
146
|
+
const divisor = 2n // may be modify
|
|
147
|
+
|
|
148
|
+
if (divisor > 0n) {
|
|
149
|
+
if (
|
|
150
|
+
this.incomingAdvertisedMaxStreams - this.incomingStreamCount >
|
|
151
|
+
this.incomingInitialMaxOpenStreams / divisor
|
|
152
|
+
) {
|
|
153
|
+
// window too large, no advertisement
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (
|
|
158
|
+
this.delegate.canSendMaxStreams() &&
|
|
159
|
+
this.incomingAdvertisedMaxStreams < this.incomingActualMaxStreams
|
|
160
|
+
) {
|
|
161
|
+
this.sendMaxStreamsFrame()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
sendMaxStreamsFrame() {
|
|
166
|
+
if (this.incomingAdvertisedMaxStreams >= this.incomingActualMaxStreams)
|
|
167
|
+
throw new Error(
|
|
168
|
+
'this.incomingAdvertisedMaxStreams >= this.incomingActualMaxStreams' +
|
|
169
|
+
this.incomingAdvertisedMaxStreams +
|
|
170
|
+
'vs.' +
|
|
171
|
+
this.incomingActualMaxStreams
|
|
172
|
+
)
|
|
173
|
+
this.incomingAdvertisedMaxStreams = this.incomingActualMaxStreams
|
|
174
|
+
this.delegate.sendMaxStreams(
|
|
175
|
+
this.incomingAdvertisedMaxStreams,
|
|
176
|
+
this.unidirectional
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
sendMaxStreamsFrameInitial() {
|
|
181
|
+
this.delegate.sendMaxStreams(
|
|
182
|
+
this.incomingAdvertisedMaxStreams,
|
|
183
|
+
this.unidirectional
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @param {bigint} streamId
|
|
189
|
+
*/
|
|
190
|
+
onStreamClosed(streamId) {
|
|
191
|
+
// Nothing to do for outgoing streams.
|
|
192
|
+
if (
|
|
193
|
+
(this.isclient && streamId & 0x1n) ||
|
|
194
|
+
(!this.isclient && !(streamId & 0x1n))
|
|
195
|
+
)
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
// If the stream is inbound, we can increase the actual stream limit and maybe
|
|
199
|
+
// advertise the new limit to the peer.
|
|
200
|
+
if (this.incomingActualMaxStreams === GetMaxStreamCount()) {
|
|
201
|
+
// Reached the maximum stream id value that the implementation
|
|
202
|
+
// supports. Nothing can be done here.
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
if (!this.stopIncreasingIncomingMaxStreams) {
|
|
206
|
+
// One stream closed, and another one can be opened.
|
|
207
|
+
this.incomingActualMaxStreams++
|
|
208
|
+
this.maybeSendMaxStreamsFrame()
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
getNextOutgoingStreamId() {
|
|
213
|
+
if (this.outgoingStreamCount >= this.outgoingMaxStreams)
|
|
214
|
+
throw new Error(
|
|
215
|
+
'Attempt to allocate a new outgoing stream that would exceed the ' +
|
|
216
|
+
'limit (' +
|
|
217
|
+
+Number(this.outgoingMaxStreams) +
|
|
218
|
+
')'
|
|
219
|
+
)
|
|
220
|
+
const id = this.nextOutgoingStreamId
|
|
221
|
+
this.nextOutgoingStreamId += 1n << 2n
|
|
222
|
+
this.outgoingStreamCount++
|
|
223
|
+
return id
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
canOpenNextOutgoingStream() {
|
|
227
|
+
return this.outgoingStreamCount < this.outgoingMaxStreams
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
isMaxStreamSet() {
|
|
231
|
+
return this.outgoingMaxStreams > 0n
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* @param {bigint} streamId
|
|
236
|
+
*/
|
|
237
|
+
maybeIncreaseLargestPeerStreamId(streamId) {
|
|
238
|
+
// |stream_id| must be an incoming stream of the right directionality.
|
|
239
|
+
|
|
240
|
+
if (this.availableStreams.has(streamId)) {
|
|
241
|
+
this.availableStreams.delete(streamId)
|
|
242
|
+
// stream_id is available.
|
|
243
|
+
return true
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Calculate increment of incoming_stream_count_ by creating stream_id.
|
|
247
|
+
const delta = 1n << 2n
|
|
248
|
+
const leastNewStreamId =
|
|
249
|
+
this.largestPeerCreatedStreamId === BigInt(Number.MAX_SAFE_INTEGER)
|
|
250
|
+
? this.getFirstIncomingStreamId()
|
|
251
|
+
: this.largestPeerCreatedStreamId + delta
|
|
252
|
+
const streamCountIncrement = (streamId - leastNewStreamId) / delta + 1n
|
|
253
|
+
|
|
254
|
+
if (
|
|
255
|
+
this.incomingStreamCount + streamCountIncrement >
|
|
256
|
+
this.incomingAdvertisedMaxStreams
|
|
257
|
+
) {
|
|
258
|
+
log(
|
|
259
|
+
'Failed to create a new incoming stream with id:' +
|
|
260
|
+
streamId +
|
|
261
|
+
', reaching MAX_STREAMS limit: ' +
|
|
262
|
+
this.incomingAdvertisedMaxStreams +
|
|
263
|
+
'.'
|
|
264
|
+
)
|
|
265
|
+
return {
|
|
266
|
+
error:
|
|
267
|
+
'Stream id ' +
|
|
268
|
+
streamId +
|
|
269
|
+
' would exceed stream count limit ' +
|
|
270
|
+
this.incomingAdvertisedMaxStreams
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
for (let id = leastNewStreamId; id < streamId; id += delta) {
|
|
275
|
+
this.availableStreams.add(id)
|
|
276
|
+
}
|
|
277
|
+
this.incomingStreamCount += streamCountIncrement
|
|
278
|
+
this.largestPeerCreatedStreamId = streamId
|
|
279
|
+
return true
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* @param {number} id
|
|
284
|
+
*/
|
|
285
|
+
isAvailableStream(id) {
|
|
286
|
+
if ((this.isclient && id & 0x1) || (!this.isclient && !(id & 0x1))) {
|
|
287
|
+
// Stream IDs under next_ougoing_stream_id_ are either open or previously
|
|
288
|
+
// open but now closed.
|
|
289
|
+
return id >= this.nextOutgoingStreamId
|
|
290
|
+
}
|
|
291
|
+
// For peer created streams, we also need to consider available streams.
|
|
292
|
+
return (
|
|
293
|
+
this.largestPeerCreatedStreamId === BigInt(Number.MAX_SAFE_INTEGER) ||
|
|
294
|
+
id > this.largestPeerCreatedStreamId ||
|
|
295
|
+
this.availableStreams.has(id)
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
getFirstOutgoingStreamId() {
|
|
300
|
+
let streamid = 0n
|
|
301
|
+
if (this.isclient) streamid |= 0x1n
|
|
302
|
+
if (this.unidirectional) streamid |= 0x2n
|
|
303
|
+
return streamid
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
getFirstIncomingStreamId() {
|
|
307
|
+
let streamid = 0n
|
|
308
|
+
if (!this.isclient) streamid |= 0x1n
|
|
309
|
+
if (this.unidirectional) streamid |= 0x2n
|
|
310
|
+
return streamid
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
get availableIncomingStreams() {
|
|
314
|
+
return this.incomingAdvertisedMaxStreams - this.incomingStreamCount
|
|
315
|
+
}
|
|
316
|
+
}
|