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