Keep the original socket error as the cause of ECONNRESET errors emitted
when an HTTP response closes before completion. Preserve the existing
aborted message, error code, and event order. Leave cause absent when
there is no underlying error.
Allow ConnResetException to accept Error options, document the behavior,
and cover TLS record errors, TCP resets, explicit destruction, and
premature closure without a socket error.
This follows investigation of #66001 and addresses lost error context.
The original TLS decryption failure remains unresolved.
Refs: https://github.com/nodejs/node/issues/66001
Signed-off-by: XadillaX <i@2333.moe>
PR-URL: https://github.com/nodejs/node/pull/66061
Reviewed-By: Robert Nagy <ronagy@icloud.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Aborting a ClientRequest after the request has finished sending
and the response has fully arrived has nothing left to cancel.
destroy() still called socket.destroy(err), but the resulting
'error' is emitted on a later tick. In that window,
responseKeepAlive() has already removed socketErrorListener while
handing the socket back to the agent's free pool, so the error
lands with no listener and crashes the process.
Skip the socket destroy when there is nothing left to cancel. This
matches the existing behavior of keepAlive: false requests, which
already drop any unread buffered response data in this situation.
Fixes: https://github.com/nodejs/node/issues/65938
Signed-off-by: Barath <barathraj048@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/65952
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Promote DEP0195 from documentation-only to a runtime deprecation.
Calling node:http constructors without `new` now emits DEP0195 via
deprecateInstantiation. This covers Agent, Server, OutgoingMessage,
IncomingMessage, ServerResponse, and ClientRequest.
Update in-tree tests that called Server/Agent without `new` to use
the keyword, and add a dedicated DEP0195 coverage test.
Refs: https://github.com/nodejs/node/pull/58518
Assisted-by: Grok
Signed-off-by: Yagiz Nizipli <yagiz@nizipli.com>
PR-URL: https://github.com/nodejs/node/pull/64853
Reviewed-By: Aviv Keller <me@aviv.sh>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
IncomingMessage 'close' is emitted when request handling completes, not
only when the underlying connection is closed. Using that event to abort
IncomingMessage.signal makes the signal abort after a request body is
read normally.
Track the underlying socket close instead, and detach the listener when
the request or response completes normally. This keeps the signal from
being aborted by normal stream completion or later keep-alive socket
closure, while still aborting it when the connection closes during the
active request lifecycle.
Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com>
PR-URL: https://github.com/nodejs/node/pull/64392
Fixes: https://github.com/nodejs/node/issues/64390
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
Reviewed-By: Gürgün Dayıoğlu <hey@gurgun.day>
`http.request({ highWaterMark })` passes the value to the TCP socket
via createConnection() but does not set it on the OutgoingMessage
internal kHighWaterMark. OutgoingMessage._writeRaw() has two mutually
exclusive write paths:
Path A (socket connected): conn.write() — uses socket HWM ✓
Path B (no socket yet): outputSize < this[kHighWaterMark] — uses
OutgoingMessage own default (64 KB) ✗
Because the OutgoingMessage constructor already accepts
options.highWaterMark, the fix is to set kHighWaterMark from the
user options after they are parsed in the ClientRequest constructor.
This resolves two symptoms:
1. write() returning the wrong boolean for pre-socket writes (the
user highWaterMark was silently ignored on all Node versions).
2. A deadlock on Node >= 24.16.0 where the incorrect false return
sets kNeedDrain, but drain never fires because the socket was
never backpressured (introduced by the stricter drain gate in
#62936).
Signed-off-by: Naman Trivedi <trivenay@amazon.com>
Fixes: https://github.com/nodejs/node/issues/64645
Refs: https://github.com/nodejs/node/pull/62936
PR-URL: https://github.com/nodejs/node/pull/64653
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
The perf_hooks HTTP client entry built the reported URL from the bare
hostname, dropping non-default ports and IPv6 brackets, and appended
the request path even after it had been rewritten to absolute-form for
proxying, duplicating the protocol and authority.
Report the connection authority captured at request creation and, for
proxied requests, use the rewritten absolute-form target as-is.
Fixes: https://github.com/nodejs/node/issues/59625
Signed-off-by: Stefano Baghino <stefano@baghino.me>
PR-URL: https://github.com/nodejs/node/pull/64311
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: theanarkh <theratliter@gmail.com>
When `options.path` passed to `http.request()` contains an absolute
URL, `http.request` has been sending it directly as the request target
in the HTTP 1.1 message. If the receiving server is a proxy,
the proxy server typically forwards the request to the destination
specified in the request target and ignores the Host header.
This means eventually the request can be forwarded to a destination
that is not consistent with `options.host`, depending on how the
receiving server behaves. Mimatched Host header and request target
also violates RFC 9112 Section 3.2, which we have been entirely
leaving to the users to verify.
This patch documents this behavior and warns that the user needs
to ensure the `path`, `option` and `headers` conform to the RFC.
If the receiving server is known to be a proxy server because
the request is routed by Node.js' built-in HTTP proxy support,
we now do a best-effort check to verify that the authority in
`options.path` (if absolute), Host headers and `options.host`
agree at request construction time. Node.js will give up on the
require target rewriting and throw an error when they don't match
at request construction.
Signed-off-by: Joyee Cheung <joyeec9h3@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/64108
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Reviewed-By: Ulises Gascón <ulisesgascongonzalez@gmail.com>
Add a new httpValidation option to http.createServer() and
http.request() / http.ClientRequest that controls how strictly
HTTP header values are validated:
- 'strict' - reject any non-ASCII or control characters (default)
- 'relaxed' - allow the non-ASCII characters permitted by the
Fetch specification (kLenientHeaderValueRelaxed)
- 'insecure' - disable all validation (like insecureHTTPParser)
The option is threaded through _storeHeader -> processHeader ->
storeHeader -> validateHeaderValue, and also through
writeInformation -> processInformationHeader -> validateHeaderValue.
Cannot be used together with insecureHTTPParser.
Fixes: https://github.com/nodejs/node/issues/61582
Signed-off-by: RajeshKumar11 <kakumanurajeshkumar@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/61597
Refs: https://github.com/nodejs/node/issues/61582
Refs: https://fetch.spec.whatwg.org/#header-value
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
Previously, this resulted in an error listener being added but not
cleaned up when the socket was freed back to the agent pool.
This also updates the docs to clarify when req.destroy() kills the
underlying socket - only if it's called during request processing (more
specifically: after onSocketNT, at which point we may have started
reading & writing).
Signed-off-by: Tim Perry <pimterry@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/62872
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
The `path` property on `ClientRequest` was only validated at
construction time. Add a getter/setter so that the same
`INVALID_PATH_REGEX` check runs whenever `req.path` is reassigned,
preventing invalid characters from reaching `_implicitHeader()`.
PR-URL: https://github.com/nodejs/node/pull/62030
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: Tim Perry <pimterry@gmail.com>
When the HTTP response ends before the request's 'finish' event fires,
responseOnEnd() and requestOnFinish() can both call responseKeepAlive(),
corrupting the socket that the agent may have already handed to another
request.
This commit adds a !req.destroyed guard to requestOnFinish() so the
second call is skipped when the socket has already been released.
Fixes: https://github.com/nodejs/node/issues/60001
PR-URL: https://github.com/nodejs/node/pull/61710
Reviewed-By: Tim Perry <pimterry@gmail.com>
This patch implements proxy support for HTTP and HTTPS clients and
agents in the `http` and `https` built-ins`. When NODE_USE_ENV_PROXY
is set to 1, the default global agent would parse the
HTTP_PROXY/http_proxy, HTTPS_PROXY/https_proxy, NO_PROXY/no_proxy
settings from the environment variables, and proxy the requests
sent through the built-in http/https client accordingly.
To support this, `http.Agent` and `https.Agent` now accept a few new
options:
- `proxyEnv`: when it's an object, the agent would read and parse
the HTTP_PROXY/http_proxy, HTTPS_PROXY/https_proxy, NO_PROXY/no_proxy
properties from it, and apply them based on the protocol it uses
to send requests. This option allows custom agents to
reuse built-in proxy support by composing options. Global agents
set this to `process.env` when NODE_USE_ENV_PROXY is 1.
- `defaultPort` and `protocol`: these allow setting of the default port
and protocol of the agents. We also need these when configuring
proxy settings and deciding whether a request should be proxied.
Implementation-wise, this adds a `ProxyConfig` internal class to handle
parsing and application of proxy configurations. The configuration
is parsed during agent construction. When requests are made,
the `createConnection()` methods on the agents would check whether
the request should be proxied. If yes, they either connect to the
proxy server (in the case of HTTP reqeusts) or establish a tunnel
(in the case of HTTPS requests) through either a TCP socket (if the
proxy uses HTTP) or a TLS socket (if the proxy uses HTTPS).
When proxying HTTPS requests through a tunnel, the connection listener
is invoked after the tunnel is established. Tunnel establishment uses
the timeout of the request options, if there is one. Otherwise it uses
the timeout of the agent.
If an error is encountered during tunnel establishment, an
ERR_PROXY_TUNNEL would be emitted on the returned socket. If the proxy
server sends a errored status code, the error would contain an
`statusCode` property. If the error is caused by timeout, the error
would contain a `proxyTunnelTimeout` property.
This implementation honors the built-in socket pool and socket limits.
Pooled sockets are still keyed by request endpoints, they are just
connected to the proxy server instead, and the persistence of the
connection can be maintained as long as the proxy server respects
connection/proxy-connection or persist by default (HTTP/1.1)
PR-URL: https://github.com/nodejs/node/pull/58980
Refs: https://github.com/nodejs/node/issues/57872
Refs: https://github.com/nodejs/node/issues/8381
Refs: https://github.com/nodejs/node/issues/15620
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This makes it possible to disable the various default headers directly
from the constructor. While this is possible for many use cases by
manually calling removeHeader on the request object instead, when
passing a raw header array to the request constructor the headers are
serialized and prepared to send immediately, and removeHeader cannot
subsequently be used.
With this change, it's now possible to 100% control sent request
headers by passing 'setDefaultHeaders: false' and a raw headers array to
http.request.
PR-URL: https://github.com/nodejs/node/pull/56112
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Yongsheng Zhang <zyszys98@gmail.com>
Reviewed-By: Marco Ippolito <marcoippolito54@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
When encountering ERR_UNESCAPED_CHARACTERS on large applications it can
be unclear which request has caused this error. Even when setting
NODE_DEBUG=http there is no information about this error since it's
thrown before any debug logs. This patch adds a debug log that contains
the invalid path.
PR-URL: https://github.com/nodejs/node/pull/45420
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Ensuring every request is assigned to a drained socket or nothing.
Because is has no benifit for a request to be attached to a non
drained socket and it prevents the request from being assigned to
a drained one, which might occur soon or already in the free pool
We achieve this by claiming a socket as free only when the socket
is drained.
PR-URL: https://github.com/nodejs/node/pull/43902
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Robert Nagy <ronagy@icloud.com>