Index: module-nio/src/test/java/org/apache/http/nio/protocol/TestAsyncNHttpHandlers.java =================================================================== --- module-nio/src/test/java/org/apache/http/nio/protocol/TestAsyncNHttpHandlers.java (revision 630309) +++ module-nio/src/test/java/org/apache/http/nio/protocol/TestAsyncNHttpHandlers.java (working copy) @@ -50,7 +50,6 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; -import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.DefaultHttpResponseFactory; import org.apache.http.message.BasicHttpEntityEnclosingRequest; @@ -202,7 +201,7 @@ } private NHttpClientHandler createHttpClientHandler( - final HttpRequestExecutionHandler requestExecutionHandler) { + final NHttpRequestExecutionHandler requestExecutionHandler) { BasicHttpProcessor httpproc = new BasicHttpProcessor(); httpproc.addInterceptor(new RequestContent()); httpproc.addInterceptor(new RequestTargetHost()); @@ -210,7 +209,7 @@ httpproc.addInterceptor(new RequestUserAgent()); httpproc.addInterceptor(new RequestExpectContinue()); - BufferingHttpClientHandler clientHandler = new BufferingHttpClientHandler( + AsyncNHttpClientHandler clientHandler = new AsyncNHttpClientHandler( httpproc, requestExecutionHandler, new DefaultConnectionReuseStrategy(), @@ -262,7 +261,7 @@ }; - HttpRequestExecutionHandler requestExecutionHandler = new HttpRequestExecutionHandler() { + NHttpRequestExecutionHandler requestExecutionHandler = new NHttpRequestExecutionHandler() { public void initalizeContext(final HttpContext context, final Object attachment) { context.setAttribute("LIST", (ByteSequence) attachment); @@ -282,6 +281,13 @@ } return get; } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) + throws IOException { + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public void handleResponse(final HttpResponse response, final HttpContext context) { NHttpConnection conn = (NHttpConnection) context.getAttribute( @@ -394,7 +400,7 @@ } }; - HttpRequestExecutionHandler requestExecutionHandler = new HttpRequestExecutionHandler() { + NHttpRequestExecutionHandler requestExecutionHandler = new NHttpRequestExecutionHandler() { public void initalizeContext(final HttpContext context, final Object attachment) { context.setAttribute("LIST", (ByteSequence) attachment); @@ -404,6 +410,13 @@ public void finalizeContext(final HttpContext context) { } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) + throws IOException { + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public HttpRequest submitRequest(final HttpContext context) { int i = ((Integer) context.getAttribute("REQ-COUNT")).intValue(); @@ -412,7 +425,7 @@ post = new BasicHttpEntityEnclosingRequest("POST", "/?" + i); byte[] data = requestData.getBytes(i); - ByteArrayEntity outgoing = new ByteArrayEntity(data); + NByteArrayEntity outgoing = new NByteArrayEntity(data); post.setEntity(outgoing); context.setAttribute("REQ-COUNT", new Integer(i + 1)); @@ -533,7 +546,7 @@ } }; - HttpRequestExecutionHandler requestExecutionHandler = new HttpRequestExecutionHandler() { + NHttpRequestExecutionHandler requestExecutionHandler = new NHttpRequestExecutionHandler() { public void initalizeContext(final HttpContext context, final Object attachment) { context.setAttribute("LIST", (ByteSequence) attachment); @@ -543,6 +556,13 @@ public void finalizeContext(final HttpContext context) { } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) + throws IOException { + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public HttpRequest submitRequest(final HttpContext context) { int i = ((Integer) context.getAttribute("REQ-COUNT")).intValue(); @@ -550,7 +570,7 @@ if (i < reqNo) { post = new BasicHttpEntityEnclosingRequest("POST", "/?" + i); byte[] data = requestData.getBytes(i); - ByteArrayEntity outgoing = new ByteArrayEntity(data); + NByteArrayEntity outgoing = new NByteArrayEntity(data); outgoing.setChunked(true); post.setEntity(outgoing); @@ -679,7 +699,7 @@ this.client.getParams().setParameter( CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0); - HttpRequestExecutionHandler requestExecutionHandler = new HttpRequestExecutionHandler() { + NHttpRequestExecutionHandler requestExecutionHandler = new NHttpRequestExecutionHandler() { public void initalizeContext(final HttpContext context, final Object attachment) { context.setAttribute("LIST", (ByteSequence) attachment); @@ -690,13 +710,20 @@ public void finalizeContext(final HttpContext context) { } + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) + throws IOException { + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } + public HttpRequest submitRequest(final HttpContext context) { int i = ((Integer) context.getAttribute("REQ-COUNT")).intValue(); BasicHttpEntityEnclosingRequest post = null; if (i < reqNo) { post = new BasicHttpEntityEnclosingRequest("POST", "/?" + i); byte[] data = requestData.getBytes(i); - ByteArrayEntity outgoing = new ByteArrayEntity(data); + NByteArrayEntity outgoing = new NByteArrayEntity(data); post.setEntity(outgoing); context.setAttribute("REQ-COUNT", new Integer(i + 1)); @@ -820,7 +847,7 @@ // Activate 'expect: continue' handshake this.client.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, true); - HttpRequestExecutionHandler requestExecutionHandler = new HttpRequestExecutionHandler() { + NHttpRequestExecutionHandler requestExecutionHandler = new NHttpRequestExecutionHandler() { public void initalizeContext(final HttpContext context, final Object attachment) { context.setAttribute("LIST", (ByteSequence) attachment); @@ -830,6 +857,13 @@ public void finalizeContext(final HttpContext context) { } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) + throws IOException { + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public HttpRequest submitRequest(final HttpContext context) { int i = ((Integer) context.getAttribute("REQ-COUNT")).intValue(); @@ -837,7 +871,7 @@ if (i < reqNo) { post = new BasicHttpEntityEnclosingRequest("POST", "/?" + i); byte[] data = requestData.getBytes(i); - ByteArrayEntity outgoing = new ByteArrayEntity(data); + NByteArrayEntity outgoing = new NByteArrayEntity(data); outgoing.setChunked(true); post.setEntity(outgoing); @@ -973,7 +1007,7 @@ // Activate 'expect: continue' handshake this.client.getParams().setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, true); - HttpRequestExecutionHandler requestExecutionHandler = new HttpRequestExecutionHandler() { + NHttpRequestExecutionHandler requestExecutionHandler = new NHttpRequestExecutionHandler() { public void initalizeContext(final HttpContext context, final Object attachment) { context.setAttribute("LIST", (ResponseSequence) attachment); @@ -983,6 +1017,13 @@ public void finalizeContext(final HttpContext context) { } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) + throws IOException { + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public HttpRequest submitRequest(final HttpContext context) { int i = ((Integer) context.getAttribute("REQ-COUNT")).intValue(); @@ -990,7 +1031,7 @@ if (i < reqNo) { post = new BasicHttpEntityEnclosingRequest("POST", "/"); post.addHeader("Secret", Integer.toString(i)); - ByteArrayEntity outgoing = new ByteArrayEntity( + NByteArrayEntity outgoing = new NByteArrayEntity( EncodingUtils.getAsciiBytes("No content")); post.setEntity(outgoing); @@ -1011,7 +1052,7 @@ HttpEntity entity = response.getEntity(); if (entity != null) { try { - entity.consumeContent(); + EntityUtils.toByteArray(entity); } catch (IOException ex) { requestCount.abort(); return; @@ -1112,7 +1153,7 @@ }; - HttpRequestExecutionHandler requestExecutionHandler = new HttpRequestExecutionHandler() { + NHttpRequestExecutionHandler requestExecutionHandler = new NHttpRequestExecutionHandler() { public void initalizeContext(final HttpContext context, final Object attachment) { context.setAttribute("LIST", (ResponseSequence) attachment); @@ -1122,6 +1163,13 @@ public void finalizeContext(final HttpContext context) { } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) + throws IOException { + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public HttpRequest submitRequest(final HttpContext context) { int i = ((Integer) context.getAttribute("REQ-COUNT")).intValue(); Index: module-nio/src/main/java/org/apache/http/nio/protocol/AbstractNHttpClientHandler.java =================================================================== --- module-nio/src/main/java/org/apache/http/nio/protocol/AbstractNHttpClientHandler.java (revision 0) +++ module-nio/src/main/java/org/apache/http/nio/protocol/AbstractNHttpClientHandler.java (revision 0) @@ -0,0 +1,75 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/branches/limewire_contrib/module-nio/src/main/java/org/apache/http/nio/protocol/NHttpClientHandlerBase.java $ + * $Revision: 547966 $ + * $Date: 2007-06-16 15:26:21 -0400 (Sat, 16 Jun 2007) $ + * + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.nio.protocol; + +import java.io.IOException; + +import org.apache.http.ConnectionReuseStrategy; +import org.apache.http.HttpException; +import org.apache.http.nio.NHttpClientConnection; +import org.apache.http.nio.NHttpClientHandler; +import org.apache.http.nio.util.ByteBufferAllocator; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpProcessor; + +public abstract class AbstractNHttpClientHandler extends NHttpHandlerBase + implements NHttpClientHandler { + + public AbstractNHttpClientHandler( + final HttpProcessor httpProcessor, + final ConnectionReuseStrategy connStrategy, + final ByteBufferAllocator allocator, + final HttpParams params) { + super(httpProcessor, connStrategy, allocator, params); + } + + public void closed(final NHttpClientConnection conn) { + if (this.eventListener != null) { + this.eventListener.connectionClosed(conn); + } + } + + public void exception(final NHttpClientConnection conn, final HttpException ex) { + closeConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalProtocolException(ex, conn); + } + } + + public void exception(final NHttpClientConnection conn, final IOException ex) { + shutdownConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalIOException(ex, conn); + } + } + +} Property changes on: module-nio/src/main/java/org/apache/http/nio/protocol/AbstractNHttpClientHandler.java ___________________________________________________________________ Name: svn:executable + * Index: module-nio/src/main/java/org/apache/http/nio/protocol/NHttpClientHandlerBase.java =================================================================== --- module-nio/src/main/java/org/apache/http/nio/protocol/NHttpClientHandlerBase.java (revision 630309) +++ module-nio/src/main/java/org/apache/http/nio/protocol/NHttpClientHandlerBase.java (working copy) @@ -36,13 +36,11 @@ import org.apache.http.ConnectionReuseStrategy; import org.apache.http.HttpException; import org.apache.http.nio.NHttpClientConnection; -import org.apache.http.nio.NHttpClientHandler; import org.apache.http.nio.util.ByteBufferAllocator; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpProcessor; -public abstract class NHttpClientHandlerBase extends NHttpHandlerBase - implements NHttpClientHandler { +public abstract class NHttpClientHandlerBase extends AbstractNHttpClientHandler { protected HttpRequestExecutionHandler execHandler; Index: module-nio/src/main/java/org/apache/http/nio/protocol/NHttpRequestExecutionHandler.java =================================================================== --- module-nio/src/main/java/org/apache/http/nio/protocol/NHttpRequestExecutionHandler.java (revision 0) +++ module-nio/src/main/java/org/apache/http/nio/protocol/NHttpRequestExecutionHandler.java (revision 0) @@ -0,0 +1,132 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/branches/limewire_contrib/module-nio/src/main/java/org/apache/http/nio/protocol/HttpRequestExecutionHandler.java $ + * $Revision: 613298 $ + * $Date: 2008-01-18 17:09:22 -0500 (Fri, 18 Jan 2008) $ + * + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.nio.protocol; + +import java.io.IOException; + +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.nio.entity.ConsumingNHttpEntity; +import org.apache.http.nio.entity.ProducingNHttpEntity; +import org.apache.http.nio.reactor.ConnectingIOReactor; +import org.apache.http.protocol.HttpContext; + +/** + * HTTP request execution handler can be used by client-side protocol handlers + * to trigger the submission of a new HTTP request and the processing of an HTTP + * response. When a new response entity is available for consumption, + * {@link #responseEntity(HttpRequest, HttpResponse, HttpContext)} is called. + * After the {@link ConsumingNHttpEntity} consumes the response body, + * {@link #handleResponse(HttpResponse, HttpContext)} is notified that the + * response is fully read. + * + * @author Oleg Kalnichevski + * + */ +public interface NHttpRequestExecutionHandler { + + /** + * Triggered when a new connection has been established and the + * HTTP context needs to be initialized. + * + *

The attachment object is the same object which was passed + * to the connecting I/O reactor when the connection request was + * made. The attachment may optionally contain some state information + * required in order to correctly initalize the HTTP context. + * + * @see ConnectingIOReactor#connect + * + * @param context the actual HTTP context + * @param attachment the object passed to the connecting I/O reactor + * upon the request for a new connection. + */ + void initalizeContext(HttpContext context, Object attachment); + + /** + * Triggered when the underlying connection is ready to send a new + * HTTP request to the target host. This method may return + * null if the client is not yet ready to send a + * request. In this case the connection will remain open and + * can be activated at a later point. + *

+ * If the request has an entity, the entity must be an + * instance of {@link ProducingNHttpEntity}. + * + * @param context the actual HTTP context + * @return an HTTP request to be sent or null if no + * request needs to be sent + */ + HttpRequest submitRequest(HttpContext context); + + /** + * Triggered when a response is received with an entity. This method should + * return a {@link ConsumingNHttpEntity} that will be used to consume the + * entity. null is a valid response value, and will indicate + * that the entity should be silently ignored. + *

+ * After the entity is fully consumed, + * {@link NHttpRequestExecutionHandler#handleResponse(HttpResponse, HttpContext)} + * is called to notify a full response & entity are ready to be processed. + * + * @param request + * The request that generated this response. + * @param response + * The response containing the existing entity. + * @param context + * the actual HTTP context + * @return An entity that will asynchronously consume the response's content + * body. + */ + ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) throws IOException; + + /** + * Triggered when an HTTP response is ready to be processed. + * + * @param response + * the HTTP response to be processed + * @param context + * the actual HTTP context + */ + void handleResponse(HttpResponse response, HttpContext context) + throws IOException; + + /** + * Triggered when the connection is terminated. This event can be used + * to release objects stored in the context or perform some other kind + * of cleanup. + * + * @param context the actual HTTP context + */ + void finalizeContext(HttpContext context); + +} Property changes on: module-nio/src/main/java/org/apache/http/nio/protocol/NHttpRequestExecutionHandler.java ___________________________________________________________________ Name: svn:executable + * Index: module-nio/src/main/java/org/apache/http/nio/protocol/AsyncNHttpClientHandler.java =================================================================== --- module-nio/src/main/java/org/apache/http/nio/protocol/AsyncNHttpClientHandler.java (revision 0) +++ module-nio/src/main/java/org/apache/http/nio/protocol/AsyncNHttpClientHandler.java (revision 0) @@ -0,0 +1,449 @@ +/* + * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/branches/limewire_contrib/module-nio/src/main/java/org/apache/http/nio/protocol/BufferingHttpClientHandler.java $ + * $Revision: 617652 $ + * $Date: 2008-02-01 16:18:00 -0500 (Fri, 01 Feb 2008) $ + * + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.nio.protocol; + +import java.io.IOException; + +import org.apache.http.ConnectionReuseStrategy; +import org.apache.http.HttpEntity; +import org.apache.http.HttpEntityEnclosingRequest; +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.nio.ContentDecoder; +import org.apache.http.nio.ContentEncoder; +import org.apache.http.nio.NHttpClientConnection; +import org.apache.http.nio.entity.ConsumingNHttpEntity; +import org.apache.http.nio.entity.ConsumingNHttpEntityTemplate; +import org.apache.http.nio.entity.ProducingNHttpEntity; +import org.apache.http.nio.entity.SkipContentListener; +import org.apache.http.nio.util.ByteBufferAllocator; +import org.apache.http.nio.util.HeapByteBufferAllocator; +import org.apache.http.params.CoreProtocolPNames; +import org.apache.http.params.DefaultedHttpParams; +import org.apache.http.params.HttpParams; +import org.apache.http.protocol.ExecutionContext; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpProcessor; + +/** + * HTTP client handler implementation that asynchronously reads & writes out the + * content of messages. + * + * @see ConsumingNHttpEntity + * @see ProducingNHttpEntity + * + * @author Oleg Kalnichevski + * @author Sam Berlin + * + */ +public class AsyncNHttpClientHandler extends AbstractNHttpClientHandler { + + protected NHttpRequestExecutionHandler execHandler; + + public AsyncNHttpClientHandler( + final HttpProcessor httpProcessor, + final NHttpRequestExecutionHandler execHandler, + final ConnectionReuseStrategy connStrategy, + final ByteBufferAllocator allocator, + final HttpParams params) { + super(httpProcessor, connStrategy, allocator, params); + if (execHandler == null) { + throw new IllegalArgumentException("HTTP request execution handler may not be null."); + } + this.execHandler = execHandler; + } + + public AsyncNHttpClientHandler( + final HttpProcessor httpProcessor, + final NHttpRequestExecutionHandler execHandler, + final ConnectionReuseStrategy connStrategy, + final HttpParams params) { + this(httpProcessor, execHandler, connStrategy, + new HeapByteBufferAllocator(), params); + } + + public void connected(final NHttpClientConnection conn, final Object attachment) { + HttpContext context = conn.getContext(); + + initialize(conn, attachment); + + ClientConnState connState = new ClientConnState(); + context.setAttribute(CONN_STATE, connState); + + if (this.eventListener != null) { + this.eventListener.connectionOpen(conn); + } + + requestReady(conn); + } + + @Override + public void closed(final NHttpClientConnection conn) { + HttpContext context = conn.getContext(); + + ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE); + connState.reset(); + + this.execHandler.finalizeContext(context); + + // TODO - replace with super.closed(conn); ? + if (this.eventListener != null) { + this.eventListener.connectionClosed(conn); + } + } + + public void requestReady(final NHttpClientConnection conn) { + HttpContext context = conn.getContext(); + + ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE); + if (connState.getOutputState() != ClientConnState.READY) { + return; + } + + try { + + HttpRequest request = this.execHandler.submitRequest(context); + if (request == null) { + return; + } + + request.setParams( + new DefaultedHttpParams(request.getParams(), this.params)); + + context.setAttribute(ExecutionContext.HTTP_REQUEST, request); + this.httpProcessor.process(request, context); + connState.setRequest(request); + conn.submitRequest(request); + connState.setOutputState(ClientConnState.REQUEST_SENT); + + if (request instanceof HttpEntityEnclosingRequest) { + HttpEntityEnclosingRequest entityReq = (HttpEntityEnclosingRequest)request; + ProducingNHttpEntity entity = (ProducingNHttpEntity)entityReq.getEntity(); + connState.setProducingEntity(entity); + + if (entityReq.expectContinue()) { + int timeout = conn.getSocketTimeout(); + connState.setTimeout(timeout); + timeout = this.params.getIntParameter( + CoreProtocolPNames.WAIT_FOR_CONTINUE, 3000); + conn.setSocketTimeout(timeout); + connState.setOutputState(ClientConnState.EXPECT_CONTINUE); + } + } + + } catch (IOException ex) { + shutdownConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalIOException(ex, conn); + } + } catch (HttpException ex) { + closeConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalProtocolException(ex, conn); + } + } + } + + public void inputReady(final NHttpClientConnection conn, final ContentDecoder decoder) { + HttpContext context = conn.getContext(); + + ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE); + + ConsumingNHttpEntity consumingEntity = connState.getConsumingEntity(); + + try { + consumingEntity.consumeContent(decoder, conn); + if (decoder.isCompleted()) { + processResponse(conn, connState); + } + + } catch (IOException ex) { + shutdownConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalIOException(ex, conn); + } + } catch (HttpException ex) { + closeConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalProtocolException(ex, conn); + } + } + } + + public void outputReady(final NHttpClientConnection conn, final ContentEncoder encoder) { + HttpContext context = conn.getContext(); + ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE); + + try { + if (connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) { + conn.suspendOutput(); + return; + } + + ProducingNHttpEntity entity = connState.getProducingEntity(); + + entity.produceContent(encoder, conn); + } catch (IOException ex) { + shutdownConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalIOException(ex, conn); + } + } + } + + public void responseReceived(final NHttpClientConnection conn) { + HttpContext context = conn.getContext(); + ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE); + + HttpResponse response = conn.getHttpResponse(); + response.setParams( + new DefaultedHttpParams(response.getParams(), this.params)); + + HttpRequest request = connState.getRequest(); + try { + + int statusCode = response.getStatusLine().getStatusCode(); + if (statusCode < HttpStatus.SC_OK) { + // 1xx intermediate response + if (statusCode == HttpStatus.SC_CONTINUE + && connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) { + continueRequest(conn, connState); + } + return; + } else { + connState.setResponse(response); + + if (connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) { + cancelRequest(conn, connState); + } + } + if (!canResponseHaveBody(request, response)) { + conn.resetInput(); + response.setEntity(null); + processResponse(conn, connState); + } else { + HttpEntity entity = response.getEntity(); + if(entity != null) { + ConsumingNHttpEntity consumingEntity = execHandler + .responseEntity(request, response, context); + if(consumingEntity == null) { + consumingEntity = new ConsumingNHttpEntityTemplate( + entity, new SkipContentListener(allocator)); + } + response.setEntity(consumingEntity); + connState.setConsumingEntity(consumingEntity); + } + } + + + } catch (IOException ex) { + shutdownConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalIOException(ex, conn); + } + } catch (HttpException ex) { + closeConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalProtocolException(ex, conn); + } + } + } + + public void timeout(final NHttpClientConnection conn) { + HttpContext context = conn.getContext(); + ClientConnState connState = (ClientConnState) context.getAttribute(CONN_STATE); + + try { + + if (connState.getOutputState() == ClientConnState.EXPECT_CONTINUE) { + continueRequest(conn, connState); + return; + } + + } catch (IOException ex) { + shutdownConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalIOException(ex, conn); + } + } + + handleTimeout(conn); + } + + private void initialize( + final NHttpClientConnection conn, + final Object attachment) { + HttpContext context = conn.getContext(); + + context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); + this.execHandler.initalizeContext(context, attachment); + } + + private void continueRequest( + final NHttpClientConnection conn, + final ClientConnState connState) throws IOException { + + int timeout = connState.getTimeout(); + conn.setSocketTimeout(timeout); + + conn.requestOutput(); + connState.setOutputState(ClientConnState.REQUEST_SENT); + } + + private void cancelRequest( + final NHttpClientConnection conn, + final ClientConnState connState) throws IOException { + + int timeout = connState.getTimeout(); + conn.setSocketTimeout(timeout); + + conn.resetOutput(); + connState.resetOutput(); + } + + private void processResponse( + final NHttpClientConnection conn, + final ClientConnState connState) throws IOException, HttpException { + + HttpContext context = conn.getContext(); + HttpResponse response = connState.getResponse(); + + context.setAttribute(ExecutionContext.HTTP_RESPONSE, response); + + this.httpProcessor.process(response, context); + + this.execHandler.handleResponse(response, context); + + if (!this.connStrategy.keepAlive(response, context)) { + conn.close(); + } else { + // Ready for another request + connState.resetInput(); + connState.resetOutput(); + conn.requestOutput(); + } + } + + protected static class ClientConnState { + + public static final int READY = 0; + public static final int REQUEST_SENT = 1; + public static final int EXPECT_CONTINUE = 2; + public static final int REQUEST_BODY_STREAM = 4; + public static final int REQUEST_BODY_DONE = 8; + public static final int RESPONSE_RECEIVED = 16; + public static final int RESPONSE_BODY_STREAM = 32; + public static final int RESPONSE_BODY_DONE = 64; + + private int outputState; + + private HttpRequest request; + private HttpResponse response; + private ConsumingNHttpEntity consumingEntity; + private ProducingNHttpEntity producingEntity; + + private int timeout; + + public void setConsumingEntity(ConsumingNHttpEntity consumingEntity) { + this.consumingEntity = consumingEntity; + } + + public void setProducingEntity(ProducingNHttpEntity entity) { + this.producingEntity = entity; + } + + public ProducingNHttpEntity getProducingEntity() { + return producingEntity; + } + + public ConsumingNHttpEntity getConsumingEntity() { + return consumingEntity; + } + + public int getOutputState() { + return this.outputState; + } + + public void setOutputState(int outputState) { + this.outputState = outputState; + } + + public HttpRequest getRequest() { + return this.request; + } + + public void setRequest(final HttpRequest request) { + this.request = request; + } + + public HttpResponse getResponse() { + return this.response; + } + + public void setResponse(final HttpResponse response) { + this.response = response; + } + + public int getTimeout() { + return this.timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public void resetInput() { + this.response = null; + if(consumingEntity != null) { + consumingEntity.finish(); + consumingEntity = null; + } + } + + public void resetOutput() { + this.request = null; + if(producingEntity != null) { + producingEntity.finish(); + producingEntity = null; + } + this.outputState = READY; + } + + public void reset() { + resetInput(); + resetOutput(); + } + } + +} Property changes on: module-nio/src/main/java/org/apache/http/nio/protocol/AsyncNHttpClientHandler.java ___________________________________________________________________ Name: svn:executable + * Index: module-nio/src/examples/org/apache/http/examples/nio/NHttpClient.java =================================================================== --- module-nio/src/examples/org/apache/http/examples/nio/NHttpClient.java (revision 630309) +++ module-nio/src/examples/org/apache/http/examples/nio/NHttpClient.java (working copy) @@ -40,23 +40,26 @@ import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.impl.DefaultConnectionReuseStrategy; -import org.apache.http.params.BasicHttpParams; import org.apache.http.impl.nio.DefaultClientIOEventDispatch; import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; import org.apache.http.message.BasicHttpRequest; import org.apache.http.nio.NHttpConnection; -import org.apache.http.nio.protocol.BufferingHttpClientHandler; +import org.apache.http.nio.entity.BufferingNHttpEntity; +import org.apache.http.nio.entity.ConsumingNHttpEntity; +import org.apache.http.nio.protocol.AsyncNHttpClientHandler; import org.apache.http.nio.protocol.EventListener; -import org.apache.http.nio.protocol.HttpRequestExecutionHandler; +import org.apache.http.nio.protocol.NHttpRequestExecutionHandler; import org.apache.http.nio.reactor.ConnectingIOReactor; import org.apache.http.nio.reactor.IOEventDispatch; import org.apache.http.nio.reactor.SessionRequest; +import org.apache.http.nio.util.HeapByteBufferAllocator; +import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; -import org.apache.http.params.CoreProtocolPNames; import org.apache.http.protocol.BasicHttpProcessor; +import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; -import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.RequestConnControl; import org.apache.http.protocol.RequestContent; import org.apache.http.protocol.RequestExpectContinue; @@ -89,7 +92,7 @@ // I/O event and main threads RequestCount requestCount = new RequestCount(3); - BufferingHttpClientHandler handler = new BufferingHttpClientHandler( + AsyncNHttpClientHandler handler = new AsyncNHttpClientHandler( httpproc, new MyHttpRequestExecutionHandler(requestCount), new DefaultConnectionReuseStrategy(), @@ -147,7 +150,7 @@ System.out.println("Done"); } - static class MyHttpRequestExecutionHandler implements HttpRequestExecutionHandler { + static class MyHttpRequestExecutionHandler implements NHttpRequestExecutionHandler { private final static String REQUEST_SENT = "request-sent"; private final static String RESPONSE_RECEIVED = "response-received"; @@ -174,6 +177,13 @@ } } } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) throws IOException { + // Create an entity that will buffer the response in memory. + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public HttpRequest submitRequest(final HttpContext context) { HttpHost targetHost = (HttpHost) context.getAttribute( Index: module-nio/src/examples/org/apache/http/examples/nio/NHttpSSLClient.java =================================================================== --- module-nio/src/examples/org/apache/http/examples/nio/NHttpSSLClient.java (revision 630309) +++ module-nio/src/examples/org/apache/http/examples/nio/NHttpSSLClient.java (working copy) @@ -46,16 +46,19 @@ import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; import org.apache.http.message.BasicHttpRequest; import org.apache.http.nio.NHttpConnection; -import org.apache.http.nio.protocol.BufferingHttpClientHandler; +import org.apache.http.nio.entity.BufferingNHttpEntity; +import org.apache.http.nio.entity.ConsumingNHttpEntity; +import org.apache.http.nio.protocol.AsyncNHttpClientHandler; import org.apache.http.nio.protocol.EventListener; -import org.apache.http.nio.protocol.HttpRequestExecutionHandler; +import org.apache.http.nio.protocol.NHttpRequestExecutionHandler; import org.apache.http.nio.reactor.ConnectingIOReactor; import org.apache.http.nio.reactor.IOEventDispatch; import org.apache.http.nio.reactor.SessionRequest; +import org.apache.http.nio.util.HeapByteBufferAllocator; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.CoreConnectionPNames; +import org.apache.http.params.CoreProtocolPNames; import org.apache.http.params.HttpParams; -import org.apache.http.params.CoreProtocolPNames; import org.apache.http.protocol.BasicHttpProcessor; import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; @@ -95,7 +98,7 @@ // I/O event and main threads RequestCount requestCount = new RequestCount(3); - BufferingHttpClientHandler handler = new BufferingHttpClientHandler( + AsyncNHttpClientHandler handler = new AsyncNHttpClientHandler( httpproc, new MyHttpRequestExecutionHandler(requestCount), new DefaultConnectionReuseStrategy(), @@ -156,7 +159,7 @@ System.out.println("Done"); } - static class MyHttpRequestExecutionHandler implements HttpRequestExecutionHandler { + static class MyHttpRequestExecutionHandler implements NHttpRequestExecutionHandler { private final static String REQUEST_SENT = "request-sent"; private final static String RESPONSE_RECEIVED = "response-received"; @@ -183,6 +186,13 @@ } } } + + @Override + public ConsumingNHttpEntity responseEntity(HttpRequest request, + HttpResponse response, HttpContext context) throws IOException { + // Create an entity that will buffer the response in memory. + return new BufferingNHttpEntity(response.getEntity(), new HeapByteBufferAllocator()); + } public HttpRequest submitRequest(final HttpContext context) { HttpHost targetHost = (HttpHost) context.getAttribute(