node-rtc-connection 2.0.4 → 2.0.6
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/index.cjs +1 -0
- package/index.mjs +1 -0
- package/package.json +10 -47
- package/{dist/types → types}/stun/stun-client.d.ts +2 -0
- package/dist/index.cjs +0 -32
- package/dist/index.mjs +0 -43
- package/src/crypto/der.ts +0 -205
- package/src/crypto/x509.ts +0 -146
- package/src/datachannel/RTCDataChannel.ts +0 -388
- package/src/dtls/RTCCertificate.ts +0 -396
- package/src/dtls/cipher.ts +0 -198
- package/src/dtls/connection.ts +0 -974
- package/src/dtls/prf.ts +0 -62
- package/src/dtls/protocol.ts +0 -204
- package/src/foundation/ByteBufferQueue.ts +0 -237
- package/src/foundation/RTCError.ts +0 -276
- package/src/ice/RTCIceCandidate.ts +0 -349
- package/src/ice/ice-agent.ts +0 -609
- package/src/ice/stun-message.ts +0 -260
- package/src/index.ts +0 -72
- package/src/peerconnection/RTCPeerConnection.ts +0 -430
- package/src/sctp/association.ts +0 -523
- package/src/sctp/chunks.ts +0 -350
- package/src/sctp/crc32c.ts +0 -57
- package/src/sctp/datachannel-manager.ts +0 -187
- package/src/sctp/dcep.ts +0 -94
- package/src/sdp/RTCSessionDescription.ts +0 -115
- package/src/sdp/sdp-utils.ts +0 -229
- package/src/stun/stun-client.ts +0 -936
- package/src/transport-stack.ts +0 -165
- /package/{dist/types → types}/crypto/der.d.ts +0 -0
- /package/{dist/types → types}/crypto/x509.d.ts +0 -0
- /package/{dist/types → types}/datachannel/RTCDataChannel.d.ts +0 -0
- /package/{dist/types → types}/dtls/RTCCertificate.d.ts +0 -0
- /package/{dist/types → types}/dtls/cipher.d.ts +0 -0
- /package/{dist/types → types}/dtls/connection.d.ts +0 -0
- /package/{dist/types → types}/dtls/prf.d.ts +0 -0
- /package/{dist/types → types}/dtls/protocol.d.ts +0 -0
- /package/{dist/types → types}/foundation/ByteBufferQueue.d.ts +0 -0
- /package/{dist/types → types}/foundation/RTCError.d.ts +0 -0
- /package/{dist/types → types}/ice/RTCIceCandidate.d.ts +0 -0
- /package/{dist/types → types}/ice/ice-agent.d.ts +0 -0
- /package/{dist/types → types}/ice/stun-message.d.ts +0 -0
- /package/{dist/types → types}/index.d.ts +0 -0
- /package/{dist/types → types}/peerconnection/RTCPeerConnection.d.ts +0 -0
- /package/{dist/types → types}/sctp/association.d.ts +0 -0
- /package/{dist/types → types}/sctp/chunks.d.ts +0 -0
- /package/{dist/types → types}/sctp/crc32c.d.ts +0 -0
- /package/{dist/types → types}/sctp/datachannel-manager.d.ts +0 -0
- /package/{dist/types → types}/sctp/dcep.d.ts +0 -0
- /package/{dist/types → types}/sdp/RTCSessionDescription.d.ts +0 -0
- /package/{dist/types → types}/sdp/sdp-utils.d.ts +0 -0
- /package/{dist/types → types}/transport-stack.d.ts +0 -0
package/src/dtls/prf.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file prf.ts
|
|
3
|
-
* @description TLS 1.2 pseudo-random function (RFC 5246 §5) with SHA-256.
|
|
4
|
-
* @module dtls/prf
|
|
5
|
-
*
|
|
6
|
-
* The cipher suite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uses P_SHA256 for
|
|
7
|
-
* the PRF. This module also exposes the underlying HMAC-based P_hash.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
'use strict';
|
|
11
|
-
|
|
12
|
-
import * as crypto from 'crypto';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* P_hash(secret, seed) expanded to `length` bytes (RFC 5246 §5).
|
|
16
|
-
* A(0) = seed
|
|
17
|
-
* A(i) = HMAC(secret, A(i-1))
|
|
18
|
-
* P_hash = HMAC(secret, A(1)+seed) | HMAC(secret, A(2)+seed) | ...
|
|
19
|
-
*
|
|
20
|
-
* @param hashAlg - Node hash name, e.g. 'sha256'.
|
|
21
|
-
* @param secret
|
|
22
|
-
* @param seed
|
|
23
|
-
* @param length
|
|
24
|
-
*/
|
|
25
|
-
export function pHash(
|
|
26
|
-
hashAlg: string,
|
|
27
|
-
secret: Buffer,
|
|
28
|
-
seed: Buffer,
|
|
29
|
-
length: number
|
|
30
|
-
): Buffer {
|
|
31
|
-
const out: Buffer[] = [];
|
|
32
|
-
let total = 0;
|
|
33
|
-
let a = seed; // A(0)
|
|
34
|
-
while (total < length) {
|
|
35
|
-
a = crypto.createHmac(hashAlg, secret).update(a).digest(); // A(i)
|
|
36
|
-
const chunk = crypto
|
|
37
|
-
.createHmac(hashAlg, secret)
|
|
38
|
-
.update(Buffer.concat([a, seed]))
|
|
39
|
-
.digest();
|
|
40
|
-
out.push(chunk);
|
|
41
|
-
total += chunk.length;
|
|
42
|
-
}
|
|
43
|
-
return Buffer.concat(out).slice(0, length);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* TLS 1.2 PRF = P_SHA256(secret, label + seed).
|
|
48
|
-
*
|
|
49
|
-
* @param secret
|
|
50
|
-
* @param label - ASCII label, e.g. "master secret".
|
|
51
|
-
* @param seed
|
|
52
|
-
* @param length
|
|
53
|
-
*/
|
|
54
|
-
export function prf(
|
|
55
|
-
secret: Buffer,
|
|
56
|
-
label: string,
|
|
57
|
-
seed: Buffer,
|
|
58
|
-
length: number
|
|
59
|
-
): Buffer {
|
|
60
|
-
const labelAndSeed = Buffer.concat([Buffer.from(label, 'ascii'), seed]);
|
|
61
|
-
return pHash('sha256', secret, labelAndSeed, length);
|
|
62
|
-
}
|
package/src/dtls/protocol.ts
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file protocol.ts
|
|
3
|
-
* @description DTLS 1.2 wire-format constants and TLV/vector encoders.
|
|
4
|
-
* @module dtls/protocol
|
|
5
|
-
*
|
|
6
|
-
* Covers exactly what WebRTC's data channel needs:
|
|
7
|
-
* cipher suite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xC02B)
|
|
8
|
-
* curve secp256r1, signature scheme ecdsa_secp256r1_sha256.
|
|
9
|
-
*
|
|
10
|
-
* References: RFC 6347 (DTLS 1.2), RFC 5246 (TLS 1.2), RFC 8422 (ECC).
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
'use strict';
|
|
14
|
-
|
|
15
|
-
// DTLS 1.2 on the wire is version 0xFEFD (i.e. ~1.2).
|
|
16
|
-
export const DTLS_1_2 = 0xfefd;
|
|
17
|
-
|
|
18
|
-
export const CONTENT_TYPE = Object.freeze({
|
|
19
|
-
CHANGE_CIPHER_SPEC: 20,
|
|
20
|
-
ALERT: 21,
|
|
21
|
-
HANDSHAKE: 22,
|
|
22
|
-
APPLICATION_DATA: 23,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
export const HANDSHAKE_TYPE = Object.freeze({
|
|
26
|
-
HELLO_REQUEST: 0,
|
|
27
|
-
CLIENT_HELLO: 1,
|
|
28
|
-
SERVER_HELLO: 2,
|
|
29
|
-
HELLO_VERIFY_REQUEST: 3,
|
|
30
|
-
CERTIFICATE: 11,
|
|
31
|
-
SERVER_KEY_EXCHANGE: 12,
|
|
32
|
-
CERTIFICATE_REQUEST: 13,
|
|
33
|
-
SERVER_HELLO_DONE: 14,
|
|
34
|
-
CERTIFICATE_VERIFY: 15,
|
|
35
|
-
CLIENT_KEY_EXCHANGE: 16,
|
|
36
|
-
FINISHED: 20,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
export const ALERT_LEVEL = Object.freeze({ WARNING: 1, FATAL: 2 });
|
|
40
|
-
export const ALERT_DESC = Object.freeze({
|
|
41
|
-
CLOSE_NOTIFY: 0,
|
|
42
|
-
HANDSHAKE_FAILURE: 40,
|
|
43
|
-
BAD_CERTIFICATE: 42,
|
|
44
|
-
DECRYPT_ERROR: 51,
|
|
45
|
-
INTERNAL_ERROR: 80,
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
|
|
49
|
-
export const CIPHER_SUITE = 0xc02b;
|
|
50
|
-
|
|
51
|
-
export const NAMED_GROUP = Object.freeze({ secp256r1: 0x0017 });
|
|
52
|
-
export const EC_POINT_FORMAT = Object.freeze({ uncompressed: 0 });
|
|
53
|
-
|
|
54
|
-
// SignatureAndHashAlgorithm
|
|
55
|
-
export const HASH_ALG = Object.freeze({ sha256: 4, sha384: 5, sha512: 6 });
|
|
56
|
-
export const SIG_ALG = Object.freeze({ rsa: 1, ecdsa: 3 });
|
|
57
|
-
|
|
58
|
-
// ClientCertificateType
|
|
59
|
-
export const CERT_TYPE = Object.freeze({ ecdsa_sign: 64, rsa_sign: 1 });
|
|
60
|
-
|
|
61
|
-
export const EXTENSION = Object.freeze({
|
|
62
|
-
SUPPORTED_GROUPS: 10,
|
|
63
|
-
EC_POINT_FORMATS: 11,
|
|
64
|
-
SIGNATURE_ALGORITHMS: 13,
|
|
65
|
-
EXTENDED_MASTER_SECRET: 23,
|
|
66
|
-
RENEGOTIATION_INFO: 0xff01,
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
export const FINISHED_LABEL = Object.freeze({
|
|
70
|
-
CLIENT: 'client finished',
|
|
71
|
-
SERVER: 'server finished',
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
/** A parsed DTLS record from {@link parseRecords}. */
|
|
75
|
-
export interface Record {
|
|
76
|
-
type: number;
|
|
77
|
-
version: number;
|
|
78
|
-
epoch: number;
|
|
79
|
-
seq: number;
|
|
80
|
-
fragment: Buffer;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** A parsed handshake message header from {@link parseHandshake}. */
|
|
84
|
-
export interface Handshake {
|
|
85
|
-
msgType: number;
|
|
86
|
-
length: number;
|
|
87
|
-
messageSeq: number;
|
|
88
|
-
fragmentOffset: number;
|
|
89
|
-
fragmentLength: number;
|
|
90
|
-
body: Buffer;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ---- Vector / integer encoders -------------------------------------------
|
|
94
|
-
|
|
95
|
-
/** Encode a uint24 (3 bytes, big-endian). */
|
|
96
|
-
export function uint24(n: number): Buffer {
|
|
97
|
-
return Buffer.from([(n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff]);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Read a uint24 at offset. */
|
|
101
|
-
export function readUint24(buf: Buffer, off: number): number {
|
|
102
|
-
return (buf[off]! << 16) | (buf[off + 1]! << 8) | buf[off + 2]!;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** Length-prefixed vector with a 1-byte length. */
|
|
106
|
-
export function vec8(body: Buffer): Buffer {
|
|
107
|
-
return Buffer.concat([Buffer.from([body.length]), body]);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/** Length-prefixed vector with a 2-byte length. */
|
|
111
|
-
export function vec16(body: Buffer): Buffer {
|
|
112
|
-
const len = Buffer.alloc(2);
|
|
113
|
-
len.writeUInt16BE(body.length, 0);
|
|
114
|
-
return Buffer.concat([len, body]);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/** Length-prefixed vector with a 3-byte length. */
|
|
118
|
-
export function vec24(body: Buffer): Buffer {
|
|
119
|
-
return Buffer.concat([uint24(body.length), body]);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// ---- Record layer ---------------------------------------------------------
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Encode a DTLS record (13-byte header + fragment).
|
|
126
|
-
* @param type - CONTENT_TYPE
|
|
127
|
-
* @param epoch
|
|
128
|
-
* @param seq - 48-bit sequence number
|
|
129
|
-
* @param fragment
|
|
130
|
-
* @param version
|
|
131
|
-
*/
|
|
132
|
-
export function encodeRecord(
|
|
133
|
-
type: number,
|
|
134
|
-
epoch: number,
|
|
135
|
-
seq: number,
|
|
136
|
-
fragment: Buffer,
|
|
137
|
-
version: number = DTLS_1_2
|
|
138
|
-
): Buffer {
|
|
139
|
-
const header = Buffer.alloc(13);
|
|
140
|
-
header.writeUInt8(type, 0);
|
|
141
|
-
header.writeUInt16BE(version, 1);
|
|
142
|
-
header.writeUInt16BE(epoch, 3);
|
|
143
|
-
header.writeUIntBE(seq, 5, 6);
|
|
144
|
-
header.writeUInt16BE(fragment.length, 11);
|
|
145
|
-
return Buffer.concat([header, fragment]);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Parse one or more DTLS records from a datagram. Multiple records may be
|
|
150
|
-
* packed into a single UDP packet.
|
|
151
|
-
* @param packet
|
|
152
|
-
*/
|
|
153
|
-
export function parseRecords(packet: Buffer): Record[] {
|
|
154
|
-
const records: Record[] = [];
|
|
155
|
-
let off = 0;
|
|
156
|
-
while (off + 13 <= packet.length) {
|
|
157
|
-
const type = packet.readUInt8(off);
|
|
158
|
-
const version = packet.readUInt16BE(off + 1);
|
|
159
|
-
const epoch = packet.readUInt16BE(off + 3);
|
|
160
|
-
const seq = packet.readUIntBE(off + 5, 6);
|
|
161
|
-
const length = packet.readUInt16BE(off + 11);
|
|
162
|
-
const start = off + 13;
|
|
163
|
-
if (start + length > packet.length) break;
|
|
164
|
-
records.push({ type, version, epoch, seq, fragment: packet.slice(start, start + length) });
|
|
165
|
-
off = start + length;
|
|
166
|
-
}
|
|
167
|
-
return records;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// ---- Handshake layer -------------------------------------------------------
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Encode a DTLS handshake message header + body (unfragmented).
|
|
174
|
-
* @param msgType - HANDSHAKE_TYPE
|
|
175
|
-
* @param messageSeq
|
|
176
|
-
* @param body
|
|
177
|
-
*/
|
|
178
|
-
export function encodeHandshake(
|
|
179
|
-
msgType: number,
|
|
180
|
-
messageSeq: number,
|
|
181
|
-
body: Buffer
|
|
182
|
-
): Buffer {
|
|
183
|
-
const header = Buffer.alloc(12);
|
|
184
|
-
header.writeUInt8(msgType, 0);
|
|
185
|
-
uint24(body.length).copy(header, 1); // length
|
|
186
|
-
header.writeUInt16BE(messageSeq, 4); // message_seq
|
|
187
|
-
uint24(0).copy(header, 6); // fragment_offset
|
|
188
|
-
uint24(body.length).copy(header, 9); // fragment_length
|
|
189
|
-
return Buffer.concat([header, body]);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Parse a handshake message header.
|
|
194
|
-
* @param buf - starts at the handshake header
|
|
195
|
-
*/
|
|
196
|
-
export function parseHandshake(buf: Buffer): Handshake {
|
|
197
|
-
const msgType = buf.readUInt8(0);
|
|
198
|
-
const length = readUint24(buf, 1);
|
|
199
|
-
const messageSeq = buf.readUInt16BE(4);
|
|
200
|
-
const fragmentOffset = readUint24(buf, 6);
|
|
201
|
-
const fragmentLength = readUint24(buf, 9);
|
|
202
|
-
const body = buf.slice(12, 12 + fragmentLength);
|
|
203
|
-
return { msgType, length, messageSeq, fragmentOffset, fragmentLength, body };
|
|
204
|
-
}
|
|
@@ -1,237 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file ByteBufferQueue - Efficient byte buffer with O(1) append and O(n) read.
|
|
3
|
-
*
|
|
4
|
-
* This class provides efficient management of byte buffers with O(1) append operations
|
|
5
|
-
* and O(n) read operations. Clients can append entire buffers then copy data out across
|
|
6
|
-
* buffer boundaries.
|
|
7
|
-
*
|
|
8
|
-
* @license MIT
|
|
9
|
-
* @author nmhung1210
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
'use strict';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* A ByteBufferQueue manages a queue of byte buffers with efficient operations.
|
|
16
|
-
*
|
|
17
|
-
* Invariants maintained:
|
|
18
|
-
* - size_ = sum of all buffer sizes - frontBufferOffset_
|
|
19
|
-
* - No buffer in the queue is empty
|
|
20
|
-
* - If queue is empty, frontBufferOffset_ = 0
|
|
21
|
-
* - Otherwise, frontBufferOffset_ < front buffer size
|
|
22
|
-
*/
|
|
23
|
-
class ByteBufferQueue {
|
|
24
|
-
/**
|
|
25
|
-
* Total number of bytes available to read.
|
|
26
|
-
* @private {number}
|
|
27
|
-
*/
|
|
28
|
-
#size: number;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Double-ended queue of byte buffers.
|
|
32
|
-
* Append() pushes to the back, ReadInto() consumes from the front.
|
|
33
|
-
* @private {Buffer[]}
|
|
34
|
-
*/
|
|
35
|
-
#buffers: Buffer[];
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Offset from which to start reading the front buffer.
|
|
39
|
-
* @private {number}
|
|
40
|
-
*/
|
|
41
|
-
#frontBufferOffset: number;
|
|
42
|
-
|
|
43
|
-
constructor() {
|
|
44
|
-
this.#size = 0;
|
|
45
|
-
this.#buffers = [];
|
|
46
|
-
this.#frontBufferOffset = 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Number of bytes that can be read.
|
|
51
|
-
* @returns {number}
|
|
52
|
-
*/
|
|
53
|
-
get size(): number {
|
|
54
|
-
return this.#size;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Returns true if no bytes are available to read.
|
|
59
|
-
* @returns {boolean}
|
|
60
|
-
*/
|
|
61
|
-
get empty(): boolean {
|
|
62
|
-
return this.#size === 0;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Copies data into the given buffer. Consumes bytes from the queue.
|
|
67
|
-
* Returns the number of bytes written to bufferOut.
|
|
68
|
-
*
|
|
69
|
-
* @param {Buffer} bufferOut - Destination buffer to read into
|
|
70
|
-
* @returns {number} Number of bytes actually read
|
|
71
|
-
* @throws {TypeError} If bufferOut is not a Buffer
|
|
72
|
-
*/
|
|
73
|
-
readInto(bufferOut: Buffer): number {
|
|
74
|
-
if (!Buffer.isBuffer(bufferOut)) {
|
|
75
|
-
throw new TypeError('bufferOut must be a Buffer');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let readAmount = 0;
|
|
79
|
-
let outputOffset = 0;
|
|
80
|
-
|
|
81
|
-
while (outputOffset < bufferOut.length && this.#buffers.length > 0) {
|
|
82
|
-
const frontBuffer = this.#buffers[0]!;
|
|
83
|
-
const availableInFront = frontBuffer.length - this.#frontBufferOffset;
|
|
84
|
-
const remainingOutput = bufferOut.length - outputOffset;
|
|
85
|
-
const toCopy = Math.min(availableInFront, remainingOutput);
|
|
86
|
-
|
|
87
|
-
// Copy data from front buffer to output
|
|
88
|
-
frontBuffer.copy(
|
|
89
|
-
bufferOut,
|
|
90
|
-
outputOffset,
|
|
91
|
-
this.#frontBufferOffset,
|
|
92
|
-
this.#frontBufferOffset + toCopy
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
readAmount += toCopy;
|
|
96
|
-
outputOffset += toCopy;
|
|
97
|
-
|
|
98
|
-
if (toCopy < availableInFront) {
|
|
99
|
-
// Partial read, update offset
|
|
100
|
-
this.#frontBufferOffset += toCopy;
|
|
101
|
-
} else {
|
|
102
|
-
// Consumed entire front buffer, remove it
|
|
103
|
-
this.#buffers.shift();
|
|
104
|
-
this.#frontBufferOffset = 0;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
this.#size -= readAmount;
|
|
109
|
-
this.#checkInvariants();
|
|
110
|
-
return readAmount;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Appends a buffer to the queue. Takes ownership of the buffer.
|
|
115
|
-
* Empty buffers are ignored.
|
|
116
|
-
*
|
|
117
|
-
* @param {Buffer} buffer - Buffer to append
|
|
118
|
-
* @throws {TypeError} If buffer is not a Buffer
|
|
119
|
-
*/
|
|
120
|
-
append(buffer: Buffer): void {
|
|
121
|
-
if (!Buffer.isBuffer(buffer)) {
|
|
122
|
-
throw new TypeError('buffer must be a Buffer');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (buffer.length === 0) {
|
|
126
|
-
return; // Ignore empty buffers
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
this.#size += buffer.length;
|
|
130
|
-
this.#buffers.push(buffer);
|
|
131
|
-
this.#checkInvariants();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Clears all stored buffers.
|
|
136
|
-
*/
|
|
137
|
-
clear(): void {
|
|
138
|
-
this.#buffers = [];
|
|
139
|
-
this.#frontBufferOffset = 0;
|
|
140
|
-
this.#size = 0;
|
|
141
|
-
this.#checkInvariants();
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Reads and consumes exactly n bytes.
|
|
146
|
-
*
|
|
147
|
-
* @param {number} n - Number of bytes to read
|
|
148
|
-
* @returns {Buffer} Buffer containing exactly n bytes
|
|
149
|
-
* @throws {RangeError} If fewer than n bytes are available
|
|
150
|
-
*/
|
|
151
|
-
read(n: number): Buffer {
|
|
152
|
-
if (n > this.#size) {
|
|
153
|
-
throw new RangeError(`Cannot read ${n} bytes, only ${this.#size} available`);
|
|
154
|
-
}
|
|
155
|
-
if (n === 0) {
|
|
156
|
-
return Buffer.allocUnsafe(0);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const result = Buffer.allocUnsafe(n);
|
|
160
|
-
const bytesRead = this.readInto(result);
|
|
161
|
-
|
|
162
|
-
if (bytesRead !== n) {
|
|
163
|
-
throw new Error(`Internal error: read ${bytesRead} bytes, expected ${n}`);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return result;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Peeks at data without consuming it.
|
|
171
|
-
*
|
|
172
|
-
* @param {number} [n=this.#size] - Number of bytes to peek
|
|
173
|
-
* @returns {Buffer} Buffer containing up to n bytes (not consumed)
|
|
174
|
-
*/
|
|
175
|
-
peek(n: number = this.#size): Buffer {
|
|
176
|
-
const peekAmount = Math.min(n, this.#size);
|
|
177
|
-
if (peekAmount === 0) {
|
|
178
|
-
return Buffer.allocUnsafe(0);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const result = Buffer.allocUnsafe(peekAmount);
|
|
182
|
-
let written = 0;
|
|
183
|
-
let bufferIndex = 0;
|
|
184
|
-
let offset = this.#frontBufferOffset;
|
|
185
|
-
|
|
186
|
-
while (written < peekAmount && bufferIndex < this.#buffers.length) {
|
|
187
|
-
const buffer = this.#buffers[bufferIndex]!;
|
|
188
|
-
const available = buffer.length - offset;
|
|
189
|
-
const toCopy = Math.min(available, peekAmount - written);
|
|
190
|
-
|
|
191
|
-
buffer.copy(result, written, offset, offset + toCopy);
|
|
192
|
-
written += toCopy;
|
|
193
|
-
|
|
194
|
-
bufferIndex++;
|
|
195
|
-
offset = 0; // Reset offset for subsequent buffers
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return result;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Checks internal invariants (development mode only).
|
|
203
|
-
* @private
|
|
204
|
-
* @throws {Error} If invariants are violated
|
|
205
|
-
*/
|
|
206
|
-
#checkInvariants(): void {
|
|
207
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
208
|
-
let bufferSizeSum = 0;
|
|
209
|
-
for (const buffer of this.#buffers) {
|
|
210
|
-
if (buffer.length === 0) {
|
|
211
|
-
throw new Error('Invariant violation: empty buffer in queue');
|
|
212
|
-
}
|
|
213
|
-
bufferSizeSum += buffer.length;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const expectedSize = bufferSizeSum - this.#frontBufferOffset;
|
|
217
|
-
if (this.#size !== expectedSize) {
|
|
218
|
-
throw new Error(
|
|
219
|
-
`Invariant violation: size=${this.#size}, expected=${expectedSize}`
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
if (this.#buffers.length === 0) {
|
|
224
|
-
if (this.#frontBufferOffset !== 0) {
|
|
225
|
-
throw new Error('Invariant violation: offset non-zero with empty queue');
|
|
226
|
-
}
|
|
227
|
-
} else {
|
|
228
|
-
if (this.#frontBufferOffset >= this.#buffers[0]!.length) {
|
|
229
|
-
throw new Error('Invariant violation: offset >= front buffer size');
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
export default ByteBufferQueue;
|
|
237
|
-
export { ByteBufferQueue };
|