@gjsify/http2 0.3.12 → 0.3.14
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/lib/esm/client-session.js +266 -272
- package/lib/esm/index.js +39 -85
- package/lib/esm/protocol.js +332 -307
- package/lib/esm/server.js +588 -609
- package/package.json +9 -9
package/lib/esm/server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { constants, getDefaultSettings } from "./protocol.js";
|
|
1
2
|
import Soup from "@girs/soup-3.0";
|
|
2
3
|
import Gio from "@girs/gio-2.0";
|
|
3
4
|
import GLib from "@girs/glib-2.0";
|
|
@@ -5,616 +6,594 @@ import { EventEmitter } from "node:events";
|
|
|
5
6
|
import { Readable, Writable } from "node:stream";
|
|
6
7
|
import { Buffer } from "node:buffer";
|
|
7
8
|
import { deferEmit, ensureMainLoop } from "@gjsify/utils";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
if (this._tlsCert) {
|
|
568
|
-
server.set_tls_certificate(this._tlsCert);
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
setSecureContext(options) {
|
|
572
|
-
if (options.cert && options.key) {
|
|
573
|
-
const certPem = _toPemString(options.cert);
|
|
574
|
-
const keyPem = _toPemString(options.key);
|
|
575
|
-
this._tlsCert = _createTlsCertificate(certPem, keyPem);
|
|
576
|
-
if (this._soupServer && this._tlsCert) {
|
|
577
|
-
this._soupServer.set_tls_certificate(this._tlsCert);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
9
|
+
|
|
10
|
+
//#region src/server.ts
|
|
11
|
+
var Http2ServerRequest = class extends Readable {
|
|
12
|
+
method = "GET";
|
|
13
|
+
url = "/";
|
|
14
|
+
headers = {};
|
|
15
|
+
rawHeaders = [];
|
|
16
|
+
authority = "";
|
|
17
|
+
scheme = "https";
|
|
18
|
+
httpVersion = "2.0";
|
|
19
|
+
httpVersionMajor = 2;
|
|
20
|
+
httpVersionMinor = 0;
|
|
21
|
+
complete = false;
|
|
22
|
+
socket = null;
|
|
23
|
+
trailers = {};
|
|
24
|
+
rawTrailers = [];
|
|
25
|
+
_stream = null;
|
|
26
|
+
_timeoutTimer = null;
|
|
27
|
+
get stream() {
|
|
28
|
+
return this._stream;
|
|
29
|
+
}
|
|
30
|
+
_setStream(stream) {
|
|
31
|
+
this._stream = stream;
|
|
32
|
+
}
|
|
33
|
+
constructor() {
|
|
34
|
+
super();
|
|
35
|
+
}
|
|
36
|
+
_read(_size) {}
|
|
37
|
+
_autoClose() {}
|
|
38
|
+
_pushBody(body) {
|
|
39
|
+
if (body && body.length > 0) {
|
|
40
|
+
this.push(Buffer.from(body));
|
|
41
|
+
}
|
|
42
|
+
this.push(null);
|
|
43
|
+
this.complete = true;
|
|
44
|
+
if (this._timeoutTimer) {
|
|
45
|
+
clearTimeout(this._timeoutTimer);
|
|
46
|
+
this._timeoutTimer = null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
setTimeout(msecs, callback) {
|
|
50
|
+
if (this._timeoutTimer) {
|
|
51
|
+
clearTimeout(this._timeoutTimer);
|
|
52
|
+
this._timeoutTimer = null;
|
|
53
|
+
}
|
|
54
|
+
if (callback) this.once("timeout", callback);
|
|
55
|
+
if (msecs > 0) {
|
|
56
|
+
this._timeoutTimer = setTimeout(() => {
|
|
57
|
+
this._timeoutTimer = null;
|
|
58
|
+
this.emit("timeout");
|
|
59
|
+
}, msecs);
|
|
60
|
+
}
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
destroy(error) {
|
|
64
|
+
if (this._timeoutTimer) {
|
|
65
|
+
clearTimeout(this._timeoutTimer);
|
|
66
|
+
this._timeoutTimer = null;
|
|
67
|
+
}
|
|
68
|
+
return super.destroy(error);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
var Http2ServerResponse = class extends Writable {
|
|
72
|
+
statusCode = 200;
|
|
73
|
+
statusMessage = "";
|
|
74
|
+
headersSent = false;
|
|
75
|
+
finished = false;
|
|
76
|
+
sendDate = true;
|
|
77
|
+
_soupMsg;
|
|
78
|
+
_headers = new Map();
|
|
79
|
+
_streaming = false;
|
|
80
|
+
_timeoutTimer = null;
|
|
81
|
+
_stream = null;
|
|
82
|
+
get stream() {
|
|
83
|
+
return this._stream;
|
|
84
|
+
}
|
|
85
|
+
get socket() {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
_setStream(stream) {
|
|
89
|
+
this._stream = stream;
|
|
90
|
+
}
|
|
91
|
+
constructor(soupMsg) {
|
|
92
|
+
super();
|
|
93
|
+
this._soupMsg = soupMsg;
|
|
94
|
+
}
|
|
95
|
+
setHeader(name, value) {
|
|
96
|
+
this._headers.set(name.toLowerCase(), typeof value === "number" ? String(value) : value);
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
getHeader(name) {
|
|
100
|
+
return this._headers.get(name.toLowerCase());
|
|
101
|
+
}
|
|
102
|
+
removeHeader(name) {
|
|
103
|
+
this._headers.delete(name.toLowerCase());
|
|
104
|
+
}
|
|
105
|
+
hasHeader(name) {
|
|
106
|
+
return this._headers.has(name.toLowerCase());
|
|
107
|
+
}
|
|
108
|
+
getHeaderNames() {
|
|
109
|
+
return Array.from(this._headers.keys());
|
|
110
|
+
}
|
|
111
|
+
getHeaders() {
|
|
112
|
+
const result = {};
|
|
113
|
+
for (const [key, value] of this._headers) {
|
|
114
|
+
result[key] = value;
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
appendHeader(name, value) {
|
|
119
|
+
const lower = name.toLowerCase();
|
|
120
|
+
const existing = this._headers.get(lower);
|
|
121
|
+
if (existing === undefined) {
|
|
122
|
+
this._headers.set(lower, value);
|
|
123
|
+
} else if (Array.isArray(existing)) {
|
|
124
|
+
Array.isArray(value) ? existing.push(...value) : existing.push(value);
|
|
125
|
+
} else {
|
|
126
|
+
this._headers.set(lower, Array.isArray(value) ? [existing, ...value] : [existing, value]);
|
|
127
|
+
}
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
130
|
+
flushHeaders() {
|
|
131
|
+
if (!this.headersSent) this.headersSent = true;
|
|
132
|
+
}
|
|
133
|
+
writeHead(statusCode, statusMessage, headers) {
|
|
134
|
+
this.statusCode = statusCode;
|
|
135
|
+
if (typeof statusMessage === "object") {
|
|
136
|
+
headers = statusMessage;
|
|
137
|
+
statusMessage = undefined;
|
|
138
|
+
}
|
|
139
|
+
if (typeof statusMessage === "string") this.statusMessage = statusMessage;
|
|
140
|
+
if (headers) {
|
|
141
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
142
|
+
this.setHeader(key, value);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
respond(headers, options) {
|
|
148
|
+
const status = Number(headers[":status"] ?? 200);
|
|
149
|
+
const rest = {};
|
|
150
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
151
|
+
if (k === ":status") continue;
|
|
152
|
+
rest[k] = typeof v === "number" ? String(v) : v;
|
|
153
|
+
}
|
|
154
|
+
this.writeHead(status, rest);
|
|
155
|
+
if (options?.endStream) this.end();
|
|
156
|
+
}
|
|
157
|
+
writeContinue(callback) {
|
|
158
|
+
if (callback) Promise.resolve().then(callback);
|
|
159
|
+
}
|
|
160
|
+
writeEarlyHints(_hints, callback) {
|
|
161
|
+
if (callback) Promise.resolve().then(callback);
|
|
162
|
+
}
|
|
163
|
+
addTrailers(_headers) {}
|
|
164
|
+
setTimeout(msecs, callback) {
|
|
165
|
+
if (this._timeoutTimer) {
|
|
166
|
+
clearTimeout(this._timeoutTimer);
|
|
167
|
+
this._timeoutTimer = null;
|
|
168
|
+
}
|
|
169
|
+
if (callback) this.once("timeout", callback);
|
|
170
|
+
if (msecs > 0) {
|
|
171
|
+
this._timeoutTimer = setTimeout(() => {
|
|
172
|
+
this._timeoutTimer = null;
|
|
173
|
+
this.emit("timeout");
|
|
174
|
+
}, msecs);
|
|
175
|
+
}
|
|
176
|
+
return this;
|
|
177
|
+
}
|
|
178
|
+
_startStreaming() {
|
|
179
|
+
if (this._streaming) return;
|
|
180
|
+
this._streaming = true;
|
|
181
|
+
this.headersSent = true;
|
|
182
|
+
if (this._timeoutTimer) {
|
|
183
|
+
clearTimeout(this._timeoutTimer);
|
|
184
|
+
this._timeoutTimer = null;
|
|
185
|
+
}
|
|
186
|
+
this._soupMsg.set_status(this.statusCode, this.statusMessage || null);
|
|
187
|
+
const responseHeaders = this._soupMsg.get_response_headers();
|
|
188
|
+
if (this._headers.has("content-length")) {
|
|
189
|
+
responseHeaders.set_encoding(Soup.Encoding.CONTENT_LENGTH);
|
|
190
|
+
} else {
|
|
191
|
+
responseHeaders.set_encoding(Soup.Encoding.CHUNKED);
|
|
192
|
+
}
|
|
193
|
+
for (const [key, value] of this._headers) {
|
|
194
|
+
if (Array.isArray(value)) {
|
|
195
|
+
for (const v of value) responseHeaders.append(key, v);
|
|
196
|
+
} else {
|
|
197
|
+
responseHeaders.replace(key, value);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
_write(chunk, encoding, callback) {
|
|
202
|
+
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
|
|
203
|
+
this._startStreaming();
|
|
204
|
+
const responseBody = this._soupMsg.get_response_body();
|
|
205
|
+
responseBody.append(new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength));
|
|
206
|
+
this._soupMsg.unpause();
|
|
207
|
+
callback();
|
|
208
|
+
}
|
|
209
|
+
_final(callback) {
|
|
210
|
+
if (this._streaming) {
|
|
211
|
+
const responseBody = this._soupMsg.get_response_body();
|
|
212
|
+
responseBody.complete();
|
|
213
|
+
this._soupMsg.unpause();
|
|
214
|
+
} else {
|
|
215
|
+
this._sendBatchResponse();
|
|
216
|
+
}
|
|
217
|
+
this.finished = true;
|
|
218
|
+
callback();
|
|
219
|
+
}
|
|
220
|
+
_sendBatchResponse() {
|
|
221
|
+
if (this.headersSent) return;
|
|
222
|
+
this.headersSent = true;
|
|
223
|
+
if (this._timeoutTimer) {
|
|
224
|
+
clearTimeout(this._timeoutTimer);
|
|
225
|
+
this._timeoutTimer = null;
|
|
226
|
+
}
|
|
227
|
+
this._soupMsg.set_status(this.statusCode, this.statusMessage || null);
|
|
228
|
+
const responseHeaders = this._soupMsg.get_response_headers();
|
|
229
|
+
for (const [key, value] of this._headers) {
|
|
230
|
+
if (Array.isArray(value)) {
|
|
231
|
+
for (const v of value) responseHeaders.append(key, v);
|
|
232
|
+
} else {
|
|
233
|
+
responseHeaders.replace(key, value);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const contentType = this._headers.get("content-type") || "text/plain";
|
|
237
|
+
this._soupMsg.set_response(contentType, Soup.MemoryUse.COPY, new Uint8Array(0));
|
|
238
|
+
}
|
|
239
|
+
end(chunk, encoding, callback) {
|
|
240
|
+
if (typeof chunk === "function") {
|
|
241
|
+
callback = chunk;
|
|
242
|
+
chunk = undefined;
|
|
243
|
+
} else if (typeof encoding === "function") {
|
|
244
|
+
callback = encoding;
|
|
245
|
+
encoding = undefined;
|
|
246
|
+
}
|
|
247
|
+
if (chunk != null) {
|
|
248
|
+
this.write(chunk, encoding);
|
|
249
|
+
}
|
|
250
|
+
super.end(callback);
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
respondWithFD(_fd, _headers, _options) {
|
|
254
|
+
throw new Error("http2 respondWithFD is not yet implemented in GJS (Phase 2)");
|
|
255
|
+
}
|
|
256
|
+
respondWithFile(_path, _headers, _options) {
|
|
257
|
+
throw new Error("http2 respondWithFile is not yet implemented in GJS (Phase 2)");
|
|
258
|
+
}
|
|
259
|
+
pushStream(_headers, _options, _callback) {
|
|
260
|
+
throw new Error("http2 server push is not yet implemented in GJS (Phase 2)");
|
|
261
|
+
}
|
|
262
|
+
createPushResponse(_headers, _callback) {
|
|
263
|
+
throw new Error("http2 server push is not yet implemented in GJS (Phase 2)");
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
var ServerHttp2Stream = class extends EventEmitter {
|
|
267
|
+
id = 1;
|
|
268
|
+
pushAllowed = false;
|
|
269
|
+
sentHeaders = {};
|
|
270
|
+
_res;
|
|
271
|
+
_session;
|
|
272
|
+
get session() {
|
|
273
|
+
return this._session;
|
|
274
|
+
}
|
|
275
|
+
get headersSent() {
|
|
276
|
+
return this._res.headersSent;
|
|
277
|
+
}
|
|
278
|
+
get closed() {
|
|
279
|
+
return this._res.writableEnded;
|
|
280
|
+
}
|
|
281
|
+
get destroyed() {
|
|
282
|
+
return this._res.destroyed;
|
|
283
|
+
}
|
|
284
|
+
get pending() {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
get state() {
|
|
288
|
+
return this.closed ? constants.NGHTTP2_STREAM_STATE_CLOSED : constants.NGHTTP2_STREAM_STATE_OPEN;
|
|
289
|
+
}
|
|
290
|
+
constructor(res, session = null) {
|
|
291
|
+
super();
|
|
292
|
+
this._res = res;
|
|
293
|
+
this._session = session;
|
|
294
|
+
res.on("finish", () => this.emit("close"));
|
|
295
|
+
res.on("error", (err) => this.emit("error", err));
|
|
296
|
+
}
|
|
297
|
+
respond(headers, options) {
|
|
298
|
+
this._res.respond(headers, options);
|
|
299
|
+
}
|
|
300
|
+
write(chunk, encoding, callback) {
|
|
301
|
+
return this._res.write(chunk, encoding, callback);
|
|
302
|
+
}
|
|
303
|
+
end(chunk, encoding, callback) {
|
|
304
|
+
this._res.end(chunk, encoding, callback);
|
|
305
|
+
return this;
|
|
306
|
+
}
|
|
307
|
+
destroy(error) {
|
|
308
|
+
this._res.destroy(error);
|
|
309
|
+
return this;
|
|
310
|
+
}
|
|
311
|
+
close(code, callback) {
|
|
312
|
+
if (callback) this.once("close", callback);
|
|
313
|
+
this._res.end();
|
|
314
|
+
}
|
|
315
|
+
priority(_options) {}
|
|
316
|
+
setTimeout(msecs, callback) {
|
|
317
|
+
this._res.setTimeout(msecs, callback);
|
|
318
|
+
return this;
|
|
319
|
+
}
|
|
320
|
+
sendTrailers(_headers) {}
|
|
321
|
+
additionalHeaders(_headers) {}
|
|
322
|
+
respondWithFD(_fd, _headers, _options) {
|
|
323
|
+
throw new Error("http2 respondWithFD is not yet implemented in GJS (Phase 2)");
|
|
324
|
+
}
|
|
325
|
+
respondWithFile(_path, _headers, _options) {
|
|
326
|
+
throw new Error("http2 respondWithFile is not yet implemented in GJS (Phase 2)");
|
|
327
|
+
}
|
|
328
|
+
pushStream(_headers, _options, _callback) {
|
|
329
|
+
throw new Error("http2 server push is not yet implemented in GJS (Phase 2)");
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
var ServerHttp2Session = class extends EventEmitter {
|
|
333
|
+
type = constants.NGHTTP2_SESSION_SERVER;
|
|
334
|
+
alpnProtocol = "h2";
|
|
335
|
+
encrypted = true;
|
|
336
|
+
_closed = false;
|
|
337
|
+
_destroyed = false;
|
|
338
|
+
_settings;
|
|
339
|
+
constructor() {
|
|
340
|
+
super();
|
|
341
|
+
this._settings = getDefaultSettings();
|
|
342
|
+
}
|
|
343
|
+
get closed() {
|
|
344
|
+
return this._closed;
|
|
345
|
+
}
|
|
346
|
+
get destroyed() {
|
|
347
|
+
return this._destroyed;
|
|
348
|
+
}
|
|
349
|
+
get pendingSettingsAck() {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
get localSettings() {
|
|
353
|
+
return { ...this._settings };
|
|
354
|
+
}
|
|
355
|
+
get remoteSettings() {
|
|
356
|
+
return getDefaultSettings();
|
|
357
|
+
}
|
|
358
|
+
get originSet() {
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
settings(settings, callback) {
|
|
362
|
+
Object.assign(this._settings, settings);
|
|
363
|
+
if (callback) Promise.resolve().then(callback);
|
|
364
|
+
}
|
|
365
|
+
goaway(code, _lastStreamId, _data) {
|
|
366
|
+
this.emit("goaway", code ?? constants.NGHTTP2_NO_ERROR);
|
|
367
|
+
this.destroy();
|
|
368
|
+
}
|
|
369
|
+
ping(_payload, callback) {
|
|
370
|
+
const buf = new Uint8Array(8);
|
|
371
|
+
if (callback) Promise.resolve().then(() => callback(null, 0, buf));
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
close(callback) {
|
|
375
|
+
if (this._closed) return;
|
|
376
|
+
this._closed = true;
|
|
377
|
+
this.emit("close");
|
|
378
|
+
if (callback) callback();
|
|
379
|
+
}
|
|
380
|
+
destroy(error, code) {
|
|
381
|
+
if (this._destroyed) return;
|
|
382
|
+
this._destroyed = true;
|
|
383
|
+
this._closed = true;
|
|
384
|
+
if (error) this.emit("error", error);
|
|
385
|
+
if (code !== undefined) this.emit("goaway", code);
|
|
386
|
+
this.emit("close");
|
|
387
|
+
}
|
|
388
|
+
altsvc(_alt, _originOrStream) {}
|
|
389
|
+
origin(..._origins) {}
|
|
390
|
+
ref() {}
|
|
391
|
+
unref() {}
|
|
392
|
+
};
|
|
393
|
+
const _activeServers = new Set();
|
|
394
|
+
var Http2Server = class extends EventEmitter {
|
|
395
|
+
listening = false;
|
|
396
|
+
maxHeadersCount = 2e3;
|
|
397
|
+
timeout = 0;
|
|
398
|
+
_soupServer = null;
|
|
399
|
+
_address = null;
|
|
400
|
+
_options;
|
|
401
|
+
get soupServer() {
|
|
402
|
+
return this._soupServer;
|
|
403
|
+
}
|
|
404
|
+
constructor(options, handler) {
|
|
405
|
+
super();
|
|
406
|
+
if (typeof options === "function") {
|
|
407
|
+
handler = options;
|
|
408
|
+
options = {};
|
|
409
|
+
}
|
|
410
|
+
this._options = options ?? {};
|
|
411
|
+
if (handler) this.on("request", handler);
|
|
412
|
+
}
|
|
413
|
+
listen(...args) {
|
|
414
|
+
let port = 0;
|
|
415
|
+
let hostname = "0.0.0.0";
|
|
416
|
+
let callback;
|
|
417
|
+
for (const arg of args) {
|
|
418
|
+
if (typeof arg === "number") port = arg;
|
|
419
|
+
else if (typeof arg === "string") hostname = arg;
|
|
420
|
+
else if (typeof arg === "function") callback = arg;
|
|
421
|
+
}
|
|
422
|
+
if (callback) this.once("listening", callback);
|
|
423
|
+
try {
|
|
424
|
+
this._soupServer = new Soup.Server({});
|
|
425
|
+
this._configureSoupServer(this._soupServer);
|
|
426
|
+
this._soupServer.add_handler(null, (_server, msg, _path) => {
|
|
427
|
+
this._handleRequest(msg);
|
|
428
|
+
});
|
|
429
|
+
this._soupServer.listen_local(port, Soup.ServerListenOptions.IPV4_ONLY);
|
|
430
|
+
ensureMainLoop();
|
|
431
|
+
const listeners = this._soupServer.get_listeners();
|
|
432
|
+
let actualPort = port;
|
|
433
|
+
if (listeners && listeners.length > 0) {
|
|
434
|
+
const addr = listeners[0].get_local_address();
|
|
435
|
+
if (addr && typeof addr.get_port === "function") {
|
|
436
|
+
actualPort = addr.get_port();
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
this.listening = true;
|
|
440
|
+
this._address = {
|
|
441
|
+
port: actualPort,
|
|
442
|
+
family: "IPv4",
|
|
443
|
+
address: hostname
|
|
444
|
+
};
|
|
445
|
+
_activeServers.add(this);
|
|
446
|
+
deferEmit(this, "listening");
|
|
447
|
+
} catch (err) {
|
|
448
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
449
|
+
if (this.listenerCount("error") === 0) throw error;
|
|
450
|
+
deferEmit(this, "error", error);
|
|
451
|
+
}
|
|
452
|
+
return this;
|
|
453
|
+
}
|
|
454
|
+
_configureSoupServer(_server) {}
|
|
455
|
+
_handleRequest(soupMsg) {
|
|
456
|
+
const req = new Http2ServerRequest();
|
|
457
|
+
const res = new Http2ServerResponse(soupMsg);
|
|
458
|
+
req.method = soupMsg.get_method();
|
|
459
|
+
const uri = soupMsg.get_uri();
|
|
460
|
+
const path = uri.get_path();
|
|
461
|
+
const query = uri.get_query();
|
|
462
|
+
req.url = query ? path + "?" + query : path;
|
|
463
|
+
req.authority = uri.get_host() ?? "";
|
|
464
|
+
req.scheme = uri.get_scheme() ?? "http";
|
|
465
|
+
const httpVersion = soupMsg.get_http_version();
|
|
466
|
+
if (httpVersion === Soup.HTTPVersion.HTTP_2_0) {
|
|
467
|
+
req.httpVersion = "2.0";
|
|
468
|
+
req.httpVersionMajor = 2;
|
|
469
|
+
req.httpVersionMinor = 0;
|
|
470
|
+
} else {
|
|
471
|
+
req.httpVersion = "1.1";
|
|
472
|
+
req.httpVersionMajor = 1;
|
|
473
|
+
req.httpVersionMinor = 1;
|
|
474
|
+
}
|
|
475
|
+
const requestHeaders = soupMsg.get_request_headers();
|
|
476
|
+
requestHeaders.foreach((name, value) => {
|
|
477
|
+
const lower = name.toLowerCase();
|
|
478
|
+
req.rawHeaders.push(name, value);
|
|
479
|
+
if (lower in req.headers) {
|
|
480
|
+
const existing = req.headers[lower];
|
|
481
|
+
if (Array.isArray(existing)) {
|
|
482
|
+
existing.push(value);
|
|
483
|
+
} else {
|
|
484
|
+
req.headers[lower] = [existing, value];
|
|
485
|
+
}
|
|
486
|
+
} else {
|
|
487
|
+
req.headers[lower] = value;
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
const remoteHost = soupMsg.get_remote_host() ?? "127.0.0.1";
|
|
491
|
+
const remoteAddr = soupMsg.get_remote_address();
|
|
492
|
+
const remotePort = remoteAddr instanceof Gio.InetSocketAddress ? remoteAddr.get_port() : 0;
|
|
493
|
+
req.socket = {
|
|
494
|
+
remoteAddress: remoteHost,
|
|
495
|
+
remotePort,
|
|
496
|
+
localAddress: this._address?.address ?? "127.0.0.1",
|
|
497
|
+
localPort: this._address?.port ?? 0,
|
|
498
|
+
encrypted: this instanceof Http2SecureServer
|
|
499
|
+
};
|
|
500
|
+
const body = soupMsg.get_request_body();
|
|
501
|
+
if (body?.data && body.data.length > 0) {
|
|
502
|
+
req._pushBody(body.data);
|
|
503
|
+
} else {
|
|
504
|
+
req._pushBody(null);
|
|
505
|
+
}
|
|
506
|
+
const streamHeaders = {
|
|
507
|
+
":method": req.method,
|
|
508
|
+
":path": req.url,
|
|
509
|
+
":authority": req.authority,
|
|
510
|
+
":scheme": req.scheme,
|
|
511
|
+
...req.headers
|
|
512
|
+
};
|
|
513
|
+
soupMsg.pause();
|
|
514
|
+
res.on("finish", () => soupMsg.unpause());
|
|
515
|
+
const session = new ServerHttp2Session();
|
|
516
|
+
const stream = new ServerHttp2Stream(res, session);
|
|
517
|
+
req._setStream(stream);
|
|
518
|
+
res._setStream(stream);
|
|
519
|
+
this.emit("stream", stream, streamHeaders);
|
|
520
|
+
this.emit("request", req, res);
|
|
521
|
+
}
|
|
522
|
+
address() {
|
|
523
|
+
return this._address;
|
|
524
|
+
}
|
|
525
|
+
close(callback) {
|
|
526
|
+
if (callback) this.once("close", callback);
|
|
527
|
+
if (this._soupServer) {
|
|
528
|
+
this._soupServer.disconnect();
|
|
529
|
+
this._soupServer = null;
|
|
530
|
+
}
|
|
531
|
+
this.listening = false;
|
|
532
|
+
_activeServers.delete(this);
|
|
533
|
+
deferEmit(this, "close");
|
|
534
|
+
return this;
|
|
535
|
+
}
|
|
536
|
+
setTimeout(msecs, callback) {
|
|
537
|
+
this.timeout = msecs;
|
|
538
|
+
if (callback) this.on("timeout", callback);
|
|
539
|
+
return this;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
var Http2SecureServer = class extends Http2Server {
|
|
543
|
+
_tlsCert = null;
|
|
544
|
+
constructor(options, handler) {
|
|
545
|
+
super(options, handler);
|
|
546
|
+
if (options.cert && options.key) {
|
|
547
|
+
const certPem = _toPemString(options.cert);
|
|
548
|
+
const keyPem = _toPemString(options.key);
|
|
549
|
+
this._tlsCert = _createTlsCertificate(certPem, keyPem);
|
|
550
|
+
} else if (options.pfx) {}
|
|
551
|
+
}
|
|
552
|
+
_configureSoupServer(server) {
|
|
553
|
+
if (this._tlsCert) {
|
|
554
|
+
server.set_tls_certificate(this._tlsCert);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
setSecureContext(options) {
|
|
558
|
+
if (options.cert && options.key) {
|
|
559
|
+
const certPem = _toPemString(options.cert);
|
|
560
|
+
const keyPem = _toPemString(options.key);
|
|
561
|
+
this._tlsCert = _createTlsCertificate(certPem, keyPem);
|
|
562
|
+
if (this._soupServer && this._tlsCert) {
|
|
563
|
+
this._soupServer.set_tls_certificate(this._tlsCert);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
};
|
|
582
568
|
function _toPemString(value) {
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
569
|
+
if (Array.isArray(value)) {
|
|
570
|
+
return value.map(_toPemString).join("\n");
|
|
571
|
+
}
|
|
572
|
+
return Buffer.isBuffer(value) ? value.toString("utf8") : value;
|
|
587
573
|
}
|
|
588
574
|
function _createTlsCertificate(certPem, keyPem) {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
}
|
|
611
|
-
}
|
|
575
|
+
const combined = certPem.trimEnd() + "\n" + keyPem.trimEnd() + "\n";
|
|
576
|
+
try {
|
|
577
|
+
return Gio.TlsCertificate.new_from_pem(combined, -1);
|
|
578
|
+
} catch (err) {
|
|
579
|
+
const tmpDir = GLib.get_tmp_dir();
|
|
580
|
+
const certPath = GLib.build_filenamev([tmpDir, "gjsify-http2-cert.pem"]);
|
|
581
|
+
const keyPath = GLib.build_filenamev([tmpDir, "gjsify-http2-key.pem"]);
|
|
582
|
+
try {
|
|
583
|
+
GLib.file_set_contents(certPath, certPem);
|
|
584
|
+
GLib.file_set_contents(keyPath, keyPem);
|
|
585
|
+
const tlsCert = Gio.TlsCertificate.new_from_files(certPath, keyPath);
|
|
586
|
+
return tlsCert;
|
|
587
|
+
} finally {
|
|
588
|
+
try {
|
|
589
|
+
Gio.File.new_for_path(certPath).delete(null);
|
|
590
|
+
} catch {}
|
|
591
|
+
try {
|
|
592
|
+
Gio.File.new_for_path(keyPath).delete(null);
|
|
593
|
+
} catch {}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
612
596
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
Http2ServerRequest,
|
|
617
|
-
Http2ServerResponse,
|
|
618
|
-
ServerHttp2Session,
|
|
619
|
-
ServerHttp2Stream
|
|
620
|
-
};
|
|
597
|
+
|
|
598
|
+
//#endregion
|
|
599
|
+
export { Http2SecureServer, Http2Server, Http2ServerRequest, Http2ServerResponse, ServerHttp2Session, ServerHttp2Stream };
|