Configurar Proxy en NodeJS

Configurar Proxy en NodeJS


Recientemente, un proyecto requería que NodeJS usara un proxy. Inicialmente pensé que sería una configuración simple, pero resultó ser bastante desafiante.

NodeJS no lee los proxies del sistema por defecto, y como NodeJS tiene múltiples métodos para enviar solicitudes de red, las formas de configurar proxies también varían. Aquí está un resumen.

Prerrequisitos

Asumamos que las direcciones del servidor proxy vienen de variables de entorno, que es la práctica estándar para muchas aplicaciones web. Por conveniencia, las llamaremos HTTP_PROXY y HTTPS_PROXY.

Fetch

Las versiones recientes de NodeJS soportan la interfaz Fetch, que intenta mantener la consistencia con Fetch del navegador, pero si fuera completamente consistente, no habría forma de configurar proxies. Afortunadamente, NodeJS abrió una puerta trasera al agregar un parámetro único dispatcher para modificar los parámetros de solicitud de red, incluyendo servidores proxy.

Podemos usar el paquete undici para configurar el dispatcher:

npm install undici

Configurar proxy para fetch

Usar ProxyAgent para configurarlo:

import { ProxyAgent } from "undici";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  await fetch(url, { dispatcher: new ProxyAgent(proxyUrl) });
}

Configuración global de proxy para fetch

setGlobalDispatcher se usa para configurar proxies para todas las llamadas fetch en el programa NodeJS:

import { setGlobalDispatcher, ProxyAgent } from "undici";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  setGlobalDispatcher(new ProxyAgent(proxyUrl));
}

Para eliminar el proxy, puedes establecer un Agent vacío:

import { Agent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new Agent());

Cómo funciona

setGlobalDispatcher en realidad usa un punto de entrada oculto en NodeJS. El objeto global en NodeJS tiene un Symbol para establecer el dispatcher global:

global[Symbol.for("undici.globalDispatcher.1")] = yourDispatcher;

Esto no parece estar documentado en NodeJS (al menos no pude encontrarlo). Para más detalles, ver: https://github.com/nodejs/node/issues/43187

Qué más puede hacer el dispatcher además de configurar proxies

Si importas Agent en lugar de ProxyAgent, también puedes configurar parámetros de red como keepAlive y timeout:

import { Agent } from "undici";

const res = await fetch("https://example.com", {
  dispatcher: new Agent({
    keepAliveTimeout: 10,
    keepAliveMaxTimeout: 10,
  }),
});
const json = await res.json();
console.log(json);

Para más detalles, consulta la documentación de undici.

Módulos nativos de NodeJS http y https

Como se mencionó anteriormente, NodeJS no tiene métodos incorporados para configurar proxies, por lo que necesitamos desarrollarlos usando http.Agent y https.Agent. Actualmente, los paquetes populares son http-proxy-agent y https-proxy-agent.

Aquí hay un ejemplo usando https-proxy-agent:

import https from "https";
import { HttpsProxyAgent } from "https-proxy-agent";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  const agent = new HttpsProxyAgent(proxyUrl);

  https.get(
    {
      host: "example.com",
      path: "/",
      agent,
    },
    (res) => {
      // Manejar respuesta
    }
  );
}

Axios

Axios es casi el estándar de la industria para JavaScript. Tiene múltiples formas de configurar proxies (en orden de prioridad de menor a mayor):

  1. Variables de entorno: Axios mismo lee las variables de entorno HTTP_PROXY y HTTPS_PROXY

  2. Parámetro proxy:

    import axios from "axios";
    
    axios.get("https://example.com", {
      proxy: {
        host: "127.0.0.1",
        port: 7890,
      },
    });
  3. Parámetros httpAgent y httpsAgent: Puedes pasar los http.Agent y https.Agent incorporados de NodeJS para controlar los parámetros de red.

    Aquí usamos los http-proxy-agent y https-proxy-agent mencionados en la sección anterior:

    import axios from "axios";
    import { HttpsProxyAgent } from "https-proxy-agent";
    import { HttpProxyAgent } from "http-proxy-agent";
    
    const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
    if (proxyUrl) {
      const httpsAgent = new HttpsProxyAgent(proxyUrl);
      const httpAgent = new HttpProxyAgent(proxyUrl);
    
      const instance = axios.create({
        httpAgent,
        httpsAgent,
      });
    }

Para más detalles, consulta la documentación de Axios.

Limitaciones de conexión HTTPS

Descubrí que si configuras un proxy HTTP usando los primeros dos métodos, axios no puede establecer conexiones HTTPS. La razón es que axios no envía el método CONNECT, por lo que no puede establecer conexiones TLS (si no entiendes el método CONNECT, puedes leer este artículo).