Added minimal HttpClient implementation (no redirects, auth, state management and proxy support)

git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1424934 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Oleg Kalnichevski 2012-12-21 14:34:26 +00:00
parent e99b061a95
commit 41e08bfa52
4 changed files with 382 additions and 4 deletions

View File

@ -28,6 +28,7 @@
package org.apache.http.impl.client;
import org.apache.http.annotation.Immutable;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
/**
* @since 4.3
@ -51,4 +52,8 @@ public class HttpClients {
return HttpClientBuilder.create().useSystemProperties().build();
}
public static CloseableHttpClient createMinimal() {
return new MinimalHttpClient(new PoolingHttpClientConnectionManager());
}
}

View File

@ -156,10 +156,10 @@ class InternalHttpClient extends CloseableHttpClient {
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(request, "Request");
HttpExecutionAware execListner = null;
Args.notNull(request, "HTTP request");
HttpExecutionAware execAware = null;
if (request instanceof HttpExecutionAware) {
execListner = (HttpExecutionAware) request;
execAware = (HttpExecutionAware) request;
}
try {
HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request);
@ -176,7 +176,7 @@ class InternalHttpClient extends CloseableHttpClient {
config = HttpClientParamConfig.getRequestConfig(params);
}
localcontext.setRequestConfig(config);
return this.execChain.execute(route, wrapper, localcontext, execListner);
return this.execChain.execute(route, wrapper, localcontext, execAware);
} catch (HttpException httpException) {
throw new ClientProtocolException(httpException);
}

View File

@ -0,0 +1,164 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.annotation.ThreadSafe;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.Configurable;
import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.client.methods.HttpRequestWrapper;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ClientConnectionRequest;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.execchain.MinimalClientExec;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.util.Args;
/**
* @since 4.3
*/
@ThreadSafe
@SuppressWarnings("deprecation")
class MinimalHttpClient extends CloseableHttpClient {
private final HttpClientConnectionManager connManager;
private final MinimalClientExec requestExecutor;
private final HttpParams params;
public MinimalHttpClient(
final HttpClientConnectionManager connManager) {
super();
this.connManager = Args.notNull(connManager, "HTTP connection manager");
this.requestExecutor = new MinimalClientExec(
new HttpRequestExecutor(),
connManager,
DefaultConnectionReuseStrategy.INSTANCE,
DefaultConnectionKeepAliveStrategy.INSTANCE);
this.params = new BasicHttpParams();
}
private HttpClientContext setupContext(final HttpContext localContext) {
HttpClientContext context = HttpClientContext.adapt(
localContext != null ? localContext : new BasicHttpContext());
return context;
}
@Override
protected CloseableHttpResponse doExecute(
final HttpHost target,
final HttpRequest request,
final HttpContext context) throws IOException, ClientProtocolException {
Args.notNull(target, "Target host");
Args.notNull(request, "HTTP request");
HttpExecutionAware execAware = null;
if (request instanceof HttpExecutionAware) {
execAware = (HttpExecutionAware) request;
}
try {
HttpRequestWrapper wrapper = HttpRequestWrapper.wrap(request);
HttpClientContext localcontext = setupContext(context);
HttpRoute route = new HttpRoute(target);
RequestConfig config = null;
if (request instanceof Configurable) {
config = ((Configurable) request).getConfig();
}
if (config == null) {
config = RequestConfig.DEFAULT;
}
if (config.getDefaultProxy() != null) {
throw new ClientProtocolException("Minimal HttpClient does not support" +
" request execution via proxy");
}
localcontext.setRequestConfig(config);
return this.requestExecutor.execute(route, wrapper, localcontext, execAware);
} catch (HttpException httpException) {
throw new ClientProtocolException(httpException);
}
}
public HttpParams getParams() {
return this.params;
}
public void close() {
this.connManager.shutdown();
}
public ClientConnectionManager getConnectionManager() {
return new ClientConnectionManager() {
public void shutdown() {
connManager.shutdown();
}
public ClientConnectionRequest requestConnection(
HttpRoute route, Object state) {
throw new UnsupportedOperationException();
}
public void releaseConnection(
ManagedClientConnection conn,
long validDuration, TimeUnit timeUnit) {
throw new UnsupportedOperationException();
}
public SchemeRegistry getSchemeRegistry() {
throw new UnsupportedOperationException();
}
public void closeIdleConnections(long idletime, TimeUnit tunit) {
connManager.closeIdleConnections(idletime, tunit);
}
public void closeExpiredConnections() {
connManager.closeExpiredConnections();
}
};
}
}

View File

@ -0,0 +1,209 @@
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.impl.client.execchain;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.URI;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpClientConnection;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.annotation.Immutable;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpExecutionAware;
import org.apache.http.client.methods.HttpRequestWrapper;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.protocol.RequestClientConnControl;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.ConnectionRequest;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.client.RequestAbortedException;
import org.apache.http.impl.conn.ConnectionShutdownException;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HttpProcessor;
import org.apache.http.protocol.HttpRequestExecutor;
import org.apache.http.protocol.ImmutableHttpProcessor;
import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
import org.apache.http.util.Args;
import org.apache.http.util.VersionInfo;
/**
* @since 4.3
*/
@Immutable
public class MinimalClientExec implements ClientExecChain {
private final Log log = LogFactory.getLog(getClass());
private final HttpRequestExecutor requestExecutor;
private final HttpClientConnectionManager connManager;
private final ConnectionReuseStrategy reuseStrategy;
private final ConnectionKeepAliveStrategy keepAliveStrategy;
private final HttpProcessor httpProcessor;
public MinimalClientExec(
final HttpRequestExecutor requestExecutor,
final HttpClientConnectionManager connManager,
final ConnectionReuseStrategy reuseStrategy,
final ConnectionKeepAliveStrategy keepAliveStrategy) {
Args.notNull(requestExecutor, "HTTP request executor");
Args.notNull(connManager, "Client connection manager");
Args.notNull(reuseStrategy, "Connection reuse strategy");
Args.notNull(keepAliveStrategy, "Connection keep alive strategy");
this.httpProcessor = new ImmutableHttpProcessor(new HttpRequestInterceptor[] {
new RequestContent(),
new RequestTargetHost(),
new RequestClientConnControl(),
new RequestUserAgent(VersionInfo.getUserAgent(
"Apache-HttpClient", "org.apache.http.client", getClass())),
} );
this.requestExecutor = requestExecutor;
this.connManager = connManager;
this.reuseStrategy = reuseStrategy;
this.keepAliveStrategy = keepAliveStrategy;
}
public CloseableHttpResponse execute(
final HttpRoute route,
final HttpRequestWrapper request,
final HttpClientContext context,
final HttpExecutionAware execAware) throws IOException, HttpException {
Args.notNull(route, "HTTP route");
Args.notNull(request, "HTTP request");
Args.notNull(context, "HTTP context");
ConnectionRequest connRequest = connManager.requestConnection(route, null);
if (execAware != null) {
if (execAware.isAborted()) {
connRequest.cancel();
throw new RequestAbortedException("Request aborted");
} else {
execAware.setCancellable(connRequest);
}
}
RequestConfig config = context.getRequestConfig();
HttpClientConnection managedConn;
try {
int timeout = config.getConnectionRequestTimeout();
managedConn = connRequest.get(timeout > 0 ? timeout : 0, TimeUnit.MILLISECONDS);
} catch(InterruptedException interrupted) {
throw new RequestAbortedException("Request aborted", interrupted);
}
ConnectionReleaseTriggerImpl releaseTrigger = new ConnectionReleaseTriggerImpl(
log, connManager, managedConn);
try {
if (execAware != null) {
if (execAware.isAborted()) {
releaseTrigger.abortConnection();
throw new RequestAbortedException("Request aborted");
} else {
execAware.setCancellable(releaseTrigger);
}
}
int timeout = config.getSocketTimeout();
if (timeout >= 0) {
managedConn.setSocketTimeout(timeout);
}
HttpHost target = null;
HttpRequest original = request.getOriginal();
if (original instanceof HttpUriRequest) {
URI uri = ((HttpUriRequest) original).getURI();
if (uri.isAbsolute()) {
target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
}
}
if (target == null) {
target = route.getTargetHost();
}
context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, target);
context.setAttribute(ExecutionContext.HTTP_REQUEST, request);
context.setAttribute(ExecutionContext.HTTP_CONNECTION, managedConn);
context.setAttribute(ClientContext.ROUTE, route);
httpProcessor.process(request, context);
HttpResponse response = requestExecutor.execute(request, managedConn, context);
httpProcessor.process(response, context);
// The connection is in or can be brought to a re-usable state.
if (reuseStrategy.keepAlive(response, context)) {
// Set the idle duration of this connection
long duration = keepAliveStrategy.getKeepAliveDuration(response, context);
releaseTrigger.setValidFor(duration, TimeUnit.MILLISECONDS);
releaseTrigger.markReusable();
} else {
releaseTrigger.markNonReusable();
}
// check for entity, release connection if possible
HttpEntity entity = response.getEntity();
if (entity == null || !entity.isStreaming()) {
// connection not needed and (assumed to be) in re-usable state
releaseTrigger.releaseConnection();
return Proxies.enhanceResponse(response, null);
} else {
return Proxies.enhanceResponse(response, releaseTrigger);
}
} catch (ConnectionShutdownException ex) {
InterruptedIOException ioex = new InterruptedIOException(
"Connection has been shut down");
ioex.initCause(ex);
throw ioex;
} catch (HttpException ex) {
releaseTrigger.abortConnection();
throw ex;
} catch (IOException ex) {
releaseTrigger.abortConnection();
throw ex;
} catch (RuntimeException ex) {
releaseTrigger.abortConnection();
throw ex;
}
}
}