quico 0.1.0 → 0.1.3
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/CHANGELOG.md +6 -0
- package/README.md +216 -143
- package/examples/webtransport_datagram_echo_server/server.js +12 -18
- package/h3_server.js +416 -0
- package/h3_socket.js +473 -0
- package/index.js +3 -2611
- package/libs/crypto.js +182 -448
- package/libs/h3.js +312 -65
- package/libs/utils.js +12 -12
- package/package.json +60 -28
- package/quic_socket.js +1550 -0
- package/quico.svg +1 -1
package/libs/h3.js
CHANGED
|
@@ -16,11 +16,11 @@
|
|
|
16
16
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
import {
|
|
20
20
|
concatUint8Arrays,
|
|
21
21
|
writeVarInt,
|
|
22
22
|
readVarInt
|
|
23
|
-
}
|
|
23
|
+
} from './utils.js';
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
var huffman_codes = new Uint32Array([
|
|
@@ -415,15 +415,15 @@ var qpack_static_table_entries = [
|
|
|
415
415
|
|
|
416
416
|
|
|
417
417
|
function decodeVarInt(buf, prefixBits, pos) {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
418
|
+
var maxPrefix = (1 << prefixBits) - 1;
|
|
419
|
+
var byte = buf[pos];
|
|
420
|
+
var value = byte & maxPrefix;
|
|
421
421
|
pos++;
|
|
422
422
|
|
|
423
423
|
if (value < maxPrefix) // נגמר במרווח-הבייט הראשון
|
|
424
424
|
return { value, next: pos };
|
|
425
425
|
|
|
426
|
-
|
|
426
|
+
var m = 0;
|
|
427
427
|
while (true) {
|
|
428
428
|
byte = buf[pos++];
|
|
429
429
|
value += (byte & 0x7f) << m;
|
|
@@ -501,32 +501,32 @@ function decodeHuffman(buf) {
|
|
|
501
501
|
|
|
502
502
|
|
|
503
503
|
function parse_qpack_header_block(buf) {
|
|
504
|
-
|
|
505
|
-
|
|
504
|
+
var pos = 0;
|
|
505
|
+
var headers = [];
|
|
506
506
|
|
|
507
507
|
// Required Insert Count (prefix-8)
|
|
508
|
-
|
|
508
|
+
var ric = decodeVarInt(buf, 8, pos);
|
|
509
509
|
pos = ric.next;
|
|
510
510
|
|
|
511
511
|
// Delta Base (prefix-7 + S-bit)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
512
|
+
var firstDbByte = buf[pos];
|
|
513
|
+
var postBase = (firstDbByte & 0x80) !== 0; // S-bit
|
|
514
|
+
var db = decodeVarInt(buf, 7, pos);
|
|
515
515
|
pos = db.next;
|
|
516
516
|
|
|
517
517
|
// Base Index = RIC ± DB לפי S-bit
|
|
518
|
-
|
|
518
|
+
var baseIndex = postBase
|
|
519
519
|
? ric.value + db.value
|
|
520
520
|
: ric.value - db.value;
|
|
521
521
|
|
|
522
522
|
// Header Field Lines
|
|
523
523
|
while (pos < buf.length) {
|
|
524
|
-
|
|
524
|
+
var byte = buf[pos];
|
|
525
525
|
|
|
526
526
|
// A. Indexed Field Line – 1xxxxxxx
|
|
527
527
|
if ((byte & 0x80) === 0x80) {
|
|
528
|
-
|
|
529
|
-
|
|
528
|
+
var fromStatic = (byte & 0x40) !== 0; // T-bit
|
|
529
|
+
var idx = decodeVarInt(buf, 6, pos); // prefix-6
|
|
530
530
|
pos = idx.next;
|
|
531
531
|
headers.push({
|
|
532
532
|
type: "indexed",
|
|
@@ -538,18 +538,18 @@ function parse_qpack_header_block(buf) {
|
|
|
538
538
|
|
|
539
539
|
// B. Literal With Name Reference – 01xxxxxx
|
|
540
540
|
if ((byte & 0xC0) === 0x40) {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
541
|
+
var neverIndexed = (byte & 0x20) !== 0; // N-bit
|
|
542
|
+
var fromStatic = (byte & 0x10) !== 0; // T-bit
|
|
543
|
+
var nameIdx = decodeVarInt(buf, 4, pos); // prefix-4
|
|
544
544
|
pos = nameIdx.next;
|
|
545
545
|
|
|
546
|
-
|
|
547
|
-
|
|
546
|
+
var valH = (buf[pos] & 0x80) !== 0;
|
|
547
|
+
var valLen = decodeVarInt(buf, 7, pos); // prefix-7
|
|
548
548
|
pos = valLen.next;
|
|
549
549
|
|
|
550
|
-
|
|
550
|
+
var valBytes = buf.slice(pos, pos + valLen.value);
|
|
551
551
|
pos += valLen.value;
|
|
552
|
-
|
|
552
|
+
var value = valH ? decodeHuffman(valBytes)
|
|
553
553
|
: new TextDecoder().decode(valBytes);
|
|
554
554
|
|
|
555
555
|
headers.push({
|
|
@@ -565,23 +565,23 @@ function parse_qpack_header_block(buf) {
|
|
|
565
565
|
|
|
566
566
|
// C. Literal With Literal Name – 001xxxxx
|
|
567
567
|
if ((byte & 0xE0) === 0x20) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
568
|
+
var neverIndexed = (byte & 0x10) !== 0; // N-bit
|
|
569
|
+
var nameH = (byte & 0x08) !== 0; // H-bit
|
|
570
|
+
var nameLen = decodeVarInt(buf, 3, pos); // prefix-3
|
|
571
571
|
pos = nameLen.next;
|
|
572
572
|
|
|
573
|
-
|
|
573
|
+
var nameBytes = buf.slice(pos, pos + nameLen.value);
|
|
574
574
|
pos += nameLen.value;
|
|
575
|
-
|
|
575
|
+
var name = nameH ? decodeHuffman(nameBytes)
|
|
576
576
|
: new TextDecoder().decode(nameBytes);
|
|
577
577
|
|
|
578
|
-
|
|
579
|
-
|
|
578
|
+
var valH = (buf[pos] & 0x80) !== 0; // H-bit
|
|
579
|
+
var valLen = decodeVarInt(buf, 7, pos); // prefix-7
|
|
580
580
|
pos = valLen.next;
|
|
581
581
|
|
|
582
|
-
|
|
582
|
+
var valBytes = buf.slice(pos, pos + valLen.value);
|
|
583
583
|
pos += valLen.value;
|
|
584
|
-
|
|
584
|
+
var value = valH ? decodeHuffman(valBytes)
|
|
585
585
|
: new TextDecoder().decode(valBytes);
|
|
586
586
|
|
|
587
587
|
headers.push({
|
|
@@ -610,24 +610,24 @@ function parse_qpack_header_block(buf) {
|
|
|
610
610
|
|
|
611
611
|
|
|
612
612
|
function parse_qpack_header_block_old(buf) {
|
|
613
|
-
|
|
614
|
-
|
|
613
|
+
var pos = 0;
|
|
614
|
+
var headers = [];
|
|
615
615
|
|
|
616
616
|
/* 1) Field-section prefix */
|
|
617
|
-
|
|
617
|
+
var ric = decodeVarInt(buf, 8, pos); // Required Insert Count (prefix-8)
|
|
618
618
|
pos = ric.next;
|
|
619
619
|
|
|
620
|
-
|
|
620
|
+
var db = decodeVarInt(buf, 7, pos); // Delta Base (prefix-7)
|
|
621
621
|
pos = db.next;
|
|
622
622
|
|
|
623
623
|
/* 2) Field-line representations */
|
|
624
624
|
while (pos < buf.length) {
|
|
625
|
-
|
|
625
|
+
var byte = buf[pos];
|
|
626
626
|
|
|
627
627
|
/* A. Indexed Field Line – 1xxxxxxx */
|
|
628
628
|
if ((byte & 0x80) === 0x80) {
|
|
629
|
-
|
|
630
|
-
|
|
629
|
+
var fromStatic = (byte & 0x40) !== 0; // T-bit
|
|
630
|
+
var idx = decodeVarInt(buf, 6, pos); // prefix-6
|
|
631
631
|
pos = idx.next;
|
|
632
632
|
headers.push({
|
|
633
633
|
type: "indexed",
|
|
@@ -639,18 +639,18 @@ function parse_qpack_header_block_old(buf) {
|
|
|
639
639
|
|
|
640
640
|
/* B. Literal Field Line + Name Reference – 01xxxxxx */
|
|
641
641
|
if ((byte & 0xC0) === 0x40) {
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
642
|
+
var neverIndexed = (byte & 0x20) !== 0; // N-bit
|
|
643
|
+
var fromStatic = (byte & 0x10) !== 0; // T-bit
|
|
644
|
+
var nameIdx = decodeVarInt(buf, 4, pos); // prefix-4
|
|
645
645
|
pos = nameIdx.next;
|
|
646
646
|
|
|
647
|
-
|
|
648
|
-
|
|
647
|
+
var valH = (buf[pos] & 0x80) !== 0;
|
|
648
|
+
var valLen = decodeVarInt(buf, 7, pos); // prefix-7
|
|
649
649
|
pos = valLen.next;
|
|
650
650
|
|
|
651
|
-
|
|
651
|
+
var valBytes = buf.slice(pos, pos + valLen.value);
|
|
652
652
|
pos += valLen.value;
|
|
653
|
-
|
|
653
|
+
var value = valH ? decodeHuffman(valBytes)
|
|
654
654
|
: new TextDecoder().decode(valBytes);
|
|
655
655
|
|
|
656
656
|
headers.push({
|
|
@@ -666,23 +666,23 @@ function parse_qpack_header_block_old(buf) {
|
|
|
666
666
|
|
|
667
667
|
/* C. Literal Field Line + Literal Name – 001xxxxx */
|
|
668
668
|
if ((byte & 0xE0) === 0x20) {
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
669
|
+
var neverIndexed = (byte & 0x10) !== 0; // N-bit
|
|
670
|
+
var nameH = (byte & 0x08) !== 0; // H-bit (שם) :contentReference[oaicite:0]{index=0}
|
|
671
|
+
var nameLen = decodeVarInt(buf, 3, pos); // prefix-3
|
|
672
672
|
pos = nameLen.next;
|
|
673
673
|
|
|
674
|
-
|
|
674
|
+
var nameBytes = buf.slice(pos, pos + nameLen.value);
|
|
675
675
|
pos += nameLen.value;
|
|
676
|
-
|
|
676
|
+
var name = nameH ? decodeHuffman(nameBytes)
|
|
677
677
|
: new TextDecoder().decode(nameBytes);
|
|
678
678
|
|
|
679
|
-
|
|
680
|
-
|
|
679
|
+
var valH = (buf[pos] & 0x80) !== 0; // H-bit (ערך)
|
|
680
|
+
var valLen = decodeVarInt(buf, 7, pos); // prefix-7
|
|
681
681
|
pos = valLen.next;
|
|
682
682
|
|
|
683
|
-
|
|
683
|
+
var valBytes = buf.slice(pos, pos + valLen.value);
|
|
684
684
|
pos += valLen.value;
|
|
685
|
-
|
|
685
|
+
var value = valH ? decodeHuffman(valBytes)
|
|
686
686
|
: new TextDecoder().decode(valBytes);
|
|
687
687
|
|
|
688
688
|
headers.push({
|
|
@@ -711,8 +711,90 @@ function parse_qpack_header_block_old(buf) {
|
|
|
711
711
|
|
|
712
712
|
|
|
713
713
|
|
|
714
|
+
// הנחות:
|
|
715
|
+
// - chunks: Array<Uint8Array>, לפי הסדר (head → tail)
|
|
716
|
+
// - from_offset: offset לוגי (למי שצריך; נעדכן אותו בסוף)
|
|
717
|
+
// - readVarInt(u8, off) => { value, byteLength }
|
|
718
|
+
// - concatUint8Arrays(arr) קיים אצלך
|
|
714
719
|
|
|
715
720
|
function extract_h3_frames_from_chunks(chunks, from_offset) {
|
|
721
|
+
// אם אין צ'אנקים – אין מה לפרסר
|
|
722
|
+
if (!chunks || chunks.length === 0) {
|
|
723
|
+
return { frames: [], new_from_offset: from_offset };
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// בונים buffers החל מהמיקום הלוגי from_offset בלי לשנות את chunks
|
|
727
|
+
var buffers = [];
|
|
728
|
+
var acc = 0;
|
|
729
|
+
for (var i = 0; i < chunks.length; i++) {
|
|
730
|
+
var c = chunks[i];
|
|
731
|
+
var nextAcc = acc + c.length;
|
|
732
|
+
if (from_offset < nextAcc) {
|
|
733
|
+
// from_offset נופל לתוך הצ'אנק הזה
|
|
734
|
+
var start = from_offset - acc;
|
|
735
|
+
buffers.push(c.slice(start));
|
|
736
|
+
// כל שאר הצ'אנקים כמות שהם
|
|
737
|
+
for (var j = i + 1; j < chunks.length; j++) {
|
|
738
|
+
buffers.push(chunks[j]);
|
|
739
|
+
}
|
|
740
|
+
break;
|
|
741
|
+
}
|
|
742
|
+
acc = nextAcc;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
if (buffers.length === 0) {
|
|
746
|
+
// from_offset מעבר לסוף הנתונים שיש כרגע
|
|
747
|
+
return { frames: [], new_from_offset: from_offset };
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
var combined = concatUint8Arrays(buffers);
|
|
751
|
+
var offset = 0;
|
|
752
|
+
var frames = [];
|
|
753
|
+
|
|
754
|
+
// קריאת VarInt בטוחה מתוך 'combined' עם קידום offset מקומי
|
|
755
|
+
function safeReadVarInt() {
|
|
756
|
+
if (offset >= combined.length) return null;
|
|
757
|
+
var firstByte = combined[offset];
|
|
758
|
+
var lengthBits = firstByte >> 6; // 0..3 → 1,2,4,8 bytes
|
|
759
|
+
var neededLength = 1 << lengthBits;
|
|
760
|
+
if (offset + neededLength > combined.length) return null;
|
|
761
|
+
var res = readVarInt(combined, offset);
|
|
762
|
+
if (!res || typeof res.byteLength !== 'number') return null;
|
|
763
|
+
offset += res.byteLength;
|
|
764
|
+
return res;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
while (offset < combined.length) {
|
|
768
|
+
var startOffset = offset;
|
|
769
|
+
|
|
770
|
+
var frameType = safeReadVarInt();
|
|
771
|
+
if (!frameType) break;
|
|
772
|
+
|
|
773
|
+
var lengthInfo = safeReadVarInt();
|
|
774
|
+
if (!lengthInfo) {
|
|
775
|
+
// לא הצלחנו אפילו לקרוא את האורך – נחזור לנק' ההתחלה ונחכה לנתונים נוספים
|
|
776
|
+
offset = startOffset;
|
|
777
|
+
break;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
var payloadLength = lengthInfo.value >>> 0; // מניחים אורך עד 2^32-1
|
|
781
|
+
if (offset + payloadLength > combined.length) {
|
|
782
|
+
// פריים חלקי – rollback מלא
|
|
783
|
+
offset = startOffset;
|
|
784
|
+
break;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
var payload = combined.slice(offset, offset + payloadLength);
|
|
788
|
+
frames.push({ frame_type: frameType.value, payload: payload });
|
|
789
|
+
offset += payloadLength;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
// לא משנים את chunks; רק מקדמים את from_offset לפי כמה באמת נקרא
|
|
793
|
+
return { frames: frames, new_from_offset: from_offset + offset };
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
|
|
797
|
+
function extract_h3_frames_from_chunks2(chunks, from_offset) {
|
|
716
798
|
var offsets = Object.keys(chunks).map(Number).sort((a, b) => a - b);
|
|
717
799
|
var buffers = [];
|
|
718
800
|
var totalLength = 0;
|
|
@@ -867,7 +949,153 @@ function safeDecodeVarInt(buf, posRef, prefixBits) {
|
|
|
867
949
|
|
|
868
950
|
/* ---------- פונקציית החילוץ העיקרית ---------- */
|
|
869
951
|
|
|
952
|
+
// הנחות:
|
|
953
|
+
// - chunks: Array<Uint8Array>, לפי הסדר (head → tail)
|
|
954
|
+
// - from_offset: מספר מצטבר (רק להחזרה/מתן עקיבה)
|
|
955
|
+
// - concatUint8Arrays(arr) קיים אצלך
|
|
956
|
+
// - safeDecodeVarInt(buf, posRef, prefixBits) -> number|null (posRef.pos מתקדם בפנים)
|
|
957
|
+
// - decodeHuffman(u8) קיים אם אתה תומך בהאףמן; אחרת אפשר להחזיר שגיאה/לקרוא כ-raw
|
|
958
|
+
|
|
870
959
|
function extract_qpack_encoder_instructions_from_chunks(chunks, from_offset) {
|
|
960
|
+
// אם אין צ'אנקים – אין מה לפרסר
|
|
961
|
+
if (!chunks || chunks.length === 0) {
|
|
962
|
+
return { instructions: [], new_from_offset: from_offset };
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// מחברים ל-buffer יחיד החל מ-from_offset, בלי לשנות את chunks
|
|
966
|
+
var buffers = [];
|
|
967
|
+
var acc = 0;
|
|
968
|
+
for (var i = 0; i < chunks.length; i++) {
|
|
969
|
+
var c = chunks[i];
|
|
970
|
+
var nextAcc = acc + c.length;
|
|
971
|
+
if (from_offset < nextAcc) {
|
|
972
|
+
// from_offset בתוך הצ'אנק הזה
|
|
973
|
+
var start = from_offset - acc;
|
|
974
|
+
buffers.push(c.slice(start));
|
|
975
|
+
// כל שאר הצ'אנקים כפי שהם
|
|
976
|
+
for (var j = i + 1; j < chunks.length; j++) {
|
|
977
|
+
buffers.push(chunks[j]);
|
|
978
|
+
}
|
|
979
|
+
break;
|
|
980
|
+
}
|
|
981
|
+
acc = nextAcc;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
if (buffers.length === 0) {
|
|
985
|
+
// from_offset מעבר לנתונים שיש כרגע
|
|
986
|
+
return { instructions: [], new_from_offset: from_offset };
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
var combined = concatUint8Arrays(buffers);
|
|
990
|
+
var posRef = { pos: 0 };
|
|
991
|
+
var instructions = [];
|
|
992
|
+
|
|
993
|
+
// 2) לולאת פירוש הוראות QPACK-Encoder
|
|
994
|
+
while (posRef.pos < combined.length) {
|
|
995
|
+
var startPos = posRef.pos;
|
|
996
|
+
if (posRef.pos >= combined.length) break;
|
|
997
|
+
var byte = combined[posRef.pos];
|
|
998
|
+
|
|
999
|
+
// --- A. Insert With Name Reference (1xxxxxxx) ---
|
|
1000
|
+
if ((byte & 0x80) === 0x80) {
|
|
1001
|
+
var fromStatic = (byte & 0x40) !== 0;
|
|
1002
|
+
var nameIdx = safeDecodeVarInt(combined, posRef, 6);
|
|
1003
|
+
if (nameIdx === null) break; // חסר נתונים
|
|
1004
|
+
|
|
1005
|
+
// value length (Huffman flag בביט העליון של בייט האורך)
|
|
1006
|
+
if (posRef.pos >= combined.length) { posRef.pos = startPos; break; }
|
|
1007
|
+
var valHuffman = (combined[posRef.pos] & 0x80) !== 0;
|
|
1008
|
+
var valLen = safeDecodeVarInt(combined, posRef, 7);
|
|
1009
|
+
if (valLen === null || posRef.pos + valLen > combined.length) {
|
|
1010
|
+
posRef.pos = startPos; break;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
var valBytes = combined.slice(posRef.pos, posRef.pos + valLen);
|
|
1014
|
+
posRef.pos += valLen;
|
|
1015
|
+
|
|
1016
|
+
var value = valHuffman ? decodeHuffman(valBytes)
|
|
1017
|
+
: new TextDecoder().decode(valBytes);
|
|
1018
|
+
|
|
1019
|
+
instructions.push({
|
|
1020
|
+
type: 'insert_with_name_ref',
|
|
1021
|
+
from_static_table: fromStatic,
|
|
1022
|
+
name_index: nameIdx,
|
|
1023
|
+
value: value
|
|
1024
|
+
});
|
|
1025
|
+
continue;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// --- B. Insert Without Name Reference (01xxxxxx) ---
|
|
1029
|
+
if ((byte & 0xC0) === 0x40) {
|
|
1030
|
+
// שים לב: בדפוס הזה ה־N-flag (שם בהאףמן) נמצא בביט 0x20 של הבייט הראשון
|
|
1031
|
+
var nameH = (byte & 0x20) !== 0;
|
|
1032
|
+
var nameLen = safeDecodeVarInt(combined, posRef, 5);
|
|
1033
|
+
if (nameLen === null || posRef.pos + nameLen > combined.length) {
|
|
1034
|
+
posRef.pos = startPos; break;
|
|
1035
|
+
}
|
|
1036
|
+
var nameBytes = combined.slice(posRef.pos, posRef.pos + nameLen);
|
|
1037
|
+
posRef.pos += nameLen;
|
|
1038
|
+
|
|
1039
|
+
// value length (Huffman flag בביט העליון של בייט האורך)
|
|
1040
|
+
if (posRef.pos >= combined.length) { posRef.pos = startPos; break; }
|
|
1041
|
+
var valH = (combined[posRef.pos] & 0x80) !== 0;
|
|
1042
|
+
var valLen2 = safeDecodeVarInt(combined, posRef, 7);
|
|
1043
|
+
if (valLen2 === null || posRef.pos + valLen2 > combined.length) {
|
|
1044
|
+
posRef.pos = startPos; break;
|
|
1045
|
+
}
|
|
1046
|
+
var valBytes2 = combined.slice(posRef.pos, posRef.pos + valLen2);
|
|
1047
|
+
posRef.pos += valLen2;
|
|
1048
|
+
|
|
1049
|
+
var nameStr = nameH ? decodeHuffman(nameBytes)
|
|
1050
|
+
: new TextDecoder().decode(nameBytes);
|
|
1051
|
+
var valueStr = valH ? decodeHuffman(valBytes2)
|
|
1052
|
+
: new TextDecoder().decode(valBytes2);
|
|
1053
|
+
|
|
1054
|
+
instructions.push({
|
|
1055
|
+
type: 'insert_without_name_ref',
|
|
1056
|
+
name: nameStr,
|
|
1057
|
+
value: valueStr
|
|
1058
|
+
});
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// --- C. Set Dynamic Table Capacity (001xxxxx) ---
|
|
1063
|
+
if ((byte & 0xE0) === 0x20) {
|
|
1064
|
+
var capacity = safeDecodeVarInt(combined, posRef, 5);
|
|
1065
|
+
if (capacity === null) { posRef.pos = startPos; break; }
|
|
1066
|
+
|
|
1067
|
+
instructions.push({
|
|
1068
|
+
type: 'set_dynamic_table_capacity',
|
|
1069
|
+
capacity: capacity
|
|
1070
|
+
});
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// --- D. Duplicate (0000xxxx) ---
|
|
1075
|
+
if ((byte & 0xF0) === 0x00) {
|
|
1076
|
+
var dupIndex = safeDecodeVarInt(combined, posRef, 4);
|
|
1077
|
+
if (dupIndex === null) { posRef.pos = startPos; break; }
|
|
1078
|
+
|
|
1079
|
+
instructions.push({
|
|
1080
|
+
type: 'duplicate',
|
|
1081
|
+
index: dupIndex
|
|
1082
|
+
});
|
|
1083
|
+
continue;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// אופקוד לא מוכר או לא נתמך – נעצור עד שיגיע עוד מידע / נטפל חיצונית
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// כמה בייטים פרשנו מתוך ה־view המאוחד
|
|
1091
|
+
var consumed = posRef.pos | 0;
|
|
1092
|
+
|
|
1093
|
+
// לא משנים את chunks; רק מקדמים offset לוגי
|
|
1094
|
+
return { instructions: instructions, new_from_offset: from_offset + consumed };
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
|
|
1098
|
+
function extract_qpack_encoder_instructions_from_chunks2(chunks, from_offset) {
|
|
871
1099
|
/* 1) חיבור הצ’אנקים החל מ-from_offset */
|
|
872
1100
|
var offsets = Object.keys(chunks).map(Number).sort(function (a, b) { return a - b; });
|
|
873
1101
|
var buffers = [];
|
|
@@ -1131,9 +1359,9 @@ function build_control_stream_old(settings_named) {
|
|
|
1131
1359
|
|
|
1132
1360
|
|
|
1133
1361
|
function encodeInt(value, prefixBits) {
|
|
1134
|
-
|
|
1362
|
+
var max = (1 << prefixBits) - 1;
|
|
1135
1363
|
if (value < max) return [value]; // נכנס כולו בפריפיקס
|
|
1136
|
-
|
|
1364
|
+
var bytes = [max];
|
|
1137
1365
|
value -= max;
|
|
1138
1366
|
while (value >= 128) { // המשך varint (7-bit groups)
|
|
1139
1367
|
bytes.push((value & 0x7F) | 0x80);
|
|
@@ -1144,23 +1372,23 @@ function encodeInt(value, prefixBits) {
|
|
|
1144
1372
|
}
|
|
1145
1373
|
|
|
1146
1374
|
function encodeStringLiteral(bytes, hFlag /* 0/1 */) {
|
|
1147
|
-
|
|
1375
|
+
var lenBytes = encodeInt(bytes.length, 7); // prefix-7
|
|
1148
1376
|
lenBytes[0] |= (hFlag << 7); // מוסיפים H
|
|
1149
1377
|
return lenBytes.concat(Array.from(bytes));
|
|
1150
1378
|
}
|
|
1151
1379
|
|
|
1152
1380
|
/* ---------- בניית HEADERS (Literal) ---------- */
|
|
1153
1381
|
function build_http3_literal_headers_frame(headers) {
|
|
1154
|
-
|
|
1382
|
+
var out = [];
|
|
1155
1383
|
out.push(0x00, 0x00); // QPACK prefix
|
|
1156
1384
|
|
|
1157
1385
|
for (var header_name in headers) {
|
|
1158
|
-
|
|
1159
|
-
|
|
1386
|
+
var nameBytes = new TextEncoder().encode(header_name.toLowerCase());
|
|
1387
|
+
var valueBytes = new TextEncoder().encode(String(headers[header_name]));
|
|
1160
1388
|
|
|
1161
1389
|
/* בייט ראשון: 001 | N=0 | H=0 | NameLen(3+) */
|
|
1162
|
-
|
|
1163
|
-
|
|
1390
|
+
var nameLenEnc = encodeInt(nameBytes.length, 3); // prefix-3
|
|
1391
|
+
var firstByte = 0x20 | nameLenEnc[0]; // 0b0010_0000
|
|
1164
1392
|
out.push(firstByte, ...nameLenEnc.slice(1), ...nameBytes);
|
|
1165
1393
|
|
|
1166
1394
|
/* value: H=0 + prefix-7 */
|
|
@@ -1199,7 +1427,25 @@ function parse_webtransport_datagram(payload) {
|
|
|
1199
1427
|
};
|
|
1200
1428
|
}
|
|
1201
1429
|
|
|
1202
|
-
|
|
1430
|
+
|
|
1431
|
+
function build_close_webtransport(errorCode, reason) {
|
|
1432
|
+
var reasonBytes = reason ? new TextEncoder().encode(reason) : new Uint8Array();
|
|
1433
|
+
var payload = [
|
|
1434
|
+
...writeVarInt(errorCode),
|
|
1435
|
+
...writeVarInt(reasonBytes.length),
|
|
1436
|
+
...reasonBytes
|
|
1437
|
+
];
|
|
1438
|
+
|
|
1439
|
+
var frame = [
|
|
1440
|
+
...writeVarInt(0x2843), // type
|
|
1441
|
+
...writeVarInt(payload.length), // length
|
|
1442
|
+
...payload
|
|
1443
|
+
];
|
|
1444
|
+
|
|
1445
|
+
return new Uint8Array(frame);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
export {
|
|
1203
1449
|
build_h3_frames,
|
|
1204
1450
|
build_settings_frame,
|
|
1205
1451
|
parse_h3_settings_frame,
|
|
@@ -1208,7 +1454,8 @@ module.exports = {
|
|
|
1208
1454
|
parse_qpack_header_block,
|
|
1209
1455
|
build_http3_literal_headers_frame,
|
|
1210
1456
|
parse_webtransport_datagram,
|
|
1457
|
+
build_close_webtransport,
|
|
1211
1458
|
build_qpack_block_header_ack,
|
|
1212
1459
|
build_qpack_known_received_count,
|
|
1213
1460
|
qpack_static_table_entries
|
|
1214
|
-
};
|
|
1461
|
+
};
|
package/libs/utils.js
CHANGED
|
@@ -24,8 +24,8 @@ function writeVarInt(value) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
if (value <= Number.MAX_SAFE_INTEGER) {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
var hi = Math.floor(value / 2 ** 32);
|
|
28
|
+
var lo = value >>> 0;
|
|
29
29
|
return new Uint8Array([
|
|
30
30
|
0xC0 | (hi >> 24),
|
|
31
31
|
(hi >> 16) & 0xff,
|
|
@@ -89,8 +89,8 @@ function writeVarInt2(value) {
|
|
|
89
89
|
function readVarInt(array, offset) {
|
|
90
90
|
if (offset >= array.length) return null;
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
var first = array[offset];
|
|
93
|
+
var prefix = first >> 6;
|
|
94
94
|
|
|
95
95
|
if (prefix === 0b00) {
|
|
96
96
|
return {
|
|
@@ -101,7 +101,7 @@ function readVarInt(array, offset) {
|
|
|
101
101
|
|
|
102
102
|
if (prefix === 0b01) {
|
|
103
103
|
if (offset + 1 >= array.length) return null;
|
|
104
|
-
|
|
104
|
+
var value = ((first & 0x3f) << 8) | array[offset + 1];
|
|
105
105
|
return {
|
|
106
106
|
value,
|
|
107
107
|
byteLength: 2
|
|
@@ -110,7 +110,7 @@ function readVarInt(array, offset) {
|
|
|
110
110
|
|
|
111
111
|
if (prefix === 0b10) {
|
|
112
112
|
if (offset + 3 >= array.length) return null;
|
|
113
|
-
|
|
113
|
+
var value = (
|
|
114
114
|
((first & 0x3F) << 24) |
|
|
115
115
|
(array[offset + 1] << 16) |
|
|
116
116
|
(array[offset + 2] << 8) |
|
|
@@ -125,21 +125,21 @@ function readVarInt(array, offset) {
|
|
|
125
125
|
if (prefix === 0b11) {
|
|
126
126
|
if (offset + 7 >= array.length) return null;
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
var hi = (
|
|
129
129
|
((first & 0x3F) << 24) |
|
|
130
130
|
(array[offset + 1] << 16) |
|
|
131
131
|
(array[offset + 2] << 8) |
|
|
132
132
|
array[offset + 3]
|
|
133
133
|
) >>> 0;
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
var lo = (
|
|
136
136
|
(array[offset + 4] << 24) |
|
|
137
137
|
(array[offset + 5] << 16) |
|
|
138
138
|
(array[offset + 6] << 8) |
|
|
139
139
|
array[offset + 7]
|
|
140
140
|
) >>> 0;
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
var full = BigInt(hi) * 4294967296n + BigInt(lo); // 2^32
|
|
143
143
|
|
|
144
144
|
if (full <= BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
145
145
|
return {
|
|
@@ -188,7 +188,7 @@ function arraybufferEqual(buf1, buf2) {
|
|
|
188
188
|
var view1 = new DataView(buf1);
|
|
189
189
|
var view2 = new DataView(buf2);
|
|
190
190
|
|
|
191
|
-
for (
|
|
191
|
+
for (var i = 0; i < buf1.byteLength; i++) {
|
|
192
192
|
if (view1.getUint8(i) !== view2.getUint8(i)) {
|
|
193
193
|
return false;
|
|
194
194
|
}
|
|
@@ -394,11 +394,11 @@ function quic_acked_info_to_ranges(ackFrame) {
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
|
|
397
|
-
|
|
397
|
+
export {
|
|
398
398
|
concatUint8Arrays,
|
|
399
399
|
arraybufferEqual,
|
|
400
400
|
readVarInt,
|
|
401
401
|
writeVarInt,
|
|
402
402
|
quic_acked_info_to_ranges,
|
|
403
403
|
build_ack_info_from_ranges
|
|
404
|
-
};
|
|
404
|
+
};
|