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