quico 0.1.3 → 0.3.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 +381 -216
- package/index.cjs +45 -0
- package/index.d.ts +158 -0
- package/index.js +49 -23
- package/package.json +83 -60
- package/src/agent.js +283 -0
- package/src/client.js +444 -0
- package/src/crypto.js +534 -0
- package/src/h3.js +920 -0
- package/src/h3_client.js +332 -0
- package/src/h3_server.js +392 -0
- package/src/quic_connection.js +2338 -0
- package/src/quic_server.js +314 -0
- package/src/quic_socket.js +76 -0
- package/src/server.js +354 -0
- package/src/streams.js +471 -0
- package/src/tls_bridge.js +273 -0
- package/src/transport.js +667 -0
- package/src/utils.js +345 -0
- package/src/webtransport.js +655 -0
- package/CHANGELOG.md +0 -31
- package/examples/webtransport_datagram_echo_server/client.html +0 -58
- package/examples/webtransport_datagram_echo_server/server.js +0 -23
- package/h3_server.js +0 -416
- package/h3_socket.js +0 -473
- package/libs/crypto.js +0 -1841
- package/libs/h3.js +0 -1461
- package/libs/utils.js +0 -404
- package/quic_socket.js +0 -1550
- package/quico.svg +0 -1
package/src/streams.js
ADDED
|
@@ -0,0 +1,471 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* quico: HTTP/3 and QUIC implementation for Node.js
|
|
3
|
+
* Copyright 2025 colocohen
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at:
|
|
8
|
+
*
|
|
9
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* This file is part of the open-source project hosted at:
|
|
12
|
+
* https://github.com/colocohen/quico
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { Readable, Writable, Duplex } from 'node:stream';
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// ============================================================
|
|
19
|
+
// IncomingMessage — extends Readable
|
|
20
|
+
//
|
|
21
|
+
// Wraps incoming HTTP data:
|
|
22
|
+
// - On server: the request body (req)
|
|
23
|
+
// - On client: the response body (res)
|
|
24
|
+
//
|
|
25
|
+
// Data is pushed in from the H3 layer via _pushData / _endData.
|
|
26
|
+
// Consumers use pipe(), for-await-of, on('data'), etc.
|
|
27
|
+
// ============================================================
|
|
28
|
+
|
|
29
|
+
class IncomingMessage extends Readable {
|
|
30
|
+
|
|
31
|
+
constructor(options) {
|
|
32
|
+
super({ highWaterMark: (options && options.highWaterMark) || 64 * 1024 });
|
|
33
|
+
|
|
34
|
+
options = options || {};
|
|
35
|
+
|
|
36
|
+
this.httpVersion = options.httpVersion || '3.0';
|
|
37
|
+
this.httpVersionMajor = parseInt(this.httpVersion) || 3;
|
|
38
|
+
this.httpVersionMinor = parseInt((this.httpVersion.split('.')[1]) || '0') || 0;
|
|
39
|
+
|
|
40
|
+
this.complete = false;
|
|
41
|
+
this.aborted = false;
|
|
42
|
+
|
|
43
|
+
this.headers = options.headers || {};
|
|
44
|
+
this.rawHeaders = [];
|
|
45
|
+
this.trailers = {};
|
|
46
|
+
this.rawTrailers = [];
|
|
47
|
+
|
|
48
|
+
// Client-side (response)
|
|
49
|
+
this.statusCode = options.statusCode || null;
|
|
50
|
+
this.statusMessage = options.statusMessage || '';
|
|
51
|
+
|
|
52
|
+
// Server-side (request)
|
|
53
|
+
this.method = options.method || null;
|
|
54
|
+
this.url = options.url || null;
|
|
55
|
+
|
|
56
|
+
// QUIC has no TCP socket — stub so libraries don't crash
|
|
57
|
+
this.socket = options.socket || _makeSocketStub();
|
|
58
|
+
this.connection = this.socket;
|
|
59
|
+
|
|
60
|
+
// H3 stream id (internal)
|
|
61
|
+
this._streamId = options.streamId != null ? options.streamId : null;
|
|
62
|
+
this._timeoutTimer = null;
|
|
63
|
+
|
|
64
|
+
// Build rawHeaders
|
|
65
|
+
for (var name in this.headers) {
|
|
66
|
+
this.rawHeaders.push(name, String(this.headers[name]));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Readable requires _read — data is pushed externally, so no-op.
|
|
71
|
+
_read(_size) { /* no-op */ }
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Called by H3 layer when a body chunk arrives.
|
|
75
|
+
*/
|
|
76
|
+
_pushData(chunk) {
|
|
77
|
+
if (this.complete) return;
|
|
78
|
+
if (typeof chunk === 'string') chunk = Buffer.from(chunk);
|
|
79
|
+
if (chunk instanceof Uint8Array && !Buffer.isBuffer(chunk)) {
|
|
80
|
+
chunk = Buffer.from(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
81
|
+
}
|
|
82
|
+
this.push(chunk);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Called by H3 layer when the stream ends (FIN received).
|
|
87
|
+
*/
|
|
88
|
+
_endData() {
|
|
89
|
+
if (this.complete) return;
|
|
90
|
+
this.complete = true;
|
|
91
|
+
this.push(null);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Abort the stream (error / timeout / reset).
|
|
96
|
+
*/
|
|
97
|
+
_abort(err) {
|
|
98
|
+
if (this.complete) return;
|
|
99
|
+
this.aborted = true;
|
|
100
|
+
this.complete = true;
|
|
101
|
+
if (err) this.destroy(err);
|
|
102
|
+
else this.push(null);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
setTimeout(msecs, callback) {
|
|
106
|
+
if (callback) this.once('timeout', callback);
|
|
107
|
+
if (this._timeoutTimer) clearTimeout(this._timeoutTimer);
|
|
108
|
+
if (msecs > 0) {
|
|
109
|
+
this._timeoutTimer = setTimeout(() => this.emit('timeout'), msecs);
|
|
110
|
+
}
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
// ============================================================
|
|
117
|
+
// ServerResponse — extends Writable
|
|
118
|
+
//
|
|
119
|
+
// Wraps outgoing HTTP response on the server side.
|
|
120
|
+
// write() / pipe(res) / end() → h3.sendHeaders + h3.sendBody
|
|
121
|
+
// ============================================================
|
|
122
|
+
|
|
123
|
+
class ServerResponse extends Writable {
|
|
124
|
+
|
|
125
|
+
constructor(options) {
|
|
126
|
+
super({ highWaterMark: (options && options.highWaterMark) || 64 * 1024 });
|
|
127
|
+
|
|
128
|
+
options = options || {};
|
|
129
|
+
|
|
130
|
+
this.statusCode = 200;
|
|
131
|
+
this.statusMessage = 'OK';
|
|
132
|
+
this.headersSent = false;
|
|
133
|
+
this.finished = false;
|
|
134
|
+
this._headers = {};
|
|
135
|
+
this._headerNames = {}; // lowercase → original-case
|
|
136
|
+
|
|
137
|
+
// H3 references (set by server when creating the response)
|
|
138
|
+
this._streamId = options.streamId != null ? options.streamId : null;
|
|
139
|
+
this._h3 = options.h3 || null;
|
|
140
|
+
|
|
141
|
+
// Paired request (Express compat: res.req)
|
|
142
|
+
this.req = options.req || null;
|
|
143
|
+
|
|
144
|
+
this.socket = options.socket || _makeSocketStub();
|
|
145
|
+
this.connection = this.socket;
|
|
146
|
+
|
|
147
|
+
this.on('finish', () => {
|
|
148
|
+
this.finished = true;
|
|
149
|
+
this.emit('close');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Express does res.__proto__ = app.response (http.ServerResponse.prototype)
|
|
153
|
+
// which kills our class methods. Bind them to the instance so they survive.
|
|
154
|
+
this.setHeader = this.setHeader.bind(this);
|
|
155
|
+
this.getHeader = this.getHeader.bind(this);
|
|
156
|
+
this.getHeaders = this.getHeaders.bind(this);
|
|
157
|
+
this.getHeaderNames = this.getHeaderNames.bind(this);
|
|
158
|
+
this.hasHeader = this.hasHeader.bind(this);
|
|
159
|
+
this.removeHeader = this.removeHeader.bind(this);
|
|
160
|
+
this.writeHead = this.writeHead.bind(this);
|
|
161
|
+
this.flushHeaders = this.flushHeaders.bind(this);
|
|
162
|
+
this._flushHeaders = this._flushHeaders.bind(this);
|
|
163
|
+
this.write = this.write.bind(this);
|
|
164
|
+
this.end = this.end.bind(this);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ---- Header management (mirrors node:http) ----
|
|
168
|
+
|
|
169
|
+
setHeader(name, value) {
|
|
170
|
+
if (this.headersSent) throw new Error('Cannot set headers after they are sent to the client');
|
|
171
|
+
var lower = name.toLowerCase();
|
|
172
|
+
this._headers[lower] = value;
|
|
173
|
+
this._headerNames[lower] = name;
|
|
174
|
+
return this;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
getHeader(name) {
|
|
178
|
+
return this._headers[name.toLowerCase()];
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
getHeaders() {
|
|
182
|
+
var out = {};
|
|
183
|
+
for (var k in this._headers) out[k] = this._headers[k];
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
getHeaderNames() {
|
|
188
|
+
return Object.keys(this._headers);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
hasHeader(name) {
|
|
192
|
+
return name.toLowerCase() in this._headers;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
removeHeader(name) {
|
|
196
|
+
if (this.headersSent) throw new Error('Cannot remove headers after they are sent to the client');
|
|
197
|
+
var lower = name.toLowerCase();
|
|
198
|
+
delete this._headers[lower];
|
|
199
|
+
delete this._headerNames[lower];
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ---- writeHead ----
|
|
204
|
+
|
|
205
|
+
writeHead(statusCode, statusMessage, headers) {
|
|
206
|
+
if (this.headersSent) return this;
|
|
207
|
+
|
|
208
|
+
if (typeof statusMessage === 'object' && !headers) {
|
|
209
|
+
headers = statusMessage;
|
|
210
|
+
statusMessage = undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.statusCode = statusCode;
|
|
214
|
+
if (statusMessage !== undefined) this.statusMessage = String(statusMessage);
|
|
215
|
+
|
|
216
|
+
if (typeof headers === 'object' && headers !== null) {
|
|
217
|
+
for (var name in headers) this.setHeader(name, headers[name]);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this._flushHeaders();
|
|
221
|
+
return this;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
flushHeaders() {
|
|
225
|
+
if (!this.headersSent) this._flushHeaders();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
_flushHeaders() {
|
|
229
|
+
if (this.headersSent || !this._h3) return;
|
|
230
|
+
|
|
231
|
+
var h3h = { ':status': String(this.statusCode) };
|
|
232
|
+
for (var lower in this._headers) h3h[lower] = String(this._headers[lower]);
|
|
233
|
+
|
|
234
|
+
this._h3.sendHeaders(this._streamId, h3h);
|
|
235
|
+
this.headersSent = true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ---- Writable implementation ----
|
|
239
|
+
|
|
240
|
+
_write(chunk, encoding, callback) {
|
|
241
|
+
if (!this.headersSent) this._flushHeaders();
|
|
242
|
+
|
|
243
|
+
if (typeof chunk === 'string') chunk = Buffer.from(chunk, encoding);
|
|
244
|
+
if (Buffer.isBuffer(chunk)) {
|
|
245
|
+
chunk = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (this._h3) this._h3.sendBody(this._streamId, chunk, false);
|
|
249
|
+
callback();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
_final(callback) {
|
|
253
|
+
if (!this.headersSent) this._flushHeaders();
|
|
254
|
+
if (this._h3) this._h3.sendBody(this._streamId, null, true);
|
|
255
|
+
callback();
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---- node:http compat overrides ----
|
|
259
|
+
|
|
260
|
+
write(chunk, encoding, callback) {
|
|
261
|
+
if (typeof encoding === 'function') { callback = encoding; encoding = undefined; }
|
|
262
|
+
return super.write(chunk, encoding, callback);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
end(data, encoding, callback) {
|
|
266
|
+
if (typeof data === 'function') { callback = data; data = undefined; encoding = undefined; }
|
|
267
|
+
if (typeof encoding === 'function') { callback = encoding; encoding = undefined; }
|
|
268
|
+
if (data != null) this.write(data, encoding);
|
|
269
|
+
return super.end(callback);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
writeContinue() { /* HTTP/3 doesn't use 100-continue — stub */ }
|
|
273
|
+
|
|
274
|
+
setTimeout(msecs, callback) {
|
|
275
|
+
if (callback) this.once('timeout', callback);
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
// ============================================================
|
|
282
|
+
// ClientRequest — extends Writable
|
|
283
|
+
//
|
|
284
|
+
// Wraps outgoing HTTP request on the client side.
|
|
285
|
+
// write() / pipe(req) buffers body data.
|
|
286
|
+
// _final() signals body-complete so the transport can send.
|
|
287
|
+
//
|
|
288
|
+
// The 'response' event delivers an IncomingMessage.
|
|
289
|
+
// ============================================================
|
|
290
|
+
|
|
291
|
+
class ClientRequest extends Writable {
|
|
292
|
+
|
|
293
|
+
constructor(options) {
|
|
294
|
+
super({ highWaterMark: (options && options.highWaterMark) || 64 * 1024 });
|
|
295
|
+
|
|
296
|
+
options = options || {};
|
|
297
|
+
|
|
298
|
+
this.method = (options.method || 'GET').toUpperCase();
|
|
299
|
+
this.path = options.path || '/';
|
|
300
|
+
this.host = options.hostname || options.host || 'localhost';
|
|
301
|
+
this.port = options.port || 443;
|
|
302
|
+
|
|
303
|
+
this.headersSent = false;
|
|
304
|
+
this.finished = false;
|
|
305
|
+
this.aborted = false;
|
|
306
|
+
this.reusedSocket = false;
|
|
307
|
+
|
|
308
|
+
this._headers = {};
|
|
309
|
+
this._headerNames = {};
|
|
310
|
+
|
|
311
|
+
// Copy initial headers
|
|
312
|
+
if (options.headers) {
|
|
313
|
+
for (var name in options.headers) this.setHeader(name, options.headers[name]);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Body buffer — accumulated via write/pipe, sent when _final fires
|
|
317
|
+
this._bodyChunks = [];
|
|
318
|
+
|
|
319
|
+
// H3 internals — set by unified client after protocol selection
|
|
320
|
+
this._h3 = null;
|
|
321
|
+
this._streamId = null;
|
|
322
|
+
this._quic = null;
|
|
323
|
+
|
|
324
|
+
// The callback from request(opts, cb)
|
|
325
|
+
this._responseCallback = options._responseCallback || null;
|
|
326
|
+
|
|
327
|
+
// Protocol that was actually used (filled after connect)
|
|
328
|
+
this.protocol = null; // 'h3', 'h2', 'https'
|
|
329
|
+
|
|
330
|
+
this.socket = options.socket || _makeSocketStub();
|
|
331
|
+
this.connection = this.socket;
|
|
332
|
+
|
|
333
|
+
this._timeoutTimer = null;
|
|
334
|
+
this._timeoutMs = 0;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ---- Header management ----
|
|
338
|
+
|
|
339
|
+
setHeader(name, value) {
|
|
340
|
+
var lower = name.toLowerCase();
|
|
341
|
+
this._headers[lower] = value;
|
|
342
|
+
this._headerNames[lower] = name;
|
|
343
|
+
return this;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
getHeader(name) {
|
|
347
|
+
return this._headers[name.toLowerCase()];
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
getHeaders() {
|
|
351
|
+
var out = {};
|
|
352
|
+
for (var k in this._headers) out[k] = this._headers[k];
|
|
353
|
+
return out;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
removeHeader(name) {
|
|
357
|
+
var lower = name.toLowerCase();
|
|
358
|
+
delete this._headers[lower];
|
|
359
|
+
delete this._headerNames[lower];
|
|
360
|
+
return this;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
hasHeader(name) {
|
|
364
|
+
return name.toLowerCase() in this._headers;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
setNoDelay() { return this; }
|
|
368
|
+
setSocketKeepAlive() { return this; }
|
|
369
|
+
|
|
370
|
+
// ---- Writable implementation ----
|
|
371
|
+
|
|
372
|
+
_write(chunk, encoding, callback) {
|
|
373
|
+
if (typeof chunk === 'string') chunk = Buffer.from(chunk, encoding);
|
|
374
|
+
if (Buffer.isBuffer(chunk)) {
|
|
375
|
+
chunk = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
376
|
+
}
|
|
377
|
+
this._bodyChunks.push(chunk);
|
|
378
|
+
callback();
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
_final(callback) {
|
|
382
|
+
this.finished = true;
|
|
383
|
+
this.emit('_bodyReady');
|
|
384
|
+
callback();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// ---- Convenience overrides ----
|
|
388
|
+
|
|
389
|
+
write(chunk, encoding, callback) {
|
|
390
|
+
if (typeof encoding === 'function') { callback = encoding; encoding = undefined; }
|
|
391
|
+
return super.write(chunk, encoding, callback);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
end(data, encoding, callback) {
|
|
395
|
+
if (typeof data === 'function') { callback = data; data = undefined; encoding = undefined; }
|
|
396
|
+
if (typeof encoding === 'function') { callback = encoding; encoding = undefined; }
|
|
397
|
+
if (data != null) this.write(data, encoding);
|
|
398
|
+
return super.end(callback);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
abort() {
|
|
402
|
+
this.aborted = true;
|
|
403
|
+
this.destroy(new Error('Request aborted'));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
setTimeout(msecs, callback) {
|
|
407
|
+
if (callback) this.once('timeout', callback);
|
|
408
|
+
this._timeoutMs = msecs;
|
|
409
|
+
if (this._timeoutTimer) clearTimeout(this._timeoutTimer);
|
|
410
|
+
if (msecs > 0) {
|
|
411
|
+
this._timeoutTimer = setTimeout(() => this.emit('timeout'), msecs);
|
|
412
|
+
}
|
|
413
|
+
return this;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Collect all buffered body chunks into one Uint8Array.
|
|
418
|
+
* Returns null if nothing was written (GET / HEAD).
|
|
419
|
+
*/
|
|
420
|
+
_getBody() {
|
|
421
|
+
if (this._bodyChunks.length === 0) return null;
|
|
422
|
+
if (this._bodyChunks.length === 1) return this._bodyChunks[0];
|
|
423
|
+
var total = 0;
|
|
424
|
+
for (var i = 0; i < this._bodyChunks.length; i++) total += this._bodyChunks[i].byteLength;
|
|
425
|
+
var merged = new Uint8Array(total);
|
|
426
|
+
var off = 0;
|
|
427
|
+
for (var i = 0; i < this._bodyChunks.length; i++) {
|
|
428
|
+
merged.set(this._bodyChunks[i], off);
|
|
429
|
+
off += this._bodyChunks[i].byteLength;
|
|
430
|
+
}
|
|
431
|
+
return merged;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
// ============================================================
|
|
437
|
+
// Socket stub — for libraries that check req.socket / res.socket
|
|
438
|
+
// Must be a real Stream so Node's eos() / Express don't crash.
|
|
439
|
+
// ============================================================
|
|
440
|
+
|
|
441
|
+
function _makeSocketStub() {
|
|
442
|
+
var sock = new Duplex({
|
|
443
|
+
read(_size) { /* no-op — no data from QUIC socket */ },
|
|
444
|
+
write(_chunk, _encoding, callback) { callback(); }
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// TLS properties
|
|
448
|
+
sock.encrypted = true;
|
|
449
|
+
sock.authorized = true;
|
|
450
|
+
sock.remoteAddress = null;
|
|
451
|
+
sock.remotePort = null;
|
|
452
|
+
sock.localAddress = null;
|
|
453
|
+
sock.localPort = null;
|
|
454
|
+
sock.alpnProtocol = 'h3';
|
|
455
|
+
|
|
456
|
+
sock.setNoDelay = function () { return sock; };
|
|
457
|
+
sock.setKeepAlive = function () { return sock; };
|
|
458
|
+
sock.setTimeout = function () { return sock; };
|
|
459
|
+
sock.ref = function () { return sock; };
|
|
460
|
+
sock.unref = function () { return sock; };
|
|
461
|
+
|
|
462
|
+
// TLS methods (Express / Helmet / etc.)
|
|
463
|
+
sock.getPeerCertificate = function () { return {}; };
|
|
464
|
+
sock.getProtocol = function () { return 'TLSv1.3'; };
|
|
465
|
+
sock.getCipher = function () { return { name: 'TLS_AES_128_GCM_SHA256', version: 'TLSv1.3' }; };
|
|
466
|
+
|
|
467
|
+
return sock;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
export { IncomingMessage, ServerResponse, ClientRequest, _makeSocketStub };
|