1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
package org.mule.transport.http; |
12 | |
|
13 | |
import org.mule.api.ExceptionPayload; |
14 | |
import org.mule.api.MuleEvent; |
15 | |
import org.mule.api.MuleException; |
16 | |
import org.mule.api.MuleMessage; |
17 | |
import org.mule.api.endpoint.OutboundEndpoint; |
18 | |
import org.mule.api.lifecycle.InitialisationException; |
19 | |
import org.mule.api.transformer.DataType; |
20 | |
import org.mule.api.transformer.Transformer; |
21 | |
import org.mule.api.transformer.TransformerException; |
22 | |
import org.mule.api.transport.DispatchException; |
23 | |
import org.mule.api.transport.OutputHandler; |
24 | |
import org.mule.endpoint.EndpointURIEndpointBuilder; |
25 | |
import org.mule.message.DefaultExceptionPayload; |
26 | |
import org.mule.transformer.TransformerChain; |
27 | |
import org.mule.transformer.types.DataTypeFactory; |
28 | |
import org.mule.transport.AbstractMessageDispatcher; |
29 | |
import org.mule.transport.http.transformers.ObjectToHttpClientMethodRequest; |
30 | |
import org.mule.util.StringUtils; |
31 | |
|
32 | |
import java.io.IOException; |
33 | |
import java.net.URI; |
34 | |
import java.util.List; |
35 | |
|
36 | |
import org.apache.commons.httpclient.Header; |
37 | |
import org.apache.commons.httpclient.HostConfiguration; |
38 | |
import org.apache.commons.httpclient.HttpClient; |
39 | |
import org.apache.commons.httpclient.HttpMethod; |
40 | |
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; |
41 | |
import org.apache.commons.httpclient.methods.EntityEnclosingMethod; |
42 | |
import org.apache.commons.httpclient.params.HttpMethodParams; |
43 | |
import org.apache.commons.httpclient.protocol.Protocol; |
44 | |
import org.apache.commons.lang.BooleanUtils; |
45 | |
|
46 | |
|
47 | |
|
48 | |
|
49 | |
public class HttpClientMessageDispatcher extends AbstractMessageDispatcher |
50 | |
{ |
51 | |
|
52 | |
|
53 | |
|
54 | |
public static final int ERROR_STATUS_CODE_RANGE_START = 400; |
55 | |
public static final int REDIRECT_STATUS_CODE_RANGE_START = 300; |
56 | |
protected final HttpConnector connector; |
57 | 0 | private volatile HttpClient client = null; |
58 | |
private final Transformer sendTransformer; |
59 | |
|
60 | |
public HttpClientMessageDispatcher(OutboundEndpoint endpoint) |
61 | |
{ |
62 | 0 | super(endpoint); |
63 | 0 | this.connector = (HttpConnector) endpoint.getConnector(); |
64 | 0 | List<Transformer> ts = connector.getDefaultOutboundTransformers(null); |
65 | 0 | if (ts.size() == 1) |
66 | |
{ |
67 | 0 | this.sendTransformer = ts.get(0); |
68 | |
} |
69 | 0 | else if (ts.size() == 0) |
70 | |
{ |
71 | 0 | this.sendTransformer = new ObjectToHttpClientMethodRequest(); |
72 | 0 | this.sendTransformer.setMuleContext(connector.getMuleContext()); |
73 | 0 | this.sendTransformer.setEndpoint(endpoint); |
74 | |
} |
75 | |
else |
76 | |
{ |
77 | 0 | this.sendTransformer = new TransformerChain(ts); |
78 | |
} |
79 | 0 | } |
80 | |
|
81 | |
@Override |
82 | |
protected void doInitialise() throws InitialisationException |
83 | |
{ |
84 | 0 | super.doInitialise(); |
85 | 0 | sendTransformer.initialise(); |
86 | 0 | } |
87 | |
|
88 | |
@Override |
89 | |
protected void doConnect() throws Exception |
90 | |
{ |
91 | 0 | if (client == null) |
92 | |
{ |
93 | 0 | client = connector.doClientConnect(); |
94 | |
} |
95 | 0 | } |
96 | |
|
97 | |
@Override |
98 | |
protected void doDisconnect() throws Exception |
99 | |
{ |
100 | 0 | client = null; |
101 | 0 | } |
102 | |
|
103 | |
@Override |
104 | |
protected void doDispatch(MuleEvent event) throws Exception |
105 | |
{ |
106 | 0 | HttpMethod httpMethod = getMethod(event); |
107 | 0 | connector.setupClientAuthorization(event, httpMethod, client, endpoint); |
108 | |
|
109 | |
try |
110 | |
{ |
111 | 0 | execute(event, httpMethod); |
112 | |
|
113 | 0 | if (returnException(event, httpMethod)) |
114 | |
{ |
115 | 0 | logger.error(httpMethod.getResponseBodyAsString()); |
116 | |
|
117 | 0 | Exception cause = new Exception(String.format("Http call returned a status of: %1d %1s", |
118 | |
httpMethod.getStatusCode(), httpMethod.getStatusText())); |
119 | 0 | throw new DispatchException(event, (OutboundEndpoint) endpoint, cause); |
120 | |
} |
121 | 0 | else if (httpMethod.getStatusCode() >= REDIRECT_STATUS_CODE_RANGE_START) |
122 | |
{ |
123 | 0 | if (logger.isInfoEnabled()) |
124 | |
{ |
125 | 0 | logger.info("Received a redirect response code: " + httpMethod.getStatusCode() + " " + httpMethod.getStatusText()); |
126 | |
} |
127 | |
} |
128 | |
} |
129 | |
finally |
130 | |
{ |
131 | 0 | httpMethod.releaseConnection(); |
132 | 0 | } |
133 | 0 | } |
134 | |
|
135 | |
protected HttpMethod execute(MuleEvent event, HttpMethod httpMethod) throws Exception |
136 | |
{ |
137 | |
|
138 | |
try |
139 | |
{ |
140 | 0 | URI uri = event.getEndpoint().getEndpointURI().getUri(); |
141 | |
|
142 | 0 | this.processCookies(event); |
143 | |
|
144 | |
|
145 | 0 | client.executeMethod(getHostConfig(uri), httpMethod); |
146 | |
|
147 | 0 | return httpMethod; |
148 | |
} |
149 | 0 | catch (IOException e) |
150 | |
{ |
151 | |
|
152 | 0 | throw new DispatchException(event, (OutboundEndpoint) endpoint, e); |
153 | |
} |
154 | 0 | catch (Exception e) |
155 | |
{ |
156 | 0 | throw new DispatchException(event, (OutboundEndpoint) endpoint, e); |
157 | |
} |
158 | |
|
159 | |
} |
160 | |
|
161 | |
protected void processCookies(MuleEvent event) |
162 | |
{ |
163 | 0 | MuleMessage msg = event.getMessage(); |
164 | |
|
165 | 0 | Object cookiesProperty = msg.getOutboundProperty(HttpConnector.HTTP_COOKIES_PROPERTY); |
166 | 0 | String cookieSpecProperty = (String) msg.getOutboundProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY); |
167 | 0 | processCookies(cookiesProperty, cookieSpecProperty, event); |
168 | |
|
169 | 0 | cookiesProperty = endpoint.getProperty(HttpConnector.HTTP_COOKIES_PROPERTY); |
170 | 0 | cookieSpecProperty = (String) endpoint.getProperty(HttpConnector.HTTP_COOKIE_SPEC_PROPERTY); |
171 | 0 | processCookies(cookiesProperty, cookieSpecProperty, event); |
172 | 0 | } |
173 | |
|
174 | |
private void processCookies(Object cookieObject, String policy, MuleEvent event) |
175 | |
{ |
176 | 0 | URI uri = this.getEndpoint().getEndpointURI().getUri(); |
177 | 0 | CookieHelper.addCookiesToClient(this.client, cookieObject, policy, event, uri); |
178 | 0 | } |
179 | |
|
180 | |
protected HttpMethod getMethod(MuleEvent event) throws TransformerException |
181 | |
{ |
182 | |
|
183 | |
|
184 | |
|
185 | |
|
186 | 0 | client.getHttpConnectionManager().getParams().setConnectionTimeout(event.getTimeout()); |
187 | 0 | client.getHttpConnectionManager().getParams().setSoTimeout(event.getTimeout()); |
188 | |
|
189 | 0 | MuleMessage msg = event.getMessage(); |
190 | 0 | setPropertyFromEndpoint(event, msg, HttpConnector.HTTP_CUSTOM_HEADERS_MAP_PROPERTY); |
191 | |
|
192 | |
HttpMethod httpMethod; |
193 | 0 | Object body = event.getMessage().getPayload(); |
194 | |
|
195 | 0 | if (body instanceof HttpMethod) |
196 | |
{ |
197 | 0 | httpMethod = (HttpMethod) body; |
198 | |
} |
199 | |
else |
200 | |
{ |
201 | 0 | httpMethod = (HttpMethod) sendTransformer.transform(msg); |
202 | |
} |
203 | |
|
204 | 0 | httpMethod.setFollowRedirects("true".equalsIgnoreCase((String)endpoint.getProperty("followRedirects"))); |
205 | 0 | return httpMethod; |
206 | |
} |
207 | |
|
208 | |
protected void setPropertyFromEndpoint(MuleEvent event, MuleMessage msg, String prop) |
209 | |
{ |
210 | 0 | Object o = msg.getOutboundProperty(prop); |
211 | 0 | if (o == null) |
212 | |
{ |
213 | 0 | o = event.getEndpoint().getProperty(prop); |
214 | 0 | if (o != null) |
215 | |
{ |
216 | 0 | msg.setOutboundProperty(prop, o); |
217 | |
} |
218 | |
} |
219 | 0 | } |
220 | |
|
221 | |
protected HttpMethod createEntityMethod(MuleEvent event, Object body, EntityEnclosingMethod postMethod) throws TransformerException |
222 | |
{ |
223 | |
HttpMethod httpMethod; |
224 | 0 | if (body instanceof String) |
225 | |
{ |
226 | 0 | httpMethod = (HttpMethod) sendTransformer.transform(body.toString()); |
227 | |
} |
228 | 0 | else if (body instanceof byte[]) |
229 | |
{ |
230 | 0 | byte[] buffer = event.transformMessage(DataType.BYTE_ARRAY_DATA_TYPE); |
231 | 0 | postMethod.setRequestEntity(new ByteArrayRequestEntity(buffer, event.getEncoding())); |
232 | 0 | httpMethod = postMethod; |
233 | 0 | } |
234 | |
else |
235 | |
{ |
236 | 0 | if (!(body instanceof OutputHandler)) |
237 | |
{ |
238 | 0 | body = event.transformMessage(DataTypeFactory.create(OutputHandler.class)); |
239 | |
} |
240 | |
|
241 | 0 | OutputHandler outputHandler = (OutputHandler) body; |
242 | 0 | postMethod.setRequestEntity(new StreamPayloadRequestEntity(outputHandler, event)); |
243 | 0 | postMethod.setContentChunked(true); |
244 | 0 | httpMethod = postMethod; |
245 | |
} |
246 | |
|
247 | 0 | return httpMethod; |
248 | |
} |
249 | |
|
250 | |
@Override |
251 | |
protected MuleMessage doSend(MuleEvent event) throws Exception |
252 | |
{ |
253 | 0 | HttpMethod httpMethod = getMethod(event); |
254 | 0 | connector.setupClientAuthorization(event, httpMethod, client, endpoint); |
255 | |
|
256 | 0 | httpMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new MuleHttpMethodRetryHandler()); |
257 | 0 | boolean releaseConn = false; |
258 | |
try |
259 | |
{ |
260 | 0 | httpMethod = execute(event, httpMethod); |
261 | |
|
262 | 0 | DefaultExceptionPayload ep = null; |
263 | |
|
264 | 0 | if (returnException(event, httpMethod)) |
265 | |
{ |
266 | 0 | ep = new DefaultExceptionPayload(new DispatchException(event, (OutboundEndpoint) endpoint, |
267 | |
new HttpResponseException(httpMethod.getStatusText(), httpMethod.getStatusCode()))); |
268 | |
} |
269 | 0 | else if (httpMethod.getStatusCode() >= REDIRECT_STATUS_CODE_RANGE_START) |
270 | |
{ |
271 | |
try |
272 | |
{ |
273 | 0 | return handleRedirect(httpMethod, event); |
274 | |
} |
275 | 0 | catch (Exception e) |
276 | |
{ |
277 | 0 | ep = new DefaultExceptionPayload(new DispatchException(event, (OutboundEndpoint) endpoint, e)); |
278 | 0 | return getResponseFromMethod(httpMethod, ep); |
279 | |
} |
280 | |
} |
281 | 0 | releaseConn = httpMethod.getResponseBodyAsStream() == null; |
282 | 0 | return getResponseFromMethod(httpMethod, ep); |
283 | |
} |
284 | 0 | catch (Exception e) |
285 | |
{ |
286 | 0 | releaseConn = true; |
287 | 0 | if (e instanceof DispatchException) |
288 | |
{ |
289 | 0 | throw (DispatchException) e; |
290 | |
} |
291 | 0 | throw new DispatchException(event, (OutboundEndpoint) endpoint, e); |
292 | |
} |
293 | |
finally |
294 | |
{ |
295 | 0 | if (releaseConn) |
296 | |
{ |
297 | 0 | httpMethod.releaseConnection(); |
298 | |
} |
299 | |
} |
300 | |
} |
301 | |
|
302 | |
protected MuleMessage handleRedirect(HttpMethod method, MuleEvent event) throws HttpResponseException, MuleException, IOException |
303 | |
{ |
304 | 0 | String followRedirects = (String)endpoint.getProperty("followRedirects"); |
305 | 0 | if (followRedirects==null || "false".equalsIgnoreCase(followRedirects)) |
306 | |
{ |
307 | 0 | if (logger.isInfoEnabled()) |
308 | |
{ |
309 | 0 | logger.info("Received a redirect, but followRedirects=false. Response code: " + method.getStatusCode() + " " + method.getStatusText()); |
310 | |
} |
311 | 0 | return getResponseFromMethod(method, null); |
312 | |
} |
313 | 0 | Header locationHeader = method.getResponseHeader(HttpConstants.HEADER_LOCATION); |
314 | 0 | if (locationHeader == null) |
315 | |
{ |
316 | 0 | throw new HttpResponseException(method.getStatusText(), method.getStatusCode()); |
317 | |
} |
318 | 0 | OutboundEndpoint out = new EndpointURIEndpointBuilder(locationHeader.getValue(), |
319 | |
connector.getMuleContext()).buildOutboundEndpoint(); |
320 | 0 | MuleEvent result = out.process(event); |
321 | 0 | if (result != null) |
322 | |
{ |
323 | 0 | return result.getMessage(); |
324 | |
} |
325 | |
else |
326 | |
{ |
327 | 0 | return null; |
328 | |
} |
329 | |
} |
330 | |
|
331 | |
protected MuleMessage getResponseFromMethod(HttpMethod httpMethod, ExceptionPayload ep) |
332 | |
throws IOException, MuleException |
333 | |
{ |
334 | 0 | MuleMessage message = createMuleMessage(httpMethod); |
335 | |
|
336 | 0 | if (logger.isDebugEnabled()) |
337 | |
{ |
338 | 0 | logger.debug("Http response is: " + message.getOutboundProperty(HttpConnector.HTTP_STATUS_PROPERTY)); |
339 | |
} |
340 | |
|
341 | 0 | message.setExceptionPayload(ep); |
342 | 0 | return message; |
343 | |
} |
344 | |
|
345 | |
protected boolean returnException(MuleEvent event, HttpMethod httpMethod) |
346 | |
{ |
347 | 0 | String disableCheck = event.getMessage().getInvocationProperty(HttpConnector.HTTP_DISABLE_STATUS_CODE_EXCEPTION_CHECK); |
348 | 0 | if (disableCheck == null) |
349 | |
{ |
350 | 0 | disableCheck = event.getMessage().getOutboundProperty(HttpConnector.HTTP_DISABLE_STATUS_CODE_EXCEPTION_CHECK); |
351 | |
} |
352 | 0 | return httpMethod.getStatusCode() >= ERROR_STATUS_CODE_RANGE_START |
353 | |
&& !BooleanUtils.toBoolean(disableCheck); |
354 | |
} |
355 | |
|
356 | |
protected HostConfiguration getHostConfig(URI uri) throws Exception |
357 | |
{ |
358 | 0 | Protocol protocol = Protocol.getProtocol(uri.getScheme().toLowerCase()); |
359 | |
|
360 | 0 | String host = uri.getHost(); |
361 | 0 | int port = uri.getPort(); |
362 | 0 | HostConfiguration config = new HostConfiguration(); |
363 | 0 | config.setHost(host, port, protocol); |
364 | 0 | if (StringUtils.isNotBlank(connector.getProxyHostname())) |
365 | |
{ |
366 | |
|
367 | 0 | config.setProxy(connector.getProxyHostname(), connector.getProxyPort()); |
368 | |
} |
369 | 0 | return config; |
370 | |
} |
371 | |
|
372 | |
@Override |
373 | |
protected void doDispose() |
374 | |
{ |
375 | |
|
376 | 0 | } |
377 | |
|
378 | |
} |