1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.http2.examples;
28
29 import java.util.List;
30 import java.util.concurrent.CountDownLatch;
31 import java.util.concurrent.TimeUnit;
32
33 import org.apache.hc.core5.concurrent.ComplexFuture;
34 import org.apache.hc.core5.concurrent.FutureCallback;
35 import org.apache.hc.core5.concurrent.FutureContribution;
36 import org.apache.hc.core5.http.Header;
37 import org.apache.hc.core5.http.HttpConnection;
38 import org.apache.hc.core5.http.HttpException;
39 import org.apache.hc.core5.http.HttpHost;
40 import org.apache.hc.core5.http.HttpRequest;
41 import org.apache.hc.core5.http.HttpResponse;
42 import org.apache.hc.core5.http.HttpStatus;
43 import org.apache.hc.core5.http.Message;
44 import org.apache.hc.core5.http.Method;
45 import org.apache.hc.core5.http.impl.Http1StreamListener;
46 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
47 import org.apache.hc.core5.http.message.BasicHttpRequest;
48 import org.apache.hc.core5.http.message.RequestLine;
49 import org.apache.hc.core5.http.message.StatusLine;
50 import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
51 import org.apache.hc.core5.http.nio.entity.DiscardingEntityConsumer;
52 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityConsumer;
53 import org.apache.hc.core5.http.nio.ssl.TlsUpgradeCapable;
54 import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
55 import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
56 import org.apache.hc.core5.http2.HttpVersionPolicy;
57 import org.apache.hc.core5.http2.config.H2Config;
58 import org.apache.hc.core5.http2.frame.RawFrame;
59 import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
60 import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
61 import org.apache.hc.core5.io.CloseMode;
62 import org.apache.hc.core5.reactor.ProtocolIOSession;
63 import org.apache.hc.core5.util.Timeout;
64
65
66
67
68 public class H2ViaHttp1ProxyExecutionExample {
69
70 public static void main(final String[] args) throws Exception {
71
72
73 final H2Config h2Config = H2Config.custom()
74 .setPushEnabled(false)
75 .build();
76
77 final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
78 .setH2Config(h2Config)
79 .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
80 .setStreamListener(new Http1StreamListener() {
81
82 @Override
83 public void onRequestHead(final HttpConnection connection, final HttpRequest request) {
84 System.out.println(connection.getRemoteAddress() + " " + new RequestLine(request));
85 }
86
87 @Override
88 public void onResponseHead(final HttpConnection connection, final HttpResponse response) {
89 System.out.println(connection.getRemoteAddress() + " " + new StatusLine(response));
90 }
91
92 @Override
93 public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
94 if (keepAlive) {
95 System.out.println(connection.getRemoteAddress() + " exchange completed (connection kept alive)");
96 } else {
97 System.out.println(connection.getRemoteAddress() + " exchange completed (connection closed)");
98 }
99 }
100
101 })
102 .setStreamListener(new H2StreamListener() {
103
104 @Override
105 public void onHeaderInput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
106 for (int i = 0; i < headers.size(); i++) {
107 System.out.println(connection.getRemoteAddress() + " (" + streamId + ") << " + headers.get(i));
108 }
109 }
110
111 @Override
112 public void onHeaderOutput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
113 for (int i = 0; i < headers.size(); i++) {
114 System.out.println(connection.getRemoteAddress() + " (" + streamId + ") >> " + headers.get(i));
115 }
116 }
117
118 @Override
119 public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) {
120 }
121
122 @Override
123 public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) {
124 }
125
126 @Override
127 public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
128 }
129
130 @Override
131 public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
132 }
133
134 })
135 .create();
136
137 Runtime.getRuntime().addShutdownHook(new Thread(() -> {
138 System.out.println("HTTP requester shutting down");
139 requester.close(CloseMode.GRACEFUL);
140 }));
141 requester.start();
142
143 final HttpHost proxy = new HttpHost("localhost", 8888);
144 final HttpHost target = new HttpHost("https", "nghttp2.org");
145
146 final ComplexFuture<AsyncClientEndpoint> tunnelFuture = new ComplexFuture<>(null);
147 tunnelFuture.setDependency(requester.connect(
148 proxy,
149 Timeout.ofSeconds(30),
150 null,
151 new FutureContribution<AsyncClientEndpoint>(tunnelFuture) {
152
153 @Override
154 public void completed(final AsyncClientEndpoint endpoint) {
155 if (endpoint instanceof TlsUpgradeCapable) {
156 final HttpRequest connect = new BasicHttpRequest(Method.CONNECT, proxy, target.toHostString());
157 endpoint.execute(
158 new BasicRequestProducer(connect, null),
159 new BasicResponseConsumer<>(new DiscardingEntityConsumer<>()),
160 new FutureContribution<Message<HttpResponse, Void>>(tunnelFuture) {
161
162 @Override
163 public void completed(final Message<HttpResponse, Void> message) {
164 final HttpResponse response = message.getHead();
165 if (response.getCode() == HttpStatus.SC_OK) {
166 ((TlsUpgradeCapable) endpoint).tlsUpgrade(
167 target,
168 new FutureContribution<ProtocolIOSession>(tunnelFuture) {
169
170 @Override
171 public void completed(final ProtocolIOSession protocolSession) {
172 System.out.println("Tunnel to " + target + " via " + proxy + " established");
173 tunnelFuture.completed(endpoint);
174 }
175
176 });
177 } else {
178 tunnelFuture.failed(new HttpException("Tunnel refused: " + new StatusLine(response)));
179 }
180 }
181
182 });
183 } else {
184 tunnelFuture.failed(new IllegalStateException("TLS upgrade not supported"));
185 }
186 }
187
188 }));
189
190 final String[] requestUris = new String[] {"/httpbin/ip", "/httpbin/user-agent", "/httpbin/headers"};
191 final AsyncClientEndpoint endpoint = tunnelFuture.get(1, TimeUnit.MINUTES);
192 try {
193 final CountDownLatch latch = new CountDownLatch(requestUris.length);
194 for (final String requestUri : requestUris) {
195 endpoint.execute(
196 new BasicRequestProducer(Method.GET, target, requestUri),
197 new BasicResponseConsumer<>(new StringAsyncEntityConsumer()),
198 new FutureCallback<Message<HttpResponse, String>>() {
199
200 @Override
201 public void completed(final Message<HttpResponse, String> message) {
202 final HttpResponse response = message.getHead();
203 final String body = message.getBody();
204 System.out.println(requestUri + "->" + response.getCode());
205 System.out.println(body);
206 latch.countDown();
207 }
208
209 @Override
210 public void failed(final Exception ex) {
211 System.out.println(requestUri + "->" + ex);
212 latch.countDown();
213 }
214
215 @Override
216 public void cancelled() {
217 System.out.println(requestUri + " cancelled");
218 latch.countDown();
219 }
220
221 });
222 }
223
224 latch.await();
225 } finally {
226 endpoint.releaseAndDiscard();
227 }
228
229 System.out.println("Shutting down I/O reactor");
230 requester.initiateShutdown();
231 }
232
233 }