View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.transport.http;
8   
9   import org.mule.RequestContext;
10  import org.mule.api.MuleEvent;
11  import org.mule.api.MuleMessage;
12  import org.mule.api.transport.OutputHandler;
13  import org.mule.transformer.types.DataTypeFactory;
14  import org.mule.transport.NullPayload;
15  
16  import java.io.ByteArrayOutputStream;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.io.OutputStream;
20  import java.io.UnsupportedEncodingException;
21  import java.util.Iterator;
22  
23  import org.apache.commons.httpclient.ChunkedInputStream;
24  import org.apache.commons.httpclient.ContentLengthInputStream;
25  import org.apache.commons.httpclient.Header;
26  import org.apache.commons.httpclient.HeaderElement;
27  import org.apache.commons.httpclient.HeaderGroup;
28  import org.apache.commons.httpclient.HttpStatus;
29  import org.apache.commons.httpclient.HttpVersion;
30  import org.apache.commons.httpclient.NameValuePair;
31  import org.apache.commons.httpclient.StatusLine;
32  
33  /**
34   * A generic HTTP response wrapper.
35   */
36  public class HttpResponse
37  {
38  
39      public static final String DEFAULT_CONTENT_CHARSET = "ISO-8859-1";
40  
41      private HttpVersion ver = HttpVersion.HTTP_1_1;
42      private int statusCode = HttpStatus.SC_OK;
43      private String phrase = HttpStatus.getStatusText(HttpStatus.SC_OK);
44      private HeaderGroup headers = new HeaderGroup();
45      private boolean keepAlive = false;
46      private boolean disableKeepAlive = false;
47      private String fallbackCharset = DEFAULT_CONTENT_CHARSET;
48      private OutputHandler outputHandler;
49  
50      public HttpResponse()
51      {
52          super();
53      }
54  
55      public HttpResponse(final StatusLine statusline, final Header[] headers, final InputStream content)
56          throws IOException
57      {
58          super();
59          if (statusline == null)
60          {
61              throw new IllegalArgumentException("Status line may not be null");
62          }
63          setStatusLine(HttpVersion.parse(statusline.getHttpVersion()), statusline.getStatusCode(),
64              statusline.getReasonPhrase());
65          setHeaders(headers);
66          if (content != null)
67          {
68              InputStream in = content;
69              Header contentLength = this.headers.getFirstHeader(HttpConstants.HEADER_CONTENT_LENGTH);
70              Header transferEncoding = this.headers.getFirstHeader(HttpConstants.HEADER_TRANSFER_ENCODING);
71  
72              if (transferEncoding != null)
73              {
74                  if (transferEncoding.getValue().indexOf(HttpConstants.TRANSFER_ENCODING_CHUNKED) != -1)
75                  {
76                      in = new ChunkedInputStream(in);
77                  }
78              }
79              else if (contentLength != null)
80              {
81                  long len = getContentLength();
82                  if (len >= 0)
83                  {
84                      in = new ContentLengthInputStream(in, len);
85                  }
86              }
87          }
88      }
89  
90      public void setStatusLine(final HttpVersion ver, int statuscode, final String phrase)
91      {
92          if (ver == null)
93          {
94              throw new IllegalArgumentException("HTTP version may not be null");
95          }
96          if (statuscode <= 0)
97          {
98              throw new IllegalArgumentException("Status code may not be negative or zero");
99          }
100         this.ver = ver;
101         this.statusCode = statuscode;
102         if (phrase != null)
103         {
104             this.phrase = phrase;
105         }
106         else
107         {
108             this.phrase = HttpStatus.getStatusText(statuscode);
109         }
110     }
111 
112     public void setStatusLine(final HttpVersion ver, int statuscode)
113     {
114         setStatusLine(ver, statuscode, null);
115     }
116 
117     public String getPhrase()
118     {
119         return this.phrase;
120     }
121 
122     /**
123      * @deprecated use {@link #getStatusCode()} instead
124      * @return HTTP status code
125      */
126     public int getStatuscode()
127     {
128         return this.getStatusCode();
129     }
130 
131     public int getStatusCode()
132     {
133         return this.statusCode;
134     }
135 
136     public HttpVersion getHttpVersion()
137     {
138         return this.ver;
139     }
140 
141     public String getStatusLine()
142     {
143         StringBuffer buffer = new StringBuffer(64);
144         buffer.append(this.ver);
145         buffer.append(' ');
146         buffer.append(this.statusCode);
147         if (this.phrase != null)
148         {
149             buffer.append(' ');
150             buffer.append(this.phrase);
151         }
152         return buffer.toString();
153     }
154 
155     public boolean containsHeader(final String name)
156     {
157         return this.headers.containsHeader(name);
158     }
159 
160     public Header[] getHeaders()
161     {
162         return this.headers.getAllHeaders();
163     }
164 
165     public Header getFirstHeader(final String name)
166     {
167         return this.headers.getFirstHeader(name);
168     }
169 
170     public void removeHeaders(final String s)
171     {
172         if (s == null)
173         {
174             return;
175         }
176         Header[] headers = this.headers.getHeaders(s);
177         for (int i = 0; i < headers.length; i++)
178         {
179             this.headers.removeHeader(headers[i]);
180         }
181     }
182 
183     public void addHeader(final Header header)
184     {
185         if (header == null)
186         {
187             return;
188         }
189         this.headers.addHeader(header);
190     }
191 
192     public void setHeader(final Header header)
193     {
194         if (header == null)
195         {
196             return;
197         }
198         removeHeaders(header.getName());
199         addHeader(header);
200     }
201 
202     public void setHeaders(final Header[] headers)
203     {
204         if (headers == null)
205         {
206             return;
207         }
208         this.headers.setHeaders(headers);
209     }
210 
211     public Iterator getHeaderIterator()
212     {
213         return this.headers.getIterator();
214     }
215 
216     public String getCharset()
217     {
218         String charset = getFallbackCharset();
219         Header contenttype = this.headers.getFirstHeader(HttpConstants.HEADER_CONTENT_TYPE);
220         if (contenttype != null)
221         {
222             HeaderElement values[] = contenttype.getElements();
223             if (values.length == 1)
224             {
225                 NameValuePair param = values[0].getParameterByName("charset");
226                 if (param != null)
227                 {
228                     charset = param.getValue();
229                 }
230             }
231         }
232         return charset;
233     }
234 
235     public long getContentLength()
236     {
237         Header contentLength = this.headers.getFirstHeader(HttpConstants.HEADER_CONTENT_LENGTH);
238         if (contentLength != null)
239         {
240             try
241             {
242                 return Long.parseLong(contentLength.getValue());
243             }
244             catch (NumberFormatException e)
245             {
246                 return -1;
247             }
248         }
249         else
250         {
251             return -1;
252         }
253     }
254 
255     public boolean hasBody()
256     {
257         return outputHandler != null;
258     }
259 
260     public OutputHandler getBody() throws IOException
261     {
262         return outputHandler; 
263     }
264     
265     public void setBody(MuleMessage msg) throws Exception
266     {
267         if (msg == null) return;
268 
269         //TODO MULE-5005 response attachments
270 //        if(msg.getOutboundAttachmentNames().size() > 0)
271 //        {
272 //            setBody(createMultipart());
273 //            setHeader(new Header(HttpConstants.HEADER_CONTENT_TYPE, MimeTypes.MULTIPART_MIXED));
274 //            return;
275 //        }
276         
277         Object payload = msg.getPayload();
278         if (payload instanceof String)
279         {
280             setBody(payload.toString());
281         }
282         else if (payload instanceof NullPayload) 
283         {
284             return;
285         }
286         else if (payload instanceof byte[]) 
287         {
288             setBody((byte[]) payload);
289         }
290         else 
291         {
292             setBody(msg.getPayload(DataTypeFactory.create(OutputHandler.class)));
293         }
294     }
295     
296     public void setBody(OutputHandler outputHandler) 
297     {
298         this.outputHandler = outputHandler;
299     }
300     
301     public void setBody(final String string)
302     {
303         byte[] raw;
304         try
305         {
306             raw = string.getBytes(getCharset());
307         }
308         catch (UnsupportedEncodingException e)
309         {
310             raw = string.getBytes();
311         }
312         setBody(raw);
313     }
314 
315     private void setBody(final byte[] raw)
316     {
317         if (!containsHeader(HttpConstants.HEADER_CONTENT_TYPE))
318         {
319             setHeader(new Header(HttpConstants.HEADER_CONTENT_TYPE, HttpConstants.DEFAULT_CONTENT_TYPE));
320         }
321         if (!containsHeader(HttpConstants.HEADER_TRANSFER_ENCODING))
322         {
323             setHeader(new Header(HttpConstants.HEADER_CONTENT_LENGTH, Long.toString(raw.length)));
324         }        
325         
326         this.outputHandler = new OutputHandler() {
327 
328             public void write(MuleEvent event, OutputStream out) throws IOException
329             {
330                 out.write(raw);
331             }
332             
333         };
334     }
335     
336     public String getBodyAsString() throws IOException 
337     {
338         if (!hasBody()) return "";
339         
340         ByteArrayOutputStream out = new ByteArrayOutputStream();
341         
342         outputHandler.write(RequestContext.getEvent(), out);
343         
344         try
345         {
346             return new String(out.toByteArray(), getCharset());
347         }
348         catch (UnsupportedEncodingException e)
349         {
350             return new String(out.toByteArray());
351         }
352     }
353     
354     public boolean isKeepAlive()
355     {
356         return !disableKeepAlive && keepAlive;
357     }
358 
359     public void setKeepAlive(boolean keepAlive)
360     {
361         this.keepAlive = keepAlive;
362     }
363     
364     /**
365      * The HTTTP spec suggests that for HTTP 1.1 persistent connections should be used, 
366      * for HTTP 1.0 the connection should not be kept alive. This method sets up the keepAlive flag
367      * according to the <code>version</code> that was passed in.
368      */
369     protected void setupKeepAliveFromRequestVersion(HttpVersion version)
370     {
371         setKeepAlive(version.equals(HttpVersion.HTTP_1_1));
372     }
373 
374     public void disableKeepAlive(boolean keepalive)
375     {
376         disableKeepAlive = keepalive;
377     }
378 
379     public String getFallbackCharset()
380     {
381         return fallbackCharset;
382     }
383 
384     public void setFallbackCharset(String overrideCharset)
385     {
386         this.fallbackCharset = overrideCharset;
387     }
388 
389       //TODO MULE-5005 response attachments
390 //    protected OutputHandler createMultipart() throws Exception
391 //    {
392 //
393 //        return new OutputHandler() {
394 //            public void write(MuleEvent event, OutputStream out) throws IOException
395 //            {
396 //                MultiPartOutputStream partStream = new MultiPartOutputStream(out, event.getEncoding());
397 //                try
398 //                {
399 //                    MuleMessage msg = event.getMessage();
400 //                    if (!(msg.getPayload() instanceof NullPayload))
401 //                    {
402 //                        String contentType = msg.getOutboundProperty(HttpConstants.HEADER_CONTENT_TYPE, MimeTypes.BINARY);
403 //                        partStream.startPart(contentType);
404 //                        try
405 //                        {
406 //                            partStream.getOut().write(msg.getPayloadAsBytes());
407 //                        }
408 //                        catch (Exception e)
409 //                        {
410 //                            throw new IOException(e);
411 //                        }
412 //                    }
413 //                    //Write attachments
414 //                    for (String name : event.getMessage().getOutboundAttachmentNames())
415 //                    {
416 //                        DataHandler dh = event.getMessage().getOutboundAttachment(name);
417 //                        partStream.startPart(dh.getContentType());
418 //                        partStream.getOut().write(IOUtils.toByteArray(dh.getInputStream()));
419 //                    }
420 //                }
421 //                finally
422 //                {
423 //                    partStream.close();
424 //                }
425 //            }
426 //        };
427 //
428 //    }
429 
430 }