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