You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2013/01/29 15:31:40 UTC
svn commit: r1439906 - in /httpcomponents/httpasyncclient/trunk: ./
httpasyncclient/src/main/java/org/apache/http/impl/nio/client/
httpasyncclient/src/test/java/org/apache/http/nio/client/integration/
Author: olegk
Date: Tue Jan 29 14:31:40 2013
New Revision: 1439906
URL: http://svn.apache.org/viewvc?rev=1439906&view=rev
Log:
HTTPASYNC-34: HttpAsyncClient fails to re-start request execution if the opposite end prematurely terminates persistent connection
Modified:
httpcomponents/httpasyncclient/trunk/RELEASE_NOTES.txt
httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java
httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/LoggingAsyncRequestExecutor.java
httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java
Modified: httpcomponents/httpasyncclient/trunk/RELEASE_NOTES.txt
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/RELEASE_NOTES.txt?rev=1439906&r1=1439905&r2=1439906&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/RELEASE_NOTES.txt (original)
+++ httpcomponents/httpasyncclient/trunk/RELEASE_NOTES.txt Tue Jan 29 14:31:40 2013
@@ -1,5 +1,9 @@
Changes since 4.0 Beta 3
+* [HTTPASYNC-34] HttpAsyncClient fails to re-start request execution if the opposite end
+ prematurely terminates persistent connection.
+ Contributed by Oleg Kalnichevski <olegk at apache.org>
+
* [HTTPASYNC-28] PoolEntry's expiry information is never updated.
Contributed by Daniel Kulp <dkulp at apache.org>
Modified: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java?rev=1439906&r1=1439905&r2=1439906&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java (original)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/DefaultAsyncRequestDirector.java Tue Jan 29 14:31:40 2013
@@ -119,6 +119,7 @@ class DefaultAsyncRequestDirector<T> imp
private final long id;
private volatile boolean closed;
+ private volatile InternalFutureCallback connRequestCallback;
private volatile ManagedClientAsyncConnection managedConn;
private RoutedRequest mainRequest;
@@ -557,6 +558,7 @@ class DefaultAsyncRequestDirector<T> imp
if (this.log.isDebugEnabled()) {
this.log.debug("[exchange: " + this.id + "] Connection allocated: " + conn);
}
+ this.connRequestCallback = null;
try {
this.managedConn = conn;
if (this.closed) {
@@ -585,6 +587,7 @@ class DefaultAsyncRequestDirector<T> imp
if (this.log.isDebugEnabled()) {
this.log.debug("[exchange: " + this.id + "] connection request failed");
}
+ this.connRequestCallback = null;
try {
this.resultCallback.failed(ex, this);
} finally {
@@ -596,6 +599,7 @@ class DefaultAsyncRequestDirector<T> imp
if (this.log.isDebugEnabled()) {
this.log.debug("[exchange: " + this.id + "] Connection request cancelled");
}
+ this.connRequestCallback = null;
try {
this.resultCallback.cancelled(this);
} finally {
@@ -626,10 +630,23 @@ class DefaultAsyncRequestDirector<T> imp
}
final long connectTimeout = HttpConnectionParams.getConnectionTimeout(this.params);
final Object userToken = this.localContext.getAttribute(ClientContext.USER_TOKEN);
+ this.connRequestCallback = new InternalFutureCallback();
this.connmgr.leaseConnection(
route, userToken,
connectTimeout, TimeUnit.MILLISECONDS,
- new InternalFutureCallback());
+ this.connRequestCallback);
+ }
+
+ public synchronized void endOfStream() {
+ if (this.managedConn != null) {
+ if (this.log.isDebugEnabled()) {
+ this.log.debug("[exchange: " + this.id + "] Unexpected end of data stream");
+ }
+ releaseConnection();
+ if (this.connRequestCallback == null) {
+ requestConnection();
+ }
+ }
}
protected HttpRoute determineRoute(
Modified: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/LoggingAsyncRequestExecutor.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/LoggingAsyncRequestExecutor.java?rev=1439906&r1=1439905&r2=1439906&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/LoggingAsyncRequestExecutor.java (original)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/main/java/org/apache/http/impl/nio/client/LoggingAsyncRequestExecutor.java Tue Jan 29 14:31:40 2013
@@ -36,6 +36,7 @@ import org.apache.http.nio.ContentDecode
import org.apache.http.nio.ContentEncoder;
import org.apache.http.nio.NHttpClientConnection;
import org.apache.http.nio.protocol.HttpAsyncRequestExecutor;
+import org.apache.http.protocol.HttpContext;
class LoggingAsyncRequestExecutor extends HttpAsyncRequestExecutor {
@@ -128,4 +129,15 @@ class LoggingAsyncRequestExecutor extend
super.timeout(conn);
}
+ @Override
+ public void endOfInput(final NHttpClientConnection conn) throws IOException {
+ super.endOfInput(conn);
+ HttpContext context = conn.getContext();
+ DefaultAsyncRequestDirector<?> handler = (DefaultAsyncRequestDirector<?>) context.getAttribute(
+ HTTP_HANDLER);
+ if (handler != null && !handler.isDone()) {
+ handler.endOfStream();
+ }
+ }
+
}
Modified: httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java?rev=1439906&r1=1439905&r2=1439906&view=diff
==============================================================================
--- httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java (original)
+++ httpcomponents/httpasyncclient/trunk/httpasyncclient/src/test/java/org/apache/http/nio/client/integration/TestClientAuthentication.java Tue Jan 29 14:31:40 2013
@@ -60,13 +60,17 @@ import org.apache.http.localserver.Basic
import org.apache.http.localserver.RequestBasicAuth;
import org.apache.http.localserver.ResponseBasicUnauthorized;
import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.nio.NHttpConnection;
import org.apache.http.nio.NHttpConnectionFactory;
import org.apache.http.nio.entity.NByteArrayEntity;
import org.apache.http.nio.entity.NStringEntity;
+import org.apache.http.nio.protocol.BasicAsyncRequestConsumer;
import org.apache.http.nio.protocol.BasicAsyncRequestHandler;
import org.apache.http.nio.protocol.BasicAsyncResponseProducer;
import org.apache.http.nio.protocol.HttpAsyncExchange;
import org.apache.http.nio.protocol.HttpAsyncExpectationVerifier;
+import org.apache.http.nio.protocol.HttpAsyncRequestConsumer;
+import org.apache.http.nio.protocol.HttpAsyncRequestHandler;
import org.apache.http.nio.protocol.HttpAsyncRequestHandlerRegistry;
import org.apache.http.nio.protocol.HttpAsyncRequestHandlerResolver;
import org.apache.http.nio.protocol.HttpAsyncService;
@@ -75,6 +79,7 @@ import org.apache.http.nio.reactor.Liste
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpContext;
+import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpRequestHandler;
@@ -173,6 +178,7 @@ public class TestClientAuthentication ex
final String creds = (String) context.getAttribute("creds");
if (creds == null || !creds.equals("test:test")) {
response.setStatusCode(HttpStatus.SC_UNAUTHORIZED);
+ response.setEntity(new NStringEntity("Unauthorized"));
} else {
response.setStatusCode(HttpStatus.SC_OK);
final NStringEntity entity = new NStringEntity("success", Consts.ASCII);
@@ -349,6 +355,67 @@ public class TestClientAuthentication ex
Assert.assertEquals("test realm", authscope.getRealm());
}
+ public class FaultyRequestHandler implements HttpAsyncRequestHandler<HttpRequest> {
+
+ private final HttpRequestHandler handler;
+
+ public FaultyRequestHandler(final HttpRequestHandler handler) {
+ super();
+ if (handler == null) {
+ throw new IllegalArgumentException("Request handler may not be null");
+ }
+ this.handler = handler;
+ }
+
+ public HttpAsyncRequestConsumer<HttpRequest> processRequest(final HttpRequest request,
+ final HttpContext context) {
+ return new BasicAsyncRequestConsumer();
+ }
+
+ public void handle(
+ final HttpRequest request,
+ final HttpAsyncExchange httpexchange,
+ final HttpContext context) throws HttpException, IOException {
+ HttpResponse response = httpexchange.getResponse();
+ this.handler.handle(request, httpexchange.getResponse(), context);
+ httpexchange.submitResponse(new BasicAsyncResponseProducer(response) {
+
+ @Override
+ public void responseCompleted(final HttpContext context) {
+ super.responseCompleted(context);
+ NHttpConnection conn = (NHttpConnection)context.getAttribute(
+ ExecutionContext.HTTP_CONNECTION);
+ try {
+ conn.shutdown();
+ } catch (IOException e) {
+ }
+ }
+
+ });
+ }
+
+ }
+
+ @Test
+ public void testBasicAuthenticationFaultyPersistentConnection() throws Exception {
+ HttpAsyncRequestHandlerRegistry registry = new HttpAsyncRequestHandlerRegistry();
+ registry.register("*", new FaultyRequestHandler(new AuthHandler(true)));
+ HttpHost target = start(registry, null);
+
+ TestCredentialsProvider credsProvider = new TestCredentialsProvider(
+ new UsernamePasswordCredentials("test", "test"));
+ this.httpclient.setCredentialsProvider(credsProvider);
+
+ HttpGet httpget = new HttpGet("/");
+ Future<HttpResponse> future = this.httpclient.execute(target, httpget, null);
+ HttpResponse response = future.get();
+ Assert.assertNotNull(response);
+ Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+ AuthScope authscope = credsProvider.getAuthScope();
+ Assert.assertNotNull(authscope);
+ Assert.assertEquals("test realm", authscope.getRealm());
+ }
+
@Test
public void testBasicAuthenticationSuccessWithNonRepeatableExpectContinue() throws Exception {
final HttpAsyncRequestHandlerRegistry registry = new HttpAsyncRequestHandlerRegistry();