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 2007/04/22 23:36:40 UTC

svn commit: r531281 - in /jakarta/httpcomponents/httpcore/trunk/module-nio/src: main/java/org/apache/http/impl/nio/ main/java/org/apache/http/nio/ main/java/org/apache/http/nio/protocol/ test/java/org/apache/http/nio/protocol/

Author: olegk
Date: Sun Apr 22 14:36:39 2007
New Revision: 531281

URL: http://svn.apache.org/viewvc?view=rev&rev=531281
Log:
HTTPCORE-66: Fixed handling of HTTP HEAD methods in buffering client and service handlers

Contributed by Steffen Pingel <spingel at limewire.com>
Reviewed and committed with some modifications by Oleg Kalnichevski

Modified:
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpClientConnection.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpServerConnection.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpClientConnection.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpServerConnection.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpClientHandler.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpServiceHandler.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/test/java/org/apache/http/nio/protocol/TestNIOHttp.java

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpClientConnection.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpClientConnection.java?view=diff&rev=531281&r1=531280&r2=531281
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpClientConnection.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpClientConnection.java Sun Apr 22 14:36:39 2007
@@ -70,13 +70,13 @@
         this.session.setBufferStatus(this);
     }
 
-    private void resetInput() {
+    public void resetInput() {
         this.response = null;
         this.contentDecoder = null;
         this.responseParser.reset();
     }
     
-    private void resetOutput() {
+    public void resetOutput() {
         this.request = null;
         this.contentEncoder = null;
     }
@@ -91,15 +91,13 @@
                 int bytesRead = this.responseParser.fillBuffer(this.session.channel());
                 this.response = (HttpResponse) this.responseParser.parse(); 
                 if (this.response != null) {
-                    handler.responseReceived(this);
-                    
                     if (this.response.getStatusLine().getStatusCode() >= 200) {
                         HttpEntity entity = prepareDecoder(this.response);
                         this.response.setEntity(entity);
-                    } else {
-                        // Discard the intermediate response
-                        this.responseParser.reset();
-                        this.response = null;
+                    }
+                    handler.responseReceived(this);
+                    if (this.contentDecoder == null) {
+                        resetInput();
                     }
                 }
                 if (bytesRead == -1) {

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpServerConnection.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpServerConnection.java?view=diff&rev=531281&r1=531280&r2=531281
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpServerConnection.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/impl/nio/DefaultNHttpServerConnection.java Sun Apr 22 14:36:39 2007
@@ -67,13 +67,13 @@
         this.requestParser = new HttpRequestParser(this.inbuf, requestFactory);
     }
 
-    private void resetInput() {
+    public void resetInput() {
         this.request = null;
         this.contentDecoder = null;
         this.requestParser.reset();
     }
     
-    private void resetOutput() {
+    public void resetOutput() {
         this.response = null;
         this.contentEncoder = null;
     }
@@ -182,7 +182,8 @@
         this.lineBuffer.clear();
         this.outbuf.writeLine(this.lineBuffer);
 
-        if (response.getStatusLine().getStatusCode() >= 200) {
+        if (response.getStatusLine().getStatusCode() >= 200 
+                && response.getEntity() != null) {
             this.response = response;
             prepareEncoder(response);
         }
@@ -193,10 +194,6 @@
         return this.response != null;
     }
 
-    public void cancelRequest() {
-        resetInput();
-    }
-    
     public String toString() {
         StringBuffer buffer = new StringBuffer();
         buffer.append("[");

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpClientConnection.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpClientConnection.java?view=diff&rev=531281&r1=531280&r2=531281
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpClientConnection.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpClientConnection.java Sun Apr 22 14:36:39 2007
@@ -63,10 +63,15 @@
     boolean isRequestSubmitted();
     
     /**
-     * Cancels pending HTTP request if it failed the target server expections
-     * (the target server responded with non 1xx status code during the
-     * 'expect: continue' handshake.
+     * Resets input state. This method can be used to prematurely terminate 
+     * processing of the outgoing HTTP request.
      */
-    void cancelRequest();
+    void resetOutput();
     
+    /**
+     * Resets output state. This method can be used to prematurely terminate
+     * processing of the incoming HTTP response. 
+     */
+    void resetInput();
+
 }

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpServerConnection.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpServerConnection.java?view=diff&rev=531281&r1=531280&r2=531281
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpServerConnection.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/NHttpServerConnection.java Sun Apr 22 14:36:39 2007
@@ -64,10 +64,15 @@
     boolean isResponseSubmitted();
     
     /**
-     * Cancels pending HTTP request if it failed the target server expections
-     * (the target server responded with non 1xx status code during the
-     * 'expect: continue' handshake).
+     * Resets output state. This method can be used to prematurely terminate
+     * processing of the incoming HTTP request. 
      */
-    void cancelRequest();
+    void resetInput();
+    
+    /**
+     * Resets input state. This method can be used to prematurely terminate 
+     * processing of the outgoing HTTP response.
+     */
+    void resetOutput();
     
 }

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpClientHandler.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpClientHandler.java?view=diff&rev=531281&r1=531280&r2=531281
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpClientHandler.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpClientHandler.java Sun Apr 22 14:36:39 2007
@@ -279,8 +279,9 @@
                     cancelRequest(conn, connState);
                 }
             }
-            
             if (!canResponseHaveBody(request, response)) {
+                conn.resetInput();
+                response.setEntity(null);
                 processResponse(conn, connState);
             }
             
@@ -389,7 +390,7 @@
         int timeout = connState.getTimeout();
         conn.setSocketTimeout(timeout);
 
-        conn.cancelRequest();
+        conn.resetOutput();
         connState.resetOutput();
     }
     
@@ -426,7 +427,6 @@
         HttpContext context = conn.getContext();
         HttpResponse response = connState.getResponse();
         
-        // Create a wrapper entity instead of the original one
         if (response.getEntity() != null) {
             response.setEntity(new BufferedContent(
                     response.getEntity(), 

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpServiceHandler.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpServiceHandler.java?view=diff&rev=531281&r1=531280&r2=531281
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpServiceHandler.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpServiceHandler.java Sun Apr 22 14:36:39 2007
@@ -190,7 +190,7 @@
                         conn.submitResponse(response);
                     } else {
                         // The request does not meet the server expections
-                        conn.cancelRequest();
+                        conn.resetInput();
                         connState.resetInput();
                         sendResponse(conn, response);
                     }
@@ -422,23 +422,39 @@
 
         this.httpProcessor.process(response, context);
 
+        if (!canResponseHaveBody(connState.getRequest(), response)) {
+            response.setEntity(null);
+        }
+        
         conn.submitResponse(response);
 
         // Update connection state
         connState.setOutputState(ServerConnState.RESPONSE_SENT);
-        
-        if (response.getEntity() != null) {
-            HttpEntity entity = response.getEntity();
-            if (entity != null) {
-                OutputStream outstream = new ContentOutputStream(buffer);
-                entity.writeTo(outstream);
-                outstream.flush();
-                outstream.close();
-            }
+
+        HttpEntity entity = response.getEntity();
+        if (entity != null) {
+            OutputStream outstream = new ContentOutputStream(buffer);
+            entity.writeTo(outstream);
+            outstream.flush();
+            outstream.close();
         } else {
             connState.resetOutput();
             conn.requestInput();
         }
+    }
+
+    private boolean canResponseHaveBody(
+            final HttpRequest request, final HttpResponse response) {
+
+        if (request != null && "HEAD".equalsIgnoreCase(request.getRequestLine().getMethod())) {
+            return false;
+        }
+        
+        int status = response.getStatusLine().getStatusCode(); 
+        return status >= HttpStatus.SC_OK 
+            && status != HttpStatus.SC_NO_CONTENT 
+            && status != HttpStatus.SC_NOT_MODIFIED
+            && status != HttpStatus.SC_RESET_CONTENT; 
     }
 
     static class ServerConnState {

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/test/java/org/apache/http/nio/protocol/TestNIOHttp.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/test/java/org/apache/http/nio/protocol/TestNIOHttp.java?view=diff&rev=531281&r1=531280&r2=531281
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/test/java/org/apache/http/nio/protocol/TestNIOHttp.java (original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/test/java/org/apache/http/nio/protocol/TestNIOHttp.java Sun Apr 22 14:36:39 2007
@@ -38,6 +38,10 @@
 import java.util.List;
 import java.util.Random;
 
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpEntityEnclosingRequest;
@@ -53,7 +57,6 @@
 import org.apache.http.nio.NHttpConnection;
 import org.apache.http.nio.mockup.TestHttpClient;
 import org.apache.http.nio.mockup.TestHttpServer;
-import org.apache.http.nio.protocol.HttpRequestExecutionHandler;
 import org.apache.http.params.HttpProtocolParams;
 import org.apache.http.protocol.HttpContext;
 import org.apache.http.protocol.HttpExecutionContext;
@@ -62,10 +65,6 @@
 import org.apache.http.util.EncodingUtils;
 import org.apache.http.util.EntityUtils;
 
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
 /**
  * HttpCore NIO integration tests.
  *
@@ -911,6 +910,161 @@
         assertEquals(HttpStatus.SC_EXPECTATION_FAILED, response.getStatusLine().getStatusCode());
         response = (HttpResponse) responses.get(2);
         assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
+    }
+    
+    /**
+     * This test case executes a series of simple (non-pipelined) HEAD requests 
+     * over multiple connections. 
+     */
+    public void testSimpleHttpHeads() throws Exception {
+        
+        final int connNo = 3;
+        final int reqNo = 20;
+        
+        final String[] method = new String[1];
+        
+        Random rnd = new Random();
+        
+        // Prepare some random data
+        final List testData = new ArrayList(reqNo);
+        for (int i = 0; i < reqNo; i++) {
+            int size = rnd.nextInt(5000);
+            byte[] data = new byte[size];
+            rnd.nextBytes(data);
+            testData.add(data);
+        }
+        
+        List[] responseData = new List[connNo];
+        for (int i = 0; i < responseData.length; i++) {
+            responseData[i] = new ArrayList();
+        }
+        
+        // Initialize the server-side request handler
+        this.server.registerHandler("*", new HttpRequestHandler() {
+
+            public void handle(
+                    final HttpRequest request, 
+                    final HttpResponse response, 
+                    final HttpContext context) throws HttpException, IOException {
+                
+                String s = request.getRequestLine().getUri();
+                URI uri;
+                try {
+                    uri = new URI(s);
+                } catch (URISyntaxException ex) {
+                    throw new HttpException("Invalid request URI: " + s);
+                }
+                int index = Integer.parseInt(uri.getQuery());
+
+                byte[] data = (byte []) testData.get(index);
+                ByteArrayEntity entity = new ByteArrayEntity(data); 
+                response.setEntity(entity);
+            }
+            
+        });
+        
+        // Initialize the client side request executor
+        this.client.setHttpRequestExecutionHandler(new HttpRequestExecutionHandler() {
+
+            public void initalizeContext(final HttpContext context, final Object attachment) {
+                context.setAttribute("LIST", (List) attachment);
+                context.setAttribute("REQ-COUNT", new Integer(0));
+                context.setAttribute("RES-COUNT", new Integer(0));
+            }
+
+            public HttpRequest submitRequest(final HttpContext context) {
+                int i = ((Integer) context.getAttribute("REQ-COUNT")).intValue();
+                BasicHttpEntityEnclosingRequest request = null;
+                if (i < reqNo) {
+                    request = new BasicHttpEntityEnclosingRequest(method[0], "/?" + i);
+                    context.setAttribute("REQ-COUNT", new Integer(i + 1));
+                }
+                return request;
+            }
+            
+            public void handleResponse(final HttpResponse response, final HttpContext context) {
+                NHttpConnection conn = (NHttpConnection) context.getAttribute(
+                        HttpExecutionContext.HTTP_CONNECTION);
+                
+                List list = (List) context.getAttribute("LIST");
+                int i = ((Integer) context.getAttribute("RES-COUNT")).intValue();
+                i++;
+                context.setAttribute("RES-COUNT", new Integer(i));
+
+                list.add(response);
+
+                if (i < reqNo) {
+                    conn.requestInput();
+                } else {
+                    try {
+                        conn.close();
+                    } catch (IOException ex) {
+                        fail(ex.getMessage());
+                    }
+                }
+            }
+            
+        });
+        
+        this.server.start();
+        this.client.start();
+        
+        InetSocketAddress serverAddress = (InetSocketAddress) this.server.getSocketAddress();
+
+        method[0] = "GET";
+        
+        for (int i = 0; i < responseData.length; i++) {
+            this.client.openConnection(
+                    new InetSocketAddress("localhost", serverAddress.getPort()), 
+                    responseData[i]);
+        }
+     
+        this.client.await(connNo, 1000);
+        assertEquals(connNo, this.client.getConnCount());
+
+        List[] responseDataGET = responseData; 
+
+        method[0] = "HEAD";
+
+        responseData = new List[connNo];
+        for (int i = 0; i < responseData.length; i++) {
+            responseData[i] = new ArrayList();
+        }
+        
+        for (int i = 0; i < responseData.length; i++) {
+            this.client.openConnection(
+                    new InetSocketAddress("localhost", serverAddress.getPort()), 
+                    responseData[i]);
+        }
+     
+        this.client.await(connNo * 2, 1000);
+        assertEquals(connNo * 2, this.client.getConnCount());
+        
+        this.client.shutdown();
+        this.server.shutdown();
+
+        for (int c = 0; c < responseData.length; c++) {
+            List headResponses = responseData[c];
+            List getResponses = responseDataGET[c];
+            List expectedPackets = testData;
+            assertEquals(expectedPackets.size(), headResponses.size());
+            assertEquals(expectedPackets.size(), getResponses.size());
+            for (int p = 0; p < testData.size(); p++) {
+                HttpResponse getResponse = (HttpResponse) getResponses.get(p);
+                HttpResponse headResponse = (HttpResponse) headResponses.get(p);
+                assertEquals(null, headResponse.getEntity());
+                
+                Header[] getHeaders = getResponse.getAllHeaders();
+                Header[] headHeaders = headResponse.getAllHeaders();
+                assertEquals(getHeaders.length, headHeaders.length);
+                for (int j = 0; j < getHeaders.length; j++) {
+                    if ("Date".equals(getHeaders[j].getName())) {
+                        continue;
+                    }
+                    assertEquals(getHeaders[j].toString(), headHeaders[j].toString());
+                }
+            }
+        }
     }
     
 }