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 }