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