From afadf10f17831b1cbfb2515b68fe45153c0ac92c Mon Sep 17 00:00:00 2001 From: Tom Granot Date: Sat, 7 Nov 2020 22:24:15 +0200 Subject: [PATCH 1/2] Makes sure custom user-agent is propagated down to CONNECT requests when a proxy is in the way, ensuring all outgoing requests bear the correct user agent. --- .../org/asynchttpclient/netty/request/NettyRequestFactory.java | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java index 663ced6ce1..4cfee06cd6 100755 --- a/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java +++ b/client/src/main/java/org/asynchttpclient/netty/request/NettyRequestFactory.java @@ -140,6 +140,7 @@ public NettyRequest newNettyRequest(Request request, boolean performConnectReque if (connect) { // assign proxy-auth as configured on request headers.set(PROXY_AUTHORIZATION, request.getHeaders().getAll(PROXY_AUTHORIZATION)); + headers.set(USER_AGENT, request.getHeaders().getAll(USER_AGENT)); } else { // assign headers as configured on request From 2835f69bb281df2be8dcbcd81403c4fb0800834f Mon Sep 17 00:00:00 2001 From: Tom Granot Date: Sat, 7 Nov 2020 22:45:10 +0200 Subject: [PATCH 2/2] Add a test for custom user agent with proxy --- .../BasicHttpProxyToHttpsTest.java | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/client/src/test/java/org/asynchttpclient/BasicHttpProxyToHttpsTest.java b/client/src/test/java/org/asynchttpclient/BasicHttpProxyToHttpsTest.java index a1919f6f4a..260395674a 100644 --- a/client/src/test/java/org/asynchttpclient/BasicHttpProxyToHttpsTest.java +++ b/client/src/test/java/org/asynchttpclient/BasicHttpProxyToHttpsTest.java @@ -31,18 +31,20 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; -import static io.netty.handler.codec.http.HttpHeaderNames.PROXY_AUTHENTICATE; -import static io.netty.handler.codec.http.HttpHeaderNames.PROXY_AUTHORIZATION; +import static io.netty.handler.codec.http.HttpHeaderNames.*; import static org.asynchttpclient.Dsl.*; import static org.asynchttpclient.test.TestUtils.addHttpConnector; import static org.asynchttpclient.test.TestUtils.addHttpsConnector; +import static org.asynchttpclient.config.AsyncHttpClientConfigDefaults.*; /** - * Test that validates that when having an HTTP proxy and trying to access an HTTPS through the proxy the proxy credentials should be passed during the CONNECT request. + * Test that validates that when having an HTTP proxy and trying to access an HTTPS + * through the proxy the proxy credentials and a custom user-agent (if set) should be passed during the CONNECT request. */ public class BasicHttpProxyToHttpsTest { private static final Logger LOGGER = LoggerFactory.getLogger(BasicHttpProxyToHttpsTest.class); + private static final String CUSTOM_USER_AGENT = "custom-user-agent"; private int httpPort; private int proxyPort; @@ -66,13 +68,24 @@ public void setUpGlobal() throws Exception { ConnectHandler connectHandler = new ConnectHandler() { @Override + // This proxy receives a CONNECT request from the client before making the real request for the target host. protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) { + + // If the userAgent of the CONNECT request is the same as the default userAgent, + // then the custom userAgent was not properly propagated and the test should fail. + String userAgent = request.getHeader(USER_AGENT.toString()); + if(userAgent.equals(defaultUserAgent())) { + return false; + } + + // If the authentication failed, the test should also fail. String authorization = request.getHeader(PROXY_AUTHORIZATION.toString()); if (authorization == null) { response.setStatus(HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED); response.setHeader(PROXY_AUTHENTICATE.toString(), "Basic realm=\"Fake Realm\""); return false; - } else if (authorization.equals("Basic am9obmRvZTpwYXNz")) { + } + else if (authorization.equals("Basic am9obmRvZTpwYXNz")) { return true; } response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); @@ -98,6 +111,7 @@ public void nonPreemptiveProxyAuthWithHttpsTarget() throws IOException, Interrup String targetUrl = "https://localhost:" + httpPort + "/foo/bar"; Request request = get(targetUrl) .setProxyServer(proxyServer("127.0.0.1", proxyPort).setRealm(realm(AuthScheme.BASIC, "johndoe", "pass"))) + .setHeader("user-agent", CUSTOM_USER_AGENT) // .setRealm(realm(AuthScheme.BASIC, "user", "passwd")) .build(); Future responseFuture = client.executeRequest(request); @@ -107,4 +121,4 @@ public void nonPreemptiveProxyAuthWithHttpsTarget() throws IOException, Interrup Assert.assertEquals("/foo/bar", response.getHeader("X-pathInfo")); } } -} \ No newline at end of file +}