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:
parent
e99b061a95
commit
41e08bfa52
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue