@fails-components/webtransport 0.3.1 → 1.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.
Files changed (162) hide show
  1. package/lib/client.js +81 -40
  2. package/lib/dom.ts +7 -5
  3. package/lib/http2/browser/browser.js +168 -0
  4. package/lib/http2/browser/browserparser.js +248 -0
  5. package/lib/http2/flowcontroller.js +371 -0
  6. package/lib/http2/node/capsuleparser.js +291 -0
  7. package/lib/http2/node/client.js +239 -0
  8. package/lib/http2/node/index.js +2 -0
  9. package/lib/http2/node/server.js +499 -0
  10. package/lib/http2/node/websocketparser.js +829 -0
  11. package/lib/http2/parserbase.js +139 -0
  12. package/lib/http2/parserbasehttp2.js +128 -0
  13. package/lib/http2/session.js +225 -0
  14. package/lib/http2/stream.js +354 -0
  15. package/lib/http2/websocketcommon.js +1 -0
  16. package/lib/{index.js → index.browser.js} +6 -10
  17. package/lib/index.node.js +27 -0
  18. package/lib/server.js +252 -69
  19. package/lib/session.js +31 -68
  20. package/lib/stream.js +103 -179
  21. package/lib/types.ts +174 -87
  22. package/lib/utils.js +2 -1
  23. package/lib/webstreams.browser.js +6 -0
  24. package/lib/webstreams.js +7 -0
  25. package/lib/webtransport.browser.js +284 -0
  26. package/lib/webtransport.node.js +137 -0
  27. package/lib/{webtransport.js → webtransportbase.js} +47 -41
  28. package/old_test/test.js +31 -29
  29. package/old_test/testsuite.js +1 -1
  30. package/package.json +18 -17
  31. package/readme.md +94 -30
  32. package/test/bidirectional-streams.spec.js +51 -9
  33. package/test/datagrams.spec.js +8 -2
  34. package/test/fixtures/known-bytes.js +29 -0
  35. package/test/fixtures/read-stream.js +1 -1
  36. package/test/fixtures/reader-value.js +1 -1
  37. package/test/fixtures/server.js +24 -10
  38. package/test/fixtures/webtransport.browser.js +20 -2
  39. package/test/fixtures/webtransport.js +1 -1
  40. package/test/index.js +33 -6
  41. package/test/pw-no-https-errors.json +5 -0
  42. package/test/session.spec.js +71 -52
  43. package/test/unidirectional-streams.spec.js +20 -4
  44. package/CMakeLists.txt +0 -991
  45. package/LICENSE.chromium +0 -27
  46. package/LICENSE.envoy +0 -207
  47. package/build.js +0 -313
  48. package/dist/lib/client.d.ts +0 -78
  49. package/dist/lib/client.d.ts.map +0 -1
  50. package/dist/lib/dom.d.ts +0 -87
  51. package/dist/lib/dom.d.ts.map +0 -1
  52. package/dist/lib/error.d.ts +0 -8
  53. package/dist/lib/error.d.ts.map +0 -1
  54. package/dist/lib/event-loop.d.ts +0 -33
  55. package/dist/lib/event-loop.d.ts.map +0 -1
  56. package/dist/lib/index.d.ts +0 -79
  57. package/dist/lib/index.d.ts.map +0 -1
  58. package/dist/lib/native.d.ts +0 -2
  59. package/dist/lib/native.d.ts.map +0 -1
  60. package/dist/lib/server.d.ts +0 -92
  61. package/dist/lib/server.d.ts.map +0 -1
  62. package/dist/lib/session.d.ts +0 -298
  63. package/dist/lib/session.d.ts.map +0 -1
  64. package/dist/lib/stream.d.ts +0 -135
  65. package/dist/lib/stream.d.ts.map +0 -1
  66. package/dist/lib/transport.d.ts +0 -34
  67. package/dist/lib/transport.d.ts.map +0 -1
  68. package/dist/lib/types.d.ts +0 -212
  69. package/dist/lib/types.d.ts.map +0 -1
  70. package/dist/lib/utils.d.ts +0 -11
  71. package/dist/lib/utils.d.ts.map +0 -1
  72. package/dist/lib/webtransport.d.ts +0 -47
  73. package/dist/lib/webtransport.d.ts.map +0 -1
  74. package/dist/test/bidirectional-streams.spec.d.ts +0 -5
  75. package/dist/test/bidirectional-streams.spec.d.ts.map +0 -1
  76. package/dist/test/datagrams.spec.d.ts +0 -5
  77. package/dist/test/datagrams.spec.d.ts.map +0 -1
  78. package/dist/test/event-loop.node.d.ts +0 -2
  79. package/dist/test/event-loop.node.d.ts.map +0 -1
  80. package/dist/test/fixtures/certificate.d.ts +0 -21
  81. package/dist/test/fixtures/certificate.d.ts.map +0 -1
  82. package/dist/test/fixtures/chai.d.ts +0 -2
  83. package/dist/test/fixtures/chai.d.ts.map +0 -1
  84. package/dist/test/fixtures/known-bytes.d.ts +0 -6
  85. package/dist/test/fixtures/known-bytes.d.ts.map +0 -1
  86. package/dist/test/fixtures/p-timeout.d.ts +0 -8
  87. package/dist/test/fixtures/p-timeout.d.ts.map +0 -1
  88. package/dist/test/fixtures/read-cert-hash.d.ts +0 -6
  89. package/dist/test/fixtures/read-cert-hash.d.ts.map +0 -1
  90. package/dist/test/fixtures/read-stream.d.ts +0 -19
  91. package/dist/test/fixtures/read-stream.d.ts.map +0 -1
  92. package/dist/test/fixtures/reader-value.d.ts +0 -22
  93. package/dist/test/fixtures/reader-value.d.ts.map +0 -1
  94. package/dist/test/fixtures/server.d.ts +0 -5
  95. package/dist/test/fixtures/server.d.ts.map +0 -1
  96. package/dist/test/fixtures/webtransport.browser.d.ts +0 -3
  97. package/dist/test/fixtures/webtransport.browser.d.ts.map +0 -1
  98. package/dist/test/fixtures/webtransport.d.ts +0 -3
  99. package/dist/test/fixtures/webtransport.d.ts.map +0 -1
  100. package/dist/test/fixtures/write-stream.d.ts +0 -10
  101. package/dist/test/fixtures/write-stream.d.ts.map +0 -1
  102. package/dist/test/index.d.ts +0 -2
  103. package/dist/test/index.d.ts.map +0 -1
  104. package/dist/test/session.spec.d.ts +0 -5
  105. package/dist/test/session.spec.d.ts.map +0 -1
  106. package/dist/test/unidirectional-streams.spec.d.ts +0 -2
  107. package/dist/test/unidirectional-streams.spec.d.ts.map +0 -1
  108. package/failslogolicensereadme.txt +0 -4
  109. package/lib/event-loop.js +0 -88
  110. package/lib/native.js +0 -28
  111. package/lib/transport.js +0 -82
  112. package/platform/icufix/fakedata.cpp +0 -8
  113. package/platform/quiche/quic/core/io/socket_win.inc +0 -3
  114. package/platform/quiche_platform_impl/quiche_bug_tracker_impl.h +0 -7
  115. package/platform/quiche_platform_impl/quiche_client_stats_impl.h +0 -7
  116. package/platform/quiche_platform_impl/quiche_command_line_flags_impl.h +0 -7
  117. package/platform/quiche_platform_impl/quiche_containers_impl.h +0 -7
  118. package/platform/quiche_platform_impl/quiche_event_loop_impl.h +0 -7
  119. package/platform/quiche_platform_impl/quiche_export_impl.h +0 -6
  120. package/platform/quiche_platform_impl/quiche_flag_utils_impl.h +0 -11
  121. package/platform/quiche_platform_impl/quiche_flags_impl.h +0 -8
  122. package/platform/quiche_platform_impl/quiche_iovec_impl.h +0 -10
  123. package/platform/quiche_platform_impl/quiche_logging_impl.h +0 -21
  124. package/platform/quiche_platform_impl/quiche_mem_slice_impl.h +0 -2
  125. package/platform/quiche_platform_impl/quiche_mutex_impl.h +0 -8
  126. package/platform/quiche_platform_impl/quiche_prefetch_impl.h +0 -11
  127. package/platform/quiche_platform_impl/quiche_reference_counted_impl.h +0 -16
  128. package/platform/quiche_platform_impl/quiche_server_stats_impl.h +0 -7
  129. package/platform/quiche_platform_impl/quiche_stack_trace_impl.h +0 -12
  130. package/platform/quiche_platform_impl/quiche_stream_buffer_allocator_impl.h +0 -8
  131. package/platform/quiche_platform_impl/quiche_testvalue_impl.h +0 -1
  132. package/platform/quiche_platform_impl/quiche_thread_local_impl.h +0 -6
  133. package/platform/quiche_platform_impl/quiche_time_utils_impl.h +0 -13
  134. package/platform/quiche_platform_impl/quiche_udp_socket_platform_impl.h +0 -39
  135. package/src/http3backendresponse.cc +0 -36
  136. package/src/http3backendresponse.h +0 -109
  137. package/src/http3client.cc +0 -1529
  138. package/src/http3client.h +0 -557
  139. package/src/http3clientsession.cc +0 -65
  140. package/src/http3clientsession.h +0 -60
  141. package/src/http3clientstream.cc +0 -52
  142. package/src/http3clientstream.h +0 -42
  143. package/src/http3dispatcher.cc +0 -62
  144. package/src/http3dispatcher.h +0 -57
  145. package/src/http3eventloop.cc +0 -1107
  146. package/src/http3eventloop.h +0 -316
  147. package/src/http3server.cc +0 -514
  148. package/src/http3server.h +0 -151
  149. package/src/http3serverbackend.cc +0 -105
  150. package/src/http3serverbackend.h +0 -98
  151. package/src/http3serversession.cc +0 -123
  152. package/src/http3serversession.h +0 -95
  153. package/src/http3serverstream.cc +0 -463
  154. package/src/http3serverstream.h +0 -114
  155. package/src/http3sessioncache.cc +0 -85
  156. package/src/http3sessioncache.h +0 -59
  157. package/src/http3wtsessionvisitor.cc +0 -46
  158. package/src/http3wtsessionvisitor.h +0 -397
  159. package/src/http3wtstreamvisitor.cc +0 -155
  160. package/src/http3wtstreamvisitor.h +0 -404
  161. package/test/event-loop.node.js +0 -24
  162. package/tsconfig.json +0 -39
@@ -0,0 +1,371 @@
1
+ import { logger } from '../utils.js'
2
+
3
+ /**
4
+ * @typedef {import('../types').FlowControlable} FlowControlable
5
+ */
6
+
7
+ const pid = typeof process !== 'undefined' ? process.pid : 0
8
+ const log = logger(`webtransport:flowcontroller(${pid})`)
9
+ // Ported from libquiche, QuicFlowController so their license applies to the original in C++ and this javascript translation
10
+ // Copyright 2014 The Chromium Authors. All rights reserved.
11
+ // Use of this source code is governed by a BSD-style license that can be
12
+ // found in the LICENSE file.
13
+
14
+ export class FlowController {
15
+ static kSessionFlowControlMultiplier = 1.5
16
+
17
+ /**
18
+ * @param {{tocontrol: FlowControlable
19
+ * sendWindowOffset: number,
20
+ * receiveWindowOffset: number,
21
+ * shouldAutoTuneReceiveWindow: boolean
22
+ * receiveWindowSizeLimit: number,
23
+ * sessionFlowController?: FlowController}} arg
24
+ */
25
+
26
+ constructor({
27
+ tocontrol,
28
+ sendWindowOffset,
29
+ receiveWindowOffset,
30
+ receiveWindowSizeLimit,
31
+ shouldAutoTuneReceiveWindow,
32
+ sessionFlowController
33
+ }) {
34
+ this.tocontrol = tocontrol
35
+ this.bytesSent = 0n
36
+ this.sendWindowOffset = BigInt(sendWindowOffset)
37
+ this.bytesConsumed = 0n
38
+ this.highestReceivedByteOffset = 0n
39
+ this.receiveWindowOffset = BigInt(receiveWindowOffset)
40
+ this.receiveWindowSize = BigInt(receiveWindowOffset)
41
+ this.receiveWindowSizeLimit = BigInt(receiveWindowSizeLimit)
42
+ this.autoTuneReceiveWindow = shouldAutoTuneReceiveWindow
43
+ this.sessionFlowController = sessionFlowController
44
+ this.lastBlockedSendWindowOffset = 0n
45
+ this.prevWindowUpdateTime = undefined
46
+
47
+ log(
48
+ 'Created flow controller ' +
49
+ ', setting initial receive window offset to: ' +
50
+ this.receiveWindowOffset +
51
+ ', max receive window to: ' +
52
+ this.receiveWindowSize +
53
+ ', max receive window limit to: ' +
54
+ this.receiveWindowSizeLimit +
55
+ ', setting send window offset to: ' +
56
+ this.sendWindowOffset
57
+ )
58
+ }
59
+
60
+ /**
61
+ * @param {Number} bytesConsumed
62
+ */
63
+ addBytesConsumed(bytesConsumed) {
64
+ this.bytesConsumed += BigInt(bytesConsumed)
65
+ log(' consumed ' + bytesConsumed + ' bytes.')
66
+
67
+ this.maybeSendWindowUpdate()
68
+ }
69
+
70
+ /**
71
+ * @param {Number} increaseOffset
72
+ */
73
+ updateHighestReceivedOffset(increaseOffset) {
74
+ // Only update if offset has increased.
75
+ /* if (newOffset <= this.highestReceivedByteOffset) {
76
+ return false
77
+ } */
78
+
79
+ log(
80
+ ' highest byte offset increased from ' + this.highestReceivedByteOffset,
81
+ ' to ',
82
+ this.highestReceivedByteOffset + BigInt(increaseOffset)
83
+ )
84
+ this.highestReceivedByteOffset += BigInt(increaseOffset)
85
+ return true
86
+ }
87
+
88
+ /**
89
+ * @param {Number} nbytesSent
90
+ */
91
+ addBytesSent(nbytesSent) {
92
+ const bytesSent = BigInt(nbytesSent)
93
+ if (this.bytesSent + bytesSent > this.sendWindowOffset) {
94
+ log(
95
+ ' Trying to send an extra ' +
96
+ bytesSent +
97
+ ' bytes, when bytes_sent = ' +
98
+ this.bytesSent +
99
+ ', and send_window_offset_ = ' +
100
+ this.sendWindowOffset
101
+ )
102
+ this.bytesSent = this.sendWindowOffset
103
+
104
+ // This is an error on our side, close the connection as soon as possible.
105
+ this.tocontrol.closeConnection({
106
+ code: 63, // QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA,
107
+ reason:
108
+ this.sendWindowOffset -
109
+ (this.bytesSent + bytesSent) +
110
+ 'bytes over send window offset'
111
+ })
112
+ return
113
+ }
114
+ this.bytesSent += bytesSent
115
+ log(' sent ' + bytesSent + ' bytes.')
116
+ }
117
+
118
+ flowControlViolation() {
119
+ if (this.highestReceivedByteOffset > this.receiveWindowOffset) {
120
+ log(
121
+ 'Flow control violation on ' +
122
+ ', receive window offset: ' +
123
+ this.receiveWindowOffset +
124
+ ', highest received byte offset: ' +
125
+ this.highestReceivedByteOffset
126
+ )
127
+ return true
128
+ }
129
+ return false
130
+ }
131
+
132
+ maybeIncreaseMaxWindowSize() {
133
+ // Core of receive window auto tuning. This method should be called before a
134
+ // WINDOW_UPDATE frame is sent. Ideally, window updates should occur close to
135
+ // once per RTT. If a window update happens much faster than RTT, it implies
136
+ // that the flow control window is imposing a bottleneck. To prevent this,
137
+ // this method will increase the receive window size (subject to a reasonable
138
+ // upper bound). For simplicity this algorithm is deliberately asymmetric, in
139
+ // that it may increase window size but never decreases.
140
+
141
+ // Keep track of timing between successive window updates.
142
+ const now = Date.now()
143
+ const prev = this.prevWindowUpdateTime
144
+ this.prevWindowUpdateTime = now
145
+ if (!prev) {
146
+ log('first window update for ')
147
+ return
148
+ }
149
+
150
+ if (!this.autoTuneReceiveWindow) {
151
+ return
152
+ }
153
+
154
+ // TODO port need a replacement for this
155
+ // Get outbound RTT.
156
+ const rtt = this.tocontrol.smoothedRtt()
157
+ if (rtt === 0) {
158
+ log('rtt zero for ')
159
+ return
160
+ }
161
+
162
+ // Now we can compare timing of window updates with RTT.
163
+ const sinceLast = now - prev
164
+ const twoRtt = 2 * rtt
165
+
166
+ if (sinceLast >= twoRtt) {
167
+ // If interval between window updates is sufficiently large, there
168
+ // is no need to increase receive_window_size_.
169
+ return
170
+ }
171
+ const oldWindow = this.receiveWindowSize
172
+ this.increaseWindowSize()
173
+
174
+ if (this.receiveWindowSize > oldWindow) {
175
+ log(
176
+ 'New max window increase for ' +
177
+ +' after ' +
178
+ sinceLast +
179
+ ' us, and RTT is ' +
180
+ rtt +
181
+ 'us. max wndw: ' +
182
+ this.receiveWindowSize
183
+ )
184
+ if (this.sessionFlowController !== undefined) {
185
+ this.sessionFlowController.ensureWindowAtLeast(
186
+ BigInt(
187
+ FlowController.kSessionFlowControlMultiplier *
188
+ Number(this.receiveWindowSize)
189
+ )
190
+ )
191
+ }
192
+ } else {
193
+ // TODO(ckrasic) - add a varz to track this (?).
194
+ log(
195
+ 'Max window at limit for ' +
196
+ ' after ' +
197
+ sinceLast +
198
+ ' us, and RTT is ' +
199
+ rtt +
200
+ 'us. Limit size: ' +
201
+ this.receiveWindowSize
202
+ )
203
+ }
204
+ }
205
+
206
+ increaseWindowSize() {
207
+ this.receiveWindowSize *= 2n
208
+ this.receiveWindowSize =
209
+ this.receiveWindowSize > this.receiveWindowSizeLimit
210
+ ? this.receiveWindowSizeLimit
211
+ : this.receiveWindowSize
212
+ }
213
+
214
+ windowUpdateThreshold() {
215
+ return this.receiveWindowSize / 2n
216
+ }
217
+
218
+ maybeSendWindowUpdate() {
219
+ if (!this.tocontrol.connected()) {
220
+ return
221
+ }
222
+ // Send WindowUpdate to increase receive window if
223
+ // (receive window offset - consumed bytes) < (max window / 2).
224
+ // This is behaviour copied from SPDY.
225
+
226
+ const availableWindow = this.receiveWindowOffset - this.bytesConsumed
227
+ const threshold = this.windowUpdateThreshold()
228
+
229
+ if (!this.prevWindowUpdateTime) {
230
+ // Treat the initial window as if it is a window update, so if 1/2 the
231
+ // window is used in less than 2 RTTs, the window is increased.
232
+ this.prevWindowUpdateTime = Date.now()
233
+ }
234
+
235
+ if (availableWindow >= threshold) {
236
+ log(
237
+ 'Not sending WindowUpdate for ' +
238
+ ', available window: ' +
239
+ availableWindow +
240
+ ' >= threshold: ' +
241
+ threshold
242
+ )
243
+ return
244
+ }
245
+
246
+ this.maybeIncreaseMaxWindowSize()
247
+ this.updateReceiveWindowOffsetAndSendWindowUpdate(availableWindow)
248
+ }
249
+
250
+ /**
251
+ * @param {bigint} availableWindow
252
+ */
253
+ updateReceiveWindowOffsetAndSendWindowUpdate(availableWindow) {
254
+ // Update our receive window.
255
+ this.receiveWindowOffset += this.receiveWindowSize - availableWindow
256
+
257
+ log(
258
+ 'Sending WindowUpdate frame for ' +
259
+ ', consumed bytes: ' +
260
+ this.bytesConsumed +
261
+ ', available window: ' +
262
+ availableWindow +
263
+ ', and threshold: ' +
264
+ this.windowUpdateThreshold() +
265
+ ', and receive window size: ' +
266
+ this.receiveWindowSize +
267
+ '. New receive window offset is: ' +
268
+ this.receiveWindowOffset
269
+ )
270
+
271
+ this.sendWindowUpdate()
272
+ }
273
+
274
+ maybeSendBlocked() {
275
+ if (
276
+ this.sendWindowSize() !== 0 ||
277
+ this.lastBlockedSendWindowOffset >= this.sendWindowOffset
278
+ ) {
279
+ return
280
+ }
281
+ log(
282
+ ' is flow control blocked. ' +
283
+ 'Send window: ' +
284
+ this.sendWindowSize() +
285
+ ', bytes sent: ' +
286
+ this.bytesSent +
287
+ ', send limit: ' +
288
+ this.sendWindowOffset
289
+ )
290
+ // The entire send_window has been consumed, we are now flow control
291
+ // blocked.
292
+
293
+ // Keep track of when we last sent a BLOCKED frame so that we only send one
294
+ // at a given send offset.
295
+ this.lastBlockedSendWindowOffset = this.sendWindowOffset
296
+ this.tocontrol.sendBlocked(this.lastBlockedSendWindowOffset)
297
+ }
298
+
299
+ /**
300
+ * @param {bigint} newSendWindowOffset
301
+ */
302
+ updateSendWindowOffset(newSendWindowOffset) {
303
+ // Only update if send window has increased.
304
+ if (newSendWindowOffset <= this.sendWindowOffset) {
305
+ return false
306
+ }
307
+
308
+ log(
309
+ 'UpdateSendWindowOffset for ' +
310
+ ' with new offset ' +
311
+ newSendWindowOffset +
312
+ ' current offset: ' +
313
+ this.sendWindowOffset +
314
+ ' bytes_sent: ' +
315
+ this.bytesSent
316
+ )
317
+
318
+ // The flow is now unblocked but could have also been unblocked
319
+ // before. Return true iff this update caused a change from blocked
320
+ // to unblocked.
321
+ const wasPreviouslyBlocked = this.isBlocked()
322
+ this.sendWindowOffset = newSendWindowOffset
323
+ return wasPreviouslyBlocked
324
+ }
325
+
326
+ /**
327
+ * @param {bigint} windowSize
328
+ */
329
+ ensureWindowAtLeast(windowSize) {
330
+ if (this.receiveWindowSizeLimit >= windowSize) {
331
+ return
332
+ }
333
+
334
+ const availableWindow = this.receiveWindowOffset - this.bytesConsumed
335
+ this.increaseWindowSize()
336
+ this.updateReceiveWindowOffsetAndSendWindowUpdate(availableWindow)
337
+ }
338
+
339
+ isBlocked() {
340
+ return this.sendWindowSize() === 0n
341
+ }
342
+
343
+ sendWindowSize() {
344
+ if (this.bytesSent > this.sendWindowOffset) {
345
+ return 0
346
+ }
347
+ return this.sendWindowOffset - this.bytesSent
348
+ }
349
+
350
+ /**
351
+ * @param {bigint} size
352
+ */
353
+ updateReceiveWindowSize(size) {
354
+ log('UpdateReceiveWindowSize for ' + ': ' + size)
355
+ if (this.receiveWindowSize !== this.receiveWindowOffset) {
356
+ log(
357
+ 'receive_window_size_:' +
358
+ this.receiveWindowSize +
359
+ ' != receive_window_offset:' +
360
+ this.receiveWindowOffset
361
+ )
362
+ return
363
+ }
364
+ this.receiveWindowSize = size
365
+ this.receiveWindowOffset = size
366
+ }
367
+
368
+ sendWindowUpdate() {
369
+ this.tocontrol.sendWindowUpdate(this.receiveWindowOffset)
370
+ }
371
+ }
@@ -0,0 +1,291 @@
1
+ import { ParserBaseHttp2, readVarInt, writeVarInt } from '../parserbasehttp2.js'
2
+ import { lengthVarInt } from '../parserbase.js'
3
+
4
+ export class Http2CapsuleParser extends ParserBaseHttp2 {
5
+ /**
6
+ * @param {import('../../types.js').ParserHttp2Init} stream
7
+ */
8
+ constructor({
9
+ stream,
10
+ nativesession,
11
+ isclient,
12
+ initialStreamSendWindowOffset,
13
+ initialStreamReceiveWindowOffset,
14
+ streamShouldAutoTuneReceiveWindow,
15
+ streamReceiveWindowSizeLimit
16
+ }) {
17
+ super({
18
+ stream,
19
+ nativesession,
20
+ isclient,
21
+ initialStreamSendWindowOffset,
22
+ initialStreamReceiveWindowOffset,
23
+ streamShouldAutoTuneReceiveWindow,
24
+ streamReceiveWindowSizeLimit
25
+ })
26
+ this.mode = 's' // capsule start
27
+ /** @type {Buffer|undefined} */
28
+ this.saveddata = undefined
29
+ /** @type {Number|undefined} */
30
+ this.rtype = undefined
31
+ }
32
+
33
+ /**
34
+ * @param {Buffer} data
35
+ */
36
+ parseData(data) {
37
+ let cdata = data
38
+ if (this.saveddata) {
39
+ cdata = Buffer.concat([this.saveddata, cdata])
40
+ delete this.saveddata
41
+ }
42
+ const bufferstate = { offset: 0, size: cdata.length, buffer: cdata }
43
+ while (bufferstate.size - bufferstate.offset > 0) {
44
+ switch (this.mode) {
45
+ case 's':
46
+ {
47
+ const capsulestart =
48
+ bufferstate.offset + bufferstate.buffer.byteOffset
49
+ const capsulemaxlength =
50
+ bufferstate.buffer.byteLength - bufferstate.offset
51
+ // we are at capsule start
52
+ if (bufferstate.size < 2 + bufferstate.offset) {
53
+ this.saveddata = Buffer.from(
54
+ bufferstate.buffer.buffer,
55
+ capsulestart,
56
+ capsulemaxlength
57
+ )
58
+ return
59
+ }
60
+ const type = Number(readVarInt(bufferstate))
61
+ if (
62
+ typeof type === 'undefined' ||
63
+ bufferstate.size < 1 + bufferstate.offset
64
+ ) {
65
+ this.saveddata = Buffer.from(
66
+ bufferstate.buffer.buffer,
67
+ capsulestart,
68
+ capsulemaxlength
69
+ )
70
+ return
71
+ }
72
+ const length = Number(readVarInt(bufferstate))
73
+ if (typeof length === 'undefined') {
74
+ this.saveddata = Buffer.from(
75
+ bufferstate.buffer.buffer,
76
+ capsulestart,
77
+ capsulemaxlength
78
+ )
79
+ return
80
+ }
81
+ let checklength = length // we want to read most times the full capsule
82
+ const offsetend = Math.min(
83
+ bufferstate.offset + length,
84
+ bufferstate.size
85
+ )
86
+ const offsetbegin = bufferstate.offset
87
+ if (
88
+ type === Http2CapsuleParser.PADDING ||
89
+ type === Http2CapsuleParser.WT_STREAM_WOFIN ||
90
+ type ===
91
+ Http2CapsuleParser.WT_STREAM_WFIN /* || type === Http2CapsuleParser.DATAGRAM */
92
+ ) {
93
+ checklength = Math.min(length, 64) // stream id + some Data
94
+ }
95
+ if (
96
+ checklength >
97
+ 4 * Number(this.session.flowController.receiveWindowSize) ||
98
+ length > 8 * Number(this.session.flowController.receiveWindowSize)
99
+ ) {
100
+ // too long abort, could be an attack vector
101
+ this.session.closeConnection({
102
+ code: 63, // QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA, // probably the right one...
103
+ reason: 'Frame length too big :' + length
104
+ })
105
+ return
106
+ }
107
+ if (bufferstate.size < checklength + bufferstate.offset) {
108
+ this.saveddata = Buffer.from(
109
+ bufferstate.buffer.buffer,
110
+ capsulestart,
111
+ capsulemaxlength
112
+ )
113
+ return
114
+ }
115
+ let streamid
116
+ switch (type) {
117
+ case Http2CapsuleParser.PADDING:
118
+ // only padding do nothing
119
+ break
120
+ case Http2CapsuleParser.WT_RESET_STREAM:
121
+ case Http2CapsuleParser.WT_STOP_SENDING:
122
+ {
123
+ const streamid = readVarInt(bufferstate)
124
+ const stream = this.wtstreams.get(streamid)
125
+ const code = readVarInt(bufferstate)
126
+ if (stream && typeof code !== 'undefined')
127
+ stream.jsobj.onStreamRecvSignal({
128
+ code,
129
+ nettask:
130
+ type === Http2CapsuleParser.WT_RESET_STREAM
131
+ ? 'resetStream'
132
+ : 'stopSending'
133
+ })
134
+ }
135
+ break
136
+ case Http2CapsuleParser.WT_STREAM_WOFIN:
137
+ case Http2CapsuleParser.WT_STREAM_WFIN:
138
+ streamid = Number(readVarInt(bufferstate))
139
+ if (typeof streamid !== 'undefined') {
140
+ let object = this.wtstreams.get(streamid)
141
+ if (!object) {
142
+ object = this.newStream(streamid)
143
+ }
144
+ // TODO submit data
145
+ if (offsetend - bufferstate.offset >= 0) {
146
+ object.recvData({
147
+ data:
148
+ offsetend - bufferstate.offset > 0
149
+ ? new Uint8Array(
150
+ bufferstate.buffer.buffer,
151
+ bufferstate.buffer.byteOffset +
152
+ bufferstate.offset,
153
+ offsetend - bufferstate.offset
154
+ )
155
+ : undefined,
156
+ fin:
157
+ type === Http2CapsuleParser.WT_STREAM_WFIN &&
158
+ bufferstate.size >= length + offsetbegin
159
+ })
160
+ }
161
+ }
162
+ break
163
+ case Http2CapsuleParser.WT_MAX_DATA:
164
+ this.onMaxData(readVarInt(bufferstate))
165
+ break
166
+ case Http2CapsuleParser.WT_MAX_STREAM_DATA:
167
+ this.onMaxStreamData(
168
+ readVarInt(bufferstate),
169
+ readVarInt(bufferstate)
170
+ )
171
+ break
172
+ case Http2CapsuleParser.WT_MAX_STREAMS_BIDI:
173
+ // this.recvSession({ maxstreams: readVarInt(bufferstate), type })
174
+ break
175
+ case Http2CapsuleParser.WT_MAX_STREAMS_UNIDI:
176
+ // this.recvSession({ maxstreams: readVarInt(bufferstate), type })
177
+ break
178
+ case Http2CapsuleParser.WT_DATA_BLOCKED:
179
+ this.onDataBlocked(readVarInt(bufferstate))
180
+ break
181
+ case Http2CapsuleParser.WT_STREAM_DATA_BLOCKED:
182
+ this.onStreamDataBlocked(
183
+ readVarInt(bufferstate),
184
+ readVarInt(bufferstate)
185
+ )
186
+ break
187
+ case Http2CapsuleParser.WT_STREAMS_BLOCKED_UNIDI:
188
+ /* {
189
+ const streamid = readVarInt(bufferstate)
190
+ const object = this.wtstreams.get(streamid)
191
+ if (object)
192
+ this.recvStream({
193
+ maxstreams: readVarInt(bufferstate),
194
+ type,
195
+ object
196
+ })
197
+ } */
198
+ break
199
+ case Http2CapsuleParser.WT_STREAMS_BLOCKED_BIDI:
200
+ /* {
201
+ const streamid = readVarInt(bufferstate)
202
+ const object = this.wtstreams.get(streamid)
203
+ if (object)
204
+ this.recvStream({
205
+ maxstreams: readVarInt(bufferstate),
206
+ type,
207
+ streamid
208
+ })
209
+ } */
210
+ break
211
+ case Http2CapsuleParser.DATAGRAM:
212
+ this.session.jsobj.onDatagramReceived({
213
+ datagram: new Uint8Array(
214
+ bufferstate.buffer.buffer,
215
+ bufferstate.buffer.byteOffset + bufferstate.offset,
216
+ offsetend - bufferstate.offset
217
+ )
218
+ })
219
+ break
220
+ default:
221
+ // do nothing
222
+ }
223
+
224
+ if (bufferstate.size < length + offsetbegin) {
225
+ this.remainlength = length + offsetbegin - bufferstate.size
226
+ this.mode = 'c'
227
+ if (streamid) {
228
+ this.rstreamid = streamid
229
+ this.rfin = type === Http2CapsuleParser.WT_STREAM_WFIN
230
+ }
231
+ }
232
+ bufferstate.offset = offsetend
233
+ }
234
+ break
235
+ case 'c':
236
+ {
237
+ const clength = Math.min(
238
+ bufferstate.size - bufferstate.offset,
239
+ this.remainlength
240
+ )
241
+ if (this.rstreamid) {
242
+ // TODO submitData
243
+ const object = this.wtstreams.get(this.rstreamid)
244
+ // TODO submit data
245
+ object.recvData({
246
+ data: new Uint8Array(
247
+ bufferstate.buffer.buffer,
248
+ bufferstate.buffer.byteOffset + bufferstate.offset,
249
+ clength
250
+ ),
251
+ fin:
252
+ this.rtype === Http2CapsuleParser.WT_STREAM_WFIN &&
253
+ this.remainlength === clength
254
+ })
255
+ }
256
+
257
+ this.remainlength = this.remainlength - clength
258
+ bufferstate.offset += clength
259
+ if (this.remainlength === 0) {
260
+ this.mode = 's'
261
+ delete this.rfin
262
+ delete this.rstreamid
263
+ }
264
+ }
265
+ break
266
+ }
267
+ }
268
+ }
269
+
270
+ /**
271
+ * @param{{type: Number, headerVints: Array<Number>, payload: Uint8Array|undefined}} bs
272
+ */
273
+ writeCapsule({ type, headerVints, payload }) {
274
+ let length = 0
275
+ for (const ind in headerVints) length += lengthVarInt(headerVints[ind])
276
+ let headlength = length
277
+ if (payload) length += payload.byteLength
278
+ headlength += lengthVarInt(length) + lengthVarInt(type)
279
+ const cdata = Buffer.alloc(headlength)
280
+ const bufferstate = { offset: 0, size: cdata.length, buffer: cdata }
281
+ writeVarInt(bufferstate, type)
282
+ writeVarInt(bufferstate, length)
283
+ for (const ind in headerVints) writeVarInt(bufferstate, headerVints[ind])
284
+ if (!this.stream) return
285
+ let blocked = !this.stream.write(cdata)
286
+ if (payload) blocked = !this.stream.write(payload) || blocked
287
+ // do something if blocked
288
+ if (blocked) this.blocked = true
289
+ return blocked
290
+ }
291
+ }