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.http.impl.conn;
29  
30  import java.io.IOException;
31  import java.io.InterruptedIOException;
32  import java.net.Socket;
33  import java.util.HashMap;
34  import java.util.Map;
35  
36  import org.apache.http.annotation.NotThreadSafe;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.http.Header;
41  import org.apache.http.HttpException;
42  import org.apache.http.HttpHost;
43  import org.apache.http.HttpRequest;
44  import org.apache.http.HttpResponse;
45  import org.apache.http.HttpResponseFactory;
46  import org.apache.http.params.HttpParams;
47  import org.apache.http.params.HttpProtocolParams;
48  import org.apache.http.protocol.HttpContext;
49  import org.apache.http.impl.SocketHttpClientConnection;
50  import org.apache.http.io.HttpMessageParser;
51  import org.apache.http.io.SessionInputBuffer;
52  import org.apache.http.io.SessionOutputBuffer;
53  
54  import org.apache.http.conn.OperatedClientConnection;
55  
56  /**
57   * Default implementation of an operated client connection.
58   * <p>
59   * The following parameters can be used to customize the behavior of this
60   * class:
61   * <ul>
62   *  <li>{@link org.apache.http.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}</li>
63   *  <li>{@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
64   *  <li>{@link org.apache.http.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li>
65   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
66   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
67   * </ul>
68   *
69   * @since 4.0
70   */
71  @NotThreadSafe // connSecure, targetHost
72  public class DefaultClientConnection extends SocketHttpClientConnection
73      implements OperatedClientConnection, HttpContext {
74  
75      private final Log log = LogFactory.getLog(getClass());
76      private final Log headerLog = LogFactory.getLog("org.apache.http.headers");
77      private final Log wireLog = LogFactory.getLog("org.apache.http.wire");
78  
79      /** The unconnected socket */
80      private volatile Socket socket;
81  
82      /** The target host of this connection. */
83      private HttpHost targetHost;
84  
85      /** Whether this connection is secure. */
86      private boolean connSecure;
87  
88      /** True if this connection was shutdown. */
89      private volatile boolean shutdown;
90  
91      /** connection specific attributes */
92      private final Map<String, Object> attributes;
93  
94      public DefaultClientConnection() {
95          super();
96          this.attributes = new HashMap<String, Object>();
97      }
98  
99      public final HttpHost getTargetHost() {
100         return this.targetHost;
101     }
102 
103     public final boolean isSecure() {
104         return this.connSecure;
105     }
106 
107     @Override
108     public final Socket getSocket() {
109         return this.socket;
110     }
111 
112     public void opening(Socket sock, HttpHost target) throws IOException {
113         assertNotOpen();
114         this.socket = sock;
115         this.targetHost = target;
116 
117         // Check for shutdown after assigning socket, so that
118         if (this.shutdown) {
119             sock.close(); // allow this to throw...
120             // ...but if it doesn't, explicitly throw one ourselves.
121             throw new InterruptedIOException("Connection already shutdown");
122         }
123     }
124 
125     public void openCompleted(boolean secure, HttpParams params) throws IOException {
126         assertNotOpen();
127         if (params == null) {
128             throw new IllegalArgumentException
129                 ("Parameters must not be null.");
130         }
131         this.connSecure = secure;
132         bind(this.socket, params);
133     }
134 
135     /**
136      * Force-closes this connection.
137      * If the connection is still in the process of being open (the method
138      * {@link #opening opening} was already called but
139      * {@link #openCompleted openCompleted} was not), the associated
140      * socket that is being connected to a remote address will be closed.
141      * That will interrupt a thread that is blocked on connecting
142      * the socket.
143      * If the connection is not yet open, this will prevent the connection
144      * from being opened.
145      *
146      * @throws IOException      in case of a problem
147      */
148     @Override
149     public void shutdown() throws IOException {
150         shutdown = true;
151         try {
152             super.shutdown();
153             if (log.isDebugEnabled()) {
154                 log.debug("Connection " + this + " shut down");
155             }
156             Socket sock = this.socket; // copy volatile attribute
157             if (sock != null)
158                 sock.close();
159         } catch (IOException ex) {
160             log.debug("I/O error shutting down connection", ex);
161         }
162     }
163 
164     @Override
165     public void close() throws IOException {
166         try {
167             super.close();
168             if (log.isDebugEnabled()) {
169                 log.debug("Connection " + this + " closed");
170             }
171         } catch (IOException ex) {
172             log.debug("I/O error closing connection", ex);
173         }
174     }
175 
176     @Override
177     protected SessionInputBuffer createSessionInputBuffer(
178             final Socket socket,
179             int buffersize,
180             final HttpParams params) throws IOException {
181         if (buffersize == -1) {
182             buffersize = 8192;
183         }
184         SessionInputBuffer inbuffer = super.createSessionInputBuffer(
185                 socket,
186                 buffersize,
187                 params);
188         if (wireLog.isDebugEnabled()) {
189             inbuffer = new LoggingSessionInputBuffer(
190                     inbuffer,
191                     new Wire(wireLog),
192                     HttpProtocolParams.getHttpElementCharset(params));
193         }
194         return inbuffer;
195     }
196 
197     @Override
198     protected SessionOutputBuffer createSessionOutputBuffer(
199             final Socket socket,
200             int buffersize,
201             final HttpParams params) throws IOException {
202         if (buffersize == -1) {
203             buffersize = 8192;
204         }
205         SessionOutputBuffer outbuffer = super.createSessionOutputBuffer(
206                 socket,
207                 buffersize,
208                 params);
209         if (wireLog.isDebugEnabled()) {
210             outbuffer = new LoggingSessionOutputBuffer(
211                     outbuffer,
212                     new Wire(wireLog),
213                     HttpProtocolParams.getHttpElementCharset(params));
214         }
215         return outbuffer;
216     }
217 
218     @Override
219     protected HttpMessageParser<HttpResponse> createResponseParser(
220             final SessionInputBuffer buffer,
221             final HttpResponseFactory responseFactory,
222             final HttpParams params) {
223         // override in derived class to specify a line parser
224         return new DefaultHttpResponseParser
225             (buffer, null, responseFactory, params);
226     }
227 
228     public void update(Socket sock, HttpHost target,
229                        boolean secure, HttpParams params)
230         throws IOException {
231 
232         assertOpen();
233         if (target == null) {
234             throw new IllegalArgumentException
235                 ("Target host must not be null.");
236         }
237         if (params == null) {
238             throw new IllegalArgumentException
239                 ("Parameters must not be null.");
240         }
241 
242         if (sock != null) {
243             this.socket = sock;
244             bind(sock, params);
245         }
246         targetHost = target;
247         connSecure = secure;
248     }
249 
250     @Override
251     public HttpResponse receiveResponseHeader() throws HttpException, IOException {
252         HttpResponse response = super.receiveResponseHeader();
253         if (log.isDebugEnabled()) {
254             log.debug("Receiving response: " + response.getStatusLine());
255         }
256         if (headerLog.isDebugEnabled()) {
257             headerLog.debug("<< " + response.getStatusLine().toString());
258             Header[] headers = response.getAllHeaders();
259             for (Header header : headers) {
260                 headerLog.debug("<< " + header.toString());
261             }
262         }
263         return response;
264     }
265 
266     @Override
267     public void sendRequestHeader(HttpRequest request) throws HttpException, IOException {
268         if (log.isDebugEnabled()) {
269             log.debug("Sending request: " + request.getRequestLine());
270         }
271         super.sendRequestHeader(request);
272         if (headerLog.isDebugEnabled()) {
273             headerLog.debug(">> " + request.getRequestLine().toString());
274             Header[] headers = request.getAllHeaders();
275             for (Header header : headers) {
276                 headerLog.debug(">> " + header.toString());
277             }
278         }
279     }
280 
281     public Object getAttribute(final String id) {
282         return this.attributes.get(id);
283     }
284 
285     public Object removeAttribute(final String id) {
286         return this.attributes.remove(id);
287     }
288 
289     public void setAttribute(final String id, final Object obj) {
290         this.attributes.put(id, obj);
291     }
292 
293 }