I'm trying to configure an HTTP proxy (without CONNECT tunnelling support) for my Spring WebFlux WebClients (based on Netty HttpClient) but without success (403 HTTP status code).
As explained in the documentation:
- https://projectreactor.io/docs/netty/snapshot/reference/http-client.html#_proxy_support
- https://docs.spring.io/projectreactor/reactor-netty/docs/current/reference/html/#_proxy_support
- https://github.com/netty/netty/issues/10475
Netty’s HTTP proxy support always uses CONNECT method in order to establish a tunnel to the specified proxy regardless of the scheme that is used http or https. (More information: Netty enforce HTTP proxy to support HTTP CONNECT method). Some proxies might not support CONNECT method when the scheme is http or might need to be configured in order to support this way of communication. Sometimes this might be the reason for not being able to connect to the proxy. Consider checking the proxy documentation whether it supports or needs an additional configuration in order to support CONNECT method.
I'm using Spring Boot WebFlux 3.5.4 and this is my configuration:
import io.netty.resolver.DefaultAddressResolverGroup;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;
@Data
@Configuration
@ConfigurationProperties(prefix = "proxy")
public class ProxyConfiguration {
private boolean enabled = false;
private HttpProxy http = new HttpProxy();
@Data
public static class HttpProxy {
private String host = "localhost";
private Integer port = 8080;
private String noProxy = "localhost|127.0.0.1";
private HttpProxyAuthentication authentication = new HttpProxyAuthentication();
@Data
public static class HttpProxyAuthentication {
private boolean enabled = false;
private String username;
private String password;
}
}
@Bean
@Primary
public HttpClient httpClient() {
HttpClient httpClient = HttpClient.create().resolver(DefaultAddressResolverGroup.INSTANCE).followRedirect(true);
if (this.isEnabled()) {
httpClient = httpClient.proxy(proxy -> {
ProxyProvider.Builder builder = proxy.type(ProxyProvider.Proxy.HTTP)
.host(this.getHttp().getHost())
.port(this.getHttp().getPort())
.nonProxyHosts(this.getHttp().getNoProxy());
// Add authentication if enabled
if (this.getHttp().getAuthentication().isEnabled() &&
this.getHttp().getAuthentication().getUsername() != null &&
this.getHttp().getAuthentication().getPassword() != null) {
builder.username(this.getHttp().getAuthentication().getUsername())
.password(ignored -> this.getHttp().getAuthentication().getPassword());
}
});
} else {
httpClient = httpClient.proxyWithSystemProperties();
}
return httpClient;
}
}
@Bean
public WebClient eebClient(HttpClient httpClient) {
return WebClient.builder()
.baseUrl(BASE_URL)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
}
What options do I have to bypass this Netty HttpClient limitation and use HTTP proxy without CONNECT tunnelling support? Switching from Netty to Jetty HttpClient or other alternatives is also OK, as long as they are supported by the Spring WebClient interface.