HTTP Proxy на чистой ноде

Forward-прокси без библиотек: HTTP — через http.request, HTTPS — через CONNECT-туннель из двух pipe.

Что это

HTTP-прокси принимает запрос от клиента и перепосылает его на нужный хост. Два режима:

  • HTTP — прокси видит полный запрос (URL, headers, body); может модифицировать.
  • HTTPS — клиент шлёт CONNECT host:443 HTTP/1.1; прокси открывает TCP к таргету и просто перекидывает байты в обе стороны. Заголовки внутри TLS прокси не видит.

API / Пример

const http = require('http');
const net = require('net');

const proxy = http.createServer((req, res) => {
  // Обычный HTTP — пайпим в targeted-сервер
  const { hostname, port, path } = new URL(req.url);
  const upstream = http.request({ hostname, port, path, method: req.method, headers: req.headers });
  req.pipe(upstream);
  upstream.on('response', (upRes) => {
    res.writeHead(upRes.statusCode, upRes.headers);
    upRes.pipe(res);
  });
});

// HTTPS: туннель через CONNECT
proxy.on('connect', (req, clientSocket, head) => {
  const { hostname, port } = new URL('https://' + req.url);
  const upstream = net.connect(port || 443, hostname, () => {
    clientSocket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    if (head.length) upstream.write(head);
    upstream.pipe(clientSocket);
    clientSocket.pipe(upstream);
  });
});

proxy.listen(8080);

Производительность / Подводные камни

  • HTTP всегда CRLF (\r\n) — даже на Linux, где OS EOL = \n. Не использовать os.EOL.
  • Двойной CRLF разделяет заголовки и тело.
  • HTTPS-прокси не видит контент — это просто TCP-туннель; для MITM нужна своя CA и подмена сертификатов.
  • pipe не обрабатывает все события — error/close нужно ловить вручную, иначе утечки сокетов.
  • new URL использует Ada-парсер — быстро и корректно по RFC.
  • socket в connect-обработчике — writable — пишем туда HTTP-ответ как байты.

🎓 Источники

  • 🎓 Node.js HTTP Proxy — ревью примеров кода · 2023-11-29
    • Тезисы: HTTP-прокси через http.request и пайпы; HTTPS — через событие connect и двусторонний TCP-pipe; \r\n (CRLF) — стандарт HTTP независимо от OS; new URL использует Ada; имена из RFC должны быть в библиотеке (предложение константы EOL для http-модуля).
    • Цитата: «HTTP всегда \r\n — независимо от того, какой EOL у операционки.»

См. также