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: 964673 $ 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 bridgeEndpoint = getAndRemoveParameter(parameters, "bridgeEndpoint", Boolean.class); 151 Boolean matchOnUriPrefix = getAndRemoveParameter(parameters, "matchOnUriPrefix", Boolean.class); 152 Boolean disableStreamCache = getAndRemoveParameter(parameters, "disableStreamCache", Boolean.class); 153 154 // validate that we could resolve all httpClient. parameters as this component is lenient 155 validateParameters(uri, parameters, "httpClient."); 156 // create the configurer to use for this endpoint 157 HttpClientConfigurer configurer = createHttpClientConfigurer(parameters); 158 URI endpointUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(httpClientParameters)); 159 // restructure uri to be based on the parameters left as we dont want to include the Camel internal options 160 URI httpUri = URISupport.createRemainingURI(new URI(addressUri), CastUtils.cast(parameters)); 161 162 // validate http uri that end-user did not duplicate the http part that can be a common error 163 String part = httpUri.getSchemeSpecificPart(); 164 if (part != null) { 165 part = part.toLowerCase(); 166 if (part.startsWith("//http//") || part.startsWith("//https//")) { 167 throw new ResolveEndpointFailedException(uri, 168 "The uri part is not configured correctly. You have duplicated the http(s) protocol."); 169 } 170 } 171 172 // register port on schema registry 173 boolean secure = isSecureConnection(uri); 174 int port = getPort(httpUri); 175 registerPort(secure, port); 176 177 // create the endpoint 178 HttpEndpoint endpoint = new HttpEndpoint(endpointUri.toString(), this, httpUri, clientParams, clientConnectionManager, configurer); 179 setEndpointHeaderFilterStrategy(endpoint); 180 181 // prefer to use endpoint configured over component configured 182 if (binding == null) { 183 // fallback to component configured 184 binding = getHttpBinding(); 185 } 186 if (binding != null) { 187 endpoint.setBinding(binding); 188 } 189 // should we use an exception for failed error codes? 190 if (throwExceptionOnFailure != null) { 191 endpoint.setThrowExceptionOnFailure(throwExceptionOnFailure); 192 } 193 if (bridgeEndpoint != null) { 194 endpoint.setBridgeEndpoint(bridgeEndpoint); 195 } 196 if (matchOnUriPrefix != null) { 197 endpoint.setMatchOnUriPrefix(matchOnUriPrefix); 198 } 199 if (disableStreamCache != null) { 200 endpoint.setDisableStreamCache(disableStreamCache); 201 } 202 203 setProperties(endpoint, parameters); 204 return endpoint; 205 } 206 207 private static int getPort(URI uri) { 208 int port = uri.getPort(); 209 if (port < 0) { 210 if ("http4".equals(uri.getScheme()) || "http".equals(uri.getScheme())) { 211 port = 80; 212 } else if ("https4".equals(uri.getScheme()) || "https".equals(uri.getScheme())) { 213 port = 443; 214 } else { 215 throw new IllegalArgumentException("Unknown scheme, cannot determine port number for uri: " + uri); 216 } 217 } 218 return port; 219 } 220 221 protected void registerPort(boolean secure, int port) { 222 SchemeRegistry registry = clientConnectionManager.getSchemeRegistry(); 223 if (secure) { 224 // must register both https and https4 225 registry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), port)); 226 LOG.info("Registering SSL scheme https on port " + port); 227 registry.register(new Scheme("https4", SSLSocketFactory.getSocketFactory(), port)); 228 LOG.info("Registering SSL scheme https4 on port " + port); 229 } else { 230 // must register both http and http4 231 registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), port)); 232 LOG.info("Registering PLAIN scheme http on port " + port); 233 registry.register(new Scheme("http4", PlainSocketFactory.getSocketFactory(), port)); 234 LOG.info("Registering PLAIN scheme http4 on port " + port); 235 } 236 } 237 238 protected ClientConnectionManager createConnectionManager() { 239 SchemeRegistry schemeRegistry = new SchemeRegistry(); 240 241 // configure additional configurations 242 HttpParams params = new BasicHttpParams(); 243 ConnManagerParamBean param = new ConnManagerParamBean(params); 244 if (getMaxTotalConnections() > 0) { 245 param.setMaxTotalConnections(getMaxTotalConnections()); 246 } 247 if (getConnectionsPerRoute() > 0) { 248 param.setConnectionsPerRoute(new ConnPerRouteBean(getConnectionsPerRoute())); 249 } 250 251 ThreadSafeClientConnManager answer = new ThreadSafeClientConnManager(params, schemeRegistry); 252 LOG.info("Created ClientConnectionManager " + answer); 253 254 return answer; 255 } 256 257 protected HttpParams configureHttpParams(Map<String, Object> parameters) throws Exception { 258 HttpParams clientParams = new BasicHttpParams(); 259 260 AuthParamBean authParamBean = new AuthParamBean(clientParams); 261 IntrospectionSupport.setProperties(authParamBean, parameters, "httpClient."); 262 263 ClientParamBean clientParamBean = new ClientParamBean(clientParams); 264 IntrospectionSupport.setProperties(clientParamBean, parameters, "httpClient."); 265 266 ConnConnectionParamBean connConnectionParamBean = new ConnConnectionParamBean(clientParams); 267 IntrospectionSupport.setProperties(connConnectionParamBean, parameters, "httpClient."); 268 269 ConnManagerParamBean connManagerParamBean = new ConnManagerParamBean(clientParams); 270 IntrospectionSupport.setProperties(connManagerParamBean, parameters, "httpClient."); 271 272 ConnRouteParamBean connRouteParamBean = new ConnRouteParamBean(clientParams); 273 IntrospectionSupport.setProperties(connRouteParamBean, parameters, "httpClient."); 274 275 CookieSpecParamBean cookieSpecParamBean = new CookieSpecParamBean(clientParams); 276 IntrospectionSupport.setProperties(cookieSpecParamBean, parameters, "httpClient."); 277 278 HttpConnectionParamBean httpConnectionParamBean = new HttpConnectionParamBean(clientParams); 279 IntrospectionSupport.setProperties(httpConnectionParamBean, parameters, "httpClient."); 280 281 HttpProtocolParamBean httpProtocolParamBean = new HttpProtocolParamBean(clientParams); 282 IntrospectionSupport.setProperties(httpProtocolParamBean, parameters, "httpClient."); 283 284 return clientParams; 285 } 286 287 private boolean isSecureConnection(String uri) { 288 return uri.startsWith("https"); 289 } 290 291 @Override 292 protected boolean useIntrospectionOnEndpoint() { 293 return false; 294 } 295 296 public HttpClientConfigurer getHttpClientConfigurer() { 297 return httpClientConfigurer; 298 } 299 300 public void setHttpClientConfigurer(HttpClientConfigurer httpClientConfigurer) { 301 this.httpClientConfigurer = httpClientConfigurer; 302 } 303 304 public ClientConnectionManager getClientConnectionManager() { 305 return clientConnectionManager; 306 } 307 308 public void setClientConnectionManager(ClientConnectionManager clientConnectionManager) { 309 this.clientConnectionManager = clientConnectionManager; 310 } 311 312 public HttpBinding getHttpBinding() { 313 return httpBinding; 314 } 315 316 public void setHttpBinding(HttpBinding httpBinding) { 317 this.httpBinding = httpBinding; 318 } 319 320 public int getMaxTotalConnections() { 321 return maxTotalConnections; 322 } 323 324 public void setMaxTotalConnections(int maxTotalConnections) { 325 this.maxTotalConnections = maxTotalConnections; 326 } 327 328 public int getConnectionsPerRoute() { 329 return connectionsPerRoute; 330 } 331 332 public void setConnectionsPerRoute(int connectionsPerRoute) { 333 this.connectionsPerRoute = connectionsPerRoute; 334 } 335 336 @Override 337 public void start() throws Exception { 338 super.start(); 339 if (clientConnectionManager == null) { 340 clientConnectionManager = createConnectionManager(); 341 } 342 } 343 344 @Override 345 public void stop() throws Exception { 346 // shutdown connection manager 347 if (clientConnectionManager != null) { 348 LOG.info("Shutting down ClientConnectionManager: " + clientConnectionManager); 349 clientConnectionManager.shutdown(); 350 clientConnectionManager = null; 351 } 352 super.stop(); 353 } 354 }