SocksClient.createConnection returns a stream connected to the requested destination. For HTTPS, that stream still needs a TLS handshake before it carries the HTTP request.

import { SocksClient } from 'socks';
import tls from 'node:tls';

const targetHost = 'service.example';
const info = await SocksClient.createConnection({
  proxy: { host: 'proxy.example', port: 1080, type: 5 },
  command: 'connect',
  destination: { host: targetHost, port: 443 },
  timeout: 10000,
});
const secure = tls.connect({
  socket: info.socket,
  servername: targetHost,
});
secure.on('error', onTransportError);
secure.on('data', onResponseBytes);
secure.once('secureConnect', () => {
  secure.write(
    `GET /data HTTP/1.1\r\nHost: ${targetHost}\r\nConnection: close\r\n\r\n`
  );
});

The SOCKS5 proxy list supplies candidates for the proxy host and port. onTransportError and onResponseBytes belong to the existing application; the latter needs to parse the HTTP response stream. TLS data chunks are not independently complete HTTP messages.

The servername is the destination's DNS name. Node's low-level tls.connect does not enable SNI by default, and the proxy hostname is not the website's certificate identity.

Keep certificate verification enabled. Supplying the connected socket avoids opening a separate direct connection to the website while preserving its TLS identity.

The SOCKS timeout bounds that negotiation. The later TLS and application exchange still need the caller's intended timeout, cancellation and cleanup policy.

Mikanda mikatakata