001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.camel.component.http; 018 019 import java.net.URI; 020 import java.util.HashMap; 021 import java.util.LinkedHashSet; 022 import java.util.Map; 023 import java.util.Set; 024 025 import org.apache.camel.Endpoint; 026 import org.apache.camel.ResolveEndpointFailedException; 027 import org.apache.camel.impl.HeaderFilterStrategyComponent; 028 import org.apache.camel.util.CastUtils; 029 import org.apache.camel.util.CollectionHelper; 030 import org.apache.camel.util.IntrospectionSupport; 031 import org.apache.camel.util.ObjectHelper; 032 import org.apache.camel.util.URISupport; 033 import org.apache.commons.httpclient.HttpConnectionManager; 034 import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; 035 import org.apache.commons.httpclient.params.HttpClientParams; 036 037 /** 038 * Defines the <a href="http://camel.apache.org/http.html">HTTP 039 * Component</a> 040 * 041 * @version $Revision: 1053029 $ 042 */ 043 public class HttpComponent extends HeaderFilterStrategyComponent { 044 protected HttpClientConfigurer httpClientConfigurer; 045 protected HttpConnectionManager httpConnectionManager = new MultiThreadedHttpConnectionManager(); 046 protected HttpBinding httpBinding; 047 protected HttpConfiguration httpConfiguration; 048 049 /** 050 * Connects the URL specified on the endpoint to the specified processor. 051 * 052 * @param consumer the consumer 053 * @throws Exception can be thrown 054 */ 055 public void connect(HttpConsumer consumer) throws Exception { 056 } 057 058 /** 059 * Disconnects the URL specified on the endpoint from the specified processor. 060 * 061 * @param consumer the consumer 062 * @throws Exception can be thrown 063 */ 064 public void disconnect(HttpConsumer consumer) throws Exception { 065 } 066 067 /** 068 * Creates the HttpClientConfigurer based on the given parameters 069 * 070 * @param parameters the map of parameters 071 * @return the configurer 072 */ 073 protected HttpClientConfigurer createHttpClientConfigurer(Map<String, Object> parameters, Set<AuthMethod> authMethods) { 074 // prefer to use endpoint configured over component configured 075 HttpClientConfigurer configurer = resolveAndRemoveReferenceParameter(parameters, "httpClientConfigurerRef", HttpClientConfigurer.class); 076 if (configurer == null) { 077 // try without ref 078 configurer = resolveAndRemoveReferenceParameter(parameters, "httpClientConfigurer", HttpClientConfigurer.class); 079 } 080 if (configurer == null) { 081 // fallback to component configured 082 configurer = getHttpClientConfigurer(); 083 } 084 085 // authentication can be endpoint configured 086 String authUsername = getAndRemoveParameter(parameters, "authUsername", String.class); 087 AuthMethod authMethod = getAndRemoveParameter(parameters, "authMethod", AuthMethod.class); 088 // validate that if auth username is given then the auth method is also provided 089 if (authUsername != null && authMethod == null) { 090 throw new IllegalArgumentException("Option authMethod must be provided to use authentication"); 091 } 092 if (authMethod != null) { 093 String authPassword = getAndRemoveParameter(parameters, "authPassword", String.class); 094 String authDomain = getAndRemoveParameter(parameters, "authDomain", String.class); 095 String authHost = getAndRemoveParameter(parameters, "authHost", String.class); 096 configurer = configureAuth(configurer, authMethod, authUsername, authPassword, authDomain, authHost, authMethods); 097 } else if (httpConfiguration != null) { 098 // or fallback to use component configuration 099 configurer = configureAuth(configurer, httpConfiguration.getAuthMethod(), httpConfiguration.getAuthUsername(), 100 httpConfiguration.getAuthPassword(), httpConfiguration.getAuthDomain(), httpConfiguration.getAuthHost(), authMethods); 101 } 102 103 // proxy authentication can be endpoint configured 104 String proxyAuthUsername = getAndRemoveParameter(parameters, "proxyAuthUsername", String.class); 105 AuthMethod proxyAuthMethod = getAndRemoveParameter(parameters, "proxyAuthMethod", AuthMethod.class); 106 // validate that if proxy auth username is given then the proxy auth method is also provided 107 if (proxyAuthUsername != null && proxyAuthMethod == null) { 108 throw new IllegalArgumentException("Option proxyAuthMethod must be provided to use proxy authentication"); 109 } 110 if (proxyAuthMethod != null) { 111 String proxyAuthPassword = getAndRemoveParameter(parameters, "proxyAuthPassword", String.class); 112 String proxyAuthDomain = getAndRemoveParameter(parameters, "proxyAuthDomain", String.class); 113 String proxyAuthHost = getAndRemoveParameter(parameters, "proxyAuthHost", String.class); 114 configurer = configureProxyAuth(configurer, proxyAuthMethod, proxyAuthUsername, proxyAuthPassword, proxyAuthDomain, proxyAuthHost, authMethods); 115 } else if (httpConfiguration != null) { 116 // or fallback to use component configuration 117 configurer = configureProxyAuth(configurer, httpConfiguration.getProxyAuthMethod(), httpConfiguration.getProxyAuthUsername(), 118 httpConfiguration.getProxyAuthPassword(), httpConfiguration.getProxyAuthDomain(), httpConfiguration.getProxyAuthHost(), authMethods); 119 } 120 121 return configurer; 122 } 123 124 /** 125 * Configures the authentication method to be used 126 * 127 * @return configurer to used 128 */ 129 protected HttpClientConfigurer configureAuth(HttpClientConfigurer configurer, AuthMethod authMethod, String username, 130 String password, String domain, String host, Set<AuthMethod> authMethods) { 131 132 // no auth is in use 133 if (username == null && authMethod == null) { 134 return configurer; 135 } 136 137 // validate mandatory options given 138 if (username != null && authMethod == null) { 139 throw new IllegalArgumentException("Option authMethod must be provided to use authentication"); 140 } 141 ObjectHelper.notNull(authMethod, "authMethod"); 142 ObjectHelper.notNull(username, "authUsername"); 143 ObjectHelper.notNull(password, "authPassword"); 144 145 // add it as a auth method used 146 authMethods.add(authMethod); 147 148 if (authMethod == AuthMethod.Basic || authMethod == AuthMethod.Digest) { 149 return CompositeHttpConfigurer.combineConfigurers(configurer, 150 new BasicAuthenticationHttpClientConfigurer(false, username, password)); 151 } else if (authMethod == AuthMethod.NTLM) { 152 // domain is mandatory for NTLM 153 ObjectHelper.notNull(domain, "authDomain"); 154 return CompositeHttpConfigurer.combineConfigurers(configurer, 155 new NTLMAuthenticationHttpClientConfigurer(false, username, password, domain, host)); 156 } 157 158 throw new IllegalArgumentException("Unknown authMethod " + authMethod); 159 } 160 161 /** 162 * Configures the proxy authentication method to be used 163 * 164 * @return configurer to used 165 */ 166 protected HttpClientConfigurer configureProxyAuth(HttpClientConfigurer configurer, AuthMethod authMethod, String username, 167 String password, String domain, String host, Set<AuthMethod> authMethods) { 168 // no proxy auth is in use 169 if (username == null && authMethod == null) { 170 return configurer; 171 } 172 173 // validate mandatory options given 174 if (username != null && authMethod == null) { 175 throw new IllegalArgumentException("Option proxyAuthMethod must be provided to use proxy authentication"); 176 } 177 ObjectHelper.notNull(authMethod, "proxyAuthMethod"); 178 ObjectHelper.notNull(username, "proxyAuthUsername"); 179 ObjectHelper.notNull(password, "proxyAuthPassword"); 180 181 // add it as a auth method used 182 authMethods.add(authMethod); 183 184 if (authMethod == AuthMethod.Basic || authMethod == AuthMethod.Digest) { 185 return CompositeHttpConfigurer.combineConfigurers(configurer, 186 new BasicAuthenticationHttpClientConfigurer(true, username, password)); 187 } else if (authMethod == AuthMethod.NTLM) { 188 // domain is mandatory for NTML 189 ObjectHelper.notNull(domain, "proxyAuthDomain"); 190 return CompositeHttpConfigurer.combineConfigurers(configurer, 191 new NTLMAuthenticationHttpClientConfigurer(true, username, password, domain, host)); 192 } 193 194 throw new IllegalArgumentException("Unknown proxyAuthMethod " + authMethod); 195 } 196 197 @Override 198 protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { 199 String addressUri = uri; 200 if (!uri.startsWith("http:") && !uri.startsWith("https:")) { 201 addressUri = remaining; 202 } 203 Map<String, Object> httpClientParameters = new HashMap<String, Object>(parameters); 204 // must extract well known parameters before we create the endpoint 205 HttpBinding binding = resolveAndRemoveReferenceParameter(parameters, "httpBindingRef", HttpBinding.class); 206 if (binding == null) { 207 // try without ref 208 binding = resolveAndRemoveReferenceParameter(parameters, "httpBinding", HttpBinding.class); 209 } 210 Boolean throwExceptionOnFailure = getAndRemoveParameter(parameters, "throwExceptionOnFailure", Boolean.class); 211 Boolean transferException = getAndRemoveParameter(parameters, "transferException", Boolean.class); 212 Boolean bridgeEndpoint = getAndRemoveParameter(parameters, "bridgeEndpoint", Boolean.class); 213 Boolean matchOnUriPrefix = getAndRemoveParameter(parameters, "matchOnUriPrefix", Boolean.class); 214 Boolean disableStreamCache = getAndRemoveParameter(parameters, "disableStreamCache", Boolean.class); 215 String proxyHost = getAndRemoveParameter(parameters, "proxyHost", String.class); 216 Integer proxyPort = getAndRemoveParameter(parameters, "proxyPort", Integer.class); 217 String authMethodPriority = getAndRemoveParameter(parameters, "authMethodPriority", String.class); 218 // http client can be configured from URI options 219 HttpClientParams clientParams = new HttpClientParams(); 220 IntrospectionSupport.setProperties(clientParams, parameters, "httpClient."); 221 // validate that we could resolve all httpClient. parameters as this component is lenient 222 validateParameters(uri, parameters, "httpClient."); 223 224 // create the configurer to use for this endpoint (authMethods contains the used methods created by the configurer) 225 final Set<AuthMethod> authMethods = new LinkedHashSet<AuthMethod>(); 226 HttpClientConfigurer configurer = createHttpClientConfigurer(parameters, authMethods); 227 URI endpointUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(httpClientParameters)); 228 // restructure uri to be based on the parameters left as we dont want to include the Camel internal options 229 URI httpUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(parameters)); 230 231 // validate http uri that end-user did not duplicate the http part that can be a common error 232 String part = httpUri.getSchemeSpecificPart(); 233 if (part != null) { 234 part = part.toLowerCase(); 235 if (part.startsWith("//http//") || part.startsWith("//https//")) { 236 throw new ResolveEndpointFailedException(uri, 237 "The uri part is not configured correctly. You have duplicated the http(s) protocol."); 238 } 239 } 240 // need to keep the parameters of http client configure to avoid unwiser endpoint caching 241 242 // create the endpoint 243 HttpEndpoint endpoint = new HttpEndpoint(endpointUri.toString(), this, httpUri, clientParams, httpConnectionManager, configurer); 244 setEndpointHeaderFilterStrategy(endpoint); 245 246 // prefer to use endpoint configured over component configured 247 if (binding == null) { 248 // fallback to component configured 249 binding = getHttpBinding(); 250 } 251 if (binding != null) { 252 endpoint.setBinding(binding); 253 } 254 // should we use an exception for failed error codes? 255 if (throwExceptionOnFailure != null) { 256 endpoint.setThrowExceptionOnFailure(throwExceptionOnFailure); 257 } 258 // should we transfer exception as serialized object 259 if (transferException != null) { 260 endpoint.setTransferException(transferException); 261 } 262 if (bridgeEndpoint != null) { 263 endpoint.setBridgeEndpoint(bridgeEndpoint); 264 } 265 if (matchOnUriPrefix != null) { 266 endpoint.setMatchOnUriPrefix(matchOnUriPrefix); 267 } 268 if (disableStreamCache != null) { 269 endpoint.setDisableStreamCache(disableStreamCache); 270 } 271 if (proxyHost != null) { 272 endpoint.setProxyHost(proxyHost); 273 endpoint.setProxyPort(proxyPort); 274 } else if (httpConfiguration != null) { 275 endpoint.setProxyHost(httpConfiguration.getProxyHost()); 276 endpoint.setProxyPort(httpConfiguration.getProxyPort()); 277 } 278 if (authMethodPriority != null) { 279 endpoint.setAuthMethodPriority(authMethodPriority); 280 } else if (httpConfiguration != null && httpConfiguration.getAuthMethodPriority() != null) { 281 endpoint.setAuthMethodPriority(httpConfiguration.getAuthMethodPriority()); 282 } else { 283 // no explicit auth method priority configured, so use convention over configuration 284 // and set priority based on auth method 285 if (!authMethods.isEmpty()) { 286 authMethodPriority = CollectionHelper.collectionAsCommaDelimitedString(authMethods); 287 endpoint.setAuthMethodPriority(authMethodPriority); 288 } 289 } 290 291 setProperties(endpoint, parameters); 292 return endpoint; 293 } 294 295 @Override 296 protected boolean useIntrospectionOnEndpoint() { 297 return false; 298 } 299 300 public HttpClientConfigurer getHttpClientConfigurer() { 301 return httpClientConfigurer; 302 } 303 304 public void setHttpClientConfigurer(HttpClientConfigurer httpClientConfigurer) { 305 this.httpClientConfigurer = httpClientConfigurer; 306 } 307 308 public HttpConnectionManager getHttpConnectionManager() { 309 return httpConnectionManager; 310 } 311 312 public void setHttpConnectionManager(HttpConnectionManager httpConnectionManager) { 313 this.httpConnectionManager = httpConnectionManager; 314 } 315 316 public HttpBinding getHttpBinding() { 317 return httpBinding; 318 } 319 320 public void setHttpBinding(HttpBinding httpBinding) { 321 this.httpBinding = httpBinding; 322 } 323 324 public HttpConfiguration getHttpConfiguration() { 325 return httpConfiguration; 326 } 327 328 public void setHttpConfiguration(HttpConfiguration httpConfiguration) { 329 this.httpConfiguration = httpConfiguration; 330 } 331 332 }