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