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.
Files changed (434) hide show
  1. package/README.md +68 -4
  2. package/THIRD_PARTY_LICENSES +1216 -0
  3. package/cli.js +4461 -321
  4. package/node/include/js_node.h +57 -1
  5. package/node/include/js_uv.h +8 -1
  6. package/node/prelude/_http_agent.js +5 -0
  7. package/node/prelude/_http_server.js +7 -0
  8. package/node/prelude/_stream_duplex.js +4 -0
  9. package/node/prelude/_stream_passthrough.js +4 -0
  10. package/node/prelude/_stream_readable.js +4 -0
  11. package/node/prelude/_stream_transform.js +4 -0
  12. package/node/prelude/_stream_writable.js +4 -0
  13. package/node/prelude/assert.js +1334 -0
  14. package/node/prelude/assert_strict.js +12 -0
  15. package/node/prelude/async_hooks.js +1 -1
  16. package/node/prelude/buffer.js +43 -3
  17. package/node/prelude/child_process.js +1098 -0
  18. package/node/prelude/cluster.js +52 -0
  19. package/node/prelude/console.js +328 -0
  20. package/node/prelude/constants.js +10 -0
  21. package/node/prelude/crypto.js +1 -1
  22. package/node/prelude/diagnostics_channel.js +1 -1
  23. package/node/prelude/dns.js +8 -2
  24. package/node/prelude/domain.js +115 -0
  25. package/node/prelude/events.js +148 -16
  26. package/node/prelude/fs.js +1138 -151
  27. package/node/prelude/fs_promises.js +117 -11
  28. package/node/prelude/http.js +784 -301
  29. package/node/prelude/http2.js +1 -1
  30. package/node/prelude/https.js +159 -0
  31. package/node/prelude/inspector.js +27 -0
  32. package/node/prelude/inspector_promises.js +4 -0
  33. package/node/prelude/internal/async_context.js +1 -1
  34. package/node/prelude/internal/buffer_transcode.js +44 -0
  35. package/node/prelude/internal/comparisons.js +916 -0
  36. package/node/prelude/internal/compression_streams.js +153 -0
  37. package/node/prelude/internal/crypto/asym.js +27 -14
  38. package/node/prelude/internal/crypto/cipher.js +16 -5
  39. package/node/prelude/internal/crypto/diffiehellman.js +3 -2
  40. package/node/prelude/internal/crypto/hash.js +2 -1
  41. package/node/prelude/internal/crypto/kdf.js +14 -9
  42. package/node/prelude/internal/crypto/keys.js +63 -50
  43. package/node/prelude/internal/crypto/random.js +2 -1
  44. package/node/prelude/internal/crypto/util.js +4 -4
  45. package/node/prelude/internal/dotenv.js +98 -0
  46. package/node/prelude/internal/errors.js +21 -5
  47. package/node/prelude/internal/events_extras.js +113 -0
  48. package/node/prelude/internal/events_on.js +100 -0
  49. package/node/prelude/internal/features.js +72 -0
  50. package/node/prelude/internal/fetch.js +364 -0
  51. package/node/prelude/internal/form_data.js +376 -0
  52. package/node/prelude/internal/fs_bigint_stats.js +41 -0
  53. package/node/prelude/internal/fs_blob.js +19 -0
  54. package/node/prelude/internal/fs_cp.js +709 -0
  55. package/node/prelude/internal/fs_dir.js +166 -0
  56. package/node/prelude/internal/fs_filehandle.js +287 -0
  57. package/node/prelude/internal/fs_glob.js +296 -0
  58. package/node/prelude/internal/fs_statfs.js +42 -0
  59. package/node/prelude/internal/fs_stats.js +89 -0
  60. package/node/prelude/internal/fs_streams.js +541 -0
  61. package/node/prelude/internal/fs_utils.js +286 -0
  62. package/node/prelude/internal/fs_watch.js +580 -0
  63. package/node/prelude/internal/http_agent.js +114 -2
  64. package/node/prelude/internal/http_symbols.js +3 -0
  65. package/node/prelude/internal/mime.js +249 -0
  66. package/node/prelude/internal/net.js +1 -0
  67. package/node/prelude/internal/net_bound.js +123 -0
  68. package/node/prelude/internal/net_onread.js +73 -0
  69. package/node/prelude/internal/net_tos.js +54 -0
  70. package/node/prelude/internal/parse_args.js +288 -0
  71. package/node/prelude/internal/perf_histogram.js +435 -0
  72. package/node/prelude/internal/perf_observe.js +125 -0
  73. package/node/prelude/internal/performance_global.js +19 -0
  74. package/node/prelude/internal/process_events.js +49 -1
  75. package/node/prelude/internal/process_memory.js +14 -0
  76. package/node/prelude/internal/process_more.js +414 -0
  77. package/node/prelude/internal/process_signals.js +2 -1
  78. package/node/prelude/internal/process_stdin.js +52 -0
  79. package/node/prelude/internal/process_stdio.js +36 -0
  80. package/node/prelude/internal/querystring.js +0 -0
  81. package/node/prelude/internal/readline.js +338 -0
  82. package/node/prelude/internal/socketaddress.js +8 -4
  83. package/node/prelude/internal/source_map.js +122 -0
  84. package/node/prelude/internal/stream_compose.js +74 -0
  85. package/node/prelude/internal/stream_from.js +6 -0
  86. package/node/prelude/internal/stream_operators.js +23 -0
  87. package/node/prelude/internal/stream_pipeline.js +7 -0
  88. package/node/prelude/internal/stream_transform.js +10 -0
  89. package/node/prelude/internal/stream_web_adapters.js +143 -0
  90. package/node/prelude/internal/streams/add_abort_signal.js +58 -0
  91. package/node/prelude/internal/streams/compose.js +244 -0
  92. package/node/prelude/internal/streams/destroy.js +358 -0
  93. package/node/prelude/internal/streams/duplex.js +199 -0
  94. package/node/prelude/internal/streams/duplexify.js +378 -0
  95. package/node/prelude/internal/streams/end_of_stream.js +387 -0
  96. package/node/prelude/internal/streams/errors.js +216 -0
  97. package/node/prelude/internal/streams/from.js +211 -0
  98. package/node/prelude/internal/streams/legacy.js +120 -0
  99. package/node/prelude/internal/streams/operators.js +375 -0
  100. package/node/prelude/internal/streams/passthrough.js +45 -0
  101. package/node/prelude/internal/streams/pipeline.js +468 -0
  102. package/node/prelude/internal/streams/readable.js +1835 -0
  103. package/node/prelude/internal/streams/state.js +48 -0
  104. package/node/prelude/internal/streams/transform.js +203 -0
  105. package/node/prelude/internal/streams/utils.js +355 -0
  106. package/node/prelude/internal/streams/validators.js +108 -0
  107. package/node/prelude/internal/streams/writable.js +1160 -0
  108. package/node/prelude/internal/structured_clone.js +238 -0
  109. package/node/prelude/internal/system_errors.js +37 -0
  110. package/node/prelude/internal/test_runner/mock.js +431 -0
  111. package/node/prelude/internal/test_runner/reporters.js +404 -0
  112. package/node/prelude/internal/test_runner/test.js +1571 -0
  113. package/node/prelude/internal/timers.js +38 -7
  114. package/node/prelude/internal/timers_global.js +1 -1
  115. package/node/prelude/internal/tls/common.js +8 -2
  116. package/node/prelude/internal/tls/wrap.js +5 -16
  117. package/node/prelude/internal/tls/x509.js +1 -1
  118. package/node/prelude/internal/tty_read_stream.js +39 -0
  119. package/node/prelude/internal/tty_streams.js +3 -0
  120. package/node/prelude/internal/util_debuglog.js +68 -0
  121. package/node/prelude/internal/util_diff.js +63 -0
  122. package/node/prelude/internal/util_more.js +111 -0
  123. package/node/prelude/internal/util_styles.js +76 -0
  124. package/node/prelude/internal/zlib_engines.js +1 -1
  125. package/node/prelude/module.js +219 -8
  126. package/node/prelude/net.js +197 -49
  127. package/node/prelude/os.js +88 -11
  128. package/node/prelude/path.js +11 -7
  129. package/node/prelude/path_posix.js +1 -1
  130. package/node/prelude/path_win32.js +1 -1
  131. package/node/prelude/perf_hooks.js +151 -0
  132. package/node/prelude/process.js +36 -1
  133. package/node/prelude/punycode.js +188 -0
  134. package/node/prelude/querystring.js +1 -1
  135. package/node/prelude/readline.js +8 -0
  136. package/node/prelude/readline_promises.js +88 -0
  137. package/node/prelude/repl.js +38 -0
  138. package/node/prelude/sea.js +23 -0
  139. package/node/prelude/stream.js +70 -1332
  140. package/node/prelude/stream_consumers.js +34 -0
  141. package/node/prelude/stream_promises.js +1 -1
  142. package/node/prelude/stream_web.js +15 -2
  143. package/node/prelude/string_decoder.js +1 -1
  144. package/node/prelude/sys.js +7 -0
  145. package/node/prelude/test.js +56 -0
  146. package/node/prelude/test_reporters.js +269 -0
  147. package/node/prelude/timers.js +7 -84
  148. package/node/prelude/timers_promises.js +1 -1
  149. package/node/prelude/tls.js +2 -1
  150. package/node/prelude/trace_events.js +52 -0
  151. package/node/prelude/tty.js +10 -4
  152. package/node/prelude/url.js +88 -27
  153. package/node/prelude/util.js +121 -5
  154. package/node/prelude/util_types.js +4 -3
  155. package/node/prelude/v8.js +814 -0
  156. package/node/prelude/vm.js +86 -0
  157. package/node/prelude/wasi.js +10 -0
  158. package/node/prelude/worker_threads.js +56 -0
  159. package/node/prelude/zlib.js +58 -25
  160. package/node/src/host_brotli.c +59 -11
  161. package/node/src/host_fs.c +342 -11
  162. package/node/src/host_fswatch.c +170 -0
  163. package/node/src/host_http.c +292 -39
  164. package/node/src/host_loop.c +25 -2
  165. package/node/src/host_net.c +130 -16
  166. package/node/src/host_os.c +55 -1
  167. package/node/src/host_proc.c +291 -0
  168. package/node/src/host_process.c +488 -0
  169. package/node/src/host_sys.c +64 -2
  170. package/node/src/host_zlib.c +27 -7
  171. package/node/src/host_zstd.c +41 -6
  172. package/node/third_party/hdrhistogram/LICENSE +42 -0
  173. package/node/third_party/hdrhistogram/README.md +5 -0
  174. package/node/third_party/libuv/LICENSE +0 -0
  175. package/node/third_party/libuv/LICENSE-extra +36 -0
  176. package/node/third_party/libuv/include/uv/aix.h +0 -0
  177. package/node/third_party/libuv/include/uv/bsd.h +0 -0
  178. package/node/third_party/libuv/include/uv/darwin.h +0 -0
  179. package/node/third_party/libuv/include/uv/errno.h +0 -0
  180. package/node/third_party/libuv/include/uv/linux.h +0 -0
  181. package/node/third_party/libuv/include/uv/os390.h +0 -0
  182. package/node/third_party/libuv/include/uv/posix.h +0 -0
  183. package/node/third_party/libuv/include/uv/sunos.h +0 -0
  184. package/node/third_party/libuv/include/uv/threadpool.h +0 -0
  185. package/node/third_party/libuv/include/uv/tree.h +0 -0
  186. package/node/third_party/libuv/include/uv/unix.h +0 -0
  187. package/node/third_party/libuv/include/uv/version.h +0 -0
  188. package/node/third_party/libuv/include/uv/win.h +0 -0
  189. package/node/third_party/libuv/include/uv.h +0 -0
  190. package/node/third_party/libuv/src/fs-poll.c +0 -0
  191. package/node/third_party/libuv/src/heap-inl.h +0 -0
  192. package/node/third_party/libuv/src/idna.c +0 -0
  193. package/node/third_party/libuv/src/idna.h +0 -0
  194. package/node/third_party/libuv/src/inet.c +0 -0
  195. package/node/third_party/libuv/src/queue.h +0 -0
  196. package/node/third_party/libuv/src/random.c +0 -0
  197. package/node/third_party/libuv/src/strscpy.c +0 -0
  198. package/node/third_party/libuv/src/strscpy.h +0 -0
  199. package/node/third_party/libuv/src/strtok.c +0 -0
  200. package/node/third_party/libuv/src/strtok.h +0 -0
  201. package/node/third_party/libuv/src/thread-common.c +0 -0
  202. package/node/third_party/libuv/src/threadpool.c +0 -0
  203. package/node/third_party/libuv/src/timer.c +0 -0
  204. package/node/third_party/libuv/src/unix/async.c +0 -0
  205. package/node/third_party/libuv/src/unix/bsd-ifaddrs.c +0 -0
  206. package/node/third_party/libuv/src/unix/core.c +0 -0
  207. package/node/third_party/libuv/src/unix/darwin-proctitle.c +0 -0
  208. package/node/third_party/libuv/src/unix/darwin-stub.h +0 -0
  209. package/node/third_party/libuv/src/unix/darwin-syscalls.h +0 -0
  210. package/node/third_party/libuv/src/unix/darwin.c +0 -0
  211. package/node/third_party/libuv/src/unix/dl.c +0 -0
  212. package/node/third_party/libuv/src/unix/fs.c +0 -0
  213. package/node/third_party/libuv/src/unix/fsevents.c +0 -0
  214. package/node/third_party/libuv/src/unix/getaddrinfo.c +0 -0
  215. package/node/third_party/libuv/src/unix/getnameinfo.c +0 -0
  216. package/node/third_party/libuv/src/unix/internal.h +0 -0
  217. package/node/third_party/libuv/src/unix/kqueue.c +0 -0
  218. package/node/third_party/libuv/src/unix/linux.c +0 -0
  219. package/node/third_party/libuv/src/unix/loop-watcher.c +0 -0
  220. package/node/third_party/libuv/src/unix/loop.c +0 -0
  221. package/node/third_party/libuv/src/unix/pipe.c +0 -0
  222. package/node/third_party/libuv/src/unix/poll.c +0 -0
  223. package/node/third_party/libuv/src/unix/process.c +0 -0
  224. package/node/third_party/libuv/src/unix/procfs-exepath.c +0 -0
  225. package/node/third_party/libuv/src/unix/proctitle.c +0 -0
  226. package/node/third_party/libuv/src/unix/random-devurandom.c +0 -0
  227. package/node/third_party/libuv/src/unix/random-getentropy.c +0 -0
  228. package/node/third_party/libuv/src/unix/random-getrandom.c +0 -0
  229. package/node/third_party/libuv/src/unix/random-sysctl-linux.c +0 -0
  230. package/node/third_party/libuv/src/unix/signal.c +0 -0
  231. package/node/third_party/libuv/src/unix/stream.c +0 -0
  232. package/node/third_party/libuv/src/unix/tcp.c +0 -0
  233. package/node/third_party/libuv/src/unix/thread.c +0 -0
  234. package/node/third_party/libuv/src/unix/tty.c +0 -0
  235. package/node/third_party/libuv/src/unix/udp.c +0 -0
  236. package/node/third_party/libuv/src/uv-common.c +0 -0
  237. package/node/third_party/libuv/src/uv-common.h +0 -0
  238. package/node/third_party/libuv/src/uv-data-getter-setters.c +0 -0
  239. package/node/third_party/libuv/src/version.c +0 -0
  240. package/node/third_party/llhttp/LICENSE +0 -0
  241. package/node/third_party/llhttp/api.c +0 -0
  242. package/node/third_party/llhttp/http.c +0 -0
  243. package/node/third_party/llhttp/llhttp.c +0 -0
  244. package/node/third_party/llhttp/llhttp.h +0 -0
  245. package/node/third_party/nodejs/LICENSE +47 -0
  246. package/node/third_party/nodejs/README.md +5 -0
  247. package/node/third_party/parseargs/LICENSE +21 -0
  248. package/node/third_party/parseargs/README.md +5 -0
  249. package/node/third_party/punycode/LICENSE +20 -0
  250. package/node/third_party/punycode/README.md +5 -0
  251. package/node/third_party/zlib/LICENSE.chromium +27 -0
  252. package/package.json +1 -1
  253. package/prebuilt/darwin-arm64/boringssl-4a47438eaf7e73ba.a +0 -0
  254. package/prebuilt/darwin-arm64/brotli-7995bbdacc52e51c.a +0 -0
  255. package/prebuilt/darwin-arm64/uv-72ab666eae197cd8.a +0 -0
  256. package/prebuilt/darwin-arm64/zlib-4ce2317a4c3f5fb5.a +0 -0
  257. package/prebuilt/darwin-arm64/zstd-015315ac7c784c73.a +0 -0
  258. package/prebuilt/darwin-x64/boringssl-4a47438eaf7e73ba.a +0 -0
  259. package/prebuilt/darwin-x64/brotli-7995bbdacc52e51c.a +0 -0
  260. package/prebuilt/darwin-x64/uv-72ab666eae197cd8.a +0 -0
  261. package/prebuilt/darwin-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  262. package/prebuilt/darwin-x64/zstd-015315ac7c784c73.a +0 -0
  263. package/prebuilt/linux-arm64/boringssl-4a47438eaf7e73ba.a +0 -0
  264. package/prebuilt/linux-arm64/brotli-65481ff838ce9def.a +0 -0
  265. package/prebuilt/linux-arm64/uv-f1353f50ff983f3c.a +0 -0
  266. package/prebuilt/linux-arm64/zlib-4ce2317a4c3f5fb5.a +0 -0
  267. package/prebuilt/linux-arm64/zstd-015315ac7c784c73.a +0 -0
  268. package/prebuilt/linux-arm64-musl/boringssl-4a47438eaf7e73ba.a +0 -0
  269. package/prebuilt/linux-arm64-musl/brotli-65481ff838ce9def.a +0 -0
  270. package/prebuilt/linux-arm64-musl/uv-f1353f50ff983f3c.a +0 -0
  271. package/prebuilt/linux-arm64-musl/zlib-4ce2317a4c3f5fb5.a +0 -0
  272. package/prebuilt/linux-arm64-musl/zstd-015315ac7c784c73.a +0 -0
  273. package/prebuilt/linux-x64/boringssl-4a47438eaf7e73ba.a +0 -0
  274. package/prebuilt/linux-x64/brotli-65481ff838ce9def.a +0 -0
  275. package/prebuilt/linux-x64/uv-f1353f50ff983f3c.a +0 -0
  276. package/prebuilt/linux-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  277. package/prebuilt/linux-x64/zstd-015315ac7c784c73.a +0 -0
  278. package/prebuilt/linux-x64-musl/boringssl-4a47438eaf7e73ba.a +0 -0
  279. package/prebuilt/linux-x64-musl/brotli-65481ff838ce9def.a +0 -0
  280. package/prebuilt/linux-x64-musl/uv-f1353f50ff983f3c.a +0 -0
  281. package/prebuilt/linux-x64-musl/zlib-4ce2317a4c3f5fb5.a +0 -0
  282. package/prebuilt/linux-x64-musl/zstd-015315ac7c784c73.a +0 -0
  283. package/prebuilt/win32-x64/boringssl-ce573858472b7fc9.a +0 -0
  284. package/prebuilt/win32-x64/brotli-20d86a61f30baeb3.a +0 -0
  285. package/prebuilt/win32-x64/uv-b11bae65d7fced82.a +0 -0
  286. package/prebuilt/win32-x64/zlib-4ce2317a4c3f5fb5.a +0 -0
  287. package/prebuilt/win32-x64/zstd-015315ac7c784c73.a +0 -0
  288. package/runtime/include/js_atoms.gen.h +0 -0
  289. package/runtime/include/js_atoms.h +5 -1
  290. package/runtime/include/js_bigint.h +1 -1
  291. package/runtime/include/js_buffer.h +4 -4
  292. package/runtime/include/js_builtins.h +41 -3
  293. package/runtime/include/js_console.h +9 -4
  294. package/runtime/include/js_convert.h +1 -1
  295. package/runtime/include/js_core.def +5 -4
  296. package/runtime/include/js_crypto.h +1 -1
  297. package/runtime/include/js_error.h +1 -1
  298. package/runtime/include/js_exception.h +21 -1
  299. package/runtime/include/js_function.h +33 -9
  300. package/runtime/include/js_gc.h +172 -30
  301. package/runtime/include/js_generator.h +2 -2
  302. package/runtime/include/js_host.h +19 -1
  303. package/runtime/include/js_jobs.h +5 -5
  304. package/runtime/include/js_module.h +1 -1
  305. package/runtime/include/js_object.h +142 -15
  306. package/runtime/include/js_ops.h +107 -5
  307. package/runtime/include/js_promise.h +3 -3
  308. package/runtime/include/js_regexp.h +1 -1
  309. package/runtime/include/js_runtime.h +0 -0
  310. package/runtime/include/js_symbol.h +1 -1
  311. package/runtime/include/js_web.h +5 -1
  312. package/runtime/include/jscell.h +22 -15
  313. package/runtime/include/jscontext.h +82 -39
  314. package/runtime/include/jsnum.h +1 -1
  315. package/runtime/include/jsstring.h +19 -6
  316. package/runtime/include/jsvalue.h +7 -3
  317. package/runtime/src/arguments.c +2 -2
  318. package/runtime/src/array.c +4 -1
  319. package/runtime/src/arraybuffer.c +19 -7
  320. package/runtime/src/async_iter.c +4 -3
  321. package/runtime/src/atoms.c +38 -4
  322. package/runtime/src/base64.c +2 -2
  323. package/runtime/src/bigint.c +1 -1
  324. package/runtime/src/blob.c +22 -9
  325. package/runtime/src/boringssl_support.cc +1 -1
  326. package/runtime/src/buffer.c +299 -91
  327. package/runtime/src/builtin_table.c +1 -1
  328. package/runtime/src/builtins.c +30 -4
  329. package/runtime/src/builtins_util.h +0 -0
  330. package/runtime/src/collections.c +18 -3
  331. package/runtime/src/components.c +1 -1
  332. package/runtime/src/console.c +14 -7
  333. package/runtime/src/convert.c +0 -0
  334. package/runtime/src/core_all.c +1 -1
  335. package/runtime/src/crypto.c +1 -1
  336. package/runtime/src/crypto_asym.c +1 -1
  337. package/runtime/src/crypto_bn.c +1 -1
  338. package/runtime/src/crypto_cipher.c +10 -3
  339. package/runtime/src/crypto_hash.c +1 -1
  340. package/runtime/src/crypto_host.c +1 -1
  341. package/runtime/src/crypto_internal.h +1 -1
  342. package/runtime/src/crypto_kdf.c +1 -1
  343. package/runtime/src/crypto_key.c +14 -1
  344. package/runtime/src/crypto_pkey.c +1 -1
  345. package/runtime/src/crypto_subtle.c +142 -58
  346. package/runtime/src/crypto_tls.c +1 -1
  347. package/runtime/src/date.c +5 -2
  348. package/runtime/src/domexception.c +2 -2
  349. package/runtime/src/elements.c +56 -17
  350. package/runtime/src/elements.h +7 -2
  351. package/runtime/src/encoding.c +1 -1
  352. package/runtime/src/error.c +4 -4
  353. package/runtime/src/events.c +42 -14
  354. package/runtime/src/exception.c +1 -1
  355. package/runtime/src/fetch.c +241 -17
  356. package/runtime/src/function.c +31 -19
  357. package/runtime/src/gc.c +1720 -211
  358. package/runtime/src/gc_visit.h +87 -0
  359. package/runtime/src/generator.c +5 -3
  360. package/runtime/src/global.c +14 -6
  361. package/runtime/src/headers.c +10 -10
  362. package/runtime/src/inspect.c +76 -17
  363. package/runtime/src/internal.h +9 -6
  364. package/runtime/src/iterate.c +94 -2
  365. package/runtime/src/iterator.c +1 -1
  366. package/runtime/src/jobs.c +11 -5
  367. package/runtime/src/jsnum.c +2 -2
  368. package/runtime/src/json.c +1 -1
  369. package/runtime/src/math.c +9 -1
  370. package/runtime/src/module.c +3 -3
  371. package/runtime/src/number.c +0 -0
  372. package/runtime/src/object.c +107 -74
  373. package/runtime/src/ops.c +21 -3
  374. package/runtime/src/os.c +192 -1
  375. package/runtime/src/os.h +33 -2
  376. package/runtime/src/process.c +73 -8
  377. package/runtime/src/promise.c +4 -4
  378. package/runtime/src/proxy.c +1 -1
  379. package/runtime/src/reflect.c +1 -1
  380. package/runtime/src/regexp.c +700 -11
  381. package/runtime/src/regexp_anchor.h +1 -1
  382. package/runtime/src/regexp_message.h +1 -1
  383. package/runtime/src/shape.c +78 -20
  384. package/runtime/src/shape.h +0 -0
  385. package/runtime/src/streams.c +22 -8
  386. package/runtime/src/streams.h +0 -0
  387. package/runtime/src/string.c +64 -25
  388. package/runtime/src/string_builtins.c +7 -7
  389. package/runtime/src/symbol.c +2 -7
  390. package/runtime/src/test_host.c +3 -3
  391. package/runtime/src/tls_roots.gen.h +102 -620
  392. package/runtime/src/typedarray.c +8 -3
  393. package/runtime/src/tznames.gen.h +2 -1
  394. package/runtime/src/uri.c +1 -1
  395. package/runtime/src/url.c +42 -39
  396. package/runtime/src/weak.c +110 -36
  397. package/runtime/src/web.c +21 -3
  398. package/runtime/src/wrapper.c +0 -0
  399. package/runtime/src/writable.c +7 -6
  400. package/runtime/third_party/ada-idna/LICENSE-MIT +0 -0
  401. package/runtime/third_party/ada-idna/README.md +7 -7
  402. package/runtime/third_party/ada-idna/convert.mjs +89 -27
  403. package/runtime/third_party/ada-idna/idna.c +30 -31
  404. package/runtime/third_party/ada-idna/idna.h +0 -0
  405. package/runtime/third_party/ada-idna/idna_tables.h +2272 -1952
  406. package/runtime/third_party/boringssl/third_party/fiat/LICENSE +15 -0
  407. package/runtime/third_party/mozilla-ca/LICENSE +373 -0
  408. package/runtime/third_party/mozilla-ca/README.md +9 -0
  409. package/runtime/third_party/nodejs/LICENSE +47 -0
  410. package/runtime/third_party/nodejs/README.md +7 -0
  411. package/runtime/third_party/quickjs-dtoa/LICENSE +0 -0
  412. package/runtime/third_party/quickjs-dtoa/README.md +1 -1
  413. package/runtime/third_party/quickjs-dtoa/dtoa.c +0 -0
  414. package/runtime/third_party/quickjs-dtoa/dtoa.h +0 -0
  415. package/runtime/third_party/quickjs-dtoa/dtoa_compat.h +0 -0
  416. package/runtime/third_party/quickjs-regexp/LICENSE +0 -0
  417. package/runtime/third_party/quickjs-regexp/README.md +2 -2
  418. package/runtime/third_party/quickjs-regexp/cutils.c +0 -0
  419. package/runtime/third_party/quickjs-regexp/cutils.h +0 -0
  420. package/runtime/third_party/quickjs-regexp/libregexp-opcode.h +0 -0
  421. package/runtime/third_party/quickjs-regexp/libregexp.c +0 -0
  422. package/runtime/third_party/quickjs-regexp/libregexp.h +0 -0
  423. package/runtime/third_party/quickjs-regexp/libunicode-table.h +0 -0
  424. package/runtime/third_party/quickjs-regexp/libunicode.c +0 -0
  425. package/runtime/third_party/quickjs-regexp/libunicode.h +0 -0
  426. package/runtime/third_party/unicode/LICENSE +39 -0
  427. package/runtime/third_party/unicode/README.md +9 -0
  428. package/runtime/third_party/v8-ieee754/LICENSE +0 -0
  429. package/runtime/third_party/v8-ieee754/LICENSE.fdlibm +10 -0
  430. package/runtime/third_party/v8-ieee754/README.md +3 -2
  431. package/runtime/third_party/v8-ieee754/convert.py +0 -0
  432. package/runtime/third_party/v8-ieee754/ieee754.c +0 -0
  433. package/runtime/third_party/v8-ieee754/ieee754.h +0 -0
  434. package/runtime/tools/regexp_compile.c +2 -2
@@ -1,4 +1,4 @@
1
- // node:http (ADR 0012, layer 3), the server side: createServer, Server, IncomingMessage and
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 { httpAttach, httpDate, httpDetach, httpExpired, httpFlush, httpParsers, httpWrite, timerUnref } from "visaljs:host";
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 body was complete */
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
- if (this.complete || this.destroyed) return;
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 (existing === undefined) headers[key] = key === "set-cookie" ? [value] : value;
140
- else if (key === "set-cookie") existing.push(value);
141
- else if (SINGLE.has(key) && !join) return;
142
- else headers[key] = existing + (key === "cookie" ? "; " : ", ") + value;
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.writableHighWaterMark = 16384;
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
- this._keepAlive = keepAlive;
283
- this._keepAliveTimeout = server ? server.keepAliveTimeout : 5000;
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) httpWrite(this._conn, this._renderHead(undefined));
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
- for (let i = 0; i < headers.length; i += 2) this.appendHeader(headers[i], headers[i + 1]);
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(undefined);
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
- let head = "HTTP/1.1 " + status + " " + (this.statusMessage ?? STATUS_CODES[status] ?? "unknown") + "\r\n";
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
- const e = new Error(this.destroyed ? "Cannot call write after a stream was destroyed" : "write after end");
387
- e.code = this.destroyed ? "ERR_STREAM_DESTROYED" : "ERR_STREAM_WRITE_AFTER_END";
388
- queueMicrotask(() => this.emit("error", e));
389
- if (typeof callback === "function") queueMicrotask(() => callback(e));
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) httpWrite(this._conn, 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
- httpWrite(this._conn, chunkLength(chunk).toString(16) + "\r\n");
398
- httpWrite(this._conn, chunk);
399
- httpWrite(this._conn, "\r\n");
791
+ this._send(chunkLength(chunk).toString(16) + "\r\n");
792
+ this._send(chunk);
793
+ this._send("\r\n");
400
794
  } else {
401
- httpWrite(this._conn, chunk);
795
+ this._send(chunk);
402
796
  }
403
797
  }
404
798
  if (typeof callback === "function") queueMicrotask(callback);
405
- return true;
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) httpWrite(this._conn, this._renderHead(length) + chunk);
421
- else {
422
- httpWrite(this._conn, this._renderHead(length));
423
- if (hasChunk && length && !this._head) httpWrite(this._conn, chunk);
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) httpWrite(this._conn, head);
429
- if (this.chunkedEncoding && !this._head) httpWrite(this._conn, "0\r\n" + this._trailer + "\r\n");
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 (!this._keepAlive && this.socket) { /* the response goes out, then the socket ends */
434
- httpFlush(this._conn);
435
- this.socket.destroySoon();
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
- export class Server extends NetServer {
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
- if (typeof options === "function") {
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("connection", (socket) => this._onSocket(socket));
503
- this.on("listening", () => this._checkConnectionsLater());
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
- _checkConnectionsLater() {
508
- if (this.connectionsCheckingInterval <= 0 || this._checking !== null) return;
509
- this._checking = timerUnref(() => {
510
- this._checking = null;
511
- if (this._handle === null) return;
512
- if (this._parsers !== -1 && (this.headersTimeout > 0 || this.requestTimeout > 0)) {
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
- this._checkConnectionsLater();
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
- for (const state of [...this._states.values()]) if (!state.req && !state.res) state.socket.destroy();
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
- if (this._checking !== null) clearTimeout(this._checking);
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 (ADR 0020), as the server's sockets' callbacks */
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) => this._states.get(conn)?.req?._push(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
- if (state.res === null || state.res.writableEnded) state.req = null;
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 = new Error("Parse Error: " + reason);
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._handle;
1087
+ const conn = connOf(socket);
575
1088
  if (conn === null) return;
576
- const state = { socket, conn, req: null, res: null, keepAliveTimeoutSet: false, listeners: null };
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._abortIncoming(state);
582
- if (socket.writable) socket.end();
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 res = state.res;
1114
+ const responses = state.res ? [state.res, ...state.outgoing] : state.outgoing;
588
1115
  state.res = null;
589
- if (res) res._connectionClosed();
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
- if (req) req._abort();
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 IncomingMessage(socket);
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
- const res = new ServerResponse(req, conn, keepAlive, this);
635
- state.res = res;
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._handle);
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: handle id → socket */
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 (ADR 0020): its response's callbacks run in it, as 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) => reqOf(conn)?.res?._push(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)) freeParser(socket);
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 = new Error("Parse Error: " + reason);
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 || socket._handle === null) return;
780
- clientSockets.set(socket._handle, socket);
781
- socket._clientConn = socket._handle;
782
- httpAttach(socket._handle, clientParsersId(), true, req.maxHeaderSize ?? maxHeaderSize);
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
- if (socket.connecting || socket._handle === null) socket.once("connect", attach);
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 = { ...input, ...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 === undefined || Boolean(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.path = options.path || "/";
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, globalAgent,
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
  };