# WebClient Proxy Configuration Leading to Connection Timeout
## Summary
A timeout error occurred when using Spring WebClient configured with an HTTP proxy. Despite correctly setting the proxy host and port, requests consistently failed with connection timeouts. The issue arose in a Java application leveraging Project Reactor's HttpClient.
## Root Cause
- **Proxy configuration wasn't applied to connection attempts**: The `HttpClient` `proxy()` configuration was syntactically correct but was overridden by subsequent network-level timeouts or misconfigured connection pooling.
- **Conflicting timeout handlers**: The manual injection of `ReadTimeoutHandler` and `WriteTimeoutHandler` via `doOnConnected()` clashed with the `CONNECT_TIMEOUT_MILLIS` channel option, destabilizing the connection handshake.
- **Proxy authentication mismatch**: The proxy server required authentication credentials not provided in the configuration, though the original code snippet omitted this detail.
## Why This Happens in Real Systems
- Complex configuration layers in reactive pipelines make it easy to override settings unintentionally.
- Timeout values for proxy connection, read, and write operations often require fine-tuning under network constraints.
- Proxies in corporate environments frequently demand explicit authentication, which developers might overlook when testing locally.
- Connection pooling parameters (like `maxIdleTime`) may prematurely terminate connections before proxy negotiation completes.
## Real-World Impact
- Service outages for upstream/downstream dependencies due to cascading request failures.
- Degraded user experience from delayed or missing API responses.
- Increased operational load due to false-positive incident alerts triggered by timeout exceptions.
- Resource leakage from stale connections stuck in half-open states due to improper timeout handling.
## Example or Code (if applicable)
Original problematic configuration:
```java
ConnectionProvider provider = ConnectionProvider.builder("fixed")
.maxConnections(maxTotalConnections)
.pendingAcquireTimeout(Duration.ofMillis(connectTimeout))
.maxIdleTime(Duration.ofMillis(connectionRequestTimeout))
.build();
HttpClient httpClient = HttpClient.create(provider)
.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP)
.host("myhost")
.port(Integer.parseInt("123")))
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout)
.doOnConnected(conn -> conn
.addHandlerLast(new ReadTimeoutHandler(connectionRequestTimeout))
.addHandlerLast(new WriteTimeoutHandler(connectionRequestTimeout)));
How Senior Engineers Fix It
- Explicit proxy authentication: Add
.username()/.password()in the proxy configuration if the proxy requires auth. - Separate proxy connection timeout: Dedicate a timeout specifically for proxy negotiation using
.connectTimeoutMillis()in the proxy builder. - Eliminate manual handlers: Replace custom Netty handlers with the native
responseTimeout()andconnectTimeout()methods ofHttpClient. - Validate connection pool settings: Adjust
maxIdleTimeandpendingAcquireTimeoutto align with proxy latency characteristics. - Enable full wire-tracing: Use advanced logging (
-Dreactor.netty.http.client.Wiretap=true) to inspect proxy handshake bytes.
Corrected configuration:
HttpClient httpClient = HttpClient.create()
.proxy(proxy -> proxy.type(ProxyProvider.Proxy.HTTP)
.host("myhost")
.port(123)
.connectTimeoutMillis(2000) // Explicit proxy connect timeout
.username("user") // When auth is required
.password("pass"))
.responseTimeout(Duration.ofMillis(connectionRequestTimeout)) // Replaces Read/Write handlers
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout);
Why Juniors Miss It
- Focus on syntax over semantics: Correctly structured code (no syntax errors) masks misconfigured timeouts/networking logic.
- Incomplete logging: Lack of reactor-netty wire-level logging prevents diagnosing proxy handshake failures.
- Environment blindness: Not accounting for differences between local development (no proxy auth) and production (strict proxy policies).
- Over-reliance on examples: Copy-pasting configuration snippets without understanding Netty handler chaining order.
- Timeouts treated as afterthoughts: Viewing timeouts as magic numbers rather than critical networking constraints.