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