goscript 0.2.2 → 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/testdata/browserapi/browserapi_test.go +36 -0
- package/compiler/lowering.go +223 -9
- package/compiler/override-registry_test.go +50 -0
- package/compiler/protobuf-ts-binding.go +154 -6
- package/compiler/protobuf-ts-binding_test.go +7 -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 +473 -15
- 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/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/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/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/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/gs/builtin/builtin.ts +11 -14
- package/gs/builtin/defer.ts +2 -2
- package/gs/builtin/hostio.ts +5 -5
- 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/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/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/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/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 +5 -4
package/gs/net/http/index.ts
CHANGED
|
@@ -110,33 +110,55 @@ export class MaxBytesError {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
export const ErrNotSupported = new ProtocolError('feature not supported')
|
|
113
|
-
export const ErrUnexpectedTrailer = new ProtocolError(
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
export const ErrUnexpectedTrailer = new ProtocolError(
|
|
114
|
+
'trailer header without chunked transfer encoding',
|
|
115
|
+
)
|
|
116
|
+
export const ErrMissingBoundary = new ProtocolError(
|
|
117
|
+
'no multipart boundary param in Content-Type',
|
|
118
|
+
)
|
|
119
|
+
export const ErrNotMultipart = new ProtocolError(
|
|
120
|
+
"request Content-Type isn't multipart/form-data",
|
|
121
|
+
)
|
|
116
122
|
export const ErrHeaderTooLong = new ProtocolError('header too long')
|
|
117
123
|
export const ErrShortBody = new ProtocolError('entity body too short')
|
|
118
|
-
export const ErrMissingContentLength = new ProtocolError(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
export const
|
|
124
|
+
export const ErrMissingContentLength = new ProtocolError(
|
|
125
|
+
'missing ContentLength in HEAD response',
|
|
126
|
+
)
|
|
127
|
+
export const ErrBodyNotAllowed = errors.New(
|
|
128
|
+
'http: request method or response status code does not allow body',
|
|
129
|
+
)
|
|
130
|
+
export const ErrBodyReadAfterClose = errors.New(
|
|
131
|
+
'http: invalid Read on closed Body',
|
|
132
|
+
)
|
|
133
|
+
export const ErrContentLength = errors.New(
|
|
134
|
+
'http: wrote more than the declared Content-Length',
|
|
135
|
+
)
|
|
122
136
|
export const ErrHandlerTimeout = errors.New('http: Handler timeout')
|
|
123
137
|
export const ErrHijacked = errors.New('http: connection has been hijacked')
|
|
124
138
|
export const ErrLineTooLong = errors.New('header line too long')
|
|
125
139
|
export const ErrMissingFile = errors.New('http: no such file')
|
|
126
140
|
export const ErrNoCookie = errors.New('http: named cookie not present')
|
|
127
141
|
export const ErrNoLocation = errors.New('http: no Location header in response')
|
|
128
|
-
export const ErrSchemeMismatch = errors.New(
|
|
142
|
+
export const ErrSchemeMismatch = errors.New(
|
|
143
|
+
'http: server gave HTTP response to HTTPS client',
|
|
144
|
+
)
|
|
129
145
|
export const ErrServerClosed = errors.New('http: Server closed')
|
|
130
146
|
export const ErrAbortHandler = errors.New('net/http: abort Handler')
|
|
131
|
-
export const ErrSkipAltProtocol = errors.New(
|
|
147
|
+
export const ErrSkipAltProtocol = errors.New(
|
|
148
|
+
'net/http: skip alternate protocol',
|
|
149
|
+
)
|
|
132
150
|
export const ErrUseLastResponse = errors.New('net/http: use last response')
|
|
133
151
|
export const ErrWriteAfterFlush = errors.New('unused')
|
|
134
152
|
const errBlankCookie = errors.New('http: blank cookie')
|
|
135
153
|
const errEqualNotFoundInCookie = errors.New("http: '=' not found in cookie")
|
|
136
154
|
const errInvalidCookieName = errors.New('http: invalid cookie name')
|
|
137
155
|
const errInvalidCookieValue = errors.New('http: invalid cookie value')
|
|
138
|
-
const errCookieNumLimitExceeded = errors.New(
|
|
139
|
-
|
|
156
|
+
const errCookieNumLimitExceeded = errors.New(
|
|
157
|
+
'http: number of cookies exceeded limit',
|
|
158
|
+
)
|
|
159
|
+
const errCrossOriginRequest = errors.New(
|
|
160
|
+
'cross-origin request detected from Sec-Fetch-Site header',
|
|
161
|
+
)
|
|
140
162
|
const errCrossOriginRequestFromOldBrowser = errors.New(
|
|
141
163
|
'cross-origin request detected, and/or browser is out of date: Sec-Fetch-Site is missing, and Origin does not match Host',
|
|
142
164
|
)
|
|
@@ -228,7 +250,7 @@ export function StatusText(code: number): string {
|
|
|
228
250
|
export type Header = Map<string, $.Slice<string>>
|
|
229
251
|
|
|
230
252
|
export const Header = Map as {
|
|
231
|
-
new(entries?: Iterable<readonly [string, $.Slice<string>]> | null): Header
|
|
253
|
+
new (entries?: Iterable<readonly [string, $.Slice<string>]> | null): Header
|
|
232
254
|
}
|
|
233
255
|
|
|
234
256
|
export function CanonicalHeaderKey(s: string): string {
|
|
@@ -271,7 +293,11 @@ export function Header_Write(h: Header, w: io.Writer): $.GoError {
|
|
|
271
293
|
return Header_WriteSubset(h, w, null)
|
|
272
294
|
}
|
|
273
295
|
|
|
274
|
-
export function Header_WriteSubset(
|
|
296
|
+
export function Header_WriteSubset(
|
|
297
|
+
h: Header,
|
|
298
|
+
w: io.Writer,
|
|
299
|
+
exclude: Map<string, boolean> | null,
|
|
300
|
+
): $.GoError {
|
|
275
301
|
for (const [key, values] of h.entries()) {
|
|
276
302
|
if (exclude?.get(key) === true) {
|
|
277
303
|
continue
|
|
@@ -377,12 +403,15 @@ function parseRequestURL(rawURL: string): [RequestURL | null, $.GoError] {
|
|
|
377
403
|
}
|
|
378
404
|
const parsed = new URL(rawURL, 'http://goscript.invalid')
|
|
379
405
|
const hasHost = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(rawURL)
|
|
380
|
-
return [
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
406
|
+
return [
|
|
407
|
+
new RequestURL(
|
|
408
|
+
parsed.pathname,
|
|
409
|
+
parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search,
|
|
410
|
+
hasHost ? parsed.protocol.replace(/:$/, '') : '',
|
|
411
|
+
hasHost ? parsed.host : '',
|
|
412
|
+
),
|
|
413
|
+
null,
|
|
414
|
+
]
|
|
386
415
|
} catch {
|
|
387
416
|
return [null, errors.New(`parse "${rawURL}": invalid URL`)]
|
|
388
417
|
}
|
|
@@ -404,6 +433,50 @@ class responseBody implements io.ReadCloser {
|
|
|
404
433
|
}
|
|
405
434
|
}
|
|
406
435
|
|
|
436
|
+
class fetchResponseBody {
|
|
437
|
+
private reader: bytes.Reader | null = null
|
|
438
|
+
private closed = false
|
|
439
|
+
|
|
440
|
+
constructor(
|
|
441
|
+
private fetched: globalThis.Response,
|
|
442
|
+
private requestContext: context.Context,
|
|
443
|
+
private abortFetch: () => void,
|
|
444
|
+
private stopContextWatch: () => void,
|
|
445
|
+
) {}
|
|
446
|
+
|
|
447
|
+
public async Read(p: $.Bytes): Promise<[number, $.GoError]> {
|
|
448
|
+
if (this.closed) {
|
|
449
|
+
return [0, ErrBodyReadAfterClose]
|
|
450
|
+
}
|
|
451
|
+
if (this.reader == null) {
|
|
452
|
+
const [data, err] = await readFetchBody(
|
|
453
|
+
this.fetched,
|
|
454
|
+
this.requestContext,
|
|
455
|
+
this.abortFetch,
|
|
456
|
+
)
|
|
457
|
+
if (err != null) {
|
|
458
|
+
this.stopContextWatch()
|
|
459
|
+
return [0, err]
|
|
460
|
+
}
|
|
461
|
+
this.stopContextWatch()
|
|
462
|
+
this.reader = bytes.NewReader(data)
|
|
463
|
+
}
|
|
464
|
+
return this.reader.Read(p)
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
public Close(): $.GoError {
|
|
468
|
+
if (this.closed) {
|
|
469
|
+
return null
|
|
470
|
+
}
|
|
471
|
+
this.closed = true
|
|
472
|
+
this.stopContextWatch()
|
|
473
|
+
if (this.reader == null) {
|
|
474
|
+
this.abortFetch()
|
|
475
|
+
}
|
|
476
|
+
return null
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
407
480
|
class noBody implements io.ReadCloser {
|
|
408
481
|
public Read(_p: $.Bytes): [number, $.GoError] {
|
|
409
482
|
return [0, io.EOF]
|
|
@@ -454,7 +527,9 @@ export class Cookie {
|
|
|
454
527
|
}
|
|
455
528
|
|
|
456
529
|
public String(): string {
|
|
457
|
-
const parts = [
|
|
530
|
+
const parts = [
|
|
531
|
+
`${this.Name}=${this.Quoted ? quoteCookieValue(this.Value) : this.Value}`,
|
|
532
|
+
]
|
|
458
533
|
if (this.Path !== '') {
|
|
459
534
|
parts.push(`Path=${this.Path}`)
|
|
460
535
|
}
|
|
@@ -499,13 +574,27 @@ function isToken(value: string): boolean {
|
|
|
499
574
|
}
|
|
500
575
|
|
|
501
576
|
function validCookieValueByte(code: number): boolean {
|
|
502
|
-
return
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
577
|
+
return (
|
|
578
|
+
code >= 0x20 &&
|
|
579
|
+
code < 0x7f &&
|
|
580
|
+
code !== 0x22 &&
|
|
581
|
+
code !== 0x3b &&
|
|
582
|
+
code !== 0x5c
|
|
583
|
+
)
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function parseCookieValue(
|
|
587
|
+
raw: string,
|
|
588
|
+
allowDoubleQuote: boolean,
|
|
589
|
+
): [string, boolean, boolean] {
|
|
506
590
|
let value = raw
|
|
507
591
|
let quoted = false
|
|
508
|
-
if (
|
|
592
|
+
if (
|
|
593
|
+
allowDoubleQuote &&
|
|
594
|
+
value.length > 1 &&
|
|
595
|
+
value[0] === '"' &&
|
|
596
|
+
value[value.length - 1] === '"'
|
|
597
|
+
) {
|
|
509
598
|
value = value.slice(1, -1)
|
|
510
599
|
quoted = true
|
|
511
600
|
}
|
|
@@ -526,7 +615,10 @@ function asciiLower(value: string): [string, boolean] {
|
|
|
526
615
|
return [value.toLowerCase(), true]
|
|
527
616
|
}
|
|
528
617
|
|
|
529
|
-
export function SetCookie(
|
|
618
|
+
export function SetCookie(
|
|
619
|
+
w: ResponseWriter | null,
|
|
620
|
+
cookie: Cookie | $.VarRef<Cookie> | null,
|
|
621
|
+
): void {
|
|
530
622
|
const c = $.pointerValue<Cookie | null>(cookie)
|
|
531
623
|
if (w == null || c == null) {
|
|
532
624
|
return
|
|
@@ -592,7 +684,7 @@ function inProcessServerRequest(request: Request): Request {
|
|
|
592
684
|
const rawQuery = request.URL?.RawQuery ?? ''
|
|
593
685
|
const query = rawQuery === '' ? '' : `?${rawQuery}`
|
|
594
686
|
req.RequestURI = `${request.URL?.Path ?? '/'}${query}`
|
|
595
|
-
req.Host = request.Host === '' ? request.URL?.Host ?? '' : request.Host
|
|
687
|
+
req.Host = request.Host === '' ? (request.URL?.Host ?? '') : request.Host
|
|
596
688
|
if (req.URL?.clone != null) {
|
|
597
689
|
req.URL = req.URL.clone()
|
|
598
690
|
req.URL.Scheme = ''
|
|
@@ -653,7 +745,9 @@ export class Request {
|
|
|
653
745
|
this.Cancel = init?.Cancel ?? null
|
|
654
746
|
this.Response = init?.Response ?? null
|
|
655
747
|
this.Pattern = init?.Pattern ?? ''
|
|
656
|
-
this.ctx =
|
|
748
|
+
this.ctx =
|
|
749
|
+
(init as { ctx?: context.Context } | undefined)?.ctx ??
|
|
750
|
+
context.Background()
|
|
657
751
|
}
|
|
658
752
|
|
|
659
753
|
public Context(): context.Context {
|
|
@@ -667,7 +761,10 @@ export class Request {
|
|
|
667
761
|
public Clone(ctx: context.Context): Request {
|
|
668
762
|
return new Request({
|
|
669
763
|
Method: this.Method,
|
|
670
|
-
URL:
|
|
764
|
+
URL:
|
|
765
|
+
this.URL?.clone != null ? this.URL.clone()
|
|
766
|
+
: this.URL == null ? null
|
|
767
|
+
: { ...this.URL },
|
|
671
768
|
Proto: this.Proto,
|
|
672
769
|
ProtoMajor: this.ProtoMajor,
|
|
673
770
|
ProtoMinor: this.ProtoMinor,
|
|
@@ -700,7 +797,10 @@ export class Request {
|
|
|
700
797
|
}
|
|
701
798
|
|
|
702
799
|
public ProtoAtLeast(major: number, minor: number): boolean {
|
|
703
|
-
return
|
|
800
|
+
return (
|
|
801
|
+
this.ProtoMajor > major ||
|
|
802
|
+
(this.ProtoMajor === major && this.ProtoMinor >= minor)
|
|
803
|
+
)
|
|
704
804
|
}
|
|
705
805
|
|
|
706
806
|
public Cookie(name: string): [Cookie | null, $.GoError] {
|
|
@@ -785,7 +885,8 @@ export class Response {
|
|
|
785
885
|
this.TLS = init?.TLS ?? null
|
|
786
886
|
if (this.Status === '' && this.StatusCode !== 0) {
|
|
787
887
|
const text = StatusText(this.StatusCode)
|
|
788
|
-
this.Status =
|
|
888
|
+
this.Status =
|
|
889
|
+
text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`
|
|
789
890
|
}
|
|
790
891
|
}
|
|
791
892
|
|
|
@@ -836,7 +937,10 @@ export class Response {
|
|
|
836
937
|
}
|
|
837
938
|
|
|
838
939
|
public ProtoAtLeast(major: number, minor: number): boolean {
|
|
839
|
-
return
|
|
940
|
+
return (
|
|
941
|
+
this.ProtoMajor > major ||
|
|
942
|
+
(this.ProtoMajor === major && this.ProtoMinor >= minor)
|
|
943
|
+
)
|
|
840
944
|
}
|
|
841
945
|
|
|
842
946
|
public Write(w: io.Writer): $.GoError {
|
|
@@ -910,7 +1014,11 @@ export class Client {
|
|
|
910
1014
|
return await this.Do(req)
|
|
911
1015
|
}
|
|
912
1016
|
|
|
913
|
-
public async Post(
|
|
1017
|
+
public async Post(
|
|
1018
|
+
url: string,
|
|
1019
|
+
contentType: string,
|
|
1020
|
+
body: io.Reader | null,
|
|
1021
|
+
): Promise<[Response | null, $.GoError]> {
|
|
914
1022
|
const [req, err] = NewRequest(MethodPost, url, body)
|
|
915
1023
|
if (err != null || req == null) {
|
|
916
1024
|
return [null, err]
|
|
@@ -919,12 +1027,21 @@ export class Client {
|
|
|
919
1027
|
return await this.Do(req)
|
|
920
1028
|
}
|
|
921
1029
|
|
|
922
|
-
public async PostForm(
|
|
923
|
-
|
|
1030
|
+
public async PostForm(
|
|
1031
|
+
url: string,
|
|
1032
|
+
data: any,
|
|
1033
|
+
): Promise<[Response | null, $.GoError]> {
|
|
1034
|
+
return await this.Post(
|
|
1035
|
+
url,
|
|
1036
|
+
'application/x-www-form-urlencoded',
|
|
1037
|
+
bytes.NewReader($.stringToBytes(encodeFormData(data))),
|
|
1038
|
+
)
|
|
924
1039
|
}
|
|
925
1040
|
|
|
926
1041
|
public CloseIdleConnections(): void {
|
|
927
|
-
const closer = this.Transport as {
|
|
1042
|
+
const closer = this.Transport as {
|
|
1043
|
+
CloseIdleConnections?: () => void
|
|
1044
|
+
} | null
|
|
928
1045
|
closer?.CloseIdleConnections?.()
|
|
929
1046
|
}
|
|
930
1047
|
}
|
|
@@ -944,10 +1061,8 @@ function encodeFormData(data: any): string {
|
|
|
944
1061
|
return data.toString()
|
|
945
1062
|
}
|
|
946
1063
|
const entries =
|
|
947
|
-
data instanceof Map ?
|
|
948
|
-
|
|
949
|
-
: typeof data === 'object' ?
|
|
950
|
-
Object.entries(data)
|
|
1064
|
+
data instanceof Map ? Array.from(data.entries())
|
|
1065
|
+
: typeof data === 'object' ? Object.entries(data)
|
|
951
1066
|
: []
|
|
952
1067
|
entries.sort(([a], [b]) => String(a).localeCompare(String(b)))
|
|
953
1068
|
const params = new URLSearchParams()
|
|
@@ -957,7 +1072,11 @@ function encodeFormData(data: any): string {
|
|
|
957
1072
|
return params.toString()
|
|
958
1073
|
}
|
|
959
1074
|
|
|
960
|
-
function appendFormValue(
|
|
1075
|
+
function appendFormValue(
|
|
1076
|
+
params: URLSearchParams,
|
|
1077
|
+
key: string,
|
|
1078
|
+
value: unknown,
|
|
1079
|
+
): void {
|
|
961
1080
|
const unwrapped = unwrapFormValue(value)
|
|
962
1081
|
if (unwrapped == null) {
|
|
963
1082
|
return
|
|
@@ -982,7 +1101,9 @@ function unwrapFormValue(value: unknown): unknown {
|
|
|
982
1101
|
}
|
|
983
1102
|
|
|
984
1103
|
export interface RoundTripper {
|
|
985
|
-
RoundTrip(
|
|
1104
|
+
RoundTrip(
|
|
1105
|
+
req: Request | $.VarRef<Request> | null,
|
|
1106
|
+
): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
|
|
986
1107
|
}
|
|
987
1108
|
|
|
988
1109
|
export class Protocols {
|
|
@@ -1051,8 +1172,17 @@ export class HTTP2Config {
|
|
|
1051
1172
|
}
|
|
1052
1173
|
|
|
1053
1174
|
export class Transport implements RoundTripper {
|
|
1054
|
-
public Proxy:
|
|
1055
|
-
|
|
1175
|
+
public Proxy:
|
|
1176
|
+
| ((req: Request | $.VarRef<Request> | null) => [any, $.GoError])
|
|
1177
|
+
| null = null
|
|
1178
|
+
public OnProxyConnectResponse:
|
|
1179
|
+
| ((
|
|
1180
|
+
ctx: context.Context,
|
|
1181
|
+
proxyURL: any,
|
|
1182
|
+
connectReq: Request,
|
|
1183
|
+
connectRes: Response,
|
|
1184
|
+
) => $.GoError)
|
|
1185
|
+
| null = null
|
|
1056
1186
|
public DialContext: any = null
|
|
1057
1187
|
public Dial: any = null
|
|
1058
1188
|
public DialTLSContext: any = null
|
|
@@ -1069,7 +1199,13 @@ export class Transport implements RoundTripper {
|
|
|
1069
1199
|
public ExpectContinueTimeout = 0
|
|
1070
1200
|
public TLSNextProto: Map<string, any> | null = null
|
|
1071
1201
|
public ProxyConnectHeader = new Header()
|
|
1072
|
-
public GetProxyConnectHeader:
|
|
1202
|
+
public GetProxyConnectHeader:
|
|
1203
|
+
| ((
|
|
1204
|
+
ctx: context.Context,
|
|
1205
|
+
proxyURL: any,
|
|
1206
|
+
target: string,
|
|
1207
|
+
) => [Header | null, $.GoError])
|
|
1208
|
+
| null = null
|
|
1073
1209
|
public MaxResponseHeaderBytes = 0
|
|
1074
1210
|
public WriteBufferSize = 0
|
|
1075
1211
|
public ReadBufferSize = 0
|
|
@@ -1081,7 +1217,9 @@ export class Transport implements RoundTripper {
|
|
|
1081
1217
|
Object.assign(this, init)
|
|
1082
1218
|
}
|
|
1083
1219
|
|
|
1084
|
-
public async RoundTrip(
|
|
1220
|
+
public async RoundTrip(
|
|
1221
|
+
req: Request | $.VarRef<Request> | null,
|
|
1222
|
+
): Promise<[Response | null, $.GoError]> {
|
|
1085
1223
|
const request = $.pointerValue<Request | null>(req)
|
|
1086
1224
|
if (request == null) {
|
|
1087
1225
|
return [null, errors.New('net/http: nil Request')]
|
|
@@ -1094,7 +1232,10 @@ export class Transport implements RoundTripper {
|
|
|
1094
1232
|
const recorder = new memoryResponseWriter()
|
|
1095
1233
|
let closeErr: $.GoError | undefined
|
|
1096
1234
|
try {
|
|
1097
|
-
const served = handler.ServeHTTP(
|
|
1235
|
+
const served = handler.ServeHTTP(
|
|
1236
|
+
recorder,
|
|
1237
|
+
inProcessServerRequest(request),
|
|
1238
|
+
)
|
|
1098
1239
|
if (served instanceof Promise) {
|
|
1099
1240
|
await served
|
|
1100
1241
|
}
|
|
@@ -1117,7 +1258,11 @@ export class Transport implements RoundTripper {
|
|
|
1117
1258
|
|
|
1118
1259
|
public RegisterProtocol(_scheme: string, _rt: RoundTripper): void {}
|
|
1119
1260
|
|
|
1120
|
-
public NewClientConn(
|
|
1261
|
+
public NewClientConn(
|
|
1262
|
+
_ctx: context.Context,
|
|
1263
|
+
_scheme: string,
|
|
1264
|
+
_address: string,
|
|
1265
|
+
): [ClientConn | null, $.GoError] {
|
|
1121
1266
|
return [null, ErrNotSupported]
|
|
1122
1267
|
}
|
|
1123
1268
|
|
|
@@ -1131,7 +1276,9 @@ export const DefaultTransport: RoundTripper = new Transport()
|
|
|
1131
1276
|
class fileTransport implements RoundTripper {
|
|
1132
1277
|
constructor(private root: FileSystem | null) {}
|
|
1133
1278
|
|
|
1134
|
-
public async RoundTrip(
|
|
1279
|
+
public async RoundTrip(
|
|
1280
|
+
req: Request | $.VarRef<Request> | null,
|
|
1281
|
+
): Promise<[Response | null, $.GoError]> {
|
|
1135
1282
|
const request = $.pointerValue<Request | null>(req)
|
|
1136
1283
|
const recorder = new memoryResponseWriter()
|
|
1137
1284
|
let closeErr: $.GoError | undefined
|
|
@@ -1155,7 +1302,9 @@ export function NewFileTransportFS(fsys: fs.FS): RoundTripper {
|
|
|
1155
1302
|
return NewFileTransport(FS(fsys))
|
|
1156
1303
|
}
|
|
1157
1304
|
|
|
1158
|
-
async function fetchRoundTrip(
|
|
1305
|
+
async function fetchRoundTrip(
|
|
1306
|
+
request: Request,
|
|
1307
|
+
): Promise<[Response | null, $.GoError]> {
|
|
1159
1308
|
const requestBody = request.Body
|
|
1160
1309
|
const closeRequestBody = (): $.GoError => {
|
|
1161
1310
|
if (requestBody == null) {
|
|
@@ -1165,7 +1314,10 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
|
|
|
1165
1314
|
}
|
|
1166
1315
|
if (typeof globalThis.fetch !== 'function') {
|
|
1167
1316
|
closeRequestBody()
|
|
1168
|
-
return [
|
|
1317
|
+
return [
|
|
1318
|
+
null,
|
|
1319
|
+
errors.New('net/http: Client.Do is not implemented in GoScript'),
|
|
1320
|
+
]
|
|
1169
1321
|
}
|
|
1170
1322
|
const ctxErr = request.Context()?.Err?.()
|
|
1171
1323
|
if (ctxErr != null) {
|
|
@@ -1179,7 +1331,11 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
|
|
|
1179
1331
|
}
|
|
1180
1332
|
}
|
|
1181
1333
|
let body: Uint8Array | undefined
|
|
1182
|
-
if (
|
|
1334
|
+
if (
|
|
1335
|
+
requestBody != null &&
|
|
1336
|
+
request.Method !== MethodGet &&
|
|
1337
|
+
request.Method !== MethodHead
|
|
1338
|
+
) {
|
|
1183
1339
|
const [data, err] = await io.ReadAll(requestBody)
|
|
1184
1340
|
const closeErr = closeRequestBody()
|
|
1185
1341
|
if (err != null) {
|
|
@@ -1195,21 +1351,40 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
|
|
|
1195
1351
|
return [null, closeErr]
|
|
1196
1352
|
}
|
|
1197
1353
|
}
|
|
1354
|
+
const fetchContext = newFetchContext(request.Context())
|
|
1198
1355
|
try {
|
|
1199
1356
|
const bodyInit = body == null ? undefined : Uint8Array.from(body).buffer
|
|
1200
|
-
const fetched = await
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1357
|
+
const [fetched, fetchErr] = await fetchContext.wait(
|
|
1358
|
+
globalThis.fetch(request.URL?.String?.() ?? '', {
|
|
1359
|
+
method: request.Method || MethodGet,
|
|
1360
|
+
headers,
|
|
1361
|
+
body: bodyInit,
|
|
1362
|
+
signal: fetchContext.signal,
|
|
1363
|
+
}),
|
|
1364
|
+
)
|
|
1365
|
+
if (fetchErr != null || fetched == null) {
|
|
1366
|
+
fetchContext.stop()
|
|
1367
|
+
return [null, fetchErr]
|
|
1368
|
+
}
|
|
1206
1369
|
const respHeader = new Header()
|
|
1207
1370
|
fetched.headers.forEach((value, key) => Header_Add(respHeader, key, value))
|
|
1371
|
+
const responseBody: io.ReadCloser =
|
|
1372
|
+
request.Method === MethodHead ?
|
|
1373
|
+
NoBody
|
|
1374
|
+
: (new fetchResponseBody(
|
|
1375
|
+
fetched,
|
|
1376
|
+
request.Context(),
|
|
1377
|
+
fetchContext.abort,
|
|
1378
|
+
fetchContext.stop,
|
|
1379
|
+
) as unknown as io.ReadCloser)
|
|
1380
|
+
if (request.Method === MethodHead) {
|
|
1381
|
+
fetchContext.stop()
|
|
1382
|
+
}
|
|
1208
1383
|
return [
|
|
1209
1384
|
new Response({
|
|
1210
1385
|
Status: `${fetched.status} ${fetched.statusText}`,
|
|
1211
1386
|
StatusCode: fetched.status,
|
|
1212
|
-
Body:
|
|
1387
|
+
Body: responseBody,
|
|
1213
1388
|
Header: respHeader,
|
|
1214
1389
|
ContentLength: Number(fetched.headers.get('content-length') ?? -1),
|
|
1215
1390
|
Request: request,
|
|
@@ -1217,20 +1392,123 @@ async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoE
|
|
|
1217
1392
|
null,
|
|
1218
1393
|
]
|
|
1219
1394
|
} catch (err) {
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1395
|
+
fetchContext.stop()
|
|
1396
|
+
return [null, errorFromUnknown(err)]
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
function newFetchContext(requestContext: context.Context): {
|
|
1401
|
+
signal: AbortSignal | undefined
|
|
1402
|
+
abort: () => void
|
|
1403
|
+
stop: () => void
|
|
1404
|
+
wait: <T>(promise: Promise<T>) => Promise<[T | null, $.GoError]>
|
|
1405
|
+
} {
|
|
1406
|
+
let stopped = false
|
|
1407
|
+
const watchController =
|
|
1408
|
+
typeof AbortController === 'undefined' ? null : new AbortController()
|
|
1409
|
+
const controller =
|
|
1410
|
+
typeof AbortController === 'undefined' ? null : new AbortController()
|
|
1411
|
+
const abort = () => {
|
|
1412
|
+
if (controller != null && !controller.signal.aborted) {
|
|
1413
|
+
controller.abort(requestContext?.Err?.() ?? context.Canceled)
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
const donePromise =
|
|
1417
|
+
requestContext == null ? null : (
|
|
1418
|
+
(async (): Promise<$.GoError> => {
|
|
1419
|
+
try {
|
|
1420
|
+
await requestContext.Done().selectReceive(0, watchController?.signal)
|
|
1421
|
+
} catch {
|
|
1422
|
+
// Closed channels and receive wakeups both mean the context is done.
|
|
1423
|
+
}
|
|
1424
|
+
const err = requestContext.Err() ?? context.Canceled
|
|
1425
|
+
if (!stopped) {
|
|
1426
|
+
abort()
|
|
1427
|
+
}
|
|
1428
|
+
return err
|
|
1429
|
+
})()
|
|
1430
|
+
)
|
|
1431
|
+
return {
|
|
1432
|
+
signal: controller?.signal,
|
|
1433
|
+
abort,
|
|
1434
|
+
stop: () => {
|
|
1435
|
+
stopped = true
|
|
1436
|
+
watchController?.abort()
|
|
1437
|
+
},
|
|
1438
|
+
wait: async <T>(promise: Promise<T>): Promise<[T | null, $.GoError]> => {
|
|
1439
|
+
const settle = promise.then(
|
|
1440
|
+
(value) => ({ value }),
|
|
1441
|
+
(thrown) => ({ thrown }),
|
|
1442
|
+
)
|
|
1443
|
+
if (donePromise == null) {
|
|
1444
|
+
const result = await settle
|
|
1445
|
+
if ('thrown' in result) {
|
|
1446
|
+
return [null, errorFromUnknown(result.thrown)]
|
|
1447
|
+
}
|
|
1448
|
+
return [result.value, null]
|
|
1449
|
+
}
|
|
1450
|
+
const result = await Promise.race<
|
|
1451
|
+
{ value: T } | { err: $.GoError } | { thrown: unknown }
|
|
1452
|
+
>([settle, donePromise.then((err) => ({ err }))])
|
|
1453
|
+
if ('err' in result) {
|
|
1454
|
+
abort()
|
|
1455
|
+
return [null, result.err]
|
|
1456
|
+
}
|
|
1457
|
+
if ('thrown' in result) {
|
|
1458
|
+
return [
|
|
1459
|
+
null,
|
|
1460
|
+
requestContext?.Err?.() ?? errorFromUnknown(result.thrown),
|
|
1461
|
+
]
|
|
1462
|
+
}
|
|
1463
|
+
return [result.value, null]
|
|
1464
|
+
},
|
|
1224
1465
|
}
|
|
1225
1466
|
}
|
|
1226
1467
|
|
|
1468
|
+
function errorFromUnknown(err: unknown): $.GoError {
|
|
1469
|
+
const message =
|
|
1470
|
+
typeof err === 'object' && err != null && 'message' in err ?
|
|
1471
|
+
String((err as { message: unknown }).message)
|
|
1472
|
+
: String(err)
|
|
1473
|
+
return errors.New(message)
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
async function readFetchBody(
|
|
1477
|
+
fetched: globalThis.Response,
|
|
1478
|
+
requestContext: context.Context,
|
|
1479
|
+
abortFetch: () => void,
|
|
1480
|
+
): Promise<[Uint8Array, $.GoError]> {
|
|
1481
|
+
const fetchContext = newFetchContext(requestContext)
|
|
1482
|
+
const [buffer, err] = await fetchContext.wait(fetched.arrayBuffer())
|
|
1483
|
+
fetchContext.stop()
|
|
1484
|
+
if (err != null) {
|
|
1485
|
+
abortFetch()
|
|
1486
|
+
return [new Uint8Array(), err]
|
|
1487
|
+
}
|
|
1488
|
+
return [new Uint8Array(buffer ?? new ArrayBuffer(0)), null]
|
|
1489
|
+
}
|
|
1490
|
+
|
|
1491
|
+
type maybePromise<T> = T | Promise<T>
|
|
1492
|
+
|
|
1227
1493
|
export interface FileSystem {
|
|
1228
1494
|
Open(name: string): [File | null, $.GoError]
|
|
1229
1495
|
}
|
|
1230
1496
|
|
|
1231
1497
|
export interface File extends io.Closer, io.Reader, io.Seeker {
|
|
1232
|
-
Readdir(count: number): [$.Slice<fs.FileInfo
|
|
1233
|
-
Stat(): [fs.FileInfo, $.GoError]
|
|
1498
|
+
Readdir(count: number): [$.Slice<fs.FileInfo> | null, $.GoError]
|
|
1499
|
+
Stat(): [fs.FileInfo | null, $.GoError]
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
interface fileServerFileSystem {
|
|
1503
|
+
Open(name: string): maybePromise<[fileServerFile | null, $.GoError]>
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
interface fileServerFile {
|
|
1507
|
+
Close(): maybePromise<$.GoError>
|
|
1508
|
+
Read(p: $.Bytes): maybePromise<[number, $.GoError]>
|
|
1509
|
+
Seek(offset: number, whence: number): maybePromise<[number, $.GoError]>
|
|
1510
|
+
Readdir(count: number): maybePromise<[$.Slice<fs.FileInfo> | null, $.GoError]>
|
|
1511
|
+
Stat(): maybePromise<[fs.FileInfo | null, $.GoError]>
|
|
1234
1512
|
}
|
|
1235
1513
|
|
|
1236
1514
|
export function FS(fsys: fs.FS): FileSystem {
|
|
@@ -1248,17 +1526,23 @@ export function FS(fsys: fs.FS): FileSystem {
|
|
|
1248
1526
|
|
|
1249
1527
|
function httpFileFromFSFile(file: Exclude<fs.File, null>): File {
|
|
1250
1528
|
const seek = (file as Partial<io.Seeker>).Seek
|
|
1251
|
-
const readdir = (
|
|
1529
|
+
const readdir = (
|
|
1530
|
+
file as { Readdir?: (count: number) => [$.Slice<fs.FileInfo>, $.GoError] }
|
|
1531
|
+
).Readdir
|
|
1252
1532
|
return {
|
|
1253
|
-
Read: (p) =>
|
|
1533
|
+
Read: (p) =>
|
|
1534
|
+
file.Read(p instanceof Uint8Array ? p : Uint8Array.from(p ?? [])),
|
|
1254
1535
|
Close: () => file.Close(),
|
|
1255
1536
|
Stat: () => file.Stat(),
|
|
1256
|
-
Seek:
|
|
1537
|
+
Seek:
|
|
1538
|
+
seek == null ?
|
|
1539
|
+
() => [0, errors.New('net/http: file does not support seek')]
|
|
1540
|
+
: seek.bind(file),
|
|
1257
1541
|
Readdir: readdir == null ? () => [null, io.EOF] : readdir.bind(file),
|
|
1258
1542
|
}
|
|
1259
1543
|
}
|
|
1260
1544
|
|
|
1261
|
-
export function FileServer(root:
|
|
1545
|
+
export function FileServer(root: fileServerFileSystem | null): Handler {
|
|
1262
1546
|
return {
|
|
1263
1547
|
async ServeHTTP(w, r): Promise<void> {
|
|
1264
1548
|
const req = $.pointerValue<Request | null>(r)
|
|
@@ -1269,13 +1553,15 @@ export function FileServer(root: FileSystem | null): Handler {
|
|
|
1269
1553
|
Error(w, 'method not allowed', StatusMethodNotAllowed)
|
|
1270
1554
|
return
|
|
1271
1555
|
}
|
|
1272
|
-
const [file, err] = root?.Open(
|
|
1556
|
+
const [file, err] = (await root?.Open(
|
|
1557
|
+
cleanFileServerPath(req.URL?.Path ?? ''),
|
|
1558
|
+
)) ?? [null, fs.ErrInvalid]
|
|
1273
1559
|
if (err != null || file == null) {
|
|
1274
1560
|
NotFound(w, req)
|
|
1275
1561
|
return
|
|
1276
1562
|
}
|
|
1277
1563
|
try {
|
|
1278
|
-
const [info, statErr] = file.Stat()
|
|
1564
|
+
const [info, statErr] = await file.Stat()
|
|
1279
1565
|
if (statErr != null) {
|
|
1280
1566
|
Error(w, statErr.Error(), StatusInternalServerError)
|
|
1281
1567
|
return
|
|
@@ -1284,7 +1570,7 @@ export function FileServer(root: FileSystem | null): Handler {
|
|
|
1284
1570
|
NotFound(w, req)
|
|
1285
1571
|
return
|
|
1286
1572
|
}
|
|
1287
|
-
const [data, readErr] = await io.ReadAll(file)
|
|
1573
|
+
const [data, readErr] = await io.ReadAll(file as io.Reader)
|
|
1288
1574
|
if (readErr != null) {
|
|
1289
1575
|
Error(w, readErr.Error(), StatusInternalServerError)
|
|
1290
1576
|
return
|
|
@@ -1297,7 +1583,7 @@ export function FileServer(root: FileSystem | null): Handler {
|
|
|
1297
1583
|
w.Write(data)
|
|
1298
1584
|
}
|
|
1299
1585
|
} finally {
|
|
1300
|
-
file.Close()
|
|
1586
|
+
await file.Close()
|
|
1301
1587
|
}
|
|
1302
1588
|
},
|
|
1303
1589
|
}
|
|
@@ -1348,8 +1634,25 @@ function cleanFileServerPath(name: string): string {
|
|
|
1348
1634
|
}
|
|
1349
1635
|
|
|
1350
1636
|
export interface Handler {
|
|
1351
|
-
ServeHTTP(
|
|
1352
|
-
|
|
1637
|
+
ServeHTTP(
|
|
1638
|
+
w: ResponseWriter | null,
|
|
1639
|
+
r: Request | $.VarRef<Request> | null,
|
|
1640
|
+
): void | Promise<void>
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
$.registerInterfaceType('http.Handler', null, [
|
|
1644
|
+
{
|
|
1645
|
+
name: 'ServeHTTP',
|
|
1646
|
+
args: [
|
|
1647
|
+
{ name: 'w', type: 'http.ResponseWriter' },
|
|
1648
|
+
{
|
|
1649
|
+
name: 'r',
|
|
1650
|
+
type: { kind: $.TypeKind.Pointer, elemType: 'http.Request' },
|
|
1651
|
+
},
|
|
1652
|
+
],
|
|
1653
|
+
returns: [],
|
|
1654
|
+
},
|
|
1655
|
+
])
|
|
1353
1656
|
|
|
1354
1657
|
export type HandlerFunc = (
|
|
1355
1658
|
w: ResponseWriter | null,
|
|
@@ -1357,10 +1660,13 @@ export type HandlerFunc = (
|
|
|
1357
1660
|
) => void | Promise<void>
|
|
1358
1661
|
|
|
1359
1662
|
export function HandlerFunc_ServeHTTP(
|
|
1360
|
-
h: HandlerFunc,
|
|
1663
|
+
h: HandlerFunc | null,
|
|
1361
1664
|
w: ResponseWriter | null,
|
|
1362
1665
|
r: Request | $.VarRef<Request> | null,
|
|
1363
1666
|
): void | Promise<void> {
|
|
1667
|
+
if (!h) {
|
|
1668
|
+
throw new globalThis.Error('http: nil HandlerFunc')
|
|
1669
|
+
}
|
|
1364
1670
|
return h(w, r)
|
|
1365
1671
|
}
|
|
1366
1672
|
|
|
@@ -1512,7 +1818,9 @@ function wildcardPatternMatches(pattern: string, path: string): boolean {
|
|
|
1512
1818
|
export class Server {
|
|
1513
1819
|
public Addr: string
|
|
1514
1820
|
public BaseContext: ((listener: any) => context.Context) | null
|
|
1515
|
-
public ConnContext:
|
|
1821
|
+
public ConnContext:
|
|
1822
|
+
| ((ctx: context.Context, conn: any) => context.Context)
|
|
1823
|
+
| null
|
|
1516
1824
|
public Handler: Handler | null
|
|
1517
1825
|
public DisableGeneralOptionsHandler: boolean
|
|
1518
1826
|
public TLSConfig: any
|
|
@@ -1533,7 +1841,8 @@ export class Server {
|
|
|
1533
1841
|
this.BaseContext = init?.BaseContext ?? null
|
|
1534
1842
|
this.ConnContext = init?.ConnContext ?? null
|
|
1535
1843
|
this.Handler = init?.Handler ?? null
|
|
1536
|
-
this.DisableGeneralOptionsHandler =
|
|
1844
|
+
this.DisableGeneralOptionsHandler =
|
|
1845
|
+
init?.DisableGeneralOptionsHandler ?? false
|
|
1537
1846
|
this.TLSConfig = init?.TLSConfig ?? null
|
|
1538
1847
|
this.ReadTimeout = init?.ReadTimeout ?? 0
|
|
1539
1848
|
this.ReadTimeoutHandler = (init as any)?.ReadTimeoutHandler ?? null
|
|
@@ -1549,11 +1858,15 @@ export class Server {
|
|
|
1549
1858
|
}
|
|
1550
1859
|
|
|
1551
1860
|
public ListenAndServe(): $.GoError {
|
|
1552
|
-
return errors.New(
|
|
1861
|
+
return errors.New(
|
|
1862
|
+
'net/http: Server.ListenAndServe is not implemented in GoScript',
|
|
1863
|
+
)
|
|
1553
1864
|
}
|
|
1554
1865
|
|
|
1555
1866
|
public ListenAndServeTLS(_certFile: string, _keyFile: string): $.GoError {
|
|
1556
|
-
return errors.New(
|
|
1867
|
+
return errors.New(
|
|
1868
|
+
'net/http: Server.ListenAndServeTLS is not implemented in GoScript',
|
|
1869
|
+
)
|
|
1557
1870
|
}
|
|
1558
1871
|
|
|
1559
1872
|
public Close(): $.GoError {
|
|
@@ -1568,11 +1881,18 @@ export class Server {
|
|
|
1568
1881
|
return ErrNotSupported
|
|
1569
1882
|
}
|
|
1570
1883
|
|
|
1571
|
-
public ServeTLS(
|
|
1884
|
+
public ServeTLS(
|
|
1885
|
+
_listener: any,
|
|
1886
|
+
_certFile: string,
|
|
1887
|
+
_keyFile: string,
|
|
1888
|
+
): $.GoError {
|
|
1572
1889
|
return ErrNotSupported
|
|
1573
1890
|
}
|
|
1574
1891
|
|
|
1575
|
-
public ServeHTTP(
|
|
1892
|
+
public ServeHTTP(
|
|
1893
|
+
w: ResponseWriter | null,
|
|
1894
|
+
r: Request | $.VarRef<Request> | null,
|
|
1895
|
+
): void | Promise<void> {
|
|
1576
1896
|
return (this.Handler ?? DefaultServeMux).ServeHTTP(w, r)
|
|
1577
1897
|
}
|
|
1578
1898
|
|
|
@@ -1581,7 +1901,10 @@ export class Server {
|
|
|
1581
1901
|
public SetKeepAlivesEnabled(_v: boolean): void {}
|
|
1582
1902
|
}
|
|
1583
1903
|
|
|
1584
|
-
export function ListenAndServe(
|
|
1904
|
+
export function ListenAndServe(
|
|
1905
|
+
_addr: string,
|
|
1906
|
+
_handler: Handler | null,
|
|
1907
|
+
): $.GoError {
|
|
1585
1908
|
return ErrNotSupported
|
|
1586
1909
|
}
|
|
1587
1910
|
|
|
@@ -1624,7 +1947,10 @@ export interface Hijacker {
|
|
|
1624
1947
|
}
|
|
1625
1948
|
|
|
1626
1949
|
export interface Pusher {
|
|
1627
|
-
Push(
|
|
1950
|
+
Push(
|
|
1951
|
+
target: string,
|
|
1952
|
+
opts: PushOptions | $.VarRef<PushOptions> | null,
|
|
1953
|
+
): $.GoError
|
|
1628
1954
|
}
|
|
1629
1955
|
|
|
1630
1956
|
export class ResponseController {
|
|
@@ -1657,7 +1983,9 @@ export class ResponseController {
|
|
|
1657
1983
|
}
|
|
1658
1984
|
}
|
|
1659
1985
|
|
|
1660
|
-
export function NewResponseController(
|
|
1986
|
+
export function NewResponseController(
|
|
1987
|
+
rw: ResponseWriter | null,
|
|
1988
|
+
): ResponseController {
|
|
1661
1989
|
return new ResponseController(rw)
|
|
1662
1990
|
}
|
|
1663
1991
|
|
|
@@ -1666,7 +1994,10 @@ class maxBytesReader implements io.ReadCloser {
|
|
|
1666
1994
|
private remaining: number
|
|
1667
1995
|
private err: $.GoError = null
|
|
1668
1996
|
|
|
1669
|
-
constructor(
|
|
1997
|
+
constructor(
|
|
1998
|
+
private reader: io.ReadCloser,
|
|
1999
|
+
limit: number,
|
|
2000
|
+
) {
|
|
1670
2001
|
this.initialLimit = Math.max(0, limit)
|
|
1671
2002
|
this.remaining = this.initialLimit
|
|
1672
2003
|
}
|
|
@@ -1719,14 +2050,19 @@ export class ServeMux implements Handler {
|
|
|
1719
2050
|
this.Handle(pattern, { ServeHTTP: handler })
|
|
1720
2051
|
}
|
|
1721
2052
|
|
|
1722
|
-
public Handler(
|
|
2053
|
+
public Handler(
|
|
2054
|
+
r: Request | $.VarRef<Request> | null,
|
|
2055
|
+
): [Handler | null, string] {
|
|
1723
2056
|
const req = $.pointerValue<Request | null>(r)
|
|
1724
2057
|
const path = req?.URL?.Path ?? ''
|
|
1725
2058
|
const handler = this.handlers.get(path) ?? null
|
|
1726
2059
|
return [handler, handler == null ? '' : path]
|
|
1727
2060
|
}
|
|
1728
2061
|
|
|
1729
|
-
public ServeHTTP(
|
|
2062
|
+
public ServeHTTP(
|
|
2063
|
+
w: ResponseWriter | null,
|
|
2064
|
+
r: Request | $.VarRef<Request> | null,
|
|
2065
|
+
): void | Promise<void> {
|
|
1730
2066
|
const [handler] = this.Handler(r)
|
|
1731
2067
|
if (handler == null) {
|
|
1732
2068
|
NotFound(w, r)
|
|
@@ -1754,7 +2090,11 @@ export function StripPrefix(prefix: string, handler: Handler | null): Handler {
|
|
|
1754
2090
|
return {
|
|
1755
2091
|
ServeHTTP(w, r) {
|
|
1756
2092
|
const req = $.pointerValue<Request | null>(r)
|
|
1757
|
-
if (
|
|
2093
|
+
if (
|
|
2094
|
+
req?.URL != null &&
|
|
2095
|
+
typeof req.URL.Path === 'string' &&
|
|
2096
|
+
req.URL.Path.startsWith(prefix)
|
|
2097
|
+
) {
|
|
1758
2098
|
req.URL = { ...req.URL, Path: req.URL.Path.slice(prefix.length) || '/' }
|
|
1759
2099
|
}
|
|
1760
2100
|
return handler?.ServeHTTP(w, req)
|
|
@@ -1796,7 +2136,11 @@ export function RedirectHandler(url: string, code: number): Handler {
|
|
|
1796
2136
|
}
|
|
1797
2137
|
}
|
|
1798
2138
|
|
|
1799
|
-
export function TimeoutHandler(
|
|
2139
|
+
export function TimeoutHandler(
|
|
2140
|
+
handler: Handler | null,
|
|
2141
|
+
_dt: number,
|
|
2142
|
+
msg: string,
|
|
2143
|
+
): Handler {
|
|
1800
2144
|
return {
|
|
1801
2145
|
ServeHTTP(w, r) {
|
|
1802
2146
|
if (handler == null) {
|
|
@@ -1808,12 +2152,19 @@ export function TimeoutHandler(handler: Handler | null, _dt: number, msg: string
|
|
|
1808
2152
|
}
|
|
1809
2153
|
}
|
|
1810
2154
|
|
|
1811
|
-
export function Error(
|
|
2155
|
+
export function Error(
|
|
2156
|
+
w: ResponseWriter | null,
|
|
2157
|
+
error: string,
|
|
2158
|
+
code: number,
|
|
2159
|
+
): void {
|
|
1812
2160
|
w?.WriteHeader(code)
|
|
1813
2161
|
w?.Write($.stringToBytes(error + '\n'))
|
|
1814
2162
|
}
|
|
1815
2163
|
|
|
1816
|
-
export function NotFound(
|
|
2164
|
+
export function NotFound(
|
|
2165
|
+
w: ResponseWriter | null,
|
|
2166
|
+
_r: Request | $.VarRef<Request> | null,
|
|
2167
|
+
): void {
|
|
1817
2168
|
Error(w, '404 page not found', StatusNotFound)
|
|
1818
2169
|
}
|
|
1819
2170
|
|
|
@@ -1833,7 +2184,10 @@ export function Redirect(
|
|
|
1833
2184
|
export function ParseTime(text: string): [time.Time, $.GoError] {
|
|
1834
2185
|
const date = new globalThis.Date(text)
|
|
1835
2186
|
if (isNaN(date.getTime())) {
|
|
1836
|
-
return [
|
|
2187
|
+
return [
|
|
2188
|
+
new time.Time(),
|
|
2189
|
+
$.newError(`parsing time "${text}" as HTTP-date: cannot parse`),
|
|
2190
|
+
]
|
|
1837
2191
|
}
|
|
1838
2192
|
return [time.UnixMilli(date.getTime()), null]
|
|
1839
2193
|
}
|
|
@@ -1881,7 +2235,10 @@ export function DetectContentType(data: $.Slice<number>): string {
|
|
|
1881
2235
|
if (startsWithBytes(bytes, new Uint8Array([0xef, 0xbb, 0xbf]))) {
|
|
1882
2236
|
return 'text/plain; charset=utf-8'
|
|
1883
2237
|
}
|
|
1884
|
-
if (
|
|
2238
|
+
if (
|
|
2239
|
+
startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) ||
|
|
2240
|
+
startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x02, 0x00]))
|
|
2241
|
+
) {
|
|
1885
2242
|
return 'image/x-icon'
|
|
1886
2243
|
}
|
|
1887
2244
|
if (startsWithASCII(bytes, 'BM')) {
|
|
@@ -1893,7 +2250,12 @@ export function DetectContentType(data: $.Slice<number>): string {
|
|
|
1893
2250
|
if (isRIFFSignature(bytes, 'WEBPVP')) {
|
|
1894
2251
|
return 'image/webp'
|
|
1895
2252
|
}
|
|
1896
|
-
if (
|
|
2253
|
+
if (
|
|
2254
|
+
startsWithBytes(
|
|
2255
|
+
bytes,
|
|
2256
|
+
new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
|
|
2257
|
+
)
|
|
2258
|
+
) {
|
|
1897
2259
|
return 'image/png'
|
|
1898
2260
|
}
|
|
1899
2261
|
if (startsWithBytes(bytes, new Uint8Array([0xff, 0xd8, 0xff]))) {
|
|
@@ -1908,7 +2270,12 @@ export function DetectContentType(data: $.Slice<number>): string {
|
|
|
1908
2270
|
if (startsWithBytes(bytes, new Uint8Array([0x4f, 0x67, 0x67, 0x53, 0x00]))) {
|
|
1909
2271
|
return 'application/ogg'
|
|
1910
2272
|
}
|
|
1911
|
-
if (
|
|
2273
|
+
if (
|
|
2274
|
+
startsWithBytes(
|
|
2275
|
+
bytes,
|
|
2276
|
+
new Uint8Array([0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06]),
|
|
2277
|
+
)
|
|
2278
|
+
) {
|
|
1912
2279
|
return 'audio/midi'
|
|
1913
2280
|
}
|
|
1914
2281
|
if (isRIFFSignature(bytes, 'AVI ')) {
|
|
@@ -1947,17 +2314,32 @@ export function DetectContentType(data: $.Slice<number>): string {
|
|
|
1947
2314
|
if (startsWithBytes(bytes, new Uint8Array([0x50, 0x4b, 0x03, 0x04]))) {
|
|
1948
2315
|
return 'application/zip'
|
|
1949
2316
|
}
|
|
1950
|
-
if (
|
|
2317
|
+
if (
|
|
2318
|
+
startsWithBytes(
|
|
2319
|
+
bytes,
|
|
2320
|
+
new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00]),
|
|
2321
|
+
)
|
|
2322
|
+
) {
|
|
1951
2323
|
return 'application/x-rar-compressed'
|
|
1952
2324
|
}
|
|
1953
|
-
if (
|
|
2325
|
+
if (
|
|
2326
|
+
startsWithBytes(
|
|
2327
|
+
bytes,
|
|
2328
|
+
new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00]),
|
|
2329
|
+
)
|
|
2330
|
+
) {
|
|
1954
2331
|
return 'application/x-rar-compressed'
|
|
1955
2332
|
}
|
|
1956
2333
|
if (startsWithBytes(bytes, new Uint8Array([0x00, 0x61, 0x73, 0x6d]))) {
|
|
1957
2334
|
return 'application/wasm'
|
|
1958
2335
|
}
|
|
1959
2336
|
for (const byte of afterWS) {
|
|
1960
|
-
if (
|
|
2337
|
+
if (
|
|
2338
|
+
byte <= 0x08 ||
|
|
2339
|
+
byte === 0x0b ||
|
|
2340
|
+
(byte >= 0x0e && byte <= 0x1a) ||
|
|
2341
|
+
(byte >= 0x1c && byte <= 0x1f)
|
|
2342
|
+
) {
|
|
1961
2343
|
return 'application/octet-stream'
|
|
1962
2344
|
}
|
|
1963
2345
|
}
|
|
@@ -1967,7 +2349,13 @@ export function DetectContentType(data: $.Slice<number>): string {
|
|
|
1967
2349
|
function firstNonWhitespace(data: Uint8Array): number {
|
|
1968
2350
|
for (let i = 0; i < data.length; i++) {
|
|
1969
2351
|
const byte = data[i]
|
|
1970
|
-
if (
|
|
2352
|
+
if (
|
|
2353
|
+
byte !== 0x09 &&
|
|
2354
|
+
byte !== 0x0a &&
|
|
2355
|
+
byte !== 0x0c &&
|
|
2356
|
+
byte !== 0x0d &&
|
|
2357
|
+
byte !== 0x20
|
|
2358
|
+
) {
|
|
1971
2359
|
return i
|
|
1972
2360
|
}
|
|
1973
2361
|
}
|
|
@@ -1990,7 +2378,11 @@ function startsWithASCII(data: Uint8Array, prefix: string): boolean {
|
|
|
1990
2378
|
return asciiMatchesAt(data, 0, prefix)
|
|
1991
2379
|
}
|
|
1992
2380
|
|
|
1993
|
-
function asciiMatchesAt(
|
|
2381
|
+
function asciiMatchesAt(
|
|
2382
|
+
data: Uint8Array,
|
|
2383
|
+
offset: number,
|
|
2384
|
+
text: string,
|
|
2385
|
+
): boolean {
|
|
1994
2386
|
if (data.length < offset + text.length) {
|
|
1995
2387
|
return false
|
|
1996
2388
|
}
|
|
@@ -2102,7 +2494,12 @@ export function ParseSetCookie(line: string): [Cookie | null, $.GoError] {
|
|
|
2102
2494
|
if (!ok) {
|
|
2103
2495
|
return [null, errInvalidCookieValue]
|
|
2104
2496
|
}
|
|
2105
|
-
const cookie = new Cookie({
|
|
2497
|
+
const cookie = new Cookie({
|
|
2498
|
+
Name: name,
|
|
2499
|
+
Value: value,
|
|
2500
|
+
Quoted: quoted,
|
|
2501
|
+
Raw: line,
|
|
2502
|
+
})
|
|
2106
2503
|
const unparsed: string[] = []
|
|
2107
2504
|
for (const raw of parts.slice(1)) {
|
|
2108
2505
|
const part = raw.trim()
|
|
@@ -2158,7 +2555,10 @@ export function ParseSetCookie(line: string): [Cookie | null, $.GoError] {
|
|
|
2158
2555
|
break
|
|
2159
2556
|
}
|
|
2160
2557
|
let secs = Number.parseInt(attrValue, 10)
|
|
2161
|
-
if (
|
|
2558
|
+
if (
|
|
2559
|
+
(secs !== 0 && attrValue[0] === '0') ||
|
|
2560
|
+
!Number.isSafeInteger(secs)
|
|
2561
|
+
) {
|
|
2162
2562
|
break
|
|
2163
2563
|
}
|
|
2164
2564
|
if (secs <= 0) {
|
|
@@ -2207,7 +2607,10 @@ export function NewRequestWithContext(
|
|
|
2207
2607
|
method = MethodGet
|
|
2208
2608
|
}
|
|
2209
2609
|
if (!isToken(method)) {
|
|
2210
|
-
return [
|
|
2610
|
+
return [
|
|
2611
|
+
null,
|
|
2612
|
+
errors.New(`net/http: invalid method ${JSON.stringify(method)}`),
|
|
2613
|
+
]
|
|
2211
2614
|
}
|
|
2212
2615
|
if (ctx == null) {
|
|
2213
2616
|
return [null, errors.New('net/http: nil Context')]
|
|
@@ -2217,7 +2620,17 @@ export function NewRequestWithContext(
|
|
|
2217
2620
|
return [null, err]
|
|
2218
2621
|
}
|
|
2219
2622
|
const bodyInfo = requestBodyInfo(body, 0)
|
|
2220
|
-
return [
|
|
2623
|
+
return [
|
|
2624
|
+
new Request({
|
|
2625
|
+
Method: method,
|
|
2626
|
+
URL: parsedURL,
|
|
2627
|
+
Body: bodyInfo.Body,
|
|
2628
|
+
ContentLength: bodyInfo.ContentLength,
|
|
2629
|
+
Host: parsedURL.Host,
|
|
2630
|
+
ctx,
|
|
2631
|
+
}),
|
|
2632
|
+
null,
|
|
2633
|
+
]
|
|
2221
2634
|
}
|
|
2222
2635
|
|
|
2223
2636
|
export async function Get(_url: string): Promise<[Response | null, $.GoError]> {
|
|
@@ -2228,7 +2641,9 @@ export async function Get(_url: string): Promise<[Response | null, $.GoError]> {
|
|
|
2228
2641
|
return await DefaultClient.Do(req)
|
|
2229
2642
|
}
|
|
2230
2643
|
|
|
2231
|
-
export async function Head(
|
|
2644
|
+
export async function Head(
|
|
2645
|
+
_url: string,
|
|
2646
|
+
): Promise<[Response | null, $.GoError]> {
|
|
2232
2647
|
const [req, err] = NewRequest(MethodHead, _url, null)
|
|
2233
2648
|
if (err != null) {
|
|
2234
2649
|
return [null, err]
|
|
@@ -2249,15 +2664,22 @@ export async function Post(
|
|
|
2249
2664
|
return await DefaultClient.Do(req)
|
|
2250
2665
|
}
|
|
2251
2666
|
|
|
2252
|
-
export async function PostForm(
|
|
2667
|
+
export async function PostForm(
|
|
2668
|
+
_url: string,
|
|
2669
|
+
data: any,
|
|
2670
|
+
): Promise<[Response | null, $.GoError]> {
|
|
2253
2671
|
return await DefaultClient.PostForm(_url, data)
|
|
2254
2672
|
}
|
|
2255
2673
|
|
|
2256
|
-
export function ProxyFromEnvironment(
|
|
2674
|
+
export function ProxyFromEnvironment(
|
|
2675
|
+
_req: Request | $.VarRef<Request> | null,
|
|
2676
|
+
): [any, $.GoError] {
|
|
2257
2677
|
return [null, null]
|
|
2258
2678
|
}
|
|
2259
2679
|
|
|
2260
|
-
export function ProxyURL(
|
|
2680
|
+
export function ProxyURL(
|
|
2681
|
+
fixedURL: any,
|
|
2682
|
+
): (req: Request | $.VarRef<Request> | null) => [any, $.GoError] {
|
|
2261
2683
|
return () => [fixedURL, null]
|
|
2262
2684
|
}
|
|
2263
2685
|
|
|
@@ -2265,7 +2687,10 @@ export function ReadRequest(_reader: any): [Request | null, $.GoError] {
|
|
|
2265
2687
|
return [null, ErrNotSupported]
|
|
2266
2688
|
}
|
|
2267
2689
|
|
|
2268
|
-
export function ReadResponse(
|
|
2690
|
+
export function ReadResponse(
|
|
2691
|
+
_reader: any,
|
|
2692
|
+
_req: Request | $.VarRef<Request> | null,
|
|
2693
|
+
): [Response | null, $.GoError] {
|
|
2269
2694
|
return [null, ErrNotSupported]
|
|
2270
2695
|
}
|
|
2271
2696
|
|
|
@@ -2303,7 +2728,10 @@ function readCloserForBody(body: io.Reader | null): io.ReadCloser | null {
|
|
|
2303
2728
|
return io.NopCloser(body)
|
|
2304
2729
|
}
|
|
2305
2730
|
|
|
2306
|
-
function requestBodyInfo(
|
|
2731
|
+
function requestBodyInfo(
|
|
2732
|
+
body: io.Reader | null,
|
|
2733
|
+
unknownLength: number,
|
|
2734
|
+
): { Body: io.ReadCloser | null; ContentLength: number } {
|
|
2307
2735
|
if (body == null) {
|
|
2308
2736
|
return { Body: null, ContentLength: 0 }
|
|
2309
2737
|
}
|
|
@@ -2311,9 +2739,16 @@ function requestBodyInfo(body: io.Reader | null, unknownLength: number): { Body:
|
|
|
2311
2739
|
if (value === NoBody) {
|
|
2312
2740
|
return { Body: NoBody, ContentLength: 0 }
|
|
2313
2741
|
}
|
|
2314
|
-
if (
|
|
2742
|
+
if (
|
|
2743
|
+
value instanceof bytes.Buffer ||
|
|
2744
|
+
value instanceof bytes.Reader ||
|
|
2745
|
+
value instanceof strings.Reader
|
|
2746
|
+
) {
|
|
2315
2747
|
const length = value.Len()
|
|
2316
|
-
return {
|
|
2748
|
+
return {
|
|
2749
|
+
Body: length === 0 ? NoBody : readCloserForBody(body),
|
|
2750
|
+
ContentLength: length,
|
|
2751
|
+
}
|
|
2317
2752
|
}
|
|
2318
2753
|
return { Body: readCloserForBody(body), ContentLength: unknownLength }
|
|
2319
2754
|
}
|