View Javadoc

1   /*
2    * $Id: HttpConnector.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.transport.http;
12  
13  import org.mule.api.MuleContext;
14  import org.mule.api.MuleEvent;
15  import org.mule.api.MuleMessage;
16  import org.mule.api.construct.FlowConstruct;
17  import org.mule.api.endpoint.ImmutableEndpoint;
18  import org.mule.api.endpoint.InboundEndpoint;
19  import org.mule.api.lifecycle.InitialisationException;
20  import org.mule.api.processor.MessageProcessor;
21  import org.mule.config.i18n.CoreMessages;
22  import org.mule.transport.tcp.TcpConnector;
23  
24  import java.io.UnsupportedEncodingException;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Iterator;
28  import java.util.Map;
29  import java.util.Set;
30  
31  import org.apache.commons.codec.binary.Base64;
32  import org.apache.commons.httpclient.HttpClient;
33  import org.apache.commons.httpclient.HttpConnectionManager;
34  import org.apache.commons.httpclient.HttpMethod;
35  import org.apache.commons.httpclient.HttpState;
36  import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
37  import org.apache.commons.httpclient.UsernamePasswordCredentials;
38  import org.apache.commons.httpclient.auth.AuthScope;
39  import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
40  
41  /**
42   * <code>HttpConnector</code> provides a way of receiving and sending http requests
43   * and responses. The Connector itself handles dispatching http requests. The
44   * <code>HttpMessageReceiver</code> handles the receiving requests and processing
45   * of headers This endpoint recognises the following properties - <p/>
46   * <ul>
47   * <li>hostname - The hostname to send and receive http requests</li>
48   * <li>port - The port to listen on. The industry standard is 80 and if this propert
49   * is not set it will default to 80</li>
50   * <li>proxyHostname - If you access the web through a proxy, this holds the server
51   * address</li>
52   * <li>proxyPort - The port the proxy is configured on</li>
53   * <li>proxyUsername - If the proxy requires authentication supply a username</li>
54   * <li>proxyPassword - If the proxy requires authentication supply a password</li>
55   * </ul>
56   * 
57   */
58  
59  public class HttpConnector extends TcpConnector
60  {
61  
62      public static final String HTTP = "http";
63      public static final String HTTP_PREFIX = "http.";
64      
65      /**
66       * MuleEvent property to pass back the status for the response
67       */
68      public static final String HTTP_STATUS_PROPERTY = HTTP_PREFIX + "status";
69      public static final String HTTP_VERSION_PROPERTY = HTTP_PREFIX + "version";
70      
71      /**
72       * @deprecated Instead users can now add properties to the outgoing request using the OUTBOUND property scope on the message.
73       */
74      @Deprecated
75      public static final String HTTP_CUSTOM_HEADERS_MAP_PROPERTY = HTTP_PREFIX + "custom.headers";
76  
77      public static final String HTTP_METHOD_PROPERTY = HTTP_PREFIX + "method";
78      
79      /**
80       * The path and query portions of the URL being accessed. 
81       */
82      public static final String HTTP_REQUEST_PROPERTY = HTTP_PREFIX + "request";
83      
84      /**
85       * The path portion of the URL being accessed. No query string is included.
86       */
87      public static final String HTTP_REQUEST_PATH_PROPERTY = HTTP_PREFIX + "request.path";
88      
89      /**
90       * The context path of the endpoint being accessed. This is the path that the 
91       * HTTP endpoint is listening on.
92       */
93      public static final String HTTP_CONTEXT_PATH_PROPERTY = HTTP_PREFIX + "context.path";
94  
95      /**
96       * Allows the user to set a {@link org.apache.commons.httpclient.params.HttpMethodParams} object in the client
97       * request to be set on the HttpMethod request object
98       */
99      public static final String HTTP_PARAMS_PROPERTY = HTTP_PREFIX + "params";
100     public static final String HTTP_GET_BODY_PARAM_PROPERTY = HTTP_PREFIX + "get.body.param";
101     public static final String DEFAULT_HTTP_GET_BODY_PARAM_PROPERTY = "body";
102     public static final String HTTP_POST_BODY_PARAM_PROPERTY = HTTP_PREFIX + "post.body.param";
103 
104     public static final String HTTP_DISABLE_STATUS_CODE_EXCEPTION_CHECK = HTTP_PREFIX + "disable.status.code.exception.check";
105     public static final String HTTP_ENCODE_PARAMVALUE = HTTP_PREFIX + "encode.paramvalue";
106     
107     public static final Set<String> HTTP_INBOUND_PROPERTIES;
108     
109     static 
110     {
111         Set<String> props = new HashSet<String>();
112         props.add(HTTP_CONTEXT_PATH_PROPERTY);
113         props.add(HTTP_GET_BODY_PARAM_PROPERTY);
114         props.add(HTTP_METHOD_PROPERTY);
115         props.add(HTTP_PARAMS_PROPERTY);
116         props.add(HTTP_POST_BODY_PARAM_PROPERTY);
117         props.add(HTTP_REQUEST_PROPERTY);
118         props.add(HTTP_REQUEST_PATH_PROPERTY);
119         props.add(HTTP_STATUS_PROPERTY);
120         props.add(HTTP_VERSION_PROPERTY);
121         props.add(HTTP_ENCODE_PARAMVALUE);
122         HTTP_INBOUND_PROPERTIES = props;
123     }
124     
125     public static final String HTTP_COOKIE_SPEC_PROPERTY = "cookieSpec";
126     public static final String HTTP_COOKIES_PROPERTY = "cookies";
127     public static final String HTTP_ENABLE_COOKIES_PROPERTY = "enableCookies";
128 
129     public static final String COOKIE_SPEC_NETSCAPE = "netscape";
130     public static final String COOKIE_SPEC_RFC2109 = "rcf2109";
131 
132     private String proxyHostname = null;
133 
134     private int proxyPort = HttpConstants.DEFAULT_HTTP_PORT;
135 
136     private String proxyUsername = null;
137 
138     private String proxyPassword = null;
139 
140     private String cookieSpec;
141 
142     private boolean enableCookies = false;
143 
144     protected HttpConnectionManager clientConnectionManager;
145 
146     public HttpConnector(MuleContext context)
147     {
148         super(context);
149     }
150     
151     @Override
152     protected void doInitialise() throws InitialisationException
153     {
154         super.doInitialise();
155         if (clientConnectionManager == null)
156         {
157             clientConnectionManager = new MultiThreadedHttpConnectionManager();
158             HttpConnectionManagerParams params = new HttpConnectionManagerParams();
159             if (getSendBufferSize() != INT_VALUE_NOT_SET)
160             {
161                 params.setSendBufferSize(getSendBufferSize());
162             }
163             if (getReceiveBufferSize() != INT_VALUE_NOT_SET)
164             {
165                 params.setReceiveBufferSize(getReceiveBufferSize());
166             }
167             if (getClientSoTimeout() != INT_VALUE_NOT_SET)
168             {
169                 params.setSoTimeout(getClientSoTimeout());
170             }
171             if (getSocketSoLinger() != INT_VALUE_NOT_SET)
172             {
173                 params.setLinger(getSocketSoLinger());
174             }
175 
176             params.setTcpNoDelay(isSendTcpNoDelay());
177             params.setMaxTotalConnections(dispatchers.getMaxTotal());
178             params.setDefaultMaxConnectionsPerHost(dispatchers.getMaxTotal());
179             clientConnectionManager.setParams(params);
180         }
181     }
182 
183     @Override
184     public void registerListener(InboundEndpoint endpoint, MessageProcessor listener, FlowConstruct flowConstruct) throws Exception
185     {
186         if (endpoint != null)
187         {
188             Map endpointProperties = endpoint.getProperties();
189             if (endpointProperties != null)
190             {
191                 // normalize properties for HTTP
192                 Map newProperties = new HashMap(endpointProperties.size());
193                 for (Iterator entries = endpointProperties.entrySet().iterator(); entries.hasNext();)
194                 {
195                     Map.Entry entry = (Map.Entry) entries.next();
196                     Object key = entry.getKey();
197                     Object normalizedKey = HttpConstants.ALL_HEADER_NAMES.get(key);
198                     if (normalizedKey != null)
199                     {
200                         // normalized property exists
201                         key = normalizedKey;
202                     }
203                     newProperties.put(key, entry.getValue());
204                 }
205                 // set normalized properties back on the endpoint
206                 endpoint.getProperties().clear();
207                 endpoint.getProperties().putAll(newProperties);
208             }
209         }
210         // proceed as usual
211         super.registerListener(endpoint, listener, flowConstruct);
212     }
213 
214     /**
215      * The method determines the key used to store the receiver against.
216      *
217      * @param endpoint the endpoint being registered for the service
218      * @return the key to store the newly created receiver against
219      */
220     @Override
221     protected Object getReceiverKey(FlowConstruct flowConstruct, InboundEndpoint endpoint)
222     {
223         String key = endpoint.getEndpointURI().toString();
224         int i = key.indexOf('?');
225         if (i > -1)
226         {
227             key = key.substring(0, i);
228         }
229         return key;
230     }
231 
232     /**
233      * @see org.mule.api.transport.Connector#getProtocol()
234      */
235     @Override
236     public String getProtocol()
237     {
238         return HTTP;
239     }
240 
241     public String getProxyHostname()
242     {
243         return proxyHostname;
244     }
245 
246     public String getProxyPassword()
247     {
248         return proxyPassword;
249     }
250 
251     public int getProxyPort()
252     {
253         return proxyPort;
254     }
255 
256     public String getProxyUsername()
257     {
258         return proxyUsername;
259     }
260 
261     public void setProxyHostname(String host)
262     {
263         proxyHostname = host;
264     }
265 
266     public void setProxyPassword(String string)
267     {
268         proxyPassword = string;
269     }
270 
271     public void setProxyPort(int port)
272     {
273         proxyPort = port;
274     }
275 
276     public void setProxyUsername(String string)
277     {
278         proxyUsername = string;
279     }
280 
281     @Override
282     public Map getReceivers()
283     {
284         return this.receivers;
285     }
286 
287     public String getCookieSpec()
288     {
289         return cookieSpec;
290     }
291 
292     public void setCookieSpec(String cookieSpec)
293     {
294         if (!(COOKIE_SPEC_NETSCAPE.equalsIgnoreCase(cookieSpec) || COOKIE_SPEC_RFC2109.equalsIgnoreCase(cookieSpec)))
295         {
296             throw new IllegalArgumentException(
297                 CoreMessages.propertyHasInvalidValue("cookieSpec", cookieSpec).toString());
298         }
299         this.cookieSpec = cookieSpec;
300     }
301 
302     public boolean isEnableCookies()
303     {
304         return enableCookies;
305     }
306 
307     public void setEnableCookies(boolean enableCookies)
308     {
309         this.enableCookies = enableCookies;
310     }
311 
312 
313     public HttpConnectionManager getClientConnectionManager()
314     {
315         return clientConnectionManager;
316     }
317 
318     public void setClientConnectionManager(HttpConnectionManager clientConnectionManager)
319     {
320         this.clientConnectionManager = clientConnectionManager;
321     }
322 
323     protected HttpClient doClientConnect() throws Exception
324     {
325         HttpState state = new HttpState();
326 
327         if (getProxyUsername() != null)
328         {
329             state.setProxyCredentials(
330                     new AuthScope(null, -1, null, null),
331                     new UsernamePasswordCredentials(getProxyUsername(), getProxyPassword()));
332         }
333 
334         HttpClient client = new HttpClient();
335         client.setState(state);
336         client.setHttpConnectionManager(getClientConnectionManager());
337 
338         return client;
339     }
340 
341     protected void setupClientAuthorization(MuleEvent event, HttpMethod httpMethod,
342                                             HttpClient client, ImmutableEndpoint endpoint)
343             throws UnsupportedEncodingException
344     {
345         httpMethod.setDoAuthentication(true);
346         client.getParams().setAuthenticationPreemptive(true);
347 
348         if (event != null && event.getCredentials() != null)
349         {
350             MuleMessage msg = event.getMessage();
351             String authScopeHost = msg.getOutboundProperty(HTTP_PREFIX + "auth.scope.host", event.getEndpoint().getEndpointURI().getHost());
352             int authScopePort = msg.getOutboundProperty(HTTP_PREFIX + "auth.scope.port", event.getEndpoint().getEndpointURI().getPort());
353             String authScopeRealm = msg.getOutboundProperty(HTTP_PREFIX + "auth.scope.realm", AuthScope.ANY_REALM);
354             String authScopeScheme = msg.getOutboundProperty(HTTP_PREFIX + "auth.scope.scheme", AuthScope.ANY_SCHEME);
355             client.getState().setCredentials(
356                 new AuthScope(authScopeHost, authScopePort, authScopeRealm, authScopeScheme),
357                 new UsernamePasswordCredentials(event.getCredentials().getUsername(), new String(
358                     event.getCredentials().getPassword())));
359         }
360         else if (endpoint.getEndpointURI().getUserInfo() != null
361             && endpoint.getProperty(HttpConstants.HEADER_AUTHORIZATION) == null)
362         {
363             // Add User Creds
364             StringBuffer header = new StringBuffer(128);
365             header.append("Basic ");
366             header.append(new String(Base64.encodeBase64(endpoint.getEndpointURI().getUserInfo().getBytes(
367                 endpoint.getEncoding()))));
368             httpMethod.addRequestHeader(HttpConstants.HEADER_AUTHORIZATION, header.toString());
369         }
370         //TODO MULE-4501 this sohuld be removed and handled only in the ObjectToHttpRequest transformer
371         else if (event!=null && event.getMessage().getOutboundProperty(HttpConstants.HEADER_AUTHORIZATION) != null &&
372                 httpMethod.getRequestHeader(HttpConstants.HEADER_AUTHORIZATION)==null)
373         {
374             String auth = event.getMessage().getOutboundProperty(HttpConstants.HEADER_AUTHORIZATION);
375             httpMethod.addRequestHeader(HttpConstants.HEADER_AUTHORIZATION, auth);
376         }
377         else
378         {
379             // don't use preemptive if there are no credentials to send
380             client.getParams().setAuthenticationPreemptive(false);
381         }
382     }
383 
384     /**
385      * Ensures that the supplied URL starts with a '/'.
386      */
387     public static String normalizeUrl(String url)
388     {
389         if (url == null) 
390         {
391             url = "/";
392         } 
393         else if (!url.startsWith("/")) 
394         {
395             url = "/" + url;
396         }
397         return url;
398     }
399 }