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