View Javadoc
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  
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 }