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