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 }