Un proxy SOCKS è un tipo di proxy che fa da intermediario tra un client e un server. A differenza di un proxy HTTP, lavora a un livello più basso: per questo motivo può inoltrare qualsiasi tipo di traffico TCP e UDP. Visto che HTTP è un protocollo che si basa su TCP, possiamo usare un proxy SOCKS per inoltrare anche le richieste HTTP. In questo post viene spiegato come usare fetch con un proxy SOCKS.

Il capitolo 5 del libro è interamente dedicato a TCP e spiega come realizzare una chat di gruppo partendo da zero e utilizzando solo TCP.

Implementazione veloce con il modulo fetch-socks

Possiamo far passare tutte le richieste HTTP di fetch attraverso un proxy SOCKS usando il modulo fetch-socks.

npm install fetch-socks

Dopo averlo installato e importato nel codice, è sufficiente creare un oggetto socksDispatcher con la configurazione del proxy SOCKS usato per poi passarlo come opzione a fetch.

import { socksDispatcher } from "fetch-socks";

const dispatcher = socksDispatcher({
  type: 5,
  host: "proxy.example.org",
  port: 9191,
  //userId: "username",
  //password: "password",
});

const res = await fetch("https://icanhazip.com/", { dispatcher });
const body = await res.text();
console.log(body);

Eseguendo questo codice verrà stampato l’IP del proxy SOCKS a cui siete collegati e non quello del PC da cui è eseguito.

È possibile verificare il corretto funzionamento aprendo l’URL https://icanhazip.com dal browser che mostrerà l’IP reale (a patto che anche il browser non stia usando lo stesso proxy).

Proxy SOCKS globale e configurabile

Se vogliamo usare il proxy per tutte le richieste HTTP in uscita senza dover passare ogni volta l’opzione, possiamo usare l’istruzione global[Symbol.for("undici.globalDispatcher.1")] = dispatcher.

Nel codice di seguito viene mostrato come prendere le informazioni del proxy attraverso delle variabili d’ambiente e come configurare il proxy per tutte le richieste di fetch.

import { env } from "process";
import { socksDispatcher } from "fetch-socks";

if (env.SOCKS_PROXY_HOST && env.SOCKS_PROXY_PORT) {
  console.log(
    `Using SOCKS5 proxy ${env.SOCKS_PROXY_HOST}:${env.SOCKS_PROXY_PORT}`
  );
  const dispatcher = socksDispatcher({
    type: 5,
    host: env.SOCKS_PROXY_HOST,
    port: parseInt(env.SOCKS_PROXY_PORT),
    //userId: "username",
    //password: "password",
  });

  // il proxy SOCKS è configurato per tutte le richieste
  global[Symbol.for("undici.globalDispatcher.1")] = dispatcher;
}

const res = await fetch("https://icanhazip.com/");
const body = await res.text();
console.log(body);

Salvando il codice in un file proxy-test.mjs è possibile eseguirlo lanciando il comando:

SOCKS_PROXY_HOST=proxy.example.org SOCKS_PROXY_PORT=9191 node proxy-test.mjs

I valori di SOCKS_PROXY_HOST e SOCKS_PROXY_PORT sono quelli relativi al proxy usato. Per accedere a un proxy protetto da credenziali sarà sufficiente aggiungere i campi userId e password, eventualmente passando anche questi attraverso delle variabili d’ambiente.

Altre funzionalità disponibili

La libreria fetch-socks permette anche di avere setup più complessi di quello mostrato sopra. Infatti, permette anche di usare proxy chains (catene di proxy) e tunnel HTTP. Tutte le sue funzionalità sono però ereditate da un altro modulo.

Implementazione personalizzata con il modulo socks

Il modulo fetch-socks è solo un wrapper che al suo interno utilizza undici come agente HTTP e socks per il collegamento. È infatti quest’ultima libreria che fornisce tutte le funzionalità legate al protocollo SOCKS.

Per usarla direttamente senza passare da fetch-socks è sufficiente copiare e adattare il codice sorgente del modulo adattandolo alle proprie esigenze.

Utilizzandolo evitiamo di scrivere delle righe di codice in più ma si tratta di righe che difficilmente verranno modificate spesso. Prima di aggiungere una dipendenza è sempre meglio valutarne pro e contro.