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