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