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.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 }