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         }
148         return null;
149     }
150 
151     @Override
152     public void close() throws IOException {
153         if (this.closed.compareAndSet(false, true)) {
154             if (LOG.isDebugEnabled()) {
155                 LOG.debug("{} Close connection", this.id);
156             }
157             super.close();
158         }
159     }
160 
161     @Override
162     public void setSocketTimeout(final Timeout timeout) {
163         if (LOG.isDebugEnabled()) {
164             LOG.debug("{} set socket timeout to {}", this.id, timeout);
165         }
166         super.setSocketTimeout(timeout);
167         socketTimeout = 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     public void bind(final SSLSocket sslSocket, final Socket socket) throws IOException {
188         super.bind(WIRE_LOG.isDebugEnabled() ?
189                 new LoggingSocketHolder(sslSocket, socket, this.id, WIRE_LOG) :
190                 new SocketHolder(sslSocket, socket));
191         socketTimeout = Timeout.ofMilliseconds(sslSocket.getSoTimeout());
192     }
193 
194     @Override
195     protected void onResponseReceived(final ClassicHttpResponse response) {
196         if (response != null && HEADER_LOG.isDebugEnabled()) {
197             HEADER_LOG.debug("{} << {}", this.id, new StatusLine(response));
198             final Header[] headers = response.getHeaders();
199             for (final Header header : headers) {
200                 HEADER_LOG.debug("{} << {}", this.id, header);
201             }
202         }
203     }
204 
205     @Override
206     protected void onRequestSubmitted(final ClassicHttpRequest request) {
207         if (request != null && HEADER_LOG.isDebugEnabled()) {
208             HEADER_LOG.debug("{} >> {}", this.id, new RequestLine(request));
209             final Header[] headers = request.getHeaders();
210             for (final Header header : headers) {
211                 HEADER_LOG.debug("{} >> {}", this.id, header);
212             }
213         }
214     }
215 
216     @Override
217     public void passivate() {
218         super.setSocketTimeout(Timeout.ZERO_MILLISECONDS);
219     }
220 
221     @Override
222     public void activate() {
223         super.setSocketTimeout(socketTimeout);
224     }
225 
226 }