Coverage Report - org.mule.providers.http.HttpMessageReceiver
 
Classes in this File Line Coverage Branch Coverage Complexity
HttpMessageReceiver
0%
0/49
0%
0/11
2.947
HttpMessageReceiver$HttpWorker
0%
0/122
0%
0/22
2.947
 
 1  
 /*
 2  
  * $Id: HttpMessageReceiver.java 7976 2007-08-21 14:26:13Z dirk.olmes $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.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.providers.http;
 12  
 
 13  
 import org.mule.impl.MuleEvent;
 14  
 import org.mule.impl.MuleMessage;
 15  
 import org.mule.impl.MuleSession;
 16  
 import org.mule.impl.NullSessionHandler;
 17  
 import org.mule.impl.OptimizedRequestContext;
 18  
 import org.mule.impl.RequestContext;
 19  
 import org.mule.providers.ConnectException;
 20  
 import org.mule.providers.NullPayload;
 21  
 import org.mule.providers.http.i18n.HttpMessages;
 22  
 import org.mule.providers.tcp.TcpMessageReceiver;
 23  
 import org.mule.umo.MessagingException;
 24  
 import org.mule.umo.UMOComponent;
 25  
 import org.mule.umo.UMOEvent;
 26  
 import org.mule.umo.UMOException;
 27  
 import org.mule.umo.UMOMessage;
 28  
 import org.mule.umo.endpoint.UMOEndpoint;
 29  
 import org.mule.umo.endpoint.UMOEndpointURI;
 30  
 import org.mule.umo.lifecycle.InitialisationException;
 31  
 import org.mule.umo.provider.UMOConnector;
 32  
 import org.mule.umo.provider.UMOMessageAdapter;
 33  
 import org.mule.umo.provider.UMOMessageReceiver;
 34  
 import org.mule.umo.transformer.TransformerException;
 35  
 import org.mule.util.MapUtils;
 36  
 import org.mule.util.ObjectUtils;
 37  
 
 38  
 import java.io.IOException;
 39  
 import java.net.Socket;
 40  
 import java.util.HashMap;
 41  
 import java.util.Iterator;
 42  
 import java.util.Map;
 43  
 
 44  
 import javax.resource.spi.work.Work;
 45  
 
 46  
 import org.apache.commons.httpclient.Cookie;
 47  
 import org.apache.commons.httpclient.Header;
 48  
 import org.apache.commons.httpclient.cookie.MalformedCookieException;
 49  
 
 50  
 /**
 51  
  * <code>HttpMessageReceiver</code> is a simple http server that can be used to
 52  
  * listen for HTTP requests on a particular port.
 53  
  */
 54  0
 public class HttpMessageReceiver extends TcpMessageReceiver
 55  
 {
 56  
 
 57  
     public HttpMessageReceiver(UMOConnector connector, UMOComponent component, UMOEndpoint endpoint)
 58  
         throws InitialisationException
 59  
     {
 60  0
         super(connector, component, endpoint);
 61  0
     }
 62  
 
 63  
     // @Override
 64  
     protected Work createWork(Socket socket) throws IOException
 65  
     {
 66  0
         return new HttpWorker(socket);
 67  
     }
 68  
 
 69  
     // @Override
 70  
     protected void doConnect() throws ConnectException
 71  
     {
 72  
         // If we already have an endpoint listening on this socket don't try and
 73  
         // start another serversocket
 74  0
         if (this.shouldConnect())
 75  
         {
 76  0
             super.doConnect();
 77  
         }
 78  0
     }
 79  
 
 80  
     protected boolean shouldConnect()
 81  
     {
 82  0
         StringBuffer requestUri = new StringBuffer(80);
 83  0
         requestUri.append(endpoint.getProtocol()).append("://");
 84  0
         requestUri.append(endpoint.getEndpointURI().getHost());
 85  0
         requestUri.append(':').append(endpoint.getEndpointURI().getPort());
 86  0
         requestUri.append('*');
 87  
 
 88  0
         UMOMessageReceiver[] receivers = connector.getReceivers(requestUri.toString());
 89  0
         for (int i = 0; i < receivers.length; i++)
 90  
         {
 91  0
             if (receivers[i].isConnected())
 92  
             {
 93  0
                 return false;
 94  
             }
 95  
         }
 96  
 
 97  0
         return true;
 98  
     }
 99  
 
 100  
 
 101  
     // @Override
 102  
     protected UMOMessage handleUnacceptedFilter(UMOMessage message)
 103  
     {
 104  0
         if(logger.isDebugEnabled())
 105  
         {
 106  0
             logger.debug("Message request '" + message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY)
 107  
                 + "' is being rejected since it does not match the filter on this endpoint: " + endpoint);
 108  
         }
 109  0
         message.setProperty(HttpConnector.HTTP_STATUS_PROPERTY, String.valueOf(HttpConstants.SC_NOT_ACCEPTABLE));
 110  0
         return message;
 111  
     }
 112  
 
 113  
     private class HttpWorker implements Work
 114  
     {
 115  0
         private HttpServerConnection conn = null;
 116  
         private String cookieSpec;
 117  0
         private boolean enableCookies = false;
 118  
 
 119  
         public HttpWorker(Socket socket) throws IOException
 120  0
         {
 121  0
             if (endpoint.getEncoding() != null)
 122  
             {
 123  0
                 conn = new HttpServerConnection(socket, endpoint.getEncoding());
 124  
             }
 125  
             else
 126  
             {
 127  0
                 conn = new HttpServerConnection(socket);
 128  
             }
 129  
 
 130  0
             cookieSpec =
 131  
                     MapUtils.getString(endpoint.getProperties(), HttpConnector.HTTP_COOKIE_SPEC_PROPERTY,
 132  
                             ((HttpConnector)connector).getCookieSpec());
 133  0
             enableCookies =
 134  
                     MapUtils.getBooleanValue(endpoint.getProperties(), HttpConnector.HTTP_ENABLE_COOKIES_PROPERTY,
 135  
                             ((HttpConnector)connector).isEnableCookies());
 136  0
         }
 137  
 
 138  
         public void run()
 139  
         {
 140  
             try
 141  
             {
 142  
                 do
 143  
                 {
 144  0
                     conn.setKeepAlive(false);
 145  0
                     HttpRequest request = conn.readRequest();
 146  0
                     if (request == null)
 147  
                     {
 148  0
                         break;
 149  
                     }
 150  0
                     conn.writeResponse(processRequest(request));
 151  
                 }
 152  0
                 while (conn.isKeepAlive());
 153  
             }
 154  0
             catch (Exception e)
 155  
             {
 156  0
                 handleException(e);
 157  
             }
 158  
             finally
 159  
             {
 160  0
                 conn.close();
 161  0
                 conn = null;
 162  0
             }
 163  0
         }
 164  
 
 165  
         protected HttpResponse processRequest(HttpRequest request) throws UMOException, IOException
 166  
         {
 167  0
             RequestLine requestLine = request.getRequestLine();
 168  0
             String method = requestLine.getMethod();
 169  
 
 170  0
             if (method.equals(HttpConstants.METHOD_HEAD))
 171  
             {
 172  0
                 return doHead(requestLine);
 173  
             }
 174  0
             else if (method.equals(HttpConstants.METHOD_GET)
 175  
                     || method.equals(HttpConstants.METHOD_POST))
 176  
             {
 177  0
                 return doGetOrPost(request, requestLine);
 178  
             }
 179  0
             else if (method.equals(HttpConstants.METHOD_OPTIONS)
 180  
                     || method.equals(HttpConstants.METHOD_PUT)
 181  
                     || method.equals(HttpConstants.METHOD_DELETE)
 182  
                     || method.equals(HttpConstants.METHOD_TRACE)
 183  
                     || method.equals(HttpConstants.METHOD_CONNECT))
 184  
             {
 185  0
                 return doOtherValid(requestLine, method);
 186  
             }
 187  
             else
 188  
             {
 189  0
                 return doBad(requestLine);
 190  
             }
 191  
         }
 192  
 
 193  
         protected HttpResponse doHead(RequestLine requestLine) throws UMOException
 194  
         {
 195  0
             UMOMessage message = new MuleMessage(NullPayload.getInstance());
 196  0
             UMOEvent event = new MuleEvent(message, endpoint, new MuleSession(message, new NullSessionHandler()), true);
 197  0
             OptimizedRequestContext.unsafeSetEvent(event);
 198  0
             HttpResponse response = new HttpResponse();
 199  0
             response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_OK);
 200  0
             response = (HttpResponse)connector.getDefaultResponseTransformer().transform(response);
 201  0
             return response;
 202  
         }
 203  
 
 204  
         protected HttpResponse doGetOrPost(HttpRequest request, RequestLine requestLine) throws IOException, UMOException
 205  
         {
 206  0
             Map headers = parseHeaders(request);
 207  
 
 208  
             // TODO Mule 2.0 generic way to set stream message adapter
 209  
             UMOMessageAdapter adapter;
 210  0
             if (endpoint.isStreaming() && request.getBody() != null)
 211  
             {
 212  0
                 adapter = buildStreamingAdapter(request, headers);
 213  
             }
 214  
             else
 215  
             {
 216  0
                 adapter = buildStandardAdapter(request, headers);
 217  
             }
 218  0
             UMOMessage message = new MuleMessage(adapter);
 219  
 
 220  0
             if (logger.isDebugEnabled())
 221  
             {
 222  0
                 logger.debug(message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY));
 223  
             }
 224  
 
 225  
             // determine if the request path on this request denotes a different receiver
 226  0
             UMOMessageReceiver receiver = getTargetReceiver(message, endpoint);
 227  
 
 228  
             HttpResponse response;
 229  
             // the response only needs to be transformed explicitly if
 230  
             // A) the request was not served or B) a null result was returned
 231  0
             if (receiver != null)
 232  
             {
 233  0
                 UMOMessage returnMessage = receiver.routeMessage(message, endpoint.isSynchronous(), null);
 234  
 
 235  
                 Object tempResponse;
 236  0
                 if (returnMessage != null)
 237  
                 {
 238  0
                     tempResponse = returnMessage.getPayload();
 239  
                 }
 240  
                 else
 241  
                 {
 242  0
                     tempResponse = NullPayload.getInstance();
 243  
                 }
 244  
                 // This removes the need for users to explicitly adding the response transformer
 245  
                 // ObjectToHttpResponse in their config
 246  0
                 if (tempResponse instanceof HttpResponse)
 247  
                 {
 248  0
                     response = (HttpResponse)tempResponse;
 249  
                 }
 250  
                 else
 251  
                 {
 252  0
                     response = (HttpResponse)connector.getDefaultResponseTransformer().transform(tempResponse);
 253  
                 }
 254  0
                 response.disableKeepAlive(!((HttpConnector)connector).isKeepAlive());
 255  
             }
 256  
             else
 257  
             {
 258  0
                 response = buildFailureResponse(requestLine, message);
 259  
             }
 260  0
             return response;
 261  
         }
 262  
 
 263  
         protected HttpResponse doOtherValid(RequestLine requestLine, String method) throws UMOException
 264  
         {
 265  0
             UMOMessage message = new MuleMessage(NullPayload.getInstance());
 266  0
             UMOEvent event = new MuleEvent(message, endpoint, new MuleSession(message, new NullSessionHandler()), true);
 267  0
             OptimizedRequestContext.unsafeSetEvent(event);
 268  0
             HttpResponse response = new HttpResponse();
 269  0
             response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_METHOD_NOT_ALLOWED);
 270  0
             response.setBodyString(HttpMessages.methodNotAllowed(method).toString() + HttpConstants.CRLF);
 271  0
             response = (HttpResponse)connector.getDefaultResponseTransformer().transform(response);
 272  0
             return response;
 273  
         }
 274  
 
 275  
         protected HttpResponse doBad(RequestLine requestLine) throws UMOException
 276  
         {
 277  0
             UMOMessage message = new MuleMessage(NullPayload.getInstance());
 278  0
             UMOEvent event = new MuleEvent(message, endpoint, new MuleSession(message, new NullSessionHandler()), true);
 279  0
             OptimizedRequestContext.unsafeSetEvent(event);
 280  0
             HttpResponse response = new HttpResponse();
 281  0
             response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_BAD_REQUEST);
 282  0
             response.setBodyString(HttpMessages.malformedSyntax().toString() + HttpConstants.CRLF);
 283  0
             response = (HttpResponse)connector.getDefaultResponseTransformer().transform(response);
 284  0
             return response;
 285  
         }
 286  
 
 287  
         protected UMOMessageAdapter buildStreamingAdapter(HttpRequest request, Map headers) throws MessagingException
 288  
         {
 289  0
             UMOMessageAdapter adapter = connector.getStreamMessageAdapter(request.getBody(), conn.getOutputStream());
 290  0
             for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();)
 291  
             {
 292  0
                 Map.Entry entry = (Map.Entry)iterator.next();
 293  0
                 adapter.setProperty((String)entry.getKey(), entry.getValue());
 294  
             }
 295  0
             return adapter;
 296  
         }
 297  
 
 298  
         protected UMOMessageAdapter buildStandardAdapter(HttpRequest request, Map headers) throws MessagingException, TransformerException, IOException
 299  
         {
 300  0
             RequestLine requestLine = request.getRequestLine();
 301  
             // respond with status code 100, for Expect handshake
 302  
             // according to rfc 2616 and http 1.1
 303  
             // the processing will continue and the request will be fully
 304  
             // read immediately after
 305  0
             if (HttpConstants.HTTP11.equals(headers.get(HttpConnector.HTTP_VERSION_PROPERTY)))
 306  
             {
 307  
                 // just in case we have something other than String in
 308  
                 // the headers map
 309  0
                 String expectHeaderValue = ObjectUtils.toString(
 310  
                         headers.get(HttpConstants.HEADER_EXPECT)).toLowerCase();
 311  0
                 if (HttpConstants.HEADER_EXPECT_CONTINUE_REQUEST_VALUE.equals(expectHeaderValue))
 312  
                 {
 313  0
                     HttpResponse expected = new HttpResponse();
 314  0
                     expected.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_CONTINUE);
 315  0
                     final MuleEvent event = new MuleEvent(new MuleMessage(expected), endpoint,
 316  
                             new MuleSession(component), true);
 317  0
                     RequestContext.setEvent(event);
 318  0
                     expected = (HttpResponse)connector.getDefaultResponseTransformer().transform(
 319  
                             expected);
 320  0
                     conn.writeResponse(expected);
 321  
                 }
 322  
             }
 323  
 
 324  0
             Object body = request.getBodyBytes();
 325  0
             if (body == null)
 326  
             {
 327  0
                 body = requestLine.getUri();
 328  
             }
 329  0
             return connector.getMessageAdapter(new Object[]{body, headers});
 330  
         }
 331  
 
 332  
         protected HttpResponse buildFailureResponse(RequestLine requestLine, UMOMessage message) throws TransformerException
 333  
         {
 334  0
             UMOEndpointURI uri = endpoint.getEndpointURI();
 335  0
             String failedPath = uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort()
 336  
                     + getRequestPath(message);
 337  
 
 338  0
             if (logger.isDebugEnabled())
 339  
             {
 340  0
                 logger.debug("Failed to bind to " + failedPath);
 341  
             }
 342  
 
 343  0
             HttpResponse response = new HttpResponse();
 344  0
             response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_NOT_FOUND);
 345  0
             response.setBodyString(HttpMessages.cannotBindToAddress(failedPath).toString());
 346  0
             RequestContext.setEvent(new MuleEvent(new MuleMessage(response), endpoint,
 347  
                     new MuleSession(component), true));
 348  
             // The DefaultResponseTransformer will set the necessary headers
 349  0
             return (HttpResponse)connector.getDefaultResponseTransformer().transform(response);
 350  
         }
 351  
 
 352  
         protected Map parseHeaders(HttpRequest request) throws MalformedCookieException
 353  
         {
 354  0
             RequestLine requestLine = request.getRequestLine();
 355  0
             Map headers = new HashMap();
 356  
 
 357  0
             for (Iterator rhi = request.getHeaderIterator(); rhi.hasNext();)
 358  
             {
 359  0
                 Header header = (Header)rhi.next();
 360  0
                 String headerName = header.getName();
 361  0
                 Object headerValue = header.getValue();
 362  
 
 363  
                 // fix Mule headers?
 364  0
                 if (headerName.startsWith("X-MULE"))
 365  
                 {
 366  0
                     headerName = headerName.substring(2);
 367  
                 }
 368  
                 // Parse cookies?
 369  0
                 else if (headerName.equals(HttpConnector.HTTP_COOKIES_PROPERTY))
 370  
                 {
 371  0
                     if (enableCookies)
 372  
                     {
 373  0
                         Cookie[] cookies = CookieHelper.parseCookies(header, cookieSpec);
 374  0
                         if (cookies.length > 0)
 375  
                         {
 376  
                             // yum!
 377  0
                             headerValue = cookies;
 378  
                         }
 379  
                         else
 380  
                         {
 381  
                             // bad cookies?!
 382  
                             continue;
 383  
                         }
 384  
                     }
 385  
                     else
 386  
                     {
 387  
                         // no cookies for you!
 388  
                         continue;
 389  
                     }
 390  
                 }
 391  
 
 392  
                 // accept header & value
 393  0
                 headers.put(headerName, headerValue);
 394  
             }
 395  
 
 396  0
             headers.put(HttpConnector.HTTP_METHOD_PROPERTY, requestLine.getMethod());
 397  0
             headers.put(HttpConnector.HTTP_REQUEST_PROPERTY, requestLine.getUri());
 398  0
             headers.put(HttpConnector.HTTP_VERSION_PROPERTY, requestLine.getHttpVersion().toString());
 399  0
             headers.put(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY, cookieSpec);
 400  0
             return headers;
 401  
         }
 402  
 
 403  
         public void release()
 404  
         {
 405  0
             conn.close();
 406  0
             conn = null;
 407  0
         }
 408  
 
 409  
     }
 410  
 
 411  
     protected String getRequestPath(UMOMessage message)
 412  
     {
 413  0
         String path = (String)message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
 414  0
         int i = path.indexOf('?');
 415  0
         if (i > -1)
 416  
         {
 417  0
             path = path.substring(0, i);
 418  
         }
 419  0
         return path;
 420  
     }
 421  
 
 422  
     protected UMOMessageReceiver getTargetReceiver(UMOMessage message, UMOEndpoint endpoint)
 423  
             throws ConnectException
 424  
     {
 425  0
         String path = (String)message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
 426  0
         int i = path.indexOf('?');
 427  0
         if (i > -1)
 428  
         {
 429  0
             path = path.substring(0, i);
 430  
         }
 431  
 
 432  0
         StringBuffer requestUri = new StringBuffer(80);
 433  0
         requestUri.append(endpoint.getProtocol()).append("://");
 434  0
         requestUri.append(endpoint.getEndpointURI().getHost());
 435  0
         requestUri.append(':').append(endpoint.getEndpointURI().getPort());
 436  
 
 437  
         // first check that there is a receiver on the root address
 438  0
         if (logger.isTraceEnabled())
 439  
         {
 440  0
             logger.trace("Looking up receiver on connector: " + connector.getName() + " with URI key: "
 441  
                          + requestUri.toString());
 442  
         }
 443  
 
 444  0
         UMOMessageReceiver receiver = connector.lookupReceiver(requestUri.toString());
 445  
 
 446  
         // If no receiver on the root and there is a request path, look up the
 447  
         // received based on the root plus request path
 448  0
         if (receiver == null && !"/".equals(path))
 449  
         {
 450  
             // remove anything after the last '/'
 451  0
             int x = path.lastIndexOf('/');
 452  0
             if (x > 1 && path.indexOf('.') > x)
 453  
             {
 454  0
                 requestUri.append(path.substring(0, x));
 455  
             }
 456  
             else
 457  
             {
 458  0
                 requestUri.append(path);
 459  
             }
 460  
 
 461  0
             if (logger.isTraceEnabled())
 462  
             {
 463  0
                 logger.trace("Secondary lookup of receiver on connector: " + connector.getName()
 464  
                              + " with URI key: " + requestUri.toString());
 465  
             }
 466  
 
 467  
             // try again
 468  0
             receiver = connector.lookupReceiver(requestUri.toString());
 469  0
             if (receiver == null && logger.isWarnEnabled())
 470  
             {
 471  0
                 logger.warn("No receiver found with secondary lookup on connector: " + connector.getName()
 472  
                             + " with URI key: " + requestUri.toString());
 473  0
                 logger.warn("Receivers on connector are: "
 474  
                             + MapUtils.toString(connector.getReceivers(), true));
 475  
             }
 476  
         }
 477  
 
 478  0
         return receiver;
 479  
     }
 480  
 
 481  
 }