Skip to content

Support HTTP_PROXY and NO_PROXY env var #1650

Closed

Description

@shttrstckak

A somewhat standard env var used in proxying applications is the HTTP_PROXY and NO_PROXY variables.

Undici should support automatically setting a global dispatcher if HTTP_PROXY env var is used. Currently Undici supports using setGlobalDispatcher() method to set a specific proxy endpoint.

However there is a sister environment variable to HTTP_PROXY that supports contextually non-proxying domains: NO_PROXY

NO_PROXY requires a list of domain names to be NOT proxied and should make a request normally.

If any of the above cannot be supported, then Undici (and ultimately Node's Fetch-Undici) should allow per-request proxy settings or function hook to allow anyone to develop the same effect as HTTP_PROXY and NO_PROXY env var in their application that uses Undici/Node Fetch.

Thoughts?

Activity

  1. mcollina commented on Sep 14, 2022

    @mcollina
    SponsorMember

    I don't know if we should support HTTP_PROXY. How is widespread the use of HTTP_PROXY in the ecosystem.

  2. shttrstckak commented on Sep 14, 2022

    @shttrstckak
    Author

    The "ecosystem" as in the JS ecosystem or the larger software ecosystem?

    For the latter, HTTP_PROXY is used everywhere.

    For JS it's a quite heavily used I imagine for the same reasons. Popular frameworks tend to implement it.

    Node.http module already supports it out of the box.

    Got, Axios, Node-fetch (npm package) require a module global-agent to utilize. Global-agent does not appear to have any plans to support Undici.

    Popular JS based testing framework Cypress supports it.

    On the other side, browsers tend to support some sort of proxy configuration.

    Overall I think if there is no interest in supporting HTTP_PROXY in Undici, then it would be better to support to being able to implement these env vars. HTTP_PROXY can already be supported easily however NO_PROXY doesn't seem currently possible.

  3. silverwind commented on Sep 15, 2022

    @silverwind
    Contributor

    Use of these environment variables is in very widespread, see number of upvotes on nodejs/node#8381. Many high-security environments restrict outbound internet access via a allowlist of domains configured on the proxy.

    I do maintain a fetch wrapper specifically for the purpose of automatic proxy discovery from environment. The crucial part is that if such proxy discovery is performed, there must be an opt-out option because sometimes one wants to fetch without proxy, irregardless of environment variables.

    Parsing of these env vars is tricky, I recommend https://github.com/Rob--W/proxy-from-env for it.

  4. mcollina commented on Sep 16, 2022

    @mcollina
    SponsorMember

    Then let's do it.

  5. shttrstckak commented on Oct 13, 2022

    @shttrstckak
    Author

    I offer myself as sacrifice and will probably get started this weekend.

    Does anyone have any good starters on developing with this repository? For example, What would be a good place in code to start investigating for this feature?

  6. mcollina commented on Oct 14, 2022

    @mcollina
    SponsorMember

    The check for automatically supporting them should likely be in https://github.com/nodejs/undici/blob/main/lib/global.js.

  7. silverwind commented on Oct 14, 2022

    @silverwind
    Contributor

    We probably should cleanly re-implement https://github.com/Rob--W/proxy-from-env because that module includes support for npm_config_* variables which I think has no place in module that is supposed to only support the standard environment variables. See discussion in https://github.com/Rob--W/proxy-from-env/issues/13.

  8. silverwind commented on Oct 17, 2022

    @silverwind
    Contributor

    Also read https://about.gitlab.com/blog/2021/01/27/we-need-to-talk-no-proxy/. It gives an overview of the different implementations and the last two paragraphs give good advice on how to implement.

    I plan to create a module that exactly implements such a parser.

  9. silverwind commented on Dec 7, 2022

    @silverwind
    Contributor

    Seems like a start, but it's definitely missing some features like wildcard (*) support in no_proxy.

    I think that this feature should be treated as plugin and loaded by node "preload"

    I disagree. Numerous environments support these variables by default, like Deno, Python, Golang and many more. It should of course be considered a breaking change, but I don't see why it should not be the default. Some kind of opt-out would be good to have, but I guess users could always overwrite the env with no_proxy=* I guess.

    Maybe the implementation here could be "inspired" by Deno's 😉.

  10. jaecktec commented on Dec 12, 2022

    @jaecktec

    @sla100 my 2 cents on this line:

     return [`${protocol}:`, new ProxyAgent(uri)];
     ...
     const proxyAgent = proxyAgents[ protocol ];
    

    you can also do http over https, that's why most frameworks specify the https option as optional. Meaning that if you haven't specified https_proxy it would do HTTP over the http-proxy :)

    that's why I propose smth like this:

    const getProxyAgent = (proto: 'http' | 'https') => {
      let agent: ProxyAgent | undefined;
      if (proto === 'http') {
        agent = process.env['https_proxy'] ? new ProxyAgent(process.env['https_proxy']) : undefined;
      }
      if (!agent) {
        agent = process.env['http_proxy'] ? new ProxyAgent(process.env['http_proxy']) : undefined;
      }
      return agent;
    };
    
    const noProxyRules = (process.env['no_proxy'] ?? '').split(',').map(rule => rule.trim());
    
    const defaultDispatcher = getGlobalDispatcher();
    
    setGlobalDispatcher(new class extends Dispatcher {
    
      dispatch(options, handler) {
        if (options.origin) {
          const {
            host,
            protocol,
          } = typeof options.origin === 'string' ? new URL(options.origin) : options.origin;
          if (!noProxyRules.some(rule => rule.startsWith('.') ? host.endsWith(rule) : host === rule)) {
            const proxyAgent = getProxyAgent(protocol);
            if (proxyAgent) {
              proxyAgent.dispatch(options, handler);
            }
          }
        }
        return defaultDispatcher.dispatch(options, handler);
      }
    });
  11. mcollina commented on Dec 13, 2022

    @mcollina
    SponsorMember

    I'd be ok to support:

    import { setGlobalDispatcher, EnvHttpProxyAgent } from 'undici'
    
    setGlobalDispatcher(new EnvHttpProxyAgent())
  12. mcollina commented on Jan 18, 2023

    @mcollina
    SponsorMember

    Here is an additional data point in favor of this: Deno supports it by default.

    https://deno.land/manual@v1.29.4/getting_started/setup_your_environment#environment-variables

    We should check if Python, Ruby and other runtimes supports this by default.

  13. 32 remaining items

  14. metcoder95 commented on Sep 2, 2024

    @metcoder95
    Member

    Since https://github.com/nodejs/undici/releases/tag/v6.14.1 you can use EnvHttpProxyAgent.

    It seems is missed from docs tho...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: help-wantedThis issue/pr is open for contributionsenhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions