shadowsocks/shadowsocks-rust · error

HTTP/HTTPS outbound proxy requires the `local-http` feature

Error message

HTTP/HTTPS outbound proxy requires the `local-http` feature

What it means

The outbound proxy chain supports HTTP/HTTPS CONNECT proxies only when the crate is compiled with the `local-http` feature. negotiate_hop explicitly rejects Http/Https hop kinds with io::ErrorKind::Unsupported in builds without that feature. The proxy config parsed fine; the capability was compiled out.

Source

Thrown at crates/shadowsocks-service/src/net/outbound/chain.rs:125

    mut stream: OutboundProxyStream,
    hop: &OutboundProxyHop,
    next_target: &Address,
) -> io::Result<OutboundProxyStream> {
    match &hop.kind {
        OutboundProxyKind::Socks5 { auth } => {
            Socks5Negotiator::establish_tcp(&mut stream, next_target.clone(), auth)
                .await
                .map_err(io::Error::other)?;
            Ok(stream)
        }
        #[cfg(feature = "local-http")]
        OutboundProxyKind::Http { auth } | OutboundProxyKind::Https { auth, .. } => {
            let local_addr = stream.local_addr()?;
            let tunnel = HttpConnectClient::establish(stream, next_target, auth).await?;
            Ok(OutboundProxyStream::from_http(local_addr, tunnel))
        }
        #[cfg(not(feature = "local-http"))]
        OutboundProxyKind::Http { .. } | OutboundProxyKind::Https { .. } => Err(io::Error::new(
            io::ErrorKind::Unsupported,
            "HTTP/HTTPS outbound proxy requires the `local-http` feature",
        )),
    }
}

#[cfg(any(feature = "local-http-native-tls", feature = "local-http-rustls"))]
async fn tls_wrap(stream: OutboundProxyStream, sni: &str) -> io::Result<OutboundProxyStream> {
    let local_addr = stream.local_addr()?;
    let tls = tls_connect(stream, sni).await?;
    Ok(OutboundProxyStream::from_tls(local_addr, tls))
}

#[cfg(not(any(feature = "local-http-native-tls", feature = "local-http-rustls")))]
async fn tls_wrap(_stream: OutboundProxyStream, _sni: &str) -> io::Result<OutboundProxyStream> {
    super::tls::tls_unsupported()
}

View on GitHub (pinned to 8eb0f0a65b)

Solutions

  1. Rebuild with the feature: cargo build --features local-http (plus the needed TLS backend)
  2. Change the outbound proxy to a supported kind such as SOCKS5 for the current build
  3. Check the package/distro build flags to confirm which features are enabled

Example fix

// before
cargo build -p shadowsocks-service
// after
cargo build -p shadowsocks-service --features local-http
Defensive patterns

Strategy: validation

Validate before calling

#[cfg(not(feature = "local-http"))]
if matches!(hop.kind, OutboundProxyKind::Http{..} | OutboundProxyKind::Https{..}) {
    return Err(anyhow!("http(s) hop requires building with --features local-http"));
}

Type guard

fn is_http_kind(k: &OutboundProxyKind) -> bool {
    matches!(k, OutboundProxyKind::Http{..} | OutboundProxyKind::Https{..})
}

Try / catch

match negotiate_result {
    Err(e) if e.kind() == io::ErrorKind::Unsupported => eprintln!("rebuild with --features local-http"),
    other => other?,
}

Prevention

When it happens

Trigger: A config lists an `http` or `https` outbound proxy hop while the shadowsocks-service crate was built without `--features local-http`; connect_chain/negotiate_hop reaches that hop and returns Unsupported.

Common situations: Using a default `cargo build`/crates.io install that omits feature flags; feature was renamed or dropped between versions; a distro package built with a minimal feature set.

Related errors


AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09). Data as JSON: /api/errors/3a5137063e579cda. Report an issue: GitHub.