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.io;
29  
30  import java.io.IOException;
31  import java.io.InterruptedIOException;
32  import java.net.Socket;
33  import java.nio.charset.CharsetDecoder;
34  import java.nio.charset.CharsetEncoder;
35  import java.util.concurrent.atomic.AtomicBoolean;
36  
37  import javax.net.ssl.SSLSession;
38  import javax.net.ssl.SSLSocket;
39  
40  import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
41  import org.apache.hc.core5.http.ClassicHttpRequest;
42  import org.apache.hc.core5.http.ClassicHttpResponse;
43  import org.apache.hc.core5.http.ContentLengthStrategy;
44  import org.apache.hc.core5.http.Header;
45  import org.apache.hc.core5.http.config.Http1Config;
46  import org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection;
47  import org.apache.hc.core5.http.impl.io.SocketHolder;
48  import org.apache.hc.core5.http.io.HttpMessageParserFactory;
49  import org.apache.hc.core5.http.io.HttpMessageWriterFactory;
50  import org.apache.hc.core5.http.io.ResponseOutOfOrderStrategy;
51  import org.apache.hc.core5.http.message.RequestLine;
52  import org.apache.hc.core5.http.message.StatusLine;
53  import org.apache.hc.core5.io.CloseMode;
54  import org.apache.hc.core5.util.Identifiable;
55  import org.apache.hc.core5.util.Timeout;
56  import org.slf4j.Logger;
57  import org.slf4j.LoggerFactory;
58  
59  final class DefaultManagedHttpClientConnection
60          extends DefaultBHttpClientConnection implements ManagedHttpClientConnection, Identifiable {
61  
62      private static final Logger LOG = LoggerFactory.getLogger(DefaultManagedHttpClientConnection.class);
63      private static final Logger HEADER_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.headers");
64      private static final Logger WIRE_LOG = LoggerFactory.getLogger("org.apache.hc.client5.http.wire");
65  
66      private final String id;
67      private final AtomicBoolean closed;
68  
69      private Timeout socketTimeout;
70  
71      public DefaultManagedHttpClientConnection(
72              final String id,
73              final CharsetDecoder charDecoder,
74              final CharsetEncoder charEncoder,
75              final Http1Config h1Config,
76              final ContentLengthStrategy incomingContentStrategy,
77              final ContentLengthStrategy outgoingContentStrategy,
78              final ResponseOutOfOrderStrategy responseOutOfOrderStrategy,
79              final HttpMessageWriterFactory<ClassicHttpRequest> requestWriterFactory,
80              final HttpMessageParserFactory<ClassicHttpResponse> responseParserFactory) {
81          super(
82                  h1Config,
83                  charDecoder,
84                  charEncoder,
85                  incomingContentStrategy,
86                  outgoingContentStrategy,
87                  responseOutOfOrderStrategy,
88                  requestWriterFactory,
89                  responseParserFactory);
90          this.id = id;
91          this.closed = new AtomicBoolean();
92      }
93  
94      public DefaultManagedHttpClientConnection(
95              final String id,
96              final CharsetDecoder charDecoder,
97              final CharsetEncoder charEncoder,
98              final Http1Config h1Config,
99              final ContentLengthStrategy incomingContentStrategy,
100             final ContentLengthStrategy outgoingContentStrategy,
101             final HttpMessageWriterFactory<ClassicHttpRequest> requestWriterFactory,
102             final HttpMessageParserFactory<ClassicHttpResponse> responseParserFactory) {
103         this(
104                 id,
105                 charDecoder,
106                 charEncoder,
107                 h1Config,
108                 incomingContentStrategy,
109                 outgoingContentStrategy,
110                 null,
111                 requestWriterFactory,
112                 responseParserFactory);
113     }
114 
115     public DefaultManagedHttpClientConnection(final String id) {
116         this(id, null, null, null, null, null, null, null);
117     }
118 
119     @Override
120     public String getId() {
121         return this.id;
122     }
123 
124     @Override
125     public void bind(final SocketHolder socketHolder) throws IOException {
126         if (this.closed.get()) {
127             final Socket socket = socketHolder.getSocket();
128             socket.close(); // allow this to throw...
129             // ...but if it doesn't, explicitly throw one ourselves.
130             throw new InterruptedIOException("Connection already shutdown");
131         }
132         super.bind(socketHolder);
133         socketTimeout = Timeout.ofMilliseconds(socketHolder.getSocket().getSoTimeout());
134     }
135 
136     @Override
137     public Socket getSocket() {
138         final SocketHolder socketHolder = getSocketHolder();
139         return socketHolder != null ? socketHolder.getSocket() : null;
140     }
141 
142     @Override
143     public SSLSession getSSLSession() {
144         final Socket socket = getSocket();
145         if (socket instanceof SSLSocket) {
146             return ((SSLSocket) socket).getSession();
147         } else {
148             return null;
149         }
150     }
151 
152     @Override
153     public void close() throws IOException {
154         if (this.closed.compareAndSet(false, true)) {
155             if (LOG.isDebugEnabled()) {
156                 LOG.debug("{} Close connection", this.id);
157             }
158             super.close();
159         }
160     }
161 
162     @Override
163     public void setSocketTimeout(final Timeout timeout) {
164         if (LOG.isDebugEnabled()) {
165             LOG.debug("{} set socket timeout to {}", this.id, timeout);
166         }
167         super.setSocketTimeout(timeout);
168     }
169 
170     @Override
171     public void close(final CloseMode closeMode) {
172         if (this.closed.compareAndSet(false, true)) {
173             if (LOG.isDebugEnabled()) {
174                 LOG.debug("{} close connection {}", this.id, closeMode);
175             }
176             super.close(closeMode);
177         }
178     }
179 
180     @Override
181     public void bind(final Socket socket) throws IOException {
182         super.bind(WIRE_LOG.isDebugEnabled() ? new LoggingSocketHolder(socket, this.id, WIRE_LOG) : new SocketHolder(socket));
183         socketTimeout = Timeout.ofMilliseconds(socket.getSoTimeout());
184     }
185 
186     @Override
187     protected void onResponseReceived(final ClassicHttpResponse response) {
188         if (response != null && HEADER_LOG.isDebugEnabled()) {
189             HEADER_LOG.debug("{} << {}", this.id, new StatusLine(response));
190             final Header[] headers = response.getHeaders();
191             for (final Header header : headers) {
192                 HEADER_LOG.debug("{} << {}", this.id, header);
193             }
194         }
195     }
196 
197     @Override
198     protected void onRequestSubmitted(final ClassicHttpRequest request) {
199         if (request != null && HEADER_LOG.isDebugEnabled()) {
200             HEADER_LOG.debug("{} >> {}", this.id, new RequestLine(request));
201             final Header[] headers = request.getHeaders();
202             for (final Header header : headers) {
203                 HEADER_LOG.debug("{} >> {}", this.id, header);
204             }
205         }
206     }
207 
208     @Override
209     public void passivate() {
210         super.setSocketTimeout(Timeout.ZERO_MILLISECONDS);
211     }
212 
213     @Override
214     public void activate() {
215         super.setSocketTimeout(socketTimeout);
216     }
217 
218 }