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
28 package org.apache.hc.client5.http.impl.async;
29
30 import java.io.IOException;
31 import java.util.Iterator;
32 import java.util.List;
33
34 import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy;
35 import org.apache.hc.core5.function.Callback;
36 import org.apache.hc.core5.http.ConnectionReuseStrategy;
37 import org.apache.hc.core5.http.Header;
38 import org.apache.hc.core5.http.HttpConnection;
39 import org.apache.hc.core5.http.HttpRequest;
40 import org.apache.hc.core5.http.HttpResponse;
41 import org.apache.hc.core5.http.config.CharCodingConfig;
42 import org.apache.hc.core5.http.config.Http1Config;
43 import org.apache.hc.core5.http.impl.Http1StreamListener;
44 import org.apache.hc.core5.http.impl.nio.ClientHttp1IOEventHandler;
45 import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
46 import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestWriterFactory;
47 import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseParserFactory;
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.AsyncPushConsumer;
51 import org.apache.hc.core5.http.nio.HandlerFactory;
52 import org.apache.hc.core5.http.nio.NHttpMessageParserFactory;
53 import org.apache.hc.core5.http.nio.NHttpMessageWriterFactory;
54 import org.apache.hc.core5.http.protocol.HttpProcessor;
55 import org.apache.hc.core5.http2.HttpVersionPolicy;
56 import org.apache.hc.core5.http2.config.H2Config;
57 import org.apache.hc.core5.http2.frame.FramePrinter;
58 import org.apache.hc.core5.http2.frame.RawFrame;
59 import org.apache.hc.core5.http2.impl.nio.ClientH2PrefaceHandler;
60 import org.apache.hc.core5.http2.impl.nio.ClientH2StreamMultiplexerFactory;
61 import org.apache.hc.core5.http2.impl.nio.ClientH2UpgradeHandler;
62 import org.apache.hc.core5.http2.impl.nio.ClientHttp1UpgradeHandler;
63 import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
64 import org.apache.hc.core5.http2.impl.nio.HttpProtocolNegotiator;
65 import org.apache.hc.core5.http2.ssl.ApplicationProtocol;
66 import org.apache.hc.core5.reactor.IOEventHandler;
67 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
68 import org.apache.hc.core5.reactor.ProtocolIOSession;
69 import org.apache.hc.core5.util.Args;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73 class HttpAsyncClientProtocolNegotiationStarter implements IOEventHandlerFactory {
74
75 private static final Logger STREAM_LOG = LoggerFactory.getLogger(InternalHttpAsyncClient.class);
76 private static final Logger HEADER_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.headers");
77 private static final Logger FRAME_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.frame");
78 private static final Logger FRAME_PAYLOAD_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.frame.payload");
79 private static final Logger FLOW_CTRL_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http2.flow");
80
81 private final HttpProcessor httpProcessor;
82 private final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory;
83 private final H2Config h2Config;
84 private final Http1Config h1Config;
85 private final CharCodingConfig charCodingConfig;
86 private final ConnectionReuseStrategy http1ConnectionReuseStrategy;
87 private final NHttpMessageParserFactory<HttpResponse> http1ResponseParserFactory;
88 private final NHttpMessageWriterFactory<HttpRequest> http1RequestWriterFactory;
89 private final Callback<Exception> exceptionCallback;
90
91 HttpAsyncClientProtocolNegotiationStarter(
92 final HttpProcessor httpProcessor,
93 final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory,
94 final H2Config h2Config,
95 final Http1Config h1Config,
96 final CharCodingConfig charCodingConfig,
97 final ConnectionReuseStrategy connectionReuseStrategy,
98 final Callback<Exception> exceptionCallback) {
99 this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
100 this.exchangeHandlerFactory = exchangeHandlerFactory;
101 this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
102 this.h1Config = h1Config != null ? h1Config : Http1Config.DEFAULT;
103 this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
104 this.http1ConnectionReuseStrategy = connectionReuseStrategy != null ? connectionReuseStrategy : DefaultClientConnectionReuseStrategy.INSTANCE;
105 this.http1ResponseParserFactory = new DefaultHttpResponseParserFactory(h1Config);
106 this.http1RequestWriterFactory = DefaultHttpRequestWriterFactory.INSTANCE;
107 this.exceptionCallback = exceptionCallback;
108 }
109
110 @Override
111 public IOEventHandler createHandler(final ProtocolIOSession ioSession, final Object attachment) {
112 final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory;
113 final ClientH2StreamMultiplexerFactory http2StreamHandlerFactory;
114
115 if (STREAM_LOG.isDebugEnabled()
116 || HEADER_LOG.isDebugEnabled()
117 || FRAME_LOG.isDebugEnabled()
118 || FRAME_PAYLOAD_LOG.isDebugEnabled()
119 || FLOW_CTRL_LOG.isDebugEnabled()) {
120 final String id = ioSession.getId();
121 http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory(
122 httpProcessor,
123 h1Config,
124 charCodingConfig,
125 http1ConnectionReuseStrategy,
126 http1ResponseParserFactory,
127 http1RequestWriterFactory,
128 new Http1StreamListener() {
129
130 @Override
131 public void onRequestHead(final HttpConnection connection, final HttpRequest request) {
132 if (HEADER_LOG.isDebugEnabled()) {
133 HEADER_LOG.debug("{} >> {}", id, new RequestLine(request));
134 for (final Iterator<Header> it = request.headerIterator(); it.hasNext(); ) {
135 HEADER_LOG.debug("{} >> {}", id, it.next());
136 }
137 }
138 }
139
140 @Override
141 public void onResponseHead(final HttpConnection connection, final HttpResponse response) {
142 if (HEADER_LOG.isDebugEnabled()) {
143 HEADER_LOG.debug("{} << {}", id, new StatusLine(response));
144 for (final Iterator<Header> it = response.headerIterator(); it.hasNext(); ) {
145 HEADER_LOG.debug("{} << {}", id, it.next());
146 }
147 }
148 }
149
150 @Override
151 public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
152 if (STREAM_LOG.isDebugEnabled()) {
153 if (keepAlive) {
154 STREAM_LOG.debug("{} Connection is kept alive", id);
155 } else {
156 STREAM_LOG.debug("{} Connection is not kept alive", id);
157 }
158 }
159 }
160
161 });
162 http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory(
163 httpProcessor,
164 exchangeHandlerFactory,
165 h2Config,
166 charCodingConfig,
167 new H2StreamListener() {
168
169 final FramePrinter framePrinter = new FramePrinter();
170
171 private void logFrameInfo(final String prefix, final RawFrame frame) {
172 try {
173 final LogAppendable logAppendable = new LogAppendable(FRAME_LOG, prefix);
174 framePrinter.printFrameInfo(frame, logAppendable);
175 logAppendable.flush();
176 } catch (final IOException ignore) {
177 }
178 }
179
180 private void logFramePayload(final String prefix, final RawFrame frame) {
181 try {
182 final LogAppendable logAppendable = new LogAppendable(FRAME_PAYLOAD_LOG, prefix);
183 framePrinter.printPayload(frame, logAppendable);
184 logAppendable.flush();
185 } catch (final IOException ignore) {
186 }
187 }
188
189 private void logFlowControl(final String prefix, final int streamId, final int delta, final int actualSize) {
190 FLOW_CTRL_LOG.debug("{} stream {} flow control {} -> {}", prefix, streamId, delta, actualSize);
191 }
192
193 @Override
194 public void onHeaderInput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
195 if (HEADER_LOG.isDebugEnabled()) {
196 for (int i = 0; i < headers.size(); i++) {
197 HEADER_LOG.debug("{} << {}", id, headers.get(i));
198 }
199 }
200 }
201
202 @Override
203 public void onHeaderOutput(final HttpConnection connection, final int streamId, final List<? extends Header> headers) {
204 if (HEADER_LOG.isDebugEnabled()) {
205 for (int i = 0; i < headers.size(); i++) {
206 HEADER_LOG.debug("{} >> {}", id, headers.get(i));
207 }
208 }
209 }
210
211 @Override
212 public void onFrameInput(final HttpConnection connection, final int streamId, final RawFrame frame) {
213 if (FRAME_LOG.isDebugEnabled()) {
214 logFrameInfo(id + " <<", frame);
215 }
216 if (FRAME_PAYLOAD_LOG.isDebugEnabled()) {
217 logFramePayload(id + " <<", frame);
218 }
219 }
220
221 @Override
222 public void onFrameOutput(final HttpConnection connection, final int streamId, final RawFrame frame) {
223 if (FRAME_LOG.isDebugEnabled()) {
224 logFrameInfo(id + " >>", frame);
225 }
226 if (FRAME_PAYLOAD_LOG.isDebugEnabled()) {
227 logFramePayload(id + " >>", frame);
228 }
229 }
230
231 @Override
232 public void onInputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
233 if (FLOW_CTRL_LOG.isDebugEnabled()) {
234 logFlowControl(id + " <<", streamId, delta, actualSize);
235 }
236 }
237
238 @Override
239 public void onOutputFlowControl(final HttpConnection connection, final int streamId, final int delta, final int actualSize) {
240 if (FLOW_CTRL_LOG.isDebugEnabled()) {
241 logFlowControl(id + " >>", streamId, delta, actualSize);
242 }
243 }
244
245 });
246 } else {
247 http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory(
248 httpProcessor,
249 h1Config,
250 charCodingConfig,
251 http1ConnectionReuseStrategy,
252 http1ResponseParserFactory,
253 http1RequestWriterFactory,
254 null);
255 http2StreamHandlerFactory = new ClientH2StreamMultiplexerFactory(
256 httpProcessor,
257 exchangeHandlerFactory,
258 h2Config,
259 charCodingConfig,
260 null);
261 }
262
263 ioSession.registerProtocol(ApplicationProtocol.HTTP_1_1.id, new ClientHttp1UpgradeHandler(http1StreamHandlerFactory));
264 ioSession.registerProtocol(ApplicationProtocol.HTTP_2.id, new ClientH2UpgradeHandler(http2StreamHandlerFactory, exceptionCallback));
265
266 final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
267 switch (versionPolicy) {
268 case FORCE_HTTP_2:
269 return new ClientH2PrefaceHandler(ioSession, http2StreamHandlerFactory, false, exceptionCallback);
270 case FORCE_HTTP_1:
271 return new ClientHttp1IOEventHandler(http1StreamHandlerFactory.create(ioSession));
272 default:
273 return new HttpProtocolNegotiator(ioSession, null);
274 }
275 }
276
277 }