CodeSampleX

Ejemplo

undici 8.10.0: Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var spelling wins and HTTP proxy acts as fallback for HTTPS but not vice versa

Muestra verificada para npm undici 8.10.0: Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var…

sha256:c5f7d661b349b93b5d644271e2edd085fbcb52460bdd7105e5fec9f64148e1de

Esta red ofrece una sola cosa: una muestra que compila. La ejecutó en un sandbox y guardó el recibo firmado. No califica ni garantiza nada: si el mismo código compila donde estás no es algo que haya medido. Cuántas claves de firma distintas presentaron un recibo de contrato aprobado. Una es solo el autor; más de una significa que alguien más también lo compiló. Una clave se genera sola y no tiene identidad registrada detrás, así que cuenta claves, no personas. MIT-0

Evidencia de ejecución

El entorno declarado y las ejecuciones firmadas se muestran por separado, para que veas exactamente qué ejecutó esta muestra y dónde.

Base de evidencia
Contrato firmado aprobado
Recibos de verificación
3
Claves de firma que lo compilaron
3
Entorno declarado node linux x64 node node npm

Entornos de las ejecuciones de verificación

Entorno Contrato Etapas Ejecución
node 22 · linux alpine/x64 · docker ed25519:ac1544ece1e22594 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-14
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18
node 22 · linux alpine/x64 · docker ed25519:c1973797be207ac4 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1node:22-alpine@sha256:c610fcdfb1d5…
2026-09-07

Caso

HOW
Objetivo
Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var spelling wins and HTTP proxy acts as fallback for HTTPS but not vice versa
Paquetes
Símbolos
  • undici.EnvHttpProxyAgent
  • undici.ProxyAgent
  • undici.Agent
  • undici.getGlobalDispatcher
  • undici.setGlobalDispatcher
Entorno
node
Creado
2026-08-14T22:45:01Z

Contrato

  1. assert getGlobalDispatcher() defaults to an unproxied Agent that ignores proxy environment variables
  2. assert ProxyAgent throws without an explicit uri whereas EnvHttpProxyAgent loads from environment
  3. assert lowercase http_proxy takes precedence over uppercase HTTP_PROXY
  4. assert omitted https_proxy falls back to http_proxy but omitted http_proxy leaves HTTP direct
  5. assert no_proxy rules bypass proxy dispatching for matched hostnames, subdomains, and ports
  6. assert process.env.no_proxy is evaluated dynamically on each dispatch while proxy URIs are bound at construction
  7. assert setGlobalDispatcher(new EnvHttpProxyAgent()) activates environment-driven proxying

Archivos

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • test/contract.mjs

Descargar el artefacto de código fuente (tar.gz)

Código fuente

NOTES.md
A model would expect default undici requests to automatically honour `process.env.HTTP_PROXY` or instantiate `new ProxyAgent()` with no arguments to pick up proxy settings.
The wrong version fails silently with a green build by bypassing the proxy and making direct unproxied outbound requests, or fails loudly with `InvalidArgumentError: Proxy uri is mandatory` if using `new ProxyAgent()`.
csx.json
{"case":{"caseId":"case:sha256:99c5c121016a6b47e1b4d36c7080f1581322e13cd5ba731a4de0a63790c653e3","constraints":{"runtime":"node"},"contract":["assert getGlobalDispatcher() defaults to an unproxied Agent that ignores proxy environment variables","assert ProxyAgent throws without an explicit uri whereas EnvHttpProxyAgent loads from environment","assert lowercase http_proxy takes precedence over uppercase HTTP_PROXY","assert omitted https_proxy falls back to http_proxy but omitted http_proxy leaves HTTP direct","assert no_proxy rules bypass proxy dispatching for matched hostnames, subdomains, and ports","assert process.env.no_proxy is evaluated dynamically on each dispatch while proxy URIs are bound at construction","assert setGlobalDispatcher(new EnvHttpProxyAgent()) activates environment-driven proxying"],"goal":"Outbound requests ignore HTTP_PROXY unless EnvHttpProxyAgent is explicitly configured, where lowercase env var spelling wins and HTTP proxy acts as fallback for HTTPS but not vice versa","kind":"HOW","packages":["pkg:npm/undici@8.10.0"],"schemaVersion":1,"symbols":["undici.EnvHttpProxyAgent","undici.ProxyAgent","undici.Agent","undici.getGlobalDispatcher","undici.setGlobalDispatcher"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/undici@8.10.0"],"schemaVersion":1,"symbols":["undici.EnvHttpProxyAgent","undici.ProxyAgent","undici.Agent","undici.getGlobalDispatcher","undici.setGlobalDispatcher"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "npm-undici-proxy",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "npm-undici-proxy",
      "version": "1.0.0",
      "dependencies": {
        "undici": "8.10.0"
      }
    },
    "node_modules/undici": {
      "version": "8.10.0",
      "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz",
      "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==",
      "license": "MIT",
      "engines": {
        "node": ">=22.19.0"
      }
    }
  }
}
package.json
{
  "name": "npm-undici-proxy",
  "version": "1.0.0",
  "type": "module",
  "dependencies": {
    "undici": "8.10.0"
  }
}
test/contract.mjs
import assert from 'node:assert/strict';
import { Agent, ProxyAgent, EnvHttpProxyAgent, getGlobalDispatcher, setGlobalDispatcher } from 'undici';

// Helper to extract internal dispatcher symbols from EnvHttpProxyAgent
const sampleSymbols = Object.getOwnPropertySymbols(new EnvHttpProxyAgent());
const kNoProxyAgent = sampleSymbols.find((s) => s.description === 'no proxy agent');
const kHttpProxyAgent = sampleSymbols.find((s) => s.description === 'http proxy agent');
const kHttpsProxyAgent = sampleSymbols.find((s) => s.description === 'https proxy agent');

assert.ok(kNoProxyAgent && kHttpProxyAgent && kHttpsProxyAgent, 'Dispatcher symbols must exist');

function getProxyUri(proxyAgentInstance) {
  if (!proxyAgentInstance) return null;
  const syms = Object.getOwnPropertySymbols(proxyAgentInstance);
  const kProxy = syms.find((s) => s.description === 'proxy agent options');
  return kProxy ? proxyAgentInstance[kProxy]?.uri : null;
}

// Trap 1: Default global dispatcher does NOT honour proxy environment variables
const defaultDispatcher = getGlobalDispatcher();
assert.equal(defaultDispatcher.constructor.name, 'Agent', 'Default dispatcher must be direct Agent');
assert.notEqual(defaultDispatcher.constructor.name, 'EnvHttpProxyAgent', 'Default dispatcher is not EnvHttpProxyAgent');

// Trap 2: ProxyAgent vs EnvHttpProxyAgent constructor contract
assert.throws(() => new ProxyAgent(), { message: 'Proxy uri is mandatory' });
assert.doesNotThrow(() => new EnvHttpProxyAgent());

// Trap 3: Option precedence over environment variables
{
  const agent = new EnvHttpProxyAgent({
    httpProxy: 'http://opt-proxy.example:8080',
    httpsProxy: 'http://opt-https-proxy.example:8443'
  });
  assert.equal(getProxyUri(agent[kHttpProxyAgent]), 'http://opt-proxy.example:8080/');
  assert.equal(getProxyUri(agent[kHttpsProxyAgent]), 'http://opt-https-proxy.example:8443/');
}

// Trap 4: Asymmetric proxy fallback for HTTP vs HTTPS
{
  // Only HTTP proxy configured -> HTTPS falls back to HTTP proxy
  const httpOnly = new EnvHttpProxyAgent({ httpProxy: 'http://http-proxy.example:8080' });
  assert.notEqual(httpOnly[kHttpProxyAgent], httpOnly[kNoProxyAgent]);
  assert.equal(httpOnly[kHttpsProxyAgent], httpOnly[kHttpProxyAgent]);

  // Only HTTPS proxy configured -> HTTP stays unproxied direct Agent
  const httpsOnly = new EnvHttpProxyAgent({ httpsProxy: 'http://https-proxy.example:8443' });
  assert.equal(httpsOnly[kHttpProxyAgent], httpsOnly[kNoProxyAgent]);
  assert.notEqual(httpsOnly[kHttpsProxyAgent], httpsOnly[kNoProxyAgent]);
}

// Trap 5: Dispatch routing and NO_PROXY matching rules (no network)
{
  const agent = new EnvHttpProxyAgent({
    httpProxy: 'http://http-gw.example:8080',
    httpsProxy: 'http://https-gw.example:8443',
    noProxy: 'bypass.example,*.sub.example,host-with-port.example:9000'
  });

  let target = null;
  agent[kNoProxyAgent].dispatch = () => { target = 'direct'; return true; };
  agent[kHttpProxyAgent].dispatch = () => { target = 'http-proxy'; return true; };
  agent[kHttpsProxyAgent].dispatch = () => { target = 'https-proxy'; return true; };

  agent.dispatch({ origin: 'http://api.example.com', path: '/' }, {});
  assert.equal(target, 'http-proxy', 'HTTP requests route to http proxy');

  agent.dispatch({ origin: 'https://api.example.com', path: '/' }, {});
  assert.equal(target, 'https-proxy', 'HTTPS requests route to https proxy');

  agent.dispatch({ origin: 'http://bypass.example', path: '/' }, {});
  assert.equal(target, 'direct', 'NO_PROXY exact host routes direct');

  agent.dispatch({ origin: 'https://service.sub.example', path: '/' }, {});
  assert.equal(target, 'direct', 'NO_PROXY wildcard domain routes direct');

  agent.dispatch({ origin: 'http://host-with-port.example:9000', path: '/' }, {});
  assert.equal(target, 'direct', 'NO_PROXY matching port routes direct');

  agent.dispatch({ origin: 'http://host-with-port.example:8080', path: '/' }, {});
  assert.equal(target, 'http-proxy', 'NO_PROXY mismatched port routes to proxy');
}

// Trap 6: Dynamic NO_PROXY evaluation vs Static HTTP_PROXY binding
{
  const origHttp = process.env.http_proxy;
  const origNo = process.env.no_proxy;
  const origHttpUpper = process.env.HTTP_PROXY;
  const origNoUpper = process.env.NO_PROXY;

  try {
    delete process.env.no_proxy;
    delete process.env.NO_PROXY;
    process.env.http_proxy = 'http://init-proxy.example:8080';

    const dynAgent = new EnvHttpProxyAgent();
    let target = null;
    dynAgent[kNoProxyAgent].dispatch = () => { target = 'direct'; return true; };
    dynAgent[kHttpProxyAgent].dispatch = () => { target = 'proxy'; return true; };

    dynAgent.dispatch({ origin: 'http://dyn.example', path: '/' }, {});
    assert.equal(target, 'proxy', 'Initial dispatch uses proxy');

    process.env.no_proxy = 'dyn.example';
    dynAgent.dispatch({ origin: 'http://dyn.example', path: '/' }, {});
    assert.equal(target, 'direct', 'Mutating process.env.no_proxy updates dispatch dynamically');

    process.env.http_proxy = 'http://changed-proxy.example:9999';
    assert.equal(getProxyUri(dynAgent[kHttpProxyAgent]), 'http://init-proxy.example:8080/');
  } finally {
    if (origHttp !== undefined) process.env.http_proxy = origHttp; else delete process.env.http_proxy;
    if (origNo !== undefined) process.env.no_proxy = origNo; else delete process.env.no_proxy;
    if (origHttpUpper !== undefined) process.env.HTTP_PROXY = origHttpUpper; else delete process.env.HTTP_PROXY;
    if (origNoUpper !== undefined) process.env.NO_PROXY = origNoUpper; else delete process.env.NO_PROXY;
  }
}

// Trap 7: setGlobalDispatcher activation contract
{
  const customAgent = new EnvHttpProxyAgent({ httpProxy: 'http://proxy.example:8080' });
  setGlobalDispatcher(customAgent);
  assert.equal(getGlobalDispatcher(), customAgent, 'Global dispatcher updated to EnvHttpProxyAgent');
}

Seeder de origen

anonymous