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