Skip to content

Commit e3d2651

Browse files
committed
Add tunnel exception
Allow handle Exchange
1 parent 996ffbb commit e3d2651

4 files changed

Lines changed: 127 additions & 4 deletions

File tree

httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/H2OverH2TunnelExchangeHandler.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
import org.apache.hc.core5.http.HttpResponse;
4141
import org.apache.hc.core5.http.Method;
4242
import org.apache.hc.core5.http.StreamControl;
43+
import org.apache.hc.core5.http.HttpRequestInterceptor;
4344
import org.apache.hc.core5.http.impl.BasicEntityDetails;
4445
import org.apache.hc.core5.http.message.BasicHttpRequest;
45-
import org.apache.hc.core5.http.message.StatusLine;
4646
import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
4747
import org.apache.hc.core5.http.nio.CapacityChannel;
4848
import org.apache.hc.core5.http.nio.DataStreamChannel;
@@ -69,6 +69,7 @@ final class H2OverH2TunnelExchangeHandler implements AsyncClientExchangeHandler
6969
private final Timeout connectTimeout;
7070
private final boolean secure;
7171
private final TlsStrategy tlsStrategy;
72+
private final HttpRequestInterceptor connectRequestInterceptor;
7273
private final IOEventHandlerFactory protocolStarter;
7374
private final FutureCallback<ProtocolIOSession> callback;
7475

@@ -85,13 +86,15 @@ final class H2OverH2TunnelExchangeHandler implements AsyncClientExchangeHandler
8586
final Timeout connectTimeout,
8687
final boolean secure,
8788
final TlsStrategy tlsStrategy,
89+
final HttpRequestInterceptor connectRequestInterceptor,
8890
final IOEventHandlerFactory protocolStarter,
8991
final FutureCallback<ProtocolIOSession> callback) {
9092
this.physicalSession = physicalSession;
9193
this.targetEndpoint = targetEndpoint;
9294
this.connectTimeout = connectTimeout;
9395
this.secure = secure;
9496
this.tlsStrategy = tlsStrategy;
97+
this.connectRequestInterceptor = connectRequestInterceptor;
9598
this.protocolStarter = protocolStarter;
9699
this.callback = callback;
97100
this.done = new AtomicBoolean(false);
@@ -123,6 +126,9 @@ public void cancel() {
123126
public void produceRequest(final RequestChannel requestChannel, final HttpContext context) throws HttpException, IOException {
124127
final HttpRequest connectRequest = new BasicHttpRequest(Method.CONNECT.name(), (String) null);
125128
connectRequest.setAuthority(new URIAuthority(targetEndpoint));
129+
if (connectRequestInterceptor != null) {
130+
connectRequestInterceptor.process(connectRequest, null, context);
131+
}
126132
requestChannel.sendRequest(connectRequest, new BasicEntityDetails(-1, null), context);
127133
}
128134

@@ -154,7 +160,7 @@ public void consumeResponse(
154160

155161
final int status = response.getCode();
156162
if (status < 200 || status >= 300) {
157-
throw new HttpException("Tunnel refused: " + new StatusLine(response));
163+
throw new TunnelRefusedException(response);
158164
}
159165

160166
if (entityDetails == null) {

httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/H2OverH2TunnelSupport.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
package org.apache.hc.core5.http2.nio.support;
2828

2929
import org.apache.hc.core5.concurrent.FutureCallback;
30+
import org.apache.hc.core5.http.HttpRequestInterceptor;
3031
import org.apache.hc.core5.http.nio.command.RequestExecutionCommand;
3132
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
3233
import org.apache.hc.core5.http.protocol.HttpCoreContext;
@@ -66,7 +67,7 @@ public static void establish(
6667
final boolean secure,
6768
final TlsStrategy tlsStrategy,
6869
final FutureCallback<ProtocolIOSession> callback) {
69-
establishInternal(proxySession, target, connectTimeout, secure, tlsStrategy, null, callback);
70+
establishInternal(proxySession, target, connectTimeout, secure, tlsStrategy, null, null, callback);
7071
}
7172

7273
public static void establish(
@@ -75,6 +76,29 @@ public static void establish(
7576
final Timeout connectTimeout,
7677
final boolean secure,
7778
final TlsStrategy tlsStrategy,
79+
final HttpRequestInterceptor connectRequestInterceptor,
80+
final FutureCallback<ProtocolIOSession> callback) {
81+
establishInternal(proxySession, target, connectTimeout, secure, tlsStrategy, connectRequestInterceptor, null, callback);
82+
}
83+
84+
public static void establish(
85+
final IOSession proxySession,
86+
final NamedEndpoint target,
87+
final Timeout connectTimeout,
88+
final boolean secure,
89+
final TlsStrategy tlsStrategy,
90+
final IOEventHandlerFactory protocolStarter,
91+
final FutureCallback<IOSession> callback) {
92+
establish(proxySession, target, connectTimeout, secure, tlsStrategy, null, protocolStarter, callback);
93+
}
94+
95+
public static void establish(
96+
final IOSession proxySession,
97+
final NamedEndpoint target,
98+
final Timeout connectTimeout,
99+
final boolean secure,
100+
final TlsStrategy tlsStrategy,
101+
final HttpRequestInterceptor connectRequestInterceptor,
78102
final IOEventHandlerFactory protocolStarter,
79103
final FutureCallback<IOSession> callback) {
80104

@@ -97,7 +121,7 @@ public void cancelled() {
97121

98122
} : null;
99123

100-
establishInternal(proxySession, target, connectTimeout, secure, tlsStrategy, protocolStarter, adapter);
124+
establishInternal(proxySession, target, connectTimeout, secure, tlsStrategy, connectRequestInterceptor, protocolStarter, adapter);
101125
}
102126

103127
private static void establishInternal(
@@ -106,6 +130,7 @@ private static void establishInternal(
106130
final Timeout connectTimeout,
107131
final boolean secure,
108132
final TlsStrategy tlsStrategy,
133+
final HttpRequestInterceptor connectRequestInterceptor,
109134
final IOEventHandlerFactory protocolStarter,
110135
final FutureCallback<ProtocolIOSession> callback) {
111136

@@ -121,6 +146,7 @@ private static void establishInternal(
121146
connectTimeout,
122147
secure,
123148
tlsStrategy,
149+
connectRequestInterceptor,
124150
protocolStarter,
125151
callback);
126152

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* ====================================================================
3+
* Licensed to the Apache Software Foundation (ASF) under one
4+
* or more contributor license agreements. See the NOTICE file
5+
* distributed with this work for additional information
6+
* regarding copyright ownership. The ASF licenses this file
7+
* to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance
9+
* with the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing,
14+
* software distributed under the License is distributed on an
15+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
* KIND, either express or implied. See the License for the
17+
* specific language governing permissions and limitations
18+
* under the License.
19+
* ====================================================================
20+
*
21+
* This software consists of voluntary contributions made by many
22+
* individuals on behalf of the Apache Software Foundation. For more
23+
* information on the Apache Software Foundation, please see
24+
* <http://www.apache.org/>.
25+
*
26+
*/
27+
package org.apache.hc.core5.http2.nio.support;
28+
29+
import org.apache.hc.core5.http.Header;
30+
import org.apache.hc.core5.http.HttpException;
31+
import org.apache.hc.core5.http.HttpResponse;
32+
import org.apache.hc.core5.http.message.BasicHttpResponse;
33+
import org.apache.hc.core5.http.message.StatusLine;
34+
import org.apache.hc.core5.util.Args;
35+
36+
/**
37+
* Exception indicating CONNECT tunnel refusal by the proxy.
38+
*
39+
* @since 5.5
40+
*/
41+
public final class TunnelRefusedException extends HttpException {
42+
43+
private static final long serialVersionUID = 1L;
44+
45+
private final HttpResponse response;
46+
47+
public TunnelRefusedException(final HttpResponse response) {
48+
super("Tunnel refused: " + new StatusLine(Args.notNull(response, "Response")));
49+
this.response = copy(response);
50+
}
51+
52+
public HttpResponse getResponse() {
53+
return response;
54+
}
55+
56+
public int getStatusCode() {
57+
return response.getCode();
58+
}
59+
60+
private static HttpResponse copy(final HttpResponse response) {
61+
final BasicHttpResponse copy = new BasicHttpResponse(response.getCode(), response.getReasonPhrase());
62+
copy.setVersion(response.getVersion());
63+
for (final Header header : response.getHeaders()) {
64+
copy.addHeader(header);
65+
}
66+
return copy;
67+
}
68+
}

httpcore5-h2/src/test/java/org/apache/hc/core5/http2/nio/support/TestH2OverH2TunnelSupport.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,25 @@ void testEstablishBuildsH2ConnectForAuthorityAndCompletes() {
8282
Assertions.assertNull(session.capturedRequest.getPath());
8383
}
8484

85+
@Test
86+
void testEstablishAppliesConnectRequestInterceptor() {
87+
final ScriptedProxySession session = new ScriptedProxySession(HttpStatus.SC_OK, true);
88+
final RecordingCallback<ProtocolIOSession> callback = new RecordingCallback<>();
89+
90+
H2OverH2TunnelSupport.establish(
91+
session,
92+
new HttpHost("https", "example.org", 443),
93+
Timeout.ofSeconds(1),
94+
false,
95+
null,
96+
(request, entityDetails, context) -> request.addHeader("Proxy-Authorization", "Basic dGVzdDp0ZXN0"),
97+
callback);
98+
99+
Assertions.assertTrue(callback.completed);
100+
Assertions.assertNotNull(session.capturedRequest);
101+
Assertions.assertEquals("Basic dGVzdDp0ZXN0", session.capturedRequest.getFirstHeader("Proxy-Authorization").getValue());
102+
}
103+
85104
@Test
86105
void testEstablishFailsOnRefusedTunnel() {
87106
final ScriptedProxySession session = new ScriptedProxySession(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, false);
@@ -97,6 +116,10 @@ void testEstablishFailsOnRefusedTunnel() {
97116

98117
Assertions.assertFalse(callback.completed);
99118
Assertions.assertNotNull(callback.failed);
119+
Assertions.assertInstanceOf(TunnelRefusedException.class, callback.failed);
120+
Assertions.assertEquals(
121+
HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED,
122+
((TunnelRefusedException) callback.failed).getStatusCode());
100123
}
101124

102125
@Test

0 commit comments

Comments
 (0)