+ * When using this {@link NHttpServiceHandler}, it is important to ensure + * that entities supplied for writing implement {@link ProducingNHttpEntity}. + * Doing so will allow the entity to be written out asynchronously. + * If entities supplied for writing do not implement + * ProducingNHttpEntity, a delegate is added that buffers the entire + * contents in memory. Additionally, the buffering might take place in the I/O + * thread, which could cause I/O to block temporarily. For best results, ensure + * that all entities set on {@link HttpResponse HttpResponses} from + * {@link HttpRequestHandler HttpRequestHandlers} implement + * ProducingNHttpEntity. + *
+ * If incoming requests have entities, HttpRequestHandlers must not call + * {@link HttpEntity#getContent()}. Doing so will throw an + * {@link UnsupportedOperationException}. Instead, handlers must expect that + * incoming entities implement {@link ConsumingNHttpEntity} and install a + * {@link ContentListener} on those entities. The ContentListener will be + * notified when new data is available for reading. After all data has been + * read, the response will automatically be sent. + *
+ * To support legacy HttpRequestHandlers that do use getContent(), you can + * wrap the handler within a {@link NBlockingHttpRequestHandler}. Doing so + * will allow the handler to be processed on a new thread, in a blocking + * manner. + *
+ * + * @author Oleg Kalnichevski + * @author Sam Berlin + * @author Steffen Pingel + * + */ +public class AsyncNHttpServiceHandler extends NHttpServiceHandlerBase + implements NHttpServiceHandler { + + public AsyncNHttpServiceHandler(final HttpProcessor httpProcessor, + final HttpResponseFactory responseFactory, + final ConnectionReuseStrategy connStrategy, + final ByteBufferAllocator allocator, final HttpParams params) { + super(httpProcessor, responseFactory, connStrategy, allocator, params); + } + + public AsyncNHttpServiceHandler(final HttpProcessor httpProcessor, + final HttpResponseFactory responseFactory, + final ConnectionReuseStrategy connStrategy, final HttpParams params) { + this(httpProcessor, responseFactory, connStrategy, + new HeapByteBufferAllocator(), params); + } + + + public void connected(final NHttpServerConnection conn) { + HttpContext context = conn.getContext(); + + ServerConnState connState = new ServerConnState(); + context.setAttribute(CONN_STATE, connState); + + if (this.eventListener != null) { + this.eventListener.connectionOpen(conn); + } + } + + public void requestReceived(final NHttpServerConnection conn) { + HttpContext context = conn.getContext(); + + HttpRequest request = conn.getHttpRequest(); + request.setParams(new DefaultedHttpParams(request.getParams(), this.params)); + + ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE); + + // Update connection state + connState.resetInput(); + connState.setRequest(request); + + ProtocolVersion ver = request.getRequestLine().getProtocolVersion(); + // TODO: should this be greaterEquals? + if (!ver.lessEquals(HttpVersion.HTTP_1_1)) { + // Downgrade protocol version if greater than HTTP/1.1 + ver = HttpVersion.HTTP_1_1; + } + + HttpResponse response; + + try { + + if (request instanceof HttpEntityEnclosingRequest) { + if (((HttpEntityEnclosingRequest) request).expectContinue()) { + response = this.responseFactory.newHttpResponse( + ver, HttpStatus.SC_CONTINUE, context); + response.setParams( + new DefaultedHttpParams(response.getParams(), this.params)); + + if (this.expectationVerifier != null) { + try { + this.expectationVerifier.verify(request, response, context); + } catch (HttpException ex) { + response = this.responseFactory.newHttpResponse( + HttpVersion.HTTP_1_0, + HttpStatus.SC_INTERNAL_SERVER_ERROR, + context); + response.setParams( + new DefaultedHttpParams(response.getParams(), this.params)); + handleException(ex, response); + } + } + + if (response.getStatusLine().getStatusCode() < 200) { + // Send 1xx response indicating the server expections + // have been met + conn.submitResponse(response); + } else { + // The request does not meet the server expections + conn.resetInput(); + connState.resetInput(); + sendResponse(conn, response); + } + } + // Request content is expected. + // Wait until the request content is fully received + } else { + // No request content is expected. + // Process request right away + conn.suspendInput(); + processRequest(conn, request); + sendResponse(conn, connState.getResponse()); + } + + } 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 closed(final NHttpServerConnection conn) { + ServerConnState connState = (ServerConnState) conn.getContext().getAttribute(CONN_STATE); + connState.resetOutput(); + + if (this.eventListener != null) { + this.eventListener.connectionClosed(conn); + } + } + + public void exception(final NHttpServerConnection conn, final HttpException httpex) { + if (conn.isResponseSubmitted()) { + closeConnection(conn, httpex); // TODO: Is this correct? + if (eventListener != null) { + eventListener.fatalProtocolException(httpex, conn); + } + return; + } + + HttpContext context = conn.getContext(); + try { + HttpResponse response = this.responseFactory.newHttpResponse( + HttpVersion.HTTP_1_0, HttpStatus.SC_INTERNAL_SERVER_ERROR, context); + response.setParams( + new DefaultedHttpParams(response.getParams(), this.params)); + handleException(httpex, response); + response.setEntity(null); + sendResponse(conn, response); + + } 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 NHttpServerConnection conn, final ContentDecoder decoder) { + HttpRequest request = conn.getHttpRequest(); + ServerConnState connState = (ServerConnState) conn.getContext().getAttribute(CONN_STATE); + HttpResponse response = connState.getResponse(); + + try { + if(response == null) { + HttpEntityEnclosingRequest entityReq = (HttpEntityEnclosingRequest) request; + // TODO: Can entityReq.getEntity() ever be null here? What to do if it is? + BasicConsumingNHttpEntity entity = new BasicConsumingNHttpEntity(entityReq.getEntity()); + entityReq.setEntity(entity); + connState.setConsumingEntity(entity); + processRequest(conn, request); + // If no listener was installed, install a listener that skips the content. + if(entity.getContentListener() == null) { + entity.setContentListener(new SkipContentListener(allocator)); + } + } + + ConsumingNHttpEntity entity = connState.getConsumingEntity(); + entity.consumeContent(decoder, conn); + + if (decoder.isCompleted()) { + conn.suspendInput(); + // If there is a blocking handler, it will notify us + // when the response is ready -- othewise we may be sending + // it before they have set an entity. + if(!connState.isHandlerBlocking()) + sendResponse(conn, connState.getResponse()); + } + + } 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 responseReady(final NHttpServerConnection conn) { + if (conn.isOpen()) { + conn.requestInput(); + // make sure any buffered requests are processed + if (((DefaultNHttpServerConnection)conn).hasBufferedInput()) { + ((DefaultNHttpServerConnection)conn).consumeInput(this); + } + } + } + + public void outputReady(final NHttpServerConnection conn, final ContentEncoder encoder) { + HttpContext context = conn.getContext(); + HttpResponse response = conn.getHttpResponse(); + + ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE); + + try { + ProducingNHttpEntity entity = connState.getProducingEntity(); + entity.produceContent(encoder, conn); + + if (encoder.isCompleted()) { + connState.resetOutput(); + if (!this.connStrategy.keepAlive(response, context)) { + conn.close(); + } + } + + } catch (IOException ex) { + shutdownConnection(conn, ex); + if (this.eventListener != null) { + this.eventListener.fatalIOException(ex, conn); + } + } + } + + @Override + protected void closeConnection(final HttpConnection conn, final Throwable cause) { + ServerConnState connState = (ServerConnState) ((NHttpServerConnection)conn).getContext().getAttribute(CONN_STATE); + connState.resetOutput(); + + super.closeConnection(conn, cause); + } + + @Override + protected void shutdownConnection(HttpConnection conn, Throwable cause) { + ServerConnState connState = (ServerConnState) ((NHttpServerConnection)conn).getContext().getAttribute(CONN_STATE); + connState.resetOutput(); + + super.shutdownConnection(conn, cause); + } + + private void handleException(final HttpException ex, final HttpResponse response) { + int code = HttpStatus.SC_INTERNAL_SERVER_ERROR; + if (ex instanceof MethodNotSupportedException) { + code = HttpStatus.SC_NOT_IMPLEMENTED; + } else if (ex instanceof UnsupportedHttpVersionException) { + code = HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED; + } else if (ex instanceof ProtocolException) { + code = HttpStatus.SC_BAD_REQUEST; + } + response.setStatusCode(code); + + byte[] msg = EncodingUtils.getAsciiBytes(ex.getMessage()); + NByteArrayEntity entity = new NByteArrayEntity(msg); + entity.setContentType("text/plain; charset=US-ASCII"); + response.setEntity(entity); + } + + private void processRequest( + final NHttpServerConnection conn, + final HttpRequest request) throws IOException, HttpException { + + HttpContext context = conn.getContext(); + ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE); + ProtocolVersion ver = request.getRequestLine().getProtocolVersion(); + + // TODO: Should this be 'greaterEquals'? -- It looks like it's going + // to upgrade HTTP 1.0 to HTTP 1.1, not downgrade 1.2 to 1.1. + if (!ver.lessEquals(HttpVersion.HTTP_1_1)) { + // Downgrade protocol version if greater than HTTP/1.1 + ver = HttpVersion.HTTP_1_1; + } + + HttpResponse response = this.responseFactory.newHttpResponse( + ver, + HttpStatus.SC_OK, + conn.getContext()); + response.setParams( + new DefaultedHttpParams(response.getParams(), this.params)); + + context.setAttribute(ExecutionContext.HTTP_REQUEST, request); + context.setAttribute(ExecutionContext.HTTP_CONNECTION, conn); + context.setAttribute(ExecutionContext.HTTP_RESPONSE, response); + + try { + + this.httpProcessor.process(request, context); + + HttpRequestHandler handler = null; + if (this.handlerResolver != null) { + String requestURI = request.getRequestLine().getUri(); + handler = this.handlerResolver.lookup(requestURI); + } + + connState.setHandlerBlocking(false); + if (handler != null) { + if(handler instanceof BlockingHandler && request instanceof HttpEntityEnclosingRequest && ((HttpEntityEnclosingRequest)request).getEntity() != null) { + connState.setHandlerBlocking(true); + ((BlockingHandler)handler).handle(request, response, context, new BlockingResponseListener(conn, this)); + } else { + handler.handle(request, response, context); + } + } else { + response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); + } + + } catch (HttpException ex) { + response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0, + HttpStatus.SC_INTERNAL_SERVER_ERROR, context); + response.setParams( + new DefaultedHttpParams(response.getParams(), this.params)); + handleException(ex, response); + } + + connState.setResponse(response); + } + + private void sendResponse(final NHttpServerConnection conn, + HttpResponse response) throws IOException, HttpException { + HttpContext context = conn.getContext(); + + ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE); + + this.httpProcessor.process(response, context); + + if (!canResponseHaveBody(connState.getRequest(), response)) { + response.setEntity(null); + } + + HttpEntity entity = response.getEntity(); + if (entity != null) { + if(entity instanceof ProducingNHttpEntity) + connState.setProducingEntity((ProducingNHttpEntity)entity); + else + connState.setProducingEntity(new NBlockingProducingNHttpEntity(entity, allocator)); + } else { + connState.resetOutput(); + if (!this.connStrategy.keepAlive(response, context)) { + conn.close(); + } + } + + connState.setResponse(null); + conn.submitResponse(response); + + } + + static class ServerConnState { + private volatile HttpRequest request; + private volatile HttpResponse response; + private volatile ConsumingNHttpEntity consumingEntity; + private volatile ProducingNHttpEntity producingEntity; + private volatile boolean handlerBlocking; + + public boolean isHandlerBlocking() { + return handlerBlocking; + } + + public void setHandlerBlocking(boolean blocking) { + this.handlerBlocking = blocking; + } + + public HttpResponse getResponse() { + return this.response; + } + + public void setResponse(HttpResponse response) { + this.response = response; + } + + public HttpRequest getRequest() { + return this.request; + } + + public ConsumingNHttpEntity getConsumingEntity() { + return consumingEntity; + } + + public void setConsumingEntity(ConsumingNHttpEntity entity) { + this.consumingEntity = entity; + } + + public ProducingNHttpEntity getProducingEntity() { + return this.producingEntity; + } + + public void setProducingEntity(ProducingNHttpEntity entity) { + this.producingEntity = entity; + } + + public void setRequest(final HttpRequest request) { + this.request = request; + } + + public void resetInput() { + this.request = null; + handlerBlocking = false; + if(consumingEntity != null) { + consumingEntity.finish(); + consumingEntity = null; + } + } + + public void resetOutput() { + response = null; + handlerBlocking = false; + if(producingEntity != null) { + producingEntity.finish(); + producingEntity = null; + } + } + } + + static class SkipContentListener implements ContentListener { + private final ByteBuffer buffer; + public SkipContentListener(ByteBufferAllocator allocator) { + this.buffer = allocator.allocate(2048); + } + + @Override + public int contentAvailable(ContentDecoder decoder, IOControl ioctrl) + throws IOException { + int totalRead = 0; + int lastRead; + do { + buffer.clear(); + lastRead = decoder.read(buffer); + if(lastRead > 0) + totalRead += lastRead; + } while(lastRead > 0); + return totalRead; + } + + @Override + public void finish() {} + } + + /** + * A response listener to help handle legacy HttpRequestHandlers that + * perform the handle in a new thread. + */ + static class BlockingResponseListener implements ResponseListener { + private final NHttpServerConnection conn; + private final AsyncNHttpServiceHandler handler; + + public BlockingResponseListener(NHttpServerConnection conn, AsyncNHttpServiceHandler handler) { + this.conn = conn; + this.handler = handler; + } + + @Override + public void handleException(HttpException httpException) { + HttpContext context = conn.getContext(); + HttpResponse response = handler.responseFactory.newHttpResponse(HttpVersion.HTTP_1_0, + HttpStatus.SC_INTERNAL_SERVER_ERROR, context); + response.setParams( + new DefaultedHttpParams(response.getParams(), handler.params)); + handler.handleException(httpException, response); + ServerConnState connState = (ServerConnState) context.getAttribute(CONN_STATE); + connState.setResponse(response); + responseReady(); + } + + @Override + public void handleException(IOException ioxException) { + handler.shutdownConnection(conn, ioxException); + if (handler.eventListener != null) { + handler.eventListener.fatalIOException(ioxException, conn); + } + } + + @Override + public void responseReady() { + ServerConnState connState = (ServerConnState) conn.getContext().getAttribute(CONN_STATE); + try { + handler.sendResponse(conn, connState.getResponse()); + } catch (IOException ex) { + handler.shutdownConnection(conn, ex); + if (handler.eventListener != null) { + handler.eventListener.fatalIOException(ex, conn); + } + } catch (HttpException ex) { + handler.closeConnection(conn, ex); + if (handler.eventListener != null) { + handler.eventListener.fatalProtocolException(ex, conn); + } + } + } + } + +} Property changes on: module-nio/src/main/java/org/apache/http/nio/protocol/AsyncNHttpServiceHandler.java ___________________________________________________________________ Name: svn:executable + * Index: module-nio/src/main/java/org/apache/http/nio/protocol/NBlockingHttpRequestHandler.java =================================================================== --- module-nio/src/main/java/org/apache/http/nio/protocol/NBlockingHttpRequestHandler.java (revision 0) +++ module-nio/src/main/java/org/apache/http/nio/protocol/NBlockingHttpRequestHandler.java (revision 0) @@ -0,0 +1,118 @@ +package org.apache.http.nio.protocol; + +import java.io.IOException; +import java.util.concurrent.Executor; + +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.nio.ContentDecoder; +import org.apache.http.nio.IOControl; +import org.apache.http.nio.entity.ConsumingNHttpEntity; +import org.apache.http.nio.entity.ContentBufferEntity; +import org.apache.http.nio.entity.ConsumingNHttpEntity.ContentListener; +import org.apache.http.nio.util.ByteBufferAllocator; +import org.apache.http.nio.util.ContentInputBuffer; +import org.apache.http.nio.util.HeapByteBufferAllocator; +import org.apache.http.nio.util.SharedInputBuffer; +import org.apache.http.protocol.ExecutionContext; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpRequestHandler; + +/** + * A {@link HttpRequestHandler} that is intended for use with + * {@link AsyncNHttpServiceHandler} and pre-existing HttpRequestHandlers that + * use {@link HttpEntity#getContent()}. Normally, calling getContent() on an + * entity from AsyncNHttpServiceHandler would throw an + * {@link UnsupportedOperationException}. This class will intercept the entity + * and allow getContent() to be called. However, a portion of the entity may be + * buffered in memory if classes do not quickly consume the content. + *
+ * Use this class sparingly. For best results, rewrite the handler to work with
+ * {@link ConsumingNHttpEntity}, and avoid using this class entirely.
+ *
+ * @author Sam Berlin
+ */
+public class NBlockingHttpRequestHandler implements HttpRequestHandler, BlockingHandler {
+
+ private final HttpRequestHandler delegate;
+ private final Executor executor;
+ private final ByteBufferAllocator allocator;
+
+ public NBlockingHttpRequestHandler(HttpRequestHandler delegate,
+ Executor executor) {
+ this(delegate, executor, new HeapByteBufferAllocator());
+ }
+
+ public NBlockingHttpRequestHandler(HttpRequestHandler delegate,
+ Executor executor, ByteBufferAllocator allocator) {
+ this.delegate = delegate;
+ this.executor = executor;
+ this.allocator = allocator;
+ }
+
+ @Override
+ public void handle(HttpRequest request, HttpResponse response,
+ HttpContext context) throws HttpException, IOException {
+ delegate.handle(request, response, context);
+ }
+
+ @Override
+ public void handle(final HttpRequest request, final HttpResponse response,
+ final HttpContext context, final ResponseListener responseListener)
+ throws HttpException, IOException {
+ if (request instanceof HttpEntityEnclosingRequest) {
+ HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+ if (entity instanceof ConsumingNHttpEntity) {
+ ConsumingNHttpEntity consumingEntity = (ConsumingNHttpEntity) entity;
+ IOControl ioctrl = (IOControl)context.getAttribute(ExecutionContext.HTTP_CONNECTION);
+ ContentReader reader = new ContentReader(allocator, ioctrl);
+ consumingEntity.setContentListener(reader);
+ entity = new ContentBufferEntity(consumingEntity, reader.getInputBuffer());
+ ((HttpEntityEnclosingRequest)request).setEntity(entity);
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ delegate.handle(request, response, context);
+ responseListener.responseReady();
+ } catch (HttpException e) {
+ responseListener.handleException(e);
+ } catch (IOException e) {
+ responseListener.handleException(e);
+ }
+ }
+ });
+ return;
+ }
+ }
+ // If we fell through, because no entity..
+ delegate.handle(request, response, context);
+ }
+
+ static class ContentReader implements ContentListener {
+ private SharedInputBuffer inputBuffer;
+
+ public ContentReader(ByteBufferAllocator allocator, IOControl ioctrl) {
+ inputBuffer = new SharedInputBuffer(2048, ioctrl, allocator);
+ }
+
+ @Override
+ public int contentAvailable(ContentDecoder decoder, IOControl ioctrl)
+ throws IOException {
+ int read = inputBuffer.consumeContent(decoder);
+ return read;
+ }
+
+ @Override
+ public void finish() {
+ inputBuffer.shutdown();
+ inputBuffer = null;
+ }
+
+ public ContentInputBuffer getInputBuffer() {
+ return inputBuffer;
+ }
+ }
+}
Property changes on: module-nio/src/main/java/org/apache/http/nio/protocol/NBlockingHttpRequestHandler.java
___________________________________________________________________
Name: svn:executable
+ *
Index: module-nio/src/main/java/org/apache/http/nio/ContentDecoderChannel.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/ContentDecoderChannel.java (revision 0)
+++ module-nio/src/main/java/org/apache/http/nio/ContentDecoderChannel.java (revision 0)
@@ -0,0 +1,36 @@
+package org.apache.http.nio;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+
+/**
+ * A {@link ReadableByteChannel} that delegates to a {@link ContentDecoder}.
+ * Attempts to close this channel are ignored, and isOpen always returns true.
+ *
+ * @author Sam Berlin
+ */
+public class ContentDecoderChannel implements ReadableByteChannel {
+
+ private final ContentDecoder decoder;
+
+ public ContentDecoderChannel(ContentDecoder decoder) {
+ this.decoder = decoder;
+ }
+
+ @Override
+ public int read(ByteBuffer dst) throws IOException {
+ return decoder.read(dst);
+ }
+
+ @Override
+ public void close() {}
+
+ @Override
+ public boolean isOpen() {
+ return true;
+ }
+
+
+
+}
Property changes on: module-nio/src/main/java/org/apache/http/nio/ContentDecoderChannel.java
___________________________________________________________________
Name: svn:executable
+ *
Index: module-nio/src/main/java/org/apache/http/nio/entity/ContentBufferEntity.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/ContentBufferEntity.java (revision 628295)
+++ module-nio/src/main/java/org/apache/http/nio/entity/ContentBufferEntity.java (working copy)
@@ -36,6 +36,7 @@
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.nio.util.ContentInputBuffer;
+@Deprecated
public class ContentBufferEntity extends BasicHttpEntity {
/** The wrapped entity. */
Index: module-nio/src/main/java/org/apache/http/nio/entity/ProducingNHttpEntity.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/ProducingNHttpEntity.java (revision 0)
+++ module-nio/src/main/java/org/apache/http/nio/entity/ProducingNHttpEntity.java (revision 0)
@@ -0,0 +1,22 @@
+package org.apache.http.nio.entity;
+
+import java.io.IOException;
+
+import org.apache.http.nio.ContentEncoder;
+import org.apache.http.nio.IOControl;
+
+/**
+ * An {@link NHttpEntity} that writes content to a {@link ContentEncoder}.
+ *
+ * @author Sam Berlin
+ */
+public interface ProducingNHttpEntity extends NHttpEntity {
+
+ /**
+ * Notification that content should be written to the encoder.
+ * When all content is finished, this MUST call {@link ContentEncoder#complete()}.
+ * Failure to do so could result in the entity never being written.
+ */
+ void produceContent(ContentEncoder encoder, IOControl ioctrl) throws IOException;
+
+}
Property changes on: module-nio/src/main/java/org/apache/http/nio/entity/ProducingNHttpEntity.java
___________________________________________________________________
Name: svn:executable
+ *
Index: module-nio/src/main/java/org/apache/http/nio/entity/ByteArrayNIOEntity.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/ByteArrayNIOEntity.java (revision 628295)
+++ module-nio/src/main/java/org/apache/http/nio/entity/ByteArrayNIOEntity.java (working copy)
@@ -1,7 +1,7 @@
/*
- * $HeadURL:$
- * $Revision:$
- * $Date:$
+ * $HeadURL$
+ * $Revision$
+ * $Date$
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
@@ -45,10 +45,11 @@
*
* @author Oleg Kalnichevski
*
- * @version $Revision:$
+ * @version $Revision$
*
* @since 4.0
*/
+@Deprecated
public class ByteArrayNIOEntity extends ByteArrayEntity implements HttpNIOEntity {
public ByteArrayNIOEntity(final byte[] b) {
Index: module-nio/src/main/java/org/apache/http/nio/entity/ContentInputStream.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/ContentInputStream.java (revision 628295)
+++ module-nio/src/main/java/org/apache/http/nio/entity/ContentInputStream.java (working copy)
@@ -36,6 +36,7 @@
import org.apache.http.nio.util.ContentInputBuffer;
+@Deprecated
public class ContentInputStream extends InputStream {
private final ContentInputBuffer buffer;
Index: module-nio/src/main/java/org/apache/http/nio/entity/ContentOutputStream.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/ContentOutputStream.java (revision 628295)
+++ module-nio/src/main/java/org/apache/http/nio/entity/ContentOutputStream.java (working copy)
@@ -36,6 +36,7 @@
import org.apache.http.nio.util.ContentOutputBuffer;
+@Deprecated
public class ContentOutputStream extends OutputStream {
private final ContentOutputBuffer buffer;
Index: module-nio/src/main/java/org/apache/http/nio/entity/NByteArrayEntity.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/NByteArrayEntity.java (revision 0)
+++ module-nio/src/main/java/org/apache/http/nio/entity/NByteArrayEntity.java (revision 0)
@@ -0,0 +1,87 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/entity/ByteArrayNIOEntity.java $
+ * $Revision: 602520 $
+ * $Date: 2007-12-08 12:42:26 -0500 (Sat, 08 Dec 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
+ *
+ * All blocking methods throw an {@link UnsupportedOperationException}.
+ *
+ * @author Sam Berlin
+ */
+public interface ConsumingNHttpEntity extends NHttpEntity {
+
+ /** Notification that content is availble for the entity to consume. */
+ int consumeContent(ContentDecoder decoder, IOControl ioctrl)
+ throws IOException;
+
+ /**
+ * Installs a listener on this entity that will be notified when content is
+ * available for consumption.
+ *
+ * If data is already available, the listener will be immediately notified.
+ */
+ void setContentListener(ContentListener listener);
+
+ /**
+ * A listener for available data on a non-blocking {@link ConsumingNHttpEntity}.
+ */
+ public static interface ContentListener {
+ /**
+ * Notification that content is available to be read from the decoder.
+ * Implementations must return the amount of data read from the decoder.
+ */
+ int contentAvailable(ContentDecoder decoder, IOControl ioctrl) throws IOException;
+
+ /** Notification that this entity has finished reading data. */
+ void finish();
+ }
+
+}
Property changes on: module-nio/src/main/java/org/apache/http/nio/entity/ConsumingNHttpEntity.java
___________________________________________________________________
Name: svn:executable
+ *
Index: module-nio/src/main/java/org/apache/http/nio/entity/AbstractNHttpEntity.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/AbstractNHttpEntity.java (revision 0)
+++ module-nio/src/main/java/org/apache/http/nio/entity/AbstractNHttpEntity.java (revision 0)
@@ -0,0 +1,37 @@
+package org.apache.http.nio.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.http.entity.AbstractHttpEntity;
+
+/**
+ * An abstract implementation of {@link NHttpEntity} that stubs out all blocking
+ * methods, throwing an {@link UnsupportedOperationException}.
+ */
+public abstract class AbstractNHttpEntity extends AbstractHttpEntity implements NHttpEntity {
+
+ @Override
+ public final InputStream getContent() throws IOException, IllegalStateException {
+ throw new UnsupportedOperationException("Does not support blocking methods");
+ }
+
+ @Override
+ public final boolean isStreaming() {
+ throw new UnsupportedOperationException("Does not support blocking methods");
+ }
+
+ @Override
+ public final void writeTo(OutputStream arg0) throws IOException {
+ throw new UnsupportedOperationException("Does not support blocking methods");
+ }
+
+ @Override
+ public final void consumeContent() throws IOException, UnsupportedOperationException {
+ throw new UnsupportedOperationException("Does not support blocking methods");
+ }
+
+
+
+}
Property changes on: module-nio/src/main/java/org/apache/http/nio/entity/AbstractNHttpEntity.java
___________________________________________________________________
Name: svn:executable
+ *
Index: module-nio/src/main/java/org/apache/http/nio/entity/BasicConsumingNHttpEntity.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/BasicConsumingNHttpEntity.java (revision 0)
+++ module-nio/src/main/java/org/apache/http/nio/entity/BasicConsumingNHttpEntity.java (revision 0)
@@ -0,0 +1,71 @@
+package org.apache.http.nio.entity;
+
+import java.io.IOException;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.nio.ContentDecoder;
+import org.apache.http.nio.IOControl;
+
+/**
+ * A default implementation of {@link ConsumingNHttpEntity}.
+ * Blocking output methods will throw {@link UnsupportedOperationException}.
+ *
+ * @author Sam Berlin
+ */
+public class BasicConsumingNHttpEntity extends AbstractNHttpEntity implements ConsumingNHttpEntity {
+
+ private ContentListener contentListener;
+ private long contentLength;
+
+ public BasicConsumingNHttpEntity(HttpEntity httpEntity) {
+ setChunked(httpEntity.isChunked());
+ setContentEncoding(httpEntity.getContentEncoding());
+ setContentLength(httpEntity.getContentLength());
+ setContentType(httpEntity.getContentType());
+ }
+
+ /**
+ * Specifies the length of the content.
+ *
+ * @param len the number of bytes in the content, or
+ * a negative number to indicate an unknown length
+ */
+ public void setContentLength(long len) {
+ this.contentLength = len;
+ }
+
+ @Override
+ public int consumeContent(ContentDecoder decoder, IOControl ioctrl)
+ throws IOException {
+ if(contentListener != null) {
+ return contentListener.contentAvailable(decoder, ioctrl);
+ } else {
+ throw new IllegalStateException("consuming content with no listener!");
+ }
+ }
+
+ @Override
+ public void setContentListener(ContentListener listener) {
+ this.contentListener = listener;
+ }
+
+ public ContentListener getContentListener() {
+ return contentListener;
+ }
+
+ @Override
+ public void finish() {
+ if(contentListener != null)
+ contentListener.finish();
+ }
+
+ @Override
+ public long getContentLength() {
+ return contentLength;
+ }
+
+ @Override
+ public boolean isRepeatable() {
+ return false;
+ }
+}
Property changes on: module-nio/src/main/java/org/apache/http/nio/entity/BasicConsumingNHttpEntity.java
___________________________________________________________________
Name: svn:executable
+ *
Index: module-nio/src/main/java/org/apache/http/nio/entity/NStringEntity.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/entity/NStringEntity.java (revision 0)
+++ module-nio/src/main/java/org/apache/http/nio/entity/NStringEntity.java (revision 0)
@@ -0,0 +1,98 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/entity/StringNIOEntity.java $
+ * $Revision: 602520 $
+ * $Date: 2007-12-08 12:42:26 -0500 (Sat, 08 Dec 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
+ *
+ * The entire output is buffered in memory.
+ *
+ * @see AsyncNHttpServiceHandler
+ *
+ * @author Sam Berlin
+ */
+public class NBlockingProducingNHttpEntity extends AbstractNHttpEntity implements
+ ProducingNHttpEntity {
+
+ private final HttpEntity entity;
+ private final ByteBufferAllocator allocator;
+ private SimpleOutputBuffer outputBuffer;
+
+ public NBlockingProducingNHttpEntity(HttpEntity entity, ByteBufferAllocator allocator) {
+ this.entity = entity;
+ this.allocator = allocator;
+ }
+
+ @Override
+ public void finish() {
+ }
+
+
+ @Override
+ public void produceContent(ContentEncoder encoder, IOControl ioctrl) throws IOException {
+ if(outputBuffer == null) {
+ outputBuffer = new SimpleOutputBuffer(2048, allocator);
+ entity.writeTo(new ContentOutputStream(outputBuffer));
+ }
+
+ outputBuffer.produceContent(encoder);
+ if(!outputBuffer.hasData())
+ encoder.complete();
+ }
+
+ @Override
+ public long getContentLength() {
+ return entity.getContentLength();
+ }
+
+ @Override
+ public boolean isRepeatable() {
+ return entity.isRepeatable();
+ }
+
+ @Override
+ public Header getContentEncoding() {
+ return entity.getContentEncoding();
+ }
+
+ @Override
+ public Header getContentType() {
+ return entity.getContentType();
+ }
+
+ @Override
+ public boolean isChunked() {
+ return entity.isChunked();
+ }
+}
Property changes on: module-nio/src/main/java/org/apache/http/nio/entity/NBlockingProducingNHttpEntity.java
___________________________________________________________________
Name: svn:executable
+ *
Index: module-nio/src/main/java/org/apache/http/nio/util/ContentInputBuffer.java
===================================================================
--- module-nio/src/main/java/org/apache/http/nio/util/ContentInputBuffer.java (revision 628295)
+++ module-nio/src/main/java/org/apache/http/nio/util/ContentInputBuffer.java (working copy)
@@ -35,6 +35,7 @@
import org.apache.http.nio.ContentDecoder;
+@Deprecated
public interface ContentInputBuffer {
int consumeContent(ContentDecoder decoder) throws IOException;
Index: module-nio/src/examples/org/apache/http/examples/nio/AsyncNHttpFileServer.java
===================================================================
--- module-nio/src/examples/org/apache/http/examples/nio/AsyncNHttpFileServer.java (revision 0)
+++ module-nio/src/examples/org/apache/http/examples/nio/AsyncNHttpFileServer.java (revision 0)
@@ -0,0 +1,243 @@
+/*
+ * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/examples/nio/NHttpFileServer.java $
+ * $Revision: 610763 $
+ * $Date: 2008-01-10 07:01:13 -0500 (Thu, 10 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
+ *