Skip to content

Commit 576decf

Browse files
authored
Proxy CONNECT custom headers (#1774)
* proxy connect custom headers * npe fix * refactor * formatting fix + version fix * npe fix * test added
1 parent d2fc371 commit 576decf

File tree

3 files changed

+148
-2
lines changed

3 files changed

+148
-2
lines changed

Diff for: client/src/main/java/org/asynchttpclient/netty/request/NettyRequestSender.java

+6
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,12 @@ private <T> ListenableFuture<T> sendRequestWithNewChannel(Request request,
275275

276276
// some headers are only set when performing the first request
277277
HttpHeaders headers = future.getNettyRequest().getHttpRequest().headers();
278+
if(proxy != null && proxy.getCustomHeaders() != null ) {
279+
HttpHeaders customHeaders = proxy.getCustomHeaders().apply(request);
280+
if(customHeaders != null) {
281+
headers.add(customHeaders);
282+
}
283+
}
278284
Realm realm = future.getRealm();
279285
Realm proxyRealm = future.getProxyRealm();
280286
requestFactory.addAuthorizationHeader(headers, perConnectionAuthorizationHeader(request, proxy, realm));

Diff for: client/src/main/java/org/asynchttpclient/proxy/ProxyServer.java

+23-2
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@
1616
*/
1717
package org.asynchttpclient.proxy;
1818

19+
import io.netty.handler.codec.http.HttpHeaders;
20+
1921
import org.asynchttpclient.Realm;
22+
import org.asynchttpclient.Request;
2023

2124
import java.util.ArrayList;
2225
import java.util.Collections;
2326
import java.util.List;
27+
import java.util.function.Function;
2428

2529
import static org.asynchttpclient.util.Assertions.assertNotNull;
2630
import static org.asynchttpclient.util.MiscUtils.isNonEmpty;
@@ -36,15 +40,22 @@ public class ProxyServer {
3640
private final Realm realm;
3741
private final List<String> nonProxyHosts;
3842
private final ProxyType proxyType;
43+
private final Function<Request, HttpHeaders> customHeaders;
3944

4045
public ProxyServer(String host, int port, int securedPort, Realm realm, List<String> nonProxyHosts,
41-
ProxyType proxyType) {
46+
ProxyType proxyType, Function<Request, HttpHeaders> customHeaders) {
4247
this.host = host;
4348
this.port = port;
4449
this.securedPort = securedPort;
4550
this.realm = realm;
4651
this.nonProxyHosts = nonProxyHosts;
4752
this.proxyType = proxyType;
53+
this.customHeaders = customHeaders;
54+
}
55+
56+
public ProxyServer(String host, int port, int securedPort, Realm realm, List<String> nonProxyHosts,
57+
ProxyType proxyType) {
58+
this(host, port, securedPort, realm, nonProxyHosts, proxyType, null);
4859
}
4960

5061
public String getHost() {
@@ -71,6 +82,10 @@ public ProxyType getProxyType() {
7182
return proxyType;
7283
}
7384

85+
public Function<Request, HttpHeaders> getCustomHeaders() {
86+
return customHeaders;
87+
}
88+
7489
/**
7590
* Checks whether proxy should be used according to nonProxyHosts settings of
7691
* it, or we want to go directly to target host. If <code>null</code> proxy is
@@ -118,6 +133,7 @@ public static class Builder {
118133
private Realm realm;
119134
private List<String> nonProxyHosts;
120135
private ProxyType proxyType;
136+
private Function<Request, HttpHeaders> customHeaders;
121137

122138
public Builder(String host, int port) {
123139
this.host = host;
@@ -157,11 +173,16 @@ public Builder setProxyType(ProxyType proxyType) {
157173
return this;
158174
}
159175

176+
public Builder setCustomHeaders(Function<Request, HttpHeaders> customHeaders) {
177+
this.customHeaders = customHeaders;
178+
return this;
179+
}
180+
160181
public ProxyServer build() {
161182
List<String> nonProxyHosts = this.nonProxyHosts != null ? Collections.unmodifiableList(this.nonProxyHosts)
162183
: Collections.emptyList();
163184
ProxyType proxyType = this.proxyType != null ? this.proxyType : ProxyType.HTTP;
164-
return new ProxyServer(host, port, securedPort, realm, nonProxyHosts, proxyType);
185+
return new ProxyServer(host, port, securedPort, realm, nonProxyHosts, proxyType, customHeaders);
165186
}
166187
}
167188
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright (c) 2010-2012 Sonatype, Inc. All rights reserved.
3+
*
4+
* This program is licensed to you under the Apache License Version 2.0,
5+
* and you may not use this file except in compliance with the Apache License Version 2.0.
6+
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
7+
*
8+
* Unless required by applicable law or agreed to in writing,
9+
* software distributed under the Apache License Version 2.0 is distributed on an
10+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
12+
*/
13+
package org.asynchttpclient.proxy;
14+
15+
import io.netty.handler.codec.http.DefaultHttpHeaders;
16+
import org.asynchttpclient.AbstractBasicTest;
17+
import org.asynchttpclient.AsyncHttpClient;
18+
import org.asynchttpclient.AsyncHttpClientConfig;
19+
import org.asynchttpclient.Response;
20+
import org.asynchttpclient.request.body.generator.ByteArrayBodyGenerator;
21+
import org.asynchttpclient.test.EchoHandler;
22+
import org.asynchttpclient.util.HttpConstants;
23+
import org.eclipse.jetty.proxy.ConnectHandler;
24+
import org.eclipse.jetty.server.Request;
25+
import org.eclipse.jetty.server.Server;
26+
import org.eclipse.jetty.server.ServerConnector;
27+
import org.eclipse.jetty.server.handler.AbstractHandler;
28+
import org.testng.annotations.AfterClass;
29+
import org.testng.annotations.BeforeClass;
30+
import org.testng.annotations.Test;
31+
32+
import javax.servlet.ServletException;
33+
import javax.servlet.http.HttpServletRequest;
34+
import javax.servlet.http.HttpServletResponse;
35+
import java.io.IOException;
36+
37+
import static org.asynchttpclient.Dsl.*;
38+
import static org.asynchttpclient.test.TestUtils.*;
39+
import static org.testng.Assert.assertEquals;
40+
41+
/**
42+
* Proxy usage tests.
43+
*/
44+
public class CustomHeaderProxyTest extends AbstractBasicTest {
45+
46+
private Server server2;
47+
48+
private final String customHeaderName = "Custom-Header";
49+
private final String customHeaderValue = "Custom-Value";
50+
51+
public AbstractHandler configureHandler() throws Exception {
52+
return new ProxyHandler(customHeaderName, customHeaderValue);
53+
}
54+
55+
@BeforeClass(alwaysRun = true)
56+
public void setUpGlobal() throws Exception {
57+
server = new Server();
58+
ServerConnector connector = addHttpConnector(server);
59+
server.setHandler(configureHandler());
60+
server.start();
61+
port1 = connector.getLocalPort();
62+
63+
server2 = new Server();
64+
ServerConnector connector2 = addHttpsConnector(server2);
65+
server2.setHandler(new EchoHandler());
66+
server2.start();
67+
port2 = connector2.getLocalPort();
68+
69+
logger.info("Local HTTP server started successfully");
70+
}
71+
72+
@AfterClass(alwaysRun = true)
73+
public void tearDownGlobal() throws Exception {
74+
server.stop();
75+
server2.stop();
76+
}
77+
78+
@Test
79+
public void testHttpProxy() throws Exception {
80+
AsyncHttpClientConfig config = config()
81+
.setFollowRedirect(true)
82+
.setProxyServer(
83+
proxyServer("localhost", port1)
84+
.setCustomHeaders((req) -> new DefaultHttpHeaders().add(customHeaderName, customHeaderValue))
85+
.build()
86+
)
87+
.setUseInsecureTrustManager(true)
88+
.build();
89+
try (AsyncHttpClient asyncHttpClient = asyncHttpClient(config)) {
90+
Response r = asyncHttpClient.executeRequest(post(getTargetUrl2()).setBody(new ByteArrayBodyGenerator(LARGE_IMAGE_BYTES))).get();
91+
assertEquals(r.getStatusCode(), 200);
92+
}
93+
}
94+
95+
public static class ProxyHandler extends ConnectHandler {
96+
String customHeaderName;
97+
String customHeaderValue;
98+
99+
public ProxyHandler(String customHeaderName, String customHeaderValue) {
100+
this.customHeaderName = customHeaderName;
101+
this.customHeaderValue = customHeaderValue;
102+
}
103+
104+
@Override
105+
public void handle(String s, Request r, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
106+
if (HttpConstants.Methods.CONNECT.equalsIgnoreCase(request.getMethod())) {
107+
if (request.getHeader(customHeaderName).equals(customHeaderValue)) {
108+
response.setStatus(HttpServletResponse.SC_OK);
109+
super.handle(s, r, request, response);
110+
} else {
111+
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
112+
r.setHandled(true);
113+
}
114+
} else {
115+
super.handle(s, r, request, response);
116+
}
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)