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/test/test_helper.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as std from 'std';
|
|
2
2
|
const GREEN = '\x1b[32m';
|
|
3
3
|
const RED = '\x1b[31m';
|
|
4
|
+
const YELLOW = '\x1b[33m';
|
|
4
5
|
const BOLD = '\x1b[1m';
|
|
5
6
|
const RESET = '\x1b[0m';
|
|
6
7
|
function formatDuration(ms) {
|
|
@@ -35,6 +36,7 @@ export function readWasmFile(path) {
|
|
|
35
36
|
export class Tester {
|
|
36
37
|
ok = 0;
|
|
37
38
|
fail = 0;
|
|
39
|
+
skip = 0;
|
|
38
40
|
startTime = Date.now();
|
|
39
41
|
sectionStart = this.startTime;
|
|
40
42
|
lastSection = '';
|
|
@@ -71,6 +73,10 @@ export class Tester {
|
|
|
71
73
|
checkTrue(name, actual) {
|
|
72
74
|
this.check(name, true, actual);
|
|
73
75
|
}
|
|
76
|
+
skipCase(name) {
|
|
77
|
+
this.skip++;
|
|
78
|
+
std.printf(' %sSKIP:%s %s\n', YELLOW, RESET, name);
|
|
79
|
+
}
|
|
74
80
|
summary() {
|
|
75
81
|
const now = Date.now();
|
|
76
82
|
if (this.lastSection) {
|
|
@@ -79,6 +85,7 @@ export class Tester {
|
|
|
79
85
|
}
|
|
80
86
|
const total = now - this.startTime;
|
|
81
87
|
const color = this.fail > 0 ? RED : GREEN;
|
|
82
|
-
|
|
88
|
+
const skipped = this.skip > 0 ? ` (${this.skip} skipped)` : '';
|
|
89
|
+
std.printf('\n%s%d/%d passed%s (%s)%s\n', color, this.ok, this.ok + this.fail, skipped, formatDuration(total), RESET);
|
|
83
90
|
}
|
|
84
91
|
}
|
package/test/test_helper.ts
CHANGED
|
@@ -2,6 +2,7 @@ import * as std from 'std'
|
|
|
2
2
|
|
|
3
3
|
const GREEN = '\x1b[32m'
|
|
4
4
|
const RED = '\x1b[31m'
|
|
5
|
+
const YELLOW = '\x1b[33m'
|
|
5
6
|
const BOLD = '\x1b[1m'
|
|
6
7
|
const RESET = '\x1b[0m'
|
|
7
8
|
|
|
@@ -34,6 +35,7 @@ export function readWasmFile(path: string): ArrayBuffer | null {
|
|
|
34
35
|
export class Tester {
|
|
35
36
|
ok = 0
|
|
36
37
|
fail = 0
|
|
38
|
+
skip = 0
|
|
37
39
|
private startTime = Date.now()
|
|
38
40
|
private sectionStart = this.startTime
|
|
39
41
|
private lastSection = ''
|
|
@@ -67,6 +69,11 @@ export class Tester {
|
|
|
67
69
|
this.check(name, true, actual)
|
|
68
70
|
}
|
|
69
71
|
|
|
72
|
+
skipCase(name: string): void {
|
|
73
|
+
this.skip++
|
|
74
|
+
std.printf(' %sSKIP:%s %s\n', YELLOW, RESET, name)
|
|
75
|
+
}
|
|
76
|
+
|
|
70
77
|
summary(): void {
|
|
71
78
|
const now = Date.now()
|
|
72
79
|
if (this.lastSection) {
|
|
@@ -75,6 +82,7 @@ export class Tester {
|
|
|
75
82
|
}
|
|
76
83
|
const total = now - this.startTime
|
|
77
84
|
const color = this.fail > 0 ? RED : GREEN
|
|
78
|
-
|
|
85
|
+
const skipped = this.skip > 0 ? ` (${this.skip} skipped)` : ''
|
|
86
|
+
std.printf('\n%s%d/%d passed%s (%s)%s\n', color, this.ok, this.ok + this.fail, skipped, formatDuration(total), RESET)
|
|
79
87
|
}
|
|
80
88
|
}
|
package/test/test_http_import.js
CHANGED
|
@@ -60,6 +60,24 @@ export const suite = {
|
|
|
60
60
|
return;
|
|
61
61
|
assert('cache has body files', after.some(function (f) { return f.endsWith('.body'); }));
|
|
62
62
|
assert('cache has meta files', after.some(function (f) { return f.endsWith('.meta'); }));
|
|
63
|
+
t.section('repeat https import (fresh url, forces TLS)');
|
|
64
|
+
// http_get_sync() short-circuits on a warm disk cache, so a second
|
|
65
|
+
// import of the same URL would never build a TLS connection. Use a
|
|
66
|
+
// distinct query string to force a real handshake into the
|
|
67
|
+
// process-wide CA store, then import once more to confirm repeated
|
|
68
|
+
// loadTrustedCerts() calls keep verifying.
|
|
69
|
+
const url2 = 'https://esm.sh/left-pad@1.3.0?twice=1';
|
|
70
|
+
let mod2;
|
|
71
|
+
try {
|
|
72
|
+
mod2 = await import(url2);
|
|
73
|
+
assert('repeat import succeeds', true);
|
|
74
|
+
assert('repeat default is function', typeof mod2.default === 'function');
|
|
75
|
+
assert('repeat left-pad works', mod2.default('hello', 8) === ' hello');
|
|
76
|
+
}
|
|
77
|
+
catch (e) {
|
|
78
|
+
assert('repeat import succeeds', false);
|
|
79
|
+
std.printf(' ERROR: %s\n', String(e));
|
|
80
|
+
}
|
|
63
81
|
for (const f of after) {
|
|
64
82
|
if (f.endsWith('.meta') && (err !== 0 || before.indexOf(f) < 0)) {
|
|
65
83
|
std.printf(' meta: %s\n', f);
|
package/test/test_http_import.ts
CHANGED
|
@@ -56,6 +56,25 @@ export const suite = {
|
|
|
56
56
|
if (err2 !== 0) return
|
|
57
57
|
assert('cache has body files', after.some(function(f: string) { return f.endsWith('.body') }))
|
|
58
58
|
assert('cache has meta files', after.some(function(f: string) { return f.endsWith('.meta') }))
|
|
59
|
+
|
|
60
|
+
t.section('repeat https import (fresh url, forces TLS)')
|
|
61
|
+
// http_get_sync() short-circuits on a warm disk cache, so a second
|
|
62
|
+
// import of the same URL would never build a TLS connection. Use a
|
|
63
|
+
// distinct query string to force a real handshake into the
|
|
64
|
+
// process-wide CA store, then import once more to confirm repeated
|
|
65
|
+
// loadTrustedCerts() calls keep verifying.
|
|
66
|
+
const url2 = 'https://esm.sh/left-pad@1.3.0?twice=1'
|
|
67
|
+
let mod2: any
|
|
68
|
+
try {
|
|
69
|
+
mod2 = await import(url2)
|
|
70
|
+
assert('repeat import succeeds', true)
|
|
71
|
+
assert('repeat default is function', typeof mod2.default === 'function')
|
|
72
|
+
assert('repeat left-pad works', mod2.default('hello', 8) === ' hello')
|
|
73
|
+
} catch (e) {
|
|
74
|
+
assert('repeat import succeeds', false)
|
|
75
|
+
std.printf(' ERROR: %s\n', String(e))
|
|
76
|
+
}
|
|
77
|
+
|
|
59
78
|
for (const f of after) {
|
|
60
79
|
if (f.endsWith('.meta') && (err !== 0 || before.indexOf(f) < 0)) {
|
|
61
80
|
std.printf(' meta: %s\n', f)
|
package/test/test_http_server.js
CHANGED
|
@@ -3,6 +3,7 @@ import * as std from 'std';
|
|
|
3
3
|
import * as os from 'os';
|
|
4
4
|
import * as sock from 'sock';
|
|
5
5
|
import { createServer } from '../lib/http-server.js';
|
|
6
|
+
import '../lib/fetch.js';
|
|
6
7
|
// ── Minimal raw HTTP client for testing ──
|
|
7
8
|
function httpSend(host, port, raw, nResponses = 1) {
|
|
8
9
|
return new Promise((resolve, reject) => {
|
|
@@ -82,6 +83,40 @@ export const suite = {
|
|
|
82
83
|
else if (path === '/json') {
|
|
83
84
|
return new Response(JSON.stringify({ method: req.method, q: Object.fromEntries(url.searchParams) }), { headers: { 'Content-Type': 'application/json' } });
|
|
84
85
|
}
|
|
86
|
+
else if (path === '/stream') {
|
|
87
|
+
// length unknown: server must send chunked frames
|
|
88
|
+
return new Response(new ReadableStream({
|
|
89
|
+
start(control) {
|
|
90
|
+
control.enqueue(new TextEncoder().encode('chunkA/'));
|
|
91
|
+
control.enqueue(new TextEncoder().encode('chunkB'));
|
|
92
|
+
control.close();
|
|
93
|
+
}
|
|
94
|
+
}), { headers: { 'Content-Type': 'text/plain' } });
|
|
95
|
+
}
|
|
96
|
+
else if (path === '/file') {
|
|
97
|
+
// length known: server streams raw bytes with Content-Length
|
|
98
|
+
return new Response(new ReadableStream({
|
|
99
|
+
start(control) {
|
|
100
|
+
control.enqueue(new TextEncoder().encode('chunkA/'));
|
|
101
|
+
control.enqueue(new TextEncoder().encode('chunkB'));
|
|
102
|
+
control.close();
|
|
103
|
+
}
|
|
104
|
+
}), { headers: { 'Content-Type': 'text/plain', 'Content-Length': '13' } });
|
|
105
|
+
}
|
|
106
|
+
else if (path === '/stream-trailer') {
|
|
107
|
+
// chunked stream with trailing headers (Node addTrailers
|
|
108
|
+
// semantics: `Trailer` declaration + frames, then the footer)
|
|
109
|
+
return new Response(new ReadableStream({
|
|
110
|
+
start(control) {
|
|
111
|
+
control.enqueue(new TextEncoder().encode('chunkA/'));
|
|
112
|
+
control.enqueue(new TextEncoder().encode('chunkB'));
|
|
113
|
+
control.close();
|
|
114
|
+
}
|
|
115
|
+
}), {
|
|
116
|
+
headers: { 'Content-Type': 'text/plain', 'Trailer': 'X-Exit-Code' },
|
|
117
|
+
trailers: Promise.resolve({ 'X-Exit-Code': '3' })
|
|
118
|
+
});
|
|
119
|
+
}
|
|
85
120
|
else {
|
|
86
121
|
return new Response('not found', { status: 404 });
|
|
87
122
|
}
|
|
@@ -101,6 +136,43 @@ export const suite = {
|
|
|
101
136
|
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0);
|
|
102
137
|
t.checkTrue('body = hello world', r.endsWith('hello world'));
|
|
103
138
|
t.checkTrue('content-type text', r.toLowerCase().indexOf('content-type: text/plain') >= 0);
|
|
139
|
+
t.checkTrue('static body gets content-length', r.toLowerCase().indexOf('content-length: 11') >= 0);
|
|
140
|
+
// ── streaming chunked response (length unknown) ──
|
|
141
|
+
t.section('streaming chunked response');
|
|
142
|
+
r = await httpSend('127.0.0.1', port, 'GET /stream HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n');
|
|
143
|
+
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0);
|
|
144
|
+
t.checkTrue('transfer-encoding chunked', r.toLowerCase().indexOf('transfer-encoding: chunked') >= 0);
|
|
145
|
+
t.checkTrue('no content-length', r.toLowerCase().indexOf('content-length:') < 0);
|
|
146
|
+
t.checkTrue('chunk frames + terminator', r.indexOf('7\r\nchunkA/\r\n6\r\nchunkB\r\n0\r\n\r\n') >= 0);
|
|
147
|
+
// ── streaming raw response with known length ──
|
|
148
|
+
t.section('streaming known-length response');
|
|
149
|
+
r = await httpSend('127.0.0.1', port, 'GET /file HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n');
|
|
150
|
+
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0);
|
|
151
|
+
t.checkTrue('content-length 13', r.toLowerCase().indexOf('content-length: 13') >= 0);
|
|
152
|
+
t.checkTrue('no chunked encoding', r.toLowerCase().indexOf('transfer-encoding: chunked') < 0);
|
|
153
|
+
t.checkTrue('streamed raw body', r.includes('chunkA/chunkB'));
|
|
154
|
+
t.checkTrue('no chunk frame headers', r.indexOf('7\r\nchunkA/') < 0);
|
|
155
|
+
// ── HEAD of a streamed body: headers only, empty body ──
|
|
156
|
+
t.section('HEAD streaming response');
|
|
157
|
+
r = await httpSend('127.0.0.1', port, 'HEAD /stream HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n');
|
|
158
|
+
t.checkTrue('HEAD 200', r.indexOf('HTTP/1.1 200') === 0);
|
|
159
|
+
t.checkTrue('HEAD content-length 13', r.toLowerCase().indexOf('content-length: 13') >= 0);
|
|
160
|
+
const headBodyIdx = r.indexOf('\r\n\r\n');
|
|
161
|
+
t.checkTrue('HEAD empty body', r.slice(headBodyIdx + 4).length === 0);
|
|
162
|
+
// ── streaming chunked response with trailing headers ──
|
|
163
|
+
t.section('streaming trailer response');
|
|
164
|
+
r = await httpSend('127.0.0.1', port, 'GET /stream-trailer HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n');
|
|
165
|
+
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0);
|
|
166
|
+
t.checkTrue('trailer declared in head', r.toLowerCase().indexOf('trailer: x-exit-code') >= 0);
|
|
167
|
+
t.checkTrue('terminator carries trailer', r.indexOf('0\r\nX-Exit-Code: 3\r\n\r\n') >= 0);
|
|
168
|
+
t.checkTrue('trailer footer after body', r.indexOf('7\r\nchunkA/\r\n6\r\nchunkB\r\n0\r\nX-Exit-Code: 3\r\n\r\n') >= 0);
|
|
169
|
+
// ── fetch client consumes chunked response with trailer footer ──
|
|
170
|
+
t.section('fetch consumes trailer response');
|
|
171
|
+
{
|
|
172
|
+
const rr = await fetch(`http://127.0.0.1:${port}/stream-trailer`);
|
|
173
|
+
t.checkTrue('status 200', rr.status === 200);
|
|
174
|
+
t.checkTrue('body decoded without trailer', await rr.text() === 'chunkA/chunkB');
|
|
175
|
+
}
|
|
104
176
|
// ── POST with body ──
|
|
105
177
|
t.section('POST with body');
|
|
106
178
|
r = await httpSend('127.0.0.1', port, 'POST /echo HTTP/1.1\r\nHost: x\r\nContent-Length: 7\r\nConnection: close\r\n\r\npayload');
|
|
@@ -132,7 +204,7 @@ export const suite = {
|
|
|
132
204
|
t.checkTrue('connection close on last', r.toLowerCase().indexOf('connection: close') >= 0);
|
|
133
205
|
// ── server received expected requests ──
|
|
134
206
|
t.section('request log');
|
|
135
|
-
t.check('total requests',
|
|
207
|
+
t.check('total requests', 12, requests.length);
|
|
136
208
|
t.checkTrue('saw GET /', requests.indexOf('GET /') >= 0);
|
|
137
209
|
t.checkTrue('saw POST /echo', requests.indexOf('POST /echo') >= 0);
|
|
138
210
|
server.close();
|
package/test/test_http_server.ts
CHANGED
|
@@ -4,6 +4,7 @@ import * as os from 'os'
|
|
|
4
4
|
import * as sock from 'sock'
|
|
5
5
|
import { Tester } from './test_helper.js'
|
|
6
6
|
import { createServer } from '../lib/http-server.js'
|
|
7
|
+
import '../lib/fetch.js'
|
|
7
8
|
|
|
8
9
|
// ── Minimal raw HTTP client for testing ──
|
|
9
10
|
|
|
@@ -69,6 +70,37 @@ export const suite = {
|
|
|
69
70
|
return new Response(req.method + ':' + await req.text(), { headers: { 'Content-Type': 'text/plain' } })
|
|
70
71
|
} else if (path === '/json') {
|
|
71
72
|
return new Response(JSON.stringify({ method: req.method, q: Object.fromEntries(url.searchParams) }), { headers: { 'Content-Type': 'application/json' } })
|
|
73
|
+
} else if (path === '/stream') {
|
|
74
|
+
// length unknown: server must send chunked frames
|
|
75
|
+
return new Response(new ReadableStream({
|
|
76
|
+
start(control) {
|
|
77
|
+
control.enqueue(new TextEncoder().encode('chunkA/'))
|
|
78
|
+
control.enqueue(new TextEncoder().encode('chunkB'))
|
|
79
|
+
control.close()
|
|
80
|
+
}
|
|
81
|
+
}), { headers: { 'Content-Type': 'text/plain' } })
|
|
82
|
+
} else if (path === '/file') {
|
|
83
|
+
// length known: server streams raw bytes with Content-Length
|
|
84
|
+
return new Response(new ReadableStream({
|
|
85
|
+
start(control) {
|
|
86
|
+
control.enqueue(new TextEncoder().encode('chunkA/'))
|
|
87
|
+
control.enqueue(new TextEncoder().encode('chunkB'))
|
|
88
|
+
control.close()
|
|
89
|
+
}
|
|
90
|
+
}), { headers: { 'Content-Type': 'text/plain', 'Content-Length': '13' } })
|
|
91
|
+
} else if (path === '/stream-trailer') {
|
|
92
|
+
// chunked stream with trailing headers (Node addTrailers
|
|
93
|
+
// semantics: `Trailer` declaration + frames, then the footer)
|
|
94
|
+
return new Response(new ReadableStream({
|
|
95
|
+
start(control) {
|
|
96
|
+
control.enqueue(new TextEncoder().encode('chunkA/'))
|
|
97
|
+
control.enqueue(new TextEncoder().encode('chunkB'))
|
|
98
|
+
control.close()
|
|
99
|
+
}
|
|
100
|
+
}), {
|
|
101
|
+
headers: { 'Content-Type': 'text/plain', 'Trailer': 'X-Exit-Code' },
|
|
102
|
+
trailers: Promise.resolve({ 'X-Exit-Code': '3' })
|
|
103
|
+
})
|
|
72
104
|
} else {
|
|
73
105
|
return new Response('not found', { status: 404 })
|
|
74
106
|
}
|
|
@@ -87,6 +119,48 @@ export const suite = {
|
|
|
87
119
|
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0)
|
|
88
120
|
t.checkTrue('body = hello world', r.endsWith('hello world'))
|
|
89
121
|
t.checkTrue('content-type text', r.toLowerCase().indexOf('content-type: text/plain') >= 0)
|
|
122
|
+
t.checkTrue('static body gets content-length', r.toLowerCase().indexOf('content-length: 11') >= 0)
|
|
123
|
+
|
|
124
|
+
// ── streaming chunked response (length unknown) ──
|
|
125
|
+
t.section('streaming chunked response')
|
|
126
|
+
r = await httpSend('127.0.0.1', port, 'GET /stream HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n')
|
|
127
|
+
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0)
|
|
128
|
+
t.checkTrue('transfer-encoding chunked', r.toLowerCase().indexOf('transfer-encoding: chunked') >= 0)
|
|
129
|
+
t.checkTrue('no content-length', r.toLowerCase().indexOf('content-length:') < 0)
|
|
130
|
+
t.checkTrue('chunk frames + terminator', r.indexOf('7\r\nchunkA/\r\n6\r\nchunkB\r\n0\r\n\r\n') >= 0)
|
|
131
|
+
|
|
132
|
+
// ── streaming raw response with known length ──
|
|
133
|
+
t.section('streaming known-length response')
|
|
134
|
+
r = await httpSend('127.0.0.1', port, 'GET /file HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n')
|
|
135
|
+
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0)
|
|
136
|
+
t.checkTrue('content-length 13', r.toLowerCase().indexOf('content-length: 13') >= 0)
|
|
137
|
+
t.checkTrue('no chunked encoding', r.toLowerCase().indexOf('transfer-encoding: chunked') < 0)
|
|
138
|
+
t.checkTrue('streamed raw body', r.includes('chunkA/chunkB'))
|
|
139
|
+
t.checkTrue('no chunk frame headers', r.indexOf('7\r\nchunkA/') < 0)
|
|
140
|
+
|
|
141
|
+
// ── HEAD of a streamed body: headers only, empty body ──
|
|
142
|
+
t.section('HEAD streaming response')
|
|
143
|
+
r = await httpSend('127.0.0.1', port, 'HEAD /stream HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n')
|
|
144
|
+
t.checkTrue('HEAD 200', r.indexOf('HTTP/1.1 200') === 0)
|
|
145
|
+
t.checkTrue('HEAD content-length 13', r.toLowerCase().indexOf('content-length: 13') >= 0)
|
|
146
|
+
const headBodyIdx = r.indexOf('\r\n\r\n')
|
|
147
|
+
t.checkTrue('HEAD empty body', r.slice(headBodyIdx + 4).length === 0)
|
|
148
|
+
|
|
149
|
+
// ── streaming chunked response with trailing headers ──
|
|
150
|
+
t.section('streaming trailer response')
|
|
151
|
+
r = await httpSend('127.0.0.1', port, 'GET /stream-trailer HTTP/1.1\r\nHost: x\r\nConnection: close\r\n\r\n')
|
|
152
|
+
t.checkTrue('200 status', r.indexOf('HTTP/1.1 200') === 0)
|
|
153
|
+
t.checkTrue('trailer declared in head', r.toLowerCase().indexOf('trailer: x-exit-code') >= 0)
|
|
154
|
+
t.checkTrue('terminator carries trailer', r.indexOf('0\r\nX-Exit-Code: 3\r\n\r\n') >= 0)
|
|
155
|
+
t.checkTrue('trailer footer after body', r.indexOf('7\r\nchunkA/\r\n6\r\nchunkB\r\n0\r\nX-Exit-Code: 3\r\n\r\n') >= 0)
|
|
156
|
+
|
|
157
|
+
// ── fetch client consumes chunked response with trailer footer ──
|
|
158
|
+
t.section('fetch consumes trailer response')
|
|
159
|
+
{
|
|
160
|
+
const rr = await fetch(`http://127.0.0.1:${port}/stream-trailer`)
|
|
161
|
+
t.checkTrue('status 200', rr.status === 200)
|
|
162
|
+
t.checkTrue('body decoded without trailer', await rr.text() === 'chunkA/chunkB')
|
|
163
|
+
}
|
|
90
164
|
|
|
91
165
|
// ── POST with body ──
|
|
92
166
|
t.section('POST with body')
|
|
@@ -124,7 +198,7 @@ export const suite = {
|
|
|
124
198
|
|
|
125
199
|
// ── server received expected requests ──
|
|
126
200
|
t.section('request log')
|
|
127
|
-
t.check('total requests',
|
|
201
|
+
t.check('total requests', 12, requests.length)
|
|
128
202
|
t.checkTrue('saw GET /', requests.indexOf('GET /') >= 0)
|
|
129
203
|
t.checkTrue('saw POST /echo', requests.indexOf('POST /echo') >= 0)
|
|
130
204
|
|
package/test/test_ipv6.js
CHANGED
|
@@ -79,7 +79,7 @@ export const suite = {
|
|
|
79
79
|
// ── 4. fetch HTTPS IPv6 ──
|
|
80
80
|
t.section('fetch https://[::1]');
|
|
81
81
|
{
|
|
82
|
-
const r = await fetch(`https://[::1]:${HTTPS_PORT}/`, { timeout: 5000 });
|
|
82
|
+
const r = await fetch(`https://[::1]:${HTTPS_PORT}/`, { timeout: 5000, rejectUnauthorized: false });
|
|
83
83
|
assert('fetch IPv6 HTTPS status 200', r.status === 200);
|
|
84
84
|
const body = await r.text();
|
|
85
85
|
assert('fetch IPv6 HTTPS body', body === 'hello from test server');
|
|
@@ -110,7 +110,7 @@ export const suite = {
|
|
|
110
110
|
// ── 6. WebSocket WSS IPv6 ──
|
|
111
111
|
t.section('wss://[::1] echo');
|
|
112
112
|
await new Promise((resolve, reject) => {
|
|
113
|
-
const ws = new WebSocket(`wss://[::1]:${HTTPS_PORT}
|
|
113
|
+
const ws = new WebSocket(`wss://[::1]:${HTTPS_PORT}/`, { rejectUnauthorized: false });
|
|
114
114
|
const timeoutId = os.setTimeout(() => reject(new Error('WSS IPv6 timeout')), 10000);
|
|
115
115
|
ws.onopen = () => {
|
|
116
116
|
t.ok++;
|
package/test/test_ipv6.ts
CHANGED
|
@@ -70,7 +70,7 @@ export const suite = {
|
|
|
70
70
|
// ── 4. fetch HTTPS IPv6 ──
|
|
71
71
|
t.section('fetch https://[::1]')
|
|
72
72
|
{
|
|
73
|
-
const r = await fetch(`https://[::1]:${HTTPS_PORT}/`, { timeout: 5000 })
|
|
73
|
+
const r = await fetch(`https://[::1]:${HTTPS_PORT}/`, { timeout: 5000, rejectUnauthorized: false })
|
|
74
74
|
assert('fetch IPv6 HTTPS status 200', r.status === 200)
|
|
75
75
|
const body = await r.text()
|
|
76
76
|
assert('fetch IPv6 HTTPS body', body === 'hello from test server')
|
|
@@ -104,7 +104,7 @@ export const suite = {
|
|
|
104
104
|
// ── 6. WebSocket WSS IPv6 ──
|
|
105
105
|
t.section('wss://[::1] echo')
|
|
106
106
|
await new Promise<void>((resolve, reject) => {
|
|
107
|
-
const ws = new WebSocket(`wss://[::1]:${HTTPS_PORT}
|
|
107
|
+
const ws = new WebSocket(`wss://[::1]:${HTTPS_PORT}/`, { rejectUnauthorized: false })
|
|
108
108
|
const timeoutId = os.setTimeout(() => reject(new Error('WSS IPv6 timeout')), 10000)
|
|
109
109
|
|
|
110
110
|
ws.onopen = () => {
|
package/test/test_net_fetch.js
CHANGED
|
@@ -14,13 +14,24 @@ export const suite = {
|
|
|
14
14
|
std.printf(' FAIL: %s\n', name);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
// Retry: public-site reachability (baidu/jsdelivr/esm.sh) depends on
|
|
18
|
+
// CI network conditions, and a single dropped connection must not be
|
|
19
|
+
// reported as a certificate regression. The last error is printed so a
|
|
20
|
+
// real failure is still diagnosable.
|
|
17
21
|
async function safeFetch(url, init) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
let last = 'unknown';
|
|
23
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
24
|
+
try {
|
|
25
|
+
return await fetch(url, init);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
last = String(e?.message || e);
|
|
29
|
+
if (attempt < 2)
|
|
30
|
+
os.sleep(500);
|
|
31
|
+
}
|
|
23
32
|
}
|
|
33
|
+
std.printf(' fetch %s failed after 3 attempts: %s\n', String(url), last);
|
|
34
|
+
return null;
|
|
24
35
|
}
|
|
25
36
|
const BASE = 'http://localhost:18923';
|
|
26
37
|
// ── Plain HTTP GET (regression: slot 0 + !s bug) ──
|
|
@@ -247,7 +258,7 @@ export const suite = {
|
|
|
247
258
|
}
|
|
248
259
|
// ── HTTPS fetch ──
|
|
249
260
|
t.section('HTTPS fetch');
|
|
250
|
-
const r11 = await safeFetch('https://localhost:18924/', { timeout: 5000 });
|
|
261
|
+
const r11 = await safeFetch('https://localhost:18924/', { timeout: 5000, rejectUnauthorized: false });
|
|
251
262
|
if (r11) {
|
|
252
263
|
assert('https status 200', r11.status === 200);
|
|
253
264
|
const body = await r11.text();
|
|
@@ -256,15 +267,67 @@ export const suite = {
|
|
|
256
267
|
else {
|
|
257
268
|
assert('https endpoint reachable', false);
|
|
258
269
|
}
|
|
259
|
-
// ──
|
|
260
|
-
//
|
|
261
|
-
//
|
|
270
|
+
// ── GlobalSign R3 root chain (regression: ASN_SIG_CONFIRM_E -155) ──
|
|
271
|
+
// baidu.com and jsdelivr terminate in GlobalSign's 2018 re-keyed R3,
|
|
272
|
+
// which reuses the self-signed R3's subject-key-identifier with a
|
|
273
|
+
// different public key. Shipping the self-signed R3 in lib/certs.ts
|
|
274
|
+
// made wolfSSL match the wrong key and fail the handshake with -155.
|
|
275
|
+
// Both are public CDNs: a 500/429 or a dropped connection then counts
|
|
276
|
+
// as a retried attempt, not an immediate failure — only a persistent
|
|
277
|
+
// failure (cert regression or long-lived outage) should fail CI.
|
|
278
|
+
t.section('GlobalSign R3 root chain');
|
|
279
|
+
{
|
|
280
|
+
async function fetchR3(url) {
|
|
281
|
+
const init = { timeout: 10000 };
|
|
282
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
283
|
+
try {
|
|
284
|
+
const r = await fetch(url, init);
|
|
285
|
+
const body = await r.text();
|
|
286
|
+
if (r.status === 200 && body.length > 0)
|
|
287
|
+
return body;
|
|
288
|
+
std.printf(' fetch %s status=%s len=%d (attempt %d)\n', String(url), String(r.status), body.length, attempt);
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
std.printf(' fetch %s error: %s (attempt %d)\n', String(url), String(e?.message || e), attempt);
|
|
292
|
+
}
|
|
293
|
+
os.sleep(1000);
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const bb = await fetchR3('https://www.baidu.com/');
|
|
298
|
+
if (bb) {
|
|
299
|
+
assert('baidu (GlobalSign R3) status', true);
|
|
300
|
+
assert('baidu (GlobalSign R3) body non-empty', bb.length > 0);
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
assert('baidu (GlobalSign R3) reachable', false);
|
|
304
|
+
}
|
|
305
|
+
const JSDELIVR_HOSTS = ['cdn.jsdelivr.net', 'fastly.jsdelivr.net', 'gcore.jsdelivr.net'];
|
|
306
|
+
for (const host of JSDELIVR_HOSTS) {
|
|
307
|
+
const bj = await fetchR3('https://' + host + '/npm/left-pad@1.3.0');
|
|
308
|
+
if (bj) {
|
|
309
|
+
assert('jsdelivr (GlobalSign R3) status', true);
|
|
310
|
+
assert('jsdelivr (GlobalSign R3) body non-empty', bj.length > 0);
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
if (host !== JSDELIVR_HOSTS[JSDELIVR_HOSTS.length - 1]) {
|
|
314
|
+
std.printf(' jsdelivr host %s failed, trying next\n', host);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
assert('jsdelivr (GlobalSign R3) reachable', false);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// ── large HTTPS body (wolfSSL_read int contract) ──
|
|
322
|
+
// read returns the raw int; -1/WANT_READ must never leak into
|
|
323
|
+
// byte accounting (regression: a leaked -1 made receivedBytes
|
|
324
|
+
// NaN → stream never closed → hang).
|
|
262
325
|
t.section('large HTTPS body');
|
|
263
326
|
{
|
|
264
327
|
const LARGE = 200000;
|
|
265
328
|
try {
|
|
266
329
|
const r = await Promise.race([
|
|
267
|
-
fetch(`https://localhost:18924/large/${LARGE}`, { timeout: 15000 }),
|
|
330
|
+
fetch(`https://localhost:18924/large/${LARGE}`, { timeout: 15000, rejectUnauthorized: false }),
|
|
268
331
|
new Promise((_, rej) => os.setTimeout(() => rej(new Error('Body hang watchdog 15s')), 15000))
|
|
269
332
|
]);
|
|
270
333
|
assert('large HTTPS status 200', r.status === 200);
|
|
@@ -304,7 +367,7 @@ export const suite = {
|
|
|
304
367
|
let rejected = false;
|
|
305
368
|
let msg = '';
|
|
306
369
|
try {
|
|
307
|
-
const r = await fetch('https://localhost:18924/stall', { timeout: 2000 });
|
|
370
|
+
const r = await fetch('https://localhost:18924/stall', { timeout: 2000, rejectUnauthorized: false });
|
|
308
371
|
await Promise.race([
|
|
309
372
|
r.arrayBuffer(),
|
|
310
373
|
new Promise((_, rej) => os.setTimeout(() => rej(new Error('Body timeout')), 8000))
|
package/test/test_net_fetch.ts
CHANGED
|
@@ -11,12 +11,22 @@ export const suite = {
|
|
|
11
11
|
else { t.fail++; std.printf(' FAIL: %s\n', name) }
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// Retry: public-site reachability (baidu/jsdelivr/esm.sh) depends on
|
|
15
|
+
// CI network conditions, and a single dropped connection must not be
|
|
16
|
+
// reported as a certificate regression. The last error is printed so a
|
|
17
|
+
// real failure is still diagnosable.
|
|
14
18
|
async function safeFetch(url: any, init?: RequestInit) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
let last = 'unknown'
|
|
20
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
21
|
+
try {
|
|
22
|
+
return await fetch(url, init)
|
|
23
|
+
} catch (e) {
|
|
24
|
+
last = String((e as Error)?.message || e)
|
|
25
|
+
if (attempt < 2) os.sleep(500)
|
|
26
|
+
}
|
|
19
27
|
}
|
|
28
|
+
std.printf(' fetch %s failed after 3 attempts: %s\n', String(url), last)
|
|
29
|
+
return null
|
|
20
30
|
}
|
|
21
31
|
const BASE = 'http://localhost:18923'
|
|
22
32
|
|
|
@@ -227,7 +237,7 @@ export const suite = {
|
|
|
227
237
|
|
|
228
238
|
// ── HTTPS fetch ──
|
|
229
239
|
t.section('HTTPS fetch')
|
|
230
|
-
const r11 = await safeFetch('https://localhost:18924/', { timeout: 5000 })
|
|
240
|
+
const r11 = await safeFetch('https://localhost:18924/', { timeout: 5000, rejectUnauthorized: false })
|
|
231
241
|
if (r11) {
|
|
232
242
|
assert('https status 200', r11.status === 200)
|
|
233
243
|
const body = await r11.text()
|
|
@@ -236,15 +246,68 @@ export const suite = {
|
|
|
236
246
|
assert('https endpoint reachable', false)
|
|
237
247
|
}
|
|
238
248
|
|
|
239
|
-
// ──
|
|
240
|
-
//
|
|
241
|
-
//
|
|
249
|
+
// ── GlobalSign R3 root chain (regression: ASN_SIG_CONFIRM_E -155) ──
|
|
250
|
+
// baidu.com and jsdelivr terminate in GlobalSign's 2018 re-keyed R3,
|
|
251
|
+
// which reuses the self-signed R3's subject-key-identifier with a
|
|
252
|
+
// different public key. Shipping the self-signed R3 in lib/certs.ts
|
|
253
|
+
// made wolfSSL match the wrong key and fail the handshake with -155.
|
|
254
|
+
// Both are public CDNs: a 500/429 or a dropped connection then counts
|
|
255
|
+
// as a retried attempt, not an immediate failure — only a persistent
|
|
256
|
+
// failure (cert regression or long-lived outage) should fail CI.
|
|
257
|
+
t.section('GlobalSign R3 root chain')
|
|
258
|
+
{
|
|
259
|
+
async function fetchR3(url: any): Promise<string | null> {
|
|
260
|
+
const init = { timeout: 10000 }
|
|
261
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
262
|
+
try {
|
|
263
|
+
const r = await fetch(url, init)
|
|
264
|
+
const body = await r.text()
|
|
265
|
+
if (r.status === 200 && body.length > 0) return body
|
|
266
|
+
std.printf(' fetch %s status=%s len=%d (attempt %d)\n',
|
|
267
|
+
String(url), String(r.status), body.length, attempt)
|
|
268
|
+
} catch (e) {
|
|
269
|
+
std.printf(' fetch %s error: %s (attempt %d)\n',
|
|
270
|
+
String(url), String((e as Error)?.message || e), attempt)
|
|
271
|
+
}
|
|
272
|
+
os.sleep(1000)
|
|
273
|
+
}
|
|
274
|
+
return null
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const bb = await fetchR3('https://www.baidu.com/')
|
|
278
|
+
if (bb) {
|
|
279
|
+
assert('baidu (GlobalSign R3) status', true)
|
|
280
|
+
assert('baidu (GlobalSign R3) body non-empty', bb.length > 0)
|
|
281
|
+
} else {
|
|
282
|
+
assert('baidu (GlobalSign R3) reachable', false)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const JSDELIVR_HOSTS = ['cdn.jsdelivr.net', 'fastly.jsdelivr.net', 'gcore.jsdelivr.net']
|
|
286
|
+
for (const host of JSDELIVR_HOSTS) {
|
|
287
|
+
const bj = await fetchR3('https://' + host + '/npm/left-pad@1.3.0')
|
|
288
|
+
if (bj) {
|
|
289
|
+
assert('jsdelivr (GlobalSign R3) status', true)
|
|
290
|
+
assert('jsdelivr (GlobalSign R3) body non-empty', bj.length > 0)
|
|
291
|
+
break
|
|
292
|
+
}
|
|
293
|
+
if (host !== JSDELIVR_HOSTS[JSDELIVR_HOSTS.length - 1]) {
|
|
294
|
+
std.printf(' jsdelivr host %s failed, trying next\n', host)
|
|
295
|
+
} else {
|
|
296
|
+
assert('jsdelivr (GlobalSign R3) reachable', false)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── large HTTPS body (wolfSSL_read int contract) ──
|
|
302
|
+
// read returns the raw int; -1/WANT_READ must never leak into
|
|
303
|
+
// byte accounting (regression: a leaked -1 made receivedBytes
|
|
304
|
+
// NaN → stream never closed → hang).
|
|
242
305
|
t.section('large HTTPS body')
|
|
243
306
|
{
|
|
244
307
|
const LARGE = 200000
|
|
245
308
|
try {
|
|
246
309
|
const r = await Promise.race([
|
|
247
|
-
fetch(`https://localhost:18924/large/${LARGE}`, { timeout: 15000 }),
|
|
310
|
+
fetch(`https://localhost:18924/large/${LARGE}`, { timeout: 15000, rejectUnauthorized: false }),
|
|
248
311
|
new Promise<never>((_, rej) =>
|
|
249
312
|
os.setTimeout(() => rej(new Error('Body hang watchdog 15s')), 15000)
|
|
250
313
|
)
|
|
@@ -292,7 +355,7 @@ export const suite = {
|
|
|
292
355
|
let rejected = false
|
|
293
356
|
let msg = ''
|
|
294
357
|
try {
|
|
295
|
-
const r = await fetch('https://localhost:18924/stall', { timeout: 2000 })
|
|
358
|
+
const r = await fetch('https://localhost:18924/stall', { timeout: 2000, rejectUnauthorized: false })
|
|
296
359
|
await Promise.race([
|
|
297
360
|
r.arrayBuffer(),
|
|
298
361
|
new Promise<never>((_, rej) =>
|
|
@@ -158,7 +158,7 @@ export const suite = {
|
|
|
158
158
|
t.section('wss basic text message');
|
|
159
159
|
const p7 = waitForTest();
|
|
160
160
|
let opened7 = false, received7 = false, closed7 = false;
|
|
161
|
-
const ws7 = new WebSocket("wss://localhost:18924");
|
|
161
|
+
const ws7 = new WebSocket("wss://localhost:18924", { rejectUnauthorized: false });
|
|
162
162
|
ws7.onopen = () => { opened7 = true; ws7.send("Hello WSS!"); };
|
|
163
163
|
ws7.onmessage = () => { received7 = true; ws7.close(1000, "done"); };
|
|
164
164
|
ws7.onclose = (e) => {
|
|
@@ -145,7 +145,7 @@ export const suite = {
|
|
|
145
145
|
t.section('wss basic text message')
|
|
146
146
|
const p7 = waitForTest()
|
|
147
147
|
let opened7 = false, received7 = false, closed7 = false
|
|
148
|
-
const ws7 = new WebSocket("wss://localhost:18924")
|
|
148
|
+
const ws7 = new WebSocket("wss://localhost:18924", { rejectUnauthorized: false })
|
|
149
149
|
ws7.onopen = () => { opened7 = true; ws7.send("Hello WSS!") }
|
|
150
150
|
ws7.onmessage = () => { received7 = true; ws7.close(1000, "done") }
|
|
151
151
|
ws7.onclose = (e) => {
|