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 }