visaljs 0.0.7 → 0.0.9
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 +68 -4
- package/THIRD_PARTY_LICENSES +1216 -0
- package/cli.js +4461 -321
- package/node/include/js_node.h +57 -1
- package/node/include/js_uv.h +8 -1
- package/node/prelude/_http_agent.js +5 -0
- package/node/prelude/_http_server.js +7 -0
- package/node/prelude/_stream_duplex.js +4 -0
- package/node/prelude/_stream_passthrough.js +4 -0
- package/node/prelude/_stream_readable.js +4 -0
- package/node/prelude/_stream_transform.js +4 -0
- package/node/prelude/_stream_writable.js +4 -0
- package/node/prelude/assert.js +1334 -0
- package/node/prelude/assert_strict.js +12 -0
- package/node/prelude/async_hooks.js +1 -1
- package/node/prelude/buffer.js +43 -3
- package/node/prelude/child_process.js +1098 -0
- package/node/prelude/cluster.js +52 -0
- package/node/prelude/console.js +328 -0
- package/node/prelude/constants.js +10 -0
- package/node/prelude/crypto.js +1 -1
- package/node/prelude/diagnostics_channel.js +1 -1
- package/node/prelude/dns.js +8 -2
- package/node/prelude/domain.js +115 -0
- package/node/prelude/events.js +148 -16
- package/node/prelude/fs.js +1138 -151
- package/node/prelude/fs_promises.js +117 -11
- package/node/prelude/http.js +784 -301
- package/node/prelude/http2.js +1 -1
- package/node/prelude/https.js +159 -0
- package/node/prelude/inspector.js +27 -0
- package/node/prelude/inspector_promises.js +4 -0
- package/node/prelude/internal/async_context.js +1 -1
- package/node/prelude/internal/buffer_transcode.js +44 -0
- package/node/prelude/internal/comparisons.js +916 -0
- package/node/prelude/internal/compression_streams.js +153 -0
- package/node/prelude/internal/crypto/asym.js +27 -14
- package/node/prelude/internal/crypto/cipher.js +16 -5
- package/node/prelude/internal/crypto/diffiehellman.js +3 -2
- package/node/prelude/internal/crypto/hash.js +2 -1
- package/node/prelude/internal/crypto/kdf.js +14 -9
- package/node/prelude/internal/crypto/keys.js +63 -50
- package/node/prelude/internal/crypto/random.js +2 -1
- package/node/prelude/internal/crypto/util.js +4 -4
- package/node/prelude/internal/dotenv.js +98 -0
- package/node/prelude/internal/errors.js +21 -5
- package/node/prelude/internal/events_extras.js +113 -0
- package/node/prelude/internal/events_on.js +100 -0
- package/node/prelude/internal/features.js +72 -0
- package/node/prelude/internal/fetch.js +364 -0
- package/node/prelude/internal/form_data.js +376 -0
- package/node/prelude/internal/fs_bigint_stats.js +41 -0
- package/node/prelude/internal/fs_blob.js +19 -0
- package/node/prelude/internal/fs_cp.js +709 -0
- package/node/prelude/internal/fs_dir.js +166 -0
- package/node/prelude/internal/fs_filehandle.js +287 -0
- package/node/prelude/internal/fs_glob.js +296 -0
- package/node/prelude/internal/fs_statfs.js +42 -0
- package/node/prelude/internal/fs_stats.js +89 -0
- package/node/prelude/internal/fs_streams.js +541 -0
- package/node/prelude/internal/fs_utils.js +286 -0
- package/node/prelude/internal/fs_watch.js +580 -0
- package/node/prelude/internal/http_agent.js +114 -2
- package/node/prelude/internal/http_symbols.js +3 -0
- package/node/prelude/internal/mime.js +249 -0
- package/node/prelude/internal/net.js +1 -0
- package/node/prelude/internal/net_bound.js +123 -0
- package/node/prelude/internal/net_onread.js +73 -0
- package/node/prelude/internal/net_tos.js +54 -0
- package/node/prelude/internal/parse_args.js +288 -0
- package/node/prelude/internal/perf_histogram.js +435 -0
- package/node/prelude/internal/perf_observe.js +125 -0
- package/node/prelude/internal/performance_global.js +19 -0
- package/node/prelude/internal/process_events.js +49 -1
- package/node/prelude/internal/process_memory.js +14 -0
- package/node/prelude/internal/process_more.js +414 -0
- package/node/prelude/internal/process_signals.js +2 -1
- package/node/prelude/internal/process_stdin.js +52 -0
- package/node/prelude/internal/process_stdio.js +36 -0
- package/node/prelude/internal/querystring.js +0 -0
- package/node/prelude/internal/readline.js +338 -0
- package/node/prelude/internal/socketaddress.js +8 -4
- package/node/prelude/internal/source_map.js +122 -0
- package/node/prelude/internal/stream_compose.js +74 -0
- package/node/prelude/internal/stream_from.js +6 -0
- package/node/prelude/internal/stream_operators.js +23 -0
- package/node/prelude/internal/stream_pipeline.js +7 -0
- package/node/prelude/internal/stream_transform.js +10 -0
- package/node/prelude/internal/stream_web_adapters.js +143 -0
- package/node/prelude/internal/streams/add_abort_signal.js +58 -0
- package/node/prelude/internal/streams/compose.js +244 -0
- package/node/prelude/internal/streams/destroy.js +358 -0
- package/node/prelude/internal/streams/duplex.js +199 -0
- package/node/prelude/internal/streams/duplexify.js +378 -0
- package/node/prelude/internal/streams/end_of_stream.js +387 -0
- package/node/prelude/internal/streams/errors.js +216 -0
- package/node/prelude/internal/streams/from.js +211 -0
- package/node/prelude/internal/streams/legacy.js +120 -0
- package/node/prelude/internal/streams/operators.js +375 -0
- package/node/prelude/internal/streams/passthrough.js +45 -0
- package/node/prelude/internal/streams/pipeline.js +468 -0
- package/node/prelude/internal/streams/readable.js +1835 -0
- package/node/prelude/internal/streams/state.js +48 -0
- package/node/prelude/internal/streams/transform.js +203 -0
- package/node/prelude/internal/streams/utils.js +355 -0
- package/node/prelude/internal/streams/validators.js +108 -0
- package/node/prelude/internal/streams/writable.js +1160 -0
- package/node/prelude/internal/structured_clone.js +238 -0
- package/node/prelude/internal/system_errors.js +37 -0
- package/node/prelude/internal/test_runner/mock.js +431 -0
- package/node/prelude/internal/test_runner/reporters.js +404 -0
- package/node/prelude/internal/test_runner/test.js +1571 -0
- package/node/prelude/internal/timers.js +38 -7
- package/node/prelude/internal/timers_global.js +1 -1
- package/node/prelude/internal/tls/common.js +8 -2
- package/node/prelude/internal/tls/wrap.js +5 -16
- package/node/prelude/internal/tls/x509.js +1 -1
- package/node/prelude/internal/tty_read_stream.js +39 -0
- package/node/prelude/internal/tty_streams.js +3 -0
- package/node/prelude/internal/util_debuglog.js +68 -0
- package/node/prelude/internal/util_diff.js +63 -0
- package/node/prelude/internal/util_more.js +111 -0
- package/node/prelude/internal/util_styles.js +76 -0
- package/node/prelude/internal/zlib_engines.js +1 -1
- package/node/prelude/module.js +219 -8
- package/node/prelude/net.js +197 -49
- package/node/prelude/os.js +88 -11
- package/node/prelude/path.js +11 -7
- package/node/prelude/path_posix.js +1 -1
- package/node/prelude/path_win32.js +1 -1
- package/node/prelude/perf_hooks.js +151 -0
- package/node/prelude/process.js +36 -1
- package/node/prelude/punycode.js +188 -0
- package/node/prelude/querystring.js +1 -1
- package/node/prelude/readline.js +8 -0
- package/node/prelude/readline_promises.js +88 -0
- package/node/prelude/repl.js +38 -0
- package/node/prelude/sea.js +23 -0
- package/node/prelude/stream.js +70 -1332
- package/node/prelude/stream_consumers.js +34 -0
- package/node/prelude/stream_promises.js +1 -1
- package/node/prelude/stream_web.js +15 -2
- package/node/prelude/string_decoder.js +1 -1
- package/node/prelude/sys.js +7 -0
- package/node/prelude/test.js +56 -0
- package/node/prelude/test_reporters.js +269 -0
- package/node/prelude/timers.js +7 -84
- package/node/prelude/timers_promises.js +1 -1
- package/node/prelude/tls.js +2 -1
- package/node/prelude/trace_events.js +52 -0
- package/node/prelude/tty.js +10 -4
- package/node/prelude/url.js +88 -27
- package/node/prelude/util.js +121 -5
- package/node/prelude/util_types.js +4 -3
- package/node/prelude/v8.js +814 -0
- package/node/prelude/vm.js +86 -0
- package/node/prelude/wasi.js +10 -0
- package/node/prelude/worker_threads.js +56 -0
- package/node/prelude/zlib.js +58 -25
- package/node/src/host_brotli.c +59 -11
- package/node/src/host_fs.c +342 -11
- package/node/src/host_fswatch.c +170 -0
- package/node/src/host_http.c +292 -39
- package/node/src/host_loop.c +25 -2
- package/node/src/host_net.c +130 -16
- package/node/src/host_os.c +55 -1
- package/node/src/host_proc.c +291 -0
- package/node/src/host_process.c +488 -0
- package/node/src/host_sys.c +64 -2
- package/node/src/host_zlib.c +27 -7
- package/node/src/host_zstd.c +41 -6
- package/node/third_party/hdrhistogram/LICENSE +42 -0
- package/node/third_party/hdrhistogram/README.md +5 -0
- package/node/third_party/libuv/LICENSE +0 -0
- package/node/third_party/libuv/LICENSE-extra +36 -0
- package/node/third_party/libuv/include/uv/aix.h +0 -0
- package/node/third_party/libuv/include/uv/bsd.h +0 -0
- package/node/third_party/libuv/include/uv/darwin.h +0 -0
- package/node/third_party/libuv/include/uv/errno.h +0 -0
- package/node/third_party/libuv/include/uv/linux.h +0 -0
- package/node/third_party/libuv/include/uv/os390.h +0 -0
- package/node/third_party/libuv/include/uv/posix.h +0 -0
- package/node/third_party/libuv/include/uv/sunos.h +0 -0
- package/node/third_party/libuv/include/uv/threadpool.h +0 -0
- package/node/third_party/libuv/include/uv/tree.h +0 -0
- package/node/third_party/libuv/include/uv/unix.h +0 -0
- package/node/third_party/libuv/include/uv/version.h +0 -0
- package/node/third_party/libuv/include/uv/win.h +0 -0
- package/node/third_party/libuv/include/uv.h +0 -0
- package/node/third_party/libuv/src/fs-poll.c +0 -0
- package/node/third_party/libuv/src/heap-inl.h +0 -0
- package/node/third_party/libuv/src/idna.c +0 -0
- package/node/third_party/libuv/src/idna.h +0 -0
- package/node/third_party/libuv/src/inet.c +0 -0
- package/node/third_party/libuv/src/queue.h +0 -0
- package/node/third_party/libuv/src/random.c +0 -0
- package/node/third_party/libuv/src/strscpy.c +0 -0
- package/node/third_party/libuv/src/strscpy.h +0 -0
- package/node/third_party/libuv/src/strtok.c +0 -0
- package/node/third_party/libuv/src/strtok.h +0 -0
- package/node/third_party/libuv/src/thread-common.c +0 -0
- package/node/third_party/libuv/src/threadpool.c +0 -0
- package/node/third_party/libuv/src/timer.c +0 -0
- package/node/third_party/libuv/src/unix/async.c +0 -0
- package/node/third_party/libuv/src/unix/bsd-ifaddrs.c +0 -0
- package/node/third_party/libuv/src/unix/core.c +0 -0
- package/node/third_party/libuv/src/unix/darwin-proctitle.c +0 -0
- package/node/third_party/libuv/src/unix/darwin-stub.h +0 -0
- package/node/third_party/libuv/src/unix/darwin-syscalls.h +0 -0
- package/node/third_party/libuv/src/unix/darwin.c +0 -0
- package/node/third_party/libuv/src/unix/dl.c +0 -0
- package/node/third_party/libuv/src/unix/fs.c +0 -0
- package/node/third_party/libuv/src/unix/fsevents.c +0 -0
- package/node/third_party/libuv/src/unix/getaddrinfo.c +0 -0
- package/node/third_party/libuv/src/unix/getnameinfo.c +0 -0
- package/node/third_party/libuv/src/unix/internal.h +0 -0
- package/node/third_party/libuv/src/unix/kqueue.c +0 -0
- package/node/third_party/libuv/src/unix/linux.c +0 -0
- package/node/third_party/libuv/src/unix/loop-watcher.c +0 -0
- package/node/third_party/libuv/src/unix/loop.c +0 -0
- package/node/third_party/libuv/src/unix/pipe.c +0 -0
- package/node/third_party/libuv/src/unix/poll.c +0 -0
- package/node/third_party/libuv/src/unix/process.c +0 -0
- package/node/third_party/libuv/src/unix/procfs-exepath.c +0 -0
- package/node/third_party/libuv/src/unix/proctitle.c +0 -0
- package/node/third_party/libuv/src/unix/random-devurandom.c +0 -0
- package/node/third_party/libuv/src/unix/random-getentropy.c +0 -0
- package/node/third_party/libuv/src/unix/random-getrandom.c +0 -0
- package/node/third_party/libuv/src/unix/random-sysctl-linux.c +0 -0
- package/node/third_party/libuv/src/unix/signal.c +0 -0
- package/node/third_party/libuv/src/unix/stream.c +0 -0
- package/node/third_party/libuv/src/unix/tcp.c +0 -0
- package/node/third_party/libuv/src/unix/thread.c +0 -0
- package/node/third_party/libuv/src/unix/tty.c +0 -0
- package/node/third_party/libuv/src/unix/udp.c +0 -0
- package/node/third_party/libuv/src/uv-common.c +0 -0
- package/node/third_party/libuv/src/uv-common.h +0 -0
- package/node/third_party/libuv/src/uv-data-getter-setters.c +0 -0
- package/node/third_party/libuv/src/version.c +0 -0
- package/node/third_party/llhttp/LICENSE +0 -0
- package/node/third_party/llhttp/api.c +0 -0
- package/node/third_party/llhttp/http.c +0 -0
- package/node/third_party/llhttp/llhttp.c +0 -0
- package/node/third_party/llhttp/llhttp.h +0 -0
- package/node/third_party/nodejs/LICENSE +47 -0
- package/node/third_party/nodejs/README.md +5 -0
- package/node/third_party/parseargs/LICENSE +21 -0
- package/node/third_party/parseargs/README.md +5 -0
- package/node/third_party/punycode/LICENSE +20 -0
- package/node/third_party/punycode/README.md +5 -0
- package/node/third_party/zlib/LICENSE.chromium +27 -0
- package/package.json +1 -1
- package/prebuilt/darwin-arm64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/darwin-arm64/brotli-7995bbdacc52e51c.a +0 -0
- package/prebuilt/darwin-arm64/uv-72ab666eae197cd8.a +0 -0
- package/prebuilt/darwin-arm64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/darwin-arm64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/darwin-x64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/darwin-x64/brotli-7995bbdacc52e51c.a +0 -0
- package/prebuilt/darwin-x64/uv-72ab666eae197cd8.a +0 -0
- package/prebuilt/darwin-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/darwin-x64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-arm64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-arm64/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-arm64/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-arm64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-arm64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-arm64-musl/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-arm64-musl/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-arm64-musl/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-arm64-musl/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-arm64-musl/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-x64/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-x64/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-x64/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-x64/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/linux-x64-musl/boringssl-4a47438eaf7e73ba.a +0 -0
- package/prebuilt/linux-x64-musl/brotli-65481ff838ce9def.a +0 -0
- package/prebuilt/linux-x64-musl/uv-f1353f50ff983f3c.a +0 -0
- package/prebuilt/linux-x64-musl/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/linux-x64-musl/zstd-015315ac7c784c73.a +0 -0
- package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
- package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
- package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
- package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
- package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
- package/runtime/include/js_atoms.gen.h +0 -0
- package/runtime/include/js_atoms.h +5 -1
- package/runtime/include/js_bigint.h +1 -1
- package/runtime/include/js_buffer.h +4 -4
- package/runtime/include/js_builtins.h +41 -3
- package/runtime/include/js_console.h +9 -4
- package/runtime/include/js_convert.h +1 -1
- package/runtime/include/js_core.def +5 -4
- package/runtime/include/js_crypto.h +1 -1
- package/runtime/include/js_error.h +1 -1
- package/runtime/include/js_exception.h +21 -1
- package/runtime/include/js_function.h +33 -9
- package/runtime/include/js_gc.h +172 -30
- package/runtime/include/js_generator.h +2 -2
- package/runtime/include/js_host.h +19 -1
- package/runtime/include/js_jobs.h +5 -5
- package/runtime/include/js_module.h +1 -1
- package/runtime/include/js_object.h +142 -15
- package/runtime/include/js_ops.h +107 -5
- package/runtime/include/js_promise.h +3 -3
- package/runtime/include/js_regexp.h +1 -1
- package/runtime/include/js_runtime.h +0 -0
- package/runtime/include/js_symbol.h +1 -1
- package/runtime/include/js_web.h +5 -1
- package/runtime/include/jscell.h +22 -15
- package/runtime/include/jscontext.h +82 -39
- package/runtime/include/jsnum.h +1 -1
- package/runtime/include/jsstring.h +19 -6
- package/runtime/include/jsvalue.h +7 -3
- package/runtime/src/arguments.c +2 -2
- package/runtime/src/array.c +4 -1
- package/runtime/src/arraybuffer.c +19 -7
- package/runtime/src/async_iter.c +4 -3
- package/runtime/src/atoms.c +38 -4
- package/runtime/src/base64.c +2 -2
- package/runtime/src/bigint.c +1 -1
- package/runtime/src/blob.c +22 -9
- package/runtime/src/boringssl_support.cc +1 -1
- package/runtime/src/buffer.c +299 -91
- package/runtime/src/builtin_table.c +1 -1
- package/runtime/src/builtins.c +30 -4
- package/runtime/src/builtins_util.h +0 -0
- package/runtime/src/collections.c +18 -3
- package/runtime/src/components.c +1 -1
- package/runtime/src/console.c +14 -7
- package/runtime/src/convert.c +0 -0
- package/runtime/src/core_all.c +1 -1
- package/runtime/src/crypto.c +1 -1
- package/runtime/src/crypto_asym.c +1 -1
- package/runtime/src/crypto_bn.c +1 -1
- package/runtime/src/crypto_cipher.c +10 -3
- package/runtime/src/crypto_hash.c +1 -1
- package/runtime/src/crypto_host.c +1 -1
- package/runtime/src/crypto_internal.h +1 -1
- package/runtime/src/crypto_kdf.c +1 -1
- package/runtime/src/crypto_key.c +14 -1
- package/runtime/src/crypto_pkey.c +1 -1
- package/runtime/src/crypto_subtle.c +142 -58
- package/runtime/src/crypto_tls.c +1 -1
- package/runtime/src/date.c +5 -2
- package/runtime/src/domexception.c +2 -2
- package/runtime/src/elements.c +56 -17
- package/runtime/src/elements.h +7 -2
- package/runtime/src/encoding.c +1 -1
- package/runtime/src/error.c +4 -4
- package/runtime/src/events.c +42 -14
- package/runtime/src/exception.c +1 -1
- package/runtime/src/fetch.c +241 -17
- package/runtime/src/function.c +31 -19
- package/runtime/src/gc.c +1720 -211
- package/runtime/src/gc_visit.h +87 -0
- package/runtime/src/generator.c +5 -3
- package/runtime/src/global.c +14 -6
- package/runtime/src/headers.c +10 -10
- package/runtime/src/inspect.c +76 -17
- package/runtime/src/internal.h +9 -6
- package/runtime/src/iterate.c +94 -2
- package/runtime/src/iterator.c +1 -1
- package/runtime/src/jobs.c +11 -5
- package/runtime/src/jsnum.c +2 -2
- package/runtime/src/json.c +1 -1
- package/runtime/src/math.c +9 -1
- package/runtime/src/module.c +3 -3
- package/runtime/src/number.c +0 -0
- package/runtime/src/object.c +107 -74
- package/runtime/src/ops.c +21 -3
- package/runtime/src/os.c +192 -1
- package/runtime/src/os.h +33 -2
- package/runtime/src/process.c +73 -8
- package/runtime/src/promise.c +4 -4
- package/runtime/src/proxy.c +1 -1
- package/runtime/src/reflect.c +1 -1
- package/runtime/src/regexp.c +700 -11
- package/runtime/src/regexp_anchor.h +1 -1
- package/runtime/src/regexp_message.h +1 -1
- package/runtime/src/shape.c +78 -20
- package/runtime/src/shape.h +0 -0
- package/runtime/src/streams.c +22 -8
- package/runtime/src/streams.h +0 -0
- package/runtime/src/string.c +64 -25
- package/runtime/src/string_builtins.c +7 -7
- package/runtime/src/symbol.c +2 -7
- package/runtime/src/test_host.c +3 -3
- package/runtime/src/tls_roots.gen.h +102 -620
- package/runtime/src/typedarray.c +8 -3
- package/runtime/src/tznames.gen.h +2 -1
- package/runtime/src/uri.c +1 -1
- package/runtime/src/url.c +42 -39
- package/runtime/src/weak.c +110 -36
- package/runtime/src/web.c +21 -3
- package/runtime/src/wrapper.c +0 -0
- package/runtime/src/writable.c +7 -6
- package/runtime/third_party/ada-idna/LICENSE-MIT +0 -0
- package/runtime/third_party/ada-idna/README.md +7 -7
- package/runtime/third_party/ada-idna/convert.mjs +89 -27
- package/runtime/third_party/ada-idna/idna.c +30 -31
- package/runtime/third_party/ada-idna/idna.h +0 -0
- package/runtime/third_party/ada-idna/idna_tables.h +2272 -1952
- package/runtime/third_party/boringssl/third_party/fiat/LICENSE +15 -0
- package/runtime/third_party/mozilla-ca/LICENSE +373 -0
- package/runtime/third_party/mozilla-ca/README.md +9 -0
- package/runtime/third_party/nodejs/LICENSE +47 -0
- package/runtime/third_party/nodejs/README.md +7 -0
- package/runtime/third_party/quickjs-dtoa/LICENSE +0 -0
- package/runtime/third_party/quickjs-dtoa/README.md +1 -1
- package/runtime/third_party/quickjs-dtoa/dtoa.c +0 -0
- package/runtime/third_party/quickjs-dtoa/dtoa.h +0 -0
- package/runtime/third_party/quickjs-dtoa/dtoa_compat.h +0 -0
- package/runtime/third_party/quickjs-regexp/LICENSE +0 -0
- package/runtime/third_party/quickjs-regexp/README.md +2 -2
- package/runtime/third_party/quickjs-regexp/cutils.c +0 -0
- package/runtime/third_party/quickjs-regexp/cutils.h +0 -0
- package/runtime/third_party/quickjs-regexp/libregexp-opcode.h +0 -0
- package/runtime/third_party/quickjs-regexp/libregexp.c +0 -0
- package/runtime/third_party/quickjs-regexp/libregexp.h +0 -0
- package/runtime/third_party/quickjs-regexp/libunicode-table.h +0 -0
- package/runtime/third_party/quickjs-regexp/libunicode.c +0 -0
- package/runtime/third_party/quickjs-regexp/libunicode.h +0 -0
- package/runtime/third_party/unicode/LICENSE +39 -0
- package/runtime/third_party/unicode/README.md +9 -0
- package/runtime/third_party/v8-ieee754/LICENSE +0 -0
- package/runtime/third_party/v8-ieee754/LICENSE.fdlibm +10 -0
- package/runtime/third_party/v8-ieee754/README.md +3 -2
- package/runtime/third_party/v8-ieee754/convert.py +0 -0
- package/runtime/third_party/v8-ieee754/ieee754.c +0 -0
- package/runtime/third_party/v8-ieee754/ieee754.h +0 -0
- package/runtime/tools/regexp_compile.c +2 -2
package/node/prelude/http.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// node:http
|
|
1
|
+
// node:http, the server side: createServer, Server, IncomingMessage and
|
|
2
2
|
// ServerResponse. As in Node, a Server is a net.Server and a request's socket a net.Socket, whose
|
|
3
3
|
// bytes llhttp parses in C (node/src/host_http.c) before JS would see them. Responses are
|
|
4
4
|
// written as Node writes them: the status line, the headers in the order set, then Date,
|
|
@@ -7,12 +7,16 @@
|
|
|
7
7
|
// upgrade (or CONNECT) hands the socket over with 'upgrade' ('connect'). Idle keep-alive
|
|
8
8
|
// connections close after keepAliveTimeout; server.timeout and setTimeout are the sockets'.
|
|
9
9
|
// Not yet: the client, headersTimeout and requestTimeout, trailers.
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
httpAttach, httpBusy, httpDate, httpPending, httpWatchDrain, httpDetach, httpExpired, httpFedClose, httpFedOpen, httpFeed, httpFlush, httpParsers, httpWrite, timerUnref,
|
|
12
|
+
} from "visaljs:host";
|
|
11
13
|
import { bindContext, currentContext, runInContext } from "./internal/async_context.js";
|
|
12
|
-
import { codeError, invalidArgType, outOfRange, validateInteger, validateString } from "./internal/errors.js";
|
|
13
|
-
import { Agent, globalAgent } from "./internal/http_agent.js";
|
|
14
|
+
import { codeError, invalidArgType, invalidArgValue, outOfRange, validateInteger, validateString } from "./internal/errors.js";
|
|
15
|
+
import { Agent, globalAgent, setGlobalAgent } from "./internal/http_agent.js";
|
|
16
|
+
import { kConnectionsCheckingInterval } from "./internal/http_symbols.js";
|
|
14
17
|
import { createConnection, Server as NetServer } from "./net.js";
|
|
15
|
-
import { Readable, Stream } from "./stream.js";
|
|
18
|
+
import { addAbortSignal, Readable, Stream } from "./stream.js";
|
|
19
|
+
import { callableClass } from "./internal/features.js";
|
|
16
20
|
|
|
17
21
|
export const METHODS = ["ACL", "BIND", "CHECKOUT", "CONNECT", "COPY", "DELETE", "GET", "HEAD", "LINK", "LOCK", "M-SEARCH", "MERGE",
|
|
18
22
|
"MKACTIVITY", "MKCALENDAR", "MKCOL", "MOVE", "NOTIFY", "OPTIONS", "PATCH", "POST", "PROPFIND", "PROPPATCH", "PURGE", "PUT", "QUERY",
|
|
@@ -75,13 +79,18 @@ export class IncomingMessage extends Readable {
|
|
|
75
79
|
this.aborted = false;
|
|
76
80
|
this.statusCode = null;
|
|
77
81
|
this.statusMessage = null;
|
|
82
|
+
/* Node's: read yet, and dumped (stream utils' isServerRequest tells a request by them) */
|
|
83
|
+
this._consuming = false;
|
|
84
|
+
this._dumped = false;
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
get connection() {
|
|
81
88
|
return this.socket;
|
|
82
89
|
}
|
|
83
90
|
|
|
84
|
-
_read() {
|
|
91
|
+
_read() {
|
|
92
|
+
this._consuming = true;
|
|
93
|
+
}
|
|
85
94
|
|
|
86
95
|
/* a response nobody reads: its body is discarded, so the socket isn't held up */
|
|
87
96
|
_dump() {
|
|
@@ -98,12 +107,35 @@ export class IncomingMessage extends Readable {
|
|
|
98
107
|
|
|
99
108
|
_finish() {
|
|
100
109
|
this.complete = true;
|
|
101
|
-
this.push(null);
|
|
110
|
+
if (!this._readableState.ended) this.push(null);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* Node's: a request without a body (the server's optimizeEmptyRequests) ended and closed at once,
|
|
114
|
+
* without its 'data', 'end' and 'close' */
|
|
115
|
+
_dumpAndCloseReadable() {
|
|
116
|
+
this._dumped = true;
|
|
117
|
+
const state = this._readableState;
|
|
118
|
+
state.ended = true;
|
|
119
|
+
state.endEmitted = true;
|
|
120
|
+
state.destroyed = true;
|
|
121
|
+
state.closed = true;
|
|
122
|
+
state.closeEmitted = true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Node's: an AbortSignal that aborts when the connection closes before the response finished */
|
|
126
|
+
get signal() {
|
|
127
|
+
if (!this._abortController) {
|
|
128
|
+
this._abortController = new AbortController();
|
|
129
|
+
if (this.destroyed && (!this.readableEnded || !this.complete)) this._abortController.abort();
|
|
130
|
+
}
|
|
131
|
+
return this._abortController.signal;
|
|
102
132
|
}
|
|
103
133
|
|
|
104
|
-
/* the connection closed before the
|
|
134
|
+
/* the connection closed before the response was finished (Node's abortIncoming): destroyed, and
|
|
135
|
+
* 'aborted' if its body wasn't all read (_destroy) */
|
|
105
136
|
_abort() {
|
|
106
|
-
|
|
137
|
+
this._abortController?.abort();
|
|
138
|
+
if (this.destroyed) return;
|
|
107
139
|
const e = new Error("aborted");
|
|
108
140
|
e.code = "ECONNRESET";
|
|
109
141
|
this.destroy(e);
|
|
@@ -136,10 +168,16 @@ const SINGLE = new Set(["age", "authorization", "content-length", "content-type"
|
|
|
136
168
|
function addHeader(headers, name, value, join) {
|
|
137
169
|
const key = name.toLowerCase();
|
|
138
170
|
const existing = headers[key];
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
else
|
|
171
|
+
if (key === "set-cookie") {
|
|
172
|
+
if (existing !== undefined) existing.push(value);
|
|
173
|
+
else headers[key] = [value];
|
|
174
|
+
} else if (SINGLE.has(key) && !join) {
|
|
175
|
+
if (existing === undefined) headers[key] = value; /* duplicates dropped */
|
|
176
|
+
} else if (typeof existing === "string") { /* as Node's: not a value inherited from Object.prototype */
|
|
177
|
+
headers[key] = existing + (key === "cookie" ? "; " : ", ") + value;
|
|
178
|
+
} else {
|
|
179
|
+
headers[key] = value;
|
|
180
|
+
}
|
|
143
181
|
}
|
|
144
182
|
|
|
145
183
|
/* ---- OutgoingMessage: a response's (and a request's) headers ------------------------------------ */
|
|
@@ -161,6 +199,26 @@ export function validateHeaderValue(name, value) {
|
|
|
161
199
|
|
|
162
200
|
const headersSent = (what) => codeError(Error, "ERR_HTTP_HEADERS_SENT", "Cannot " + what + " headers after they are sent to the client");
|
|
163
201
|
|
|
202
|
+
/* Node's write_: a chunk is a string or bytes */
|
|
203
|
+
function validateChunk(chunk) {
|
|
204
|
+
if (chunk === null) throw codeError(TypeError, "ERR_STREAM_NULL_VALUES", "May not write null values to stream");
|
|
205
|
+
if (typeof chunk !== "string" && !(chunk instanceof Uint8Array)) {
|
|
206
|
+
throw invalidArgType("chunk", "of type string or an instance of Buffer or Uint8Array", chunk);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* llhttp's error, as Node's: its code and reason, how far it parsed the read, and the read */
|
|
211
|
+
function parseError(code, reason, bytesParsed, rawPacket) {
|
|
212
|
+
const e = new Error("Parse Error: " + reason);
|
|
213
|
+
e.code = code;
|
|
214
|
+
e.reason = reason;
|
|
215
|
+
e.bytesParsed = bytesParsed;
|
|
216
|
+
if (rawPacket !== undefined) {
|
|
217
|
+
e.rawPacket = Buffer.isBuffer(rawPacket) ? rawPacket : Buffer.from(rawPacket.buffer, rawPacket.byteOffset, rawPacket.byteLength);
|
|
218
|
+
}
|
|
219
|
+
return e;
|
|
220
|
+
}
|
|
221
|
+
|
|
164
222
|
export class OutgoingMessage extends Stream {
|
|
165
223
|
constructor() {
|
|
166
224
|
super();
|
|
@@ -168,7 +226,7 @@ export class OutgoingMessage extends Stream {
|
|
|
168
226
|
this.destroyed = false;
|
|
169
227
|
this.writableCorked = 0;
|
|
170
228
|
this.writableNeedDrain = false;
|
|
171
|
-
this.
|
|
229
|
+
this._highWaterMark = 65536; /* Node's default, until there's a socket */
|
|
172
230
|
this.headersSent = false;
|
|
173
231
|
this.finished = false;
|
|
174
232
|
this.writableEnded = false;
|
|
@@ -182,10 +240,18 @@ export class OutgoingMessage extends Stream {
|
|
|
182
240
|
this._trailer = "";
|
|
183
241
|
this._headers = new Map(); /* lowercase name → [name as set, value] */
|
|
184
242
|
this.socket = null;
|
|
243
|
+
/* Node's: the head once stored, and the bytes kept until there's a socket */
|
|
244
|
+
this.outputData = [];
|
|
245
|
+
this.outputSize = 0;
|
|
246
|
+
this._header = null;
|
|
247
|
+
this._headerSent = false;
|
|
248
|
+
this._last = false;
|
|
249
|
+
this.shouldKeepAlive = true;
|
|
250
|
+
this._closed = false;
|
|
185
251
|
}
|
|
186
252
|
|
|
187
253
|
setHeader(name, value) {
|
|
188
|
-
if (this.headersSent) throw headersSent("set");
|
|
254
|
+
if (this._header || this.headersSent) throw headersSent("set");
|
|
189
255
|
validateHeaderName(name);
|
|
190
256
|
validateHeaderValue(name, value);
|
|
191
257
|
this._headers.set(String(name).toLowerCase(), [String(name), value]);
|
|
@@ -193,7 +259,7 @@ export class OutgoingMessage extends Stream {
|
|
|
193
259
|
}
|
|
194
260
|
|
|
195
261
|
appendHeader(name, value) {
|
|
196
|
-
if (this.headersSent) throw headersSent("append");
|
|
262
|
+
if (this._header || this.headersSent) throw headersSent("append");
|
|
197
263
|
validateHeaderName(name);
|
|
198
264
|
validateHeaderValue(name, value);
|
|
199
265
|
const key = String(name).toLowerCase();
|
|
@@ -203,20 +269,94 @@ export class OutgoingMessage extends Stream {
|
|
|
203
269
|
return this;
|
|
204
270
|
}
|
|
205
271
|
|
|
272
|
+
/* the socket's, as Node's, else the default */
|
|
273
|
+
get writableHighWaterMark() {
|
|
274
|
+
return this.socket ? this.socket.writableHighWaterMark : this._highWaterMark;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
set writableHighWaterMark(value) {
|
|
278
|
+
this._highWaterMark = value;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* headers from a Headers or a Map, as Node's: set-cookie's values together */
|
|
282
|
+
setHeaders(headers) {
|
|
283
|
+
if (this._header || this.headersSent) throw headersSent("set");
|
|
284
|
+
if (!headers || Array.isArray(headers) || typeof headers.keys !== "function" || typeof headers.get !== "function") {
|
|
285
|
+
throw invalidArgType("headers", "an instance of Headers or Map", headers);
|
|
286
|
+
}
|
|
287
|
+
let cookies = null;
|
|
288
|
+
for (const [key, value] of headers) {
|
|
289
|
+
if (key === "set-cookie") {
|
|
290
|
+
cookies ??= [];
|
|
291
|
+
if (Array.isArray(value)) cookies.push(...value);
|
|
292
|
+
else cookies.push(value);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
this.setHeader(key, value);
|
|
296
|
+
}
|
|
297
|
+
if (cookies !== null) this.setHeader("set-cookie", cookies);
|
|
298
|
+
return this;
|
|
299
|
+
}
|
|
300
|
+
|
|
206
301
|
get connection() {
|
|
207
302
|
return this.socket;
|
|
208
303
|
}
|
|
209
304
|
|
|
305
|
+
set connection(value) {
|
|
306
|
+
this.socket = value;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/* as Node's: what's kept until there's a socket, and the socket's own */
|
|
210
310
|
get writableLength() {
|
|
211
|
-
return 0;
|
|
311
|
+
return this.outputSize + (this.socket ? this.socket.writableLength : 0);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
get writableObjectMode() {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
get errored() {
|
|
319
|
+
return this._errored ?? null;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
get closed() {
|
|
323
|
+
return this._closed;
|
|
212
324
|
}
|
|
213
325
|
|
|
214
326
|
cork() {
|
|
215
327
|
this.writableCorked++;
|
|
328
|
+
if (this.socket) this.socket.cork();
|
|
216
329
|
}
|
|
217
330
|
|
|
218
331
|
uncork() {
|
|
219
332
|
if (this.writableCorked) this.writableCorked--;
|
|
333
|
+
if (this.socket) this.socket.uncork();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* the socket's timeout, once there is a socket, as Node's */
|
|
337
|
+
setTimeout(msecs, callback) {
|
|
338
|
+
if (callback) this.on("timeout", callback);
|
|
339
|
+
if (!this.socket) this.once("socket", (socket) => socket.setTimeout(msecs));
|
|
340
|
+
else this.socket.setTimeout(msecs);
|
|
341
|
+
return this;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/* as Node's: the socket destroyed with it, or 'close' on the next tick without one */
|
|
345
|
+
destroy(error) {
|
|
346
|
+
if (this.destroyed) return this;
|
|
347
|
+
this.destroyed = true;
|
|
348
|
+
this._errored = error;
|
|
349
|
+
if (this.socket) {
|
|
350
|
+
this.socket.destroy(error);
|
|
351
|
+
} else {
|
|
352
|
+
process.nextTick(() => {
|
|
353
|
+
if (!this._closed) {
|
|
354
|
+
this._closed = true;
|
|
355
|
+
this.emit("close");
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return this;
|
|
220
360
|
}
|
|
221
361
|
|
|
222
362
|
getHeader(name) {
|
|
@@ -231,7 +371,7 @@ export class OutgoingMessage extends Stream {
|
|
|
231
371
|
|
|
232
372
|
removeHeader(name) {
|
|
233
373
|
validateString(name, "name");
|
|
234
|
-
if (this.headersSent) throw headersSent("remove");
|
|
374
|
+
if (this._header || this.headersSent) throw headersSent("remove");
|
|
235
375
|
if (name.toLowerCase() === "content-length") this._removedContLen = true;
|
|
236
376
|
this._headers.delete(String(name).toLowerCase());
|
|
237
377
|
}
|
|
@@ -250,6 +390,192 @@ export class OutgoingMessage extends Stream {
|
|
|
250
390
|
return [...this._headers.values()].map(([name]) => name);
|
|
251
391
|
}
|
|
252
392
|
|
|
393
|
+
/* Node's OutgoingMessage, as a ClientRequest (and an OutgoingMessage made by hand) writes: the
|
|
394
|
+
* head stored, then the bytes through the socket (ServerResponse has its own, on llhttp's
|
|
395
|
+
* connection). _storeHeader: the first line, the headers as set (or as given in an array), then
|
|
396
|
+
* Connection and the body's framing */
|
|
397
|
+
_storeHeader(firstLine, headersArray) {
|
|
398
|
+
let head = firstLine;
|
|
399
|
+
let hasConnection = false, hasLength = false, hasTE = false, hasTrailer = false;
|
|
400
|
+
const add = (name, value) => {
|
|
401
|
+
const key = String(name).toLowerCase();
|
|
402
|
+
if (key === "connection") {
|
|
403
|
+
hasConnection = true;
|
|
404
|
+
if (/close/i.test(String(value))) this._last = true;
|
|
405
|
+
else this.shouldKeepAlive = true;
|
|
406
|
+
} else if (key === "content-length") hasLength = true;
|
|
407
|
+
else if (key === "transfer-encoding") {
|
|
408
|
+
hasTE = true;
|
|
409
|
+
if (/chunked/i.test(String(value))) this.chunkedEncoding = true;
|
|
410
|
+
} else if (key === "trailer") hasTrailer = true;
|
|
411
|
+
if (Array.isArray(value)) for (const v of value) head += name + ": " + v + "\r\n";
|
|
412
|
+
else head += name + ": " + value + "\r\n";
|
|
413
|
+
};
|
|
414
|
+
if (headersArray) {
|
|
415
|
+
if (Array.isArray(headersArray[0])) for (const [name, value] of headersArray) add(name, value);
|
|
416
|
+
else for (let i = 0; i < headersArray.length; i += 2) add(headersArray[i], headersArray[i + 1]);
|
|
417
|
+
} else {
|
|
418
|
+
for (const [, [name, value]] of this._headers) add(name, value);
|
|
419
|
+
}
|
|
420
|
+
if (!hasConnection) {
|
|
421
|
+
const shouldSendKeepAlive = this.shouldKeepAlive && (hasLength || this.useChunkedEncodingByDefault || this.agent);
|
|
422
|
+
if (shouldSendKeepAlive) head += "Connection: keep-alive\r\n";
|
|
423
|
+
else {
|
|
424
|
+
this._last = true;
|
|
425
|
+
head += "Connection: close\r\n";
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (!hasLength && !hasTE) {
|
|
429
|
+
if (!this._hasBody) this.chunkedEncoding = false;
|
|
430
|
+
else if (!this.useChunkedEncodingByDefault) this._last = true;
|
|
431
|
+
else if (!hasTrailer && !this._removedContLen && typeof this._contentLength === "number") head += "Content-Length: " + this._contentLength + "\r\n";
|
|
432
|
+
else {
|
|
433
|
+
head += "Transfer-Encoding: chunked\r\n";
|
|
434
|
+
this.chunkedEncoding = true;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
this._header = head + "\r\n";
|
|
438
|
+
this._headerSent = false;
|
|
439
|
+
this.headersSent = true;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/* bytes for the socket: written now, or kept until it's assigned */
|
|
443
|
+
_writeRaw(data, encoding, callback) {
|
|
444
|
+
const conn = this.socket;
|
|
445
|
+
if (conn?.destroyed) return false;
|
|
446
|
+
if (conn && conn._httpMessage === this && conn.writable) {
|
|
447
|
+
if (this.outputData.length) this._flushOutput(conn);
|
|
448
|
+
return conn.write(data, encoding, callback);
|
|
449
|
+
}
|
|
450
|
+
this.outputData.push({ data, encoding, callback });
|
|
451
|
+
this.outputSize += data.length;
|
|
452
|
+
return this.outputSize < this.writableHighWaterMark;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/* the head goes with the first bytes: in the same string when it can be */
|
|
456
|
+
_send(data, encoding, callback) {
|
|
457
|
+
if (!this._headerSent && this._header !== null) {
|
|
458
|
+
this._headerSent = true;
|
|
459
|
+
if (typeof data === "string" && (encoding === "utf8" || encoding === "latin1" || !encoding)) data = this._header + data;
|
|
460
|
+
else this._writeRaw(this._header, "latin1", null);
|
|
461
|
+
}
|
|
462
|
+
return this._writeRaw(data, encoding, callback);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
_flushOutput(socket) {
|
|
466
|
+
const output = this.outputData;
|
|
467
|
+
if (output.length === 0) return undefined;
|
|
468
|
+
this.outputData = [];
|
|
469
|
+
this.outputSize = 0;
|
|
470
|
+
let ret;
|
|
471
|
+
for (const { data, encoding, callback } of output) ret = socket.write(data, encoding, callback);
|
|
472
|
+
return ret;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
_flush() {
|
|
476
|
+
const socket = this.socket;
|
|
477
|
+
if (socket?.writable) {
|
|
478
|
+
const ret = this._flushOutput(socket);
|
|
479
|
+
if (!this.finished && ret && this._needDrain) {
|
|
480
|
+
this._needDrain = false;
|
|
481
|
+
this.emit("drain");
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
write(chunk, encoding, callback) {
|
|
487
|
+
if (typeof encoding === "function") {
|
|
488
|
+
callback = encoding;
|
|
489
|
+
encoding = null;
|
|
490
|
+
}
|
|
491
|
+
validateChunk(chunk); /* first, as Node's write_ (called on another object too) */
|
|
492
|
+
const ret = this._write_(chunk, encoding, callback, false);
|
|
493
|
+
if (!ret) this._needDrain = true;
|
|
494
|
+
return ret;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
_write_(chunk, encoding, callback, fromEnd) {
|
|
498
|
+
if (typeof callback !== "function") callback = () => {};
|
|
499
|
+
if (chunk === null) throw codeError(TypeError, "ERR_STREAM_NULL_VALUES", "May not write null values to stream");
|
|
500
|
+
if (typeof chunk !== "string" && !(chunk instanceof Uint8Array)) {
|
|
501
|
+
throw invalidArgType("chunk", "of type string or an instance of Buffer or Uint8Array", chunk);
|
|
502
|
+
}
|
|
503
|
+
let err;
|
|
504
|
+
if (this.finished) err = codeError(Error, "ERR_STREAM_WRITE_AFTER_END", "write after end");
|
|
505
|
+
else if (this.destroyed) err = codeError(Error, "ERR_STREAM_DESTROYED", "Cannot call write after a stream was destroyed");
|
|
506
|
+
if (err) {
|
|
507
|
+
if (!this.destroyed) process.nextTick(() => this.emit("error", err));
|
|
508
|
+
process.nextTick(callback, err);
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
511
|
+
const len = typeof chunk === "string" ? Buffer.byteLength(chunk, encoding || "utf8") : chunk.byteLength;
|
|
512
|
+
if (!this._header) {
|
|
513
|
+
if (fromEnd) this._contentLength = len;
|
|
514
|
+
this._implicitHeader();
|
|
515
|
+
}
|
|
516
|
+
if (!this._hasBody) {
|
|
517
|
+
process.nextTick(callback);
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
if (this.chunkedEncoding && len !== 0) {
|
|
521
|
+
this._send(len.toString(16), "latin1", null);
|
|
522
|
+
this._send("\r\n", "latin1", null);
|
|
523
|
+
this._send(chunk, encoding, null);
|
|
524
|
+
return this._send("\r\n", "latin1", callback);
|
|
525
|
+
}
|
|
526
|
+
return this._send(chunk, encoding, callback);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
end(chunk, encoding, callback) {
|
|
530
|
+
if (typeof chunk === "function") {
|
|
531
|
+
callback = chunk;
|
|
532
|
+
chunk = null;
|
|
533
|
+
encoding = null;
|
|
534
|
+
} else if (typeof encoding === "function") {
|
|
535
|
+
callback = encoding;
|
|
536
|
+
encoding = null;
|
|
537
|
+
}
|
|
538
|
+
if (chunk) {
|
|
539
|
+
if (this.finished) {
|
|
540
|
+
this._write_(chunk, encoding, null, true);
|
|
541
|
+
return this;
|
|
542
|
+
}
|
|
543
|
+
this._write_(chunk, encoding, null, true);
|
|
544
|
+
} else if (this.finished) {
|
|
545
|
+
if (typeof callback === "function") {
|
|
546
|
+
if (!this.writableFinished) this.on("finish", callback);
|
|
547
|
+
else callback(codeError(Error, "ERR_STREAM_ALREADY_FINISHED", "Cannot call end after a stream was finished"));
|
|
548
|
+
}
|
|
549
|
+
return this;
|
|
550
|
+
} else if (!this._header) {
|
|
551
|
+
this._contentLength = 0;
|
|
552
|
+
this._implicitHeader();
|
|
553
|
+
}
|
|
554
|
+
if (typeof callback === "function") this.once("finish", callback);
|
|
555
|
+
const finish = () => {
|
|
556
|
+
if (this.socket && this.socket._hadError) return;
|
|
557
|
+
this.writableFinished = true;
|
|
558
|
+
this.emit("finish");
|
|
559
|
+
};
|
|
560
|
+
if (this._hasBody && this.chunkedEncoding) this._send("0\r\n" + this._trailer + "\r\n", "latin1", finish);
|
|
561
|
+
else if (!this._headerSent || chunk) this._send("", "latin1", finish);
|
|
562
|
+
else process.nextTick(finish);
|
|
563
|
+
this.finished = true;
|
|
564
|
+
this.writableEnded = true;
|
|
565
|
+
this.writable = false;
|
|
566
|
+
return this;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
flushHeaders() {
|
|
570
|
+
if (!this._header) this._implicitHeader();
|
|
571
|
+
this._send("");
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/* as Node's: a subclass renders its first line (ClientRequest, ServerResponse) */
|
|
575
|
+
_implicitHeader() {
|
|
576
|
+
throw codeError(Error, "ERR_METHOD_NOT_IMPLEMENTED", "The _implicitHeader() method is not implemented");
|
|
577
|
+
}
|
|
578
|
+
|
|
253
579
|
/* trailers, sent after a chunked body: { name: value } or [[name, value], …] */
|
|
254
580
|
addTrailers(headers) {
|
|
255
581
|
this._trailer = "";
|
|
@@ -279,25 +605,66 @@ export class ServerResponse extends OutgoingMessage {
|
|
|
279
605
|
this.statusMessage = undefined;
|
|
280
606
|
this.sendDate = true;
|
|
281
607
|
this._conn = conn;
|
|
282
|
-
|
|
283
|
-
this.
|
|
608
|
+
/* one made by hand (new ServerResponse(req)) is kept alive, without a Keep-Alive timeout, as Node's */
|
|
609
|
+
this._keepAlive = keepAlive ?? true;
|
|
610
|
+
this._keepAliveTimeout = server ? server.keepAliveTimeout : 0;
|
|
284
611
|
this._head = req.method === "HEAD";
|
|
285
612
|
}
|
|
286
613
|
|
|
614
|
+
/* the response's bytes: to the connection's parser, which corks them; or, for a response made by
|
|
615
|
+
* hand (new ServerResponse(req), Node's assignSocket), to its socket, kept until it has one */
|
|
616
|
+
_send(data, latin1 = false) {
|
|
617
|
+
if (this._queued) (this._unsent ??= []).push([data, latin1]); /* behind the connection's earlier responses */
|
|
618
|
+
else if (this._conn !== undefined) httpWrite(this._conn, data, latin1);
|
|
619
|
+
else if (this.socket) this.socket.write(data, latin1 ? "latin1" : undefined);
|
|
620
|
+
else (this._unsent ??= []).push([data, latin1]);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/** A socket for a response made by hand, as Node's: its writes go there. */
|
|
624
|
+
assignSocket(socket) {
|
|
625
|
+
if (socket._httpMessage) throw codeError(Error, "ERR_HTTP_SOCKET_ASSIGNED", "Socket already assigned");
|
|
626
|
+
socket._httpMessage = this;
|
|
627
|
+
this.socket = socket;
|
|
628
|
+
this.emit("socket", socket);
|
|
629
|
+
const unsent = this._unsent;
|
|
630
|
+
this._unsent = undefined;
|
|
631
|
+
if (unsent) for (const [data, latin1] of unsent) socket.write(data, latin1 ? "latin1" : undefined);
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
detachSocket(socket) {
|
|
635
|
+
socket._httpMessage = null;
|
|
636
|
+
this.socket = null;
|
|
637
|
+
}
|
|
638
|
+
|
|
287
639
|
setTimeout(msecs, callback) {
|
|
288
640
|
if (typeof callback === "function") this.on("timeout", callback);
|
|
289
641
|
if (this.socket) this.socket.setTimeout(msecs);
|
|
290
642
|
return this;
|
|
291
643
|
}
|
|
292
644
|
|
|
645
|
+
/* the head a write sends without writeHead, through writeHead as Node's: a wrapper of it runs
|
|
646
|
+
* (on-headers: compression sets Vary and Content-Encoding there). Middleware calls it too, before
|
|
647
|
+
* writing (compression). `length`: the whole body's, when end() knows it */
|
|
648
|
+
_implicitHeader(length) {
|
|
649
|
+
this._implicitLength = length;
|
|
650
|
+
try {
|
|
651
|
+
this.writeHead(this.statusCode);
|
|
652
|
+
} finally {
|
|
653
|
+
this._implicitLength = undefined;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
293
657
|
/* sends the head now: the body that follows is chunked */
|
|
294
658
|
flushHeaders() {
|
|
295
|
-
if (!this.headersSent
|
|
659
|
+
if (!this.headersSent && Object.hasOwn(this, "writeHead")) this._implicitHeader(undefined); /* a wrapper sees it */
|
|
660
|
+
const head = this._takeHead(undefined);
|
|
661
|
+
if (head) this._send(head, true);
|
|
296
662
|
}
|
|
297
663
|
|
|
298
664
|
destroy(error) {
|
|
299
665
|
if (this.destroyed) return this;
|
|
300
666
|
this.destroyed = true;
|
|
667
|
+
this._errored = error;
|
|
301
668
|
this.writable = false;
|
|
302
669
|
this._closeEmitted = true;
|
|
303
670
|
if (this.socket) this.socket.destroy();
|
|
@@ -318,6 +685,13 @@ export class ServerResponse extends OutgoingMessage {
|
|
|
318
685
|
}
|
|
319
686
|
|
|
320
687
|
writeHead(statusCode, statusMessage, headers) {
|
|
688
|
+
/* as Node's: an integer status from 100 to 999 */
|
|
689
|
+
const originalStatusCode = statusCode;
|
|
690
|
+
statusCode |= 0;
|
|
691
|
+
if (statusCode < 100 || statusCode > 999) {
|
|
692
|
+
const shown = typeof originalStatusCode === "object" && originalStatusCode !== null ? JSON.stringify(originalStatusCode) : String(originalStatusCode);
|
|
693
|
+
throw codeError(RangeError, "ERR_HTTP_INVALID_STATUS_CODE", `Invalid status code: ${shown}`);
|
|
694
|
+
}
|
|
321
695
|
if (typeof statusMessage !== "string") {
|
|
322
696
|
headers = statusMessage;
|
|
323
697
|
statusMessage = undefined;
|
|
@@ -325,13 +699,16 @@ export class ServerResponse extends OutgoingMessage {
|
|
|
325
699
|
this.statusCode = statusCode;
|
|
326
700
|
if (statusMessage !== undefined) this.statusMessage = statusMessage;
|
|
327
701
|
if (Array.isArray(headers)) {
|
|
328
|
-
|
|
702
|
+
/* as Node's: they replace the headers set before, and may repeat a name among themselves */
|
|
703
|
+
if (headers.length % 2 !== 0) throw invalidArgValue("headers", "is invalid", headers);
|
|
704
|
+
for (let i = 0; i < headers.length; i += 2) if (this.hasHeader(headers[i])) this.removeHeader(headers[i]);
|
|
705
|
+
for (let i = 0; i < headers.length; i += 2) if (headers[i]) this.appendHeader(headers[i], headers[i + 1]);
|
|
329
706
|
} else if (headers) {
|
|
330
707
|
for (const name of Object.keys(headers)) this.setHeader(name, headers[name]);
|
|
331
708
|
}
|
|
332
709
|
/* Node renders the head now (sent with the first write): without a known length, the body
|
|
333
710
|
* is chunked even when end() brings all of it */
|
|
334
|
-
if (!this.headersSent) this._pendingHead = this._renderHead(
|
|
711
|
+
if (!this.headersSent) this._pendingHead = this._renderHead(this._implicitLength);
|
|
335
712
|
return this;
|
|
336
713
|
}
|
|
337
714
|
|
|
@@ -347,7 +724,8 @@ export class ServerResponse extends OutgoingMessage {
|
|
|
347
724
|
_renderHead(length) {
|
|
348
725
|
const status = this.statusCode;
|
|
349
726
|
const noBody = status === 204 || status === 304 || (status >= 100 && status < 200);
|
|
350
|
-
|
|
727
|
+
/* an empty statusMessage gets the standard one, as in Node (Next.js's route handlers set "") */
|
|
728
|
+
let head = "HTTP/1.1 " + status + " " + (this.statusMessage || STATUS_CODES[status] || "unknown") + "\r\n";
|
|
351
729
|
for (const [, [name, value]] of this._headers) {
|
|
352
730
|
if (Array.isArray(value)) for (const v of value) head += name + ": " + v + "\r\n";
|
|
353
731
|
else head += name + ": " + value + "\r\n";
|
|
@@ -376,33 +754,82 @@ export class ServerResponse extends OutgoingMessage {
|
|
|
376
754
|
this.chunkedEncoding = true;
|
|
377
755
|
}
|
|
378
756
|
}
|
|
757
|
+
/* as Node's _storeHeader: only a chunked body can have trailers (the head isn't sent) */
|
|
758
|
+
if (this.chunkedEncoding !== true && this._headers.has("trailer")) {
|
|
759
|
+
throw codeError(Error, "ERR_HTTP_TRAILER_INVALID", "Trailers are invalid with this transfer encoding");
|
|
760
|
+
}
|
|
379
761
|
this.headersSent = true;
|
|
380
762
|
return head + "\r\n";
|
|
381
763
|
}
|
|
382
764
|
|
|
383
765
|
write(chunk, encoding, callback) {
|
|
384
766
|
if (typeof encoding === "function") callback = encoding;
|
|
767
|
+
validateChunk(chunk);
|
|
385
768
|
if (this.writableEnded || this.destroyed) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
769
|
+
/* Node's write_: after end before destroyed; the callback, then 'error' (Node's emitErrorNt),
|
|
770
|
+
* none on a destroyed response (it closed, or its client went away) */
|
|
771
|
+
const ended = this.writableEnded;
|
|
772
|
+
const e = new Error(ended ? "write after end" : "Cannot call write after a stream was destroyed");
|
|
773
|
+
e.code = ended ? "ERR_STREAM_WRITE_AFTER_END" : "ERR_STREAM_DESTROYED";
|
|
774
|
+
if (!this.destroyed) {
|
|
775
|
+
process.nextTick(() => {
|
|
776
|
+
if (typeof callback === "function") callback(e);
|
|
777
|
+
if (!this.destroyed) this.emit("error", e);
|
|
778
|
+
});
|
|
779
|
+
} else if (typeof callback === "function") {
|
|
780
|
+
process.nextTick(callback, e);
|
|
781
|
+
}
|
|
390
782
|
return false;
|
|
391
783
|
}
|
|
392
784
|
if (typeof chunk === "string" && typeof encoding === "string" && encoding !== "utf8" && encoding !== "utf-8") chunk = Buffer.from(chunk, encoding);
|
|
785
|
+
/* a writeHead of the response's own (a wrapper) sees the implicit head, as in Node */
|
|
786
|
+
if (!this.headersSent && Object.hasOwn(this, "writeHead")) this._implicitHeader(undefined);
|
|
393
787
|
const head = this._takeHead(undefined);
|
|
394
|
-
if (head)
|
|
788
|
+
if (head) this._send(head, true);
|
|
395
789
|
if (chunk !== undefined && chunk !== null && chunkLength(chunk) > 0 && !this._head) {
|
|
396
790
|
if (this.chunkedEncoding) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
791
|
+
this._send(chunkLength(chunk).toString(16) + "\r\n");
|
|
792
|
+
this._send(chunk);
|
|
793
|
+
this._send("\r\n");
|
|
400
794
|
} else {
|
|
401
|
-
|
|
795
|
+
this._send(chunk);
|
|
402
796
|
}
|
|
403
797
|
}
|
|
404
798
|
if (typeof callback === "function") queueMicrotask(callback);
|
|
405
|
-
return
|
|
799
|
+
return this._underHighWaterMark();
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
get writableLength() {
|
|
803
|
+
return this._pendingBytes();
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/* bytes written that haven't gone out: corked in C and in libuv's queue, or a TLS socket's */
|
|
807
|
+
_pendingBytes() {
|
|
808
|
+
if (this._conn === undefined || this._queued) return 0;
|
|
809
|
+
return isFed(this.socket) ? httpPending(this._conn) + (this.socket?.writableLength ?? 0) : httpPending(this._conn);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/* write()'s answer, as Node's: false over the high-water mark, with 'drain' once it all went out */
|
|
813
|
+
_underHighWaterMark() {
|
|
814
|
+
if (this._pendingBytes() < this.writableHighWaterMark) return true;
|
|
815
|
+
/* over it while corked: sent now, libuv writing what the kernel takes at once, as Node's socket
|
|
816
|
+
* does; only what's left counts */
|
|
817
|
+
if (!isFed(this.socket)) {
|
|
818
|
+
httpFlush(this._conn);
|
|
819
|
+
if (this._pendingBytes() < this.writableHighWaterMark) return true;
|
|
820
|
+
}
|
|
821
|
+
if (!this.writableNeedDrain) {
|
|
822
|
+
this.writableNeedDrain = true;
|
|
823
|
+
if (isFed(this.socket)) this.socket.once("drain", () => this._emitDrain());
|
|
824
|
+
else if (httpWatchDrain(this._conn)) process.nextTick(() => this._emitDrain());
|
|
825
|
+
}
|
|
826
|
+
return false;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
_emitDrain() {
|
|
830
|
+
if (!this.writableNeedDrain) return;
|
|
831
|
+
this.writableNeedDrain = false;
|
|
832
|
+
this.emit("drain");
|
|
406
833
|
}
|
|
407
834
|
|
|
408
835
|
end(chunk, encoding, callback) {
|
|
@@ -412,33 +839,66 @@ export class ServerResponse extends OutgoingMessage {
|
|
|
412
839
|
} else if (typeof encoding === "function") {
|
|
413
840
|
callback = encoding;
|
|
414
841
|
}
|
|
842
|
+
if (chunk !== undefined && chunk !== null) validateChunk(chunk);
|
|
415
843
|
if (this.writableEnded) return this;
|
|
416
844
|
if (typeof chunk === "string" && typeof encoding === "string" && encoding !== "utf8" && encoding !== "utf-8") chunk = Buffer.from(chunk, encoding);
|
|
845
|
+
if (!this.headersSent && Object.hasOwn(this, "writeHead")) this._implicitHeader(chunk !== undefined && chunk !== null ? chunkLength(chunk) : 0);
|
|
417
846
|
if (!this.headersSent) {
|
|
418
847
|
const hasChunk = chunk !== undefined && chunk !== null;
|
|
419
848
|
const length = hasChunk ? chunkLength(chunk) : 0;
|
|
420
|
-
if (hasChunk && typeof chunk === "string" && !this._head)
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (
|
|
849
|
+
if (hasChunk && typeof chunk === "string" && !this._head) {
|
|
850
|
+
/* the head's bytes are latin1, the body's UTF-8: corked into one write by the parser's
|
|
851
|
+
* connection; a response made by hand writes them as one string, as Node's _send */
|
|
852
|
+
if (this._conn === undefined) {
|
|
853
|
+
this._send(this._renderHead(length) + chunk);
|
|
854
|
+
} else {
|
|
855
|
+
this._send(this._renderHead(length), true);
|
|
856
|
+
this._send(chunk);
|
|
857
|
+
}
|
|
858
|
+
} else {
|
|
859
|
+
this._send(this._renderHead(length), true);
|
|
860
|
+
if (hasChunk && length && !this._head) this._send(chunk);
|
|
424
861
|
}
|
|
425
862
|
} else {
|
|
426
863
|
if (chunk !== undefined && chunk !== null) this.write(chunk);
|
|
427
864
|
const head = this._takeHead(undefined);
|
|
428
|
-
if (head)
|
|
429
|
-
if (this.chunkedEncoding && !this._head)
|
|
865
|
+
if (head) this._send(head, true);
|
|
866
|
+
if (this.chunkedEncoding && !this._head) this._send("0\r\n" + this._trailer + "\r\n", true);
|
|
430
867
|
}
|
|
431
868
|
this.writableEnded = this.finished = true;
|
|
432
869
|
this.writable = false;
|
|
433
|
-
if (
|
|
434
|
-
|
|
435
|
-
this
|
|
870
|
+
if (this._queued) { /* finished once it has gone out, after the responses before it */
|
|
871
|
+
this._endCallback = callback;
|
|
872
|
+
return this;
|
|
873
|
+
}
|
|
874
|
+
this._afterEnd(callback);
|
|
875
|
+
return this;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/* a response that waited behind the connection's earlier ones: its turn (Node's assignSocket) */
|
|
879
|
+
_activate() {
|
|
880
|
+
this._queued = false;
|
|
881
|
+
const unsent = this._unsent;
|
|
882
|
+
this._unsent = undefined;
|
|
883
|
+
if (unsent) for (const [data, latin1] of unsent) httpWrite(this._conn, data, latin1);
|
|
884
|
+
if (this.writableEnded) this._afterEnd(this._endCallback);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
/* the response is all written: the socket ends after the last one (not kept alive, or the client
|
|
888
|
+
* ended its side), then 'finish' and 'close' */
|
|
889
|
+
_afterEnd(callback) {
|
|
890
|
+
/* the response is all written: sent now, so that by 'finish' it has gone to the socket, as Node's */
|
|
891
|
+
if (this._conn !== undefined) httpFlush(this._conn);
|
|
892
|
+
if ((!this._keepAlive || this._last) && this.socket) { /* the response goes out, then the socket ends */
|
|
893
|
+
if (typeof this.socket.destroySoon === "function") this.socket.destroySoon(); /* as Node's resOnFinish */
|
|
894
|
+
else this.socket.end();
|
|
436
895
|
}
|
|
437
896
|
queueMicrotask(() => {
|
|
438
897
|
this.writableFinished = true;
|
|
439
898
|
this.emit("finish");
|
|
440
899
|
if (!this._closeEmitted) {
|
|
441
900
|
this._closeEmitted = true;
|
|
901
|
+
this.destroyed = true; /* as Node's emitCloseNT */
|
|
442
902
|
this.emit("close");
|
|
443
903
|
}
|
|
444
904
|
if (typeof callback === "function") callback();
|
|
@@ -455,23 +915,29 @@ const requestTimeoutResponse = "HTTP/1.1 408 Request Timeout\r\nConnection: clos
|
|
|
455
915
|
|
|
456
916
|
function noop() {}
|
|
457
917
|
|
|
458
|
-
|
|
918
|
+
/** (options, requestListener) or (requestListener): the options an object. */
|
|
919
|
+
function serverArguments(options, requestListener) {
|
|
920
|
+
if (typeof options === "function") return [{}, options];
|
|
921
|
+
if (options === undefined || options === null) return [{}, requestListener];
|
|
922
|
+
if (typeof options !== "object" || Array.isArray(options)) throw invalidArgType("options", "of type object", options); /* validateObject */
|
|
923
|
+
return [options, requestListener];
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
class ServerClass extends NetServer {
|
|
459
927
|
constructor(options, requestListener) {
|
|
460
|
-
|
|
461
|
-
requestListener = options;
|
|
462
|
-
options = {};
|
|
463
|
-
} else if (options === undefined || options === null) {
|
|
464
|
-
options = {};
|
|
465
|
-
} else if (typeof options !== "object") {
|
|
466
|
-
throw invalidArgType("options", "of type object", options);
|
|
467
|
-
}
|
|
928
|
+
[options, requestListener] = serverArguments(options, requestListener);
|
|
468
929
|
super({ allowHalfOpen: true, noDelay: options.noDelay ?? true, keepAlive: options.keepAlive, keepAliveInitialDelay: options.keepAliveInitialDelay });
|
|
930
|
+
this._initHttp(options, requestListener, "connection");
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/* Node's storeHTTPOptions and the server's timeouts, and its sockets' requests: those of each
|
|
934
|
+
* 'connection' (https.Server's: of each 'secureConnection') */
|
|
935
|
+
_initHttp(options, requestListener, connectionEvent) {
|
|
469
936
|
if (typeof requestListener === "function") this.on("request", requestListener);
|
|
470
937
|
this.httpAllowHalfOpen = false;
|
|
471
938
|
this.timeout = 0;
|
|
472
939
|
this.maxHeadersCount = null;
|
|
473
940
|
this.maxRequestsPerSocket = 0;
|
|
474
|
-
/* Node's storeHTTPOptions and the server's timeouts */
|
|
475
941
|
const integer = (name, fallback) => {
|
|
476
942
|
const value = options[name];
|
|
477
943
|
if (value === undefined) return fallback;
|
|
@@ -489,6 +955,10 @@ export class Server extends NetServer {
|
|
|
489
955
|
this.insecureHTTPParser = boolean("insecureHTTPParser", undefined);
|
|
490
956
|
this.requireHostHeader = boolean("requireHostHeader", true);
|
|
491
957
|
this.joinDuplicateHeaders = boolean("joinDuplicateHeaders", undefined);
|
|
958
|
+
/* the classes requests and responses are made of (subclasses of node:http's), as Node's */
|
|
959
|
+
this._IncomingMessage = options.IncomingMessage || IncomingMessage;
|
|
960
|
+
this._ServerResponse = options.ServerResponse || ServerResponse;
|
|
961
|
+
this._optimizeEmptyRequests = boolean("optimizeEmptyRequests", false);
|
|
492
962
|
this.requestTimeout = integer("requestTimeout", 300_000);
|
|
493
963
|
this.headersTimeout = integer("headersTimeout", Math.min(60_000, this.requestTimeout));
|
|
494
964
|
if (this.requestTimeout > 0 && this.headersTimeout > 0 && this.headersTimeout > this.requestTimeout) {
|
|
@@ -496,27 +966,63 @@ export class Server extends NetServer {
|
|
|
496
966
|
}
|
|
497
967
|
this.keepAliveTimeout = integer("keepAliveTimeout", 5000);
|
|
498
968
|
this.connectionsCheckingInterval = integer("connectionsCheckingInterval", 30_000);
|
|
499
|
-
this._checking = null; /* the connections-checking timer, unref'd, while listening */
|
|
500
969
|
this._parsers = -1;
|
|
501
970
|
this._states = new Map(); /* connection → { socket, req, res, keepAliveTimeoutSet } */
|
|
502
|
-
this.on(
|
|
503
|
-
this.on("listening", () => this.
|
|
971
|
+
this.on(connectionEvent, (socket) => this._onSocket(socket));
|
|
972
|
+
this.on("listening", () => this._startConnectionsChecking());
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/* captureRejections: a 'request' listener's rejection answers 500, its headers dropped (or the
|
|
976
|
+
* response destroyed, once sent), as Node's; another event's goes to net.Server's */
|
|
977
|
+
[Symbol.for("nodejs.rejection")](err, event, ...args) {
|
|
978
|
+
if (event === "request") {
|
|
979
|
+
const res = args[1];
|
|
980
|
+
if (!res.headersSent && !res.writableEnded) {
|
|
981
|
+
for (const name of res.getHeaderNames()) res.removeHeader(name);
|
|
982
|
+
res.statusCode = 500;
|
|
983
|
+
res.end(STATUS_CODES[500]);
|
|
984
|
+
} else {
|
|
985
|
+
res.destroy();
|
|
986
|
+
}
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
const base = NetServer.prototype[Symbol.for("nodejs.rejection")];
|
|
990
|
+
if (typeof base === "function") base.call(this, err, event, ...args);
|
|
991
|
+
else this.emit("error", err);
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
/* Node's setupConnectionsTracking: the connections checked every connectionsCheckingInterval ms
|
|
995
|
+
* (unref'd), a new timer for each 'listening'. server[kConnectionsCheckingInterval] stands for
|
|
996
|
+
* Node's interval: its _destroyed says whether it was stopped. */
|
|
997
|
+
_startConnectionsChecking() {
|
|
998
|
+
this._stopConnectionsChecking();
|
|
999
|
+
const interval = { _destroyed: false, _timer: null };
|
|
1000
|
+
this[kConnectionsCheckingInterval] = interval;
|
|
1001
|
+
const tick = () => {
|
|
1002
|
+
if (interval._destroyed) return;
|
|
1003
|
+
this._checkConnections();
|
|
1004
|
+
interval._timer = timerUnref(tick, this.connectionsCheckingInterval);
|
|
1005
|
+
};
|
|
1006
|
+
if (this.connectionsCheckingInterval > 0) interval._timer = timerUnref(tick, this.connectionsCheckingInterval);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
_stopConnectionsChecking() {
|
|
1010
|
+
const interval = this[kConnectionsCheckingInterval];
|
|
1011
|
+
if (!interval || interval._destroyed) return;
|
|
1012
|
+
interval._destroyed = true;
|
|
1013
|
+
if (interval._timer !== null) clearTimeout(interval._timer);
|
|
1014
|
+
interval._timer = null;
|
|
504
1015
|
}
|
|
505
1016
|
|
|
506
1017
|
/* Node's checkConnections: a request (or its head) over requestTimeout (headersTimeout) gets a 408 */
|
|
507
|
-
|
|
508
|
-
if (this.
|
|
509
|
-
this.
|
|
510
|
-
this.
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
for (const conn of httpExpired(this._parsers, this.headersTimeout, this.requestTimeout)) {
|
|
514
|
-
const state = this._states.get(conn);
|
|
515
|
-
if (state) socketOnError.call(state.socket, codeError(Error, "ERR_HTTP_REQUEST_TIMEOUT", "Request timeout"));
|
|
516
|
-
}
|
|
1018
|
+
_checkConnections() {
|
|
1019
|
+
if (this._handle === null) return;
|
|
1020
|
+
if (this._parsers !== -1 && (this.headersTimeout > 0 || this.requestTimeout > 0)) {
|
|
1021
|
+
for (const conn of httpExpired(this._parsers, this.headersTimeout, this.requestTimeout)) {
|
|
1022
|
+
const state = this._states.get(conn);
|
|
1023
|
+
if (state) socketOnError.call(state.socket, codeError(Error, "ERR_HTTP_REQUEST_TIMEOUT", "Request timeout"));
|
|
517
1024
|
}
|
|
518
|
-
|
|
519
|
-
}, this.connectionsCheckingInterval);
|
|
1025
|
+
}
|
|
520
1026
|
}
|
|
521
1027
|
|
|
522
1028
|
setTimeout(msecs, callback) {
|
|
@@ -531,28 +1037,35 @@ export class Server extends NetServer {
|
|
|
531
1037
|
}
|
|
532
1038
|
|
|
533
1039
|
closeIdleConnections() {
|
|
534
|
-
|
|
1040
|
+
/* as Node's: idle is no request under way (none begun on it, nor a response unfinished) */
|
|
1041
|
+
for (const state of [...this._states.values()]) {
|
|
1042
|
+
if (state.incoming.length === 0 && !state.res && !httpBusy(state.conn)) state.socket.destroy();
|
|
1043
|
+
}
|
|
535
1044
|
}
|
|
536
1045
|
|
|
537
1046
|
close(callback) {
|
|
538
1047
|
this.closeIdleConnections();
|
|
539
|
-
|
|
540
|
-
this._checking = null;
|
|
1048
|
+
this._stopConnectionsChecking();
|
|
541
1049
|
return super.close(callback);
|
|
542
1050
|
}
|
|
543
1051
|
|
|
544
1052
|
/* a connection: its bytes go to llhttp; its end, timeout, errors and close are the requests' */
|
|
545
1053
|
_onSocket(socket) {
|
|
546
1054
|
if (this._parsers === -1) {
|
|
547
|
-
/* in the async context of the connection's
|
|
1055
|
+
/* in the async context of the connection's, as the server's sockets' callbacks */
|
|
548
1056
|
this._parsers = httpParsers(...[
|
|
549
1057
|
(conn, method, url, raw, major, minor, keepAlive, upgrade) => this._onRequest(conn, method, url, raw, major, minor, keepAlive, upgrade),
|
|
550
|
-
(conn, chunk) =>
|
|
1058
|
+
(conn, chunk) => {
|
|
1059
|
+
/* the bytes llhttp reads in C are the socket's activity, as Node's onParserExecute counts them */
|
|
1060
|
+
const state = this._states.get(conn);
|
|
1061
|
+
state?.socket._unrefTimer?.();
|
|
1062
|
+
state?.req?._push(chunk);
|
|
1063
|
+
},
|
|
551
1064
|
(conn, trailers) => {
|
|
552
1065
|
const state = this._states.get(conn);
|
|
553
1066
|
const req = state?.req;
|
|
554
1067
|
if (req) {
|
|
555
|
-
|
|
1068
|
+
state.req = null; /* parsed; it stays in state.incoming until its response finishes */
|
|
556
1069
|
if (trailers) {
|
|
557
1070
|
req.rawTrailers = trailers;
|
|
558
1071
|
for (let i = 0; i < trailers.length; i += 2) addHeader(req.trailers, trailers[i], trailers[i + 1]);
|
|
@@ -561,42 +1074,64 @@ export class Server extends NetServer {
|
|
|
561
1074
|
}
|
|
562
1075
|
},
|
|
563
1076
|
(conn, head) => this._onUpgrade(conn, head),
|
|
564
|
-
(conn, code, reason) => {
|
|
1077
|
+
(conn, code, reason, bytesParsed, rawPacket) => {
|
|
565
1078
|
const state = this._states.get(conn);
|
|
566
1079
|
if (!state) return;
|
|
567
|
-
const e =
|
|
568
|
-
e.code = code;
|
|
569
|
-
e.reason = reason;
|
|
1080
|
+
const e = parseError(code, reason, bytesParsed, rawPacket);
|
|
570
1081
|
socketOnError.call(state.socket, e);
|
|
571
1082
|
},
|
|
1083
|
+
/* the connection's output went out: the response waiting for it gets 'drain' */
|
|
1084
|
+
(conn) => this._states.get(conn)?.res?._emitDrain(),
|
|
572
1085
|
].map(bindContext));
|
|
573
1086
|
}
|
|
574
|
-
const conn = socket
|
|
1087
|
+
const conn = connOf(socket);
|
|
575
1088
|
if (conn === null) return;
|
|
576
|
-
|
|
1089
|
+
socket.server = this; /* as Node's: the http server, even for a socket another server accepted */
|
|
1090
|
+
/* as Node's connection state: the request being parsed (req); those whose responses aren't
|
|
1091
|
+
* finished (incoming); the response writing to the connection (res), and those waiting behind it
|
|
1092
|
+
* (outgoing: pipelined requests are answered in order) */
|
|
1093
|
+
const state = { socket, conn, req: null, incoming: [], res: null, outgoing: [], keepAliveTimeoutSet: false, listeners: null };
|
|
577
1094
|
this._states.set(conn, state);
|
|
578
1095
|
if (this.timeout && typeof socket.setTimeout === "function") socket.setTimeout(this.timeout);
|
|
579
1096
|
const onTimeout = () => this._onTimeout(state);
|
|
1097
|
+
/* Node's socketOnEnd: the client ended its side; with httpAllowHalfOpen, the socket ends after
|
|
1098
|
+
* the last response, else now */
|
|
580
1099
|
const onEnd = () => {
|
|
581
|
-
this.
|
|
582
|
-
|
|
1100
|
+
if (!this.httpAllowHalfOpen) {
|
|
1101
|
+
if (socket.writable) socket.end();
|
|
1102
|
+
} else if (state.outgoing.length) {
|
|
1103
|
+
state.outgoing[state.outgoing.length - 1]._last = true;
|
|
1104
|
+
} else if (state.res) {
|
|
1105
|
+
state.res._last = true;
|
|
1106
|
+
} else if (socket.writable) {
|
|
1107
|
+
socket.end();
|
|
1108
|
+
}
|
|
583
1109
|
};
|
|
1110
|
+
/* Node's socketOnClose: the requests and responses not finished are aborted */
|
|
584
1111
|
const onClose = () => {
|
|
585
1112
|
this._states.delete(conn);
|
|
586
1113
|
this._abortIncoming(state);
|
|
587
|
-
const
|
|
1114
|
+
const responses = state.res ? [state.res, ...state.outgoing] : state.outgoing;
|
|
588
1115
|
state.res = null;
|
|
589
|
-
|
|
1116
|
+
state.outgoing = [];
|
|
1117
|
+
for (const res of responses) res._connectionClosed();
|
|
590
1118
|
};
|
|
591
1119
|
state.listeners = { timeout: onTimeout, end: onEnd, close: onClose, error: socketOnError };
|
|
592
1120
|
for (const [event, fn] of Object.entries(state.listeners)) socket.on(event, fn);
|
|
593
1121
|
httpAttach(conn, this._parsers, false, this.maxHeaderSize ?? maxHeaderSize);
|
|
1122
|
+
/* bytes the socket read before it was the server's (a program that peeked, then emitted
|
|
1123
|
+
* 'connection' itself): parsed first, a chunk at a time, as Node's 'data' brings them */
|
|
1124
|
+
const readable = socket._readableState;
|
|
1125
|
+
while (readable && !isFed(socket) && socket.readableLength > 0 && this._states.has(conn)) {
|
|
1126
|
+
const chunk = socket.read(readable.buffer[readable.bufferIndex]?.length ?? socket.readableLength);
|
|
1127
|
+
if (chunk === null) break;
|
|
1128
|
+
httpFeed(conn, typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
1129
|
+
}
|
|
594
1130
|
}
|
|
595
1131
|
|
|
596
1132
|
_abortIncoming(state) {
|
|
597
|
-
const req = state.req;
|
|
598
1133
|
state.req = null;
|
|
599
|
-
|
|
1134
|
+
for (const req of state.incoming.splice(0)) req._abort();
|
|
600
1135
|
}
|
|
601
1136
|
|
|
602
1137
|
/* Node's socketOnTimeout: the request's, the response's or the server's 'timeout', or the end */
|
|
@@ -613,11 +1148,12 @@ export class Server extends NetServer {
|
|
|
613
1148
|
const state = this._states.get(conn);
|
|
614
1149
|
if (!state) return 0;
|
|
615
1150
|
const socket = state.socket;
|
|
1151
|
+
socket._unrefTimer?.(); /* the head's bytes are the socket's activity */
|
|
616
1152
|
if (state.keepAliveTimeoutSet) {
|
|
617
1153
|
socket.setTimeout(0);
|
|
618
1154
|
state.keepAliveTimeoutSet = false;
|
|
619
1155
|
}
|
|
620
|
-
const req = new
|
|
1156
|
+
const req = new this._IncomingMessage(socket);
|
|
621
1157
|
req.method = method;
|
|
622
1158
|
req.url = url;
|
|
623
1159
|
req.httpVersionMajor = major;
|
|
@@ -631,25 +1167,43 @@ export class Server extends NetServer {
|
|
|
631
1167
|
return 2;
|
|
632
1168
|
}
|
|
633
1169
|
state.req = req;
|
|
634
|
-
|
|
635
|
-
|
|
1170
|
+
state.incoming.push(req);
|
|
1171
|
+
if (this._optimizeEmptyRequests && !("content-length" in req.headers) && !("transfer-encoding" in req.headers)) req._dumpAndCloseReadable();
|
|
1172
|
+
const res = new this._ServerResponse(req, conn, keepAlive, this);
|
|
1173
|
+
if (state.res) { /* a pipelined request: its response waits for the one before */
|
|
1174
|
+
res._queued = true;
|
|
1175
|
+
state.outgoing.push(res);
|
|
1176
|
+
} else {
|
|
1177
|
+
state.res = res;
|
|
1178
|
+
}
|
|
1179
|
+
res.once("finish", () => this._resOnFinish(state, req, res));
|
|
636
1180
|
if (this.requireHostHeader && major === 1 && minor === 1 && !req.headers.host) { /* Node: a 400, and the connection ends */
|
|
637
1181
|
res.writeHead(400, ["Connection", "close"]);
|
|
638
1182
|
res.end();
|
|
639
1183
|
return 0;
|
|
640
1184
|
}
|
|
641
|
-
res.once("finish", () => {
|
|
642
|
-
if (state.res === res) state.res = null;
|
|
643
|
-
if (state.req === req && req.complete) state.req = null;
|
|
644
|
-
if (res._keepAlive && !state.res && this.keepAliveTimeout && socket.writable) {
|
|
645
|
-
socket.setTimeout(this.keepAliveTimeout);
|
|
646
|
-
state.keepAliveTimeoutSet = true;
|
|
647
|
-
}
|
|
648
|
-
});
|
|
649
1185
|
this.emit("request", req, res);
|
|
650
1186
|
return 0;
|
|
651
1187
|
}
|
|
652
1188
|
|
|
1189
|
+
/* Node's resOnFinish: the request is done with; the next response's turn, or the idle keep-alive
|
|
1190
|
+
* timeout */
|
|
1191
|
+
_resOnFinish(state, req, res) {
|
|
1192
|
+
const i = state.incoming.indexOf(req);
|
|
1193
|
+
if (i !== -1) state.incoming.splice(i, 1);
|
|
1194
|
+
if (state.req === req && req.complete) state.req = null;
|
|
1195
|
+
if (state.res === res) state.res = null;
|
|
1196
|
+
if (res._last || !res._keepAlive) return; /* the socket is ending */
|
|
1197
|
+
const next = state.outgoing.shift();
|
|
1198
|
+
if (next) {
|
|
1199
|
+
state.res = next;
|
|
1200
|
+
next._activate();
|
|
1201
|
+
} else if (this.keepAliveTimeout && state.socket.writable) {
|
|
1202
|
+
state.socket.setTimeout(this.keepAliveTimeout);
|
|
1203
|
+
state.keepAliveTimeoutSet = true;
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
|
|
653
1207
|
/* the parser let go of the socket after an upgrade's head: it's the 'upgrade' listener's */
|
|
654
1208
|
_onUpgrade(conn, head) {
|
|
655
1209
|
const state = this._states.get(conn);
|
|
@@ -658,6 +1212,7 @@ export class Server extends NetServer {
|
|
|
658
1212
|
this._states.delete(conn);
|
|
659
1213
|
for (const [event, fn] of Object.entries(state.listeners)) socket.removeListener(event, fn);
|
|
660
1214
|
if (socket.listenerCount("error") === 0) socket.on("error", noop);
|
|
1215
|
+
unfeed(socket);
|
|
661
1216
|
/* what the socket reads next goes to its stream, buffered until someone reads it */
|
|
662
1217
|
socket._readableState.flowing = null;
|
|
663
1218
|
const bodyHead = Buffer.from(head.buffer, head.byteOffset, head.byteLength);
|
|
@@ -666,6 +1221,47 @@ export class Server extends NetServer {
|
|
|
666
1221
|
else socket.destroy();
|
|
667
1222
|
}
|
|
668
1223
|
}
|
|
1224
|
+
export const Server = callableClass(ServerClass, "Server"); /* Server(…) without new, as Node's */
|
|
1225
|
+
|
|
1226
|
+
/* ---- Connections: llhttp on a socket's handle, or fed --------------------------------------- */
|
|
1227
|
+
|
|
1228
|
+
/* A socket whose bytes aren't its handle's: a TLSSocket's handle carries ciphertext, a Duplex
|
|
1229
|
+
* has none. llhttp parses what it reads ('data') and writes through it (write()). */
|
|
1230
|
+
const isFed = (socket) => socket.encrypted === true || socket._handle === undefined;
|
|
1231
|
+
const kFed = Symbol("kFed");
|
|
1232
|
+
|
|
1233
|
+
/** The socket's connection for llhttp: its net handle's id (the parser reads the handle in C), or a
|
|
1234
|
+
* fed connection, made the first time; null for a net socket without a handle yet. */
|
|
1235
|
+
function connOf(socket) {
|
|
1236
|
+
if (!isFed(socket)) return socket._handle;
|
|
1237
|
+
const fed = socket[kFed];
|
|
1238
|
+
if (fed) return fed.conn;
|
|
1239
|
+
const conn = httpFedOpen((bytes) => {
|
|
1240
|
+
if (!socket.destroyed) socket.write(Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength));
|
|
1241
|
+
});
|
|
1242
|
+
const onData = (chunk) => httpFeed(conn, typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
1243
|
+
const onEnd = () => httpFeed(conn, null);
|
|
1244
|
+
const onClose = () => unfeed(socket);
|
|
1245
|
+
socket[kFed] = { conn, onData, onEnd, onClose };
|
|
1246
|
+
socket.on("data", onData);
|
|
1247
|
+
socket.on("end", onEnd);
|
|
1248
|
+
socket.on("close", onClose);
|
|
1249
|
+
return conn;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/** A socket's connection if it has one: its handle's id, or its fed connection's. */
|
|
1253
|
+
const connKey = (socket) => (isFed(socket) ? socket[kFed]?.conn : socket._handle);
|
|
1254
|
+
|
|
1255
|
+
/* the socket's bytes stop going to llhttp: it upgraded, or closed */
|
|
1256
|
+
function unfeed(socket) {
|
|
1257
|
+
const fed = socket[kFed];
|
|
1258
|
+
if (!fed) return;
|
|
1259
|
+
socket[kFed] = undefined;
|
|
1260
|
+
socket.removeListener("data", fed.onData);
|
|
1261
|
+
socket.removeListener("end", fed.onEnd);
|
|
1262
|
+
socket.removeListener("close", fed.onClose);
|
|
1263
|
+
httpFedClose(fed.conn);
|
|
1264
|
+
}
|
|
669
1265
|
|
|
670
1266
|
/* Node's socketOnError: 'clientError', or a 400 (when nothing was written yet) and the end */
|
|
671
1267
|
function socketOnError(e) {
|
|
@@ -673,7 +1269,7 @@ function socketOnError(e) {
|
|
|
673
1269
|
if (this.listenerCount("error") === 0) this.on("error", noop);
|
|
674
1270
|
const server = this.server;
|
|
675
1271
|
if (!server.emit("clientError", e, this)) {
|
|
676
|
-
const state = server._states.get(this
|
|
1272
|
+
const state = server._states.get(connKey(this));
|
|
677
1273
|
const res = state?.res;
|
|
678
1274
|
if (this.writable && (!res || !res.headersSent)) {
|
|
679
1275
|
if (state) httpFlush(state.conn);
|
|
@@ -702,11 +1298,11 @@ function urlToHttpOptions(url) {
|
|
|
702
1298
|
return options;
|
|
703
1299
|
}
|
|
704
1300
|
|
|
705
|
-
/* the sockets whose responses llhttp parses:
|
|
1301
|
+
/* the sockets whose responses llhttp parses: connection → socket */
|
|
706
1302
|
const clientSockets = new Map();
|
|
707
1303
|
let clientParsers = -1;
|
|
708
1304
|
|
|
709
|
-
/* the async context a request was made in
|
|
1305
|
+
/* the async context a request was made in: its response's callbacks run in it, as in
|
|
710
1306
|
* Node, whichever request's the socket was */
|
|
711
1307
|
const kAsyncContext = Symbol("kAsyncContext");
|
|
712
1308
|
const inRequestContext = (fn) => (conn, ...args) =>
|
|
@@ -731,7 +1327,10 @@ function clientParsersId() {
|
|
|
731
1327
|
res.upgrade = upgrade;
|
|
732
1328
|
return parserOnIncomingClient(req, socket, res, keepAlive);
|
|
733
1329
|
},
|
|
734
|
-
(conn, chunk) =>
|
|
1330
|
+
(conn, chunk) => {
|
|
1331
|
+
clientSockets.get(conn)?._unrefTimer?.(); /* the socket's activity, as Node's onParserExecute */
|
|
1332
|
+
reqOf(conn)?.res?._push(chunk);
|
|
1333
|
+
},
|
|
735
1334
|
(conn, trailers) => {
|
|
736
1335
|
const socket = clientSockets.get(conn);
|
|
737
1336
|
const req = socket?._httpMessage;
|
|
@@ -745,21 +1344,20 @@ function clientParsersId() {
|
|
|
745
1344
|
res.rawTrailers = trailers;
|
|
746
1345
|
for (let i = 0; i < trailers.length; i += 2) addHeader(res.trailers, trailers[i], trailers[i + 1]);
|
|
747
1346
|
}
|
|
748
|
-
if (!statusIsInformational(res.statusCode))
|
|
1347
|
+
if (!statusIsInformational(res.statusCode)) freeParserAfterRead(socket);
|
|
749
1348
|
res._finish();
|
|
750
1349
|
},
|
|
751
1350
|
(conn, head) => {
|
|
752
1351
|
const socket = clientSockets.get(conn);
|
|
753
1352
|
if (socket?._httpMessage) clientUpgrade(socket._httpMessage, socket, head);
|
|
754
1353
|
},
|
|
755
|
-
(conn, code, reason) => {
|
|
1354
|
+
(conn, code, reason, bytesParsed, rawPacket) => {
|
|
756
1355
|
const socket = clientSockets.get(conn);
|
|
757
1356
|
const req = socket?._httpMessage;
|
|
758
|
-
const e =
|
|
759
|
-
e.code = code;
|
|
760
|
-
e.reason = reason;
|
|
1357
|
+
const e = parseError(code, reason, bytesParsed, rawPacket);
|
|
761
1358
|
if (socket) {
|
|
762
1359
|
freeParser(socket);
|
|
1360
|
+
socket.removeListener("end", socketOnEnd); /* as Node's socketOnData */
|
|
763
1361
|
socket.destroy();
|
|
764
1362
|
}
|
|
765
1363
|
if (req) {
|
|
@@ -776,12 +1374,15 @@ const statusIsInformational = (status) => status < 200 && status >= 100 && statu
|
|
|
776
1374
|
/* the socket's bytes go to a response parser: now, or once it has connected */
|
|
777
1375
|
function attachParser(socket, req) {
|
|
778
1376
|
const attach = () => {
|
|
779
|
-
if (socket._httpMessage !== req
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
1377
|
+
if (socket._httpMessage !== req) return;
|
|
1378
|
+
const conn = connOf(socket);
|
|
1379
|
+
if (conn === null) return;
|
|
1380
|
+
clientSockets.set(conn, socket);
|
|
1381
|
+
socket._clientConn = conn;
|
|
1382
|
+
httpAttach(conn, clientParsersId(), true, req.maxHeaderSize ?? maxHeaderSize);
|
|
783
1383
|
};
|
|
784
|
-
|
|
1384
|
+
/* a net socket's handle is its once connected; a fed socket's bytes come as they come */
|
|
1385
|
+
if (!isFed(socket) && (socket.connecting || socket._handle === null)) socket.once("connect", attach);
|
|
785
1386
|
else attach();
|
|
786
1387
|
}
|
|
787
1388
|
|
|
@@ -789,10 +1390,25 @@ function freeParser(socket) {
|
|
|
789
1390
|
const conn = socket._clientConn;
|
|
790
1391
|
if (conn === undefined) return;
|
|
791
1392
|
socket._clientConn = undefined;
|
|
1393
|
+
socket.parser = null; /* as Node's: no parser on a socket between requests */
|
|
792
1394
|
clientSockets.delete(conn);
|
|
793
1395
|
httpDetach(conn);
|
|
794
1396
|
}
|
|
795
1397
|
|
|
1398
|
+
/* a complete response's parser, let go of once the read is parsed, as Node's socketOnData frees it:
|
|
1399
|
+
* what follows the response in the read is still parsed, and an error there is the request's (the
|
|
1400
|
+
* socket's entry stays until then) */
|
|
1401
|
+
function freeParserAfterRead(socket) {
|
|
1402
|
+
const conn = socket._clientConn;
|
|
1403
|
+
if (conn === undefined) return;
|
|
1404
|
+
socket._clientConn = undefined;
|
|
1405
|
+
socket.parser = null; /* as Node's: no parser on a socket between requests */
|
|
1406
|
+
httpDetach(conn, true);
|
|
1407
|
+
queueMicrotask(() => {
|
|
1408
|
+
if (clientSockets.get(conn) === socket && socket._clientConn !== conn) clientSockets.delete(conn);
|
|
1409
|
+
});
|
|
1410
|
+
}
|
|
1411
|
+
|
|
796
1412
|
/* Node's parserOnIncomingClient: 0, or 1 for no body, or 2 to take the socket */
|
|
797
1413
|
function parserOnIncomingClient(req, socket, res, shouldKeepAlive) {
|
|
798
1414
|
if (req.res) { /* a second response to one request */
|
|
@@ -833,6 +1449,7 @@ function clientUpgrade(req, socket, head) {
|
|
|
833
1449
|
if (req.timeoutCb) socket.removeListener("timeout", req.timeoutCb);
|
|
834
1450
|
socket.removeListener("timeout", responseOnTimeout);
|
|
835
1451
|
freeParser(socket);
|
|
1452
|
+
unfeed(socket);
|
|
836
1453
|
const bodyHead = Buffer.from(head.buffer, head.byteOffset, head.byteLength);
|
|
837
1454
|
const eventName = req.method === "CONNECT" ? "connect" : "upgrade";
|
|
838
1455
|
if (req.listenerCount(eventName) > 0) {
|
|
@@ -949,6 +1566,7 @@ function socketOnEnd() {
|
|
|
949
1566
|
}
|
|
950
1567
|
|
|
951
1568
|
const INVALID_PATH_REGEX = /[^!-ÿ]/;
|
|
1569
|
+
const kPath = Symbol("kPath");
|
|
952
1570
|
|
|
953
1571
|
export class ClientRequest extends OutgoingMessage {
|
|
954
1572
|
constructor(input, options, cb) {
|
|
@@ -965,7 +1583,7 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
965
1583
|
cb = options;
|
|
966
1584
|
options = input || {};
|
|
967
1585
|
} else {
|
|
968
|
-
options = {
|
|
1586
|
+
options = Object.assign({ __proto__: null }, input, options); /* as Node's: no prototype's options */
|
|
969
1587
|
}
|
|
970
1588
|
let agent = options.agent;
|
|
971
1589
|
const defaultAgent = options._defaultAgent || globalAgent;
|
|
@@ -993,7 +1611,7 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
993
1611
|
return host;
|
|
994
1612
|
};
|
|
995
1613
|
const host = optsWithoutSignal.host = validHost(options.hostname, "hostname") || validHost(options.host, "host") || "localhost";
|
|
996
|
-
const setHost = options.setHost
|
|
1614
|
+
const setHost = options.setHost !== undefined ? Boolean(options.setHost) : options.setDefaultHeaders !== false;
|
|
997
1615
|
this.socketPath = options.socketPath;
|
|
998
1616
|
if (options.timeout !== undefined) {
|
|
999
1617
|
if (typeof options.timeout !== "number") throw invalidArgType("timeout", "of type number", options.timeout);
|
|
@@ -1001,13 +1619,8 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1001
1619
|
}
|
|
1002
1620
|
const signal = options.signal;
|
|
1003
1621
|
if (signal) {
|
|
1622
|
+
addAbortSignal(signal, this); /* as Node's: destroyed with an AbortError, now if already aborted */
|
|
1004
1623
|
delete optsWithoutSignal.signal;
|
|
1005
|
-
const onAbort = () => this.destroy(codeError(Error, "ABORT_ERR", "The operation was aborted"));
|
|
1006
|
-
if (signal.aborted) process.nextTick(onAbort);
|
|
1007
|
-
else {
|
|
1008
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
1009
|
-
this.once("close", () => signal.removeEventListener("abort", onAbort));
|
|
1010
|
-
}
|
|
1011
1624
|
}
|
|
1012
1625
|
let method = options.method;
|
|
1013
1626
|
if (method !== null && method !== undefined && typeof method !== "string") throw invalidArgType("options.method", "of type string", method);
|
|
@@ -1019,8 +1632,28 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1019
1632
|
}
|
|
1020
1633
|
if (options.maxHeaderSize !== undefined) validateInteger(options.maxHeaderSize, "maxHeaderSize");
|
|
1021
1634
|
this.maxHeaderSize = options.maxHeaderSize;
|
|
1635
|
+
const insecureHTTPParser = options.insecureHTTPParser;
|
|
1636
|
+
if (insecureHTTPParser !== undefined && typeof insecureHTTPParser !== "boolean") {
|
|
1637
|
+
throw invalidArgType("options.insecureHTTPParser", "of type boolean", insecureHTTPParser);
|
|
1638
|
+
}
|
|
1639
|
+
this.insecureHTTPParser = insecureHTTPParser;
|
|
1640
|
+
const httpValidation = options.httpValidation;
|
|
1641
|
+
if (httpValidation !== undefined) {
|
|
1642
|
+
if (httpValidation !== "strict" && httpValidation !== "relaxed" && httpValidation !== "insecure") {
|
|
1643
|
+
throw codeError(TypeError, "ERR_INVALID_ARG_VALUE",
|
|
1644
|
+
`The property 'options.httpValidation' must be one of: 'strict', 'relaxed', 'insecure'. Received ${JSON.stringify(httpValidation)}`);
|
|
1645
|
+
}
|
|
1646
|
+
if (insecureHTTPParser !== undefined) {
|
|
1647
|
+
throw codeError(TypeError, "ERR_INVALID_ARG_VALUE",
|
|
1648
|
+
`The property 'options.httpValidation' cannot be used together with options.insecureHTTPParser. Received ${JSON.stringify(httpValidation)}`);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
this.httpValidation = httpValidation;
|
|
1652
|
+
if (options.joinDuplicateHeaders !== undefined && typeof options.joinDuplicateHeaders !== "boolean") {
|
|
1653
|
+
throw invalidArgType("options.joinDuplicateHeaders", "of type boolean", options.joinDuplicateHeaders);
|
|
1654
|
+
}
|
|
1022
1655
|
this.joinDuplicateHeaders = options.joinDuplicateHeaders;
|
|
1023
|
-
this
|
|
1656
|
+
this[kPath] = options.path || "/";
|
|
1024
1657
|
if (cb) this.once("response", cb);
|
|
1025
1658
|
this.useChunkedEncodingByDefault = !(method === "GET" || method === "HEAD" || method === "DELETE" || method === "OPTIONS" ||
|
|
1026
1659
|
method === "TRACE" || method === "CONNECT");
|
|
@@ -1094,191 +1727,19 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1094
1727
|
}
|
|
1095
1728
|
}
|
|
1096
1729
|
|
|
1097
|
-
/* Node's _storeHeader: the request line, the headers as set (or as given in an array), then
|
|
1098
|
-
* Connection and the body's framing */
|
|
1099
|
-
_storeHeader(firstLine, headersArray) {
|
|
1100
|
-
let head = firstLine;
|
|
1101
|
-
let hasConnection = false, hasLength = false, hasTE = false, hasTrailer = false;
|
|
1102
|
-
const add = (name, value) => {
|
|
1103
|
-
const key = String(name).toLowerCase();
|
|
1104
|
-
if (key === "connection") {
|
|
1105
|
-
hasConnection = true;
|
|
1106
|
-
if (/close/i.test(String(value))) this._last = true;
|
|
1107
|
-
else this.shouldKeepAlive = true;
|
|
1108
|
-
} else if (key === "content-length") hasLength = true;
|
|
1109
|
-
else if (key === "transfer-encoding") {
|
|
1110
|
-
hasTE = true;
|
|
1111
|
-
if (/chunked/i.test(String(value))) this.chunkedEncoding = true;
|
|
1112
|
-
} else if (key === "trailer") hasTrailer = true;
|
|
1113
|
-
if (Array.isArray(value)) for (const v of value) head += name + ": " + v + "\r\n";
|
|
1114
|
-
else head += name + ": " + value + "\r\n";
|
|
1115
|
-
};
|
|
1116
|
-
if (headersArray) {
|
|
1117
|
-
if (Array.isArray(headersArray[0])) for (const [name, value] of headersArray) add(name, value);
|
|
1118
|
-
else for (let i = 0; i < headersArray.length; i += 2) add(headersArray[i], headersArray[i + 1]);
|
|
1119
|
-
} else {
|
|
1120
|
-
for (const [, [name, value]] of this._headers) add(name, value);
|
|
1121
|
-
}
|
|
1122
|
-
if (!hasConnection) {
|
|
1123
|
-
const shouldSendKeepAlive = this.shouldKeepAlive && (hasLength || this.useChunkedEncodingByDefault || this.agent);
|
|
1124
|
-
if (shouldSendKeepAlive) head += "Connection: keep-alive\r\n";
|
|
1125
|
-
else {
|
|
1126
|
-
this._last = true;
|
|
1127
|
-
head += "Connection: close\r\n";
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
if (!hasLength && !hasTE) {
|
|
1131
|
-
if (!this._hasBody) this.chunkedEncoding = false;
|
|
1132
|
-
else if (!this.useChunkedEncodingByDefault) this._last = true;
|
|
1133
|
-
else if (!hasTrailer && !this._removedContLen && typeof this._contentLength === "number") head += "Content-Length: " + this._contentLength + "\r\n";
|
|
1134
|
-
else {
|
|
1135
|
-
head += "Transfer-Encoding: chunked\r\n";
|
|
1136
|
-
this.chunkedEncoding = true;
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
this._header = head + "\r\n";
|
|
1140
|
-
this._headerSent = false;
|
|
1141
|
-
this.headersSent = true;
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
1730
|
_implicitHeader() {
|
|
1145
1731
|
if (this._header) throw codeError(Error, "ERR_HTTP_HEADERS_SENT", "Cannot render headers after they are sent to the client");
|
|
1146
1732
|
this._storeHeader(this.method + " " + this.path + " HTTP/1.1\r\n", null);
|
|
1147
1733
|
}
|
|
1148
1734
|
|
|
1149
|
-
/* bytes for the socket: written now, or kept until it's assigned */
|
|
1150
|
-
_writeRaw(data, encoding, callback) {
|
|
1151
|
-
const conn = this.socket;
|
|
1152
|
-
if (conn?.destroyed) return false;
|
|
1153
|
-
if (conn && conn._httpMessage === this && conn.writable) {
|
|
1154
|
-
if (this.outputData.length) this._flushOutput(conn);
|
|
1155
|
-
return conn.write(data, encoding, callback);
|
|
1156
|
-
}
|
|
1157
|
-
this.outputData.push({ data, encoding, callback });
|
|
1158
|
-
this.outputSize += data.length;
|
|
1159
|
-
return this.outputSize < this.writableHighWaterMark;
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
/* the head goes with the first bytes: in the same string when it can be */
|
|
1163
|
-
_send(data, encoding, callback) {
|
|
1164
|
-
if (!this._headerSent && this._header !== null) {
|
|
1165
|
-
this._headerSent = true;
|
|
1166
|
-
if (typeof data === "string" && (encoding === "utf8" || encoding === "latin1" || !encoding)) data = this._header + data;
|
|
1167
|
-
else this._writeRaw(this._header, "latin1", null);
|
|
1168
|
-
}
|
|
1169
|
-
return this._writeRaw(data, encoding, callback);
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
_flushOutput(socket) {
|
|
1173
|
-
const output = this.outputData;
|
|
1174
|
-
if (output.length === 0) return undefined;
|
|
1175
|
-
this.outputData = [];
|
|
1176
|
-
this.outputSize = 0;
|
|
1177
|
-
let ret;
|
|
1178
|
-
for (const { data, encoding, callback } of output) ret = socket.write(data, encoding, callback);
|
|
1179
|
-
return ret;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
_flush() {
|
|
1183
|
-
const socket = this.socket;
|
|
1184
|
-
if (socket?.writable) {
|
|
1185
|
-
const ret = this._flushOutput(socket);
|
|
1186
|
-
if (!this.finished && ret && this._needDrain) {
|
|
1187
|
-
this._needDrain = false;
|
|
1188
|
-
this.emit("drain");
|
|
1189
|
-
}
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
|
|
1193
|
-
write(chunk, encoding, callback) {
|
|
1194
|
-
if (typeof encoding === "function") {
|
|
1195
|
-
callback = encoding;
|
|
1196
|
-
encoding = null;
|
|
1197
|
-
}
|
|
1198
|
-
const ret = this._write_(chunk, encoding, callback, false);
|
|
1199
|
-
if (!ret) this._needDrain = true;
|
|
1200
|
-
return ret;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
_write_(chunk, encoding, callback, fromEnd) {
|
|
1204
|
-
if (typeof callback !== "function") callback = () => {};
|
|
1205
|
-
if (chunk === null) throw codeError(TypeError, "ERR_STREAM_NULL_VALUES", "May not write null values to stream");
|
|
1206
|
-
if (typeof chunk !== "string" && !(chunk instanceof Uint8Array)) {
|
|
1207
|
-
throw invalidArgType("chunk", "of type string or an instance of Buffer or Uint8Array", chunk);
|
|
1208
|
-
}
|
|
1209
|
-
let err;
|
|
1210
|
-
if (this.finished) err = codeError(Error, "ERR_STREAM_WRITE_AFTER_END", "write after end");
|
|
1211
|
-
else if (this.destroyed) err = codeError(Error, "ERR_STREAM_DESTROYED", "Cannot call write after a stream was destroyed");
|
|
1212
|
-
if (err) {
|
|
1213
|
-
if (!this.destroyed) process.nextTick(() => this.emit("error", err));
|
|
1214
|
-
process.nextTick(callback, err);
|
|
1215
|
-
return false;
|
|
1216
|
-
}
|
|
1217
|
-
const len = typeof chunk === "string" ? Buffer.byteLength(chunk, encoding || "utf8") : chunk.byteLength;
|
|
1218
|
-
if (!this._header) {
|
|
1219
|
-
if (fromEnd) this._contentLength = len;
|
|
1220
|
-
this._implicitHeader();
|
|
1221
|
-
}
|
|
1222
|
-
if (!this._hasBody) {
|
|
1223
|
-
process.nextTick(callback);
|
|
1224
|
-
return true;
|
|
1225
|
-
}
|
|
1226
|
-
if (this.chunkedEncoding && len !== 0) {
|
|
1227
|
-
this._send(len.toString(16), "latin1", null);
|
|
1228
|
-
this._send("\r\n", "latin1", null);
|
|
1229
|
-
this._send(chunk, encoding, null);
|
|
1230
|
-
return this._send("\r\n", "latin1", callback);
|
|
1231
|
-
}
|
|
1232
|
-
return this._send(chunk, encoding, callback);
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
end(chunk, encoding, callback) {
|
|
1236
|
-
if (typeof chunk === "function") {
|
|
1237
|
-
callback = chunk;
|
|
1238
|
-
chunk = null;
|
|
1239
|
-
encoding = null;
|
|
1240
|
-
} else if (typeof encoding === "function") {
|
|
1241
|
-
callback = encoding;
|
|
1242
|
-
encoding = null;
|
|
1243
|
-
}
|
|
1244
|
-
if (chunk) {
|
|
1245
|
-
if (this.finished) {
|
|
1246
|
-
this._write_(chunk, encoding, null, true);
|
|
1247
|
-
return this;
|
|
1248
|
-
}
|
|
1249
|
-
this._write_(chunk, encoding, null, true);
|
|
1250
|
-
} else if (this.finished) {
|
|
1251
|
-
if (typeof callback === "function") {
|
|
1252
|
-
if (!this.writableFinished) this.on("finish", callback);
|
|
1253
|
-
else callback(codeError(Error, "ERR_STREAM_ALREADY_FINISHED", "Cannot call end after a stream was finished"));
|
|
1254
|
-
}
|
|
1255
|
-
return this;
|
|
1256
|
-
} else if (!this._header) {
|
|
1257
|
-
this._contentLength = 0;
|
|
1258
|
-
this._implicitHeader();
|
|
1259
|
-
}
|
|
1260
|
-
if (typeof callback === "function") this.once("finish", callback);
|
|
1261
|
-
const finish = () => {
|
|
1262
|
-
if (this.socket && this.socket._hadError) return;
|
|
1263
|
-
this.writableFinished = true;
|
|
1264
|
-
this.emit("finish");
|
|
1265
|
-
};
|
|
1266
|
-
if (this._hasBody && this.chunkedEncoding) this._send("0\r\n" + this._trailer + "\r\n", "latin1", finish);
|
|
1267
|
-
else if (!this._headerSent || chunk) this._send("", "latin1", finish);
|
|
1268
|
-
else process.nextTick(finish);
|
|
1269
|
-
this.finished = true;
|
|
1270
|
-
this.writableEnded = true;
|
|
1271
|
-
this.writable = false;
|
|
1272
|
-
return this;
|
|
1273
|
-
}
|
|
1274
|
-
|
|
1275
|
-
flushHeaders() {
|
|
1276
|
-
if (!this._header) this._implicitHeader();
|
|
1277
|
-
this._send("");
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
1735
|
/* the socket the agent (or createConnection) gave: its listeners, the parser, then the output */
|
|
1281
1736
|
onSocket(socket, err) {
|
|
1737
|
+
/* as Node's: the socket's request and error listener set now, so an error before the next tick
|
|
1738
|
+
* reaches the request, and a request destroyed meanwhile gives its socket back to the agent */
|
|
1739
|
+
if (socket && !err) {
|
|
1740
|
+
socket._httpMessage = this;
|
|
1741
|
+
socket.on("error", socketErrorListener);
|
|
1742
|
+
}
|
|
1282
1743
|
process.nextTick(() => {
|
|
1283
1744
|
if (this.destroyed || err) {
|
|
1284
1745
|
this.destroyed = true;
|
|
@@ -1291,6 +1752,7 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1291
1752
|
if (socket) {
|
|
1292
1753
|
if (!err && this.agent && !socket.destroyed) {
|
|
1293
1754
|
socket.emit("free");
|
|
1755
|
+
socket.removeListener("error", socketErrorListener);
|
|
1294
1756
|
done(this._error);
|
|
1295
1757
|
} else {
|
|
1296
1758
|
socket.once("close", () => done(err || this._error));
|
|
@@ -1302,8 +1764,6 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1302
1764
|
return;
|
|
1303
1765
|
}
|
|
1304
1766
|
this.socket = socket;
|
|
1305
|
-
socket._httpMessage = this;
|
|
1306
|
-
socket.on("error", socketErrorListener);
|
|
1307
1767
|
socket.on("end", socketOnEnd);
|
|
1308
1768
|
socket.on("close", socketCloseListener);
|
|
1309
1769
|
if (this.timeout !== undefined || this.agent?.options?.timeout) listenSocketTimeout(this);
|
|
@@ -1329,6 +1789,7 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1329
1789
|
this.destroyed = true;
|
|
1330
1790
|
if (this.res) this.res._dump();
|
|
1331
1791
|
this._error = err;
|
|
1792
|
+
this._errored = err;
|
|
1332
1793
|
this.socket?.destroy(err);
|
|
1333
1794
|
return this;
|
|
1334
1795
|
}
|
|
@@ -1362,6 +1823,21 @@ export class ClientRequest extends OutgoingMessage {
|
|
|
1362
1823
|
}
|
|
1363
1824
|
}
|
|
1364
1825
|
|
|
1826
|
+
/* the request path, checked when set too: a path changed after the request was made can't smuggle
|
|
1827
|
+
* a header in (Node's) */
|
|
1828
|
+
Object.defineProperty(ClientRequest.prototype, "path", {
|
|
1829
|
+
get() {
|
|
1830
|
+
return this[kPath];
|
|
1831
|
+
},
|
|
1832
|
+
set(value) {
|
|
1833
|
+
const path = String(value);
|
|
1834
|
+
if (INVALID_PATH_REGEX.test(path)) throw codeError(TypeError, "ERR_UNESCAPED_CHARACTERS", "Request path contains unescaped characters");
|
|
1835
|
+
this[kPath] = path;
|
|
1836
|
+
},
|
|
1837
|
+
configurable: true,
|
|
1838
|
+
enumerable: true,
|
|
1839
|
+
});
|
|
1840
|
+
|
|
1365
1841
|
export function request(url, options, cb) {
|
|
1366
1842
|
return new ClientRequest(url, options, cb);
|
|
1367
1843
|
}
|
|
@@ -1384,6 +1860,13 @@ export function setMaxIdleHTTPParsers(max) {
|
|
|
1384
1860
|
export { Agent, globalAgent };
|
|
1385
1861
|
|
|
1386
1862
|
export default {
|
|
1387
|
-
METHODS, STATUS_CODES, Agent, ClientRequest, IncomingMessage, OutgoingMessage, ServerResponse, Server, createServer, get,
|
|
1863
|
+
METHODS, STATUS_CODES, Agent, ClientRequest, IncomingMessage, OutgoingMessage, ServerResponse, Server, createServer, get,
|
|
1388
1864
|
maxHeaderSize, request, setMaxIdleHTTPParsers, validateHeaderName, validateHeaderValue,
|
|
1865
|
+
/* an accessor, as Node's: a program may replace the agent requests use by default */
|
|
1866
|
+
get globalAgent() {
|
|
1867
|
+
return globalAgent;
|
|
1868
|
+
},
|
|
1869
|
+
set globalAgent(value) {
|
|
1870
|
+
setGlobalAgent(value);
|
|
1871
|
+
},
|
|
1389
1872
|
};
|