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.http4; 018 019 import java.net.URI; 020 import java.util.HashMap; 021 import java.util.Map; 022 023 import org.apache.camel.Endpoint; 024 import org.apache.camel.ResolveEndpointFailedException; 025 import org.apache.camel.impl.HeaderFilterStrategyComponent; 026 import org.apache.camel.util.CastUtils; 027 import org.apache.camel.util.IntrospectionSupport; 028 import org.apache.camel.util.URISupport; 029 import org.apache.commons.logging.Log; 030 import org.apache.commons.logging.LogFactory; 031 import org.apache.http.auth.params.AuthParamBean; 032 import org.apache.http.client.params.ClientParamBean; 033 import org.apache.http.conn.ClientConnectionManager; 034 import org.apache.http.conn.params.ConnConnectionParamBean; 035 import org.apache.http.conn.params.ConnManagerParamBean; 036 import org.apache.http.conn.params.ConnPerRouteBean; 037 import org.apache.http.conn.params.ConnRouteParamBean; 038 import org.apache.http.conn.scheme.PlainSocketFactory; 039 import org.apache.http.conn.scheme.Scheme; 040 import org.apache.http.conn.scheme.SchemeRegistry; 041 import org.apache.http.conn.ssl.SSLSocketFactory; 042 import org.apache.http.cookie.params.CookieSpecParamBean; 043 import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; 044 import org.apache.http.params.BasicHttpParams; 045 import org.apache.http.params.HttpConnectionParamBean; 046 import org.apache.http.params.HttpParams; 047 import org.apache.http.params.HttpProtocolParamBean; 048 049 /** 050 * Defines the <a href="http://camel.apache.org/http.html">HTTP 051 * Component</a> 052 * 053 * @version $Revision: 1053033 $ 054 */ 055 public class HttpComponent extends HeaderFilterStrategyComponent { 056 private static final transient Log LOG = LogFactory.getLog(HttpComponent.class); 057 058 protected HttpClientConfigurer httpClientConfigurer; 059 protected ClientConnectionManager clientConnectionManager; 060 protected HttpBinding httpBinding; 061 062 // options to the default created http connection manager 063 protected int maxTotalConnections = 200; 064 protected int connectionsPerRoute = 20; 065 066 /** 067 * Connects the URL specified on the endpoint to the specified processor. 068 * 069 * @param consumer the consumer 070 * @throws Exception can be thrown 071 */ 072 public void connect(HttpConsumer consumer) throws Exception { 073 } 074 075 /** 076 * Disconnects the URL specified on the endpoint from the specified processor. 077 * 078 * @param consumer the consumer 079 * @throws Exception can be thrown 080 */ 081 public void disconnect(HttpConsumer consumer) throws Exception { 082 } 083 084 /** 085 * Creates the HttpClientConfigurer based on the given parameters 086 * 087 * @param parameters the map of parameters 088 * @return the configurer 089 */ 090 protected HttpClientConfigurer createHttpClientConfigurer(Map<String, Object> parameters) { 091 // prefer to use endpoint configured over component configured 092 HttpClientConfigurer configurer = resolveAndRemoveReferenceParameter(parameters, "httpClientConfigurerRef", HttpClientConfigurer.class); 093 if (configurer == null) { 094 // try without ref 095 configurer = resolveAndRemoveReferenceParameter(parameters, "httpClientConfigurer", HttpClientConfigurer.class); 096 } 097 if (configurer == null) { 098 // fallback to component configured 099 configurer = getHttpClientConfigurer(); 100 } 101 102 // check the user name and password for basic authentication 103 String username = getAndRemoveParameter(parameters, "username", String.class); 104 String password = getAndRemoveParameter(parameters, "password", String.class); 105 String domain = getAndRemoveParameter(parameters, "domain", String.class); 106 String host = getAndRemoveParameter(parameters, "host", String.class); 107 if (username != null && password != null) { 108 configurer = CompositeHttpConfigurer.combineConfigurers( 109 configurer, 110 new BasicAuthenticationHttpClientConfigurer(username, password, domain, host)); 111 } 112 113 // check the proxy details for proxy configuration 114 String proxyHost = getAndRemoveParameter(parameters, "proxyHost", String.class); 115 Integer proxyPort = getAndRemoveParameter(parameters, "proxyPort", Integer.class); 116 if (proxyHost != null && proxyPort != null) { 117 String proxyUsername = getAndRemoveParameter(parameters, "proxyUsername", String.class); 118 String proxyPassword = getAndRemoveParameter(parameters, "proxyPassword", String.class); 119 String proxyDomain = getAndRemoveParameter(parameters, "proxyDomain", String.class); 120 String proxyNtHost = getAndRemoveParameter(parameters, "proxyNtHost", String.class); 121 if (proxyUsername != null && proxyPassword != null) { 122 configurer = CompositeHttpConfigurer.combineConfigurers( 123 configurer, new ProxyHttpClientConfigurer(proxyHost, proxyPort, proxyUsername, proxyPassword, proxyDomain, proxyNtHost)); 124 } else { 125 configurer = CompositeHttpConfigurer.combineConfigurers( 126 configurer, new ProxyHttpClientConfigurer(proxyHost, proxyPort)); 127 } 128 } 129 130 return configurer; 131 } 132 133 @Override 134 protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { 135 String addressUri = uri; 136 if (!uri.startsWith("http4:") && !uri.startsWith("https4:")) { 137 addressUri = remaining; 138 } 139 Map<String, Object> httpClientParameters = new HashMap<String, Object>(parameters); 140 // http client can be configured from URI options 141 HttpParams clientParams = configureHttpParams(parameters); 142 143 // must extract well known parameters before we create the endpoint 144 HttpBinding binding = resolveAndRemoveReferenceParameter(parameters, "httpBindingRef", HttpBinding.class); 145 if (binding == null) { 146 // try without ref 147 binding = resolveAndRemoveReferenceParameter(parameters, "httpBinding", HttpBinding.class); 148 } 149 Boolean throwExceptionOnFailure = getAndRemoveParameter(parameters, "throwExceptionOnFailure", Boolean.class); 150 Boolean transferException = getAndRemoveParameter(parameters, "transferException", Boolean.class); 151 Boolean bridgeEndpoint = getAndRemoveParameter(parameters, "bridgeEndpoint", Boolean.class); 152 Boolean matchOnUriPrefix = getAndRemoveParameter(parameters, "matchOnUriPrefix", Boolean.class); 153 Boolean disableStreamCache = getAndRemoveParameter(parameters, "disableStreamCache", Boolean.class); 154 155 // validate that we could resolve all httpClient. parameters as this component is lenient 156 validateParameters(uri, parameters, "httpClient."); 157 // create the configurer to use for this endpoint 158 HttpClientConfigurer configurer = createHttpClientConfigurer(parameters); 159 URI endpointUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(httpClientParameters)); 160 // restructure uri to be based on the parameters left as we dont want to include the Camel internal options 161 URI httpUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(parameters)); 162 163 // validate http uri that end-user did not duplicate the http part that can be a common error 164 String part = httpUri.getSchemeSpecificPart(); 165 if (part != null) { 166 part = part.toLowerCase(); 167 if (part.startsWith("//http//") || part.startsWith("//https//")) { 168 throw new ResolveEndpointFailedException(uri, 169 "The uri part is not configured correctly. You have duplicated the http(s) protocol."); 170 } 171 } 172 173 // register port on schema registry 174 boolean secure = isSecureConnection(uri); 175 int port = getPort(httpUri); 176 registerPort(secure, port); 177 178 // create the endpoint 179 HttpEndpoint endpoint = new HttpEndpoint(endpointUri.toString(), this, httpUri, clientParams, clientConnectionManager, configurer); 180 setEndpointHeaderFilterStrategy(endpoint); 181 182 // prefer to use endpoint configured over component configured 183 if (binding == null) { 184 // fallback to component configured 185 binding = getHttpBinding(); 186 } 187 if (binding != null) { 188 endpoint.setBinding(binding); 189 } 190 // should we use an exception for failed error codes? 191 if (throwExceptionOnFailure != null) { 192 endpoint.setThrowExceptionOnFailure(throwExceptionOnFailure); 193 } 194 // should we transfer exception as serialized object 195 if (transferException != null) { 196 endpoint.setTransferException(transferException); 197 } 198 if (bridgeEndpoint != null) { 199 endpoint.setBridgeEndpoint(bridgeEndpoint); 200 } 201 if (matchOnUriPrefix != null) { 202 endpoint.setMatchOnUriPrefix(matchOnUriPrefix); 203 } 204 if (disableStreamCache != null) { 205 endpoint.setDisableStreamCache(disableStreamCache); 206 } 207 208 setProperties(endpoint, parameters); 209 return endpoint; 210 } 211 212 private static int getPort(URI uri) { 213 int port = uri.getPort(); 214 if (port < 0) { 215 if ("http4".equals(uri.getScheme()) || "http".equals(uri.getScheme())) { 216 port = 80; 217 } else if ("https4".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { 218 port = 443; 219 } else { 220 throw new IllegalArgumentException("Unknown scheme, cannot determine port number for uri: " + uri); 221 } 222 } 223 return port; 224 } 225 226 protected void registerPort(boolean secure, int port) { 227 SchemeRegistry registry = clientConnectionManager.getSchemeRegistry(); 228 if (secure) { 229 // must register both https and https4 230 registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), port)); 231 LOG.info("Registering SSL scheme https on port " + port); 232 registry.register(new Scheme("https4", SSLSocketFactory.getSocketFactory(), port)); 233 LOG.info("Registering SSL scheme https4 on port " + port); 234 } else { 235 // must register both http and http4 236 registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), port)); 237 LOG.info("Registering PLAIN scheme http on port " + port); 238 registry.register(new Scheme("http4", PlainSocketFactory.getSocketFactory(), port)); 239 LOG.info("Registering PLAIN scheme http4 on port " + port); 240 } 241 } 242 243 protected ClientConnectionManager createConnectionManager() { 244 SchemeRegistry schemeRegistry = new SchemeRegistry(); 245 246 // configure additional configurations 247 HttpParams params = new BasicHttpParams(); 248 ConnManagerParamBean param = new ConnManagerParamBean(params); 249 if (getMaxTotalConnections() > 0) { 250 param.setMaxTotalConnections(getMaxTotalConnections()); 251 } 252 if (getConnectionsPerRoute() > 0) { 253 param.setConnectionsPerRoute(new ConnPerRouteBean(getConnectionsPerRoute())); 254 } 255 256 ThreadSafeClientConnManager answer = new ThreadSafeClientConnManager(params, schemeRegistry); 257 LOG.info("Created ClientConnectionManager " + answer); 258 259 return answer; 260 } 261 262 protected HttpParams configureHttpParams(Map<String, Object> parameters) throws Exception { 263 HttpParams clientParams = new BasicHttpParams(); 264 265 AuthParamBean authParamBean = new AuthParamBean(clientParams); 266 IntrospectionSupport.setProperties(authParamBean, parameters, "httpClient."); 267 268 ClientParamBean clientParamBean = new ClientParamBean(clientParams); 269 IntrospectionSupport.setProperties(clientParamBean, parameters, "httpClient."); 270 271 ConnConnectionParamBean connConnectionParamBean = new ConnConnectionParamBean(clientParams); 272 IntrospectionSupport.setProperties(connConnectionParamBean, parameters, "httpClient."); 273 274 ConnManagerParamBean connManagerParamBean = new ConnManagerParamBean(clientParams); 275 IntrospectionSupport.setProperties(connManagerParamBean, parameters, "httpClient."); 276 277 ConnRouteParamBean connRouteParamBean = new ConnRouteParamBean(clientParams); 278 IntrospectionSupport.setProperties(connRouteParamBean, parameters, "httpClient."); 279 280 CookieSpecParamBean cookieSpecParamBean = new CookieSpecParamBean(clientParams); 281 IntrospectionSupport.setProperties(cookieSpecParamBean, parameters, "httpClient."); 282 283 HttpConnectionParamBean httpConnectionParamBean = new HttpConnectionParamBean(clientParams); 284 IntrospectionSupport.setProperties(httpConnectionParamBean, parameters, "httpClient."); 285 286 HttpProtocolParamBean httpProtocolParamBean = new HttpProtocolParamBean(clientParams); 287 IntrospectionSupport.setProperties(httpProtocolParamBean, parameters, "httpClient."); 288 289 return clientParams; 290 } 291 292 private boolean isSecureConnection(String uri) { 293 return uri.startsWith("https"); 294 } 295 296 @Override 297 protected boolean useIntrospectionOnEndpoint() { 298 return false; 299 } 300 301 public HttpClientConfigurer getHttpClientConfigurer() { 302 return httpClientConfigurer; 303 } 304 305 public void setHttpClientConfigurer(HttpClientConfigurer httpClientConfigurer) { 306 this.httpClientConfigurer = httpClientConfigurer; 307 } 308 309 public ClientConnectionManager getClientConnectionManager() { 310 return clientConnectionManager; 311 } 312 313 public void setClientConnectionManager(ClientConnectionManager clientConnectionManager) { 314 this.clientConnectionManager = clientConnectionManager; 315 } 316 317 public HttpBinding getHttpBinding() { 318 return httpBinding; 319 } 320 321 public void setHttpBinding(HttpBinding httpBinding) { 322 this.httpBinding = httpBinding; 323 } 324 325 public int getMaxTotalConnections() { 326 return maxTotalConnections; 327 } 328 329 public void setMaxTotalConnections(int maxTotalConnections) { 330 this.maxTotalConnections = maxTotalConnections; 331 } 332 333 public int getConnectionsPerRoute() { 334 return connectionsPerRoute; 335 } 336 337 public void setConnectionsPerRoute(int connectionsPerRoute) { 338 this.connectionsPerRoute = connectionsPerRoute; 339 } 340 341 @Override 342 public void start() throws Exception { 343 super.start(); 344 if (clientConnectionManager == null) { 345 clientConnectionManager = createConnectionManager(); 346 } 347 } 348 349 @Override 350 public void stop() throws Exception { 351 // shutdown connection manager 352 if (clientConnectionManager != null) { 353 LOG.info("Shutting down ClientConnectionManager: " + clientConnectionManager); 354 clientConnectionManager.shutdown(); 355 clientConnectionManager = null; 356 } 357 super.stop(); 358 } 359 }