quickwin 1.3.0 → 2.1.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.en.md +18 -24
- package/README.md +17 -23
- package/examples/PdfCanvas.js +8 -37
- package/examples/PdfCanvas.tsx +11 -41
- package/examples/exec_server.js +20155 -1420
- package/examples/exec_server.tsx +489 -0
- package/examples/exec_server_worker.js +383 -33
- package/examples/exec_server_worker.ts +408 -30
- package/examples/pdf_preview2.js +25 -82
- package/examples/pdf_preview2.ts +27 -84
- package/examples/pdf_viewer.js +1424 -1180
- package/examples/pdf_viewer.tsx +16 -67
- package/examples/setres.js +22 -47
- package/examples/setres.ts +23 -57
- package/examples/test_gallery.js +1418 -1075
- package/examples/test_react_lazy.js +1391 -1059
- package/examples/test_react_listbox.js +1390 -1058
- package/examples/test_root_window.js +1384 -1052
- package/examples/test_tab.js +1391 -1059
- package/lib/abort.js +70 -0
- package/lib/abort.ts +69 -0
- package/lib/certs.js +219 -0
- package/lib/certs.ts +229 -0
- package/lib/exec-log.js +15 -0
- package/lib/exec-log.ts +30 -0
- package/lib/fetch.js +88 -7
- package/lib/fetch.ts +73 -7
- package/lib/ffi/bind.js +441 -0
- package/lib/ffi/bind.ts +539 -0
- package/lib/ffi/ctype.js +194 -0
- package/lib/ffi/ctype.ts +369 -0
- package/lib/ffi/index.js +6 -0
- package/lib/ffi/index.ts +6 -0
- package/lib/ffi/struct.js +490 -0
- package/lib/ffi/struct.ts +753 -0
- package/lib/http-common.js +43 -5
- package/lib/http-common.ts +48 -6
- package/lib/http-server.js +187 -29
- package/lib/http-server.ts +177 -26
- package/lib/react-qw/components/DateTimePicker.js +18 -37
- package/lib/react-qw/components/Input.js +3 -10
- package/lib/react-qw/components/Link.js +6 -19
- package/lib/react-qw/components/ListView.js +50 -154
- package/lib/react-qw/components/PathPicker.js +28 -88
- package/lib/react-qw/components/ScrollView.js +15 -8
- package/lib/react-qw/components/Slider.js +2 -1
- package/lib/react-qw/components/Tab.js +6 -22
- package/lib/react-qw/components/Tooltip.js +10 -21
- package/lib/react-qw/components/TreeView.js +8 -31
- package/lib/react-qw/index.js +5 -3
- package/lib/react-qw/index.ts +8 -3
- package/lib/react-qw/nmhdr.js +3 -24
- package/lib/react-qw/nmhdr.ts +5 -30
- package/lib/react-qw/props.js +7 -2
- package/lib/react-qw/props.ts +7 -3
- package/lib/react-qw/reconciler.js +23 -13
- package/lib/react-qw/reconciler.ts +22 -13
- package/lib/text-measure.js +20 -23
- package/lib/text-measure.ts +22 -26
- package/lib/websocket.js +44 -4
- package/lib/websocket.ts +40 -4
- package/lib/windows/advapi32.js +53 -0
- package/lib/windows/advapi32.ts +64 -0
- package/lib/windows/comctl32.js +18 -0
- package/lib/windows/comctl32.ts +19 -0
- package/lib/windows/comdlg32.js +8 -0
- package/lib/windows/comdlg32.ts +10 -0
- package/lib/windows/gdi32.js +392 -0
- package/lib/windows/gdi32.ts +420 -0
- package/lib/windows/kernel32.js +262 -0
- package/lib/windows/kernel32.ts +291 -0
- package/lib/windows/msimg32.js +38 -0
- package/lib/windows/msimg32.ts +45 -0
- package/lib/windows/ntdll.js +9 -0
- package/lib/windows/ntdll.ts +11 -0
- package/lib/windows/ole32.js +7 -0
- package/lib/windows/ole32.ts +9 -0
- package/lib/windows/psapi.js +32 -0
- package/lib/windows/psapi.ts +37 -0
- package/lib/windows/shell32.js +13 -0
- package/lib/windows/shell32.ts +16 -0
- package/lib/windows/structs.js +519 -0
- package/lib/windows/structs.ts +566 -0
- package/lib/windows/user32.js +572 -0
- package/lib/windows/user32.ts +607 -0
- package/lib/windows/version.js +29 -0
- package/lib/windows/version.ts +35 -0
- package/lib/windows/winspool.js +71 -0
- package/lib/windows/winspool.ts +80 -0
- package/package.json +1 -1
- package/quickwin.d.ts +31 -40
- package/quickwin_const.d.ts +137 -0
- package/qwin-nowasm-x86.exe +0 -0
- package/qwin-nowasm.exe +0 -0
- package/qwin-x86.exe +0 -0
- package/qwin.exe +0 -0
- package/test/bench_ffi_callraw.js +236 -0
- package/test/bench_ffi_callraw.ts +236 -0
- package/test/bench_ffi_overhead.js +139 -0
- package/test/bench_ffi_overhead.ts +141 -0
- package/test/bench_sendmessage.js +104 -0
- package/test/bench_sendmessage.ts +100 -0
- package/test/bench_sendmessage_nonnull.js +187 -0
- package/test/bench_sendmessage_nonnull.ts +188 -0
- package/test/run.js +19 -4
- package/test/run.ts +20 -5
- package/test/test_basic_fetch.js +29 -0
- package/test/test_basic_fetch.ts +26 -0
- package/test/test_ffi.js +27 -45
- package/test/test_ffi.ts +35 -54
- package/test/test_ffi_abi.js +290 -0
- package/test/test_ffi_abi.ts +274 -0
- package/test/test_ffi_array.js +116 -0
- package/test/test_ffi_array.ts +141 -0
- package/test/test_ffi_bind.js +620 -8
- package/test/test_ffi_bind.ts +630 -8
- package/test/test_ffi_struct.js +632 -88
- package/test/test_ffi_struct.ts +666 -91
- package/test/test_ffi_struct_layout.js +495 -0
- package/test/test_ffi_struct_layout.ts +543 -0
- package/test/test_ffi_user32.js +103 -0
- package/test/test_ffi_user32.ts +113 -0
- package/test/test_ffi_windows.js +101 -0
- package/test/test_ffi_windows.ts +113 -0
- package/test/test_files.js +102 -0
- package/test/test_files.ts +121 -0
- package/test/test_gdi_draw.js +681 -0
- package/test/test_gdi_draw.ts +725 -0
- package/test/test_helper.js +8 -1
- package/test/test_helper.ts +9 -1
- package/test/test_http_import.js +18 -0
- package/test/test_http_import.ts +19 -0
- package/test/test_http_server.js +73 -1
- package/test/test_http_server.ts +75 -1
- package/test/test_ipv6.js +2 -2
- package/test/test_ipv6.ts +2 -2
- package/test/test_net_fetch.js +74 -11
- package/test/test_net_fetch.ts +73 -10
- package/test/test_net_websocket.js +1 -1
- package/test/test_net_websocket.ts +1 -1
- package/test/test_printer.js +137 -0
- package/test/test_printer.ts +148 -0
- package/test/test_proc.js +141 -0
- package/test/test_proc.ts +163 -0
- package/test/test_react_complex.js +1401 -1069
- package/test/test_react_counter.js +1391 -1059
- package/test/test_react_flex.js +1411 -1072
- package/test/test_react_hidden.js +1394 -1062
- package/test/test_react_listview_link.js +1391 -1059
- package/test/test_react_notify.js +18638 -0
- package/test/test_react_render.js +1391 -1059
- package/test/test_registry.js +74 -0
- package/test/test_registry.ts +95 -0
- package/test/test_sysinfo.js +122 -0
- package/test/test_sysinfo.ts +141 -0
- package/test/test_wolfssl_read.js +45 -43
- package/test/test_wolfssl_read.ts +42 -40
- package/test/test_worker_net.js +35 -17
- package/test/test_worker_net.ts +27 -15
- package/tsconfig.json +1 -1
- package/examples/exec_server.ts +0 -80
- package/lib/ffi-bind.js +0 -85
- package/lib/ffi-bind.ts +0 -170
- package/lib/ffi-struct.js +0 -224
- package/lib/ffi-struct.ts +0 -239
- package/lib/win-common-structs.js +0 -39
- package/lib/win-common-structs.ts +0 -41
package/lib/fetch.js
CHANGED
|
@@ -26,6 +26,27 @@ function parseHeaders(data) {
|
|
|
26
26
|
}
|
|
27
27
|
return { status, statusText, headers };
|
|
28
28
|
}
|
|
29
|
+
/** True when the chunked body is complete. With a declared `Trailer`,
|
|
30
|
+
* the terminating block is followed by 0..n header lines and a final
|
|
31
|
+
* empty line (`\r\n0\r\n<trailer...>\r\n\r\n`) instead of the bare
|
|
32
|
+
* `\r\n0\r\n\r\n` form. */
|
|
33
|
+
function chunkedEndReached(data, hasTrailer) {
|
|
34
|
+
if (!hasTrailer)
|
|
35
|
+
return _hasChunkedEnd(data);
|
|
36
|
+
const tail = '\r\n0\r\n';
|
|
37
|
+
for (let i = data.length - 1; i >= 0; i--) {
|
|
38
|
+
let j = 0;
|
|
39
|
+
while (j < tail.length && i + j < data.length && data[i + j] === tail.charCodeAt(j))
|
|
40
|
+
j++;
|
|
41
|
+
if (j === tail.length) {
|
|
42
|
+
const after = i + tail.length;
|
|
43
|
+
if (data.length >= after + 2 &&
|
|
44
|
+
data[data.length - 2] === 0x0D && data[data.length - 1] === 0x0A)
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
29
50
|
// ── State machine constants ──
|
|
30
51
|
const ST_CONNECTING = 0;
|
|
31
52
|
const ST_HANDSHAKE = 1;
|
|
@@ -35,6 +56,9 @@ const ST_DONE = 5;
|
|
|
35
56
|
// ── Main fetch request ──
|
|
36
57
|
async function fetchRequest(req) {
|
|
37
58
|
const parsedUrl = new URL(req.url);
|
|
59
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
60
|
+
throw new TypeError('fetch: unsupported protocol "' + parsedUrl.protocol + '"');
|
|
61
|
+
}
|
|
38
62
|
const method = req.method;
|
|
39
63
|
const headers = new HeadersImpl(req.headers);
|
|
40
64
|
const timeout = req.timeout || 30000;
|
|
@@ -76,6 +100,7 @@ async function fetchRequest(req) {
|
|
|
76
100
|
let _controller = null;
|
|
77
101
|
let headerRaw = new Uint8Array(0);
|
|
78
102
|
let isChunked = false;
|
|
103
|
+
let hasTrailer = false;
|
|
79
104
|
let contentLength = 0;
|
|
80
105
|
let chunkedParts = [];
|
|
81
106
|
let receivedBytes = 0;
|
|
@@ -168,15 +193,25 @@ async function fetchRequest(req) {
|
|
|
168
193
|
doReject(new Error('SSL_CTX_new failed'));
|
|
169
194
|
return;
|
|
170
195
|
}
|
|
171
|
-
|
|
196
|
+
if (req.rejectUnauthorized === false) {
|
|
197
|
+
wolfssl.wolfSSL_CTX_set_verify(ctx, 0 /* wolfssl.VerifyMode.SSL_VERIFY_NONE */);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
wolfssl.wolfSSL_CTX_set_verify(ctx, 1 /* wolfssl.VerifyMode.SSL_VERIFY_PEER */);
|
|
201
|
+
wolfssl.loadTrustedCerts(ctx);
|
|
202
|
+
}
|
|
172
203
|
ssl = wolfssl.wolfSSL_new(ctx);
|
|
173
204
|
if (!ssl) {
|
|
174
205
|
doReject(new Error('SSL_new failed'));
|
|
175
206
|
return;
|
|
176
207
|
}
|
|
177
208
|
wolfssl.wolfSSL_set_fd(ssl, sock.get_fd(fd));
|
|
178
|
-
if (parsedUrl.hostname)
|
|
209
|
+
if (parsedUrl.hostname) {
|
|
179
210
|
wolfssl.wolfSSL_UseSNI(ssl, 0 /* wolfssl.SniType.WOLFSSL_SNI_HOST_NAME */, parsedUrl.hostname);
|
|
211
|
+
if (req.rejectUnauthorized !== false) {
|
|
212
|
+
wolfssl.wolfSSL_check_domain_name(ssl, parsedUrl.hostname);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
180
215
|
state = ST_HANDSHAKE;
|
|
181
216
|
}
|
|
182
217
|
else {
|
|
@@ -209,7 +244,21 @@ async function fetchRequest(req) {
|
|
|
209
244
|
break;
|
|
210
245
|
let data;
|
|
211
246
|
if (isHTTPS && ssl) {
|
|
212
|
-
|
|
247
|
+
const buf = new ArrayBuffer(8192);
|
|
248
|
+
const n = wolfssl.wolfSSL_read(ssl, buf);
|
|
249
|
+
if (n > 0) {
|
|
250
|
+
data = buf.slice(0, n);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
const err = wolfssl.wolfSSL_get_error(ssl, n);
|
|
254
|
+
if (err === 2 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ */ ||
|
|
255
|
+
err === 3 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE */)
|
|
256
|
+
break;
|
|
257
|
+
if (n === 0 || err === 6 /* wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN */)
|
|
258
|
+
break;
|
|
259
|
+
doReject(new Error('TLS read failed: ' + err));
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
213
262
|
}
|
|
214
263
|
else if (s !== null && s >= 0) {
|
|
215
264
|
data = sock.recv(s, 8192);
|
|
@@ -237,6 +286,7 @@ async function fetchRequest(req) {
|
|
|
237
286
|
}
|
|
238
287
|
const trailingBodyBytes = headerRaw.subarray(headerEnd + 4);
|
|
239
288
|
isChunked = (parsed.headers.get('transfer-encoding') || '').toLowerCase().includes('chunked');
|
|
289
|
+
hasTrailer = parsed.headers.get('trailer') != null;
|
|
240
290
|
contentLength = isChunked ? 0 : parseInt(parsed.headers.get('content-length') || '0', 10);
|
|
241
291
|
_controller = null;
|
|
242
292
|
stream = new ReadableStream({
|
|
@@ -246,7 +296,7 @@ async function fetchRequest(req) {
|
|
|
246
296
|
if (trailingBodyBytes.length > 0) {
|
|
247
297
|
if (isChunked) {
|
|
248
298
|
chunkedParts = [trailingBodyBytes];
|
|
249
|
-
if (
|
|
299
|
+
if (chunkedEndReached(trailingBodyBytes, hasTrailer)) {
|
|
250
300
|
const decoded = decodeChunked(trailingBodyBytes);
|
|
251
301
|
_controller.enqueue(decoded);
|
|
252
302
|
_controller.close();
|
|
@@ -281,7 +331,31 @@ async function fetchRequest(req) {
|
|
|
281
331
|
break;
|
|
282
332
|
let data;
|
|
283
333
|
if (isHTTPS && ssl) {
|
|
284
|
-
|
|
334
|
+
const buf = new ArrayBuffer(8192);
|
|
335
|
+
const n = wolfssl.wolfSSL_read(ssl, buf);
|
|
336
|
+
if (n > 0) {
|
|
337
|
+
data = buf.slice(0, n);
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
const err = wolfssl.wolfSSL_get_error(ssl, n);
|
|
341
|
+
if (err === 2 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ */ ||
|
|
342
|
+
err === 3 /* wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE */)
|
|
343
|
+
break;
|
|
344
|
+
if (n === 0 || err === 6 /* wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN */)
|
|
345
|
+
break;
|
|
346
|
+
// Fatal TLS error mid-body: the response already
|
|
347
|
+
// resolved, so fail the body stream instead of
|
|
348
|
+
// silently waiting for an FD event that never comes.
|
|
349
|
+
if (_controller) {
|
|
350
|
+
try {
|
|
351
|
+
_controller.error(new Error('TLS read failed: ' + err));
|
|
352
|
+
}
|
|
353
|
+
catch { /* already closed */ }
|
|
354
|
+
}
|
|
355
|
+
stream = null;
|
|
356
|
+
cleanupSocket();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
285
359
|
}
|
|
286
360
|
else if (s !== null && s >= 0) {
|
|
287
361
|
data = sock.recv(s, 8192);
|
|
@@ -294,7 +368,7 @@ async function fetchRequest(req) {
|
|
|
294
368
|
if (isChunked) {
|
|
295
369
|
chunkedParts.push(new Uint8Array(data));
|
|
296
370
|
const combined = _concat(chunkedParts);
|
|
297
|
-
if (
|
|
371
|
+
if (chunkedEndReached(combined, hasTrailer)) {
|
|
298
372
|
const decoded = decodeChunked(combined);
|
|
299
373
|
_controller.enqueue(decoded);
|
|
300
374
|
_controller.close();
|
|
@@ -329,7 +403,14 @@ async function fetchRequest(req) {
|
|
|
329
403
|
break;
|
|
330
404
|
let data;
|
|
331
405
|
if (isHTTPS && ssl) {
|
|
332
|
-
|
|
406
|
+
const buf = new ArrayBuffer(8192);
|
|
407
|
+
const n = wolfssl.wolfSSL_read(ssl, buf);
|
|
408
|
+
// Best-effort drain: any non-data result (EOF, WANT_*,
|
|
409
|
+
// late error) ends the drain — the stream closes below.
|
|
410
|
+
if (n > 0)
|
|
411
|
+
data = buf.slice(0, n);
|
|
412
|
+
else
|
|
413
|
+
break;
|
|
333
414
|
}
|
|
334
415
|
else if (s !== null && s >= 0) {
|
|
335
416
|
data = sock.recv(s, 8192);
|
package/lib/fetch.ts
CHANGED
|
@@ -47,6 +47,25 @@ function parseHeaders(data: string): ParsedResponse | null {
|
|
|
47
47
|
return { status, statusText, headers }
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
/** True when the chunked body is complete. With a declared `Trailer`,
|
|
51
|
+
* the terminating block is followed by 0..n header lines and a final
|
|
52
|
+
* empty line (`\r\n0\r\n<trailer...>\r\n\r\n`) instead of the bare
|
|
53
|
+
* `\r\n0\r\n\r\n` form. */
|
|
54
|
+
function chunkedEndReached(data: Uint8Array, hasTrailer: boolean): boolean {
|
|
55
|
+
if (!hasTrailer) return _hasChunkedEnd(data)
|
|
56
|
+
const tail = '\r\n0\r\n'
|
|
57
|
+
for (let i = data.length - 1; i >= 0; i--) {
|
|
58
|
+
let j = 0
|
|
59
|
+
while (j < tail.length && i + j < data.length && data[i + j] === tail.charCodeAt(j)) j++
|
|
60
|
+
if (j === tail.length) {
|
|
61
|
+
const after = i + tail.length
|
|
62
|
+
if (data.length >= after + 2 &&
|
|
63
|
+
data[data.length - 2] === 0x0D && data[data.length - 1] === 0x0A) return true
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
50
69
|
// ── State machine constants ──
|
|
51
70
|
|
|
52
71
|
const ST_CONNECTING = 0
|
|
@@ -59,6 +78,9 @@ const ST_DONE = 5
|
|
|
59
78
|
|
|
60
79
|
async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
61
80
|
const parsedUrl = new URL(req.url)
|
|
81
|
+
if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
|
|
82
|
+
throw new TypeError('fetch: unsupported protocol "' + parsedUrl.protocol + '"')
|
|
83
|
+
}
|
|
62
84
|
const method = req.method
|
|
63
85
|
const headers = new HeadersImpl(req.headers)
|
|
64
86
|
const timeout = req.timeout || 30000
|
|
@@ -101,6 +123,7 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
101
123
|
let _controller: ReadableStreamDefaultController<Uint8Array> | null = null
|
|
102
124
|
let headerRaw: Uint8Array = new Uint8Array(0)
|
|
103
125
|
let isChunked = false
|
|
126
|
+
let hasTrailer = false
|
|
104
127
|
let contentLength = 0
|
|
105
128
|
let chunkedParts: Uint8Array[] = []
|
|
106
129
|
let receivedBytes = 0
|
|
@@ -163,11 +186,21 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
163
186
|
const tlsMethod = wolfssl.wolfTLSv1_2_client_method()
|
|
164
187
|
ctx = wolfssl.wolfSSL_CTX_new(tlsMethod)
|
|
165
188
|
if (!ctx) { doReject(new Error('SSL_CTX_new failed')); return }
|
|
166
|
-
|
|
189
|
+
if (req.rejectUnauthorized === false) {
|
|
190
|
+
wolfssl.wolfSSL_CTX_set_verify(ctx, wolfssl.VerifyMode.SSL_VERIFY_NONE)
|
|
191
|
+
} else {
|
|
192
|
+
wolfssl.wolfSSL_CTX_set_verify(ctx, wolfssl.VerifyMode.SSL_VERIFY_PEER)
|
|
193
|
+
wolfssl.loadTrustedCerts(ctx)
|
|
194
|
+
}
|
|
167
195
|
ssl = wolfssl.wolfSSL_new(ctx)
|
|
168
196
|
if (!ssl) { doReject(new Error('SSL_new failed')); return }
|
|
169
197
|
wolfssl.wolfSSL_set_fd(ssl, sock.get_fd(fd))
|
|
170
|
-
if (parsedUrl.hostname)
|
|
198
|
+
if (parsedUrl.hostname) {
|
|
199
|
+
wolfssl.wolfSSL_UseSNI(ssl, wolfssl.SniType.WOLFSSL_SNI_HOST_NAME, parsedUrl.hostname)
|
|
200
|
+
if (req.rejectUnauthorized !== false) {
|
|
201
|
+
wolfssl.wolfSSL_check_domain_name(ssl, parsedUrl.hostname)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
171
204
|
state = ST_HANDSHAKE
|
|
172
205
|
} else {
|
|
173
206
|
sock.send(fd, httpRequest)
|
|
@@ -195,7 +228,18 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
195
228
|
if (s !== null && s < 0 && !ssl) break
|
|
196
229
|
let data: ArrayBuffer | null
|
|
197
230
|
if (isHTTPS && ssl) {
|
|
198
|
-
|
|
231
|
+
const buf = new ArrayBuffer(8192)
|
|
232
|
+
const n = wolfssl.wolfSSL_read(ssl, buf)
|
|
233
|
+
if (n > 0) {
|
|
234
|
+
data = buf.slice(0, n)
|
|
235
|
+
} else {
|
|
236
|
+
const err = wolfssl.wolfSSL_get_error(ssl, n)
|
|
237
|
+
if (err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ ||
|
|
238
|
+
err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE) break
|
|
239
|
+
if (n === 0 || err === wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN) break
|
|
240
|
+
doReject(new Error('TLS read failed: ' + err))
|
|
241
|
+
return
|
|
242
|
+
}
|
|
199
243
|
} else if (s !== null && s >= 0) {
|
|
200
244
|
data = sock.recv(s, 8192)
|
|
201
245
|
} else { break }
|
|
@@ -217,6 +261,7 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
217
261
|
const trailingBodyBytes = headerRaw.subarray(headerEnd + 4)
|
|
218
262
|
|
|
219
263
|
isChunked = (parsed.headers.get('transfer-encoding') || '').toLowerCase().includes('chunked')
|
|
264
|
+
hasTrailer = parsed.headers.get('trailer') != null
|
|
220
265
|
contentLength = isChunked ? 0 : parseInt(
|
|
221
266
|
parsed.headers.get('content-length') || '0', 10
|
|
222
267
|
)
|
|
@@ -228,7 +273,7 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
228
273
|
if (trailingBodyBytes.length > 0) {
|
|
229
274
|
if (isChunked) {
|
|
230
275
|
chunkedParts = [trailingBodyBytes]
|
|
231
|
-
if (
|
|
276
|
+
if (chunkedEndReached(trailingBodyBytes, hasTrailer)) {
|
|
232
277
|
const decoded = decodeChunked(trailingBodyBytes)
|
|
233
278
|
_controller!.enqueue(decoded)
|
|
234
279
|
_controller!.close()
|
|
@@ -264,7 +309,23 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
264
309
|
if (s !== null && s < 0 && !ssl) break
|
|
265
310
|
let data: ArrayBuffer | null
|
|
266
311
|
if (isHTTPS && ssl) {
|
|
267
|
-
|
|
312
|
+
const buf = new ArrayBuffer(8192)
|
|
313
|
+
const n = wolfssl.wolfSSL_read(ssl, buf)
|
|
314
|
+
if (n > 0) {
|
|
315
|
+
data = buf.slice(0, n)
|
|
316
|
+
} else {
|
|
317
|
+
const err = wolfssl.wolfSSL_get_error(ssl, n)
|
|
318
|
+
if (err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_READ ||
|
|
319
|
+
err === wolfssl.ErrorCode.WOLFSSL_ERROR_WANT_WRITE) break
|
|
320
|
+
if (n === 0 || err === wolfssl.ErrorCode.WOLFSSL_ERROR_ZERO_RETURN) break
|
|
321
|
+
// Fatal TLS error mid-body: the response already
|
|
322
|
+
// resolved, so fail the body stream instead of
|
|
323
|
+
// silently waiting for an FD event that never comes.
|
|
324
|
+
if (_controller) { try { _controller.error(new Error('TLS read failed: ' + err)) } catch { /* already closed */ } }
|
|
325
|
+
stream = null
|
|
326
|
+
cleanupSocket()
|
|
327
|
+
return
|
|
328
|
+
}
|
|
268
329
|
} else if (s !== null && s >= 0) {
|
|
269
330
|
data = sock.recv(s, 8192)
|
|
270
331
|
} else { break }
|
|
@@ -272,7 +333,7 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
272
333
|
if (isChunked) {
|
|
273
334
|
chunkedParts.push(new Uint8Array(data))
|
|
274
335
|
const combined = _concat(chunkedParts)
|
|
275
|
-
if (
|
|
336
|
+
if (chunkedEndReached(combined, hasTrailer)) {
|
|
276
337
|
const decoded = decodeChunked(combined)
|
|
277
338
|
_controller!.enqueue(decoded)
|
|
278
339
|
_controller!.close()
|
|
@@ -304,7 +365,12 @@ async function fetchRequest(req: RequestImpl): Promise<ResponseImpl> {
|
|
|
304
365
|
if (s !== null && s < 0 && !ssl) break
|
|
305
366
|
let data: ArrayBuffer | null
|
|
306
367
|
if (isHTTPS && ssl) {
|
|
307
|
-
|
|
368
|
+
const buf = new ArrayBuffer(8192)
|
|
369
|
+
const n = wolfssl.wolfSSL_read(ssl, buf)
|
|
370
|
+
// Best-effort drain: any non-data result (EOF, WANT_*,
|
|
371
|
+
// late error) ends the drain — the stream closes below.
|
|
372
|
+
if (n > 0) data = buf.slice(0, n)
|
|
373
|
+
else break
|
|
308
374
|
} else if (s !== null && s >= 0) {
|
|
309
375
|
data = sock.recv(s, 8192)
|
|
310
376
|
} else { break }
|