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