Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
313f535
feat: add https server implementation
bliuchak Oct 3, 2025
e6adb19
fix: add onTLSClientError handler
bliuchak Oct 7, 2025
0158e0c
fix: add https edge case tests
bliuchak Oct 7, 2025
44fc388
fix: resolve broken test with raw TCP and TLS
bliuchak Oct 8, 2025
8893eca
fix: skip fixture integrity checks for older node versions
bliuchak Oct 8, 2025
57cb93e
fix: beter dockerfile
bliuchak Oct 8, 2025
9956192
chore: bump minor version
bliuchak Oct 10, 2025
e5a1171
chore: extract https defaults into separate const
bliuchak Oct 10, 2025
8a90e7e
docs: add info about https server and other related stuff
bliuchak Oct 13, 2025
de12161
refactor: rewrite https example to js for consistency
bliuchak Oct 13, 2025
4c036a0
fix: export few user facing interfaces
bliuchak Oct 13, 2025
934693a
fix: add tls overhead bytes into final statistic
bliuchak Oct 21, 2025
597b942
fix: better comment
bliuchak Oct 21, 2025
e6ea53b
fix: log error message on socket only when there are no other handlers
bliuchak Nov 6, 2025
359c959
fix: add tests for tls overhead
bliuchak Nov 13, 2025
63409da
fix: update socks tests
bliuchak Nov 18, 2025
48de780
fix: add dns lookup tests
bliuchak Nov 18, 2025
f3c20ad
fix: make puppeteer great again
bliuchak Nov 19, 2025
e6ad99c
refactor: simplify serverType logic to default on http server
bliuchak Nov 20, 2025
9e3b740
fix: add websocket tls overhead tests
bliuchak Nov 20, 2025
cdaa704
fix: source leak and ports conflict for https edge cases
bliuchak Nov 20, 2025
a887d0c
Merge branch 'master' into feat/https
bliuchak Nov 20, 2025
c7be88a
fix: add tls overhead tests with socks upstream
bliuchak Nov 21, 2025
30079e8
fix: add one more test for websocket + tls overhead
bliuchak Nov 21, 2025
6f1e498
fix: minor cleanup in tests
bliuchak Nov 21, 2025
79fc222
fix: version bump
bliuchak Nov 21, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules
.git
.gitignore
dist
coverage
.nyc_output
*.log
.DS_Store
.idea
.vscode
docs
README.md

120 changes: 118 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ the world's most popular web craling library for Node.js.

The proxy-chain package currently supports HTTP/SOCKS forwarding and HTTP CONNECT tunneling to forward arbitrary protocols such as HTTPS or FTP ([learn more](https://blog.apify.com/tunneling-arbitrary-protocols-over-http-proxy-with-static-ip-address-b3a2222191ff)). The HTTP CONNECT tunneling also supports the SOCKS protocol. Also, proxy-chain only supports the Basic [Proxy-Authorization](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Proxy-Authorization).

## Run a simple HTTP/HTTPS proxy server
## Run a simple HTTP proxy server

```javascript
const ProxyChain = require('proxy-chain');
Expand All @@ -30,7 +30,7 @@ server.listen(() => {
});
```

## Run a HTTP/HTTPS proxy server with credentials and upstream proxy
## Run a HTTP proxy server with credentials and upstream proxy

```javascript
const ProxyChain = require('proxy-chain');
Expand Down Expand Up @@ -110,6 +110,108 @@ server.on('requestFailed', ({ request, error }) => {
});
```

## Run simple HTTPS proxy server

This example demonstrates how to create an HTTPS proxy server with a self-signed certificate.
The HTTPS proxy server works identically to the HTTP version but with TLS encryption.

```javascript
// examples/https_proxy_server.js
const { Server, generateCertificate } = require('proxy-chain');

(async () => {
// Generate a self-signed certificate for development/testing
// In production, you should use a proper certificate from a Certificate Authority
console.log('Generating self-signed certificate...');
const { key, cert } = generateCertificate({
commonName: 'localhost',
validityDays: 365,
organization: 'Development',
});

console.log('Certificate generated successfully!');

// Create an HTTPS proxy server
const server = new Server({
// Main difference between 'http' and 'https' is additional event listening:
//
// http
// -> listen for 'connection' events to track raw TCP sockets
//
// https:
// -> listen for 'securedConnection' events (insted of 'connection') to track only post-TLS-handshake sockets
// -> additionally listen for 'tlsClientError' events to handle TLS handshake errors
//
// Default value is 'http'
serverType: 'https',

// Provide the TLS certificate and private key
httpsOptions: {
key,
cert,
},

// Port where the server will listen
port: 8443,

// Enable verbose logging to see what's happening
verbose: true,

// Optional: Add authentication and upstream proxy configuration
prepareRequestFunction: ({ username, hostname, port }) => {
console.log(`Request to ${hostname}:${port} from user: ${username || 'anonymous'}`);

// Allow the request
return {};
},
});

// Handle failed HTTP/HTTPS requests
server.on('requestFailed', ({ request, error }) => {
console.log(`Request ${request.url} failed`);
console.error(error);
});

// Handle TLS handshake errors
server.on('tlsError', ({ error, socket }) => {
console.error(`TLS error from ${socket.remoteAddress}: ${error.message}`);
});

// Emitted when HTTP/HTTPS connection is closed
server.on('connectionClosed', ({ connectionId, stats }) => {
console.log(`Connection ${connectionId} closed`);
console.dir(stats);
});

// Start the server
await server.listen();

// Handle graceful shutdown
process.on('SIGINT', async () => {
console.log('\nShutting down server...');
await server.close(true);
console.log('Server closed.');
process.exit(0);
});

// Keep the server running
await new Promise(() => { });
})();
```

Run server:

```bash
node examples/https_proxy_server.js
```

Send request via proxy:

```bash
curl --proxy-insecure -x https://localhost:8443 -k https://example.com
```

Note: flag `--proxy-insecure` is used since our certificate is self-signed.
## Use custom HTTP agents for connection pooling

You can provide custom HTTP/HTTPS agents to enable connection pooling and reuse with upstream proxies. This is particularly useful for maintaining sticky IP addresses or reducing connection overhead:
Expand Down Expand Up @@ -156,6 +258,20 @@ SOCKS protocol is supported for versions 4 and 5, specifically: `['socks', 'sock

You can use an `upstreamProxyUrl` like `socks://username:password@proxy.example.com:1080`.

## Emitted Events

The `Server` class emits the following events that you can listen to for monitoring and debugging purposes:

| Event Name | Description | Event Data |
|------------|-------------|------------|
| `connectionClosed` | Emitted when an HTTP/HTTPS connection to the proxy server is closed. Useful for monitoring traffic and cleaning up resources. | `{ connectionId: number, stats: ConnectionStats }` |
| `requestFailed` | Emitted when an HTTP/HTTPS request fails with an unexpected error (not a `RequestError`). Useful for error monitoring and logging. | `{ error: Error, request: http.IncomingMessage }` |
| `tlsError` | Emitted when TLS handshake fails (HTTPS servers only). Useful for monitoring SSL/TLS issues. The server handles the error internally. | `{ error: Error, socket: tls.TLSSocket }` |
| `tunnelConnectResponded` | Emitted when a CONNECT tunnel to an upstream proxy is successfully established. Useful for accessing response headers from the upstream proxy. | `{ proxyChainId: number, response: http.IncomingMessage, socket: net.Socket, head: Buffer, customTag?: unknown }` |
| `tunnelConnectFailed` | Emitted when a CONNECT tunnel to an upstream proxy fails (receives non-200 status code). Useful for monitoring upstream proxy issues. | `{ proxyChainId: number, response: http.IncomingMessage, socket: net.Socket, head: Buffer, customTag?: unknown }` |

All events are optional to handle - the proxy server will function correctly without any event listeners.

## Error status codes

The `502 Bad Gateway` HTTP status code is not comprehensive enough. Therefore, the server may respond with `590-599` instead:
Expand Down
75 changes: 75 additions & 0 deletions examples/https_proxy_server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable no-console */
const { Server, generateCertificate } = require('..');

// This example demonstrates how to create an HTTPS proxy server with a self-signed certificate.
// The HTTPS proxy server works identically to the HTTP version but with TLS encryption.

(async () => {
// Generate a self-signed certificate for development/testing
// In production, you should use a proper certificate from a Certificate Authority
console.log('Generating self-signed certificate...');
const { key, cert } = generateCertificate({
commonName: 'localhost',
validityDays: 365,
organization: 'Development',
});

console.log('Certificate generated successfully!');

// Create an HTTPS proxy server
const server = new Server({
// Use HTTPS instead of HTTP
serverType: 'https',

// Provide the TLS certificate and private key
httpsOptions: {
key,
cert,
},

// Port where the server will listen
port: 8443,

// Enable verbose logging to see what's happening
verbose: true,

// Optional: Add authentication and upstream proxy configuration
prepareRequestFunction: ({ username, hostname, port }) => {
console.log(`Request to ${hostname}:${port} from user: ${username || 'anonymous'}`);

// Allow the request
return {};
},
});

// Handle failed HTTP/HTTPS requests
server.on('requestFailed', ({ request, error }) => {
console.log(`Request ${request.url} failed`);
console.error(error);
});

// Handle TLS handshake errors
server.on('tlsError', ({ error, socket }) => {
console.error(`TLS error from ${socket.remoteAddress}: ${error.message}`);
});

// Emitted when HTTP/HTTPS connection is closed
server.on('connectionClosed', ({ connectionId, stats }) => {
console.log(`Connection ${connectionId} closed`);
console.dir(stats);
});

// Start the server
await server.listen();

// Handle graceful shutdown
process.on('SIGINT', async () => {
console.log('\nShutting down server...');
await server.close(true);
console.log('Server closed.');
process.exit(0);
});

// Keep the server running
await new Promise(() => { });
})();
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "proxy-chain",
"version": "2.6.0",
"version": "2.7.0",
"description": "Node.js implementation of a proxy server (think Squid) with support for SSL, authentication, upstream proxy chaining, and protocol tunneling.",
"main": "dist/index.js",
"keywords": [
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './request_error';
export * from './server';
export * from './utils/redact_url';
export * from './utils/generate_certificate';
export * from './anonymize_proxy';
export * from './tcp_tunnel_tools';

Expand Down
Loading
Loading