goscript 0.1.4 → 0.2.1
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.md +5 -2
- package/cmd/go_js_wasm_exec/main.go +201 -0
- package/cmd/go_js_wasm_exec/main_test.go +83 -0
- package/cmd/goscript/{cmd_compile.go → cmd-compile.go} +7 -0
- package/cmd/goscript/cmd-test.go +14 -0
- package/cmd/goscript/cmd-test_test.go +1 -1
- package/cmd/goscript-wasm/main.go +38 -6
- package/compiler/compile-request.go +12 -9
- package/compiler/compliance_test.go +0 -1
- package/compiler/config.go +2 -0
- package/compiler/diagnostic.go +104 -12
- package/compiler/diagnostic_test.go +106 -0
- package/compiler/gotest/request.go +28 -0
- package/compiler/gotest/runner.go +354 -44
- package/compiler/gotest/runner_test.go +293 -1
- package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
- package/compiler/gotest/testdata/browserapi/go.mod +3 -0
- package/compiler/index.test.ts +23 -0
- package/compiler/lowered-program.go +33 -24
- package/compiler/lowering.go +746 -194
- package/compiler/lowering_bench_test.go +42 -27
- package/compiler/lowering_internal_test.go +18 -0
- package/compiler/override-facts.go +15 -0
- package/compiler/override-parity-verifier.go +450 -0
- package/compiler/override-parity.go +122 -0
- package/compiler/override-registry_test.go +559 -0
- package/compiler/protobuf-ts-binding.go +567 -0
- package/compiler/protobuf-ts-binding_test.go +402 -0
- package/compiler/runtime-contract.go +4 -0
- package/compiler/runtime-contract_test.go +2 -0
- package/compiler/semantic-model-types.go +9 -4
- package/compiler/semantic-model.go +282 -70
- package/compiler/semantic-model_test.go +82 -1
- package/compiler/service.go +21 -1
- package/compiler/skeleton_test.go +118 -10
- package/compiler/typescript-emitter.go +128 -13
- package/compiler/wasm/compile_test.go +37 -4
- package/compiler/{wasm_api.go → wasm-api.go} +57 -7
- package/dist/gs/builtin/hostio.js +5 -0
- package/dist/gs/builtin/hostio.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +13 -2
- package/dist/gs/builtin/slice.js +187 -6
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +13 -5
- package/dist/gs/builtin/type.js +153 -60
- package/dist/gs/builtin/type.js.map +1 -1
- package/dist/gs/builtin/varRef.d.ts +11 -0
- package/dist/gs/builtin/varRef.js +57 -2
- package/dist/gs/builtin/varRef.js.map +1 -1
- package/dist/gs/bytes/buffer.gs.js +1 -1
- package/dist/gs/bytes/buffer.gs.js.map +1 -1
- package/dist/gs/bytes/reader.gs.js +1 -1
- package/dist/gs/bytes/reader.gs.js.map +1 -1
- package/dist/gs/compress/zlib/index.d.ts +10 -3
- package/dist/gs/compress/zlib/index.js +50 -16
- package/dist/gs/compress/zlib/index.js.map +1 -1
- package/dist/gs/encoding/json/index.d.ts +114 -0
- package/dist/gs/encoding/json/index.js +544 -36
- package/dist/gs/encoding/json/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +101 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +589 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.d.ts +1 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js +17 -11
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.js.map +1 -1
- package/dist/gs/github.com/pkg/errors/errors.js +54 -30
- package/dist/gs/github.com/pkg/errors/errors.js.map +1 -1
- package/dist/gs/go/scanner/index.d.ts +2 -0
- package/dist/gs/go/scanner/index.js +29 -5
- package/dist/gs/go/scanner/index.js.map +1 -1
- package/dist/gs/go/token/index.js +22 -6
- package/dist/gs/go/token/index.js.map +1 -1
- package/dist/gs/hash/index.d.ts +6 -0
- package/dist/gs/hash/index.js +20 -0
- package/dist/gs/hash/index.js.map +1 -1
- package/dist/gs/internal/byteorder/index.js +2 -2
- package/dist/gs/internal/byteorder/index.js.map +1 -1
- package/dist/gs/internal/goarch/index.d.ts +43 -3
- package/dist/gs/internal/goarch/index.js +42 -10
- package/dist/gs/internal/goarch/index.js.map +1 -1
- package/dist/gs/io/fs/fs.js +26 -14
- package/dist/gs/io/fs/fs.js.map +1 -1
- package/dist/gs/io/fs/readdir.js +4 -2
- package/dist/gs/io/fs/readdir.js.map +1 -1
- package/dist/gs/io/fs/sub.js +8 -1
- package/dist/gs/io/fs/sub.js.map +1 -1
- package/dist/gs/io/io.d.ts +2 -0
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/math/bits/index.d.ts +5 -0
- package/dist/gs/math/bits/index.js +16 -4
- package/dist/gs/math/bits/index.js.map +1 -1
- package/dist/gs/mime/index.d.ts +16 -0
- package/dist/gs/mime/index.js +315 -6
- package/dist/gs/mime/index.js.map +1 -1
- package/dist/gs/net/http/httptest/index.d.ts +12 -0
- package/dist/gs/net/http/httptest/index.js +85 -6
- package/dist/gs/net/http/httptest/index.js.map +1 -1
- package/dist/gs/net/http/index.d.ts +300 -5
- package/dist/gs/net/http/index.js +1598 -58
- package/dist/gs/net/http/index.js.map +1 -1
- package/dist/gs/os/dir_unix.gs.js +1 -1
- package/dist/gs/os/dir_unix.gs.js.map +1 -1
- package/dist/gs/os/error.gs.js +1 -1
- package/dist/gs/os/error.gs.js.map +1 -1
- package/dist/gs/os/exec.gs.d.ts +1 -0
- package/dist/gs/os/exec.gs.js +4 -8
- package/dist/gs/os/exec.gs.js.map +1 -1
- package/dist/gs/os/exec_posix.gs.js +1 -1
- package/dist/gs/os/exec_posix.gs.js.map +1 -1
- package/dist/gs/os/index.d.ts +1 -1
- package/dist/gs/os/index.js +1 -1
- package/dist/gs/os/index.js.map +1 -1
- package/dist/gs/os/proc.gs.d.ts +4 -0
- package/dist/gs/os/proc.gs.js +12 -6
- package/dist/gs/os/proc.gs.js.map +1 -1
- package/dist/gs/os/root_js.gs.js +1 -1
- package/dist/gs/os/root_js.gs.js.map +1 -1
- package/dist/gs/os/types.gs.js +1 -1
- package/dist/gs/os/types.gs.js.map +1 -1
- package/dist/gs/os/types_js.gs.js +1 -1
- package/dist/gs/os/types_js.gs.js.map +1 -1
- package/dist/gs/os/types_unix.gs.js +1 -1
- package/dist/gs/os/types_unix.gs.js.map +1 -1
- package/dist/gs/path/path.js +11 -7
- package/dist/gs/path/path.js.map +1 -1
- package/dist/gs/reflect/index.d.ts +5 -4
- package/dist/gs/reflect/index.js +4 -3
- package/dist/gs/reflect/index.js.map +1 -1
- package/dist/gs/reflect/map.js +15 -0
- package/dist/gs/reflect/map.js.map +1 -1
- package/dist/gs/reflect/type.d.ts +25 -6
- package/dist/gs/reflect/type.js +1475 -228
- package/dist/gs/reflect/type.js.map +1 -1
- package/dist/gs/reflect/types.d.ts +14 -6
- package/dist/gs/reflect/types.js +35 -1
- package/dist/gs/reflect/types.js.map +1 -1
- package/dist/gs/reflect/value.d.ts +1 -0
- package/dist/gs/reflect/value.js +83 -41
- package/dist/gs/reflect/value.js.map +1 -1
- package/dist/gs/reflect/visiblefields.js +4 -140
- package/dist/gs/reflect/visiblefields.js.map +1 -1
- package/dist/gs/runtime/pprof/index.d.ts +8 -2
- package/dist/gs/runtime/pprof/index.js +50 -30
- package/dist/gs/runtime/pprof/index.js.map +1 -1
- package/dist/gs/runtime/runtime.js +5 -4
- package/dist/gs/runtime/runtime.js.map +1 -1
- package/dist/gs/runtime/trace/index.js +5 -19
- package/dist/gs/runtime/trace/index.js.map +1 -1
- package/dist/gs/strconv/atoi.gs.js +1 -1
- package/dist/gs/strconv/atoi.gs.js.map +1 -1
- package/dist/gs/strconv/complex.gs.d.ts +3 -0
- package/dist/gs/strconv/complex.gs.js +148 -0
- package/dist/gs/strconv/complex.gs.js.map +1 -0
- package/dist/gs/strconv/index.d.ts +1 -0
- package/dist/gs/strconv/index.js +1 -0
- package/dist/gs/strconv/index.js.map +1 -1
- package/dist/gs/strings/builder.js +1 -1
- package/dist/gs/strings/reader.js +9 -5
- package/dist/gs/strings/reader.js.map +1 -1
- package/dist/gs/strings/replace.js +15 -7
- package/dist/gs/strings/replace.js.map +1 -1
- package/dist/gs/strings/strings.d.ts +5 -0
- package/dist/gs/strings/strings.js +57 -5
- package/dist/gs/strings/strings.js.map +1 -1
- package/dist/gs/sync/atomic/doc_64.gs.js +7 -6
- package/dist/gs/sync/atomic/doc_64.gs.js.map +1 -1
- package/dist/gs/sync/atomic/type.gs.js +9 -9
- package/dist/gs/sync/atomic/type.gs.js.map +1 -1
- package/dist/gs/sync/atomic/value.gs.js +2 -2
- package/dist/gs/sync/atomic/value.gs.js.map +1 -1
- package/dist/gs/syscall/env.js +22 -14
- package/dist/gs/syscall/env.js.map +1 -1
- package/dist/gs/testing/testing.js +55 -13
- package/dist/gs/testing/testing.js.map +1 -1
- package/dist/gs/time/time.d.ts +24 -1
- package/dist/gs/time/time.js +43 -3
- package/dist/gs/time/time.js.map +1 -1
- package/dist/gs/unique/index.js +7 -1
- package/dist/gs/unique/index.js.map +1 -1
- package/go.mod +3 -3
- package/go.sum +16 -0
- package/gs/builtin/hostio.test.ts +16 -0
- package/gs/builtin/hostio.ts +7 -0
- package/gs/builtin/runtime-contract.test.ts +246 -21
- package/gs/builtin/slice.ts +269 -24
- package/gs/builtin/type.ts +226 -59
- package/gs/builtin/varRef.ts +85 -2
- package/gs/bytes/buffer.gs.ts +1 -1
- package/gs/bytes/reader.gs.ts +1 -1
- package/gs/compress/zlib/index.test.ts +62 -1
- package/gs/compress/zlib/index.ts +53 -16
- package/gs/compress/zlib/parity.json +51 -0
- package/gs/encoding/json/index.test.ts +360 -6
- package/gs/encoding/json/index.ts +679 -38
- package/gs/encoding/json/parity.json +81 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +373 -3
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +893 -1
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.test.ts +18 -0
- package/gs/github.com/aperturerobotics/protobuf-go-lite/json/index.ts +17 -11
- package/gs/github.com/pkg/errors/errors.ts +54 -30
- package/gs/go/scanner/index.test.ts +39 -56
- package/gs/go/scanner/index.ts +33 -5
- package/gs/go/scanner/parity.json +27 -0
- package/gs/go/token/index.ts +22 -6
- package/gs/hash/index.test.ts +20 -33
- package/gs/hash/index.ts +28 -0
- package/gs/hash/parity.json +21 -0
- package/gs/internal/byteorder/index.test.ts +2 -2
- package/gs/internal/byteorder/index.ts +2 -2
- package/gs/internal/goarch/index.test.ts +32 -0
- package/gs/internal/goarch/index.ts +45 -13
- package/gs/internal/goarch/parity.json +144 -0
- package/gs/io/fs/fs.ts +26 -14
- package/gs/io/fs/readdir.ts +4 -4
- package/gs/io/fs/sub.ts +8 -1
- package/gs/io/io.ts +1 -0
- package/gs/io/parity.json +162 -0
- package/gs/math/bits/index.test.ts +14 -1
- package/gs/math/bits/index.ts +23 -4
- package/gs/math/bits/parity.json +156 -0
- package/gs/mime/index.test.ts +90 -0
- package/gs/mime/index.ts +369 -6
- package/gs/mime/parity.json +36 -0
- package/gs/net/http/httptest/index.test.ts +98 -2
- package/gs/net/http/httptest/index.ts +101 -6
- package/gs/net/http/httptest/parity.json +15 -0
- package/gs/net/http/index.test.ts +781 -12
- package/gs/net/http/index.ts +1860 -139
- package/gs/net/http/meta.json +16 -1
- package/gs/net/http/parity.json +193 -0
- package/gs/os/dir_unix.gs.ts +1 -1
- package/gs/os/error.gs.ts +1 -1
- package/gs/os/exec.gs.ts +4 -8
- package/gs/os/exec_posix.gs.ts +1 -1
- package/gs/os/index.test.ts +9 -0
- package/gs/os/index.ts +1 -0
- package/gs/os/parity.json +9 -0
- package/gs/os/proc.gs.ts +18 -5
- package/gs/os/proc.test.ts +26 -0
- package/gs/os/root_js.gs.ts +1 -1
- package/gs/os/types.gs.ts +1 -1
- package/gs/os/types_js.gs.ts +1 -1
- package/gs/os/types_unix.gs.ts +1 -1
- package/gs/path/path.ts +11 -7
- package/gs/reflect/field.test.ts +37 -15
- package/gs/reflect/function-types.test.ts +518 -22
- package/gs/reflect/index.ts +8 -6
- package/gs/reflect/map.ts +20 -0
- package/gs/reflect/meta.json +6 -4
- package/gs/reflect/parity.json +234 -0
- package/gs/reflect/sliceat.test.ts +156 -0
- package/gs/reflect/structof.test.ts +401 -0
- package/gs/reflect/type.ts +1961 -317
- package/gs/reflect/typefor.test.ts +530 -10
- package/gs/reflect/types.ts +43 -18
- package/gs/reflect/value.ts +105 -45
- package/gs/reflect/visiblefields.ts +5 -168
- package/gs/runtime/parity.json +24 -0
- package/gs/runtime/pprof/index.test.ts +29 -7
- package/gs/runtime/pprof/index.ts +56 -30
- package/gs/runtime/pprof/parity.json +27 -0
- package/gs/runtime/runtime.test.ts +3 -1
- package/gs/runtime/runtime.ts +4 -3
- package/gs/runtime/trace/index.test.ts +5 -3
- package/gs/runtime/trace/index.ts +8 -20
- package/gs/runtime/trace/parity.json +36 -0
- package/gs/strconv/atoi.gs.ts +1 -1
- package/gs/strconv/complex.gs.ts +174 -0
- package/gs/strconv/complex.test.ts +65 -0
- package/gs/strconv/index.ts +1 -0
- package/gs/strconv/parity.json +120 -0
- package/gs/strings/builder.ts +1 -1
- package/gs/strings/parity.json +186 -0
- package/gs/strings/reader.ts +9 -5
- package/gs/strings/replace.ts +15 -7
- package/gs/strings/strings.test.ts +22 -2
- package/gs/strings/strings.ts +64 -6
- package/gs/sync/atomic/doc_64.gs.ts +6 -7
- package/gs/sync/atomic/doc_64.test.ts +43 -0
- package/gs/sync/atomic/type.gs.ts +9 -9
- package/gs/sync/atomic/value.gs.ts +2 -2
- package/gs/syscall/env.ts +29 -14
- package/gs/testing/testing.test.ts +67 -0
- package/gs/testing/testing.ts +87 -19
- package/gs/time/parity.json +225 -0
- package/gs/time/time.test.ts +20 -2
- package/gs/time/time.ts +49 -7
- package/gs/unique/index.ts +7 -1
- package/package.json +4 -2
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.d.ts +0 -217
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js +0 -926
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
- package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -38
- package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1361
- package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
package/gs/net/http/index.ts
CHANGED
|
@@ -4,70 +4,225 @@ import * as context from '@goscript/context/index.js'
|
|
|
4
4
|
import * as errors from '@goscript/errors/index.js'
|
|
5
5
|
import * as fs from '@goscript/io/fs/fs.js'
|
|
6
6
|
import * as io from '@goscript/io/index.js'
|
|
7
|
+
import * as strings from '@goscript/strings/index.js'
|
|
7
8
|
import * as time from '@goscript/time/index.js'
|
|
8
9
|
|
|
10
|
+
export const StatusContinue = 100
|
|
11
|
+
export const StatusSwitchingProtocols = 101
|
|
12
|
+
export const StatusProcessing = 102
|
|
13
|
+
export const StatusEarlyHints = 103
|
|
9
14
|
export const StatusOK = 200
|
|
10
15
|
export const StatusCreated = 201
|
|
16
|
+
export const StatusAccepted = 202
|
|
17
|
+
export const StatusNonAuthoritativeInfo = 203
|
|
18
|
+
export const StatusNoContent = 204
|
|
19
|
+
export const StatusResetContent = 205
|
|
11
20
|
export const StatusPartialContent = 206
|
|
21
|
+
export const StatusMultiStatus = 207
|
|
22
|
+
export const StatusAlreadyReported = 208
|
|
23
|
+
export const StatusIMUsed = 226
|
|
24
|
+
export const StatusMultipleChoices = 300
|
|
12
25
|
export const StatusMovedPermanently = 301
|
|
26
|
+
export const StatusFound = 302
|
|
27
|
+
export const StatusSeeOther = 303
|
|
28
|
+
export const StatusNotModified = 304
|
|
29
|
+
export const StatusUseProxy = 305
|
|
30
|
+
export const StatusTemporaryRedirect = 307
|
|
31
|
+
export const StatusPermanentRedirect = 308
|
|
13
32
|
export const StatusBadRequest = 400
|
|
14
33
|
export const StatusUnauthorized = 401
|
|
34
|
+
export const StatusPaymentRequired = 402
|
|
15
35
|
export const StatusForbidden = 403
|
|
36
|
+
export const StatusNotFound = 404
|
|
16
37
|
export const StatusMethodNotAllowed = 405
|
|
38
|
+
export const StatusNotAcceptable = 406
|
|
39
|
+
export const StatusProxyAuthRequired = 407
|
|
17
40
|
export const StatusRequestTimeout = 408
|
|
18
41
|
export const StatusConflict = 409
|
|
19
|
-
export const
|
|
42
|
+
export const StatusGone = 410
|
|
43
|
+
export const StatusLengthRequired = 411
|
|
44
|
+
export const StatusPreconditionFailed = 412
|
|
45
|
+
export const StatusRequestEntityTooLarge = 413
|
|
46
|
+
export const StatusRequestURITooLong = 414
|
|
20
47
|
export const StatusUnsupportedMediaType = 415
|
|
48
|
+
export const StatusRequestedRangeNotSatisfiable = 416
|
|
49
|
+
export const StatusExpectationFailed = 417
|
|
21
50
|
export const StatusTeapot = 418
|
|
51
|
+
export const StatusMisdirectedRequest = 421
|
|
52
|
+
export const StatusUnprocessableEntity = 422
|
|
53
|
+
export const StatusLocked = 423
|
|
54
|
+
export const StatusFailedDependency = 424
|
|
55
|
+
export const StatusTooEarly = 425
|
|
56
|
+
export const StatusUpgradeRequired = 426
|
|
57
|
+
export const StatusPreconditionRequired = 428
|
|
22
58
|
export const StatusTooManyRequests = 429
|
|
23
|
-
export const
|
|
59
|
+
export const StatusRequestHeaderFieldsTooLarge = 431
|
|
60
|
+
export const StatusUnavailableForLegalReasons = 451
|
|
24
61
|
export const StatusInternalServerError = 500
|
|
62
|
+
export const StatusNotImplemented = 501
|
|
63
|
+
export const StatusBadGateway = 502
|
|
25
64
|
export const StatusServiceUnavailable = 503
|
|
65
|
+
export const StatusGatewayTimeout = 504
|
|
66
|
+
export const StatusHTTPVersionNotSupported = 505
|
|
67
|
+
export const StatusVariantAlsoNegotiates = 506
|
|
68
|
+
export const StatusInsufficientStorage = 507
|
|
69
|
+
export const StatusLoopDetected = 508
|
|
70
|
+
export const StatusNotExtended = 510
|
|
71
|
+
export const StatusNetworkAuthenticationRequired = 511
|
|
26
72
|
|
|
27
73
|
export const MethodGet = 'GET'
|
|
74
|
+
export const MethodHead = 'HEAD'
|
|
28
75
|
export const MethodPost = 'POST'
|
|
76
|
+
export const MethodPut = 'PUT'
|
|
77
|
+
export const MethodPatch = 'PATCH'
|
|
29
78
|
export const MethodDelete = 'DELETE'
|
|
79
|
+
export const MethodConnect = 'CONNECT'
|
|
80
|
+
export const MethodOptions = 'OPTIONS'
|
|
81
|
+
export const MethodTrace = 'TRACE'
|
|
82
|
+
|
|
83
|
+
export const DefaultMaxHeaderBytes = 1 << 20
|
|
84
|
+
export const DefaultMaxIdleConnsPerHost = 2
|
|
85
|
+
export const TimeFormat = 'Mon, 02 Jan 2006 15:04:05 GMT'
|
|
86
|
+
export const TrailerPrefix = 'Trailer:'
|
|
87
|
+
|
|
88
|
+
export class ProtocolError {
|
|
89
|
+
public ErrorString: string
|
|
90
|
+
|
|
91
|
+
constructor(errorString: string) {
|
|
92
|
+
this.ErrorString = errorString
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public Error(): string {
|
|
96
|
+
return this.ErrorString
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export class MaxBytesError {
|
|
101
|
+
public Limit: number
|
|
102
|
+
|
|
103
|
+
constructor(init?: Partial<MaxBytesError>) {
|
|
104
|
+
this.Limit = init?.Limit ?? 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
public Error(): string {
|
|
108
|
+
return 'http: request body too large'
|
|
109
|
+
}
|
|
110
|
+
}
|
|
30
111
|
|
|
31
|
-
export const ErrNotSupported =
|
|
112
|
+
export const ErrNotSupported = new ProtocolError('feature not supported')
|
|
113
|
+
export const ErrUnexpectedTrailer = new ProtocolError('trailer header without chunked transfer encoding')
|
|
114
|
+
export const ErrMissingBoundary = new ProtocolError('no multipart boundary param in Content-Type')
|
|
115
|
+
export const ErrNotMultipart = new ProtocolError("request Content-Type isn't multipart/form-data")
|
|
116
|
+
export const ErrHeaderTooLong = new ProtocolError('header too long')
|
|
117
|
+
export const ErrShortBody = new ProtocolError('entity body too short')
|
|
118
|
+
export const ErrMissingContentLength = new ProtocolError('missing ContentLength in HEAD response')
|
|
119
|
+
export const ErrBodyNotAllowed = errors.New('http: request method or response status code does not allow body')
|
|
120
|
+
export const ErrBodyReadAfterClose = errors.New('http: invalid Read on closed Body')
|
|
121
|
+
export const ErrContentLength = errors.New('http: wrote more than the declared Content-Length')
|
|
122
|
+
export const ErrHandlerTimeout = errors.New('http: Handler timeout')
|
|
123
|
+
export const ErrHijacked = errors.New('http: connection has been hijacked')
|
|
124
|
+
export const ErrLineTooLong = errors.New('header line too long')
|
|
125
|
+
export const ErrMissingFile = errors.New('http: no such file')
|
|
126
|
+
export const ErrNoCookie = errors.New('http: named cookie not present')
|
|
127
|
+
export const ErrNoLocation = errors.New('http: no Location header in response')
|
|
128
|
+
export const ErrSchemeMismatch = errors.New('http: server gave HTTP response to HTTPS client')
|
|
129
|
+
export const ErrServerClosed = errors.New('http: Server closed')
|
|
130
|
+
export const ErrAbortHandler = errors.New('net/http: abort Handler')
|
|
131
|
+
export const ErrSkipAltProtocol = errors.New('net/http: skip alternate protocol')
|
|
132
|
+
export const ErrUseLastResponse = errors.New('net/http: use last response')
|
|
133
|
+
export const ErrWriteAfterFlush = errors.New('unused')
|
|
134
|
+
const errBlankCookie = errors.New('http: blank cookie')
|
|
135
|
+
const errEqualNotFoundInCookie = errors.New("http: '=' not found in cookie")
|
|
136
|
+
const errInvalidCookieName = errors.New('http: invalid cookie name')
|
|
137
|
+
const errInvalidCookieValue = errors.New('http: invalid cookie value')
|
|
138
|
+
const errCookieNumLimitExceeded = errors.New('http: number of cookies exceeded limit')
|
|
139
|
+
const errCrossOriginRequest = errors.New('cross-origin request detected from Sec-Fetch-Site header')
|
|
140
|
+
const errCrossOriginRequestFromOldBrowser = errors.New(
|
|
141
|
+
'cross-origin request detected, and/or browser is out of date: Sec-Fetch-Site is missing, and Origin does not match Host',
|
|
142
|
+
)
|
|
32
143
|
export const ServerContextKey = Symbol('net/http ServerContextKey')
|
|
144
|
+
export const LocalAddrContextKey = Symbol('net/http LocalAddrContextKey')
|
|
145
|
+
|
|
146
|
+
export type SameSite = number
|
|
147
|
+
export const SameSiteDefaultMode = 1
|
|
148
|
+
export const SameSiteLaxMode = 2
|
|
149
|
+
export const SameSiteStrictMode = 3
|
|
150
|
+
export const SameSiteNoneMode = 4
|
|
151
|
+
|
|
152
|
+
export type ConnState = number
|
|
153
|
+
export const StateNew = 0
|
|
154
|
+
export const StateActive = 1
|
|
155
|
+
export const StateIdle = 2
|
|
156
|
+
export const StateHijacked = 3
|
|
157
|
+
export const StateClosed = 4
|
|
158
|
+
|
|
159
|
+
const statusTexts = new Map<number, string>([
|
|
160
|
+
[StatusContinue, 'Continue'],
|
|
161
|
+
[StatusSwitchingProtocols, 'Switching Protocols'],
|
|
162
|
+
[StatusProcessing, 'Processing'],
|
|
163
|
+
[StatusEarlyHints, 'Early Hints'],
|
|
164
|
+
[StatusOK, 'OK'],
|
|
165
|
+
[StatusCreated, 'Created'],
|
|
166
|
+
[StatusAccepted, 'Accepted'],
|
|
167
|
+
[StatusNonAuthoritativeInfo, 'Non-Authoritative Information'],
|
|
168
|
+
[StatusNoContent, 'No Content'],
|
|
169
|
+
[StatusResetContent, 'Reset Content'],
|
|
170
|
+
[StatusPartialContent, 'Partial Content'],
|
|
171
|
+
[StatusMultiStatus, 'Multi-Status'],
|
|
172
|
+
[StatusAlreadyReported, 'Already Reported'],
|
|
173
|
+
[StatusIMUsed, 'IM Used'],
|
|
174
|
+
[StatusMultipleChoices, 'Multiple Choices'],
|
|
175
|
+
[StatusMovedPermanently, 'Moved Permanently'],
|
|
176
|
+
[StatusFound, 'Found'],
|
|
177
|
+
[StatusSeeOther, 'See Other'],
|
|
178
|
+
[StatusNotModified, 'Not Modified'],
|
|
179
|
+
[StatusUseProxy, 'Use Proxy'],
|
|
180
|
+
[StatusTemporaryRedirect, 'Temporary Redirect'],
|
|
181
|
+
[StatusPermanentRedirect, 'Permanent Redirect'],
|
|
182
|
+
[StatusBadRequest, 'Bad Request'],
|
|
183
|
+
[StatusUnauthorized, 'Unauthorized'],
|
|
184
|
+
[StatusPaymentRequired, 'Payment Required'],
|
|
185
|
+
[StatusForbidden, 'Forbidden'],
|
|
186
|
+
[StatusNotFound, 'Not Found'],
|
|
187
|
+
[StatusMethodNotAllowed, 'Method Not Allowed'],
|
|
188
|
+
[StatusNotAcceptable, 'Not Acceptable'],
|
|
189
|
+
[StatusProxyAuthRequired, 'Proxy Authentication Required'],
|
|
190
|
+
[StatusRequestTimeout, 'Request Timeout'],
|
|
191
|
+
[StatusConflict, 'Conflict'],
|
|
192
|
+
[StatusGone, 'Gone'],
|
|
193
|
+
[StatusLengthRequired, 'Length Required'],
|
|
194
|
+
[StatusPreconditionFailed, 'Precondition Failed'],
|
|
195
|
+
[StatusRequestEntityTooLarge, 'Request Entity Too Large'],
|
|
196
|
+
[StatusRequestURITooLong, 'Request URI Too Long'],
|
|
197
|
+
[StatusUnsupportedMediaType, 'Unsupported Media Type'],
|
|
198
|
+
[StatusRequestedRangeNotSatisfiable, 'Requested Range Not Satisfiable'],
|
|
199
|
+
[StatusExpectationFailed, 'Expectation Failed'],
|
|
200
|
+
[StatusTeapot, "I'm a teapot"],
|
|
201
|
+
[StatusMisdirectedRequest, 'Misdirected Request'],
|
|
202
|
+
[StatusUnprocessableEntity, 'Unprocessable Entity'],
|
|
203
|
+
[StatusLocked, 'Locked'],
|
|
204
|
+
[StatusFailedDependency, 'Failed Dependency'],
|
|
205
|
+
[StatusTooEarly, 'Too Early'],
|
|
206
|
+
[StatusUpgradeRequired, 'Upgrade Required'],
|
|
207
|
+
[StatusPreconditionRequired, 'Precondition Required'],
|
|
208
|
+
[StatusTooManyRequests, 'Too Many Requests'],
|
|
209
|
+
[StatusRequestHeaderFieldsTooLarge, 'Request Header Fields Too Large'],
|
|
210
|
+
[StatusUnavailableForLegalReasons, 'Unavailable For Legal Reasons'],
|
|
211
|
+
[StatusInternalServerError, 'Internal Server Error'],
|
|
212
|
+
[StatusNotImplemented, 'Not Implemented'],
|
|
213
|
+
[StatusBadGateway, 'Bad Gateway'],
|
|
214
|
+
[StatusServiceUnavailable, 'Service Unavailable'],
|
|
215
|
+
[StatusGatewayTimeout, 'Gateway Timeout'],
|
|
216
|
+
[StatusHTTPVersionNotSupported, 'HTTP Version Not Supported'],
|
|
217
|
+
[StatusVariantAlsoNegotiates, 'Variant Also Negotiates'],
|
|
218
|
+
[StatusInsufficientStorage, 'Insufficient Storage'],
|
|
219
|
+
[StatusLoopDetected, 'Loop Detected'],
|
|
220
|
+
[StatusNotExtended, 'Not Extended'],
|
|
221
|
+
[StatusNetworkAuthenticationRequired, 'Network Authentication Required'],
|
|
222
|
+
])
|
|
33
223
|
|
|
34
224
|
export function StatusText(code: number): string {
|
|
35
|
-
|
|
36
|
-
case StatusOK:
|
|
37
|
-
return 'OK'
|
|
38
|
-
case StatusMovedPermanently:
|
|
39
|
-
return 'Moved Permanently'
|
|
40
|
-
case StatusUnauthorized:
|
|
41
|
-
return 'Unauthorized'
|
|
42
|
-
case StatusForbidden:
|
|
43
|
-
return 'Forbidden'
|
|
44
|
-
case StatusMethodNotAllowed:
|
|
45
|
-
return 'Method Not Allowed'
|
|
46
|
-
case StatusBadRequest:
|
|
47
|
-
return 'Bad Request'
|
|
48
|
-
case StatusRequestTimeout:
|
|
49
|
-
return 'Request Timeout'
|
|
50
|
-
case StatusConflict:
|
|
51
|
-
return 'Conflict'
|
|
52
|
-
case StatusNotFound:
|
|
53
|
-
return 'Not Found'
|
|
54
|
-
case StatusUnsupportedMediaType:
|
|
55
|
-
return 'Unsupported Media Type'
|
|
56
|
-
case StatusTooManyRequests:
|
|
57
|
-
return 'Too Many Requests'
|
|
58
|
-
case StatusPartialContent:
|
|
59
|
-
return 'Partial Content'
|
|
60
|
-
case StatusRequestedRangeNotSatisfiable:
|
|
61
|
-
return 'Requested Range Not Satisfiable'
|
|
62
|
-
case StatusTeapot:
|
|
63
|
-
return "I'm a teapot"
|
|
64
|
-
case StatusInternalServerError:
|
|
65
|
-
return 'Internal Server Error'
|
|
66
|
-
case StatusServiceUnavailable:
|
|
67
|
-
return 'Service Unavailable'
|
|
68
|
-
default:
|
|
69
|
-
return ''
|
|
70
|
-
}
|
|
225
|
+
return statusTexts.get(code) ?? ''
|
|
71
226
|
}
|
|
72
227
|
|
|
73
228
|
export type Header = Map<string, $.Slice<string>>
|
|
@@ -76,6 +231,10 @@ export const Header = Map as {
|
|
|
76
231
|
new(entries?: Iterable<readonly [string, $.Slice<string>]> | null): Header
|
|
77
232
|
}
|
|
78
233
|
|
|
234
|
+
export function CanonicalHeaderKey(s: string): string {
|
|
235
|
+
return canonicalMIMEHeaderKey(s)
|
|
236
|
+
}
|
|
237
|
+
|
|
79
238
|
export function Header_Add(h: Header, key: string, value: string): void {
|
|
80
239
|
key = canonicalMIMEHeaderKey(key)
|
|
81
240
|
const values = Array.from(h.get(key) ?? [])
|
|
@@ -96,6 +255,48 @@ export function Header_Set(h: Header, key: string, value: string): void {
|
|
|
96
255
|
h.set(canonicalMIMEHeaderKey(key), $.arrayToSlice([value]))
|
|
97
256
|
}
|
|
98
257
|
|
|
258
|
+
export function Header_Values(h: Header, key: string): $.Slice<string> {
|
|
259
|
+
return h.get(canonicalMIMEHeaderKey(key)) ?? null
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function Header_Clone(h: Header): Header {
|
|
263
|
+
const cloned = new Header()
|
|
264
|
+
for (const [key, values] of h.entries()) {
|
|
265
|
+
cloned.set(key, $.arrayToSlice(Array.from(values ?? [])))
|
|
266
|
+
}
|
|
267
|
+
return cloned
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function Header_Write(h: Header, w: io.Writer): $.GoError {
|
|
271
|
+
return Header_WriteSubset(h, w, null)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
export function Header_WriteSubset(h: Header, w: io.Writer, exclude: Map<string, boolean> | null): $.GoError {
|
|
275
|
+
for (const [key, values] of h.entries()) {
|
|
276
|
+
if (exclude?.get(key) === true) {
|
|
277
|
+
continue
|
|
278
|
+
}
|
|
279
|
+
for (const value of Array.from(values ?? [])) {
|
|
280
|
+
const [, err] = w.Write($.stringToBytes(`${key}: ${value}\r\n`))
|
|
281
|
+
if (err != null) {
|
|
282
|
+
return err
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return null
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export type Dir = string
|
|
290
|
+
|
|
291
|
+
export interface CookieJar {
|
|
292
|
+
SetCookies(u: any, cookies: $.Slice<Cookie | $.VarRef<Cookie> | null>): void
|
|
293
|
+
Cookies(u: any): $.Slice<Cookie | $.VarRef<Cookie> | null>
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export interface CloseNotifier {
|
|
297
|
+
CloseNotify(): any
|
|
298
|
+
}
|
|
299
|
+
|
|
99
300
|
function canonicalMIMEHeaderKey(key: string): string {
|
|
100
301
|
let upper = true
|
|
101
302
|
let out = ''
|
|
@@ -169,18 +370,21 @@ class RequestURL {
|
|
|
169
370
|
}
|
|
170
371
|
}
|
|
171
372
|
|
|
172
|
-
function parseRequestURL(rawURL: string): RequestURL {
|
|
373
|
+
function parseRequestURL(rawURL: string): [RequestURL | null, $.GoError] {
|
|
173
374
|
try {
|
|
375
|
+
if (/%(?![0-9A-Fa-f]{2})/.test(rawURL)) {
|
|
376
|
+
return [null, errors.New(`parse "${rawURL}": invalid URL escape`)]
|
|
377
|
+
}
|
|
174
378
|
const parsed = new URL(rawURL, 'http://goscript.invalid')
|
|
175
379
|
const hasHost = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(rawURL)
|
|
176
|
-
return new RequestURL(
|
|
380
|
+
return [new RequestURL(
|
|
177
381
|
parsed.pathname,
|
|
178
382
|
parsed.search.startsWith('?') ? parsed.search.slice(1) : parsed.search,
|
|
179
383
|
hasHost ? parsed.protocol.replace(/:$/, '') : '',
|
|
180
384
|
hasHost ? parsed.host : '',
|
|
181
|
-
)
|
|
385
|
+
), null]
|
|
182
386
|
} catch {
|
|
183
|
-
return
|
|
387
|
+
return [null, errors.New(`parse "${rawURL}": invalid URL`)]
|
|
184
388
|
}
|
|
185
389
|
}
|
|
186
390
|
|
|
@@ -200,6 +404,136 @@ class responseBody implements io.ReadCloser {
|
|
|
200
404
|
}
|
|
201
405
|
}
|
|
202
406
|
|
|
407
|
+
class noBody implements io.ReadCloser {
|
|
408
|
+
public Read(_p: $.Bytes): [number, $.GoError] {
|
|
409
|
+
return [0, io.EOF]
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
public Close(): $.GoError {
|
|
413
|
+
return null
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
public clone(): noBody {
|
|
417
|
+
return this
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export const NoBody: io.ReadCloser = new noBody()
|
|
422
|
+
|
|
423
|
+
export class Cookie {
|
|
424
|
+
public Name: string
|
|
425
|
+
public Value: string
|
|
426
|
+
public Quoted: boolean
|
|
427
|
+
public Path: string
|
|
428
|
+
public Domain: string
|
|
429
|
+
public Expires: time.Time
|
|
430
|
+
public RawExpires: string
|
|
431
|
+
public MaxAge: number
|
|
432
|
+
public Secure: boolean
|
|
433
|
+
public HttpOnly: boolean
|
|
434
|
+
public SameSite: number
|
|
435
|
+
public Partitioned: boolean
|
|
436
|
+
public Raw: string
|
|
437
|
+
public Unparsed: $.Slice<string>
|
|
438
|
+
|
|
439
|
+
constructor(init?: Partial<Cookie>) {
|
|
440
|
+
this.Name = init?.Name ?? ''
|
|
441
|
+
this.Value = init?.Value ?? ''
|
|
442
|
+
this.Quoted = init?.Quoted ?? false
|
|
443
|
+
this.Path = init?.Path ?? ''
|
|
444
|
+
this.Domain = init?.Domain ?? ''
|
|
445
|
+
this.Expires = init?.Expires ?? new time.Time()
|
|
446
|
+
this.RawExpires = init?.RawExpires ?? ''
|
|
447
|
+
this.MaxAge = init?.MaxAge ?? 0
|
|
448
|
+
this.Secure = init?.Secure ?? false
|
|
449
|
+
this.HttpOnly = init?.HttpOnly ?? false
|
|
450
|
+
this.SameSite = init?.SameSite ?? 0
|
|
451
|
+
this.Partitioned = init?.Partitioned ?? false
|
|
452
|
+
this.Raw = init?.Raw ?? ''
|
|
453
|
+
this.Unparsed = init?.Unparsed ?? null
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
public String(): string {
|
|
457
|
+
const parts = [`${this.Name}=${this.Quoted ? quoteCookieValue(this.Value) : this.Value}`]
|
|
458
|
+
if (this.Path !== '') {
|
|
459
|
+
parts.push(`Path=${this.Path}`)
|
|
460
|
+
}
|
|
461
|
+
if (this.Domain !== '') {
|
|
462
|
+
parts.push(`Domain=${this.Domain}`)
|
|
463
|
+
}
|
|
464
|
+
if (this.MaxAge > 0) {
|
|
465
|
+
parts.push(`Max-Age=${this.MaxAge}`)
|
|
466
|
+
} else if (this.MaxAge < 0) {
|
|
467
|
+
parts.push('Max-Age=0')
|
|
468
|
+
}
|
|
469
|
+
if (this.HttpOnly) {
|
|
470
|
+
parts.push('HttpOnly')
|
|
471
|
+
}
|
|
472
|
+
if (this.Secure) {
|
|
473
|
+
parts.push('Secure')
|
|
474
|
+
}
|
|
475
|
+
switch (this.SameSite) {
|
|
476
|
+
case SameSiteLaxMode:
|
|
477
|
+
parts.push('SameSite=Lax')
|
|
478
|
+
break
|
|
479
|
+
case SameSiteStrictMode:
|
|
480
|
+
parts.push('SameSite=Strict')
|
|
481
|
+
break
|
|
482
|
+
case SameSiteNoneMode:
|
|
483
|
+
parts.push('SameSite=None')
|
|
484
|
+
break
|
|
485
|
+
}
|
|
486
|
+
if (this.Partitioned) {
|
|
487
|
+
parts.push('Partitioned')
|
|
488
|
+
}
|
|
489
|
+
return parts.join('; ')
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function quoteCookieValue(value: string): string {
|
|
494
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function isToken(value: string): boolean {
|
|
498
|
+
return /^[!#$%&'*+\-.^_`{}|~0-9A-Za-z]+$/.test(value)
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function validCookieValueByte(code: number): boolean {
|
|
502
|
+
return code >= 0x20 && code < 0x7f && code !== 0x22 && code !== 0x3b && code !== 0x5c
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function parseCookieValue(raw: string, allowDoubleQuote: boolean): [string, boolean, boolean] {
|
|
506
|
+
let value = raw
|
|
507
|
+
let quoted = false
|
|
508
|
+
if (allowDoubleQuote && value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
|
|
509
|
+
value = value.slice(1, -1)
|
|
510
|
+
quoted = true
|
|
511
|
+
}
|
|
512
|
+
for (let i = 0; i < value.length; i++) {
|
|
513
|
+
if (!validCookieValueByte(value.charCodeAt(i))) {
|
|
514
|
+
return ['', quoted, false]
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
return [value, quoted, true]
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function asciiLower(value: string): [string, boolean] {
|
|
521
|
+
for (let i = 0; i < value.length; i++) {
|
|
522
|
+
if (value.charCodeAt(i) > 0x7f) {
|
|
523
|
+
return ['', false]
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return [value.toLowerCase(), true]
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
export function SetCookie(w: ResponseWriter | null, cookie: Cookie | $.VarRef<Cookie> | null): void {
|
|
530
|
+
const c = $.pointerValue<Cookie | null>(cookie)
|
|
531
|
+
if (w == null || c == null) {
|
|
532
|
+
return
|
|
533
|
+
}
|
|
534
|
+
Header_Add(w.Header(), 'Set-Cookie', c.String())
|
|
535
|
+
}
|
|
536
|
+
|
|
203
537
|
class memoryResponseWriter implements ResponseWriter {
|
|
204
538
|
public Code = StatusOK
|
|
205
539
|
public Body = new bytes.Buffer()
|
|
@@ -244,12 +578,29 @@ export function RegisterInProcessServer(handler: Handler | null): string {
|
|
|
244
578
|
}
|
|
245
579
|
|
|
246
580
|
export function UnregisterInProcessServer(rawURL: string): void {
|
|
247
|
-
const parsed = parseRequestURL(rawURL)
|
|
248
|
-
if (parsed.Host !== '') {
|
|
581
|
+
const [parsed] = parseRequestURL(rawURL)
|
|
582
|
+
if (parsed != null && parsed.Host !== '') {
|
|
249
583
|
inProcessServers.delete(parsed.Host)
|
|
250
584
|
}
|
|
251
585
|
}
|
|
252
586
|
|
|
587
|
+
function inProcessServerRequest(request: Request): Request {
|
|
588
|
+
const req = Object.assign(
|
|
589
|
+
Object.create(Object.getPrototypeOf(request)),
|
|
590
|
+
request,
|
|
591
|
+
) as Request
|
|
592
|
+
const rawQuery = request.URL?.RawQuery ?? ''
|
|
593
|
+
const query = rawQuery === '' ? '' : `?${rawQuery}`
|
|
594
|
+
req.RequestURI = `${request.URL?.Path ?? '/'}${query}`
|
|
595
|
+
req.Host = request.Host === '' ? request.URL?.Host ?? '' : request.Host
|
|
596
|
+
if (req.URL?.clone != null) {
|
|
597
|
+
req.URL = req.URL.clone()
|
|
598
|
+
req.URL.Scheme = ''
|
|
599
|
+
req.URL.Host = ''
|
|
600
|
+
}
|
|
601
|
+
return req
|
|
602
|
+
}
|
|
603
|
+
|
|
253
604
|
export interface ResponseWriter {
|
|
254
605
|
Header(): Header
|
|
255
606
|
Write(p: $.Slice<number>): [number, $.GoError]
|
|
@@ -259,21 +610,49 @@ export interface ResponseWriter {
|
|
|
259
610
|
export class Request {
|
|
260
611
|
public Method: string
|
|
261
612
|
public URL: any
|
|
613
|
+
public Proto: string
|
|
614
|
+
public ProtoMajor: number
|
|
615
|
+
public ProtoMinor: number
|
|
262
616
|
public Body: io.ReadCloser | null
|
|
263
617
|
public Header: Header
|
|
264
618
|
public ContentLength: number
|
|
619
|
+
public TransferEncoding: $.Slice<string>
|
|
620
|
+
public Close: boolean
|
|
621
|
+
public Host: string
|
|
622
|
+
public Form: any
|
|
623
|
+
public PostForm: any
|
|
624
|
+
public MultipartForm: any
|
|
625
|
+
public Trailer: Header
|
|
265
626
|
public RequestURI: string
|
|
266
627
|
public RemoteAddr: string
|
|
628
|
+
public TLS: any
|
|
629
|
+
public Cancel: any
|
|
630
|
+
public Response: Response | $.VarRef<Response> | null
|
|
631
|
+
public Pattern: string
|
|
267
632
|
private ctx: context.Context
|
|
268
633
|
|
|
269
634
|
constructor(init?: Partial<Request> & { ctx?: context.Context }) {
|
|
270
635
|
this.Method = init?.Method ?? ''
|
|
271
636
|
this.URL = init?.URL ?? null
|
|
637
|
+
this.Proto = init?.Proto ?? 'HTTP/1.1'
|
|
638
|
+
this.ProtoMajor = init?.ProtoMajor ?? 1
|
|
639
|
+
this.ProtoMinor = init?.ProtoMinor ?? 1
|
|
272
640
|
this.Body = init?.Body ?? null
|
|
273
641
|
this.Header = init?.Header ?? new Header()
|
|
274
642
|
this.ContentLength = init?.ContentLength ?? 0
|
|
643
|
+
this.TransferEncoding = init?.TransferEncoding ?? null
|
|
644
|
+
this.Close = init?.Close ?? false
|
|
645
|
+
this.Host = init?.Host ?? ''
|
|
646
|
+
this.Form = init?.Form ?? null
|
|
647
|
+
this.PostForm = init?.PostForm ?? null
|
|
648
|
+
this.MultipartForm = init?.MultipartForm ?? null
|
|
649
|
+
this.Trailer = init?.Trailer ?? new Header()
|
|
275
650
|
this.RequestURI = init?.RequestURI ?? ''
|
|
276
651
|
this.RemoteAddr = init?.RemoteAddr ?? ''
|
|
652
|
+
this.TLS = init?.TLS ?? null
|
|
653
|
+
this.Cancel = init?.Cancel ?? null
|
|
654
|
+
this.Response = init?.Response ?? null
|
|
655
|
+
this.Pattern = init?.Pattern ?? ''
|
|
277
656
|
this.ctx = (init as { ctx?: context.Context } | undefined)?.ctx ?? context.Background()
|
|
278
657
|
}
|
|
279
658
|
|
|
@@ -289,11 +668,25 @@ export class Request {
|
|
|
289
668
|
return new Request({
|
|
290
669
|
Method: this.Method,
|
|
291
670
|
URL: this.URL?.clone != null ? this.URL.clone() : this.URL == null ? null : { ...this.URL },
|
|
671
|
+
Proto: this.Proto,
|
|
672
|
+
ProtoMajor: this.ProtoMajor,
|
|
673
|
+
ProtoMinor: this.ProtoMinor,
|
|
292
674
|
Body: this.Body,
|
|
293
|
-
Header: this.Header,
|
|
675
|
+
Header: Header_Clone(this.Header),
|
|
294
676
|
ContentLength: this.ContentLength,
|
|
677
|
+
TransferEncoding: this.TransferEncoding,
|
|
678
|
+
Close: this.Close,
|
|
679
|
+
Host: this.Host,
|
|
680
|
+
Form: this.Form,
|
|
681
|
+
PostForm: this.PostForm,
|
|
682
|
+
MultipartForm: this.MultipartForm,
|
|
683
|
+
Trailer: Header_Clone(this.Trailer),
|
|
295
684
|
RequestURI: this.RequestURI,
|
|
296
685
|
RemoteAddr: this.RemoteAddr,
|
|
686
|
+
TLS: this.TLS,
|
|
687
|
+
Cancel: this.Cancel,
|
|
688
|
+
Response: this.Response,
|
|
689
|
+
Pattern: this.Pattern,
|
|
297
690
|
ctx,
|
|
298
691
|
})
|
|
299
692
|
}
|
|
@@ -302,6 +695,57 @@ export class Request {
|
|
|
302
695
|
return Header_Get(this.Header, 'User-Agent')
|
|
303
696
|
}
|
|
304
697
|
|
|
698
|
+
public Referer(): string {
|
|
699
|
+
return Header_Get(this.Header, 'Referer')
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
public ProtoAtLeast(major: number, minor: number): boolean {
|
|
703
|
+
return this.ProtoMajor > major || (this.ProtoMajor === major && this.ProtoMinor >= minor)
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
public Cookie(name: string): [Cookie | null, $.GoError] {
|
|
707
|
+
for (const cookie of Array.from(this.Cookies() ?? [])) {
|
|
708
|
+
if (cookie?.Name === name) {
|
|
709
|
+
return [cookie, null]
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
return [null, ErrNoCookie]
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
public Cookies(): $.Slice<Cookie | null> {
|
|
716
|
+
const raw = Header_Get(this.Header, 'Cookie')
|
|
717
|
+
if (raw === '') {
|
|
718
|
+
return null
|
|
719
|
+
}
|
|
720
|
+
const [cookies] = ParseCookie(raw)
|
|
721
|
+
return cookies
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
public AddCookie(cookie: Cookie | $.VarRef<Cookie> | null): void {
|
|
725
|
+
const c = $.pointerValue<Cookie | null>(cookie)
|
|
726
|
+
if (c != null) {
|
|
727
|
+
Header_Add(this.Header, 'Cookie', c.String())
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
public SetBasicAuth(username: string, password: string): void {
|
|
732
|
+
const encoded = globalThis.btoa(`${username}:${password}`)
|
|
733
|
+
Header_Set(this.Header, 'Authorization', `Basic ${encoded}`)
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
public BasicAuth(): [string, string, boolean] {
|
|
737
|
+
const value = Header_Get(this.Header, 'Authorization')
|
|
738
|
+
if (!value.startsWith('Basic ')) {
|
|
739
|
+
return ['', '', false]
|
|
740
|
+
}
|
|
741
|
+
const decoded = globalThis.atob(value.slice('Basic '.length))
|
|
742
|
+
const idx = decoded.indexOf(':')
|
|
743
|
+
if (idx < 0) {
|
|
744
|
+
return ['', '', false]
|
|
745
|
+
}
|
|
746
|
+
return [decoded.slice(0, idx), decoded.slice(idx + 1), true]
|
|
747
|
+
}
|
|
748
|
+
|
|
305
749
|
public FormValue(key: string): string {
|
|
306
750
|
const query = this.URL?.Query
|
|
307
751
|
return typeof query === 'function' ? query.call(this.URL).Get(key) : ''
|
|
@@ -311,18 +755,34 @@ export class Request {
|
|
|
311
755
|
export class Response {
|
|
312
756
|
public Status: string
|
|
313
757
|
public StatusCode: number
|
|
758
|
+
public Proto: string
|
|
759
|
+
public ProtoMajor: number
|
|
760
|
+
public ProtoMinor: number
|
|
314
761
|
public Body: io.ReadCloser | null
|
|
315
762
|
public Header: Header
|
|
316
763
|
public ContentLength: number
|
|
764
|
+
public TransferEncoding: $.Slice<string>
|
|
765
|
+
public Close: boolean
|
|
766
|
+
public Uncompressed: boolean
|
|
767
|
+
public Trailer: Header
|
|
317
768
|
public Request: Request | $.VarRef<Request> | null
|
|
769
|
+
public TLS: any
|
|
318
770
|
|
|
319
771
|
constructor(init?: Partial<Response>) {
|
|
320
772
|
this.Status = init?.Status ?? ''
|
|
321
773
|
this.StatusCode = init?.StatusCode ?? 0
|
|
774
|
+
this.Proto = init?.Proto ?? 'HTTP/1.1'
|
|
775
|
+
this.ProtoMajor = init?.ProtoMajor ?? 1
|
|
776
|
+
this.ProtoMinor = init?.ProtoMinor ?? 1
|
|
322
777
|
this.Body = init?.Body ?? null
|
|
323
778
|
this.Header = init?.Header ?? new Header()
|
|
324
779
|
this.ContentLength = init?.ContentLength ?? 0
|
|
780
|
+
this.TransferEncoding = init?.TransferEncoding ?? null
|
|
781
|
+
this.Close = init?.Close ?? false
|
|
782
|
+
this.Uncompressed = init?.Uncompressed ?? false
|
|
783
|
+
this.Trailer = init?.Trailer ?? new Header()
|
|
325
784
|
this.Request = init?.Request ?? null
|
|
785
|
+
this.TLS = init?.TLS ?? null
|
|
326
786
|
if (this.Status === '' && this.StatusCode !== 0) {
|
|
327
787
|
const text = StatusText(this.StatusCode)
|
|
328
788
|
this.Status = text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`
|
|
@@ -335,10 +795,90 @@ export class Response {
|
|
|
335
795
|
Header: this.Header,
|
|
336
796
|
Status: this.Status,
|
|
337
797
|
StatusCode: this.StatusCode,
|
|
798
|
+
Proto: this.Proto,
|
|
799
|
+
ProtoMajor: this.ProtoMajor,
|
|
800
|
+
ProtoMinor: this.ProtoMinor,
|
|
338
801
|
ContentLength: this.ContentLength,
|
|
802
|
+
TransferEncoding: this.TransferEncoding,
|
|
803
|
+
Close: this.Close,
|
|
804
|
+
Uncompressed: this.Uncompressed,
|
|
805
|
+
Trailer: this.Trailer,
|
|
339
806
|
Request: this.Request,
|
|
807
|
+
TLS: this.TLS,
|
|
340
808
|
})
|
|
341
809
|
}
|
|
810
|
+
|
|
811
|
+
public Cookies(): $.Slice<Cookie | null> {
|
|
812
|
+
const values = Header_Values(this.Header, 'Set-Cookie')
|
|
813
|
+
if (values == null) {
|
|
814
|
+
return null
|
|
815
|
+
}
|
|
816
|
+
const cookies: Array<Cookie | null> = []
|
|
817
|
+
for (const value of Array.from(values)) {
|
|
818
|
+
const [cookie] = ParseSetCookie(String(value))
|
|
819
|
+
if (cookie != null) {
|
|
820
|
+
cookies.push(cookie)
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return $.arrayToSlice(cookies)
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
public Location(): [any, $.GoError] {
|
|
827
|
+
const location = Header_Get(this.Header, 'Location')
|
|
828
|
+
if (location === '') {
|
|
829
|
+
return [null, ErrNoLocation]
|
|
830
|
+
}
|
|
831
|
+
try {
|
|
832
|
+
return [new URL(location), null]
|
|
833
|
+
} catch (err) {
|
|
834
|
+
return [null, errors.New(String(err))]
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
public ProtoAtLeast(major: number, minor: number): boolean {
|
|
839
|
+
return this.ProtoMajor > major || (this.ProtoMajor === major && this.ProtoMinor >= minor)
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
public Write(w: io.Writer): $.GoError {
|
|
843
|
+
const write = (data: $.Bytes): $.GoError => {
|
|
844
|
+
const [n, err] = w.Write(data)
|
|
845
|
+
if (err != null) {
|
|
846
|
+
return err
|
|
847
|
+
}
|
|
848
|
+
return n === $.len(data) ? null : io.ErrShortWrite
|
|
849
|
+
}
|
|
850
|
+
let err = write($.stringToBytes(`${this.Proto} ${this.Status}\r\n`))
|
|
851
|
+
if (err != null) {
|
|
852
|
+
return err
|
|
853
|
+
}
|
|
854
|
+
err = Header_Write(this.Header, w)
|
|
855
|
+
if (err != null) {
|
|
856
|
+
return err
|
|
857
|
+
}
|
|
858
|
+
err = write($.stringToBytes('\r\n'))
|
|
859
|
+
if (err != null) {
|
|
860
|
+
return err
|
|
861
|
+
}
|
|
862
|
+
if (this.Body == null) {
|
|
863
|
+
return null
|
|
864
|
+
}
|
|
865
|
+
const buf = $.makeSlice<number>(32 * 1024, undefined, 'byte')
|
|
866
|
+
while (true) {
|
|
867
|
+
const [n, readErr] = this.Body.Read(buf)
|
|
868
|
+
if (n > 0) {
|
|
869
|
+
err = write($.goSlice(buf, 0, n))
|
|
870
|
+
if (err != null) {
|
|
871
|
+
return err
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
if (readErr === io.EOF) {
|
|
875
|
+
return null
|
|
876
|
+
}
|
|
877
|
+
if (readErr != null) {
|
|
878
|
+
return readErr
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
342
882
|
}
|
|
343
883
|
|
|
344
884
|
export class Client {
|
|
@@ -353,133 +893,820 @@ export class Client {
|
|
|
353
893
|
): Promise<[Response | null, $.GoError]> {
|
|
354
894
|
return await (this.Transport ?? DefaultTransport).RoundTrip(_req)
|
|
355
895
|
}
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
export const DefaultClient = new Client()
|
|
359
|
-
|
|
360
|
-
export interface RoundTripper {
|
|
361
|
-
RoundTrip(req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
|
|
362
|
-
}
|
|
363
896
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
return [null, errors.New('net/http: nil Request')]
|
|
897
|
+
public async Get(url: string): Promise<[Response | null, $.GoError]> {
|
|
898
|
+
const [req, err] = NewRequest(MethodGet, url, null)
|
|
899
|
+
if (err != null) {
|
|
900
|
+
return [null, err]
|
|
369
901
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
902
|
+
return await this.Do(req)
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
public async Head(url: string): Promise<[Response | null, $.GoError]> {
|
|
906
|
+
const [req, err] = NewRequest(MethodHead, url, null)
|
|
907
|
+
if (err != null) {
|
|
908
|
+
return [null, err]
|
|
374
909
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
910
|
+
return await this.Do(req)
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
public async Post(url: string, contentType: string, body: io.Reader | null): Promise<[Response | null, $.GoError]> {
|
|
914
|
+
const [req, err] = NewRequest(MethodPost, url, body)
|
|
915
|
+
if (err != null || req == null) {
|
|
916
|
+
return [null, err]
|
|
379
917
|
}
|
|
380
|
-
|
|
918
|
+
Header_Set(req.Header, 'Content-Type', contentType)
|
|
919
|
+
return await this.Do(req)
|
|
381
920
|
}
|
|
382
|
-
}
|
|
383
921
|
|
|
384
|
-
|
|
922
|
+
public async PostForm(url: string, data: any): Promise<[Response | null, $.GoError]> {
|
|
923
|
+
return await this.Post(url, 'application/x-www-form-urlencoded', bytes.NewReader($.stringToBytes(encodeFormData(data))))
|
|
924
|
+
}
|
|
385
925
|
|
|
386
|
-
|
|
387
|
-
|
|
926
|
+
public CloseIdleConnections(): void {
|
|
927
|
+
const closer = this.Transport as { CloseIdleConnections?: () => void } | null
|
|
928
|
+
closer?.CloseIdleConnections?.()
|
|
929
|
+
}
|
|
388
930
|
}
|
|
389
931
|
|
|
390
|
-
export
|
|
391
|
-
|
|
392
|
-
|
|
932
|
+
export const DefaultClient = new Client()
|
|
933
|
+
|
|
934
|
+
export class ClientConn {}
|
|
935
|
+
|
|
936
|
+
function encodeFormData(data: any): string {
|
|
937
|
+
if (data == null) {
|
|
938
|
+
return ''
|
|
939
|
+
}
|
|
940
|
+
if (typeof data.Encode === 'function') {
|
|
941
|
+
return String(data.Encode())
|
|
942
|
+
}
|
|
943
|
+
if (data instanceof URLSearchParams) {
|
|
944
|
+
return data.toString()
|
|
945
|
+
}
|
|
946
|
+
const entries =
|
|
947
|
+
data instanceof Map ?
|
|
948
|
+
Array.from(data.entries())
|
|
949
|
+
: typeof data === 'object' ?
|
|
950
|
+
Object.entries(data)
|
|
951
|
+
: []
|
|
952
|
+
entries.sort(([a], [b]) => String(a).localeCompare(String(b)))
|
|
953
|
+
const params = new URLSearchParams()
|
|
954
|
+
for (const [key, value] of entries) {
|
|
955
|
+
appendFormValue(params, String(key), value)
|
|
956
|
+
}
|
|
957
|
+
return params.toString()
|
|
393
958
|
}
|
|
394
959
|
|
|
395
|
-
|
|
396
|
-
|
|
960
|
+
function appendFormValue(params: URLSearchParams, key: string, value: unknown): void {
|
|
961
|
+
const unwrapped = unwrapFormValue(value)
|
|
962
|
+
if (unwrapped == null) {
|
|
963
|
+
return
|
|
964
|
+
}
|
|
965
|
+
if (Array.isArray(unwrapped)) {
|
|
966
|
+
for (const item of unwrapped) {
|
|
967
|
+
appendFormValue(params, key, item)
|
|
968
|
+
}
|
|
969
|
+
return
|
|
970
|
+
}
|
|
971
|
+
params.append(key, String(unwrapped))
|
|
397
972
|
}
|
|
398
973
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
974
|
+
function unwrapFormValue(value: unknown): unknown {
|
|
975
|
+
if ($.isVarRef(value)) {
|
|
976
|
+
return value.value
|
|
977
|
+
}
|
|
978
|
+
if (typeof value === 'object' && value !== null && '__goValue' in value) {
|
|
979
|
+
return (value as { __goValue: unknown }).__goValue
|
|
980
|
+
}
|
|
981
|
+
return value
|
|
982
|
+
}
|
|
403
983
|
|
|
404
|
-
export
|
|
405
|
-
|
|
406
|
-
w: ResponseWriter | null,
|
|
407
|
-
r: Request | $.VarRef<Request> | null,
|
|
408
|
-
): void | Promise<void> {
|
|
409
|
-
return h(w, r)
|
|
984
|
+
export interface RoundTripper {
|
|
985
|
+
RoundTrip(req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
|
|
410
986
|
}
|
|
411
987
|
|
|
412
|
-
export class
|
|
413
|
-
|
|
414
|
-
public BaseContext: ((listener: any) => context.Context) | null
|
|
415
|
-
public Handler: Handler | null
|
|
416
|
-
public ReadHeaderTimeout: number
|
|
417
|
-
public WriteTimeout: number
|
|
988
|
+
export class Protocols {
|
|
989
|
+
private bits = 0
|
|
418
990
|
|
|
419
|
-
|
|
420
|
-
this.
|
|
421
|
-
this.BaseContext = init?.BaseContext ?? null
|
|
422
|
-
this.Handler = init?.Handler ?? null
|
|
423
|
-
this.ReadHeaderTimeout = init?.ReadHeaderTimeout ?? 0
|
|
424
|
-
this.WriteTimeout = init?.WriteTimeout ?? 0
|
|
991
|
+
public HTTP1(): boolean {
|
|
992
|
+
return (this.bits & 1) !== 0
|
|
425
993
|
}
|
|
426
994
|
|
|
427
|
-
public
|
|
428
|
-
|
|
995
|
+
public SetHTTP1(ok: boolean): void {
|
|
996
|
+
this.setBit(1, ok)
|
|
429
997
|
}
|
|
430
998
|
|
|
431
|
-
public
|
|
432
|
-
return
|
|
999
|
+
public HTTP2(): boolean {
|
|
1000
|
+
return (this.bits & 2) !== 0
|
|
433
1001
|
}
|
|
434
1002
|
|
|
435
|
-
public
|
|
436
|
-
|
|
1003
|
+
public SetHTTP2(ok: boolean): void {
|
|
1004
|
+
this.setBit(2, ok)
|
|
437
1005
|
}
|
|
438
1006
|
|
|
439
|
-
public
|
|
440
|
-
return
|
|
1007
|
+
public UnencryptedHTTP2(): boolean {
|
|
1008
|
+
return (this.bits & 4) !== 0
|
|
441
1009
|
}
|
|
442
|
-
}
|
|
443
1010
|
|
|
444
|
-
|
|
445
|
-
|
|
1011
|
+
public SetUnencryptedHTTP2(ok: boolean): void {
|
|
1012
|
+
this.setBit(4, ok)
|
|
1013
|
+
}
|
|
446
1014
|
|
|
447
|
-
|
|
448
|
-
|
|
1015
|
+
public String(): string {
|
|
1016
|
+
const names: string[] = []
|
|
1017
|
+
if (this.HTTP1()) {
|
|
1018
|
+
names.push('HTTP1')
|
|
1019
|
+
}
|
|
1020
|
+
if (this.HTTP2()) {
|
|
1021
|
+
names.push('HTTP2')
|
|
1022
|
+
}
|
|
1023
|
+
if (this.UnencryptedHTTP2()) {
|
|
1024
|
+
names.push('UnencryptedHTTP2')
|
|
1025
|
+
}
|
|
1026
|
+
return `{${names.join(',')}}`
|
|
449
1027
|
}
|
|
450
|
-
}
|
|
451
1028
|
|
|
452
|
-
|
|
453
|
-
|
|
1029
|
+
private setBit(bit: number, ok: boolean): void {
|
|
1030
|
+
this.bits = ok ? this.bits | bit : this.bits & ~bit
|
|
1031
|
+
}
|
|
454
1032
|
}
|
|
455
1033
|
|
|
456
|
-
export
|
|
457
|
-
|
|
1034
|
+
export class HTTP2Config {
|
|
1035
|
+
public MaxConcurrentStreams = 0
|
|
1036
|
+
public StrictMaxConcurrentRequests = false
|
|
1037
|
+
public MaxDecoderHeaderTableSize = 0
|
|
1038
|
+
public MaxEncoderHeaderTableSize = 0
|
|
1039
|
+
public MaxReadFrameSize = 0
|
|
1040
|
+
public MaxReceiveBufferPerConnection = 0
|
|
1041
|
+
public MaxReceiveBufferPerStream = 0
|
|
1042
|
+
public SendPingTimeout = 0
|
|
1043
|
+
public PingTimeout = 0
|
|
1044
|
+
public WriteByteTimeout = 0
|
|
1045
|
+
public PermitProhibitedCipherSuites = false
|
|
1046
|
+
public CountError: ((errType: string) => void) | null = null
|
|
1047
|
+
|
|
1048
|
+
constructor(init?: Partial<HTTP2Config>) {
|
|
1049
|
+
Object.assign(this, init)
|
|
1050
|
+
}
|
|
458
1051
|
}
|
|
459
1052
|
|
|
460
|
-
export
|
|
461
|
-
|
|
462
|
-
|
|
1053
|
+
export class Transport implements RoundTripper {
|
|
1054
|
+
public Proxy: ((req: Request | $.VarRef<Request> | null) => [any, $.GoError]) | null = null
|
|
1055
|
+
public OnProxyConnectResponse: ((ctx: context.Context, proxyURL: any, connectReq: Request, connectRes: Response) => $.GoError) | null = null
|
|
1056
|
+
public DialContext: any = null
|
|
1057
|
+
public Dial: any = null
|
|
1058
|
+
public DialTLSContext: any = null
|
|
1059
|
+
public DialTLS: any = null
|
|
1060
|
+
public TLSClientConfig: any = null
|
|
1061
|
+
public TLSHandshakeTimeout = 0
|
|
1062
|
+
public DisableKeepAlives = false
|
|
1063
|
+
public DisableCompression = false
|
|
1064
|
+
public MaxIdleConns = 0
|
|
1065
|
+
public MaxIdleConnsPerHost = 0
|
|
1066
|
+
public MaxConnsPerHost = 0
|
|
1067
|
+
public IdleConnTimeout = 0
|
|
1068
|
+
public ResponseHeaderTimeout = 0
|
|
1069
|
+
public ExpectContinueTimeout = 0
|
|
1070
|
+
public TLSNextProto: Map<string, any> | null = null
|
|
1071
|
+
public ProxyConnectHeader = new Header()
|
|
1072
|
+
public GetProxyConnectHeader: ((ctx: context.Context, proxyURL: any, target: string) => [Header | null, $.GoError]) | null = null
|
|
1073
|
+
public MaxResponseHeaderBytes = 0
|
|
1074
|
+
public WriteBufferSize = 0
|
|
1075
|
+
public ReadBufferSize = 0
|
|
1076
|
+
public ForceAttemptHTTP2 = false
|
|
1077
|
+
public HTTP2: HTTP2Config | null = null
|
|
1078
|
+
public Protocols: Protocols | null = null
|
|
1079
|
+
|
|
1080
|
+
constructor(init?: Partial<Transport>) {
|
|
1081
|
+
Object.assign(this, init)
|
|
1082
|
+
}
|
|
463
1083
|
|
|
464
|
-
|
|
465
|
-
|
|
1084
|
+
public async RoundTrip(req: Request | $.VarRef<Request> | null): Promise<[Response | null, $.GoError]> {
|
|
1085
|
+
const request = $.pointerValue<Request | null>(req)
|
|
1086
|
+
if (request == null) {
|
|
1087
|
+
return [null, errors.New('net/http: nil Request')]
|
|
1088
|
+
}
|
|
1089
|
+
const host = request.URL?.Host ?? ''
|
|
1090
|
+
const handler = host === '' ? null : inProcessServers.get(host)
|
|
1091
|
+
if (handler == null) {
|
|
1092
|
+
return await fetchRoundTrip(request)
|
|
1093
|
+
}
|
|
1094
|
+
const recorder = new memoryResponseWriter()
|
|
1095
|
+
let closeErr: $.GoError | undefined
|
|
1096
|
+
try {
|
|
1097
|
+
const served = handler.ServeHTTP(recorder, inProcessServerRequest(request))
|
|
1098
|
+
if (served instanceof Promise) {
|
|
1099
|
+
await served
|
|
1100
|
+
}
|
|
1101
|
+
} finally {
|
|
1102
|
+
closeErr = request.Body?.Close?.() ?? null
|
|
1103
|
+
}
|
|
1104
|
+
if (closeErr != null) {
|
|
1105
|
+
return [null, closeErr]
|
|
1106
|
+
}
|
|
1107
|
+
const response = recorder.Result()
|
|
1108
|
+
if (request.Method === MethodHead) {
|
|
1109
|
+
response.Body = NoBody
|
|
1110
|
+
}
|
|
1111
|
+
return [response, null]
|
|
1112
|
+
}
|
|
466
1113
|
|
|
467
|
-
|
|
468
|
-
|
|
1114
|
+
public CloseIdleConnections(): void {}
|
|
1115
|
+
|
|
1116
|
+
public CancelRequest(_req: Request | $.VarRef<Request> | null): void {}
|
|
1117
|
+
|
|
1118
|
+
public RegisterProtocol(_scheme: string, _rt: RoundTripper): void {}
|
|
1119
|
+
|
|
1120
|
+
public NewClientConn(_ctx: context.Context, _scheme: string, _address: string): [ClientConn | null, $.GoError] {
|
|
1121
|
+
return [null, ErrNotSupported]
|
|
469
1122
|
}
|
|
470
1123
|
|
|
471
|
-
public
|
|
472
|
-
|
|
473
|
-
flusher?.Flush?.()
|
|
474
|
-
return null
|
|
1124
|
+
public Clone(): Transport {
|
|
1125
|
+
return new Transport(this)
|
|
475
1126
|
}
|
|
476
1127
|
}
|
|
477
1128
|
|
|
478
|
-
export
|
|
479
|
-
return new ResponseController(rw)
|
|
480
|
-
}
|
|
1129
|
+
export const DefaultTransport: RoundTripper = new Transport()
|
|
481
1130
|
|
|
482
|
-
|
|
1131
|
+
class fileTransport implements RoundTripper {
|
|
1132
|
+
constructor(private root: FileSystem | null) {}
|
|
1133
|
+
|
|
1134
|
+
public async RoundTrip(req: Request | $.VarRef<Request> | null): Promise<[Response | null, $.GoError]> {
|
|
1135
|
+
const request = $.pointerValue<Request | null>(req)
|
|
1136
|
+
const recorder = new memoryResponseWriter()
|
|
1137
|
+
let closeErr: $.GoError | undefined
|
|
1138
|
+
try {
|
|
1139
|
+
await FileServer(this.root).ServeHTTP(recorder, request)
|
|
1140
|
+
} finally {
|
|
1141
|
+
closeErr = request?.Body?.Close?.() ?? null
|
|
1142
|
+
}
|
|
1143
|
+
if (closeErr != null) {
|
|
1144
|
+
return [null, closeErr]
|
|
1145
|
+
}
|
|
1146
|
+
return [recorder.Result(), null]
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
export function NewFileTransport(root: FileSystem | null): RoundTripper {
|
|
1151
|
+
return new fileTransport(root)
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
export function NewFileTransportFS(fsys: fs.FS): RoundTripper {
|
|
1155
|
+
return NewFileTransport(FS(fsys))
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
async function fetchRoundTrip(request: Request): Promise<[Response | null, $.GoError]> {
|
|
1159
|
+
const requestBody = request.Body
|
|
1160
|
+
const closeRequestBody = (): $.GoError => {
|
|
1161
|
+
if (requestBody == null) {
|
|
1162
|
+
return null
|
|
1163
|
+
}
|
|
1164
|
+
return requestBody.Close()
|
|
1165
|
+
}
|
|
1166
|
+
if (typeof globalThis.fetch !== 'function') {
|
|
1167
|
+
closeRequestBody()
|
|
1168
|
+
return [null, errors.New('net/http: Client.Do is not implemented in GoScript')]
|
|
1169
|
+
}
|
|
1170
|
+
const ctxErr = request.Context()?.Err?.()
|
|
1171
|
+
if (ctxErr != null) {
|
|
1172
|
+
closeRequestBody()
|
|
1173
|
+
return [null, ctxErr]
|
|
1174
|
+
}
|
|
1175
|
+
const headers = new globalThis.Headers()
|
|
1176
|
+
for (const [key, values] of request.Header.entries()) {
|
|
1177
|
+
for (const value of Array.from(values ?? [])) {
|
|
1178
|
+
headers.append(key, String(value))
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
let body: Uint8Array | undefined
|
|
1182
|
+
if (requestBody != null && request.Method !== MethodGet && request.Method !== MethodHead) {
|
|
1183
|
+
const [data, err] = await io.ReadAll(requestBody)
|
|
1184
|
+
const closeErr = closeRequestBody()
|
|
1185
|
+
if (err != null) {
|
|
1186
|
+
return [null, err]
|
|
1187
|
+
}
|
|
1188
|
+
if (closeErr != null) {
|
|
1189
|
+
return [null, closeErr]
|
|
1190
|
+
}
|
|
1191
|
+
body = Uint8Array.from(data ?? [])
|
|
1192
|
+
} else {
|
|
1193
|
+
const closeErr = closeRequestBody()
|
|
1194
|
+
if (closeErr != null) {
|
|
1195
|
+
return [null, closeErr]
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
try {
|
|
1199
|
+
const bodyInit = body == null ? undefined : Uint8Array.from(body).buffer
|
|
1200
|
+
const fetched = await globalThis.fetch(request.URL?.String?.() ?? '', {
|
|
1201
|
+
method: request.Method || MethodGet,
|
|
1202
|
+
headers,
|
|
1203
|
+
body: bodyInit,
|
|
1204
|
+
})
|
|
1205
|
+
const data = new Uint8Array(await fetched.arrayBuffer())
|
|
1206
|
+
const respHeader = new Header()
|
|
1207
|
+
fetched.headers.forEach((value, key) => Header_Add(respHeader, key, value))
|
|
1208
|
+
return [
|
|
1209
|
+
new Response({
|
|
1210
|
+
Status: `${fetched.status} ${fetched.statusText}`,
|
|
1211
|
+
StatusCode: fetched.status,
|
|
1212
|
+
Body: new responseBody(data),
|
|
1213
|
+
Header: respHeader,
|
|
1214
|
+
ContentLength: Number(fetched.headers.get('content-length') ?? -1),
|
|
1215
|
+
Request: request,
|
|
1216
|
+
}),
|
|
1217
|
+
null,
|
|
1218
|
+
]
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
const message = typeof err === 'object' && err != null && 'message' in err
|
|
1221
|
+
? String((err as { message: unknown }).message)
|
|
1222
|
+
: String(err)
|
|
1223
|
+
return [null, errors.New(message)]
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
export interface FileSystem {
|
|
1228
|
+
Open(name: string): [File | null, $.GoError]
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
export interface File extends io.Closer, io.Reader, io.Seeker {
|
|
1232
|
+
Readdir(count: number): [$.Slice<fs.FileInfo>, $.GoError]
|
|
1233
|
+
Stat(): [fs.FileInfo, $.GoError]
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
export function FS(fsys: fs.FS): FileSystem {
|
|
1237
|
+
return {
|
|
1238
|
+
Open(name: string): [File | null, $.GoError] {
|
|
1239
|
+
const cleaned = cleanFileServerPath(name)
|
|
1240
|
+
const [file, err] = fsys?.Open(cleaned) ?? [null, fs.ErrInvalid]
|
|
1241
|
+
if (err != null || file == null) {
|
|
1242
|
+
return [null, err]
|
|
1243
|
+
}
|
|
1244
|
+
return [httpFileFromFSFile(file), null]
|
|
1245
|
+
},
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
function httpFileFromFSFile(file: Exclude<fs.File, null>): File {
|
|
1250
|
+
const seek = (file as Partial<io.Seeker>).Seek
|
|
1251
|
+
const readdir = (file as { Readdir?: (count: number) => [$.Slice<fs.FileInfo>, $.GoError] }).Readdir
|
|
1252
|
+
return {
|
|
1253
|
+
Read: (p) => file.Read(p instanceof Uint8Array ? p : Uint8Array.from(p ?? [])),
|
|
1254
|
+
Close: () => file.Close(),
|
|
1255
|
+
Stat: () => file.Stat(),
|
|
1256
|
+
Seek: seek == null ? () => [0, errors.New('net/http: file does not support seek')] : seek.bind(file),
|
|
1257
|
+
Readdir: readdir == null ? () => [null, io.EOF] : readdir.bind(file),
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
export function FileServer(root: FileSystem | null): Handler {
|
|
1262
|
+
return {
|
|
1263
|
+
async ServeHTTP(w, r): Promise<void> {
|
|
1264
|
+
const req = $.pointerValue<Request | null>(r)
|
|
1265
|
+
if (w == null || req == null) {
|
|
1266
|
+
return
|
|
1267
|
+
}
|
|
1268
|
+
if (req.Method !== MethodGet && req.Method !== MethodHead) {
|
|
1269
|
+
Error(w, 'method not allowed', StatusMethodNotAllowed)
|
|
1270
|
+
return
|
|
1271
|
+
}
|
|
1272
|
+
const [file, err] = root?.Open(cleanFileServerPath(req.URL?.Path ?? '')) ?? [null, fs.ErrInvalid]
|
|
1273
|
+
if (err != null || file == null) {
|
|
1274
|
+
NotFound(w, req)
|
|
1275
|
+
return
|
|
1276
|
+
}
|
|
1277
|
+
try {
|
|
1278
|
+
const [info, statErr] = file.Stat()
|
|
1279
|
+
if (statErr != null) {
|
|
1280
|
+
Error(w, statErr.Error(), StatusInternalServerError)
|
|
1281
|
+
return
|
|
1282
|
+
}
|
|
1283
|
+
if (info?.IsDir?.() === true) {
|
|
1284
|
+
NotFound(w, req)
|
|
1285
|
+
return
|
|
1286
|
+
}
|
|
1287
|
+
const [data, readErr] = await io.ReadAll(file)
|
|
1288
|
+
if (readErr != null) {
|
|
1289
|
+
Error(w, readErr.Error(), StatusInternalServerError)
|
|
1290
|
+
return
|
|
1291
|
+
}
|
|
1292
|
+
if (info?.Size != null) {
|
|
1293
|
+
Header_Set(w.Header(), 'Content-Length', String(info.Size()))
|
|
1294
|
+
}
|
|
1295
|
+
w.WriteHeader(StatusOK)
|
|
1296
|
+
if (req.Method !== MethodHead) {
|
|
1297
|
+
w.Write(data)
|
|
1298
|
+
}
|
|
1299
|
+
} finally {
|
|
1300
|
+
file.Close()
|
|
1301
|
+
}
|
|
1302
|
+
},
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
export function FileServerFS(fsys: fs.FS): Handler {
|
|
1307
|
+
return FileServer(FS(fsys))
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
export function ServeFile(
|
|
1311
|
+
w: ResponseWriter | null,
|
|
1312
|
+
r: Request | $.VarRef<Request> | null,
|
|
1313
|
+
_name: string,
|
|
1314
|
+
): void {
|
|
1315
|
+
const req = $.pointerValue<Request | null>(r)
|
|
1316
|
+
if (w == null || req == null) {
|
|
1317
|
+
return
|
|
1318
|
+
}
|
|
1319
|
+
if (req.Method !== MethodGet && req.Method !== MethodHead) {
|
|
1320
|
+
Error(w, 'method not allowed', StatusMethodNotAllowed)
|
|
1321
|
+
return
|
|
1322
|
+
}
|
|
1323
|
+
NotFound(w, req)
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
export function ServeFileFS(
|
|
1327
|
+
w: ResponseWriter | null,
|
|
1328
|
+
r: Request | $.VarRef<Request> | null,
|
|
1329
|
+
_fsys: fs.FS,
|
|
1330
|
+
name: string,
|
|
1331
|
+
): void {
|
|
1332
|
+
ServeFile(w, r, name)
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
function cleanFileServerPath(name: string): string {
|
|
1336
|
+
const parts: string[] = []
|
|
1337
|
+
for (const part of name.split('?')[0].split('/')) {
|
|
1338
|
+
if (part === '' || part === '.') {
|
|
1339
|
+
continue
|
|
1340
|
+
}
|
|
1341
|
+
if (part === '..') {
|
|
1342
|
+
parts.pop()
|
|
1343
|
+
continue
|
|
1344
|
+
}
|
|
1345
|
+
parts.push(part)
|
|
1346
|
+
}
|
|
1347
|
+
return parts.length === 0 ? '.' : parts.join('/')
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
export interface Handler {
|
|
1351
|
+
ServeHTTP(w: ResponseWriter | null, r: Request | $.VarRef<Request> | null): void | Promise<void>
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
export type HandlerFunc = (
|
|
1355
|
+
w: ResponseWriter | null,
|
|
1356
|
+
r: Request | $.VarRef<Request> | null,
|
|
1357
|
+
) => void | Promise<void>
|
|
1358
|
+
|
|
1359
|
+
export function HandlerFunc_ServeHTTP(
|
|
1360
|
+
h: HandlerFunc,
|
|
1361
|
+
w: ResponseWriter | null,
|
|
1362
|
+
r: Request | $.VarRef<Request> | null,
|
|
1363
|
+
): void | Promise<void> {
|
|
1364
|
+
return h(w, r)
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
export class CrossOriginProtection {
|
|
1368
|
+
private denyHandler: Handler | null = null
|
|
1369
|
+
private trustedOrigins = new Set<string>()
|
|
1370
|
+
private bypassPatterns: string[] = []
|
|
1371
|
+
|
|
1372
|
+
public AddInsecureBypassPattern(pattern: string): void {
|
|
1373
|
+
this.bypassPatterns.push(pattern)
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
public AddTrustedOrigin(origin: string): $.GoError {
|
|
1377
|
+
if (!/^[A-Za-z][A-Za-z0-9+.-]*:\/\/[^/?#]+$/.test(origin)) {
|
|
1378
|
+
return $.newError(`invalid origin "${origin}"`)
|
|
1379
|
+
}
|
|
1380
|
+
try {
|
|
1381
|
+
const parsed = new URL(origin)
|
|
1382
|
+
if (parsed.protocol === '' || parsed.host === '') {
|
|
1383
|
+
return $.newError(`invalid origin "${origin}"`)
|
|
1384
|
+
}
|
|
1385
|
+
} catch (err) {
|
|
1386
|
+
return $.newError(`invalid origin "${origin}": ${String(err)}`)
|
|
1387
|
+
}
|
|
1388
|
+
this.trustedOrigins.add(origin)
|
|
1389
|
+
return null
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
public Check(r: Request | $.VarRef<Request> | null): $.GoError {
|
|
1393
|
+
const req = $.pointerValue<Request | null>(r)
|
|
1394
|
+
if (req == null) {
|
|
1395
|
+
return errCrossOriginRequest
|
|
1396
|
+
}
|
|
1397
|
+
switch (req.Method) {
|
|
1398
|
+
case MethodGet:
|
|
1399
|
+
case MethodHead:
|
|
1400
|
+
case MethodOptions:
|
|
1401
|
+
return null
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
switch (Header_Get(req.Header, 'Sec-Fetch-Site')) {
|
|
1405
|
+
case '':
|
|
1406
|
+
break
|
|
1407
|
+
case 'same-origin':
|
|
1408
|
+
case 'none':
|
|
1409
|
+
return null
|
|
1410
|
+
default:
|
|
1411
|
+
if (this.isRequestExempt(req)) {
|
|
1412
|
+
return null
|
|
1413
|
+
}
|
|
1414
|
+
return errCrossOriginRequest
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
const origin = Header_Get(req.Header, 'Origin')
|
|
1418
|
+
if (origin === '') {
|
|
1419
|
+
return null
|
|
1420
|
+
}
|
|
1421
|
+
if (originHost(origin) === req.Host) {
|
|
1422
|
+
return null
|
|
1423
|
+
}
|
|
1424
|
+
if (this.isRequestExempt(req)) {
|
|
1425
|
+
return null
|
|
1426
|
+
}
|
|
1427
|
+
return errCrossOriginRequestFromOldBrowser
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
public Handler(handler: Handler | null): Handler {
|
|
1431
|
+
return {
|
|
1432
|
+
ServeHTTP: (w, r) => {
|
|
1433
|
+
const err = this.Check(r)
|
|
1434
|
+
if (err != null) {
|
|
1435
|
+
const deny = this.denyHandler
|
|
1436
|
+
if (deny != null) {
|
|
1437
|
+
return deny.ServeHTTP(w, r)
|
|
1438
|
+
}
|
|
1439
|
+
Error(w, err.Error(), StatusForbidden)
|
|
1440
|
+
return
|
|
1441
|
+
}
|
|
1442
|
+
return handler?.ServeHTTP(w, r)
|
|
1443
|
+
},
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
public SetDenyHandler(handler: Handler | null): void {
|
|
1448
|
+
this.denyHandler = handler
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
private isRequestExempt(req: Request): boolean {
|
|
1452
|
+
for (const pattern of this.bypassPatterns) {
|
|
1453
|
+
if (bypassPatternMatches(pattern, req)) {
|
|
1454
|
+
return true
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
const origin = Header_Get(req.Header, 'Origin')
|
|
1458
|
+
return origin !== '' && this.trustedOrigins.has(origin)
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
export function NewCrossOriginProtection(): CrossOriginProtection {
|
|
1463
|
+
return new CrossOriginProtection()
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function originHost(origin: string): string {
|
|
1467
|
+
try {
|
|
1468
|
+
return new URL(origin).host
|
|
1469
|
+
} catch {
|
|
1470
|
+
return ''
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
function bypassPatternMatches(pattern: string, req: Request): boolean {
|
|
1475
|
+
let method = ''
|
|
1476
|
+
let pathPattern = pattern
|
|
1477
|
+
const space = pattern.indexOf(' ')
|
|
1478
|
+
if (space > 0) {
|
|
1479
|
+
method = pattern.slice(0, space)
|
|
1480
|
+
pathPattern = pattern.slice(space + 1)
|
|
1481
|
+
}
|
|
1482
|
+
if (method !== '' && method !== req.Method) {
|
|
1483
|
+
return false
|
|
1484
|
+
}
|
|
1485
|
+
const path = req.URL?.Path ?? ''
|
|
1486
|
+
if (pathPattern.includes('{')) {
|
|
1487
|
+
return wildcardPatternMatches(pathPattern, path)
|
|
1488
|
+
}
|
|
1489
|
+
if (pathPattern.endsWith('/')) {
|
|
1490
|
+
return path === pathPattern || path.startsWith(pathPattern)
|
|
1491
|
+
}
|
|
1492
|
+
return path === pathPattern
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
function wildcardPatternMatches(pattern: string, path: string): boolean {
|
|
1496
|
+
const patternParts = pattern.split('/').filter((part) => part !== '')
|
|
1497
|
+
const pathParts = path.split('/').filter((part) => part !== '')
|
|
1498
|
+
if (patternParts.length !== pathParts.length) {
|
|
1499
|
+
return false
|
|
1500
|
+
}
|
|
1501
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
1502
|
+
if (/^\{[^}]+\}$/.test(patternParts[i])) {
|
|
1503
|
+
continue
|
|
1504
|
+
}
|
|
1505
|
+
if (patternParts[i] !== pathParts[i]) {
|
|
1506
|
+
return false
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
return true
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
export class Server {
|
|
1513
|
+
public Addr: string
|
|
1514
|
+
public BaseContext: ((listener: any) => context.Context) | null
|
|
1515
|
+
public ConnContext: ((ctx: context.Context, conn: any) => context.Context) | null
|
|
1516
|
+
public Handler: Handler | null
|
|
1517
|
+
public DisableGeneralOptionsHandler: boolean
|
|
1518
|
+
public TLSConfig: any
|
|
1519
|
+
public ReadTimeout: number
|
|
1520
|
+
public ReadTimeoutHandler: any
|
|
1521
|
+
public ReadHeaderTimeout: number
|
|
1522
|
+
public WriteTimeout: number
|
|
1523
|
+
public IdleTimeout: number
|
|
1524
|
+
public MaxHeaderBytes: number
|
|
1525
|
+
public TLSNextProto: Map<string, any> | null
|
|
1526
|
+
public ConnState: ((conn: any, state: ConnState) => void) | null
|
|
1527
|
+
public ErrorLog: any
|
|
1528
|
+
public HTTP2: HTTP2Config | null
|
|
1529
|
+
public Protocols: Protocols | null
|
|
1530
|
+
|
|
1531
|
+
constructor(init?: Partial<Server>) {
|
|
1532
|
+
this.Addr = init?.Addr ?? ''
|
|
1533
|
+
this.BaseContext = init?.BaseContext ?? null
|
|
1534
|
+
this.ConnContext = init?.ConnContext ?? null
|
|
1535
|
+
this.Handler = init?.Handler ?? null
|
|
1536
|
+
this.DisableGeneralOptionsHandler = init?.DisableGeneralOptionsHandler ?? false
|
|
1537
|
+
this.TLSConfig = init?.TLSConfig ?? null
|
|
1538
|
+
this.ReadTimeout = init?.ReadTimeout ?? 0
|
|
1539
|
+
this.ReadTimeoutHandler = (init as any)?.ReadTimeoutHandler ?? null
|
|
1540
|
+
this.ReadHeaderTimeout = init?.ReadHeaderTimeout ?? 0
|
|
1541
|
+
this.WriteTimeout = init?.WriteTimeout ?? 0
|
|
1542
|
+
this.IdleTimeout = init?.IdleTimeout ?? 0
|
|
1543
|
+
this.MaxHeaderBytes = init?.MaxHeaderBytes ?? 0
|
|
1544
|
+
this.TLSNextProto = init?.TLSNextProto ?? null
|
|
1545
|
+
this.ConnState = init?.ConnState ?? null
|
|
1546
|
+
this.ErrorLog = init?.ErrorLog ?? null
|
|
1547
|
+
this.HTTP2 = init?.HTTP2 ?? null
|
|
1548
|
+
this.Protocols = init?.Protocols ?? null
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
public ListenAndServe(): $.GoError {
|
|
1552
|
+
return errors.New('net/http: Server.ListenAndServe is not implemented in GoScript')
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
public ListenAndServeTLS(_certFile: string, _keyFile: string): $.GoError {
|
|
1556
|
+
return errors.New('net/http: Server.ListenAndServeTLS is not implemented in GoScript')
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
public Close(): $.GoError {
|
|
1560
|
+
return null
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
public Shutdown(_ctx: context.Context): $.GoError {
|
|
1564
|
+
return null
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
public Serve(_listener: any): $.GoError {
|
|
1568
|
+
return ErrNotSupported
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
public ServeTLS(_listener: any, _certFile: string, _keyFile: string): $.GoError {
|
|
1572
|
+
return ErrNotSupported
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
public ServeHTTP(w: ResponseWriter | null, r: Request | $.VarRef<Request> | null): void | Promise<void> {
|
|
1576
|
+
return (this.Handler ?? DefaultServeMux).ServeHTTP(w, r)
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
public RegisterOnShutdown(_f: () => void): void {}
|
|
1580
|
+
|
|
1581
|
+
public SetKeepAlivesEnabled(_v: boolean): void {}
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
export function ListenAndServe(_addr: string, _handler: Handler | null): $.GoError {
|
|
1585
|
+
return ErrNotSupported
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
export function ListenAndServeTLS(
|
|
1589
|
+
_addr: string,
|
|
1590
|
+
_certFile: string,
|
|
1591
|
+
_keyFile: string,
|
|
1592
|
+
_handler: Handler | null,
|
|
1593
|
+
): $.GoError {
|
|
1594
|
+
return ErrNotSupported
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
export function Serve(_listener: any, _handler: Handler | null): $.GoError {
|
|
1598
|
+
return ErrNotSupported
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
export function ServeTLS(
|
|
1602
|
+
_listener: any,
|
|
1603
|
+
_handler: Handler | null,
|
|
1604
|
+
_certFile: string,
|
|
1605
|
+
_keyFile: string,
|
|
1606
|
+
): $.GoError {
|
|
1607
|
+
return ErrNotSupported
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
export class PushOptions {
|
|
1611
|
+
public Header: Header
|
|
1612
|
+
|
|
1613
|
+
constructor(init?: Partial<PushOptions>) {
|
|
1614
|
+
this.Header = init?.Header ?? new Header()
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
export interface Flusher {
|
|
1619
|
+
Flush(): void
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
export interface Hijacker {
|
|
1623
|
+
Hijack(): [any, any, $.GoError]
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
export interface Pusher {
|
|
1627
|
+
Push(target: string, opts: PushOptions | $.VarRef<PushOptions> | null): $.GoError
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
export class ResponseController {
|
|
1631
|
+
public rw: ResponseWriter | null
|
|
1632
|
+
|
|
1633
|
+
constructor(rw: ResponseWriter | null) {
|
|
1634
|
+
this.rw = rw
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
public Flush(): $.GoError {
|
|
1638
|
+
const flusher = this.rw as (Flusher & ResponseWriter) | null
|
|
1639
|
+
flusher?.Flush?.()
|
|
1640
|
+
return null
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
public Hijack(): [any, any, $.GoError] {
|
|
1644
|
+
return [null, null, ErrNotSupported]
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
public SetReadDeadline(_deadline: time.Time): $.GoError {
|
|
1648
|
+
return ErrNotSupported
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
public SetWriteDeadline(_deadline: time.Time): $.GoError {
|
|
1652
|
+
return ErrNotSupported
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
public EnableFullDuplex(): $.GoError {
|
|
1656
|
+
return ErrNotSupported
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
export function NewResponseController(rw: ResponseWriter | null): ResponseController {
|
|
1661
|
+
return new ResponseController(rw)
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
class maxBytesReader implements io.ReadCloser {
|
|
1665
|
+
private initialLimit: number
|
|
1666
|
+
private remaining: number
|
|
1667
|
+
private err: $.GoError = null
|
|
1668
|
+
|
|
1669
|
+
constructor(private reader: io.ReadCloser, limit: number) {
|
|
1670
|
+
this.initialLimit = Math.max(0, limit)
|
|
1671
|
+
this.remaining = this.initialLimit
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
public Read(p: $.Bytes): [number, $.GoError] {
|
|
1675
|
+
if (this.err != null) {
|
|
1676
|
+
return [0, this.err]
|
|
1677
|
+
}
|
|
1678
|
+
if ($.len(p) === 0) {
|
|
1679
|
+
return [0, null]
|
|
1680
|
+
}
|
|
1681
|
+
const readLen =
|
|
1682
|
+
$.len(p) - 1 > this.remaining ? this.remaining + 1 : $.len(p)
|
|
1683
|
+
const target = $.goSlice(p, 0, readLen)
|
|
1684
|
+
const [n, err] = this.reader.Read(target)
|
|
1685
|
+
if (n <= this.remaining) {
|
|
1686
|
+
this.remaining -= n
|
|
1687
|
+
this.err = err
|
|
1688
|
+
return [n, err]
|
|
1689
|
+
}
|
|
1690
|
+
const accepted = this.remaining
|
|
1691
|
+
this.remaining = 0
|
|
1692
|
+
this.err = new MaxBytesError({ Limit: this.initialLimit })
|
|
1693
|
+
return [accepted, this.err]
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
public Close(): $.GoError {
|
|
1697
|
+
return this.reader.Close()
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
export function MaxBytesReader(
|
|
1702
|
+
_w: ResponseWriter | null,
|
|
1703
|
+
r: io.ReadCloser | null,
|
|
1704
|
+
n: number,
|
|
1705
|
+
): io.ReadCloser {
|
|
1706
|
+
return new maxBytesReader(r ?? NoBody, n)
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
export class ServeMux implements Handler {
|
|
483
1710
|
private handlers = new Map<string, Handler>()
|
|
484
1711
|
|
|
485
1712
|
public Handle(pattern: string, handler: Handler | null): void {
|
|
@@ -509,14 +1736,18 @@ export class ServeMux implements Handler {
|
|
|
509
1736
|
}
|
|
510
1737
|
}
|
|
511
1738
|
|
|
512
|
-
const
|
|
1739
|
+
export const DefaultServeMux = new ServeMux()
|
|
513
1740
|
|
|
514
1741
|
export function NewServeMux(): ServeMux {
|
|
515
1742
|
return new ServeMux()
|
|
516
1743
|
}
|
|
517
1744
|
|
|
1745
|
+
export function Handle(pattern: string, handler: Handler | null): void {
|
|
1746
|
+
DefaultServeMux.Handle(pattern, handler)
|
|
1747
|
+
}
|
|
1748
|
+
|
|
518
1749
|
export function HandleFunc(pattern: string, handler: HandlerFunc): void {
|
|
519
|
-
|
|
1750
|
+
DefaultServeMux.HandleFunc(pattern, handler)
|
|
520
1751
|
}
|
|
521
1752
|
|
|
522
1753
|
export function StripPrefix(prefix: string, handler: Handler | null): Handler {
|
|
@@ -531,6 +1762,52 @@ export function StripPrefix(prefix: string, handler: Handler | null): Handler {
|
|
|
531
1762
|
}
|
|
532
1763
|
}
|
|
533
1764
|
|
|
1765
|
+
export function AllowQuerySemicolons(handler: Handler | null): Handler {
|
|
1766
|
+
return handler ?? NotFoundHandler()
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
export function MaxBytesHandler(handler: Handler | null, n: number): Handler {
|
|
1770
|
+
return {
|
|
1771
|
+
ServeHTTP(w, r) {
|
|
1772
|
+
const req = $.pointerValue<Request | null>(r)
|
|
1773
|
+
let wrappedReq = req
|
|
1774
|
+
if (req != null && req.Body != null) {
|
|
1775
|
+
const reqCopy = Object.assign(
|
|
1776
|
+
Object.create(Object.getPrototypeOf(req)),
|
|
1777
|
+
req,
|
|
1778
|
+
) as Request
|
|
1779
|
+
reqCopy.Body = MaxBytesReader(w, req.Body, n)
|
|
1780
|
+
wrappedReq = reqCopy
|
|
1781
|
+
}
|
|
1782
|
+
return handler?.ServeHTTP(w, wrappedReq)
|
|
1783
|
+
},
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
export function NotFoundHandler(): Handler {
|
|
1788
|
+
return { ServeHTTP: NotFound }
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
export function RedirectHandler(url: string, code: number): Handler {
|
|
1792
|
+
return {
|
|
1793
|
+
ServeHTTP(w, r) {
|
|
1794
|
+
Redirect(w, r, url, code)
|
|
1795
|
+
},
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
export function TimeoutHandler(handler: Handler | null, _dt: number, msg: string): Handler {
|
|
1800
|
+
return {
|
|
1801
|
+
ServeHTTP(w, r) {
|
|
1802
|
+
if (handler == null) {
|
|
1803
|
+
Error(w, msg, StatusServiceUnavailable)
|
|
1804
|
+
return
|
|
1805
|
+
}
|
|
1806
|
+
return handler.ServeHTTP(w, r)
|
|
1807
|
+
},
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
|
|
534
1811
|
export function Error(w: ResponseWriter | null, error: string, code: number): void {
|
|
535
1812
|
w?.WriteHeader(code)
|
|
536
1813
|
w?.Write($.stringToBytes(error + '\n'))
|
|
@@ -561,16 +1838,367 @@ export function ParseTime(text: string): [time.Time, $.GoError] {
|
|
|
561
1838
|
return [time.UnixMilli(date.getTime()), null]
|
|
562
1839
|
}
|
|
563
1840
|
|
|
1841
|
+
export function DetectContentType(data: $.Slice<number>): string {
|
|
1842
|
+
const bytes = Uint8Array.from(data ?? []).subarray(0, 512)
|
|
1843
|
+
const firstNonWS = firstNonWhitespace(bytes)
|
|
1844
|
+
const afterWS = bytes.subarray(firstNonWS)
|
|
1845
|
+
if (
|
|
1846
|
+
htmlSig(afterWS, '<!DOCTYPE HTML') ||
|
|
1847
|
+
htmlSig(afterWS, '<HTML') ||
|
|
1848
|
+
htmlSig(afterWS, '<HEAD') ||
|
|
1849
|
+
htmlSig(afterWS, '<SCRIPT') ||
|
|
1850
|
+
htmlSig(afterWS, '<IFRAME') ||
|
|
1851
|
+
htmlSig(afterWS, '<H1') ||
|
|
1852
|
+
htmlSig(afterWS, '<DIV') ||
|
|
1853
|
+
htmlSig(afterWS, '<FONT') ||
|
|
1854
|
+
htmlSig(afterWS, '<TABLE') ||
|
|
1855
|
+
htmlSig(afterWS, '<A') ||
|
|
1856
|
+
htmlSig(afterWS, '<STYLE') ||
|
|
1857
|
+
htmlSig(afterWS, '<TITLE') ||
|
|
1858
|
+
htmlSig(afterWS, '<B') ||
|
|
1859
|
+
htmlSig(afterWS, '<BODY') ||
|
|
1860
|
+
htmlSig(afterWS, '<BR') ||
|
|
1861
|
+
htmlSig(afterWS, '<P') ||
|
|
1862
|
+
startsWithASCII(afterWS, '<!--')
|
|
1863
|
+
) {
|
|
1864
|
+
return 'text/html; charset=utf-8'
|
|
1865
|
+
}
|
|
1866
|
+
if (startsWithASCII(afterWS, '<?xml')) {
|
|
1867
|
+
return 'text/xml; charset=utf-8'
|
|
1868
|
+
}
|
|
1869
|
+
if (startsWithASCII(bytes, '%PDF-')) {
|
|
1870
|
+
return 'application/pdf'
|
|
1871
|
+
}
|
|
1872
|
+
if (startsWithASCII(bytes, '%!PS-Adobe-')) {
|
|
1873
|
+
return 'application/postscript'
|
|
1874
|
+
}
|
|
1875
|
+
if (startsWithBytes(bytes, new Uint8Array([0xfe, 0xff]))) {
|
|
1876
|
+
return 'text/plain; charset=utf-16be'
|
|
1877
|
+
}
|
|
1878
|
+
if (startsWithBytes(bytes, new Uint8Array([0xff, 0xfe]))) {
|
|
1879
|
+
return 'text/plain; charset=utf-16le'
|
|
1880
|
+
}
|
|
1881
|
+
if (startsWithBytes(bytes, new Uint8Array([0xef, 0xbb, 0xbf]))) {
|
|
1882
|
+
return 'text/plain; charset=utf-8'
|
|
1883
|
+
}
|
|
1884
|
+
if (startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x01, 0x00])) || startsWithBytes(bytes, new Uint8Array([0x00, 0x00, 0x02, 0x00]))) {
|
|
1885
|
+
return 'image/x-icon'
|
|
1886
|
+
}
|
|
1887
|
+
if (startsWithASCII(bytes, 'BM')) {
|
|
1888
|
+
return 'image/bmp'
|
|
1889
|
+
}
|
|
1890
|
+
if (startsWithASCII(bytes, 'GIF87a') || startsWithASCII(bytes, 'GIF89a')) {
|
|
1891
|
+
return 'image/gif'
|
|
1892
|
+
}
|
|
1893
|
+
if (isRIFFSignature(bytes, 'WEBPVP')) {
|
|
1894
|
+
return 'image/webp'
|
|
1895
|
+
}
|
|
1896
|
+
if (startsWithBytes(bytes, new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
|
|
1897
|
+
return 'image/png'
|
|
1898
|
+
}
|
|
1899
|
+
if (startsWithBytes(bytes, new Uint8Array([0xff, 0xd8, 0xff]))) {
|
|
1900
|
+
return 'image/jpeg'
|
|
1901
|
+
}
|
|
1902
|
+
if (isFORMSignature(bytes, 'AIFF')) {
|
|
1903
|
+
return 'audio/aiff'
|
|
1904
|
+
}
|
|
1905
|
+
if (startsWithASCII(bytes, 'ID3')) {
|
|
1906
|
+
return 'audio/mpeg'
|
|
1907
|
+
}
|
|
1908
|
+
if (startsWithBytes(bytes, new Uint8Array([0x4f, 0x67, 0x67, 0x53, 0x00]))) {
|
|
1909
|
+
return 'application/ogg'
|
|
1910
|
+
}
|
|
1911
|
+
if (startsWithBytes(bytes, new Uint8Array([0x4d, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06]))) {
|
|
1912
|
+
return 'audio/midi'
|
|
1913
|
+
}
|
|
1914
|
+
if (isRIFFSignature(bytes, 'AVI ')) {
|
|
1915
|
+
return 'video/avi'
|
|
1916
|
+
}
|
|
1917
|
+
if (isRIFFSignature(bytes, 'WAVE')) {
|
|
1918
|
+
return 'audio/wave'
|
|
1919
|
+
}
|
|
1920
|
+
if (isMP4Signature(bytes)) {
|
|
1921
|
+
return 'video/mp4'
|
|
1922
|
+
}
|
|
1923
|
+
if (startsWithBytes(bytes, new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]))) {
|
|
1924
|
+
return 'video/webm'
|
|
1925
|
+
}
|
|
1926
|
+
if (isEOTSignature(bytes)) {
|
|
1927
|
+
return 'application/vnd.ms-fontobject'
|
|
1928
|
+
}
|
|
1929
|
+
if (startsWithBytes(bytes, new Uint8Array([0x00, 0x01, 0x00, 0x00]))) {
|
|
1930
|
+
return 'font/ttf'
|
|
1931
|
+
}
|
|
1932
|
+
if (startsWithASCII(bytes, 'OTTO')) {
|
|
1933
|
+
return 'font/otf'
|
|
1934
|
+
}
|
|
1935
|
+
if (startsWithASCII(bytes, 'ttcf')) {
|
|
1936
|
+
return 'font/collection'
|
|
1937
|
+
}
|
|
1938
|
+
if (startsWithASCII(bytes, 'wOFF')) {
|
|
1939
|
+
return 'font/woff'
|
|
1940
|
+
}
|
|
1941
|
+
if (startsWithASCII(bytes, 'wOF2')) {
|
|
1942
|
+
return 'font/woff2'
|
|
1943
|
+
}
|
|
1944
|
+
if (startsWithBytes(bytes, new Uint8Array([0x1f, 0x8b, 0x08]))) {
|
|
1945
|
+
return 'application/x-gzip'
|
|
1946
|
+
}
|
|
1947
|
+
if (startsWithBytes(bytes, new Uint8Array([0x50, 0x4b, 0x03, 0x04]))) {
|
|
1948
|
+
return 'application/zip'
|
|
1949
|
+
}
|
|
1950
|
+
if (startsWithBytes(bytes, new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00]))) {
|
|
1951
|
+
return 'application/x-rar-compressed'
|
|
1952
|
+
}
|
|
1953
|
+
if (startsWithBytes(bytes, new Uint8Array([0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00]))) {
|
|
1954
|
+
return 'application/x-rar-compressed'
|
|
1955
|
+
}
|
|
1956
|
+
if (startsWithBytes(bytes, new Uint8Array([0x00, 0x61, 0x73, 0x6d]))) {
|
|
1957
|
+
return 'application/wasm'
|
|
1958
|
+
}
|
|
1959
|
+
for (const byte of afterWS) {
|
|
1960
|
+
if (byte <= 0x08 || byte === 0x0b || (byte >= 0x0e && byte <= 0x1a) || (byte >= 0x1c && byte <= 0x1f)) {
|
|
1961
|
+
return 'application/octet-stream'
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
return 'text/plain; charset=utf-8'
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
function firstNonWhitespace(data: Uint8Array): number {
|
|
1968
|
+
for (let i = 0; i < data.length; i++) {
|
|
1969
|
+
const byte = data[i]
|
|
1970
|
+
if (byte !== 0x09 && byte !== 0x0a && byte !== 0x0c && byte !== 0x0d && byte !== 0x20) {
|
|
1971
|
+
return i
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
return data.length
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1977
|
+
function startsWithBytes(data: Uint8Array, prefix: Uint8Array): boolean {
|
|
1978
|
+
if (data.length < prefix.length) {
|
|
1979
|
+
return false
|
|
1980
|
+
}
|
|
1981
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
1982
|
+
if (data[i] !== prefix[i]) {
|
|
1983
|
+
return false
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
return true
|
|
1987
|
+
}
|
|
1988
|
+
|
|
1989
|
+
function startsWithASCII(data: Uint8Array, prefix: string): boolean {
|
|
1990
|
+
return asciiMatchesAt(data, 0, prefix)
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
function asciiMatchesAt(data: Uint8Array, offset: number, text: string): boolean {
|
|
1994
|
+
if (data.length < offset + text.length) {
|
|
1995
|
+
return false
|
|
1996
|
+
}
|
|
1997
|
+
for (let i = 0; i < text.length; i++) {
|
|
1998
|
+
if (data[offset + i] !== text.charCodeAt(i)) {
|
|
1999
|
+
return false
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
return true
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
function isRIFFSignature(data: Uint8Array, form: string): boolean {
|
|
2006
|
+
return asciiMatchesAt(data, 0, 'RIFF') && asciiMatchesAt(data, 8, form)
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
function isFORMSignature(data: Uint8Array, form: string): boolean {
|
|
2010
|
+
return asciiMatchesAt(data, 0, 'FORM') && asciiMatchesAt(data, 8, form)
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
function isMP4Signature(data: Uint8Array): boolean {
|
|
2014
|
+
if (data.length < 12 || !asciiMatchesAt(data, 4, 'ftyp')) {
|
|
2015
|
+
return false
|
|
2016
|
+
}
|
|
2017
|
+
const boxSize =
|
|
2018
|
+
data[0] * 0x1000000 + data[1] * 0x10000 + data[2] * 0x100 + data[3]
|
|
2019
|
+
if (boxSize < 12 || data.length < boxSize || boxSize % 4 !== 0) {
|
|
2020
|
+
return false
|
|
2021
|
+
}
|
|
2022
|
+
for (let offset = 8; offset + 3 <= boxSize; offset += 4) {
|
|
2023
|
+
if (offset === 12) {
|
|
2024
|
+
continue
|
|
2025
|
+
}
|
|
2026
|
+
if (asciiMatchesAt(data, offset, 'mp4')) {
|
|
2027
|
+
return true
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
return false
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
function isEOTSignature(data: Uint8Array): boolean {
|
|
2034
|
+
return data.length >= 36 && data[34] === 0x4c && data[35] === 0x50
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
function htmlSig(data: Uint8Array, sig: string): boolean {
|
|
2038
|
+
if (data.length < sig.length + 1) {
|
|
2039
|
+
return false
|
|
2040
|
+
}
|
|
2041
|
+
for (let i = 0; i < sig.length; i++) {
|
|
2042
|
+
const got = data[i] >= 0x61 && data[i] <= 0x7a ? data[i] & 0xdf : data[i]
|
|
2043
|
+
if (got !== sig.charCodeAt(i)) {
|
|
2044
|
+
return false
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
const term = data[sig.length]
|
|
2048
|
+
return term === 0x20 || term === 0x3e
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
export function ParseHTTPVersion(vers: string): [number, number, boolean] {
|
|
2052
|
+
const match = /^HTTP\/(\d+)\.(\d+)$/.exec(vers)
|
|
2053
|
+
if (match == null) {
|
|
2054
|
+
return [0, 0, false]
|
|
2055
|
+
}
|
|
2056
|
+
return [Number(match[1]), Number(match[2]), true]
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
export function ParseCookie(line: string): [$.Slice<Cookie | null>, $.GoError] {
|
|
2060
|
+
const parts = line.trim().split(';')
|
|
2061
|
+
if (parts.length > 3000) {
|
|
2062
|
+
return [null, errCookieNumLimitExceeded]
|
|
2063
|
+
}
|
|
2064
|
+
if (parts.length === 1 && parts[0] === '') {
|
|
2065
|
+
return [null, errBlankCookie]
|
|
2066
|
+
}
|
|
2067
|
+
const cookies: Array<Cookie | null> = []
|
|
2068
|
+
for (const raw of parts) {
|
|
2069
|
+
const part = raw.trim()
|
|
2070
|
+
const eq = part.indexOf('=')
|
|
2071
|
+
if (eq < 0) {
|
|
2072
|
+
return [null, errEqualNotFoundInCookie]
|
|
2073
|
+
}
|
|
2074
|
+
const name = part.slice(0, eq)
|
|
2075
|
+
if (!isToken(name)) {
|
|
2076
|
+
return [null, errInvalidCookieName]
|
|
2077
|
+
}
|
|
2078
|
+
const [value, quoted, ok] = parseCookieValue(part.slice(eq + 1), true)
|
|
2079
|
+
if (!ok) {
|
|
2080
|
+
return [null, errInvalidCookieValue]
|
|
2081
|
+
}
|
|
2082
|
+
cookies.push(new Cookie({ Name: name, Value: value, Quoted: quoted }))
|
|
2083
|
+
}
|
|
2084
|
+
return [$.arrayToSlice(cookies), null]
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2087
|
+
export function ParseSetCookie(line: string): [Cookie | null, $.GoError] {
|
|
2088
|
+
const parts = line.trim().split(';')
|
|
2089
|
+
if (parts.length === 1 && parts[0] === '') {
|
|
2090
|
+
return [null, errBlankCookie]
|
|
2091
|
+
}
|
|
2092
|
+
const first = parts[0].trim()
|
|
2093
|
+
const eq = first.indexOf('=')
|
|
2094
|
+
if (eq < 0) {
|
|
2095
|
+
return [null, errEqualNotFoundInCookie]
|
|
2096
|
+
}
|
|
2097
|
+
const name = first.slice(0, eq).trim()
|
|
2098
|
+
if (!isToken(name)) {
|
|
2099
|
+
return [null, errInvalidCookieName]
|
|
2100
|
+
}
|
|
2101
|
+
const [value, quoted, ok] = parseCookieValue(first.slice(eq + 1), true)
|
|
2102
|
+
if (!ok) {
|
|
2103
|
+
return [null, errInvalidCookieValue]
|
|
2104
|
+
}
|
|
2105
|
+
const cookie = new Cookie({ Name: name, Value: value, Quoted: quoted, Raw: line })
|
|
2106
|
+
const unparsed: string[] = []
|
|
2107
|
+
for (const raw of parts.slice(1)) {
|
|
2108
|
+
const part = raw.trim()
|
|
2109
|
+
if (part === '') {
|
|
2110
|
+
continue
|
|
2111
|
+
}
|
|
2112
|
+
const attrEq = part.indexOf('=')
|
|
2113
|
+
const attr = attrEq < 0 ? part : part.slice(0, attrEq)
|
|
2114
|
+
const rawValue = attrEq < 0 ? '' : part.slice(attrEq + 1)
|
|
2115
|
+
const [lowerAttr, ascii] = asciiLower(attr)
|
|
2116
|
+
if (!ascii) {
|
|
2117
|
+
continue
|
|
2118
|
+
}
|
|
2119
|
+
const [attrValue, , attrOK] = parseCookieValue(rawValue, false)
|
|
2120
|
+
if (!attrOK) {
|
|
2121
|
+
unparsed.push(part)
|
|
2122
|
+
continue
|
|
2123
|
+
}
|
|
2124
|
+
switch (lowerAttr) {
|
|
2125
|
+
case 'samesite': {
|
|
2126
|
+
const [lowerValue, valueASCII] = asciiLower(attrValue)
|
|
2127
|
+
if (!valueASCII) {
|
|
2128
|
+
cookie.SameSite = SameSiteDefaultMode
|
|
2129
|
+
continue
|
|
2130
|
+
}
|
|
2131
|
+
switch (lowerValue) {
|
|
2132
|
+
case 'lax':
|
|
2133
|
+
cookie.SameSite = SameSiteLaxMode
|
|
2134
|
+
break
|
|
2135
|
+
case 'strict':
|
|
2136
|
+
cookie.SameSite = SameSiteStrictMode
|
|
2137
|
+
break
|
|
2138
|
+
case 'none':
|
|
2139
|
+
cookie.SameSite = SameSiteNoneMode
|
|
2140
|
+
break
|
|
2141
|
+
default:
|
|
2142
|
+
cookie.SameSite = SameSiteDefaultMode
|
|
2143
|
+
break
|
|
2144
|
+
}
|
|
2145
|
+
continue
|
|
2146
|
+
}
|
|
2147
|
+
case 'secure':
|
|
2148
|
+
cookie.Secure = true
|
|
2149
|
+
continue
|
|
2150
|
+
case 'httponly':
|
|
2151
|
+
cookie.HttpOnly = true
|
|
2152
|
+
continue
|
|
2153
|
+
case 'domain':
|
|
2154
|
+
cookie.Domain = attrValue
|
|
2155
|
+
continue
|
|
2156
|
+
case 'max-age': {
|
|
2157
|
+
if (!/^[+-]?\d+$/.test(attrValue)) {
|
|
2158
|
+
break
|
|
2159
|
+
}
|
|
2160
|
+
let secs = Number.parseInt(attrValue, 10)
|
|
2161
|
+
if ((secs !== 0 && attrValue[0] === '0') || !Number.isSafeInteger(secs)) {
|
|
2162
|
+
break
|
|
2163
|
+
}
|
|
2164
|
+
if (secs <= 0) {
|
|
2165
|
+
secs = -1
|
|
2166
|
+
}
|
|
2167
|
+
cookie.MaxAge = secs
|
|
2168
|
+
continue
|
|
2169
|
+
}
|
|
2170
|
+
case 'expires': {
|
|
2171
|
+
cookie.RawExpires = attrValue
|
|
2172
|
+
const parsed = new globalThis.Date(attrValue)
|
|
2173
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
2174
|
+
break
|
|
2175
|
+
}
|
|
2176
|
+
cookie.Expires = time.UnixMilli(parsed.getTime())
|
|
2177
|
+
continue
|
|
2178
|
+
}
|
|
2179
|
+
case 'path':
|
|
2180
|
+
cookie.Path = attrValue
|
|
2181
|
+
continue
|
|
2182
|
+
case 'partitioned':
|
|
2183
|
+
cookie.Partitioned = true
|
|
2184
|
+
continue
|
|
2185
|
+
}
|
|
2186
|
+
unparsed.push(part)
|
|
2187
|
+
}
|
|
2188
|
+
cookie.Unparsed = unparsed.length === 0 ? null : $.arrayToSlice(unparsed)
|
|
2189
|
+
return [cookie, null]
|
|
2190
|
+
}
|
|
2191
|
+
|
|
564
2192
|
export function NewRequest(
|
|
565
2193
|
method: string,
|
|
566
2194
|
url: string,
|
|
567
2195
|
body: io.Reader | null,
|
|
568
2196
|
): [Request | null, $.GoError] {
|
|
569
|
-
return NewRequestWithContext(
|
|
2197
|
+
return NewRequestWithContext(context.Background(), method, url, body)
|
|
570
2198
|
}
|
|
571
2199
|
|
|
572
2200
|
export function NewRequestWithContext(
|
|
573
|
-
|
|
2201
|
+
ctx: context.Context,
|
|
574
2202
|
method: string,
|
|
575
2203
|
url: string,
|
|
576
2204
|
body: io.Reader | null,
|
|
@@ -578,12 +2206,90 @@ export function NewRequestWithContext(
|
|
|
578
2206
|
if (method === '') {
|
|
579
2207
|
method = MethodGet
|
|
580
2208
|
}
|
|
581
|
-
|
|
582
|
-
|
|
2209
|
+
if (!isToken(method)) {
|
|
2210
|
+
return [null, errors.New(`net/http: invalid method ${JSON.stringify(method)}`)]
|
|
2211
|
+
}
|
|
2212
|
+
if (ctx == null) {
|
|
2213
|
+
return [null, errors.New('net/http: nil Context')]
|
|
2214
|
+
}
|
|
2215
|
+
const [parsedURL, err] = parseRequestURL(url)
|
|
2216
|
+
if (err != null || parsedURL == null) {
|
|
2217
|
+
return [null, err]
|
|
2218
|
+
}
|
|
2219
|
+
const bodyInfo = requestBodyInfo(body, 0)
|
|
2220
|
+
return [new Request({ Method: method, URL: parsedURL, Body: bodyInfo.Body, ContentLength: bodyInfo.ContentLength, Host: parsedURL.Host, ctx }), null]
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
export async function Get(_url: string): Promise<[Response | null, $.GoError]> {
|
|
2224
|
+
const [req, err] = NewRequest(MethodGet, _url, null)
|
|
2225
|
+
if (err != null) {
|
|
2226
|
+
return [null, err]
|
|
2227
|
+
}
|
|
2228
|
+
return await DefaultClient.Do(req)
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
export async function Head(_url: string): Promise<[Response | null, $.GoError]> {
|
|
2232
|
+
const [req, err] = NewRequest(MethodHead, _url, null)
|
|
2233
|
+
if (err != null) {
|
|
2234
|
+
return [null, err]
|
|
2235
|
+
}
|
|
2236
|
+
return await DefaultClient.Do(req)
|
|
2237
|
+
}
|
|
2238
|
+
|
|
2239
|
+
export async function Post(
|
|
2240
|
+
_url: string,
|
|
2241
|
+
contentType: string,
|
|
2242
|
+
body: io.Reader | null,
|
|
2243
|
+
): Promise<[Response | null, $.GoError]> {
|
|
2244
|
+
const [req, err] = NewRequest(MethodPost, _url, body)
|
|
2245
|
+
if (err != null || req == null) {
|
|
2246
|
+
return [null, err]
|
|
2247
|
+
}
|
|
2248
|
+
Header_Set(req.Header, 'Content-Type', contentType)
|
|
2249
|
+
return await DefaultClient.Do(req)
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
export async function PostForm(_url: string, data: any): Promise<[Response | null, $.GoError]> {
|
|
2253
|
+
return await DefaultClient.PostForm(_url, data)
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
export function ProxyFromEnvironment(_req: Request | $.VarRef<Request> | null): [any, $.GoError] {
|
|
2257
|
+
return [null, null]
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
export function ProxyURL(fixedURL: any): (req: Request | $.VarRef<Request> | null) => [any, $.GoError] {
|
|
2261
|
+
return () => [fixedURL, null]
|
|
2262
|
+
}
|
|
2263
|
+
|
|
2264
|
+
export function ReadRequest(_reader: any): [Request | null, $.GoError] {
|
|
2265
|
+
return [null, ErrNotSupported]
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
export function ReadResponse(_reader: any, _req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] {
|
|
2269
|
+
return [null, ErrNotSupported]
|
|
583
2270
|
}
|
|
584
2271
|
|
|
585
|
-
export function
|
|
586
|
-
|
|
2272
|
+
export async function ServeContent(
|
|
2273
|
+
w: ResponseWriter | null,
|
|
2274
|
+
req: Request | $.VarRef<Request> | null,
|
|
2275
|
+
_name: string,
|
|
2276
|
+
_modtime: time.Time,
|
|
2277
|
+
content: io.Reader | null,
|
|
2278
|
+
): Promise<void> {
|
|
2279
|
+
if (content == null) {
|
|
2280
|
+
NotFound(w, req)
|
|
2281
|
+
return
|
|
2282
|
+
}
|
|
2283
|
+
const [data, err] = await io.ReadAll(content)
|
|
2284
|
+
if (err != null) {
|
|
2285
|
+
Error(w, err.Error(), StatusInternalServerError)
|
|
2286
|
+
return
|
|
2287
|
+
}
|
|
2288
|
+
w?.WriteHeader(StatusOK)
|
|
2289
|
+
const request = $.pointerValueOrNil(req)
|
|
2290
|
+
if (request?.Method !== MethodHead) {
|
|
2291
|
+
w?.Write(data)
|
|
2292
|
+
}
|
|
587
2293
|
}
|
|
588
2294
|
|
|
589
2295
|
function readCloserForBody(body: io.Reader | null): io.ReadCloser | null {
|
|
@@ -596,3 +2302,18 @@ function readCloserForBody(body: io.Reader | null): io.ReadCloser | null {
|
|
|
596
2302
|
}
|
|
597
2303
|
return io.NopCloser(body)
|
|
598
2304
|
}
|
|
2305
|
+
|
|
2306
|
+
function requestBodyInfo(body: io.Reader | null, unknownLength: number): { Body: io.ReadCloser | null; ContentLength: number } {
|
|
2307
|
+
if (body == null) {
|
|
2308
|
+
return { Body: null, ContentLength: 0 }
|
|
2309
|
+
}
|
|
2310
|
+
const value = $.pointerValueOrNil<any>(body as any)
|
|
2311
|
+
if (value === NoBody) {
|
|
2312
|
+
return { Body: NoBody, ContentLength: 0 }
|
|
2313
|
+
}
|
|
2314
|
+
if (value instanceof bytes.Buffer || value instanceof bytes.Reader || value instanceof strings.Reader) {
|
|
2315
|
+
const length = value.Len()
|
|
2316
|
+
return { Body: length === 0 ? NoBody : readCloserForBody(body), ContentLength: length }
|
|
2317
|
+
}
|
|
2318
|
+
return { Body: readCloserForBody(body), ContentLength: unknownLength }
|
|
2319
|
+
}
|