@trevrpc/trevrpc-js 0.2.3 → 1.0.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/README.md +89 -47
- package/bin/protoc-gen-trevrpc-js.js +1 -1
- package/package.json +11 -41
- package/src/bytes.js +10 -0
- package/src/call.js +287 -0
- package/src/channel.js +131 -296
- package/src/client.js +203 -1292
- package/src/framing.js +125 -519
- package/src/generator.js +26 -103
- package/src/index.d.ts +79 -452
- package/src/index.js +4 -3
- package/src/status.js +24 -70
- package/src/wire.js +4 -59
- package/examples/greeter/README.md +0 -35
- package/examples/greeter/client.js +0 -172
- package/examples/greeter/greeter.node.trevrpc.d.ts +0 -53
- package/examples/greeter/greeter.node.trevrpc.js +0 -9
- package/examples/greeter/greeter.proto +0 -18
- package/examples/greeter/greeter.trevrpc.d.ts +0 -55
- package/examples/greeter/greeter.trevrpc.js +0 -103
- package/examples/greeter/index.html +0 -140
- package/examples/greeter/static-server.js +0 -168
- package/src/advanced.d.ts +0 -29
- package/src/advanced.js +0 -1
- package/src/browser-channel.js +0 -49
- package/src/connect.js +0 -6
- package/src/connect.node.js +0 -6
- package/src/index.node.d.ts +0 -10
- package/src/index.node.js +0 -3
- package/src/native-loader.js +0 -118
- package/src/node-advanced.d.ts +0 -27
- package/src/node-advanced.js +0 -1
- package/src/node-generated.d.ts +0 -24
- package/src/node-generated.js +0 -126
- package/src/node-index.d.ts +0 -1
- package/src/node-index.js +0 -7
- package/src/node.d.ts +0 -298
- package/src/node.js +0 -1456
- package/src/runtime.js +0 -66
- package/src/server-response.js +0 -49
- package/src/timer.js +0 -29
- package/src/webtransport.js +0 -639
package/src/framing.js
CHANGED
|
@@ -1,534 +1,211 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
RpcStreamFrameKind,
|
|
5
|
-
preflightWireMessage,
|
|
6
|
-
validateWireMessage,
|
|
7
|
-
} from "./wire.js";
|
|
1
|
+
import { byteView } from "./bytes.js";
|
|
2
|
+
import { Code, invalidArgument, resourceExhausted, unavailable } from "./status.js";
|
|
3
|
+
import { RpcStreamFrame, RpcStreamFrameKind, preflightWireMessage } from "./wire.js";
|
|
8
4
|
|
|
9
5
|
export const DefaultMaxFrameSize = 4 * 1024 * 1024;
|
|
10
6
|
|
|
11
|
-
const
|
|
7
|
+
const HeaderLength = 4;
|
|
8
|
+
/** Tag of RpcStreamFrame.body: field 4, length-delimited. */
|
|
12
9
|
const StreamFrameBodyTag = (4 << 3) | 2;
|
|
13
10
|
const EmptyBytes = new Uint8Array(0);
|
|
14
|
-
const utf8Decoder = new TextDecoder("utf-8", { fatal: true });
|
|
15
11
|
|
|
16
12
|
/** Encodes a protobuf message body. */
|
|
17
13
|
export function marshalMessage(messageType, message) {
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
try {
|
|
15
|
+
return messageType.encode(messageType.fromObject(message)).finish();
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw invalidArgument(
|
|
18
|
+
`failed to encode protobuf message: ${error?.message ?? String(error)}`,
|
|
19
|
+
error,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
/** Decodes a protobuf message body. */
|
|
24
|
+
/** Decodes a protobuf message body, rejecting malformed wire data. */
|
|
23
25
|
export function unmarshalMessage(messageType, body) {
|
|
24
26
|
try {
|
|
25
27
|
preflightWireMessage(messageType, body);
|
|
26
28
|
return messageType.decode(body);
|
|
27
29
|
} catch (error) {
|
|
28
|
-
|
|
30
|
+
throw invalidArgument(
|
|
29
31
|
`failed to decode protobuf message: ${error?.message ?? String(error)}`,
|
|
32
|
+
error,
|
|
30
33
|
);
|
|
31
|
-
failure.reason = error?.reason ?? "malformed_protobuf";
|
|
32
|
-
failure.cause = error;
|
|
33
|
-
throw failure;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Encodes a protobuf message into a length-prefixed
|
|
37
|
+
/** Encodes a protobuf message into a length-prefixed frame. */
|
|
38
38
|
export function encodeFrame(messageType, message, maxFrameSize = DefaultMaxFrameSize) {
|
|
39
39
|
const body = marshalMessage(messageType, message);
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const frame = new Uint8Array(FrameHeaderLength + body.byteLength);
|
|
45
|
-
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
46
|
-
view.setUint32(0, body.byteLength, false);
|
|
47
|
-
frame.set(body, FrameHeaderLength);
|
|
40
|
+
checkFrameLength(body.byteLength, maxFrameSize);
|
|
41
|
+
const frame = new Uint8Array(HeaderLength + body.byteLength);
|
|
42
|
+
new DataView(frame.buffer).setUint32(0, body.byteLength, false);
|
|
43
|
+
frame.set(body, HeaderLength);
|
|
48
44
|
return frame;
|
|
49
45
|
}
|
|
50
46
|
|
|
51
|
-
/**
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
maxFrameSize
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
byteBodies[index - range.start] = body;
|
|
69
|
-
const bodyLength = messageStreamFrameBodyLength(body.byteLength);
|
|
70
|
-
if (bodyLength > maxFrameSize) {
|
|
71
|
-
throw new FrameTooLargeError(bodyLength, maxFrameSize);
|
|
72
|
-
}
|
|
73
|
-
totalLength += FrameHeaderLength + bodyLength;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const frames = new Uint8Array(totalLength);
|
|
77
|
-
const view = new DataView(frames.buffer, frames.byteOffset, frames.byteLength);
|
|
78
|
-
let offset = 0;
|
|
79
|
-
for (const body of byteBodies) {
|
|
80
|
-
const bodyLength = messageStreamFrameBodyLength(body.byteLength);
|
|
81
|
-
view.setUint32(offset, bodyLength, false);
|
|
82
|
-
offset += FrameHeaderLength;
|
|
83
|
-
if (body.byteLength > 0) {
|
|
84
|
-
frames[offset++] = StreamFrameBodyTag;
|
|
85
|
-
offset = appendVarint(frames, offset, body.byteLength);
|
|
86
|
-
frames.set(body, offset);
|
|
87
|
-
offset += body.byteLength;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return frames;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Encodes each streaming message frame separately for vectored writers. */
|
|
95
|
-
export function encodeMessageStreamFrameParts(
|
|
96
|
-
bodies,
|
|
97
|
-
maxFrameSize = DefaultMaxFrameSize,
|
|
98
|
-
start = 0,
|
|
99
|
-
end = undefined,
|
|
100
|
-
) {
|
|
101
|
-
const range = bodyRange(bodies, start, end);
|
|
102
|
-
const frames = Array.from({ length: range.end - range.start });
|
|
103
|
-
for (let index = range.start; index < range.end; index += 1) {
|
|
104
|
-
frames[index - range.start] = encodeMessageStreamFrame(range.bodies[index], maxFrameSize);
|
|
47
|
+
/**
|
|
48
|
+
* Encodes a streaming message frame carrying an already-encoded protobuf body.
|
|
49
|
+
* This is byte-identical to encodeFrame(RpcStreamFrame, { body }) without building the
|
|
50
|
+
* intermediate message.
|
|
51
|
+
*/
|
|
52
|
+
export function encodeMessageFrame(body, maxFrameSize = DefaultMaxFrameSize) {
|
|
53
|
+
const frameLength =
|
|
54
|
+
body.byteLength === 0 ? 0 : 1 + varintLength(body.byteLength) + body.byteLength;
|
|
55
|
+
checkFrameLength(frameLength, maxFrameSize);
|
|
56
|
+
|
|
57
|
+
const frame = new Uint8Array(HeaderLength + frameLength);
|
|
58
|
+
new DataView(frame.buffer).setUint32(0, frameLength, false);
|
|
59
|
+
if (body.byteLength > 0) {
|
|
60
|
+
let offset = HeaderLength;
|
|
61
|
+
frame[offset++] = StreamFrameBodyTag;
|
|
62
|
+
offset = writeVarint(frame, offset, body.byteLength);
|
|
63
|
+
frame.set(body, offset);
|
|
105
64
|
}
|
|
106
|
-
return
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/** Decodes a protobuf message from a TrevRPC frame body. */
|
|
110
|
-
export function decodeFrame(messageType, body) {
|
|
111
|
-
return unmarshalMessage(messageType, body);
|
|
65
|
+
return frame;
|
|
112
66
|
}
|
|
113
67
|
|
|
114
|
-
/** Decodes
|
|
115
|
-
export function
|
|
68
|
+
/** Decodes the body of a response stream frame, validating its kind. */
|
|
69
|
+
export function decodeStreamFrame(body) {
|
|
116
70
|
const bytes = byteView(body);
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const failure = invalidArgument(
|
|
121
|
-
`failed to decode protobuf message: ${error?.message ?? String(error)}`,
|
|
122
|
-
);
|
|
123
|
-
failure.reason = error?.reason ?? "malformed_protobuf";
|
|
124
|
-
failure.cause = error;
|
|
125
|
-
throw failure;
|
|
126
|
-
}
|
|
127
|
-
if (bytes.byteLength === 0) {
|
|
128
|
-
return streamFrameMessage(EmptyBytes);
|
|
71
|
+
const messageBody = bareMessageBody(bytes);
|
|
72
|
+
if (messageBody !== undefined) {
|
|
73
|
+
return { kind: RpcStreamFrameKind.Message, status: Code.Ok, message: "", body: messageBody };
|
|
129
74
|
}
|
|
130
75
|
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
let message = "";
|
|
135
|
-
let frameBody = new Uint8Array(0);
|
|
136
|
-
|
|
137
|
-
while (cursor.offset < bytes.byteLength) {
|
|
138
|
-
const tag = consumeVarint(bytes, cursor, "invalid stream frame field tag");
|
|
139
|
-
if (tag === 0) {
|
|
140
|
-
throw invalidArgument("invalid stream frame field tag");
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
const field = Math.floor(tag / 8);
|
|
144
|
-
const wireType = tag % 8;
|
|
145
|
-
switch (field) {
|
|
146
|
-
case 1:
|
|
147
|
-
requireWireType(wireType, 0, "invalid stream frame kind wire type");
|
|
148
|
-
kind = consumeVarint(bytes, cursor, "truncated stream frame kind");
|
|
149
|
-
break;
|
|
150
|
-
case 2:
|
|
151
|
-
requireWireType(wireType, 0, "invalid stream frame status wire type");
|
|
152
|
-
status = consumeVarint(bytes, cursor, "truncated stream frame status");
|
|
153
|
-
break;
|
|
154
|
-
case 3:
|
|
155
|
-
requireWireType(wireType, 2, "invalid stream frame message wire type");
|
|
156
|
-
message = decodeUtf8(consumeLengthDelimited(bytes, cursor, "invalid stream frame message"));
|
|
157
|
-
break;
|
|
158
|
-
case 4:
|
|
159
|
-
requireWireType(wireType, 2, "invalid stream frame body wire type");
|
|
160
|
-
frameBody = consumeLengthDelimited(bytes, cursor, "invalid stream frame body");
|
|
161
|
-
break;
|
|
162
|
-
case 5: {
|
|
163
|
-
const frame = decodeFrame(RpcStreamFrame, bytes);
|
|
164
|
-
validateDecodedWireMessage(RpcStreamFrame, frame);
|
|
165
|
-
return frame;
|
|
166
|
-
}
|
|
167
|
-
default:
|
|
168
|
-
skipProtoField(bytes, cursor, wireType);
|
|
169
|
-
break;
|
|
170
|
-
}
|
|
76
|
+
const frame = unmarshalMessage(RpcStreamFrame, bytes);
|
|
77
|
+
if (frame.kind !== RpcStreamFrameKind.Message && frame.kind !== RpcStreamFrameKind.Status) {
|
|
78
|
+
throw invalidArgument(`unsupported stream frame kind ${frame.kind}`);
|
|
171
79
|
}
|
|
172
|
-
|
|
173
|
-
const frame = { kind, status, message, body: frameBody, metadata: {} };
|
|
174
|
-
validateDecodedWireMessage(RpcStreamFrame, frame);
|
|
175
80
|
return frame;
|
|
176
81
|
}
|
|
177
82
|
|
|
178
|
-
/**
|
|
179
|
-
export
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
bodies,
|
|
187
|
-
maxFrameSize = DefaultMaxFrameSize,
|
|
188
|
-
start = 0,
|
|
189
|
-
end = undefined,
|
|
190
|
-
) {
|
|
191
|
-
if (typeof writer.writev === "function") {
|
|
192
|
-
await writer.writev(encodeMessageStreamFrameParts(bodies, maxFrameSize, start, end));
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
await writer.write(encodeMessageStreamFrames(bodies, maxFrameSize, start, end));
|
|
83
|
+
/** Validates the body length stored in a frame header. */
|
|
84
|
+
export function frameBodyLength(header, maxFrameSize = DefaultMaxFrameSize) {
|
|
85
|
+
const length = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint32(
|
|
86
|
+
0,
|
|
87
|
+
false,
|
|
88
|
+
);
|
|
89
|
+
checkFrameLength(length, maxFrameSize);
|
|
90
|
+
return length;
|
|
197
91
|
}
|
|
198
92
|
|
|
199
|
-
/** Reads length-prefixed
|
|
93
|
+
/** Reads length-prefixed frames from a byte stream reader. */
|
|
200
94
|
export class FrameReader {
|
|
201
|
-
|
|
95
|
+
#reader;
|
|
96
|
+
/** Bytes received from the reader but not yet consumed. */
|
|
97
|
+
#pending = EmptyBytes;
|
|
98
|
+
|
|
99
|
+
/** Creates a frame reader over a ReadableStream reader. */
|
|
202
100
|
constructor(reader) {
|
|
203
|
-
this
|
|
204
|
-
this.chunks = [];
|
|
205
|
-
this.chunkHead = 0;
|
|
206
|
-
this.buffered = 0;
|
|
101
|
+
this.#reader = reader;
|
|
207
102
|
}
|
|
208
103
|
|
|
209
|
-
/** Reads and decodes one frame. */
|
|
104
|
+
/** Reads and decodes one frame; a stream that ends first is an error. */
|
|
210
105
|
async readFrame(messageType, maxFrameSize = DefaultMaxFrameSize) {
|
|
211
|
-
|
|
212
|
-
const length = frameBodyLength(header, maxFrameSize);
|
|
213
|
-
const body = await this.readExact(length, false);
|
|
214
|
-
return decodeFrame(messageType, body);
|
|
106
|
+
return unmarshalMessage(messageType, await this.#readBody(maxFrameSize, false));
|
|
215
107
|
}
|
|
216
108
|
|
|
217
|
-
/** Reads
|
|
218
|
-
async
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
return null;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const length = frameBodyLength(header, maxFrameSize);
|
|
225
|
-
const body = await this.readExact(length, false);
|
|
226
|
-
return decodeFrame(messageType, body);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/** Reads and decodes one streaming RPC frame, or returns null when already at EOF. */
|
|
230
|
-
async readStreamFrameOrEOF(maxFrameSize = DefaultMaxFrameSize) {
|
|
231
|
-
const header = await this.readExact(FrameHeaderLength, true);
|
|
232
|
-
if (header == null) {
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
const length = frameBodyLength(header, maxFrameSize);
|
|
237
|
-
const body = await this.readExact(length, false);
|
|
238
|
-
return decodeStreamFrameBody(body);
|
|
109
|
+
/** Reads one streaming RPC frame, or returns null when the stream ended cleanly. */
|
|
110
|
+
async readStreamFrame(maxFrameSize = DefaultMaxFrameSize) {
|
|
111
|
+
const body = await this.#readBody(maxFrameSize, true);
|
|
112
|
+
return body === null ? null : decodeStreamFrame(body);
|
|
239
113
|
}
|
|
240
114
|
|
|
241
|
-
/**
|
|
242
|
-
async
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const limit = Math.max(1, Math.floor(maxBatch));
|
|
249
|
-
const frames = [first];
|
|
250
|
-
while (frames.length < limit) {
|
|
251
|
-
const body = this.tryReadFrameBody(maxFrameSize);
|
|
252
|
-
if (body === undefined) {
|
|
253
|
-
break;
|
|
115
|
+
/** Resolves when the stream ends cleanly; rejects if more data arrives first. */
|
|
116
|
+
async expectEnd() {
|
|
117
|
+
if (this.#pending.byteLength === 0) {
|
|
118
|
+
const { value, done } = await this.#nextChunk();
|
|
119
|
+
if (done) {
|
|
120
|
+
return;
|
|
254
121
|
}
|
|
255
|
-
|
|
122
|
+
this.#pending = value;
|
|
256
123
|
}
|
|
257
|
-
|
|
124
|
+
throw unavailable("stream carried data after the final frame");
|
|
258
125
|
}
|
|
259
126
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (body == null) {
|
|
127
|
+
async #readBody(maxFrameSize, allowEnd) {
|
|
128
|
+
const header = await this.#readExact(HeaderLength, allowEnd);
|
|
129
|
+
if (header === null) {
|
|
264
130
|
return null;
|
|
265
131
|
}
|
|
266
|
-
|
|
267
|
-
const limit = Math.max(1, Math.floor(maxBatch));
|
|
268
|
-
const result = { bodies: [], status: null };
|
|
269
|
-
pushStreamBodyOrStatus(result, body);
|
|
270
|
-
while (result.status == null && result.bodies.length < limit) {
|
|
271
|
-
const nextBody = this.tryReadFrameBody(maxFrameSize);
|
|
272
|
-
if (nextBody === undefined) {
|
|
273
|
-
break;
|
|
274
|
-
}
|
|
275
|
-
pushStreamBodyOrStatus(result, nextBody);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
return result;
|
|
132
|
+
return await this.#readExact(frameBodyLength(header, maxFrameSize), false);
|
|
279
133
|
}
|
|
280
134
|
|
|
281
|
-
/**
|
|
282
|
-
async
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
return null;
|
|
135
|
+
/** Returns exactly size bytes, or null if allowEnd and the stream ended before the first byte. */
|
|
136
|
+
async #readExact(size, allowEnd) {
|
|
137
|
+
if (this.#pending.byteLength >= size) {
|
|
138
|
+
return this.#take(size);
|
|
286
139
|
}
|
|
287
140
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const header = this.peek(FrameHeaderLength);
|
|
298
|
-
const length = frameBodyLength(header, maxFrameSize);
|
|
299
|
-
if (this.buffered < FrameHeaderLength + length) {
|
|
300
|
-
return undefined;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
this.consume(FrameHeaderLength);
|
|
304
|
-
return this.consume(length);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/** Reads exactly size bytes from the underlying reader. */
|
|
308
|
-
async readExact(size, allowEofAtStart) {
|
|
309
|
-
if (size === 0) {
|
|
310
|
-
return EmptyBytes;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
while (this.buffered < size) {
|
|
314
|
-
const { value, done } = await this.reader.read();
|
|
141
|
+
// Copies are needed only when a read spans chunks; whole frames inside one chunk are views.
|
|
142
|
+
const out = new Uint8Array(size);
|
|
143
|
+
let filled = this.#pending.byteLength;
|
|
144
|
+
out.set(this.#pending);
|
|
145
|
+
this.#pending = EmptyBytes;
|
|
146
|
+
while (filled < size) {
|
|
147
|
+
const { value, done } = await this.#nextChunk();
|
|
315
148
|
if (done) {
|
|
316
|
-
if (
|
|
149
|
+
if (allowEnd && filled === 0) {
|
|
317
150
|
return null;
|
|
318
151
|
}
|
|
319
|
-
|
|
320
|
-
const error = unavailable("transport unavailable: stream ended in the middle of a frame");
|
|
321
|
-
error.reason = "incomplete_frame";
|
|
322
|
-
throw error;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
if (value == null || value.byteLength === 0) {
|
|
326
|
-
continue;
|
|
152
|
+
throw unavailable("stream ended before a complete frame");
|
|
327
153
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
this
|
|
154
|
+
const count = Math.min(value.byteLength, size - filled);
|
|
155
|
+
out.set(value.subarray(0, count), filled);
|
|
156
|
+
filled += count;
|
|
157
|
+
this.#pending = value.subarray(count);
|
|
332
158
|
}
|
|
333
|
-
|
|
334
|
-
return this.consume(size);
|
|
159
|
+
return out;
|
|
335
160
|
}
|
|
336
161
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const first = this.chunks[this.chunkHead];
|
|
344
|
-
if (first != null && first.byteLength >= size) {
|
|
345
|
-
const result = first.subarray(0, size);
|
|
346
|
-
if (first.byteLength === size) {
|
|
347
|
-
this.chunkHead += 1;
|
|
348
|
-
this.compactChunks();
|
|
349
|
-
} else {
|
|
350
|
-
this.chunks[this.chunkHead] = first.subarray(size);
|
|
162
|
+
async #nextChunk() {
|
|
163
|
+
for (;;) {
|
|
164
|
+
const { value, done } = await this.#reader.read();
|
|
165
|
+
if (done) {
|
|
166
|
+
return { done: true };
|
|
351
167
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
const result = new Uint8Array(size);
|
|
357
|
-
let offset = 0;
|
|
358
|
-
|
|
359
|
-
while (offset < size) {
|
|
360
|
-
const chunk = this.chunks[this.chunkHead];
|
|
361
|
-
const needed = size - offset;
|
|
362
|
-
if (chunk.byteLength <= needed) {
|
|
363
|
-
result.set(chunk, offset);
|
|
364
|
-
offset += chunk.byteLength;
|
|
365
|
-
this.chunkHead += 1;
|
|
366
|
-
} else {
|
|
367
|
-
result.set(chunk.subarray(0, needed), offset);
|
|
368
|
-
this.chunks[this.chunkHead] = chunk.subarray(needed);
|
|
369
|
-
offset += needed;
|
|
168
|
+
if (value != null && value.byteLength > 0) {
|
|
169
|
+
return { done: false, value: byteView(value) };
|
|
370
170
|
}
|
|
371
171
|
}
|
|
372
|
-
|
|
373
|
-
this.buffered -= size;
|
|
374
|
-
this.compactChunks();
|
|
375
|
-
return result;
|
|
376
172
|
}
|
|
377
173
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const first = this.chunks[this.chunkHead];
|
|
385
|
-
if (first != null && first.byteLength >= size) {
|
|
386
|
-
return first.subarray(0, size);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const result = new Uint8Array(size);
|
|
390
|
-
let offset = 0;
|
|
391
|
-
|
|
392
|
-
for (let index = this.chunkHead; index < this.chunks.length; index += 1) {
|
|
393
|
-
const chunk = this.chunks[index];
|
|
394
|
-
if (offset >= size) {
|
|
395
|
-
break;
|
|
396
|
-
}
|
|
397
|
-
const count = Math.min(chunk.byteLength, size - offset);
|
|
398
|
-
result.set(chunk.subarray(0, count), offset);
|
|
399
|
-
offset += count;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
return result;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
compactChunks() {
|
|
406
|
-
if (this.chunkHead === 0) {
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
if (this.chunkHead === this.chunks.length) {
|
|
410
|
-
this.chunks.length = 0;
|
|
411
|
-
this.chunkHead = 0;
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
if (this.chunkHead >= 32 && this.chunkHead * 2 >= this.chunks.length) {
|
|
415
|
-
this.chunks.splice(0, this.chunkHead);
|
|
416
|
-
this.chunkHead = 0;
|
|
417
|
-
}
|
|
174
|
+
#take(size) {
|
|
175
|
+
const bytes = this.#pending.subarray(0, size);
|
|
176
|
+
this.#pending = this.#pending.subarray(size);
|
|
177
|
+
return bytes;
|
|
418
178
|
}
|
|
419
179
|
}
|
|
420
180
|
|
|
421
|
-
function
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
result.bodies.push(messageBody);
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const frame = decodeStreamFrameBody(body);
|
|
429
|
-
switch (frame.kind) {
|
|
430
|
-
case RpcStreamFrameKind.Message:
|
|
431
|
-
result.bodies.push(frame.body ?? EmptyBytes);
|
|
432
|
-
break;
|
|
433
|
-
case RpcStreamFrameKind.Status:
|
|
434
|
-
result.status = frame;
|
|
435
|
-
break;
|
|
436
|
-
default:
|
|
437
|
-
throw invalidArgument("stream frame contained an unknown frame kind");
|
|
181
|
+
function checkFrameLength(length, maxFrameSize) {
|
|
182
|
+
if (length > maxFrameSize) {
|
|
183
|
+
throw resourceExhausted(`frame length ${length} exceeds maximum ${maxFrameSize}`);
|
|
438
184
|
}
|
|
439
185
|
}
|
|
440
186
|
|
|
441
|
-
|
|
442
|
-
|
|
187
|
+
/** Returns the body of a message-only stream frame, or undefined if it carries anything else. */
|
|
188
|
+
function bareMessageBody(bytes) {
|
|
443
189
|
if (bytes.byteLength === 0) {
|
|
444
190
|
return EmptyBytes;
|
|
445
191
|
}
|
|
446
|
-
|
|
447
192
|
if (bytes[0] !== StreamFrameBodyTag) {
|
|
448
193
|
return undefined;
|
|
449
194
|
}
|
|
450
195
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
/** Decodes and validates the body length stored in a TrevRPC frame header. */
|
|
461
|
-
export function frameBodyLength(header, maxFrameSize = DefaultMaxFrameSize) {
|
|
462
|
-
const view = new DataView(header.buffer, header.byteOffset, header.byteLength);
|
|
463
|
-
const length = view.getUint32(0, false);
|
|
464
|
-
if (length > maxFrameSize) {
|
|
465
|
-
throw new FrameTooLargeError(length, maxFrameSize);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
return length;
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
function validateDecodedWireMessage(messageType, message) {
|
|
472
|
-
try {
|
|
473
|
-
validateWireMessage(messageType, message);
|
|
474
|
-
} catch (error) {
|
|
475
|
-
const failure = invalidArgument(error?.message ?? String(error));
|
|
476
|
-
failure.reason = error?.reason;
|
|
477
|
-
failure.cause = error;
|
|
478
|
-
throw failure;
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
function prepareMessage(messageType, message) {
|
|
483
|
-
const prepared =
|
|
484
|
-
typeof messageType.fromObject === "function" ? messageType.fromObject(message) : message;
|
|
485
|
-
const error = typeof messageType.verify === "function" ? messageType.verify(prepared) : null;
|
|
486
|
-
if (error != null) {
|
|
487
|
-
throw invalidArgument(`failed to encode protobuf message: ${error}`);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
return prepared;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function streamFrameMessage(body) {
|
|
494
|
-
return {
|
|
495
|
-
kind: RpcStreamFrameKind.Message,
|
|
496
|
-
status: 0,
|
|
497
|
-
message: "",
|
|
498
|
-
body,
|
|
499
|
-
metadata: {},
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
function byteView(value) {
|
|
504
|
-
if (value instanceof Uint8Array) {
|
|
505
|
-
return value;
|
|
506
|
-
}
|
|
507
|
-
if (ArrayBuffer.isView(value)) {
|
|
508
|
-
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
509
|
-
}
|
|
510
|
-
return new Uint8Array(value ?? 0);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
function bodyRange(bodies, start, end) {
|
|
514
|
-
const source = Array.isArray(bodies) ? bodies : Array.from(bodies);
|
|
515
|
-
const from = clampRangeIndex(start, source.length);
|
|
516
|
-
const to = Math.max(from, clampRangeIndex(end ?? source.length, source.length));
|
|
517
|
-
return { bodies: source, start: from, end: to };
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
function clampRangeIndex(value, length) {
|
|
521
|
-
if (!Number.isFinite(value)) {
|
|
522
|
-
return length;
|
|
196
|
+
let length = 0;
|
|
197
|
+
let offset = 1;
|
|
198
|
+
for (let shift = 0; shift < 35 && offset < bytes.byteLength; shift += 7) {
|
|
199
|
+
const byte = bytes[offset++];
|
|
200
|
+
length += (byte & 0x7f) * 2 ** shift;
|
|
201
|
+
if (byte < 0x80) {
|
|
202
|
+
return offset + length === bytes.byteLength ? bytes.subarray(offset) : undefined;
|
|
203
|
+
}
|
|
523
204
|
}
|
|
524
|
-
return
|
|
205
|
+
return undefined;
|
|
525
206
|
}
|
|
526
207
|
|
|
527
|
-
function
|
|
528
|
-
return bodyLength === 0 ? 0 : 1 + varintLength(bodyLength) + bodyLength;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function appendVarint(output, offset, value) {
|
|
208
|
+
function writeVarint(output, offset, value) {
|
|
532
209
|
let remaining = value;
|
|
533
210
|
while (remaining >= 0x80) {
|
|
534
211
|
output[offset++] = (remaining % 0x80) | 0x80;
|
|
@@ -539,80 +216,9 @@ function appendVarint(output, offset, value) {
|
|
|
539
216
|
}
|
|
540
217
|
|
|
541
218
|
function varintLength(value) {
|
|
542
|
-
let remaining = value;
|
|
543
219
|
let length = 1;
|
|
544
|
-
|
|
545
|
-
remaining = Math.floor(remaining / 0x80);
|
|
220
|
+
for (let remaining = value; remaining >= 0x80; remaining = Math.floor(remaining / 0x80)) {
|
|
546
221
|
length += 1;
|
|
547
222
|
}
|
|
548
223
|
return length;
|
|
549
224
|
}
|
|
550
|
-
|
|
551
|
-
function consumeVarint(data, cursor, message) {
|
|
552
|
-
let value = 0;
|
|
553
|
-
let multiplier = 1;
|
|
554
|
-
for (let index = 0; index < 10; index += 1) {
|
|
555
|
-
if (cursor.offset >= data.byteLength) {
|
|
556
|
-
throw invalidArgument(message);
|
|
557
|
-
}
|
|
558
|
-
const byte = data[cursor.offset++];
|
|
559
|
-
value += (byte & 0x7f) * multiplier;
|
|
560
|
-
if (byte < 0x80) {
|
|
561
|
-
return value;
|
|
562
|
-
}
|
|
563
|
-
multiplier *= 0x80;
|
|
564
|
-
}
|
|
565
|
-
throw invalidArgument("stream frame varint exceeded 64 bits");
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
function requireWireType(actual, expected, message) {
|
|
569
|
-
if (actual !== expected) {
|
|
570
|
-
throw invalidArgument(message);
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
function consumeLengthDelimited(data, cursor, message) {
|
|
575
|
-
const length = consumeVarint(data, cursor, message);
|
|
576
|
-
const end = cursor.offset + length;
|
|
577
|
-
if (!Number.isSafeInteger(end) || end > data.byteLength) {
|
|
578
|
-
throw invalidArgument(message);
|
|
579
|
-
}
|
|
580
|
-
const value = data.subarray(cursor.offset, end);
|
|
581
|
-
cursor.offset = end;
|
|
582
|
-
return value;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
function skipProtoField(data, cursor, wireType) {
|
|
586
|
-
switch (wireType) {
|
|
587
|
-
case 0:
|
|
588
|
-
consumeVarint(data, cursor, "truncated stream frame varint");
|
|
589
|
-
return;
|
|
590
|
-
case 1:
|
|
591
|
-
skipFixed(data, cursor, 8);
|
|
592
|
-
return;
|
|
593
|
-
case 2:
|
|
594
|
-
consumeLengthDelimited(data, cursor, "truncated stream frame field");
|
|
595
|
-
return;
|
|
596
|
-
case 5:
|
|
597
|
-
skipFixed(data, cursor, 4);
|
|
598
|
-
return;
|
|
599
|
-
default:
|
|
600
|
-
throw invalidArgument("stream frame contained an unsupported wire type");
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function skipFixed(data, cursor, length) {
|
|
605
|
-
const end = cursor.offset + length;
|
|
606
|
-
if (end > data.byteLength) {
|
|
607
|
-
throw invalidArgument("truncated stream frame fixed field");
|
|
608
|
-
}
|
|
609
|
-
cursor.offset = end;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function decodeUtf8(bytes) {
|
|
613
|
-
try {
|
|
614
|
-
return utf8Decoder.decode(bytes);
|
|
615
|
-
} catch (error) {
|
|
616
|
-
throw invalidArgument(`stream frame message was not valid UTF-8: ${error.message}`);
|
|
617
|
-
}
|
|
618
|
-
}
|