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