@amritk/nish-x86_64-linux 0.17.0 → 0.20.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/INSTALL.md +43 -25
- package/bin/nish +0 -0
- package/package.json +2 -2
- package/runtime/nish.d.ts +55 -5
- package/runtime/nish.h +231 -20
- package/runtime/nish.mjs +16 -0
- package/runtime/runtime-aes.c +490 -0
- package/runtime/runtime-cross.c +366 -0
- package/runtime/runtime-host.c +7 -6
- package/runtime/runtime-net.c +93 -34
- package/runtime/runtime-os.c +213 -15
- package/runtime/runtime-parallel.c +590 -107
- package/runtime/runtime-simd.c +203 -0
- package/runtime/runtime-wasm.c +16 -3
- package/runtime/runtime.c +89 -23
- package/runtime/shim.mjs +71 -1
- package/scripts/build.sh +225 -20
- package/std/README.md +86 -55
- package/std/bytes.ts +163 -0
- package/std/crypto/LICENSE-libsecp256k1 +19 -0
- package/std/crypto/aes.ts +238 -76
- package/std/crypto/chacha20poly1305.ts +43 -9
- package/std/crypto/ed25519.ts +840 -0
- package/std/crypto/hkdf.ts +304 -12
- package/std/crypto/hmac.ts +245 -53
- package/std/crypto/p256.ts +1376 -42
- package/std/crypto/x25519.ts +55 -37
- package/std/crypto/x509-random.ts +71 -0
- package/std/crypto/x509.ts +9 -9
- package/std/json-tokenizer.ts +1296 -0
- package/std/json-typed.ts +1014 -0
- package/std/json-value.ts +1163 -0
- package/std/json.ts +1412 -70
- package/std/net/buffer-pool.ts +151 -0
- package/std/net/hpack.ts +416 -112
- package/std/net/http-fields.ts +93 -0
- package/std/net/http1-server.ts +417 -55
- package/std/net/http1.ts +31 -6
- package/std/net/http2-tls.ts +112 -24
- package/std/net/http2.ts +333 -75
- package/std/net/http3-server.ts +231 -45
- package/std/net/http3.ts +187 -28
- package/std/net/prefork.ts +412 -0
- package/std/net/quic-conn-params.ts +86 -16
- package/std/net/quic-listener.ts +576 -219
- package/std/net/quic-packet.ts +443 -58
- package/std/net/quic-recovery.ts +361 -30
- package/std/net/quic-stream.ts +434 -28
- package/std/net/quic.ts +922 -206
- package/std/net/router.ts +676 -0
- package/std/net/tls/codec.ts +426 -27
- package/std/net/tls/record-server.ts +478 -132
- package/std/net/tls/record.ts +133 -42
- package/std/net/tls/schedule.ts +464 -55
- package/std/net/tls/ticket.ts +610 -0
- package/std/net/tls-tcp.ts +204 -57
- package/std/net/tls.ts +1132 -305
- package/std/net/url.ts +571 -0
- package/std/random.ts +94 -0
- package/std/text.ts +77 -0
- package/std/threads.ts +438 -14
package/std/net/http3.ts
CHANGED
|
@@ -86,9 +86,11 @@
|
|
|
86
86
|
* **No server push.** It never sends PUSH_PROMISE; a client's MAX_PUSH_ID is
|
|
87
87
|
* allowed, checked and ignored.
|
|
88
88
|
*
|
|
89
|
-
* **Caps are start-up numbers**, all in `Http3Config
|
|
90
|
-
*
|
|
91
|
-
*
|
|
89
|
+
* **Caps are start-up numbers**, all in `Http3Config`. The stream state for
|
|
90
|
+
* every QUIC stream slot and the control frame are made by the constructor;
|
|
91
|
+
* the body chunk and the field section are borrowed, around one read, from
|
|
92
|
+
* pools that a server's connections share (`share`) and that a standalone
|
|
93
|
+
* connection keeps one buffer each of. A field section's
|
|
92
94
|
* names and values are copied into arrays the connection keeps, which grow to
|
|
93
95
|
* the largest section seen and are then reused, so a warm connection
|
|
94
96
|
* allocates nothing per request (`docs/security/http3.md`, H3-2). A peer's
|
|
@@ -121,6 +123,13 @@
|
|
|
121
123
|
* Private helpers share the importing program's flat symbol namespace
|
|
122
124
|
* (`docs/wp26-stdlib.md` §3e), which is why each one carries the module's name.
|
|
123
125
|
*
|
|
126
|
+
* **The Date.** A response carries the `date` in `conn.date`, 29 bytes of
|
|
127
|
+
* IMF-fixdate, right after `:status`, unless the program's fields hold their
|
|
128
|
+
* own; trailers carry none. It counts against the peer's
|
|
129
|
+
* SETTINGS_MAX_FIELD_SECTION_SIZE. A bare connection has no clock, so `date`
|
|
130
|
+
* starts empty and nothing is sent; `Http3Server` fills it from its
|
|
131
|
+
* `HttpDate` (RFC 9110 §6.6.1).
|
|
132
|
+
*
|
|
124
133
|
* Written from RFC 9114 and RFC 9204, not ported from another implementation.
|
|
125
134
|
*/
|
|
126
135
|
import {
|
|
@@ -191,8 +200,9 @@ import {
|
|
|
191
200
|
h3VarintLength,
|
|
192
201
|
} from "nish/net/http3-frame"
|
|
193
202
|
import { QUIC_STATE_CONNECTED, QuicConnection } from "nish/net/quic"
|
|
194
|
-
import { QUIC_STREAM_END, QUIC_STREAM_ERR_RESET, QuicStream } from "nish/net/quic-stream"
|
|
203
|
+
import { QUIC_STREAM_END, QUIC_STREAM_ERR_RESET, QuicStream, quicStreamGrowFree } from "nish/net/quic-stream"
|
|
195
204
|
import { quicVarintPut } from "nish/net/quic-packet"
|
|
205
|
+
import { BufferPool } from "nish/net/buffer-pool"
|
|
196
206
|
|
|
197
207
|
/** The ALPN protocol identifier of HTTP/3 (RFC 9114 §3.1). */
|
|
198
208
|
export const H3_ALPN: string = "h3"
|
|
@@ -344,6 +354,21 @@ const http3CheckCap = (what: string, value: i32, low: i32, high: i32): void => {
|
|
|
344
354
|
}
|
|
345
355
|
}
|
|
346
356
|
|
|
357
|
+
/** Whether one of `names` is `date` in any ASCII case (RFC 9110 §5.1). */
|
|
358
|
+
const http3NamesHoldDate = (names: u8[][]): boolean => {
|
|
359
|
+
// An ASCII letter's case is its 0x20 bit, and every octet of `date` is a letter.
|
|
360
|
+
const bit: i32 = 32
|
|
361
|
+
for (const name of names) {
|
|
362
|
+
if (toI32(name.length) === 4) {
|
|
363
|
+
const d: boolean = (toI32(name[0]) | bit) === 100 && (toI32(name[1]) | bit) === 97
|
|
364
|
+
if (d && (toI32(name[2]) | bit) === 116 && (toI32(name[3]) | bit) === 101) {
|
|
365
|
+
return true
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return false
|
|
370
|
+
}
|
|
371
|
+
|
|
347
372
|
/** Empties `bytes`, keeping its storage for the next fill. */
|
|
348
373
|
const http3Empty = (bytes: u8[]): void => {
|
|
349
374
|
while (toI32(bytes.length) > 0) {
|
|
@@ -381,10 +406,20 @@ export class Http3Connection {
|
|
|
381
406
|
/** The client's SETTINGS. */
|
|
382
407
|
peer: Http3Settings
|
|
383
408
|
header: Http3FrameHeader
|
|
384
|
-
/**
|
|
409
|
+
/**
|
|
410
|
+
* H3_DATA's bytes, and where a payload being skipped is read to: a
|
|
411
|
+
* `bodyChunk`-byte buffer borrowed from `dataPool` for the read that needs
|
|
412
|
+
* it and held while an H3_DATA window is live, else `none`.
|
|
413
|
+
*/
|
|
385
414
|
data: u8[]
|
|
386
|
-
/** A HEADERS payload read whole, for the decoder
|
|
415
|
+
/** A HEADERS payload read whole, for the decoder: a buffer borrowed from `sectionPool` around one read and decode, else `none`. */
|
|
387
416
|
section: u8[]
|
|
417
|
+
/** The empty array `data` and `section` are while they hold nothing borrowed. */
|
|
418
|
+
none: u8[]
|
|
419
|
+
/** Where `data` is borrowed from: the connection's own pool until `share` hands it its server's. */
|
|
420
|
+
dataPool: BufferPool
|
|
421
|
+
/** Where `section` is borrowed from, likewise. */
|
|
422
|
+
sectionPool: BufferPool
|
|
388
423
|
/** A control frame's payload, read whole. */
|
|
389
424
|
control: u8[]
|
|
390
425
|
/** Frame headers, SETTINGS and GOAWAY on their way out. */
|
|
@@ -400,6 +435,15 @@ export class Http3Connection {
|
|
|
400
435
|
valueList: u8[][]
|
|
401
436
|
statusName: u8[]
|
|
402
437
|
statusValue: u8[]
|
|
438
|
+
/**
|
|
439
|
+
* The `date` every response carries (RFC 9110 §6.6.1), as the 29 bytes of
|
|
440
|
+
* an IMF-fixdate, or empty for none. A bare connection has no clock, so it
|
|
441
|
+
* starts empty; `Http3Server` points it at its `HttpDate`'s bytes, which
|
|
442
|
+
* that rewrites in place once a second.
|
|
443
|
+
*/
|
|
444
|
+
date: u8[]
|
|
445
|
+
/** The bytes of `date`, the field's name, made once. */
|
|
446
|
+
dateName: u8[]
|
|
403
447
|
/** The SETTINGS this side sends. */
|
|
404
448
|
settingIds: i64[]
|
|
405
449
|
settingValues: i64[]
|
|
@@ -460,6 +504,8 @@ export class Http3Connection {
|
|
|
460
504
|
dataLength: i32 = 0
|
|
461
505
|
/** How much of `control` is filled. */
|
|
462
506
|
controlFill: i32 = 0
|
|
507
|
+
/** How far into the borrowed `data` reads have written: what its give-back zeroes. */
|
|
508
|
+
dataUsed: i32 = 0
|
|
463
509
|
state: i32 = 0
|
|
464
510
|
/** Requests open: taken and not yet finished both ways. */
|
|
465
511
|
live: i32 = 0
|
|
@@ -523,8 +569,13 @@ export class Http3Connection {
|
|
|
523
569
|
this.fields = new HttpFields()
|
|
524
570
|
this.peer = new Http3Settings()
|
|
525
571
|
this.header = new Http3FrameHeader()
|
|
526
|
-
this.
|
|
527
|
-
this.
|
|
572
|
+
this.none = []
|
|
573
|
+
this.data = this.none
|
|
574
|
+
this.section = this.none
|
|
575
|
+
this.dataPool = new BufferPool("Http3Connection.data", config.bodyChunk, 1)
|
|
576
|
+
this.sectionPool = new BufferPool("Http3Connection.section", config.maxFieldSectionSize, 1)
|
|
577
|
+
quicStreamGrowFree(this.dataPool.free, 1, this.none)
|
|
578
|
+
quicStreamGrowFree(this.sectionPool.free, 1, this.none)
|
|
528
579
|
this.control = new Array<u8>(H3_CONTROL_FRAME_MAX)
|
|
529
580
|
this.scratch = new Array<u8>(64)
|
|
530
581
|
this.zeros = new Array<u8>(256)
|
|
@@ -539,6 +590,8 @@ export class Http3Connection {
|
|
|
539
590
|
this.valueList = []
|
|
540
591
|
this.statusName = httpFieldBytes(":status")
|
|
541
592
|
this.statusValue = [48, 48, 48]
|
|
593
|
+
this.date = []
|
|
594
|
+
this.dateName = httpFieldBytes("date")
|
|
542
595
|
this.settingIds = [
|
|
543
596
|
H3_SETTINGS_QPACK_MAX_TABLE_CAPACITY,
|
|
544
597
|
H3_SETTINGS_QPACK_BLOCKED_STREAMS,
|
|
@@ -580,9 +633,11 @@ export class Http3Connection {
|
|
|
580
633
|
/**
|
|
581
634
|
* Puts the connection back as the constructor left it, for the QUIC
|
|
582
635
|
* connection's next peer (call it after `quic.reset`): no stream known,
|
|
583
|
-
* QPACK's state and the client's SETTINGS forgotten
|
|
636
|
+
* QPACK's state and the client's SETTINGS forgotten, and any buffer
|
|
637
|
+
* still borrowed given back. It allocates nothing.
|
|
584
638
|
*/
|
|
585
639
|
restart(): void {
|
|
640
|
+
this.giveBack()
|
|
586
641
|
this.slotId.fill(H3_NONE)
|
|
587
642
|
this.flags.fill(H3_ZERO)
|
|
588
643
|
this.headLen.fill(H3_ZERO)
|
|
@@ -713,6 +768,89 @@ export class Http3Connection {
|
|
|
713
768
|
* connection takes, until it answers H3_NEED_MORE or H3_ERROR.
|
|
714
769
|
*/
|
|
715
770
|
next(): i32 {
|
|
771
|
+
// The last H3_DATA window ends here. A body read chunk by chunk keeps the
|
|
772
|
+
// one buffer; any other event, H3_NEED_MORE among them, gives it back.
|
|
773
|
+
const event: i32 = this.nextEvent()
|
|
774
|
+
if (event !== H3_DATA) {
|
|
775
|
+
this.giveData()
|
|
776
|
+
}
|
|
777
|
+
return event
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Lends this connection its server's pools in place of its own, giving
|
|
782
|
+
* back to the old pools whatever it holds. Panics unless their buffers are
|
|
783
|
+
* `bodyChunk` and `maxFieldSectionSize` bytes long, as this connection's
|
|
784
|
+
* reads need.
|
|
785
|
+
*/
|
|
786
|
+
share(data: BufferPool, section: BufferPool): void {
|
|
787
|
+
if (data.size !== this.config.bodyChunk || section.size !== this.config.maxFieldSectionSize) {
|
|
788
|
+
panic(
|
|
789
|
+
`Http3Connection: pools of ${data.size} and ${section.size} bytes shared with a connection of bodyChunk ${this.config.bodyChunk} and maxFieldSectionSize ${this.config.maxFieldSectionSize}`
|
|
790
|
+
)
|
|
791
|
+
}
|
|
792
|
+
this.giveBack()
|
|
793
|
+
this.dataPool = data
|
|
794
|
+
this.sectionPool = section
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/** Releases the QUIC connection (`QuicConnection.release`) and gives back every pooled buffer this connection holds. */
|
|
798
|
+
release(): void {
|
|
799
|
+
this.quic.release()
|
|
800
|
+
this.giveBack()
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/** Gives back `data` and `section` if either is borrowed: the connection holds no pooled buffer after it. */
|
|
804
|
+
giveBack(): void {
|
|
805
|
+
this.giveData()
|
|
806
|
+
this.giveSection(H3_ZERO)
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/** `data`, borrowed from `dataPool` first if it is not already. */
|
|
810
|
+
borrowData(): u8[] {
|
|
811
|
+
if (this.data.length === 0) {
|
|
812
|
+
this.data = this.dataPool.take()
|
|
813
|
+
this.dataUsed = 0
|
|
814
|
+
}
|
|
815
|
+
return this.data
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/** Gives `data` back to `dataPool`, zeroed as far as reads wrote, if it is borrowed. */
|
|
819
|
+
giveData(): void {
|
|
820
|
+
if (this.data.length > 0) {
|
|
821
|
+
this.dataPool.give(this.data, this.dataUsed)
|
|
822
|
+
this.data = this.none
|
|
823
|
+
this.dataUsed = 0
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/** Reads up to `want` bytes of stream `id` into the start of `data`, borrowed for it: `streamRead`'s answer. */
|
|
828
|
+
readData(id: i64, want: i32): i32 {
|
|
829
|
+
const n: i32 = this.quic.streamRead(id, this.borrowData(), H3_ZERO, want)
|
|
830
|
+
if (n > this.dataUsed) {
|
|
831
|
+
this.dataUsed = n
|
|
832
|
+
}
|
|
833
|
+
return n
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/** `section`, borrowed from `sectionPool`. */
|
|
837
|
+
borrowSection(): u8[] {
|
|
838
|
+
if (this.section.length === 0) {
|
|
839
|
+
this.section = this.sectionPool.take()
|
|
840
|
+
}
|
|
841
|
+
return this.section
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/** Gives `section` back to `sectionPool`, zeroed over `[0, used)`, if it is borrowed. */
|
|
845
|
+
giveSection(used: i32): void {
|
|
846
|
+
if (this.section.length > 0) {
|
|
847
|
+
this.sectionPool.give(this.section, used)
|
|
848
|
+
this.section = this.none
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/** `next`'s work: everything but the lending of `data` around it. */
|
|
853
|
+
nextEvent(): i32 {
|
|
716
854
|
if (this.state === H3_STATE_FAILED) {
|
|
717
855
|
return H3_ERROR
|
|
718
856
|
}
|
|
@@ -924,7 +1062,7 @@ export class Http3Connection {
|
|
|
924
1062
|
} else if (phase === H3_PHASE_CONTROL) {
|
|
925
1063
|
return this.controlStep(k, id)
|
|
926
1064
|
} else if (phase === H3_PHASE_ENCODER || phase === H3_PHASE_DECODER) {
|
|
927
|
-
const n: i32 = this.
|
|
1065
|
+
const n: i32 = this.readData(id, this.config.bodyChunk)
|
|
928
1066
|
if (n === 0) {
|
|
929
1067
|
return H3_NEED_MORE
|
|
930
1068
|
}
|
|
@@ -1027,10 +1165,12 @@ export class Http3Connection {
|
|
|
1027
1165
|
this.controlFill = 0
|
|
1028
1166
|
}
|
|
1029
1167
|
if (this.frameLeft[k] > 0) {
|
|
1030
|
-
const
|
|
1031
|
-
const
|
|
1032
|
-
const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(
|
|
1033
|
-
const n: i32 =
|
|
1168
|
+
const skip: boolean = this.frameType[k] === H3_SKIP_TYPE
|
|
1169
|
+
const room: i32 = skip ? this.config.bodyChunk : toI32(this.control.length) - this.controlFill
|
|
1170
|
+
const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(room)))
|
|
1171
|
+
const n: i32 = skip
|
|
1172
|
+
? this.readData(id, want)
|
|
1173
|
+
: this.quic.streamRead(id, this.control, this.controlFill, want)
|
|
1034
1174
|
if (n === 0) {
|
|
1035
1175
|
return H3_NEED_MORE
|
|
1036
1176
|
}
|
|
@@ -1118,9 +1258,9 @@ export class Http3Connection {
|
|
|
1118
1258
|
|
|
1119
1259
|
/** Reads stream `id` into `data` until nothing more is there; answers 0, or the `QUIC_STREAM_*` that ended it. */
|
|
1120
1260
|
drop(id: i64): i32 {
|
|
1121
|
-
let n: i32 = this.
|
|
1261
|
+
let n: i32 = this.readData(id, this.config.bodyChunk)
|
|
1122
1262
|
while (n > 0) {
|
|
1123
|
-
n = this.
|
|
1263
|
+
n = this.readData(id, this.config.bodyChunk)
|
|
1124
1264
|
}
|
|
1125
1265
|
return n
|
|
1126
1266
|
}
|
|
@@ -1189,7 +1329,7 @@ export class Http3Connection {
|
|
|
1189
1329
|
if (phase === H3_PHASE_WT) {
|
|
1190
1330
|
if ((this.flags[k] & H3_FLAG_RECV_DONE) === 0) {
|
|
1191
1331
|
const resetCode: i64 = stream.resetCode
|
|
1192
|
-
const n: i32 = this.
|
|
1332
|
+
const n: i32 = this.readData(id, this.config.bodyChunk)
|
|
1193
1333
|
if (n > 0) {
|
|
1194
1334
|
this.stream = id
|
|
1195
1335
|
this.dataStart = 0
|
|
@@ -1421,24 +1561,28 @@ export class Http3Connection {
|
|
|
1421
1561
|
return this.writable(k, id)
|
|
1422
1562
|
}
|
|
1423
1563
|
const length: i32 = toI32(this.frameLeft[k])
|
|
1424
|
-
const n: i32 = this.quic.streamRead(id, this.
|
|
1564
|
+
const n: i32 = this.quic.streamRead(id, this.borrowSection(), H3_ZERO, length)
|
|
1425
1565
|
if (n < 0) {
|
|
1566
|
+
this.giveSection(H3_ZERO)
|
|
1426
1567
|
return this.ended(k, id, n, stream.resetCode)
|
|
1427
1568
|
}
|
|
1428
1569
|
if (n !== length) {
|
|
1570
|
+
this.giveSection(n)
|
|
1429
1571
|
return this.writable(k, id)
|
|
1430
1572
|
}
|
|
1431
1573
|
this.frameLeft[k] = -1
|
|
1574
|
+
// `fieldSection` copies what it keeps into `fields`: the section is not read after it.
|
|
1432
1575
|
const event: i32 = this.fieldSection(k, id, length)
|
|
1576
|
+
this.giveSection(length)
|
|
1433
1577
|
if (event !== H3_NEED_MORE) {
|
|
1434
1578
|
return event
|
|
1435
1579
|
}
|
|
1436
1580
|
continue
|
|
1437
1581
|
}
|
|
1438
1582
|
// DATA, a payload being skipped, or an unknown frame's.
|
|
1439
|
-
const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(
|
|
1583
|
+
const want: i32 = toI32(http3Min(this.frameLeft[k], toI64(this.config.bodyChunk)))
|
|
1440
1584
|
const resetCode: i64 = stream.resetCode
|
|
1441
|
-
const n: i32 = this.
|
|
1585
|
+
const n: i32 = this.readData(id, want)
|
|
1442
1586
|
if (n === 0) {
|
|
1443
1587
|
return this.writable(k, id)
|
|
1444
1588
|
}
|
|
@@ -1711,13 +1855,19 @@ export class Http3Connection {
|
|
|
1711
1855
|
/**
|
|
1712
1856
|
* A request whose field section is past `maxFieldSectionSize`: answered
|
|
1713
1857
|
* with a 431 and its stream asked to stop with H3_NO_ERROR (§4.2.2,
|
|
1714
|
-
* §4.1.2). The program never sees it.
|
|
1858
|
+
* §4.1.2). The program never sees it. A peer whose own
|
|
1859
|
+
* SETTINGS_MAX_FIELD_SECTION_SIZE has room for a bare 431 but not for one
|
|
1860
|
+
* with the date gets it without the date rather than a reset.
|
|
1715
1861
|
*/
|
|
1716
1862
|
answerTooLarge(k: i32, id: i64): i32 {
|
|
1717
1863
|
this.tooLarge = h3Count(this.tooLarge)
|
|
1718
1864
|
const empty: u8[][] = this.nameList
|
|
1719
1865
|
http3EmptyList(empty)
|
|
1720
|
-
|
|
1866
|
+
let r: i32 = this.writeHead(k, id, H3_STATUS_TOO_LARGE, empty, empty, true, true)
|
|
1867
|
+
if (r === H3_TOO_LARGE) {
|
|
1868
|
+
r = this.writeHead(k, id, H3_STATUS_TOO_LARGE, empty, empty, true, false)
|
|
1869
|
+
}
|
|
1870
|
+
if (r !== 0) {
|
|
1721
1871
|
this.abandon(k, id, H3_EXCESSIVE_LOAD)
|
|
1722
1872
|
}
|
|
1723
1873
|
this.quic.streamStopSending(id, H3_NO_ERROR)
|
|
@@ -1833,11 +1983,14 @@ export class Http3Connection {
|
|
|
1833
1983
|
}
|
|
1834
1984
|
|
|
1835
1985
|
/**
|
|
1836
|
-
* Encodes `:status` (unless it is 0, for trailers)
|
|
1837
|
-
*
|
|
1838
|
-
*
|
|
1986
|
+
* Encodes `:status` (unless it is 0, for trailers), the server's `date`
|
|
1987
|
+
* for a final status when `dated` unless `names` holds one, and
|
|
1988
|
+
* `names`/`values` into a HEADERS frame and writes it whole to stream `id`,
|
|
1989
|
+
* with the FIN when `fin`. The date counts against the peer's
|
|
1990
|
+
* SETTINGS_MAX_FIELD_SECTION_SIZE as any field does. Answers 0, H3_AGAIN,
|
|
1991
|
+
* or H3_TOO_LARGE.
|
|
1839
1992
|
*/
|
|
1840
|
-
writeHead(k: i32, id: i64, status: i32, names: u8[][], values: u8[][], fin: boolean): i32 {
|
|
1993
|
+
writeHead(k: i32, id: i64, status: i32, names: u8[][], values: u8[][], fin: boolean, dated: boolean): i32 {
|
|
1841
1994
|
const out: u8[] = this.encoded
|
|
1842
1995
|
http3Empty(out)
|
|
1843
1996
|
this.encoder.beginSection(out)
|
|
@@ -1862,6 +2015,12 @@ export class Http3Connection {
|
|
|
1862
2015
|
)
|
|
1863
2016
|
size = toI64(nameLength + valueLength + QPACK_FIELD_OVERHEAD)
|
|
1864
2017
|
}
|
|
2018
|
+
if (dated && status >= 200 && toI32(this.date.length) > 0 && !http3NamesHoldDate(names)) {
|
|
2019
|
+
const nl: i32 = toI32(this.dateName.length)
|
|
2020
|
+
const vl: i32 = toI32(this.date.length)
|
|
2021
|
+
this.encoder.encodeField(out, this.dateName, H3_ZERO, nl, this.date, H3_ZERO, vl, false, true)
|
|
2022
|
+
size = size + toI64(nl + vl + QPACK_FIELD_OVERHEAD)
|
|
2023
|
+
}
|
|
1865
2024
|
const n: i32 = toI32(names.length)
|
|
1866
2025
|
for (let j: i32 = 0; j < n && j < toI32(names.length) && j < toI32(values.length); j++) {
|
|
1867
2026
|
const name: u8[] = names[j]
|
|
@@ -1922,7 +2081,7 @@ export class Http3Connection {
|
|
|
1922
2081
|
) {
|
|
1923
2082
|
return H3_INVALID
|
|
1924
2083
|
}
|
|
1925
|
-
return this.writeHead(k, id, status, names, values, endStream)
|
|
2084
|
+
return this.writeHead(k, id, status, names, values, endStream, true)
|
|
1926
2085
|
}
|
|
1927
2086
|
|
|
1928
2087
|
/**
|
|
@@ -1942,7 +2101,7 @@ export class Http3Connection {
|
|
|
1942
2101
|
) {
|
|
1943
2102
|
return H3_INVALID
|
|
1944
2103
|
}
|
|
1945
|
-
return this.writeHead(k, id, H3_ZERO, names, values, true)
|
|
2104
|
+
return this.writeHead(k, id, H3_ZERO, names, values, true, false)
|
|
1946
2105
|
}
|
|
1947
2106
|
|
|
1948
2107
|
/**
|