1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.http;
12
13 import org.mule.DefaultMuleEvent;
14 import org.mule.DefaultMuleMessage;
15 import org.mule.DefaultMuleSession;
16 import org.mule.MuleServer;
17 import org.mule.NullSessionHandler;
18 import org.mule.OptimizedRequestContext;
19 import org.mule.RequestContext;
20 import org.mule.api.MessagingException;
21 import org.mule.api.MuleEvent;
22 import org.mule.api.MuleException;
23 import org.mule.api.MuleMessage;
24 import org.mule.api.config.MuleProperties;
25 import org.mule.api.endpoint.EndpointURI;
26 import org.mule.api.endpoint.ImmutableEndpoint;
27 import org.mule.api.endpoint.InboundEndpoint;
28 import org.mule.api.lifecycle.CreateException;
29 import org.mule.api.service.Service;
30 import org.mule.api.transformer.TransformerException;
31 import org.mule.api.transport.Connector;
32 import org.mule.api.transport.MessageAdapter;
33 import org.mule.api.transport.MessageReceiver;
34 import org.mule.transport.ConnectException;
35 import org.mule.transport.NullPayload;
36 import org.mule.transport.http.i18n.HttpMessages;
37 import org.mule.transport.tcp.TcpConnector;
38 import org.mule.transport.tcp.TcpMessageReceiver;
39 import org.mule.util.MapUtils;
40 import org.mule.util.ObjectUtils;
41 import org.mule.util.monitor.Expirable;
42
43 import java.io.IOException;
44 import java.net.Socket;
45 import java.net.SocketAddress;
46 import java.util.HashMap;
47 import java.util.Iterator;
48 import java.util.Map;
49
50 import javax.resource.spi.work.Work;
51
52 import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
53
54 import org.apache.commons.httpclient.Cookie;
55 import org.apache.commons.httpclient.Header;
56 import org.apache.commons.httpclient.cookie.MalformedCookieException;
57 import org.apache.commons.logging.Log;
58 import org.apache.commons.logging.LogFactory;
59
60
61
62
63
64 public class HttpMessageReceiver extends TcpMessageReceiver
65 {
66 protected final Log logger = LogFactory.getLog(getClass());
67
68 public HttpMessageReceiver(Connector connector, Service service, InboundEndpoint endpoint)
69 throws CreateException
70 {
71 super(connector, service, endpoint);
72 }
73
74
75 protected Work createWork(Socket socket) throws IOException
76 {
77 return new HttpWorker(socket);
78 }
79
80
81 protected void doConnect() throws ConnectException
82 {
83
84
85 if (this.shouldConnect())
86 {
87 super.doConnect();
88 }
89 }
90
91 protected boolean shouldConnect()
92 {
93 StringBuffer requestUri = new StringBuffer(80);
94 requestUri.append(endpoint.getProtocol()).append("://");
95 requestUri.append(endpoint.getEndpointURI().getHost());
96 requestUri.append(':').append(endpoint.getEndpointURI().getPort());
97 requestUri.append('*');
98
99 MessageReceiver[] receivers = connector.getReceivers(requestUri.toString());
100 for (int i = 0; i < receivers.length; i++)
101 {
102 if (receivers[i].isConnected())
103 {
104 return false;
105 }
106 }
107
108 return true;
109 }
110
111
112
113 protected MuleMessage handleUnacceptedFilter(MuleMessage message)
114 {
115 if (logger.isDebugEnabled())
116 {
117 logger.debug("Message request '" + message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY)
118 + "' is being rejected since it does not match the filter on this endpoint: " + endpoint);
119 }
120 message.setProperty(HttpConnector.HTTP_STATUS_PROPERTY, String.valueOf(HttpConstants.SC_NOT_ACCEPTABLE));
121 return message;
122 }
123
124 protected class HttpWorker implements Work, Expirable
125 {
126 private HttpServerConnection conn;
127 private String cookieSpec;
128 private boolean enableCookies;
129 private String remoteClientAddress;
130
131 public HttpWorker(Socket socket) throws IOException
132 {
133 String encoding = endpoint.getEncoding();
134 if (encoding == null)
135 {
136 encoding = MuleServer.getMuleContext().getConfiguration().getDefaultEncoding();
137 }
138
139 conn = new HttpServerConnection(socket, encoding, (HttpConnector) connector);
140
141 cookieSpec =
142 MapUtils.getString(endpoint.getProperties(), HttpConnector.HTTP_COOKIE_SPEC_PROPERTY,
143 ((HttpConnector) connector).getCookieSpec());
144 enableCookies =
145 MapUtils.getBooleanValue(endpoint.getProperties(), HttpConnector.HTTP_ENABLE_COOKIES_PROPERTY,
146 ((HttpConnector) connector).isEnableCookies());
147
148 final SocketAddress clientAddress = socket.getRemoteSocketAddress();
149 if (clientAddress != null)
150 {
151 remoteClientAddress = clientAddress.toString();
152 }
153 }
154
155 public void expired()
156 {
157 if (conn.isOpen())
158 {
159 conn.close();
160 }
161 }
162
163 public void run()
164 {
165 long keepAliveTimeout = ((TcpConnector) connector).getKeepAliveTimeout();
166
167 try
168 {
169 do
170 {
171 conn.setKeepAlive(false);
172
173
174 if(keepAliveTimeout > 0)
175 {
176 ((HttpConnector) connector).getKeepAliveMonitor().addExpirable(
177 keepAliveTimeout, TimeUnit.MILLISECONDS, this);
178 }
179
180 HttpRequest request = conn.readRequest();
181 if (request == null)
182 {
183 break;
184 }
185
186
187 ((HttpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
188
189 conn.writeResponse(processRequest(request));
190 }
191 while (conn.isKeepAlive());
192 }
193 catch (Exception e)
194 {
195 handleException(e);
196 }
197 finally
198 {
199 logger.info("Closing HTTP connection.");
200
201 if (conn.isOpen())
202 {
203 conn.close();
204 conn = null;
205
206
207 ((HttpConnector) connector).getKeepAliveMonitor().removeExpirable(this);
208 }
209 }
210 }
211
212 protected HttpResponse processRequest(HttpRequest request) throws MuleException, IOException
213 {
214 RequestLine requestLine = request.getRequestLine();
215 String method = requestLine.getMethod();
216
217 if (method.equals(HttpConstants.METHOD_HEAD))
218 {
219 return doHead(requestLine);
220 }
221 else if (method.equals(HttpConstants.METHOD_GET)
222 || method.equals(HttpConstants.METHOD_POST)
223 || method.equals(HttpConstants.METHOD_OPTIONS)
224 || method.equals(HttpConstants.METHOD_PUT)
225 || method.equals(HttpConstants.METHOD_DELETE)
226 || method.equals(HttpConstants.METHOD_TRACE)
227 || method.equals(HttpConstants.METHOD_CONNECT))
228 {
229 return doRequest(request, requestLine);
230 }
231 else
232 {
233 return doBad(requestLine);
234 }
235 }
236
237 protected HttpResponse doHead(RequestLine requestLine) throws MuleException
238 {
239 MuleMessage message = new DefaultMuleMessage(NullPayload.getInstance());
240 MuleEvent event = new DefaultMuleEvent(message, endpoint, new DefaultMuleSession(message, new NullSessionHandler(), connector.getMuleContext()), true);
241 OptimizedRequestContext.unsafeSetEvent(event);
242 HttpResponse response = new HttpResponse();
243 response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_OK);
244 return transformResponse(response);
245 }
246
247 protected HttpResponse doRequest(HttpRequest request,
248 RequestLine requestLine) throws IOException, MuleException
249 {
250 Map headers = parseHeaders(request);
251
252
253 MessageAdapter adapter = buildStandardAdapter(request, headers);
254
255 MuleMessage message = new DefaultMuleMessage(adapter);
256
257 if (logger.isDebugEnabled())
258 {
259 logger.debug(message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY));
260 }
261
262
263 MessageReceiver receiver = getTargetReceiver(message, endpoint);
264
265 HttpResponse response;
266
267
268 if (receiver != null)
269 {
270 preRouteMessage(message);
271 MuleMessage returnMessage = receiver.routeMessage(message, endpoint.isSynchronous(), null);
272
273 Object tempResponse;
274 if (returnMessage != null)
275 {
276 tempResponse = returnMessage.getPayload();
277 }
278 else
279 {
280 tempResponse = NullPayload.getInstance();
281 }
282
283
284 if (tempResponse instanceof HttpResponse)
285 {
286 response = (HttpResponse)tempResponse;
287 }
288 else
289 {
290 response = transformResponse(returnMessage);
291 }
292 response.disableKeepAlive(!((HttpConnector)connector).isKeepAlive());
293 }
294 else
295 {
296 response = buildFailureResponse(requestLine, message);
297 }
298 return response;
299 }
300
301 protected HttpResponse doOtherValid(RequestLine requestLine, String method) throws MuleException
302 {
303 MuleMessage message = new DefaultMuleMessage(NullPayload.getInstance());
304 MuleEvent event = new DefaultMuleEvent(message, endpoint, new DefaultMuleSession(message, new NullSessionHandler(), connector.getMuleContext()), true);
305 OptimizedRequestContext.unsafeSetEvent(event);
306 HttpResponse response = new HttpResponse();
307 response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_METHOD_NOT_ALLOWED);
308 response.setBody(HttpMessages.methodNotAllowed(method).toString() + HttpConstants.CRLF);
309 return transformResponse(response);
310 }
311
312 protected HttpResponse doBad(RequestLine requestLine) throws MuleException
313 {
314 MuleMessage message = new DefaultMuleMessage(NullPayload.getInstance());
315 MuleEvent event = new DefaultMuleEvent(message, endpoint, new DefaultMuleSession(message, new NullSessionHandler(), connector.getMuleContext()), true);
316 OptimizedRequestContext.unsafeSetEvent(event);
317 HttpResponse response = new HttpResponse();
318 response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_BAD_REQUEST);
319 response.setBody(HttpMessages.malformedSyntax().toString() + HttpConstants.CRLF);
320 return transformResponse(response);
321 }
322
323 protected MessageAdapter buildStandardAdapter(final HttpRequest request,
324 final Map headers) throws MessagingException, TransformerException, IOException
325 {
326 final RequestLine requestLine = request.getRequestLine();
327
328 sendExpect100(headers, requestLine);
329
330 Object body = request.getBody();
331 if (body == null)
332 {
333 body = requestLine.getUri();
334 }
335
336 return connector.getMessageAdapter(new Object[]{body, headers});
337 }
338
339 private void sendExpect100(Map headers, RequestLine requestLine)
340 throws TransformerException, IOException
341 {
342
343
344
345
346 if (HttpConstants.HTTP11.equals(headers.get(HttpConnector.HTTP_VERSION_PROPERTY)))
347 {
348
349
350 String expectHeaderValue = ObjectUtils.toString(
351 headers.get(HttpConstants.HEADER_EXPECT)).toLowerCase();
352 if (HttpConstants.HEADER_EXPECT_CONTINUE_REQUEST_VALUE.equals(expectHeaderValue))
353 {
354 HttpResponse expected = new HttpResponse();
355 expected.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_CONTINUE);
356 final DefaultMuleEvent event = new DefaultMuleEvent(new DefaultMuleMessage(expected), endpoint,
357 new DefaultMuleSession(service, connector.getMuleContext()), true);
358 RequestContext.setEvent(event);
359 conn.writeResponse(transformResponse(expected));
360 }
361 }
362 }
363
364 protected HttpResponse buildFailureResponse(RequestLine requestLine, MuleMessage message) throws TransformerException
365 {
366 EndpointURI uri = endpoint.getEndpointURI();
367 String failedPath = uri.getScheme() + "://" + uri.getHost() + ":" + uri.getPort()
368 + getRequestPath(message);
369
370 if (logger.isDebugEnabled())
371 {
372 logger.debug("Failed to bind to " + failedPath);
373 }
374
375 HttpResponse response = new HttpResponse();
376 response.setStatusLine(requestLine.getHttpVersion(), HttpConstants.SC_NOT_FOUND);
377 response.setBody(HttpMessages.cannotBindToAddress(failedPath).toString());
378 RequestContext.setEvent(new DefaultMuleEvent(new DefaultMuleMessage(response), endpoint,
379 new DefaultMuleSession(service, connector.getMuleContext()), true));
380
381 return transformResponse(response);
382 }
383
384 protected Map parseHeaders(HttpRequest request) throws MalformedCookieException
385 {
386 RequestLine requestLine = request.getRequestLine();
387 Map headers = new HashMap();
388
389 for (Iterator rhi = request.getHeaderIterator(); rhi.hasNext();)
390 {
391 Header header = (Header)rhi.next();
392 String headerName = header.getName();
393 Object headerValue = header.getValue();
394
395
396 if (headerName.startsWith("X-MULE"))
397 {
398 headerName = headerName.substring(2);
399 }
400
401 else if (headerName.equals(HttpConnector.HTTP_COOKIES_PROPERTY))
402 {
403 if (enableCookies)
404 {
405 Cookie[] cookies = CookieHelper.parseCookies(header, cookieSpec);
406 if (cookies.length > 0)
407 {
408
409 headerValue = cookies;
410 }
411 else
412 {
413
414 continue;
415 }
416 }
417 else
418 {
419
420 continue;
421 }
422 }
423
424
425 headers.put(headerName, headerValue);
426 }
427
428 headers.put(HttpConnector.HTTP_METHOD_PROPERTY, requestLine.getMethod());
429 headers.put(HttpConnector.HTTP_REQUEST_PROPERTY, requestLine.getUri());
430 headers.put(HttpConnector.HTTP_VERSION_PROPERTY, requestLine.getHttpVersion().toString());
431 headers.put(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY, cookieSpec);
432 return headers;
433 }
434
435 protected void preRouteMessage(MuleMessage message)
436 {
437 message.setProperty(MuleProperties.MULE_REMOTE_CLIENT_ADDRESS, remoteClientAddress);
438 }
439
440 public void release()
441 {
442 conn.close();
443 conn = null;
444 }
445 }
446
447 protected String getRequestPath(MuleMessage message)
448 {
449 String path = (String) message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
450 int i = path.indexOf('?');
451 if (i > -1)
452 {
453 path = path.substring(0, i);
454 }
455 return path;
456 }
457
458 protected MessageReceiver getTargetReceiver(MuleMessage message, ImmutableEndpoint endpoint)
459 throws ConnectException
460 {
461 String path = (String) message.getProperty(HttpConnector.HTTP_REQUEST_PROPERTY);
462 int i = path.indexOf('?');
463 if (i > -1)
464 {
465 path = path.substring(0, i);
466 }
467
468 StringBuffer requestUri = new StringBuffer(80);
469 if (path.indexOf("://")==-1)
470 {
471 requestUri.append(endpoint.getProtocol()).append("://");
472 requestUri.append(endpoint.getEndpointURI().getHost());
473 requestUri.append(':').append(endpoint.getEndpointURI().getPort());
474 }
475
476 if (logger.isTraceEnabled())
477 {
478 logger.trace("Looking up receiver on connector: " + connector.getName() + " with URI key: "
479 + requestUri.toString());
480 }
481
482 MessageReceiver receiver = connector.lookupReceiver(requestUri.toString());
483
484
485
486 if (receiver == null && !"/".equals(path))
487 {
488
489 int x = path.lastIndexOf('/');
490 if (x > 1 && path.indexOf('.') > x)
491 {
492 requestUri.append(path.substring(0, x));
493 }
494 else
495 {
496 requestUri.append(path);
497 }
498
499 if (logger.isDebugEnabled())
500 {
501 logger.debug("Secondary lookup of receiver on connector: " + connector.getName()
502 + " with URI key: " + requestUri.toString());
503 }
504
505
506 String uriStr = requestUri.toString();
507 receiver = connector.lookupReceiver(uriStr);
508
509 if (receiver == null)
510 {
511 receiver = findReceiverByStem(connector.getReceivers(), uriStr);
512 }
513
514 if (receiver == null && logger.isWarnEnabled())
515 {
516 logger.warn("No receiver found with secondary lookup on connector: " + connector.getName()
517 + " with URI key: " + requestUri.toString());
518 logger.warn("Receivers on connector are: "
519 + MapUtils.toString(connector.getReceivers(), true));
520 }
521 }
522
523 return receiver;
524 }
525
526 protected HttpResponse transformResponse(Object response) throws TransformerException
527 {
528 MuleMessage message;
529 if(response instanceof MuleMessage)
530 {
531 message = (MuleMessage)response;
532 }
533 else
534 {
535 message = new DefaultMuleMessage(response);
536 }
537
538
539 message.applyTransformers(connector.getDefaultResponseTransformers(), HttpResponse.class);
540 return (HttpResponse) message.getPayload();
541 }
542
543 public static MessageReceiver findReceiverByStem(Map receivers, String uriStr)
544 {
545 int match = 0;
546 MessageReceiver receiver = null;
547 for (Iterator itr = receivers.entrySet().iterator(); itr.hasNext();)
548 {
549 Map.Entry e = (Map.Entry)itr.next();
550 String key = (String)e.getKey();
551 MessageReceiver candidate = (MessageReceiver)e.getValue();
552 if (uriStr.startsWith(key) && match < key.length())
553 {
554 match = key.length();
555 receiver = candidate;
556 }
557 }
558 return receiver;
559 }
560
561 }