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();