-
-
Notifications
You must be signed in to change notification settings - Fork 32.1k
http,https: add built-in proxy support in http/https.request and Agent #58980
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Rewrite to ESM to use TLA. Also add a test to make sure case precedence is honored. Refs: https://about.gitlab.com/blog/we-need-to-talk-no-proxy
Review requested:
|
055344f
to
0e19610
Compare
0e19610
to
2bcc125
Compare
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)
2bcc125
to
fd6026e
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #58980 +/- ##
==========================================
+ Coverage 90.06% 90.10% +0.03%
==========================================
Files 640 641 +1
Lines 188523 189243 +720
Branches 36982 37119 +137
==========================================
+ Hits 169796 170509 +713
+ Misses 11449 11438 -11
- Partials 7278 7296 +18
🚀 New features to boost your workflow:
|
822460a
to
2172897
Compare
The
notable-change
Please suggest a text for the release notes if you'd like to include a more detailed summary, then proceed to update the PR description with the text or a link to the notable change suggested text comment. Otherwise, the commit will be placed in the Other Notable Changes section. |
This patch implements proxy support for HTTP and HTTPS clients and
agents in the
http
andhttps
built-ins`. When NODE_USE_ENV_PROXYis 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
andhttps.Agent
now accept a few newoptions:
proxyEnv
: when it's an object, the agent would read and parsethe 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
andprotocol
: these allow setting of the default portand protocol of the agents. We also need these when configuring
proxy settings and deciding whether a request should be proxied.
Example
Starting a Node.js process with proxy support enabled for all requests sent
through the default global agent:
NODE_USE_ENV_PROXY=1 HTTP_PROXY=http://proxy.example.com:8080 NO_PROXY=localhost,127.0.0.1 node client.js
To create a custom agent with built-in proxy support:
Alternatively, the following also works:
Implementation
Implementation-wise, this adds a
ProxyConfig
internal class to handleparsing and application of proxy configurations. The configuration
is parsed during agent construction. When requests are made,
the
createConnection()
methods on the agents would check whetherthe 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 errorwould 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)
Testing
Most of the diff of this patch are tests for various cases that a proxied client can run into. I also tested it with a production proxy server behind a firewall.
To check how transparent the sockets behave when they are going through a proxy, I also ran the existing http/https tests over a minimal testing proxy server:
So >88% of the existing HTTP/HTTPS use cases work transparently when they go through a proxy. Among the failures most of them are caused by testing proxy server not being transparent since I didn't put a lot of thought into it, or that the tests are expecting specific things (e.g. events, errors) that have to come from a server in the same process. I think this is good enough as an initial implementation and we can continue iterating to make the proxied behavior as transparent as possible.
Some TODOs:
The first commit comes from #58950 - I split it off since it just moved the existing tests for fetch into a new client-proxy directory and made the testing proxy server a bit more versatile, not strictly tied to what this patch tries to implement.
Refs: #57872
Refs: #8381
Refs: #15620