goscript 0.2.1 → 0.2.3
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/compiler/gotest/runner.go +98 -0
- package/compiler/gotest/runner_test.go +45 -0
- package/compiler/gotest/testdata/browserapi/browserapi_test.go +36 -0
- package/compiler/lowering.go +227 -11
- package/compiler/override-registry_test.go +50 -0
- package/compiler/protobuf-ts-binding.go +155 -7
- package/compiler/protobuf-ts-binding_test.go +116 -2
- package/compiler/runtime-contract.go +2 -0
- package/compiler/runtime-contract_test.go +1 -0
- package/compiler/semantic-model.go +16 -0
- package/compiler/semantic-model_test.go +38 -0
- package/compiler/skeleton_test.go +477 -16
- package/compiler/typescript-emitter.go +4 -0
- package/dist/gs/builtin/builtin.js +7 -9
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/defer.js +2 -2
- package/dist/gs/builtin/hostio.js +5 -5
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/map.js +2 -1
- package/dist/gs/builtin/map.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +3 -0
- package/dist/gs/builtin/slice.js +39 -0
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.js +49 -0
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/compress/zlib/index.js +5 -2
- package/dist/gs/compress/zlib/index.js.map +1 -1
- package/dist/gs/crypto/aes/index.d.ts +15 -0
- package/dist/gs/crypto/aes/index.js +57 -0
- package/dist/gs/crypto/aes/index.js.map +1 -0
- package/dist/gs/crypto/cipher/index.d.ts +41 -0
- package/dist/gs/crypto/cipher/index.js +255 -0
- package/dist/gs/crypto/cipher/index.js.map +1 -0
- package/dist/gs/crypto/ecdh/index.js +27 -8
- package/dist/gs/crypto/ecdh/index.js.map +1 -1
- package/dist/gs/crypto/ed25519/index.js +3 -3
- package/dist/gs/crypto/ed25519/index.js.map +1 -1
- package/dist/gs/crypto/rand/index.js +6 -3
- package/dist/gs/crypto/rand/index.js.map +1 -1
- package/dist/gs/embed/index.js +9 -3
- package/dist/gs/embed/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +33 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/mr-tron/base58/base58/index.js +4 -1
- package/dist/gs/github.com/mr-tron/base58/base58/index.js.map +1 -1
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.d.ts +31 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js +117 -0
- package/dist/gs/golang.org/x/crypto/chacha20poly1305/index.js.map +1 -0
- package/dist/gs/golang.org/x/crypto/scrypt/index.d.ts +2 -0
- package/dist/gs/golang.org/x/crypto/scrypt/index.js +39 -0
- package/dist/gs/golang.org/x/crypto/scrypt/index.js.map +1 -0
- package/dist/gs/hash/fnv/index.js +13 -5
- package/dist/gs/hash/fnv/index.js.map +1 -1
- package/dist/gs/io/fs/glob.d.ts +3 -3
- package/dist/gs/io/fs/glob.js +8 -8
- package/dist/gs/io/fs/glob.js.map +1 -1
- package/dist/gs/io/fs/readdir.d.ts +2 -2
- package/dist/gs/io/fs/readdir.js +13 -74
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/sub.js +4 -4
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/fs/walk.js +1 -1
- package/dist/gs/io/fs/walk.js.map +1 -1
- package/dist/gs/io/io.js +18 -2
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/maps/iter.js.map +1 -1
- package/dist/gs/maps/maps.js.map +1 -1
- package/dist/gs/mime/index.js +5 -2
- package/dist/gs/mime/index.js.map +1 -1
- package/dist/gs/net/http/httptest/index.js +6 -3
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +16 -4
- package/dist/gs/net/http/index.js +236 -40
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/net/http/pprof/index.js.map +1 -1
- package/dist/gs/reflect/iter.js +1 -1
- package/dist/gs/reflect/iter.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +2 -0
- package/dist/gs/reflect/type.js +53 -21
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/runtime/debug/index.js +2 -1
- package/dist/gs/runtime/debug/index.js.map +1 -1
- package/dist/gs/runtime/pprof/index.js.map +1 -1
- package/dist/gs/runtime/runtime.js +2 -2
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/runtime/trace/index.js.map +1 -1
- package/dist/gs/slices/slices.d.ts +1 -1
- package/dist/gs/slices/slices.js +37 -4
- package/dist/gs/slices/slices.js.map +1 -1
- package/go.mod +2 -2
- package/go.sum +2 -0
- package/gs/builtin/builtin.ts +11 -14
- package/gs/builtin/defer.ts +2 -2
- package/gs/builtin/hostio.test.ts +8 -3
- package/gs/builtin/hostio.ts +5 -7
- package/gs/builtin/map.ts +4 -1
- package/gs/builtin/slice.test.ts +14 -0
- package/gs/builtin/slice.ts +64 -0
- package/gs/builtin/type.ts +72 -0
- package/gs/bytes/bytes.test.ts +14 -13
- package/gs/compress/zlib/index.test.ts +19 -5
- package/gs/compress/zlib/index.ts +16 -7
- package/gs/context/context.test.ts +3 -1
- package/gs/crypto/aes/index.test.ts +120 -0
- package/gs/crypto/aes/index.ts +76 -0
- package/gs/crypto/cipher/index.ts +345 -0
- package/gs/crypto/cipher/meta.json +6 -0
- package/gs/crypto/ecdh/index.test.ts +6 -2
- package/gs/crypto/ecdh/index.ts +49 -12
- package/gs/crypto/ed25519/index.ts +20 -7
- package/gs/crypto/rand/index.ts +6 -3
- package/gs/embed/index.test.ts +3 -3
- package/gs/embed/index.ts +9 -3
- package/gs/fmt/fmt.test.ts +29 -4
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +126 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +46 -0
- package/gs/github.com/mr-tron/base58/base58/index.ts +9 -3
- package/gs/github.com/zeebo/blake3/internal/consts/index.test.ts +2 -8
- package/gs/golang.org/x/crypto/chacha20poly1305/index.test.ts +91 -0
- package/gs/golang.org/x/crypto/chacha20poly1305/index.ts +245 -0
- package/gs/golang.org/x/crypto/scrypt/index.test.ts +81 -0
- package/gs/golang.org/x/crypto/scrypt/index.ts +54 -0
- package/gs/golang.org/x/crypto/scrypt/meta.json +5 -0
- package/gs/hash/fnv/index.test.ts +1 -8
- package/gs/hash/fnv/index.ts +27 -10
- package/gs/io/fs/glob.ts +13 -10
- package/gs/io/fs/meta.json +2 -0
- package/gs/io/fs/readdir.test.ts +63 -2
- package/gs/io/fs/readdir.ts +33 -30
- package/gs/io/fs/sub.ts +4 -4
- package/gs/io/fs/walk.ts +1 -1
- package/gs/io/io.test.ts +56 -1
- package/gs/io/io.ts +19 -2
- package/gs/maps/iter.ts +9 -9
- package/gs/maps/maps.ts +4 -4
- package/gs/math/bits/index.test.ts +10 -1
- package/gs/mime/index.test.ts +33 -15
- package/gs/mime/index.ts +9 -2
- package/gs/net/http/httptest/index.test.ts +17 -3
- package/gs/net/http/httptest/index.ts +8 -3
- package/gs/net/http/index.test.ts +645 -123
- package/gs/net/http/index.ts +548 -113
- package/gs/net/http/pprof/index.ts +24 -6
- package/gs/os/file_unix_js.test.ts +22 -0
- package/gs/reflect/iter.ts +4 -2
- package/gs/reflect/map.test.ts +56 -1
- package/gs/reflect/type.ts +76 -37
- package/gs/runtime/debug/index.test.ts +32 -4
- package/gs/runtime/debug/index.ts +5 -2
- package/gs/runtime/pprof/index.test.ts +7 -1
- package/gs/runtime/pprof/index.ts +5 -1
- package/gs/runtime/runtime.test.ts +7 -0
- package/gs/runtime/runtime.ts +2 -4
- package/gs/runtime/trace/index.test.ts +9 -1
- package/gs/runtime/trace/index.ts +5 -1
- package/gs/slices/meta.json +3 -0
- package/gs/slices/slices.test.ts +59 -21
- package/gs/slices/slices.ts +61 -20
- package/gs/strconv/complex.test.ts +17 -3
- package/gs/sync/atomic/doc_64.test.ts +2 -9
- package/gs/sync/sync.test.ts +18 -8
- package/gs/syscall/js/index.test.ts +9 -4
- package/package.json +13 -5
|
@@ -324,7 +324,10 @@ function parseRequestURL(rawURL) {
|
|
|
324
324
|
}
|
|
325
325
|
const parsed = new URL(rawURL, 'http://goscript.invalid');
|
|
326
326
|
const hasHost = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(rawURL);
|
|
327
|
-
return [
|
|
327
|
+
return [
|
|
328
|
+
new RequestURL(parsed.pathname, parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search, hasHost ? parsed.protocol.replace(/:$/, '') : '', hasHost ? parsed.host : ''),
|
|
329
|
+
null,
|
|
330
|
+
];
|
|
328
331
|
}
|
|
329
332
|
catch {
|
|
330
333
|
return [null, errors.New(`parse "${rawURL}": invalid URL`)];
|
|
@@ -342,6 +345,46 @@ class responseBody {
|
|
|
342
345
|
return null;
|
|
343
346
|
}
|
|
344
347
|
}
|
|
348
|
+
class fetchResponseBody {
|
|
349
|
+
fetched;
|
|
350
|
+
requestContext;
|
|
351
|
+
abortFetch;
|
|
352
|
+
stopContextWatch;
|
|
353
|
+
reader = null;
|
|
354
|
+
closed = false;
|
|
355
|
+
constructor(fetched, requestContext, abortFetch, stopContextWatch) {
|
|
356
|
+
this.fetched = fetched;
|
|
357
|
+
this.requestContext = requestContext;
|
|
358
|
+
this.abortFetch = abortFetch;
|
|
359
|
+
this.stopContextWatch = stopContextWatch;
|
|
360
|
+
}
|
|
361
|
+
async Read(p) {
|
|
362
|
+
if (this.closed) {
|
|
363
|
+
return [0, ErrBodyReadAfterClose];
|
|
364
|
+
}
|
|
365
|
+
if (this.reader == null) {
|
|
366
|
+
const [data, err] = await readFetchBody(this.fetched, this.requestContext, this.abortFetch);
|
|
367
|
+
if (err != null) {
|
|
368
|
+
this.stopContextWatch();
|
|
369
|
+
return [0, err];
|
|
370
|
+
}
|
|
371
|
+
this.stopContextWatch();
|
|
372
|
+
this.reader = bytes.NewReader(data);
|
|
373
|
+
}
|
|
374
|
+
return this.reader.Read(p);
|
|
375
|
+
}
|
|
376
|
+
Close() {
|
|
377
|
+
if (this.closed) {
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
this.closed = true;
|
|
381
|
+
this.stopContextWatch();
|
|
382
|
+
if (this.reader == null) {
|
|
383
|
+
this.abortFetch();
|
|
384
|
+
}
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
345
388
|
class noBody {
|
|
346
389
|
Read(_p) {
|
|
347
390
|
return [0, io.EOF];
|
|
@@ -386,7 +429,9 @@ export class Cookie {
|
|
|
386
429
|
this.Unparsed = init?.Unparsed ?? null;
|
|
387
430
|
}
|
|
388
431
|
String() {
|
|
389
|
-
const parts = [
|
|
432
|
+
const parts = [
|
|
433
|
+
`${this.Name}=${this.Quoted ? quoteCookieValue(this.Value) : this.Value}`,
|
|
434
|
+
];
|
|
390
435
|
if (this.Path !== '') {
|
|
391
436
|
parts.push(`Path=${this.Path}`);
|
|
392
437
|
}
|
|
@@ -429,12 +474,19 @@ function isToken(value) {
|
|
|
429
474
|
return /^[!#$%&'*+\-.^_`{}|~0-9A-Za-z]+$/.test(value);
|
|
430
475
|
}
|
|
431
476
|
function validCookieValueByte(code) {
|
|
432
|
-
return code >= 0x20 &&
|
|
477
|
+
return (code >= 0x20 &&
|
|
478
|
+
code < 0x7f &&
|
|
479
|
+
code !== 0x22 &&
|
|
480
|
+
code !== 0x3b &&
|
|
481
|
+
code !== 0x5c);
|
|
433
482
|
}
|
|
434
483
|
function parseCookieValue(raw, allowDoubleQuote) {
|
|
435
484
|
let value = raw;
|
|
436
485
|
let quoted = false;
|
|
437
|
-
if (allowDoubleQuote &&
|
|
486
|
+
if (allowDoubleQuote &&
|
|
487
|
+
value.length > 1 &&
|
|
488
|
+
value[0] === '"' &&
|
|
489
|
+
value[value.length - 1] === '"') {
|
|
438
490
|
value = value.slice(1, -1);
|
|
439
491
|
quoted = true;
|
|
440
492
|
}
|
|
@@ -507,7 +559,7 @@ function inProcessServerRequest(request) {
|
|
|
507
559
|
const rawQuery = request.URL?.RawQuery ?? '';
|
|
508
560
|
const query = rawQuery === '' ? '' : `?${rawQuery}`;
|
|
509
561
|
req.RequestURI = `${request.URL?.Path ?? '/'}${query}`;
|
|
510
|
-
req.Host = request.Host === '' ? request.URL?.Host ?? '' : request.Host;
|
|
562
|
+
req.Host = request.Host === '' ? (request.URL?.Host ?? '') : request.Host;
|
|
511
563
|
if (req.URL?.clone != null) {
|
|
512
564
|
req.URL = req.URL.clone();
|
|
513
565
|
req.URL.Scheme = '';
|
|
@@ -560,7 +612,9 @@ export class Request {
|
|
|
560
612
|
this.Cancel = init?.Cancel ?? null;
|
|
561
613
|
this.Response = init?.Response ?? null;
|
|
562
614
|
this.Pattern = init?.Pattern ?? '';
|
|
563
|
-
this.ctx =
|
|
615
|
+
this.ctx =
|
|
616
|
+
init?.ctx ??
|
|
617
|
+
context.Background();
|
|
564
618
|
}
|
|
565
619
|
Context() {
|
|
566
620
|
return this.ctx;
|
|
@@ -571,7 +625,9 @@ export class Request {
|
|
|
571
625
|
Clone(ctx) {
|
|
572
626
|
return new Request({
|
|
573
627
|
Method: this.Method,
|
|
574
|
-
URL: this.URL?.clone != null ? this.URL.clone()
|
|
628
|
+
URL: this.URL?.clone != null ? this.URL.clone()
|
|
629
|
+
: this.URL == null ? null
|
|
630
|
+
: { ...this.URL },
|
|
575
631
|
Proto: this.Proto,
|
|
576
632
|
ProtoMajor: this.ProtoMajor,
|
|
577
633
|
ProtoMinor: this.ProtoMinor,
|
|
@@ -601,7 +657,8 @@ export class Request {
|
|
|
601
657
|
return Header_Get(this.Header, 'Referer');
|
|
602
658
|
}
|
|
603
659
|
ProtoAtLeast(major, minor) {
|
|
604
|
-
return this.ProtoMajor > major ||
|
|
660
|
+
return (this.ProtoMajor > major ||
|
|
661
|
+
(this.ProtoMajor === major && this.ProtoMinor >= minor));
|
|
605
662
|
}
|
|
606
663
|
Cookie(name) {
|
|
607
664
|
for (const cookie of Array.from(this.Cookies() ?? [])) {
|
|
@@ -678,7 +735,8 @@ export class Response {
|
|
|
678
735
|
this.TLS = init?.TLS ?? null;
|
|
679
736
|
if (this.Status === '' && this.StatusCode !== 0) {
|
|
680
737
|
const text = StatusText(this.StatusCode);
|
|
681
|
-
this.Status =
|
|
738
|
+
this.Status =
|
|
739
|
+
text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`;
|
|
682
740
|
}
|
|
683
741
|
}
|
|
684
742
|
clone() {
|
|
@@ -726,7 +784,8 @@ export class Response {
|
|
|
726
784
|
}
|
|
727
785
|
}
|
|
728
786
|
ProtoAtLeast(major, minor) {
|
|
729
|
-
return this.ProtoMajor > major ||
|
|
787
|
+
return (this.ProtoMajor > major ||
|
|
788
|
+
(this.ProtoMajor === major && this.ProtoMinor >= minor));
|
|
730
789
|
}
|
|
731
790
|
Write(w) {
|
|
732
791
|
const write = (data) => {
|
|
@@ -820,10 +879,8 @@ function encodeFormData(data) {
|
|
|
820
879
|
if (data instanceof URLSearchParams) {
|
|
821
880
|
return data.toString();
|
|
822
881
|
}
|
|
823
|
-
const entries = data instanceof Map ?
|
|
824
|
-
|
|
825
|
-
: typeof data === 'object' ?
|
|
826
|
-
Object.entries(data)
|
|
882
|
+
const entries = data instanceof Map ? Array.from(data.entries())
|
|
883
|
+
: typeof data === 'object' ? Object.entries(data)
|
|
827
884
|
: [];
|
|
828
885
|
entries.sort(([a], [b]) => String(a).localeCompare(String(b)));
|
|
829
886
|
const params = new URLSearchParams();
|
|
@@ -1015,7 +1072,10 @@ async function fetchRoundTrip(request) {
|
|
|
1015
1072
|
};
|
|
1016
1073
|
if (typeof globalThis.fetch !== 'function') {
|
|
1017
1074
|
closeRequestBody();
|
|
1018
|
-
return [
|
|
1075
|
+
return [
|
|
1076
|
+
null,
|
|
1077
|
+
errors.New('net/http: Client.Do is not implemented in GoScript'),
|
|
1078
|
+
];
|
|
1019
1079
|
}
|
|
1020
1080
|
const ctxErr = request.Context()?.Err?.();
|
|
1021
1081
|
if (ctxErr != null) {
|
|
@@ -1029,7 +1089,9 @@ async function fetchRoundTrip(request) {
|
|
|
1029
1089
|
}
|
|
1030
1090
|
}
|
|
1031
1091
|
let body;
|
|
1032
|
-
if (requestBody != null &&
|
|
1092
|
+
if (requestBody != null &&
|
|
1093
|
+
request.Method !== MethodGet &&
|
|
1094
|
+
request.Method !== MethodHead) {
|
|
1033
1095
|
const [data, err] = await io.ReadAll(requestBody);
|
|
1034
1096
|
const closeErr = closeRequestBody();
|
|
1035
1097
|
if (err != null) {
|
|
@@ -1046,21 +1108,32 @@ async function fetchRoundTrip(request) {
|
|
|
1046
1108
|
return [null, closeErr];
|
|
1047
1109
|
}
|
|
1048
1110
|
}
|
|
1111
|
+
const fetchContext = newFetchContext(request.Context());
|
|
1049
1112
|
try {
|
|
1050
1113
|
const bodyInit = body == null ? undefined : Uint8Array.from(body).buffer;
|
|
1051
|
-
const fetched = await globalThis.fetch(request.URL?.String?.() ?? '', {
|
|
1114
|
+
const [fetched, fetchErr] = await fetchContext.wait(globalThis.fetch(request.URL?.String?.() ?? '', {
|
|
1052
1115
|
method: request.Method || MethodGet,
|
|
1053
1116
|
headers,
|
|
1054
1117
|
body: bodyInit,
|
|
1055
|
-
|
|
1056
|
-
|
|
1118
|
+
signal: fetchContext.signal,
|
|
1119
|
+
}));
|
|
1120
|
+
if (fetchErr != null || fetched == null) {
|
|
1121
|
+
fetchContext.stop();
|
|
1122
|
+
return [null, fetchErr];
|
|
1123
|
+
}
|
|
1057
1124
|
const respHeader = new Header();
|
|
1058
1125
|
fetched.headers.forEach((value, key) => Header_Add(respHeader, key, value));
|
|
1126
|
+
const responseBody = request.Method === MethodHead ?
|
|
1127
|
+
NoBody
|
|
1128
|
+
: new fetchResponseBody(fetched, request.Context(), fetchContext.abort, fetchContext.stop);
|
|
1129
|
+
if (request.Method === MethodHead) {
|
|
1130
|
+
fetchContext.stop();
|
|
1131
|
+
}
|
|
1059
1132
|
return [
|
|
1060
1133
|
new Response({
|
|
1061
1134
|
Status: `${fetched.status} ${fetched.statusText}`,
|
|
1062
1135
|
StatusCode: fetched.status,
|
|
1063
|
-
Body:
|
|
1136
|
+
Body: responseBody,
|
|
1064
1137
|
Header: respHeader,
|
|
1065
1138
|
ContentLength: Number(fetched.headers.get('content-length') ?? -1),
|
|
1066
1139
|
Request: request,
|
|
@@ -1069,12 +1142,79 @@ async function fetchRoundTrip(request) {
|
|
|
1069
1142
|
];
|
|
1070
1143
|
}
|
|
1071
1144
|
catch (err) {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
: String(err);
|
|
1075
|
-
return [null, errors.New(message)];
|
|
1145
|
+
fetchContext.stop();
|
|
1146
|
+
return [null, errorFromUnknown(err)];
|
|
1076
1147
|
}
|
|
1077
1148
|
}
|
|
1149
|
+
function newFetchContext(requestContext) {
|
|
1150
|
+
let stopped = false;
|
|
1151
|
+
const watchController = typeof AbortController === 'undefined' ? null : new AbortController();
|
|
1152
|
+
const controller = typeof AbortController === 'undefined' ? null : new AbortController();
|
|
1153
|
+
const abort = () => {
|
|
1154
|
+
if (controller != null && !controller.signal.aborted) {
|
|
1155
|
+
controller.abort(requestContext?.Err?.() ?? context.Canceled);
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
const donePromise = requestContext == null ? null : ((async () => {
|
|
1159
|
+
try {
|
|
1160
|
+
await requestContext.Done().selectReceive(0, watchController?.signal);
|
|
1161
|
+
}
|
|
1162
|
+
catch {
|
|
1163
|
+
// Closed channels and receive wakeups both mean the context is done.
|
|
1164
|
+
}
|
|
1165
|
+
const err = requestContext.Err() ?? context.Canceled;
|
|
1166
|
+
if (!stopped) {
|
|
1167
|
+
abort();
|
|
1168
|
+
}
|
|
1169
|
+
return err;
|
|
1170
|
+
})());
|
|
1171
|
+
return {
|
|
1172
|
+
signal: controller?.signal,
|
|
1173
|
+
abort,
|
|
1174
|
+
stop: () => {
|
|
1175
|
+
stopped = true;
|
|
1176
|
+
watchController?.abort();
|
|
1177
|
+
},
|
|
1178
|
+
wait: async (promise) => {
|
|
1179
|
+
const settle = promise.then((value) => ({ value }), (thrown) => ({ thrown }));
|
|
1180
|
+
if (donePromise == null) {
|
|
1181
|
+
const result = await settle;
|
|
1182
|
+
if ('thrown' in result) {
|
|
1183
|
+
return [null, errorFromUnknown(result.thrown)];
|
|
1184
|
+
}
|
|
1185
|
+
return [result.value, null];
|
|
1186
|
+
}
|
|
1187
|
+
const result = await Promise.race([settle, donePromise.then((err) => ({ err }))]);
|
|
1188
|
+
if ('err' in result) {
|
|
1189
|
+
abort();
|
|
1190
|
+
return [null, result.err];
|
|
1191
|
+
}
|
|
1192
|
+
if ('thrown' in result) {
|
|
1193
|
+
return [
|
|
1194
|
+
null,
|
|
1195
|
+
requestContext?.Err?.() ?? errorFromUnknown(result.thrown),
|
|
1196
|
+
];
|
|
1197
|
+
}
|
|
1198
|
+
return [result.value, null];
|
|
1199
|
+
},
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
function errorFromUnknown(err) {
|
|
1203
|
+
const message = typeof err === 'object' && err != null && 'message' in err ?
|
|
1204
|
+
String(err.message)
|
|
1205
|
+
: String(err);
|
|
1206
|
+
return errors.New(message);
|
|
1207
|
+
}
|
|
1208
|
+
async function readFetchBody(fetched, requestContext, abortFetch) {
|
|
1209
|
+
const fetchContext = newFetchContext(requestContext);
|
|
1210
|
+
const [buffer, err] = await fetchContext.wait(fetched.arrayBuffer());
|
|
1211
|
+
fetchContext.stop();
|
|
1212
|
+
if (err != null) {
|
|
1213
|
+
abortFetch();
|
|
1214
|
+
return [new Uint8Array(), err];
|
|
1215
|
+
}
|
|
1216
|
+
return [new Uint8Array(buffer ?? new ArrayBuffer(0)), null];
|
|
1217
|
+
}
|
|
1078
1218
|
export function FS(fsys) {
|
|
1079
1219
|
return {
|
|
1080
1220
|
Open(name) {
|
|
@@ -1094,7 +1234,9 @@ function httpFileFromFSFile(file) {
|
|
|
1094
1234
|
Read: (p) => file.Read(p instanceof Uint8Array ? p : Uint8Array.from(p ?? [])),
|
|
1095
1235
|
Close: () => file.Close(),
|
|
1096
1236
|
Stat: () => file.Stat(),
|
|
1097
|
-
Seek: seek == null ?
|
|
1237
|
+
Seek: seek == null ?
|
|
1238
|
+
() => [0, errors.New('net/http: file does not support seek')]
|
|
1239
|
+
: seek.bind(file),
|
|
1098
1240
|
Readdir: readdir == null ? () => [null, io.EOF] : readdir.bind(file),
|
|
1099
1241
|
};
|
|
1100
1242
|
}
|
|
@@ -1109,13 +1251,13 @@ export function FileServer(root) {
|
|
|
1109
1251
|
Error(w, 'method not allowed', StatusMethodNotAllowed);
|
|
1110
1252
|
return;
|
|
1111
1253
|
}
|
|
1112
|
-
const [file, err] = root?.Open(cleanFileServerPath(req.URL?.Path ?? '')) ?? [null, fs.ErrInvalid];
|
|
1254
|
+
const [file, err] = (await root?.Open(cleanFileServerPath(req.URL?.Path ?? ''))) ?? [null, fs.ErrInvalid];
|
|
1113
1255
|
if (err != null || file == null) {
|
|
1114
1256
|
NotFound(w, req);
|
|
1115
1257
|
return;
|
|
1116
1258
|
}
|
|
1117
1259
|
try {
|
|
1118
|
-
const [info, statErr] = file.Stat();
|
|
1260
|
+
const [info, statErr] = await file.Stat();
|
|
1119
1261
|
if (statErr != null) {
|
|
1120
1262
|
Error(w, statErr.Error(), StatusInternalServerError);
|
|
1121
1263
|
return;
|
|
@@ -1138,7 +1280,7 @@ export function FileServer(root) {
|
|
|
1138
1280
|
}
|
|
1139
1281
|
}
|
|
1140
1282
|
finally {
|
|
1141
|
-
file.Close();
|
|
1283
|
+
await file.Close();
|
|
1142
1284
|
}
|
|
1143
1285
|
},
|
|
1144
1286
|
};
|
|
@@ -1174,7 +1316,23 @@ function cleanFileServerPath(name) {
|
|
|
1174
1316
|
}
|
|
1175
1317
|
return parts.length === 0 ? '.' : parts.join('/');
|
|
1176
1318
|
}
|
|
1319
|
+
$.registerInterfaceType('http.Handler', null, [
|
|
1320
|
+
{
|
|
1321
|
+
name: 'ServeHTTP',
|
|
1322
|
+
args: [
|
|
1323
|
+
{ name: 'w', type: 'http.ResponseWriter' },
|
|
1324
|
+
{
|
|
1325
|
+
name: 'r',
|
|
1326
|
+
type: { kind: $.TypeKind.Pointer, elemType: 'http.Request' },
|
|
1327
|
+
},
|
|
1328
|
+
],
|
|
1329
|
+
returns: [],
|
|
1330
|
+
},
|
|
1331
|
+
]);
|
|
1177
1332
|
export function HandlerFunc_ServeHTTP(h, w, r) {
|
|
1333
|
+
if (!h) {
|
|
1334
|
+
throw new globalThis.Error('http: nil HandlerFunc');
|
|
1335
|
+
}
|
|
1178
1336
|
return h(w, r);
|
|
1179
1337
|
}
|
|
1180
1338
|
export class CrossOriginProtection {
|
|
@@ -1334,7 +1492,8 @@ export class Server {
|
|
|
1334
1492
|
this.BaseContext = init?.BaseContext ?? null;
|
|
1335
1493
|
this.ConnContext = init?.ConnContext ?? null;
|
|
1336
1494
|
this.Handler = init?.Handler ?? null;
|
|
1337
|
-
this.DisableGeneralOptionsHandler =
|
|
1495
|
+
this.DisableGeneralOptionsHandler =
|
|
1496
|
+
init?.DisableGeneralOptionsHandler ?? false;
|
|
1338
1497
|
this.TLSConfig = init?.TLSConfig ?? null;
|
|
1339
1498
|
this.ReadTimeout = init?.ReadTimeout ?? 0;
|
|
1340
1499
|
this.ReadTimeoutHandler = init?.ReadTimeoutHandler ?? null;
|
|
@@ -1492,7 +1651,9 @@ export function StripPrefix(prefix, handler) {
|
|
|
1492
1651
|
return {
|
|
1493
1652
|
ServeHTTP(w, r) {
|
|
1494
1653
|
const req = $.pointerValue(r);
|
|
1495
|
-
if (req?.URL != null &&
|
|
1654
|
+
if (req?.URL != null &&
|
|
1655
|
+
typeof req.URL.Path === 'string' &&
|
|
1656
|
+
req.URL.Path.startsWith(prefix)) {
|
|
1496
1657
|
req.URL = { ...req.URL, Path: req.URL.Path.slice(prefix.length) || '/' };
|
|
1497
1658
|
}
|
|
1498
1659
|
return handler?.ServeHTTP(w, req);
|
|
@@ -1554,7 +1715,10 @@ export function Redirect(w, _r, url, code) {
|
|
|
1554
1715
|
export function ParseTime(text) {
|
|
1555
1716
|
const date = new globalThis.Date(text);
|
|
1556
1717
|
if (isNaN(date.getTime())) {
|
|
1557
|
-
return [
|
|
1718
|
+
return [
|
|
1719
|
+
new time.Time(),
|
|
1720
|
+
$.newError(`parsing time "${text}" as HTTP-date: cannot parse`),
|
|
1721
|
+
];
|
|
1558
1722
|
}
|
|
1559
1723
|
return [time.UnixMilli(date.getTime()), null];
|
|
1560
1724
|
}
|
|
@@ -1599,7 +1763,8 @@ export function DetectContentType(data) {
|
|
|
1599
1763
|
if (startsWithBytes(bytes, new Uint8Array([0xef, 0xbb, 0xbf]))) {
|
|
1600
1764
|
return 'text/plain; charset=utf-8';
|
|
1601
1765
|
}
|
|
1602
|
-
if (startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) ||
|
|
1766
|
+
if (startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) ||
|
|
1767
|
+
startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x02, 0x00]))) {
|
|
1603
1768
|
return 'image/x-icon';
|
|
1604
1769
|
}
|
|
1605
1770
|
if (startsWithASCII(bytes, 'BM')) {
|
|
@@ -1675,7 +1840,10 @@ export function DetectContentType(data) {
|
|
|
1675
1840
|
return 'application/wasm';
|
|
1676
1841
|
}
|
|
1677
1842
|
for (const byte of afterWS) {
|
|
1678
|
-
if (byte <= 0x08 ||
|
|
1843
|
+
if (byte <= 0x08 ||
|
|
1844
|
+
byte === 0x0b ||
|
|
1845
|
+
(byte >= 0x0e && byte <= 0x1a) ||
|
|
1846
|
+
(byte >= 0x1c && byte <= 0x1f)) {
|
|
1679
1847
|
return 'application/octet-stream';
|
|
1680
1848
|
}
|
|
1681
1849
|
}
|
|
@@ -1684,7 +1852,11 @@ export function DetectContentType(data) {
|
|
|
1684
1852
|
function firstNonWhitespace(data) {
|
|
1685
1853
|
for (let i = 0; i < data.length; i++) {
|
|
1686
1854
|
const byte = data[i];
|
|
1687
|
-
if (byte !== 0x09 &&
|
|
1855
|
+
if (byte !== 0x09 &&
|
|
1856
|
+
byte !== 0x0a &&
|
|
1857
|
+
byte !== 0x0c &&
|
|
1858
|
+
byte !== 0x0d &&
|
|
1859
|
+
byte !== 0x20) {
|
|
1688
1860
|
return i;
|
|
1689
1861
|
}
|
|
1690
1862
|
}
|
|
@@ -1807,7 +1979,12 @@ export function ParseSetCookie(line) {
|
|
|
1807
1979
|
if (!ok) {
|
|
1808
1980
|
return [null, errInvalidCookieValue];
|
|
1809
1981
|
}
|
|
1810
|
-
const cookie = new Cookie({
|
|
1982
|
+
const cookie = new Cookie({
|
|
1983
|
+
Name: name,
|
|
1984
|
+
Value: value,
|
|
1985
|
+
Quoted: quoted,
|
|
1986
|
+
Raw: line,
|
|
1987
|
+
});
|
|
1811
1988
|
const unparsed = [];
|
|
1812
1989
|
for (const raw of parts.slice(1)) {
|
|
1813
1990
|
const part = raw.trim();
|
|
@@ -1863,7 +2040,8 @@ export function ParseSetCookie(line) {
|
|
|
1863
2040
|
break;
|
|
1864
2041
|
}
|
|
1865
2042
|
let secs = Number.parseInt(attrValue, 10);
|
|
1866
|
-
if ((secs !== 0 && attrValue[0] === '0') ||
|
|
2043
|
+
if ((secs !== 0 && attrValue[0] === '0') ||
|
|
2044
|
+
!Number.isSafeInteger(secs)) {
|
|
1867
2045
|
break;
|
|
1868
2046
|
}
|
|
1869
2047
|
if (secs <= 0) {
|
|
@@ -1901,7 +2079,10 @@ export function NewRequestWithContext(ctx, method, url, body) {
|
|
|
1901
2079
|
method = MethodGet;
|
|
1902
2080
|
}
|
|
1903
2081
|
if (!isToken(method)) {
|
|
1904
|
-
return [
|
|
2082
|
+
return [
|
|
2083
|
+
null,
|
|
2084
|
+
errors.New(`net/http: invalid method ${JSON.stringify(method)}`),
|
|
2085
|
+
];
|
|
1905
2086
|
}
|
|
1906
2087
|
if (ctx == null) {
|
|
1907
2088
|
return [null, errors.New('net/http: nil Context')];
|
|
@@ -1911,7 +2092,17 @@ export function NewRequestWithContext(ctx, method, url, body) {
|
|
|
1911
2092
|
return [null, err];
|
|
1912
2093
|
}
|
|
1913
2094
|
const bodyInfo = requestBodyInfo(body, 0);
|
|
1914
|
-
return [
|
|
2095
|
+
return [
|
|
2096
|
+
new Request({
|
|
2097
|
+
Method: method,
|
|
2098
|
+
URL: parsedURL,
|
|
2099
|
+
Body: bodyInfo.Body,
|
|
2100
|
+
ContentLength: bodyInfo.ContentLength,
|
|
2101
|
+
Host: parsedURL.Host,
|
|
2102
|
+
ctx,
|
|
2103
|
+
}),
|
|
2104
|
+
null,
|
|
2105
|
+
];
|
|
1915
2106
|
}
|
|
1916
2107
|
export async function Get(_url) {
|
|
1917
2108
|
const [req, err] = NewRequest(MethodGet, _url, null);
|
|
@@ -1984,9 +2175,14 @@ function requestBodyInfo(body, unknownLength) {
|
|
|
1984
2175
|
if (value === NoBody) {
|
|
1985
2176
|
return { Body: NoBody, ContentLength: 0 };
|
|
1986
2177
|
}
|
|
1987
|
-
if (value instanceof bytes.Buffer ||
|
|
2178
|
+
if (value instanceof bytes.Buffer ||
|
|
2179
|
+
value instanceof bytes.Reader ||
|
|
2180
|
+
value instanceof strings.Reader) {
|
|
1988
2181
|
const length = value.Len();
|
|
1989
|
-
return {
|
|
2182
|
+
return {
|
|
2183
|
+
Body: length === 0 ? NoBody : readCloserForBody(body),
|
|
2184
|
+
ContentLength: length,
|
|
2185
|
+
};
|
|
1990
2186
|
}
|
|
1991
2187
|
return { Body: readCloserForBody(body), ContentLength: unknownLength };
|
|
1992
2188
|
}
|