View Javadoc

1   /*
2    * $Id: HttpServerConnection.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.http;
12  
13  import org.mule.RequestContext;
14  import org.mule.api.transformer.TransformerException;
15  import org.mule.api.transport.Connector;
16  import org.mule.api.transport.OutputHandler;
17  import org.mule.util.SystemUtils;
18  
19  import java.io.DataOutputStream;
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  import java.io.UnsupportedEncodingException;
24  import java.net.Socket;
25  import java.net.SocketException;
26  import java.util.Iterator;
27  
28  import org.apache.commons.httpclient.ChunkedOutputStream;
29  import org.apache.commons.httpclient.Header;
30  import org.apache.commons.httpclient.HttpParser;
31  import org.apache.commons.httpclient.StatusLine;
32  import org.apache.commons.io.IOUtils;
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  
36  /** A connection to the SimpleHttpServer. */
37  public class HttpServerConnection
38  {
39      private static final Log logger = LogFactory.getLog(HttpServerConnection.class);
40  
41      private Socket socket;
42      private final InputStream in;
43      private final OutputStream out;
44      // this should rather be isKeepSocketOpen as this is the main purpose of this flag
45      private boolean keepAlive = false;
46      private final String encoding;
47  
48      public HttpServerConnection(final Socket socket, String encoding, HttpConnector connector) throws IOException
49      {
50          super();
51  
52          if (socket == null)
53          {
54              throw new IllegalArgumentException("Socket may not be null");
55          }
56  
57          this.socket = socket;
58          setSocketTcpNoDelay();
59          this.socket.setKeepAlive(connector.isKeepAlive());
60          
61          if (connector.getReceiveBufferSize() != Connector.INT_VALUE_NOT_SET
62              && socket.getReceiveBufferSize() != connector.getReceiveBufferSize())
63          {
64              socket.setReceiveBufferSize(connector.getReceiveBufferSize());            
65          }
66          if (connector.getServerSoTimeout() != Connector.INT_VALUE_NOT_SET
67              && socket.getSoTimeout() != connector.getServerSoTimeout())
68          {
69              socket.setSoTimeout(connector.getServerSoTimeout());
70          }
71          
72          this.in = socket.getInputStream();
73          this.out = new DataOutputStream(socket.getOutputStream());
74          this.encoding = encoding;
75      }
76  
77      private void setSocketTcpNoDelay() throws IOException
78      {
79          try
80          {
81              socket.setTcpNoDelay(true);
82          }
83          catch (SocketException se)
84          {
85              if (SystemUtils.IS_OS_SOLARIS || SystemUtils.IS_OS_SUN_OS)
86              {
87                  // this is a known Solaris bug, see
88                  // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6378870
89                  
90                  if (logger.isDebugEnabled())
91                  {
92                      logger.debug("Failed to set tcpNoDelay on socket", se);
93                  }
94              }
95              else
96              {
97                  throw se;
98              }
99          }
100     }
101 
102     public synchronized void close()
103     {
104         try
105         {
106             if (socket != null)
107             {
108                 if (logger.isDebugEnabled())
109                 {
110                     logger.debug("Closing: " + socket);
111                 }
112                 
113                 try
114                 {
115                     socket.shutdownOutput();
116                 }
117                 catch (UnsupportedOperationException e)
118                 {
119                     //Can't shutdown in/output on SSL sockets
120                 }
121                 
122                 if (in != null)
123                 {
124                     in.close();
125                 }
126                 if (out != null)
127                 {
128                     out.close();
129                 }
130                 socket.close();
131             }
132         }
133         catch (IOException e)
134         {
135             if (logger.isDebugEnabled())
136             {
137                 logger.debug("(Ignored) Error closing the socket: " + e.getMessage());
138             }
139         }
140         finally
141         {
142             socket = null;
143         }
144     }
145 
146     public synchronized boolean isOpen()
147     {
148         return this.socket != null;
149     }
150 
151     public void setKeepAlive(boolean b)
152     {
153         this.keepAlive = b;
154     }
155 
156     public boolean isKeepAlive()
157     {
158         return this.keepAlive;
159     }
160 
161     public InputStream getInputStream()
162     {
163         return this.in;
164     }
165 
166     public OutputStream getOutputStream()
167     {
168         return this.out;
169     }
170 
171     /**
172      * Returns the ResponseWriter used to write the output to the socket.
173      *
174      * @return This connection's ResponseWriter
175      */
176     public ResponseWriter getWriter() throws UnsupportedEncodingException
177     {
178         return new ResponseWriter(out);
179     }
180 
181     public HttpRequest readRequest() throws IOException
182     {
183         try
184         {
185             String line = readLine();
186             if (line == null)
187             {
188                 return null;
189             }
190             return new HttpRequest(RequestLine.parseLine(line), HttpParser.parseHeaders(this.in, encoding), this.in, encoding);
191         }
192         catch (IOException e)
193         {
194             close();
195             throw e;
196         }
197     }
198 
199     public HttpResponse readResponse() throws IOException
200     {
201         try
202         {
203             String line = readLine();
204             return new HttpResponse(new StatusLine(line), HttpParser.parseHeaders(this.in, encoding), this.in);
205         }
206         catch (IOException e)
207         {
208             close();
209             throw e;
210         }
211     }
212 
213     private String readLine() throws IOException
214     {
215         String line;
216 
217         do
218         {
219             line = HttpParser.readLine(in, encoding);
220         }
221         while (line != null && line.length() == 0);
222 
223         if (line == null)
224         {
225             setKeepAlive(false);
226             return null;
227         }
228 
229         return line;
230     }
231 
232     public void writeRequest(final HttpRequest request) throws IOException
233     {
234         if (request == null)
235         {
236             return;
237         }
238         ResponseWriter writer = new ResponseWriter(this.out, encoding);
239         writer.println(request.getRequestLine().toString());
240         Iterator item = request.getHeaderIterator();
241         while (item.hasNext())
242         {
243             Header header = (Header) item.next();
244             writer.print(header.toExternalForm());
245         }
246         writer.println();
247         writer.flush();
248 
249         OutputStream outstream = this.out;
250         InputStream content = request.getBody();
251         if (content != null)
252         {
253             Header transferenc = request.getFirstHeader(HttpConstants.HEADER_TRANSFER_ENCODING);
254             if (transferenc != null)
255             {
256                 request.removeHeaders(HttpConstants.HEADER_CONTENT_LENGTH);
257                 if (transferenc.getValue().indexOf(HttpConstants.TRANSFER_ENCODING_CHUNKED) != -1)
258                 {
259                     outstream = new ChunkedOutputStream(outstream);
260                 }
261             }
262 
263             IOUtils.copy(content, outstream);
264 
265             if (outstream instanceof ChunkedOutputStream)
266             {
267                 ((ChunkedOutputStream) outstream).finish();
268             }
269         }
270 
271         outstream.flush();
272     }
273 
274     public void writeResponse(final HttpResponse response) throws IOException, TransformerException
275     {
276         if (response == null)
277         {
278             return;
279         }
280         
281         if (!response.isKeepAlive()) 
282         {
283             Header header = new Header(HttpConstants.HEADER_CONNECTION, "close");
284             response.setHeader(header);
285         }
286         
287         setKeepAlive(response.isKeepAlive());
288         
289         ResponseWriter writer = new ResponseWriter(this.out, encoding);
290         OutputStream outstream = this.out;
291 
292         writer.println(response.getStatusLine());
293         Iterator item = response.getHeaderIterator();
294         while (item.hasNext())
295         {
296             Header header = (Header) item.next();
297             writer.print(header.toExternalForm());
298         }
299         writer.println();
300         writer.flush();
301 
302         OutputHandler content = response.getBody();
303         if (content != null)
304         {
305             Header transferenc = response.getFirstHeader(HttpConstants.HEADER_TRANSFER_ENCODING);
306             if (transferenc != null)
307             {
308                 response.removeHeaders(HttpConstants.HEADER_CONTENT_LENGTH);
309                 if (transferenc.getValue().indexOf(HttpConstants.TRANSFER_ENCODING_CHUNKED) != -1)
310                 {
311                     outstream = new ChunkedOutputStream(outstream);
312                 }
313             }
314 
315             content.write(RequestContext.getEvent(), outstream);
316 
317             if (outstream instanceof ChunkedOutputStream)
318             {
319                 ((ChunkedOutputStream) outstream).finish();
320             }
321         }
322 
323         outstream.flush();
324     }
325 
326     public int getSocketTimeout() throws SocketException
327     {
328         return this.socket.getSoTimeout();
329     }
330 
331     public void setSocketTimeout(int timeout) throws SocketException
332     {
333         this.socket.setSoTimeout(timeout);
334     }
335 }