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