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