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;
29  
30  import java.io.IOException;
31  import java.net.InetAddress;
32  import java.net.InetSocketAddress;
33  import java.net.Socket;
34  import java.net.SocketAddress;
35  import java.net.SocketException;
36  
37  import org.apache.http.HttpInetConnection;
38  import org.apache.http.annotation.NotThreadSafe;
39  import org.apache.http.impl.io.SocketInputBuffer;
40  import org.apache.http.impl.io.SocketOutputBuffer;
41  import org.apache.http.io.SessionInputBuffer;
42  import org.apache.http.io.SessionOutputBuffer;
43  import org.apache.http.params.HttpConnectionParams;
44  import org.apache.http.params.HttpParams;
45  
46  /**
47   * Implementation of a client-side HTTP connection that can be bound to an
48   * arbitrary {@link Socket} for receiving data from and transmitting data to
49   * a remote server.
50   * <p>
51   * The following parameters can be used to customize the behavior of this
52   * class:
53   * <ul>
54   *  <li>{@link org.apache.http.params.CoreProtocolPNames#STRICT_TRANSFER_ENCODING}</li>
55   *  <li>{@link org.apache.http.params.CoreProtocolPNames#HTTP_ELEMENT_CHARSET}</li>
56   *  <li>{@link org.apache.http.params.CoreConnectionPNames#SOCKET_BUFFER_SIZE}</li>
57   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_LINE_LENGTH}</li>
58   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MAX_HEADER_COUNT}</li>
59   *  <li>{@link org.apache.http.params.CoreConnectionPNames#MIN_CHUNK_LIMIT}</li>
60   * </ul>
61   *
62   * @since 4.0
63   */
64  @NotThreadSafe
65  public class SocketHttpClientConnection
66          extends AbstractHttpClientConnection implements HttpInetConnection {
67  
68      private volatile boolean open;
69      private volatile Socket socket = null;
70  
71      public SocketHttpClientConnection() {
72          super();
73      }
74  
75      protected void assertNotOpen() {
76          if (this.open) {
77              throw new IllegalStateException("Connection is already open");
78          }
79      }
80  
81      @Override
82      protected void assertOpen() {
83          if (!this.open) {
84              throw new IllegalStateException("Connection is not open");
85          }
86      }
87  
88      /**
89       * Creates an instance of {@link SocketInputBuffer} to be used for
90       * receiving data from the given {@link Socket}.
91       * <p>
92       * This method can be overridden in a super class in order to provide
93       * a custom implementation of {@link SessionInputBuffer} interface.
94       *
95       * @see SocketInputBuffer#SocketInputBuffer(Socket, int, HttpParams)
96       *
97       * @param socket the socket.
98       * @param buffersize the buffer size.
99       * @param params HTTP parameters.
100      * @return session input buffer.
101      * @throws IOException in case of an I/O error.
102      */
103     protected SessionInputBuffer createSessionInputBuffer(
104             final Socket socket,
105             int buffersize,
106             final HttpParams params) throws IOException {
107         return new SocketInputBuffer(socket, buffersize, params);
108     }
109 
110     /**
111      * Creates an instance of {@link SessionOutputBuffer} to be used for
112      * sending data to the given {@link Socket}.
113      * <p>
114      * This method can be overridden in a super class in order to provide
115      * a custom implementation of {@link SocketOutputBuffer} interface.
116      *
117      * @see SocketOutputBuffer#SocketOutputBuffer(Socket, int, HttpParams)
118      *
119      * @param socket the socket.
120      * @param buffersize the buffer size.
121      * @param params HTTP parameters.
122      * @return session output buffer.
123      * @throws IOException in case of an I/O error.
124      */
125     protected SessionOutputBuffer createSessionOutputBuffer(
126             final Socket socket,
127             int buffersize,
128             final HttpParams params) throws IOException {
129         return new SocketOutputBuffer(socket, buffersize, params);
130     }
131 
132     /**
133      * Binds this connection to the given {@link Socket}. This socket will be
134      * used by the connection to send and receive data.
135      * <p>
136      * This method will invoke {@link #createSessionInputBuffer(Socket, int, HttpParams)}
137      * and {@link #createSessionOutputBuffer(Socket, int, HttpParams)} methods
138      * to create session input / output buffers bound to this socket and then
139      * will invoke {@link #init(SessionInputBuffer, SessionOutputBuffer, HttpParams)}
140      * method to pass references to those buffers to the underlying HTTP message
141      * parser and formatter.
142      * <p>
143      * After this method's execution the connection status will be reported
144      * as open and the {@link #isOpen()} will return <code>true</code>.
145      *
146      * @param socket the socket.
147      * @param params HTTP parameters.
148      * @throws IOException in case of an I/O error.
149      */
150     protected void bind(
151             final Socket socket,
152             final HttpParams params) throws IOException {
153         if (socket == null) {
154             throw new IllegalArgumentException("Socket may not be null");
155         }
156         if (params == null) {
157             throw new IllegalArgumentException("HTTP parameters may not be null");
158         }
159         this.socket = socket;
160 
161         int buffersize = HttpConnectionParams.getSocketBufferSize(params);
162 
163         init(
164                 createSessionInputBuffer(socket, buffersize, params),
165                 createSessionOutputBuffer(socket, buffersize, params),
166                 params);
167 
168         this.open = true;
169     }
170 
171     public boolean isOpen() {
172         return this.open;
173     }
174 
175     protected Socket getSocket() {
176         return this.socket;
177     }
178 
179     public InetAddress getLocalAddress() {
180         if (this.socket != null) {
181             return this.socket.getLocalAddress();
182         } else {
183             return null;
184         }
185     }
186 
187     public int getLocalPort() {
188         if (this.socket != null) {
189             return this.socket.getLocalPort();
190         } else {
191             return -1;
192         }
193     }
194 
195     public InetAddress getRemoteAddress() {
196         if (this.socket != null) {
197             return this.socket.getInetAddress();
198         } else {
199             return null;
200         }
201     }
202 
203     public int getRemotePort() {
204         if (this.socket != null) {
205             return this.socket.getPort();
206         } else {
207             return -1;
208         }
209     }
210 
211     public void setSocketTimeout(int timeout) {
212         assertOpen();
213         if (this.socket != null) {
214             try {
215                 this.socket.setSoTimeout(timeout);
216             } catch (SocketException ignore) {
217                 // It is not quite clear from the Sun's documentation if there are any
218                 // other legitimate cases for a socket exception to be thrown when setting
219                 // SO_TIMEOUT besides the socket being already closed
220             }
221         }
222     }
223 
224     public int getSocketTimeout() {
225         if (this.socket != null) {
226             try {
227                 return this.socket.getSoTimeout();
228             } catch (SocketException ignore) {
229                 return -1;
230             }
231         } else {
232             return -1;
233         }
234     }
235 
236     public void shutdown() throws IOException {
237         this.open = false;
238         Socket tmpsocket = this.socket;
239         if (tmpsocket != null) {
240             tmpsocket.close();
241         }
242     }
243 
244     public void close() throws IOException {
245         if (!this.open) {
246             return;
247         }
248         this.open = false;
249         Socket sock = this.socket;
250         try {
251             doFlush();
252             try {
253                 try {
254                     sock.shutdownOutput();
255                 } catch (IOException ignore) {
256                 }
257                 try {
258                     sock.shutdownInput();
259                 } catch (IOException ignore) {
260                 }
261             } catch (UnsupportedOperationException ignore) {
262                 // if one isn't supported, the other one isn't either
263             }
264         } finally {
265             sock.close();
266         }
267     }
268 
269     private static void formatAddress(final StringBuilder buffer, final SocketAddress socketAddress) {
270         if (socketAddress instanceof InetSocketAddress) {
271             InetSocketAddress addr = ((InetSocketAddress) socketAddress);
272             buffer.append(addr.getAddress() != null ? addr.getAddress().getHostAddress() :
273                 addr.getAddress())
274             .append(':')
275             .append(addr.getPort());
276         } else {
277             buffer.append(socketAddress);
278         }
279     }
280 
281     @Override
282     public String toString() {
283         if (this.socket != null) {
284             StringBuilder buffer = new StringBuilder();
285             SocketAddress remoteAddress = this.socket.getRemoteSocketAddress();
286             SocketAddress localAddress = this.socket.getLocalSocketAddress();
287             if (remoteAddress != null && localAddress != null) {
288                 formatAddress(buffer, localAddress);
289                 buffer.append("<->");
290                 formatAddress(buffer, remoteAddress);
291             }
292             return buffer.toString();
293         } else {
294             return super.toString();
295         }
296     }
297 
298 }