goscript 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.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} +35 -8
- package/cmd/goscript/cmd-test.go +14 -0
- package/cmd/goscript/cmd-test_test.go +1 -1
- package/cmd/goscript/cmd_compile_test.go +105 -6
- package/compiler/build-flags.go +9 -10
- package/compiler/compile-request.go +12 -9
- package/compiler/compliance_test.go +0 -1
- package/compiler/config.go +2 -0
- package/compiler/gotest/request.go +28 -0
- package/compiler/gotest/runner.go +353 -27
- package/compiler/gotest/runner_test.go +400 -1
- package/compiler/gotest/testdata/browserapi/browserapi_test.go +20 -0
- package/compiler/gotest/testdata/browserapi/go.mod +3 -0
- package/compiler/lowered-program.go +24 -17
- package/compiler/lowering.go +988 -263
- package/compiler/lowering_bench_test.go +364 -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/package-graph.go +61 -4
- package/compiler/package-graph_test.go +30 -0
- package/compiler/protobuf-ts-binding.go +514 -0
- package/compiler/protobuf-ts-binding_test.go +172 -0
- package/compiler/semantic-model-types.go +17 -4
- package/compiler/semantic-model.go +709 -72
- package/compiler/semantic-model_test.go +219 -0
- package/compiler/service.go +20 -1
- package/compiler/skeleton_test.go +1008 -20
- package/compiler/typescript-emitter.go +147 -15
- package/dist/gs/builtin/builtin.d.ts +2 -2
- package/dist/gs/builtin/builtin.js +20 -0
- package/dist/gs/builtin/builtin.js.map +1 -1
- package/dist/gs/builtin/slice.d.ts +2 -1
- package/dist/gs/builtin/slice.js +34 -4
- package/dist/gs/builtin/slice.js.map +1 -1
- package/dist/gs/builtin/type.d.ts +14 -6
- package/dist/gs/builtin/type.js +224 -64
- 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 +13 -6
- package/dist/gs/compress/zlib/index.js +131 -35
- package/dist/gs/compress/zlib/index.js.map +1 -1
- package/dist/gs/crypto/sha1/index.js +2 -5
- package/dist/gs/crypto/sha1/index.js.map +1 -1
- package/dist/gs/crypto/sha256/index.js +2 -5
- package/dist/gs/crypto/sha256/index.js.map +1 -1
- package/dist/gs/crypto/sha512/index.js +2 -5
- package/dist/gs/crypto/sha512/index.js.map +1 -1
- package/dist/gs/embed/index.d.ts +6 -0
- package/dist/gs/embed/index.js +210 -5
- package/dist/gs/embed/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/fmt/fmt.d.ts +3 -3
- package/dist/gs/fmt/fmt.js +29 -16
- package/dist/gs/fmt/fmt.js.map +1 -1
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.d.ts +100 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js +564 -0
- package/dist/gs/github.com/aperturerobotics/protobuf-go-lite/index.js.map +1 -1
- package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.d.ts +45 -0
- package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js +229 -0
- package/dist/gs/github.com/go-git/go-billy/v6/osfs/index.js.map +1 -0
- 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/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 +8 -4
- 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 +12 -6
- package/dist/gs/io/io.js +87 -42
- package/dist/gs/io/io.js.map +1 -1
- package/dist/gs/math/bits/index.d.ts +31 -5
- package/dist/gs/math/bits/index.js +29 -28
- 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 +303 -6
- package/dist/gs/net/http/index.js +1615 -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.d.ts +6 -2
- package/dist/gs/os/types_js.gs.js +170 -9
- 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 +26 -6
- package/dist/gs/reflect/type.js +1498 -279
- 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.d.ts +1 -1
- package/dist/gs/strings/reader.js +11 -7
- 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/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/sync/sync.d.ts +2 -1
- package/dist/gs/sync/sync.js +37 -16
- package/dist/gs/sync/sync.js.map +1 -1
- package/dist/gs/syscall/env.js +22 -14
- package/dist/gs/syscall/env.js.map +1 -1
- package/dist/gs/syscall/js/index.js +9 -0
- package/dist/gs/syscall/js/index.js.map +1 -1
- package/dist/gs/testing/testing.js +59 -15
- 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/builtin.ts +25 -2
- package/gs/builtin/runtime-contract.test.ts +260 -18
- package/gs/builtin/slice.ts +51 -4
- package/gs/builtin/type.ts +310 -63
- 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 +159 -1
- package/gs/compress/zlib/index.ts +164 -37
- package/gs/compress/zlib/meta.json +4 -1
- package/gs/compress/zlib/parity.json +51 -0
- package/gs/crypto/sha1/index.test.ts +19 -2
- package/gs/crypto/sha1/index.ts +3 -6
- package/gs/crypto/sha256/index.test.ts +14 -2
- package/gs/crypto/sha256/index.ts +3 -6
- package/gs/crypto/sha512/index.test.ts +17 -2
- package/gs/crypto/sha512/index.ts +3 -6
- package/gs/embed/index.test.ts +87 -0
- package/gs/embed/index.ts +229 -5
- 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/fmt/fmt.test.ts +41 -3
- package/gs/fmt/fmt.ts +40 -17
- package/gs/fmt/meta.json +6 -1
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.test.ts +211 -3
- package/gs/github.com/aperturerobotics/protobuf-go-lite/index.ts +857 -1
- package/gs/github.com/go-git/go-billy/v6/osfs/index.test.ts +110 -0
- package/gs/github.com/go-git/go-billy/v6/osfs/index.ts +280 -0
- package/gs/github.com/go-git/go-billy/v6/osfs/meta.json +8 -0
- 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/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.test.ts +38 -0
- package/gs/io/fs/readdir.ts +8 -4
- package/gs/io/fs/sub.ts +8 -1
- package/gs/io/io.test.ts +77 -6
- package/gs/io/io.ts +115 -52
- package/gs/io/meta.json +7 -1
- package/gs/io/parity.json +162 -0
- package/gs/math/bits/index.test.ts +14 -1
- package/gs/math/bits/index.ts +75 -32
- 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 +797 -12
- package/gs/net/http/index.ts +1874 -136
- 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/file_unix_js.test.ts +52 -0
- package/gs/os/index.test.ts +9 -0
- package/gs/os/index.ts +1 -0
- package/gs/os/meta.json +4 -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/readdir.test.ts +56 -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 +170 -9
- package/gs/os/types_unix.gs.ts +1 -1
- package/gs/path/path.ts +11 -7
- package/gs/reflect/deepequal.test.ts +10 -1
- 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 +1980 -365
- package/gs/reflect/typefor.test.ts +540 -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/meta.json +5 -2
- package/gs/strings/parity.json +186 -0
- package/gs/strings/reader.test.ts +2 -2
- package/gs/strings/reader.ts +11 -7
- 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/type.gs.ts +9 -9
- package/gs/sync/atomic/value.gs.ts +2 -2
- package/gs/sync/meta.json +1 -0
- package/gs/sync/sync.test.ts +41 -1
- package/gs/sync/sync.ts +41 -16
- package/gs/syscall/env.ts +29 -14
- package/gs/syscall/js/index.test.ts +18 -0
- package/gs/syscall/js/index.ts +12 -0
- package/gs/testing/testing.test.ts +99 -3
- package/gs/testing/testing.ts +95 -24
- 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 -814
- package/dist/gs/github.com/aperturerobotics/starpc/srpc/index.js.map +0 -1
- package/gs/github.com/aperturerobotics/starpc/srpc/index.test.ts +0 -31
- package/gs/github.com/aperturerobotics/starpc/srpc/index.ts +0 -1233
- package/gs/github.com/aperturerobotics/starpc/srpc/meta.json +0 -46
- /package/compiler/{wasm_api.go → wasm-api.go} +0 -0
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
|
|
262
|
-
public
|
|
613
|
+
public Proto: string
|
|
614
|
+
public ProtoMajor: number
|
|
615
|
+
public ProtoMinor: number
|
|
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,14 +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
|
|
763
|
+
public ContentLength: number
|
|
764
|
+
public TransferEncoding: $.Slice<string>
|
|
765
|
+
public Close: boolean
|
|
766
|
+
public Uncompressed: boolean
|
|
767
|
+
public Trailer: Header
|
|
768
|
+
public Request: Request | $.VarRef<Request> | null
|
|
769
|
+
public TLS: any
|
|
316
770
|
|
|
317
771
|
constructor(init?: Partial<Response>) {
|
|
318
772
|
this.Status = init?.Status ?? ''
|
|
319
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
|
|
320
777
|
this.Body = init?.Body ?? null
|
|
321
778
|
this.Header = init?.Header ?? new Header()
|
|
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()
|
|
784
|
+
this.Request = init?.Request ?? null
|
|
785
|
+
this.TLS = init?.TLS ?? null
|
|
322
786
|
if (this.Status === '' && this.StatusCode !== 0) {
|
|
323
787
|
const text = StatusText(this.StatusCode)
|
|
324
788
|
this.Status = text === '' ? String(this.StatusCode) : `${this.StatusCode} ${text}`
|
|
@@ -331,8 +795,90 @@ export class Response {
|
|
|
331
795
|
Header: this.Header,
|
|
332
796
|
Status: this.Status,
|
|
333
797
|
StatusCode: this.StatusCode,
|
|
798
|
+
Proto: this.Proto,
|
|
799
|
+
ProtoMajor: this.ProtoMajor,
|
|
800
|
+
ProtoMinor: this.ProtoMinor,
|
|
801
|
+
ContentLength: this.ContentLength,
|
|
802
|
+
TransferEncoding: this.TransferEncoding,
|
|
803
|
+
Close: this.Close,
|
|
804
|
+
Uncompressed: this.Uncompressed,
|
|
805
|
+
Trailer: this.Trailer,
|
|
806
|
+
Request: this.Request,
|
|
807
|
+
TLS: this.TLS,
|
|
334
808
|
})
|
|
335
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
|
+
}
|
|
336
882
|
}
|
|
337
883
|
|
|
338
884
|
export class Client {
|
|
@@ -347,125 +893,767 @@ export class Client {
|
|
|
347
893
|
): Promise<[Response | null, $.GoError]> {
|
|
348
894
|
return await (this.Transport ?? DefaultTransport).RoundTrip(_req)
|
|
349
895
|
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
export const DefaultClient = new Client()
|
|
353
|
-
|
|
354
|
-
export interface RoundTripper {
|
|
355
|
-
RoundTrip(req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
|
|
356
|
-
}
|
|
357
896
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
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]
|
|
363
901
|
}
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
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]
|
|
368
909
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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]
|
|
373
917
|
}
|
|
374
|
-
|
|
918
|
+
Header_Set(req.Header, 'Content-Type', contentType)
|
|
919
|
+
return await this.Do(req)
|
|
375
920
|
}
|
|
376
|
-
}
|
|
377
921
|
|
|
378
|
-
|
|
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
|
+
}
|
|
379
925
|
|
|
380
|
-
|
|
381
|
-
|
|
926
|
+
public CloseIdleConnections(): void {
|
|
927
|
+
const closer = this.Transport as { CloseIdleConnections?: () => void } | null
|
|
928
|
+
closer?.CloseIdleConnections?.()
|
|
929
|
+
}
|
|
382
930
|
}
|
|
383
931
|
|
|
384
|
-
export
|
|
385
|
-
|
|
386
|
-
|
|
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()
|
|
387
958
|
}
|
|
388
959
|
|
|
389
|
-
|
|
390
|
-
|
|
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))
|
|
391
972
|
}
|
|
392
973
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
+
}
|
|
397
983
|
|
|
398
|
-
export
|
|
399
|
-
|
|
400
|
-
w: ResponseWriter | null,
|
|
401
|
-
r: Request | $.VarRef<Request> | null,
|
|
402
|
-
): void | Promise<void> {
|
|
403
|
-
return h(w, r)
|
|
984
|
+
export interface RoundTripper {
|
|
985
|
+
RoundTrip(req: Request | $.VarRef<Request> | null): [Response | null, $.GoError] | Promise<[Response | null, $.GoError]>
|
|
404
986
|
}
|
|
405
987
|
|
|
406
|
-
export class
|
|
407
|
-
|
|
408
|
-
public BaseContext: ((listener: any) => context.Context) | null
|
|
409
|
-
public Handler: Handler | null
|
|
410
|
-
public ReadHeaderTimeout: number
|
|
411
|
-
public WriteTimeout: number
|
|
988
|
+
export class Protocols {
|
|
989
|
+
private bits = 0
|
|
412
990
|
|
|
413
|
-
|
|
414
|
-
this.
|
|
415
|
-
this.BaseContext = init?.BaseContext ?? null
|
|
416
|
-
this.Handler = init?.Handler ?? null
|
|
417
|
-
this.ReadHeaderTimeout = init?.ReadHeaderTimeout ?? 0
|
|
418
|
-
this.WriteTimeout = init?.WriteTimeout ?? 0
|
|
991
|
+
public HTTP1(): boolean {
|
|
992
|
+
return (this.bits & 1) !== 0
|
|
419
993
|
}
|
|
420
994
|
|
|
421
|
-
public
|
|
422
|
-
|
|
995
|
+
public SetHTTP1(ok: boolean): void {
|
|
996
|
+
this.setBit(1, ok)
|
|
423
997
|
}
|
|
424
998
|
|
|
425
|
-
public
|
|
426
|
-
return
|
|
999
|
+
public HTTP2(): boolean {
|
|
1000
|
+
return (this.bits & 2) !== 0
|
|
427
1001
|
}
|
|
428
1002
|
|
|
429
|
-
public
|
|
430
|
-
|
|
1003
|
+
public SetHTTP2(ok: boolean): void {
|
|
1004
|
+
this.setBit(2, ok)
|
|
431
1005
|
}
|
|
432
1006
|
|
|
433
|
-
public
|
|
434
|
-
return
|
|
1007
|
+
public UnencryptedHTTP2(): boolean {
|
|
1008
|
+
return (this.bits & 4) !== 0
|
|
435
1009
|
}
|
|
436
|
-
}
|
|
437
1010
|
|
|
438
|
-
|
|
439
|
-
|
|
1011
|
+
public SetUnencryptedHTTP2(ok: boolean): void {
|
|
1012
|
+
this.setBit(4, ok)
|
|
1013
|
+
}
|
|
440
1014
|
|
|
441
|
-
|
|
442
|
-
|
|
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(',')}}`
|
|
443
1027
|
}
|
|
444
|
-
}
|
|
445
1028
|
|
|
446
|
-
|
|
447
|
-
|
|
1029
|
+
private setBit(bit: number, ok: boolean): void {
|
|
1030
|
+
this.bits = ok ? this.bits | bit : this.bits & ~bit
|
|
1031
|
+
}
|
|
448
1032
|
}
|
|
449
1033
|
|
|
450
|
-
export
|
|
451
|
-
|
|
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
|
+
}
|
|
452
1051
|
}
|
|
453
1052
|
|
|
454
|
-
export
|
|
455
|
-
|
|
456
|
-
|
|
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
|
+
}
|
|
457
1083
|
|
|
458
|
-
|
|
459
|
-
|
|
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
|
+
}
|
|
460
1113
|
|
|
461
|
-
|
|
462
|
-
|
|
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]
|
|
463
1122
|
}
|
|
464
1123
|
|
|
465
|
-
public
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
1124
|
+
public Clone(): Transport {
|
|
1125
|
+
return new Transport(this)
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
export const DefaultTransport: RoundTripper = new Transport()
|
|
1130
|
+
|
|
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
|
|
469
1657
|
}
|
|
470
1658
|
}
|
|
471
1659
|
|
|
@@ -473,6 +1661,51 @@ export function NewResponseController(rw: ResponseWriter | null): ResponseContro
|
|
|
473
1661
|
return new ResponseController(rw)
|
|
474
1662
|
}
|
|
475
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
|
+
|
|
476
1709
|
export class ServeMux implements Handler {
|
|
477
1710
|
private handlers = new Map<string, Handler>()
|
|
478
1711
|
|
|
@@ -503,14 +1736,18 @@ export class ServeMux implements Handler {
|
|
|
503
1736
|
}
|
|
504
1737
|
}
|
|
505
1738
|
|
|
506
|
-
const
|
|
1739
|
+
export const DefaultServeMux = new ServeMux()
|
|
507
1740
|
|
|
508
1741
|
export function NewServeMux(): ServeMux {
|
|
509
1742
|
return new ServeMux()
|
|
510
1743
|
}
|
|
511
1744
|
|
|
1745
|
+
export function Handle(pattern: string, handler: Handler | null): void {
|
|
1746
|
+
DefaultServeMux.Handle(pattern, handler)
|
|
1747
|
+
}
|
|
1748
|
+
|
|
512
1749
|
export function HandleFunc(pattern: string, handler: HandlerFunc): void {
|
|
513
|
-
|
|
1750
|
+
DefaultServeMux.HandleFunc(pattern, handler)
|
|
514
1751
|
}
|
|
515
1752
|
|
|
516
1753
|
export function StripPrefix(prefix: string, handler: Handler | null): Handler {
|
|
@@ -525,6 +1762,52 @@ export function StripPrefix(prefix: string, handler: Handler | null): Handler {
|
|
|
525
1762
|
}
|
|
526
1763
|
}
|
|
527
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
|
+
|
|
528
1811
|
export function Error(w: ResponseWriter | null, error: string, code: number): void {
|
|
529
1812
|
w?.WriteHeader(code)
|
|
530
1813
|
w?.Write($.stringToBytes(error + '\n'))
|
|
@@ -555,16 +1838,367 @@ export function ParseTime(text: string): [time.Time, $.GoError] {
|
|
|
555
1838
|
return [time.UnixMilli(date.getTime()), null]
|
|
556
1839
|
}
|
|
557
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
|
+
|
|
558
2192
|
export function NewRequest(
|
|
559
2193
|
method: string,
|
|
560
2194
|
url: string,
|
|
561
2195
|
body: io.Reader | null,
|
|
562
2196
|
): [Request | null, $.GoError] {
|
|
563
|
-
return NewRequestWithContext(
|
|
2197
|
+
return NewRequestWithContext(context.Background(), method, url, body)
|
|
564
2198
|
}
|
|
565
2199
|
|
|
566
2200
|
export function NewRequestWithContext(
|
|
567
|
-
|
|
2201
|
+
ctx: context.Context,
|
|
568
2202
|
method: string,
|
|
569
2203
|
url: string,
|
|
570
2204
|
body: io.Reader | null,
|
|
@@ -572,10 +2206,114 @@ export function NewRequestWithContext(
|
|
|
572
2206
|
if (method === '') {
|
|
573
2207
|
method = MethodGet
|
|
574
2208
|
}
|
|
575
|
-
|
|
576
|
-
|
|
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]
|
|
2270
|
+
}
|
|
2271
|
+
|
|
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
|
+
}
|
|
577
2293
|
}
|
|
578
2294
|
|
|
579
|
-
|
|
580
|
-
|
|
2295
|
+
function readCloserForBody(body: io.Reader | null): io.ReadCloser | null {
|
|
2296
|
+
if (body == null) {
|
|
2297
|
+
return null
|
|
2298
|
+
}
|
|
2299
|
+
const closer = body as io.Reader & Partial<io.Closer>
|
|
2300
|
+
if (typeof closer.Close === 'function') {
|
|
2301
|
+
return closer as io.ReadCloser
|
|
2302
|
+
}
|
|
2303
|
+
return io.NopCloser(body)
|
|
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 }
|
|
581
2319
|
}
|