# How the PortForwarder Service Enables HTTP Proxy Support in Shadowsocks-Windows

> Discover how the PortForwarder service enables HTTP proxy support in Shadowsocks-Windows by bridging local listeners to Privoxy. Seamlessly add HTTP/HTTPS proxy capabilities without altering the core.

- Repository: [shadowsocks/shadowsocks-windows](https://github.com/shadowsocks/shadowsocks-windows)
- Tags: internals
- Published: 2026-03-05

---

**The PortForwarder service acts as a lightweight TCP bridge that forwards HTTP proxy requests from the local Shadowsocks listener to the bundled Privoxy instance, enabling standard HTTP/HTTPS proxy support without adding HTTP protocol logic to the Shadowsocks core.**

The **PortForwarder** is a specialized service within the `shadowsocks/shadowsocks-windows` client that transforms the SOCKS5 proxy into a fully functional HTTP proxy. While Shadowsocks natively operates as a SOCKS5 proxy, many Windows applications require a standard HTTP proxy interface—this is where the PortForwarder service bridges the gap by transparently forwarding traffic to an embedded Privoxy instance.

## Architecture and Service Integration

The PortForwarder operates as one of several services managed by the main `Listener` class. When the Shadowsocks client starts, the `ShadowsocksController` constructs a list of services that handle different types of incoming traffic.

In [`shadowsocks-csharp/Controller/ShadowsocksController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/ShadowsocksController.cs), the controller initializes `PrivoxyRunner` to launch the bundled Privoxy executable, then injects the PortForwarder with Privoxy's listening port:

```csharp
// shadowsocks-csharp/Controller/ShadowsocksController.cs
List<Listener.IService> services = new List<Listener.IService>
{
    tcpRelay,
    udpRelay,
    _pacServer,
    new PortForwarder(privoxyRunner.RunningPort)   // HTTP proxy bridge
};

```

The `Listener` class (defined in [`shadowsocks-csharp/Controller/Service/Listener.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/Service/Listener.cs)) implements a service dispatcher pattern. It accepts inbound TCP connections on the configured local port (`_config.localPort`) and iterates through the service list, calling each service's `Handle` method until one returns `true`:

```csharp
// shadowsocks-csharp/Controller/Service/Listener.cs
public interface IService
{
    bool Handle(byte[] firstPacket, int length, Socket socket, object state);
    void Stop();
}

```

When `PortForwarder.Handle` detects an HTTP proxy connection attempt, it assumes responsibility for that socket, preventing further processing by the SOCKS5 relay.

## How PortForwarder Processes HTTP Proxy Connections

The PortForwarder implementation in [`shadowsocks-csharp/Controller/Service/PortForwarder.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/Service/PortForwarder.cs) manages the complete lifecycle of HTTP proxy connections through five distinct phases:

### Service Entry Point and Validation

The `Handle` method (lines 18–26) performs initial validation to ensure the socket uses TCP protocol, then immediately spawns a `Handler` instance to manage the connection asynchronously. This non-blocking approach allows the Listener to continue accepting new connections while the PortForwarder manages the data pipeline.

### Establishing the Remote Endpoint

The `Handler` class connects to the Privoxy instance via `WrappedSocket`. In the `Start` method (lines 55–60), it establishes a TCP connection to `127.0.0.1` (or `[::1]` for IPv6) on the port provided during PortForwarder construction—the `privoxyRunner.RunningPort`:

```csharp
// From PortForwarder.cs Start method
// Connects to localhost:PrivoxyPort
remote = new WrappedSocket();
remote.BeginConnect(destEndPoint, ConnectCallback, null);

```

### Handshake and Initial Data Forwarding

Once the remote socket connects, the handler immediately forwards the `firstPacket` (typically an HTTP `CONNECT` request) to Privoxy without modification. The `HandshakeReceive` callback (lines 94–98) uses `BeginSend` to transmit this initial data:

```csharp
// Forward the first packet (HTTP CONNECT) to Privoxy
remote.BeginSend(_firstPacket, 0, _firstPacket.Length, 
                 SocketFlags.None, PipeRemoteSendCallback, null);

```

This transparent forwarding ensures that Privoxy receives the complete HTTP request headers necessary to parse the target destination and establish appropriate tunneling.

### Bidirectional Data Piping

After the handshake completes, two asynchronous read loops manage data flow:

- **Remote → Local**: `PipeRemoteReceiveCallback` (lines 126–174) reads data from Privoxy and writes it back to the client socket
- **Local → Remote**: `PipeConnectionReceiveCallback` (lines 152–170) reads from the client and forwards to Privoxy

Each callback implements EOF detection. When a read operation returns zero bytes, that side of the connection has initiated shutdown. The handler tracks shutdown state through boolean flags (`_remoteShutdown`, `_localShutdown`) to coordinate graceful closure.

### Connection Cleanup

The `CheckClose` method (lines 178–226) monitors both shutdown flags. When both directions have closed, it invokes the `Close` method to dispose the `WrappedSocket` instances and free system resources, preventing file descriptor leaks during high-throughput HTTP proxy usage.

## Why This Design Uses Privoxy

The **PortForwarder service** delegates HTTP protocol handling to **Privoxy** rather than implementing HTTP proxy logic directly for three critical reasons:

- **Separation of concerns**: Shadowsocks core focuses exclusively on encrypted tunneling (SOCKS5/UDP), while Privoxy—a mature, standalone project—handles HTTP parsing, header rewriting, and `CONNECT` method semantics
- **Protocol completeness**: Privoxy supports both HTTP and HTTPS transparent proxying, including PAC (Proxy Auto-Configuration) file serving, which would require significant additional code to implement natively
- **Performance efficiency**: The PortForwarder acts as a zero-copy byte pipe, imposing negligible latency overhead while leveraging Privoxy's optimized HTTP processing

## Practical Implementation Examples

### Using the HTTP Proxy in C# Applications

When Shadowsocks-Windows runs with system proxy enabled, applications can route traffic through the local HTTP proxy endpoint. The PortForwarder automatically bridges this to Privoxy:

```csharp
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

class HttpProxyExample
{
    static async Task Main()
    {
        // Shadowsocks-Windows exposes HTTP proxy on the local port (default 1080)
        // PortForwarder internally forwards this to Privoxy
        var proxy = new WebProxy("http://127.0.0.1:1080");
        var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true };
        var client = new HttpClient(handler);

        // Request flows: Application → PortForwarder → Privoxy → Shadowsocks tunnel
        var response = await client.GetStringAsync("https://httpbin.org/ip");
        Console.WriteLine($"Response via Shadowsocks HTTP proxy: {response}");
    }
}

```

### Manual PortForwarder Instantiation for Testing

Developers can create PortForwarder instances programmatically to test HTTP proxy handling without running the full Shadowsocks client:

```csharp
using System.Net;
using System.Net.Sockets;
using Shadowsocks.Controller;

class PortForwarderTest
{
    static void TestForwarder()
    {
        // Privoxy default port when running standalone
        int privoxyPort = 8118;
        var forwarder = new PortForwarder(privoxyPort);
        
        // Simulate incoming HTTP proxy request
        using (var client = new Socket(AddressFamily.InterNetwork, 
                                       SocketType.Stream, ProtocolType.Tcp))
        {
            client.Connect(IPAddress.Loopback, 1080);
            
            byte[] connectRequest = System.Text.Encoding.ASCII.GetBytes(
                "CONNECT www.example.com:443 HTTP/1.1\r\nHost: www.example.com:443\r\n\r\n"
            );
            
            // PortForwarder.Handle takes ownership of the socket
            bool handled = forwarder.Handle(connectRequest, 
                                           connectRequest.Length, 
                                           client, 
                                           null);
            Console.WriteLine($"Connection handled by PortForwarder: {handled}");
        }
    }
}

```

This low-level access allows unit testing of the connection bridging logic without requiring a full proxy chain.

## Summary

- **PortForwarder** is implemented in [`shadowsocks-csharp/Controller/Service/PortForwarder.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Controller/Service/PortForwarder.cs) as a TCP-only bridge service
- It integrates with the `Listener` service chain via the `IService` interface, specifically handling connections that target the HTTP proxy port
- The service forwards raw TCP streams to **Privoxy** (running on `privoxyRunner.RunningPort`), which handles HTTP `CONNECT` methods and header parsing
- **Bidirectional asynchronous piping** (`PipeRemoteReceiveCallback` and `PipeConnectionReceiveCallback`) maintains high-performance data flow between client and Privoxy
- This architecture allows Shadowsocks-Windows to expose a standard `http://127.0.0.1:<localPort>` endpoint to applications while keeping HTTP protocol complexity out of the core encryption layer

## Frequently Asked Questions

### What is Privoxy and why does Shadowsocks-Windows use it?

**Privoxy** is a non-caching web proxy with advanced filtering capabilities. Shadowsocks-Windows bundles Privoxy to handle HTTP proxy protocol specifics—including `CONNECT` tunneling for HTTPS—without bloating the Shadowsocks core with HTTP parsing logic. According to the source code in `shadowsocks-csharp/Data/privoxy.exe.gz`, the client extracts and launches this dedicated proxy process, then uses PortForwarder to route traffic to it.

### How does PortForwarder differ from the standard TCP relay?

While both handle TCP connections, the **TCP relay** (`tcpRelay` in the service list) implements the SOCKS5 protocol state machine—handling authentication requests, CONNECT commands, and relaying to remote Shadowsocks servers. The **PortForwarder** simply bridges TCP streams to the local Privoxy port without interpreting any protocol; it operates transparently on raw bytes rather than SOCKS5 frames.

### Can I use the HTTP proxy feature without Privoxy running?

No. If Privoxy fails to start or the `privoxyRunner.RunningPort` is unavailable, the PortForwarder cannot establish its remote endpoint. The `Handler` attempts to connect to the configured Privoxy port during the `Start` phase; if this connection fails, the HTTP proxy functionality becomes unavailable, though the SOCKS5 proxy (handled by `tcpRelay`) continues operating normally.

### Which port does the HTTP proxy listen on?

The HTTP proxy listens on the same port as the Shadowsocks local SOCKS5 proxy—defined by `Configuration.localPort` (default **1080**). The PortForwarder distinguishes HTTP proxy traffic from SOCKS5 traffic by its position in the service chain and socket handling characteristics, allowing both protocols to coexist on a single port.