summaryrefslogtreecommitdiffstats
path: root/simple/simple-http/src/main/java/org/simpleframework/http/core
diff options
context:
space:
mode:
Diffstat (limited to 'simple/simple-http/src/main/java/org/simpleframework/http/core')
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoder.java108
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderException.java58
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderFactory.java118
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/BodyObserver.java121
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ChunkedEncoder.java221
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/CloseEncoder.java179
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/Collector.java50
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/Container.java62
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerController.java161
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerEvent.java93
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerSocketProcessor.java155
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerTransportProcessor.java96
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/Controller.java100
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/Conversation.java358
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/EmptyEncoder.java132
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/FixedLengthEncoder.java198
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/QueryBuilder.java148
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/QueryCombiner.java148
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCertificate.java183
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCollector.java184
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/RequestDispatcher.java128
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/RequestEntity.java398
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/RequestMessage.java341
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/RequestReader.java131
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseBuffer.java303
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEncoder.java324
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEntity.java437
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseException.java58
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseMessage.java283
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseObserver.java238
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/core/Timer.java94
31 files changed, 5608 insertions, 0 deletions
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoder.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoder.java
new file mode 100644
index 0000000..a2cd6dd
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoder.java
@@ -0,0 +1,108 @@
+/*
+ * BodyEncoder.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * The <code>BodyEncoder</code> object is used to encode content from
+ * the HTTP response. This acts in much the same way as an output
+ * stream would. As a requirement of RFC 2616 any HTTP/1.1 compliant
+ * server must support a set of transfer types. These are fixed size,
+ * chunked encoded, and connection close. A producer implementation
+ * is required to implement one of this formats for delivery of the
+ * response message.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.BodyObserver
+ */
+interface BodyEncoder {
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ */
+ void encode(byte[] array) throws IOException;
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ * @param off this is the offset within the array to send from
+ * @param size this is the number of bytes that are to be sent
+ */
+ void encode(byte[] array, int off, int size) throws IOException;
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ */
+ void encode(ByteBuffer buffer) throws IOException;
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ * @param off this is the offset within the buffer to send from
+ * @param size this is the number of bytes that are to be sent
+ */
+ void encode(ByteBuffer buffer, int off, int size) throws IOException;
+
+ /**
+ * This method is used to flush the contents of the buffer to
+ * the client. This method will block until such time as all of
+ * the data has been sent to the client. If at any point there
+ * is an error sending the content an exception is thrown.
+ */
+ void flush() throws IOException;
+
+ /**
+ * This is used to signal to the producer that all content has
+ * been written and the user no longer needs to write. This will
+ * either close the underlying transport or it will notify the
+ * monitor that the response has completed and the next request
+ * can begin. This ensures the content is flushed to the client.
+ */
+ void close() throws IOException;
+}
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderException.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderException.java
new file mode 100644
index 0000000..7a0a86a
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderException.java
@@ -0,0 +1,58 @@
+/*
+ * BodyEncoderException.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+/**
+ * The <code>BodyEncoderException</code> object is used to represent
+ * an exception that is thrown when there is a problem producing the
+ * response body. This can be used to wrap <code>IOException</code>
+ * objects that are thrown from the underlying transport.
+ *
+ * @author Niall Gallagher
+ */
+class BodyEncoderException extends IOException {
+
+ /**
+ * Constructor for the <code>BodyEncoderException</code> object. This
+ * is used to represent an exception that is thrown when producing
+ * the response body. The encoder exception is an I/O exception
+ * and thus exceptions can propagate out of stream methods.
+ *
+ * @param message this is the message describing the exception
+ */
+ public BodyEncoderException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor for the <code>BodyEncoderException</code> object. This
+ * is used to represent an exception that is thrown when producing
+ * the response body. The encoder exception is an I/O exception
+ * and thus exceptions can propagate out of stream methods.
+ *
+ * @param message this is the message describing the exception
+ * @param cause this is the cause of the encoder exception
+ */
+ public BodyEncoderException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderFactory.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderFactory.java
new file mode 100644
index 0000000..93aab52
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyEncoderFactory.java
@@ -0,0 +1,118 @@
+/*
+ * BodyEncoderFactory.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteWriter;
+
+/**
+ * The <code>BodyEncoderFactory</code> is used to create a producer to
+ * match the HTTP header sent with the response. This interprets the
+ * headers within the response and composes a producer that will
+ * match those. Producers can be created to send in chunked encoding
+ * format, as well as fixed length and connection close for HTTP/1.0.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.ResponseEncoder
+ */
+class BodyEncoderFactory {
+
+ /**
+ * This is used to determine the semantics of the HTTP pipeline.
+ */
+ private final Conversation support;
+
+ /**
+ * This is the monitor used to notify the initiator of events.
+ */
+ private final BodyObserver observer;
+
+ /**
+ * This is the underlying sender used to deliver the raw data.
+ */
+ private final ByteWriter writer;
+
+ /**
+ * Constructor for the <code>BodyEncoderFactory</code> object.
+ * This is used to create producers that can encode data in a HTTP
+ * compliant format. Each producer created will produce its data
+ * and deliver it to the specified sender, should an I/O events
+ * occur such as an error, or completion of the response then
+ * the monitor is notified and the server kernel takes action.
+ *
+ * @param observer this is used to deliver signals to the kernel
+ * @param support this contains details regarding the semantics
+ * @param writer this is used to send to the underlying transport
+ */
+ public BodyEncoderFactory(BodyObserver observer, Conversation support, Channel channel) {
+ this.writer = channel.getWriter();
+ this.observer = observer;
+ this.support = support;
+ }
+
+ /**
+ * This is used to create an a <code>BodyEncoder</code> object
+ * that can be used to encode content according to the HTTP header.
+ * If the request was from a HTTP/1.0 client that did not ask
+ * for keep alive connection semantics a simple close producer
+ * is created. Otherwise the content is chunked encoded or sent
+ * according the the Content-Length.
+ *
+ * @return this returns the producer used to send the response
+ */
+ public BodyEncoder getInstance() {
+ boolean keepAlive = support.isKeepAlive();
+ boolean chunkable = support.isChunkedEncoded();
+ boolean tunnel = support.isTunnel();
+
+ if(!keepAlive || tunnel) {
+ return new CloseEncoder(observer, writer);
+ }
+ return getInstance(chunkable);
+ }
+
+ /**
+ * This is used to create an a <code>BodyEncoder</code> object
+ * that can be used to encode content according to the HTTP header.
+ * If the request was from a HTTP/1.0 client that did not ask
+ * for keep alive connection semantics a simple close producer
+ * is created. Otherwise the content is chunked encoded or sent
+ * according the the Content-Length.
+ *
+ * @param chunkable does the connected client support chunked
+ *
+ * @return this returns the producer used to send the response
+ */
+ private BodyEncoder getInstance(boolean chunkable) {
+ long length = support.getContentLength();
+
+ if(!support.isHead()) {
+ if(length > 0) {
+ return new FixedLengthEncoder(observer, writer, length);
+ }
+ if(chunkable) {
+ return new ChunkedEncoder(observer, writer);
+ }
+ }
+ return new EmptyEncoder(observer, writer);
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyObserver.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyObserver.java
new file mode 100644
index 0000000..eebefc7
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/BodyObserver.java
@@ -0,0 +1,121 @@
+/*
+ * BodyObserver.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import org.simpleframework.transport.ByteWriter;
+
+/**
+ * The <code>BodyObserver</code> object is core to how the requests
+ * are processed from a pipeline. This observes the progress of the
+ * response streams as they are written to the underlying transport
+ * which is typically TCP. If at any point there is an error in
+ * the delivery of the response the observer is notified. It can
+ * then shutdown the connection, as RFC 2616 suggests on errors.
+ * <p>
+ * If however the response is delivered successfully the monitor is
+ * notified of this event. On successful delivery the monitor will
+ * hand the <code>Channel</code> back to the server kernel so that
+ * the next request can be processed. This ensures ordering of the
+ * responses matches ordering of the requests.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.Controller
+ */
+interface BodyObserver {
+
+ /**
+ * This is used to close the underlying transport. A closure is
+ * typically done when the response is to a HTTP/1.0 client
+ * that does not require a keep alive connection. Also, if the
+ * container requests an explicit closure this is used when all
+ * of the content for the response has been sent.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ void close(ByteWriter writer);
+
+ /**
+ * This is used when there is an error sending the response. On
+ * error RFC 2616 suggests a connection closure is the best
+ * means to handle the condition, and the one clients should be
+ * expecting and support. All errors result in closure of the
+ * underlying transport and no more requests are processed.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ void error(ByteWriter writer);
+
+ /**
+ * This is used when the response has been sent correctly and
+ * the connection supports persisted HTTP. When ready the channel
+ * is handed back in to the server kernel where the next request
+ * on the pipeline is read and used to compose the next entity.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ void ready(ByteWriter writer);
+
+ /**
+ * This is used to notify the monitor that the HTTP response is
+ * committed and that the header can no longer be changed. It
+ * is also used to indicate whether the response can be reset.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ void commit(ByteWriter writer);
+
+ /**
+ * This can be used to determine whether the response has been
+ * committed. If the response is committed then the header can
+ * no longer be manipulated and the response has been partially
+ * send to the client.
+ *
+ * @return true if the response headers have been committed
+ */
+ boolean isCommitted();
+
+ /**
+ * This is used to determine if the response has completed or
+ * if there has been an error. This basically allows the writer
+ * of the response to take action on certain I/O events.
+ *
+ * @return this returns true if there was an error or close
+ */
+ boolean isClosed();
+
+ /**
+ * This is used to determine if the response was in error. If
+ * the response was in error this allows the writer to throw an
+ * exception indicating that there was a problem responding.
+ *
+ * @return this returns true if there was a response error
+ */
+ boolean isError();
+
+ /**
+ * This represents the time at which the response was either
+ * ready, closed or in error. Providing a time here is useful
+ * as it allows the time taken to generate a response to be
+ * determined even if the response is written asynchronously.
+ *
+ * @return the time when the response completed or failed
+ */
+ long getTime();
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ChunkedEncoder.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ChunkedEncoder.java
new file mode 100644
index 0000000..5663d4b
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ChunkedEncoder.java
@@ -0,0 +1,221 @@
+/*
+ * ChunkedEncoder.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.simpleframework.transport.ByteWriter;
+
+/**
+ * The <code>ChunkedEncoder</code> object is used to encode data in
+ * the chunked encoding format. A chunked producer is required when
+ * the length of the emitted content is unknown. It enables the HTTP
+ * pipeline to remain open as it is a self delimiting format. This
+ * is preferred over the <code>CloseEncoder</code> for HTTP/1.1 as
+ * it maintains the pipeline and thus the cost of creating it.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.message.ChunkedConsumer
+ */
+class ChunkedEncoder implements BodyEncoder {
+
+ /**
+ * This is the size line which is used to generate the size.
+ */
+ private byte[] size = { '0', '0', '0', '0', '0', '0', '0', '0', '\r', '\n' };
+
+ /**
+ * This is the hexadecimal alphabet used to translate the size.
+ */
+ private byte[] index = { '0', '1', '2', '3', '4', '5','6', '7', '8', '9', 'a', 'b', 'c', 'd','e', 'f' };
+
+ /**
+ * This is the zero length chunk sent when this is completed.
+ */
+ private byte[] zero = { '0', '\r', '\n', '\r', '\n' };
+
+ /**
+ * This is the observer used to notify the selector of events.
+ */
+ private BodyObserver observer;
+
+ /**
+ * This is the underlying writer used to deliver the encoded data.
+ */
+ private ByteWriter writer;
+
+ /**
+ * Constructor for the <code>ChunkedEncoder</code> object. This
+ * is used to create a producer that can sent data in the chunked
+ * encoding format. Once the data is encoded in the format it is
+ * handed to the provided <code>ByteWriter</code> object which will
+ * then deliver it to the client using the underlying transport.
+ *
+ * @param observer this is the observer used to signal I/O events
+ * @param writer this is the writer used to deliver the content
+ */
+ public ChunkedEncoder(BodyObserver observer, ByteWriter writer) {
+ this.observer = observer;
+ this.writer = writer;
+ }
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 complaint format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ */
+ public void encode(byte[] array) throws IOException {
+ encode(array, 0, array.length);
+ }
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 complaint format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ * @param off this is the offset within the array to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void encode(byte[] array, int off, int len) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(array, off, len);
+
+ if(len > 0) {
+ encode(buffer);
+ }
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ */
+ public void encode(ByteBuffer buffer) throws IOException {
+ int mark = buffer.position();
+ int size = buffer.limit();
+
+ if(mark > size) {
+ throw new BodyEncoderException("Buffer position greater than limit");
+ }
+ encode(buffer, 0, size - mark);
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ * @param off this is the offset within the buffer to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void encode(ByteBuffer buffer, int off, int len) throws IOException {
+ int pos = 7;
+
+ if(observer.isClosed()) {
+ throw new BodyEncoderException("Stream has been closed");
+ }
+ if(len > 0) {
+ for(int num = len; num > 0; num >>>= 4){
+ size[pos--] = index[num & 0xf];
+ }
+ try {
+ writer.write(size, pos + 1, 9 - pos);
+ writer.write(buffer, off, len);
+ writer.write(size, 8, 2);
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.error(writer);
+ }
+ throw new BodyEncoderException("Error sending response", cause);
+ }
+ }
+ }
+
+ /**
+ * This method is used to flush the contents of the buffer to
+ * the client. This method will block until such time as all of
+ * the data has been sent to the client. If at any point there
+ * is an error sending the content an exception is thrown.
+ */
+ public void flush() throws IOException {
+ try {
+ if(!observer.isClosed()) {
+ writer.flush();
+ }
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.close(writer);
+ }
+ throw new BodyEncoderException("Error sending response", cause);
+ }
+ }
+
+ /**
+ * This method is used to write the zero length chunk. Writing
+ * the zero length chunk tells the client that the response has
+ * been fully sent, and the next sequence of bytes from the HTTP
+ * pipeline is the start of the next response. This will signal
+ * to the server kernel that the next request is read to read.
+ */
+ private void finish() throws IOException {
+ try {
+ writer.write(zero);
+ observer.ready(writer);
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.close(writer);
+ }
+ throw new BodyEncoderException("Error flushing response", cause);
+ }
+ }
+
+ /**
+ * This is used to signal to the producer that all content has
+ * been written and the user no longer needs to write. This will
+ * either close the underlying transport or it will notify the
+ * monitor that the response has completed and the next request
+ * can begin. This ensures the content is flushed to the client.
+ */
+ public void close() throws IOException {
+ if(!observer.isClosed()) {
+ finish();
+ }
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/CloseEncoder.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/CloseEncoder.java
new file mode 100644
index 0000000..8abd726
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/CloseEncoder.java
@@ -0,0 +1,179 @@
+/*
+ * CloseEncoder.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.simpleframework.transport.ByteWriter;
+
+/**
+ * The <code>CloseEncoder</code> is used to close a connection once
+ * all of the content has been produced. This is typically used if
+ * the connected client supports the HTTP/1.0 protocol and there is
+ * no Connection header with the keep-alive token. For reasons of
+ * performance this should not be used for HTTP/1.1 clients.
+ *
+ * @author Niall Gallagher
+ */
+class CloseEncoder implements BodyEncoder {
+
+ /**
+ * This is the observer used to notify the selector of events.
+ */
+ private final BodyObserver observer;
+
+ /**
+ * This is the underlying writer used to deliver the raw data.
+ */
+ private final ByteWriter writer;
+
+ /**
+ * Constructor for the <code>CloseEncoder</code> object. This is
+ * used to create a producer that will close the underlying socket
+ * as a means to signal that the response is fully sent. This is
+ * typically used with HTTP/1.0 connections.
+ *
+ * @param writer this is used to send to the underlying transport
+ * @param observer this is used to deliver signals to the kernel
+ */
+ public CloseEncoder(BodyObserver observer, ByteWriter writer) {
+ this.observer = observer;
+ this.writer = writer;
+ }
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 complaint format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ */
+ public void encode(byte[] array) throws IOException {
+ encode(array, 0, array.length);
+ }
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 complaint format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ * @param off this is the offset within the array to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void encode(byte[] array, int off, int len) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(array, off, len);
+
+ if(len > 0) {
+ encode(buffer);
+ }
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ */
+ public void encode(ByteBuffer buffer) throws IOException {
+ int mark = buffer.position();
+ int size = buffer.limit();
+
+ if(mark > size) {
+ throw new BodyEncoderException("Buffer position greater than limit");
+ }
+ encode(buffer, 0, size - mark);
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ * @param off this is the offset within the buffer to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void encode(ByteBuffer buffer, int off, int len) throws IOException {
+ if(observer.isClosed()) {
+ throw new BodyEncoderException("Stream has been closed");
+ }
+ try {
+ writer.write(buffer, off, len);
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.error(writer);
+ }
+ throw new BodyEncoderException("Error sending response", cause);
+ }
+ }
+
+ /**
+ * This method is used to flush the contents of the buffer to
+ * the client. This method will block until such time as all of
+ * the data has been sent to the client. If at any point there
+ * is an error sending the content an exception is thrown.
+ */
+ public void flush() throws IOException {
+ try {
+ if(!observer.isClosed()) {
+ writer.flush();
+ }
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.error(writer);
+ }
+ throw new BodyEncoderException("Error sending response", cause);
+ }
+ }
+
+ /**
+ * This is used to signal to the producer that all content has
+ * been written and the user no longer needs to write. This will
+ * close the underlying transport which tells the client that
+ * all of the content has been sent over the connection.
+ */
+ public void close() throws IOException {
+ try {
+ if(!observer.isClosed()) {
+ observer.close(writer);
+ writer.close();
+ }
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.error(writer);
+ }
+ throw new BodyEncoderException("Error sending response", cause);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/Collector.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/Collector.java
new file mode 100644
index 0000000..df5b4ca
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/Collector.java
@@ -0,0 +1,50 @@
+/*
+ * Collector.java October 2002
+ *
+ * Copyright (C) 2002, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.http.message.Entity;
+
+/**
+ * The <code>Collector</code> object is used to collect all of the
+ * data used to form a request entity. This will collect the data
+ * fragment by fragment from the underlying transport. When all
+ * of the data is consumed and the entity is created and then it
+ * is sent to the <code>Controller</code> object for processing.
+ * If the request has completed the next request can be collected
+ * from the underlying transport using a new collector object.
+ *
+ * @author Niall Gallagher
+ */
+interface Collector extends Entity {
+
+ /**
+ * This is used to collect the data from a <code>Channel</code>
+ * which is used to compose the entity. If at any stage there
+ * are no ready bytes on the socket the controller provided can be
+ * used to queue the collector until such time as the socket is
+ * ready to read. Also, should the entity have completed reading
+ * all required content it is handed to the controller as ready,
+ * which processes the entity as a new client HTTP request.
+ *
+ * @param controller this is the controller used to queue this
+ */
+ void collect(Controller controller) throws IOException;
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/Container.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/Container.java
new file mode 100644
index 0000000..91a034c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/Container.java
@@ -0,0 +1,62 @@
+/*
+ * Container.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+/**
+ * The <code>Container</code> object is used to process HTTP requests
+ * and compose HTTP responses. The <code>Request</code> objects that
+ * are handed to this container contain all information relating to
+ * the received message. The responsibility of the container is to
+ * interpret the request and compose a suitable response.
+ * <p>
+ * All implementations must ensure that the container is thread safe
+ * as it will receive multiple HTTP transactions concurrently. Also
+ * it should be known that the <code>Response</code> object used to
+ * deliver the HTTP response will only commit and send once it has
+ * its <code>OutputStream</code> closed.
+ * <p>
+ * The <code>Container</code> is entirely responsible for the HTTP
+ * message headers and body. It is up to the implementation to ensure
+ * that it complies to RFC 2616 or any previous specification. All
+ * headers and the status line can be modified by this object.
+ *
+ * @author Niall Gallagher
+ */
+public interface Container {
+
+ /**
+ * Used to pass the <code>Request</code> and <code>Response</code>
+ * to the container for processing. Any implementation of this
+ * must ensure that this is thread safe, as it will receive many
+ * concurrent invocations each with a unique HTTP request.
+ * <p>
+ * The request and response objects are used to interact with the
+ * connected pipeline, in such a way that requests and response
+ * objects can be delivered in sequence and without interference.
+ * The next request from a HTTP pipeline is only processed once
+ * the <code>Response</code> object has been closed and committed.
+ *
+ * @param req the request that contains the client HTTP message
+ * @param resp the response used to deliver the server response
+ */
+ void handle(Request req, Response resp);
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerController.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerController.java
new file mode 100644
index 0000000..16a6374
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerController.java
@@ -0,0 +1,161 @@
+/*
+ * ContainerController.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static java.nio.channels.SelectionKey.OP_READ;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.thread.ConcurrentExecutor;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.TransportException;
+import org.simpleframework.transport.reactor.ExecutorReactor;
+import org.simpleframework.transport.reactor.Reactor;
+
+/**
+ * The <code>ContainerController</code> object is essentially the core
+ * processing engine for the server. This is used to collect requests
+ * from the connected channels and dispatch those requests to the
+ * provided <code>Container</code> object. This contains two thread
+ * pools. The first is used to collect data from the channels and
+ * create request entities. The second is used to take the created
+ * entities and service them with the provided container.
+ *
+ * @author Niall Gallagher
+ */
+class ContainerController implements Controller {
+
+ /**
+ * This is the thread pool used for servicing the requests.
+ */
+ private final ConcurrentExecutor executor;
+
+ /**
+ * This is the thread pool used for collecting the requests.
+ */
+ private final ConcurrentExecutor collect;
+
+ /**
+ * This is the allocator used to create the buffers needed.
+ */
+ private final Allocator allocator;
+
+ /**
+ * This is the container used to service the requests.
+ */
+ private final Container container;
+
+ /**
+ * This is the reactor used to schedule the collectors.
+ */
+ private final Reactor reactor;
+
+ /**
+ * Constructor for the <code>ContainerController</code> object. This
+ * is used to create a controller which will collect and dispatch
+ * requests using two thread pools. The first is used to collect
+ * the requests, the second is used to service those requests.
+ *
+ * @param container this is the container used to service requests
+ * @param allocator this is used to allocate any buffers needed
+ * @param count this is the number of threads per thread pool
+ * @param select this is the number of controller threads to use
+ */
+ public ContainerController(Container container, Allocator allocator, int count, int select) throws IOException {
+ this.executor = new ConcurrentExecutor(RequestDispatcher.class, count);
+ this.collect = new ConcurrentExecutor(RequestReader.class, count);
+ this.reactor = new ExecutorReactor(collect, select);
+ this.allocator = allocator;
+ this.container = container;
+ }
+
+ /**
+ * This is used to initiate the processing of the channel. Once
+ * the channel is passed in to the initiator any bytes ready on
+ * the HTTP pipeline will be processed and parsed in to a HTTP
+ * request. When the request has been built a callback is made
+ * to the <code>Container</code> to process the request. Also
+ * when the request is completed the channel is passed back in
+ * to the initiator so that the next request can be dealt with.
+ *
+ * @param channel the channel to process the request from
+ */
+ public void start(Channel channel) throws IOException {
+ start(new RequestCollector(allocator, channel));
+ }
+
+ /**
+ * The start event is used to immediately consume bytes form the
+ * underlying transport, it does not require a select to check
+ * if the socket is read ready which improves performance. Also,
+ * when a response has been delivered the next request from the
+ * pipeline is consumed immediately.
+ *
+ * @param collector this is the collector used to collect data
+ */
+ public void start(Collector collector) throws IOException {
+ reactor.process(new RequestReader(this, collector));
+ }
+
+ /**
+ * The select event is used to register the connected socket with
+ * a Java NIO selector which can efficiently determine when there
+ * are bytes ready to read from the socket.
+ *
+ * @param collector this is the collector used to collect data
+ */
+ public void select(Collector collector) throws IOException {
+ reactor.process(new RequestReader(this, collector), OP_READ);
+ }
+
+ /**
+ * The ready event is used when a full HTTP entity has been
+ * collected from the underlying transport. On such an event the
+ * request and response can be handled by a container.
+ *
+ * @param collector this is the collector used to collect data
+ */
+ public void ready(Collector collector) throws IOException {
+ executor.execute(new RequestDispatcher(container, this, collector));
+ }
+
+ /**
+ * This method is used to stop the <code>Selector</code> so that
+ * all resources are released. As well as freeing occupied memory
+ * this will also stop all threads, which means that is can no
+ * longer be used to collect data from the pipelines.
+ * <p>
+ * Here we stop the <code>Reactor</code> first, this ensures
+ * that there are no further selects performed if a given socket
+ * does not have enough data to fulfil a request. From there we
+ * stop the main dispatch <code>Executor</code> so that all of
+ * the currently executing tasks complete. The final stage of
+ * termination requires the collector thread pool to be stopped.
+ */
+ public void stop() throws IOException {
+ try {
+ reactor.stop();
+ executor.stop();
+ collect.stop();
+ } catch(Exception cause) {
+ throw new TransportException("Error stopping", cause);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerEvent.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerEvent.java
new file mode 100644
index 0000000..ecd96a3
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerEvent.java
@@ -0,0 +1,93 @@
+/*
+ * ContainerEvent.java October 2012
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+/**
+ * The <code>ContainerEvent</code> enum represents events that occur when
+ * processing a HTTP transaction. Here each phase of processing has a
+ * single event to represent it. If a <code>Trace</code> object has been
+ * associated with the connection then the server will notify the trace
+ * when the connection enters a specific phase of processing.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.transport.trace.Trace
+ */
+public enum ContainerEvent {
+
+ /**
+ * This event indicates that the server is reading the request header.
+ */
+ READ_HEADER,
+
+ /**
+ * This event indicates that the server is reading the request body.
+ */
+ READ_BODY,
+
+ /**
+ * This event indicates that the server is writing the response header.
+ */
+ WRITE_HEADER,
+
+ /**
+ * This event indicates that the server is writing the response body.
+ */
+ WRITE_BODY,
+
+ /**
+ * This indicates that the server has fully read the request header.
+ */
+ HEADER_FINISHED,
+
+ /**
+ * This indicates that the server has fully read the request body.
+ */
+ BODY_FINISHED,
+
+ /**
+ * This event indicates that the server sent a HTTP continue reply.
+ */
+ DISPATCH_CONTINUE,
+
+ /**
+ * This event indicates that the request is ready for processing.
+ */
+ REQUEST_READY,
+
+ /**
+ * This indicates that the request has been dispatched for processing.
+ */
+ DISPATCH_REQUEST,
+
+ /**
+ * This indicates that the dispatch thread has completed the dispatch.
+ */
+ DISPATCH_FINISHED,
+
+ /**
+ * This indicates that all the bytes within the response are sent.
+ */
+ RESPONSE_FINISHED,
+
+ /**
+ * This indicates that there was some error event with the request.
+ */
+ ERROR;
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerSocketProcessor.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerSocketProcessor.java
new file mode 100644
index 0000000..0bf1a44
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerSocketProcessor.java
@@ -0,0 +1,155 @@
+/*
+ * ContainerSocketProcessor.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.FileAllocator;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.TransportSocketProcessor;
+import org.simpleframework.transport.SocketProcessor;
+import org.simpleframework.transport.Socket;
+
+/**
+ * The <code>ContainerSocketProcessor</code> object is a connector
+ * that dispatch requests from a connected pipeline. SSL connections
+ * and plain connections can be processed by this implementation. It
+ * collects data from the connected pipelines and constructs the
+ * requests and responses used to dispatch to the container.
+ * <p>
+ * In order to process the requests this uses two thread pools. One
+ * is used to collect data from the pipelines and create the requests.
+ * The other is used to service those requests. Such an architecture
+ * ensures that the serving thread does not have to deal with I/O
+ * operations. All data is consumed before it is serviced.
+ *
+ * @author Niall Gallagher
+ */
+public class ContainerSocketProcessor implements SocketProcessor {
+
+ /**
+ * This is the transporter used to process the connections.
+ */
+ private final TransportProcessor processor;
+
+ /**
+ * This is used to deliver pipelines to the container.
+ */
+ private final SocketProcessor adapter;
+
+ /**
+ * Constructor for the <code>ContainerSocketProcessor</code> object.
+ * The connector created will collect HTTP requests from the pipelines
+ * provided and dispatch those requests to the provided container.
+ *
+ * @param container this is the container used to service requests
+ */
+ public ContainerSocketProcessor(Container container) throws IOException {
+ this(container, 8);
+ }
+
+ /**
+ * Constructor for the <code>ContainerSocketProcessor</code> object.
+ * The connector created will collect HTTP requests from the pipelines
+ * provided and dispatch those requests to the provided container.
+ *
+ * @param container this is the container used to service requests
+ * @param count this is the number of threads used for each pool
+ */
+ public ContainerSocketProcessor(Container container, int count) throws IOException {
+ this(container, count, 1);
+ }
+
+ /**
+ * Constructor for the <code>ContainerSocketProcessor</code> object. The
+ * connector created will collect HTTP requests from the pipelines
+ * provided and dispatch those requests to the provided container.
+ *
+ * @param container this is the container used to service requests
+ * @param count this is the number of threads used for each pool
+ * @param select this is the number of selector threads to use
+ */
+ public ContainerSocketProcessor(Container container, int count, int select) throws IOException {
+ this(container, new FileAllocator(), count, select);
+ }
+
+ /**
+ * Constructor for the <code>ContainerSocketProcessor</code> object.
+ * The connector created will collect HTTP requests from the pipelines
+ * provided and dispatch those requests to the provided container.
+ *
+ * @param container this is the container used to service requests
+ * @param allocator this is the allocator used to create buffers
+ */
+ public ContainerSocketProcessor(Container container, Allocator allocator) throws IOException {
+ this(container, allocator, 8);
+ }
+
+ /**
+ * Constructor for the <code>ContainerSocketProcessor</code> object.
+ * The connector created will collect HTTP requests from the pipelines
+ * provided and dispatch those requests to the provided container.
+ *
+ * @param container this is the container used to service requests
+ * @param allocator this is the allocator used to create buffers
+ * @param count this is the number of threads used for each pool
+ */
+ public ContainerSocketProcessor(Container container, Allocator allocator, int count) throws IOException {
+ this(container, allocator, count, 1);
+ }
+
+ /**
+ * Constructor for the <code>ContainerSocketProcessor</code> object.
+ * The connector created will collect HTTP requests from the pipelines
+ * provided and dispatch those requests to the provided container.
+ *
+ * @param container this is the container used to service requests
+ * @param allocator this is the allocator used to create buffers
+ * @param count this is the number of threads used for each pool
+ * @param select this is the number of selector threads to use
+ */
+ public ContainerSocketProcessor(Container container, Allocator allocator, int count, int select) throws IOException {
+ this.processor = new ContainerTransportProcessor(container, allocator, count, select);
+ this.adapter = new TransportSocketProcessor(processor, count);
+ }
+
+ /**
+ * This is used to consume HTTP messages that arrive on the socket
+ * and dispatch them to the internal container. Depending on whether
+ * the socket contains an <code>SSLEngine</code> an SSL handshake may
+ * be performed before any HTTP messages are consumed. This can be
+ * called from multiple threads and does not block.
+ *
+ * @param socket this is the connected HTTP pipeline to process
+ */
+ public void process(Socket socket) throws IOException {
+ adapter.process(socket);
+ }
+
+ /**
+ * This method is used to stop the connector in such a way that it
+ * will not accept and process any further messages. If there are
+ * resources to clean up they may be cleaned up asynchronously
+ * so that this method can return without blocking.
+ */
+ public void stop() throws IOException {
+ adapter.stop();
+ }
+ }
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerTransportProcessor.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerTransportProcessor.java
new file mode 100644
index 0000000..dd6df1a
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ContainerTransportProcessor.java
@@ -0,0 +1,96 @@
+/*
+ * ContainerProcessor.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.transport.TransportProcessor;
+import org.simpleframework.transport.Transport;
+import org.simpleframework.transport.TransportChannel;
+
+/**
+ * The <code>ContainerProcessor</code> object is used to create
+ * channels which can be used to consume and process requests. This
+ * is basically an adapter to the <code>Selector</code> which will
+ * convert the provided transport to a usable channel. Each of the
+ * connected pipelines will end up at this object, regardless of
+ * whether those connections are SSL or plain data.
+ *
+ * @author Niall Gallagher
+ */
+public class ContainerTransportProcessor implements TransportProcessor {
+
+ /**
+ * This is the controller used to process the created channels.
+ */
+ private final Controller controller;
+
+ /**
+ * Constructor for the <code>ContainerProcessor</code> object.
+ * This is used to create a processor which will convert the
+ * provided transport objects to channels, which can then be
+ * processed by the controller and dispatched to the container.
+ *
+ * @param container the container to dispatch requests to
+ * @param allocator this is the allocator used to buffer data
+ * @param count this is the number of threads to be used
+ */
+ public ContainerTransportProcessor(Container container, Allocator allocator, int count) throws IOException {
+ this(container, allocator, count, 1);
+ }
+
+ /**
+ * Constructor for the <code>ContainerProcessor</code> object.
+ * This is used to create a processor which will convert the
+ * provided transport objects to channels, which can then be
+ * processed by the controller and dispatched to the container.
+ *
+ * @param container the container to dispatch requests to
+ * @param allocator this is the allocator used to buffer data
+ * @param count this is the number of threads to be used
+ * @param select this is the number of controller threads to use
+ */
+ public ContainerTransportProcessor(Container container, Allocator allocator, int count, int select) throws IOException {
+ this.controller = new ContainerController(container, allocator, count, select);
+ }
+
+ /**
+ * This is used to consume HTTP messages that arrive on the given
+ * transport. All messages consumed from the transport are then
+ * handed to the <code>Container</code> for processing. The response
+ * will also be delivered over the provided transport. At this point
+ * the SSL handshake will have fully completed.
+ *
+ * @param transport the transport to process requests from
+ */
+ public void process(Transport transport) throws IOException {
+ controller.start(new TransportChannel(transport));
+ }
+
+ /**
+ * This method is used to stop the connector in such a way that it
+ * will not accept and process any further messages. If there are
+ * resources to clean up they may be cleaned up asynchronously
+ * so that this method can return without blocking.
+ */
+ public void stop() throws IOException {
+ controller.stop();
+ }
+ } \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/Controller.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/Controller.java
new file mode 100644
index 0000000..3e152bd
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/Controller.java
@@ -0,0 +1,100 @@
+/*
+ * Controller.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.Channel;
+
+/**
+ * The <code>Controller</code> interface represents an object which
+ * is used to process collection events. The sequence of events that
+ * typically take place is for the collection to start, if not all
+ * of the bytes can be consumed it selects, and finally when all of
+ * the bytes within the entity have been consumed it is ready.
+ * <p>
+ * The start event is used to immediately consume bytes form the
+ * underlying transport, it does not require a select to determine
+ * if the socket is read ready which provides an initial performance
+ * enhancement. Also when a response has been delivered the next
+ * request from the pipeline is consumed immediately.
+ * <p>
+ * The select event is used to register the connected socket with a
+ * Java NIO selector which can efficiently determine when there are
+ * bytes ready to read from the socket. Finally, the ready event
+ * is used when a full HTTP entity has been collected from the
+ * underlying transport. On such an event the request and response
+ * can be handled by a container.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.Collector
+ */
+interface Controller {
+
+ /**
+ * This is used to initiate the processing of the channel. Once
+ * the channel is passed in to the initiator any bytes ready on
+ * the HTTP pipeline will be processed and parsed in to a HTTP
+ * request. When the request has been built a callback is made
+ * to the <code>Container</code> to process the request. Also
+ * when the request is completed the channel is passed back in
+ * to the initiator so that the next request can be dealt with.
+ *
+ * @param channel the channel to process the request from
+ */
+ void start(Channel channel) throws IOException;
+
+ /**
+ * The start event is used to immediately consume bytes form the
+ * underlying transport, it does not require a select to check
+ * if the socket is read ready which improves performance. Also,
+ * when a response has been delivered the next request from the
+ * pipeline is consumed immediately.
+ *
+ * @param collector this is the collector used to collect data
+ */
+ void start(Collector collector) throws IOException;
+
+ /**
+ * The select event is used to register the connected socket with
+ * a Java NIO selector which can efficiently determine when there
+ * are bytes ready to read from the socket.
+ *
+ * @param collector this is the collector used to collect data
+ */
+ void select(Collector collector) throws IOException;
+
+ /**
+ * The ready event is used when a full HTTP entity has been
+ * collected from the underlying transport. On such an event the
+ * request and response can be handled by a container.
+ *
+ * @param collector this is the collector used to collect data
+ */
+ void ready(Collector collector) throws IOException;
+
+ /**
+ * This method is used to stop the <code>Selector</code> so that
+ * all resources are released. As well as freeing occupied memory
+ * this will also stop all threads, which means that is can no
+ * longer be used to collect data from the pipelines.
+ */
+ void stop() throws IOException;
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/Conversation.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/Conversation.java
new file mode 100644
index 0000000..07318ce
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/Conversation.java
@@ -0,0 +1,358 @@
+/*
+ * Conversation.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.Method.CONNECT;
+import static org.simpleframework.http.Method.HEAD;
+import static org.simpleframework.http.Protocol.CHUNKED;
+import static org.simpleframework.http.Protocol.CLOSE;
+import static org.simpleframework.http.Protocol.CONNECTION;
+import static org.simpleframework.http.Protocol.CONTENT_LENGTH;
+import static org.simpleframework.http.Protocol.KEEP_ALIVE;
+import static org.simpleframework.http.Protocol.TRANSFER_ENCODING;
+import static org.simpleframework.http.Protocol.UPGRADE;
+import static org.simpleframework.http.Protocol.WEBSOCKET;
+
+import org.simpleframework.http.RequestHeader;
+import org.simpleframework.http.ResponseHeader;
+
+/**
+ * The <code>Conversation</code> object is used to set and interpret
+ * the semantics of the HTTP headers with regard to the encoding
+ * used for the response. This will ensure the the correct headers
+ * are used so that if chunked encoding or a connection close is
+ * needed that the headers are set accordingly. This allows both the
+ * server and client to agree on the best semantics to use.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.ResponseBuffer
+ * @see org.simpleframework.http.core.ResponseEncoder
+ */
+public class Conversation {
+
+ /**
+ * This is the response object that requires HTTP headers set.
+ */
+ private final ResponseHeader response;
+
+ /**
+ * This contains the request headers and protocol version.
+ */
+ private final RequestHeader request;
+
+ /**
+ * Constructor for the <code>Conversation</code> object. This is
+ * used to create an object that makes use of both the request
+ * and response HTTP headers to determine how best to deliver
+ * the response body. Depending on the protocol version and the
+ * existing response headers suitable semantics are determined.
+ *
+ * @param request this is the request from the client
+ * @param response this is the response that is to be sent
+ */
+ public Conversation(RequestHeader request, ResponseHeader response) {
+ this.response = response;
+ this.request = request;
+ }
+
+ /**
+ * This provides the <code>Request</code> object. This can be
+ * used to acquire the request HTTP headers and protocl version
+ * used by the client. Typically the conversation provides all
+ * the data needed to determine the type of response required.
+ *
+ * @return this returns the request object for the conversation
+ */
+ public RequestHeader getRequest() {
+ return request;
+ }
+
+ /**
+ * This provides the <code>Response</code> object. This is used
+ * when the commit is required on the response. By committing
+ * the response the HTTP header is generated and delivered to
+ * the underlying transport.
+ *
+ * @return this returns the response for the conversation
+ */
+ public ResponseHeader getResponse() {
+ return response;
+ }
+
+ /**
+ * This is used to acquire the content length for the response.
+ * The content length is acquired fromt he Content-Length header
+ * if it has been set. If not then this will return a -1 value.
+ *
+ * @return this returns the value for the content length header
+ */
+ public long getContentLength() {
+ return response.getContentLength();
+ }
+
+ /**
+ * This is used to determine if the <code>Response</code> has a
+ * message body. If this does not have a message body then true
+ * is returned. This is determined as of RFC 2616 rules for the
+ * presence of a message body. A message body must not be
+ * included with a HEAD request or with a 304 or a 204 response.
+ * If when this is called there is no message length delimiter
+ * as specified by section RFC 2616 4.4, then there is no body.
+ *
+ * @return true if there is no response body, false otherwise
+ */
+ public boolean isEmpty() {
+ int code = response.getCode();
+
+ if(code == 204){
+ return true;
+ }
+ if(code == 304){
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This is used to determine if the request method was HEAD. This
+ * is of particular interest in a HTTP conversation as it tells
+ * the response whether a response body is to be sent or not.
+ * If the method is head the delimeters for the response should
+ * be as they would be for a similar GET, however no body is sent.
+ *
+ * @return true if the request method was a HEAD method
+ */
+ public boolean isHead() {
+ String method = request.getMethod();
+
+ if(method != null) {
+ return method.equalsIgnoreCase(HEAD);
+ }
+ return false;
+ }
+
+ /**
+ * This is used to determine if the method was a CONNECT. The
+ * connect method is typically used when a client wishes to
+ * establish a connection directly with an origin server. Such a
+ * direct connection is useful when using TLS as it ensures there
+ * is not man in the middle with respect to key exchanges.
+ *
+ * @return this returns true if the method was a CONNECT method
+ */
+ public boolean isConnect() {
+ String method = request.getMethod();
+
+ if(method != null) {
+ return method.equalsIgnoreCase(CONNECT);
+ }
+ return false;
+ }
+
+ /**
+ * This is used to set the content length for the response. If
+ * the HTTP version is HTTP/1.1 then the Content-Length header is
+ * used, if an earlier protocol version is used then connection
+ * close semantics are also used to ensure client compatibility.
+ *
+ * @param length this is the length to set HTTP header to
+ */
+ public void setContentLength(long length) {
+ boolean keepAlive = isKeepAlive();
+
+ if(keepAlive) {
+ response.setValue(CONNECTION, KEEP_ALIVE);
+ } else {
+ response.setValue(CONNECTION, CLOSE);
+ }
+ response.setLong(CONTENT_LENGTH, length);
+ }
+
+ /**
+ * This checks the protocol version used in the request to check
+ * whether it supports persistent HTTP connections. By default the
+ * HTTP/1.1 protocol supports persistent connnections, this can
+ * onlyy be overridden with a Connection header with the close
+ * token. Earlier protocol versions are connection close.
+ *
+ * @return this returns true if the protocol is HTTP/1.1 or above
+ */
+ public boolean isPersistent() {
+ String token = request.getValue(CONNECTION);
+
+ if(token != null) {
+ return token.equalsIgnoreCase(KEEP_ALIVE);
+ }
+ int major = request.getMajor();
+ int minor = request.getMinor();
+
+ if(major > 1) {
+ return true;
+ }
+ if(major == 1) {
+ return minor > 0;
+ }
+ return false;
+ }
+
+ /**
+ * The <code>isKeepAlive</code> method is used to determine if
+ * the connection semantics are set to maintain the connection.
+ * This checks to see if there is a Connection header with the
+ * keep-alive token, if so then the connection is keep alive, if
+ * however there is no connection header the version is used.
+ *
+ * @return true if the response connection is to be maintained
+ */
+ public boolean isKeepAlive() {
+ String token = response.getValue(CONNECTION);
+
+ if(token != null) {
+ return !token.equalsIgnoreCase(CLOSE);
+ }
+ return isPersistent();
+ }
+
+ /**
+ * The <code>isChunkable</code> method is used to determine if
+ * the client supports chunked encoding. If the client does not
+ * support chunked encoding then a connection close should be used
+ * instead, this allows HTTP/1.0 clients to be supported properly.
+ *
+ * @return true if the client supports chunked transfer encoding
+ */
+ public boolean isChunkable() {
+ int major = request.getMajor();
+ int minor = request.getMinor();
+
+ if(major >= 1) {
+ return minor >= 1;
+ }
+ return false;
+ }
+
+ /**
+ * This is used when the output is encoded in the chunked encoding.
+ * This should only be used if the protocol version is HTTP/1.1 or
+ * above. If the protocol version supports chunked encoding then it
+ * will encode the data as specified in RFC 2616 section 3.6.1.
+ */
+ public void setChunkedEncoded() {
+ boolean keepAlive = isKeepAlive();
+ boolean chunkable = isChunkable();
+
+ if(keepAlive && chunkable) {
+ response.setValue(TRANSFER_ENCODING, CHUNKED);
+ response.setValue(CONNECTION, KEEP_ALIVE);
+ } else {
+ response.setValue(CONNECTION, CLOSE);
+ }
+ }
+
+ /**
+ * This is used to set the response to a connection upgrade. The
+ * response for an upgrade contains no payload delimeter such as
+ * content length or transfer encoding. It is typically used when
+ * establishing a web socket connection or a HTTP tunnel.
+ */
+ public void setConnectionUpgrade() {
+ response.setValue(TRANSFER_ENCODING, null);
+ response.setValue(CONTENT_LENGTH, null);
+ response.setValue(CONNECTION, UPGRADE);
+ }
+
+ /**
+ * This will remove all explicit transfer encoding headers from
+ * the response header. By default the identity encoding is used
+ * for all connections, it basically means no encoding. So if the
+ * response uses a Content-Length it implicitly assumes tha the
+ * encoding of the response is identity encoding.
+ */
+ public void setIdentityEncoded() {
+ response.setValue(TRANSFER_ENCODING, null);
+ }
+
+ /**
+ * The <code>isChunkedEncoded</code> is used to determine whether
+ * the chunked encoding scheme is desired. This is enables data to
+ * be encoded in such a way that a connection can be maintained
+ * without a Content-Length header. If the output is chunked then
+ * the connection is keep alive.
+ *
+ * @return true if the response output is chunked encoded
+ */
+ public boolean isChunkedEncoded() {
+ String token = response.getValue(TRANSFER_ENCODING);
+
+ if(token != null) {
+ return token.equalsIgnoreCase(CHUNKED);
+ }
+ return false;
+ }
+
+ /**
+ * This is used to determine if a WebSocket upgrade was requested
+ * and established. An upgrade to use a WebSocket is done when the
+ * client requests the upgrade and the server responds with an
+ * upgrade confirmation, this is the basic handshake required.
+ *
+ * @return this returns true if a WebSocket handshake succeeded
+ */
+ public boolean isWebSocket() {
+ String token = request.getValue(UPGRADE);
+ int code = response.getCode();
+
+ if(token != null && code == 101) {
+ String reply = response.getValue(UPGRADE);
+
+ if(token.equalsIgnoreCase(WEBSOCKET)) {
+ return token.equalsIgnoreCase(reply);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This is used to determine if a tunnel should be established.
+ * A tunnel is where the the HTTP server no longer processes any
+ * HTTP requests, it simply forms a byte stream with the client.
+ * Scenarios where tunnels are useful are when WebSockets are
+ * required or when a client wants a TLS connection to an origin
+ * server through a proxy server.
+ *
+ * @return this returns true if a tunnel has been established
+ */
+ public boolean isTunnel() {
+ boolean socket = isWebSocket();
+
+ if(!socket) {
+ int code = response.getCode();
+
+ if(code < 200) {
+ return false;
+ }
+ if(code >= 300) {
+ return false;
+ }
+ return isConnect();
+ }
+ return true;
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/EmptyEncoder.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/EmptyEncoder.java
new file mode 100644
index 0000000..7ec78fb
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/EmptyEncoder.java
@@ -0,0 +1,132 @@
+/*
+ * EmptyEncoder.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.simpleframework.transport.ByteWriter;
+
+/**
+ * The <code>EmptyEncoder</code> object is a producer used if there
+ * is not response body to be delivered. Typically this is used when
+ * the HTTP request method is HEAD or if there is some status code
+ * sent to the client that does not require a response body.
+ *
+ * @author Niall Gallagher
+ */
+class EmptyEncoder implements BodyEncoder {
+
+ /**
+ * This is the observer that is used to process the pipeline.
+ */
+ private final BodyObserver observer;
+
+ /**
+ * This is the writer that is passed to the monitor when ready.
+ */
+ private final ByteWriter writer;
+
+ /**
+ * Constructor for the <code>EmptyEncoder</code> object. Once
+ * created this producer will signal the kernel the the next
+ * request is ready to read from the HTTP pipeline as there is
+ * no content to be delivered with this producer object.
+ *
+ * @param writer this is used to send to the underlying transport
+ * @param observer this is used to deliver signals to the kernel
+ */
+ public EmptyEncoder(BodyObserver observer, ByteWriter writer) {
+ this.observer = observer;
+ this.writer = writer;
+ }
+
+ /**
+ * This method performs no operation. Because this producer is
+ * not required to generate a response body this will ignore all
+ * data that is provided to sent to the underlying transport.
+ *
+ * @param array this is the array of bytes to send to the client
+ */
+ public void encode(byte[] array) throws IOException {
+ return;
+ }
+
+ /**
+ * This method performs no operation. Because this producer is
+ * not required to generate a response body this will ignore all
+ * data that is provided to sent to the underlying transport.
+ *
+ * @param array this is the array of bytes to send to the client
+ * @param off this is the offset within the array to send from
+ * @param size this is the number of bytes that are to be sent
+ */
+ public void encode(byte[] array, int off, int size) throws IOException {
+ return;
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ */
+ public void encode(ByteBuffer buffer) throws IOException {
+ return;
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ * @param off this is the offset within the buffer to send from
+ * @param size this is the number of bytes that are to be sent
+ */
+ public void encode(ByteBuffer buffer, int off, int size) throws IOException {
+ return;
+ }
+
+ /**
+ * This method performs no operation. Because this producer is
+ * not required to generate a response body this will ignore all
+ * data that is provided to sent to the underlying transport.
+ */
+ public void flush() throws IOException {
+ return;
+ }
+
+ /**
+ * This method performs no operation. Because this producer is
+ * not required to generate a response body this will ignore all
+ * data that is provided to sent to the underlying transport.
+ */
+ public void close() throws IOException {
+ observer.ready(writer);
+ }
+}
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/FixedLengthEncoder.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/FixedLengthEncoder.java
new file mode 100644
index 0000000..86148eb
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/FixedLengthEncoder.java
@@ -0,0 +1,198 @@
+/*
+ * FixedLengthEncoder.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.simpleframework.transport.ByteWriter;
+
+/**
+ * The <code>FixedLengthEncoder</code> object produces content without
+ * any encoding, but limited to a fixed number of bytes. This is used if
+ * the length of the content being delivered is know beforehand. It
+ * will simply count the number of bytes being send and signal the
+ * server kernel that the next request is ready to read once all of
+ * the bytes have been sent to the client.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.message.FixedLengthConsumer
+ */
+class FixedLengthEncoder implements BodyEncoder{
+
+ /**
+ * This is the observer used to notify the initiator of events.
+ */
+ private BodyObserver observer;
+
+ /**
+ * This is the underlying writer used to deliver the raw data.
+ */
+ private ByteWriter writer;
+
+ /**
+ * This is the number of bytes that have been sent so far.
+ */
+ private long count;
+
+ /**
+ * This is the number of bytes this producer is limited to.
+ */
+ private long limit;
+
+ /**
+ * Constructor for the <code>FixedLengthEncoder</code> object. This
+ * is used to create an encoder that will count the number of bytes
+ * that are sent over the pipeline, once all bytes have been sent
+ * this will signal that the next request is ready to read.
+ *
+ * @param observer this is used to deliver signals to the kernel
+ * @param writer this is used to send to the underlying transport
+ * @param limit this is used to limit the number of bytes sent
+ */
+ public FixedLengthEncoder(BodyObserver observer, ByteWriter writer, long limit) {
+ this.observer = observer;
+ this.writer = writer;
+ this.limit = limit;
+ }
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 complaint format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ */
+ public void encode(byte[] array) throws IOException {
+ encode(array, 0, array.length);
+ }
+
+ /**
+ * This method is used to encode the provided array of bytes in
+ * a HTTP/1.1 complaint format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param array this is the array of bytes to send to the client
+ * @param off this is the offset within the array to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void encode(byte[] array, int off, int len) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(array, off, len);
+
+ if(len > 0) {
+ encode(buffer);
+ }
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ */
+ public void encode(ByteBuffer buffer) throws IOException {
+ int mark = buffer.position();
+ int size = buffer.limit();
+
+ if(mark > size) {
+ throw new BodyEncoderException("Buffer position greater than limit");
+ }
+ encode(buffer, 0, size - mark);
+ }
+
+ /**
+ * This method is used to encode the provided buffer of bytes in
+ * a HTTP/1.1 compliant format and sent it to the client. Once
+ * the data has been encoded it is handed to the transport layer
+ * within the server, which may choose to buffer the data if the
+ * content is too small to send efficiently or if the socket is
+ * not write ready.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ * @param off this is the offset within the buffer to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void encode(ByteBuffer buffer, int off, int len) throws IOException {
+ long size = Math.min(len, limit - count);
+
+ try {
+ if(observer.isClosed()) {
+ throw new BodyEncoderException("Response content complete");
+ }
+ writer.write(buffer, off, (int)size);
+
+ if(count + size == limit) {
+ observer.ready(writer);
+ }
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.error(writer);
+ }
+ throw new BodyEncoderException("Error sending response", cause);
+ }
+ count += size;
+ }
+
+ /**
+ * This method is used to flush the contents of the buffer to
+ * the client. This method will block until such time as all of
+ * the data has been sent to the client. If at any point there
+ * is an error sending the content an exception is thrown.
+ */
+ public void flush() throws IOException {
+ try {
+ if(!observer.isClosed()) {
+ writer.flush();
+ }
+ } catch(Exception cause) {
+ if(writer != null) {
+ observer.error(writer);
+ }
+ throw new BodyEncoderException("Error flushing", cause);
+ }
+ }
+
+ /**
+ * This is used to signal to the producer that all content has
+ * been written and the user no longer needs to write. This will
+ * either close the underlying transport or it will notify the
+ * monitor that the response has completed and the next request
+ * can begin. This ensures the content is flushed to the client.
+ */
+ public void close() throws IOException {
+ if(!observer.isClosed()) {
+ if(count < limit) {
+ observer.error(writer);
+ } else {
+ observer.ready(writer);
+ }
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/QueryBuilder.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/QueryBuilder.java
new file mode 100644
index 0000000..7942d77
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/QueryBuilder.java
@@ -0,0 +1,148 @@
+/*
+ * QueryBuilder.java October 2002
+ *
+ * Copyright (C) 2002, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.Protocol.APPLICATION;
+import static org.simpleframework.http.Protocol.URL_ENCODED;
+
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Query;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.http.message.Header;
+
+/**
+ * The <code>QueryBuilder</code> object is used to create the query.
+ * It is created using the request URI query and a form post body if
+ * sent. The application/x-www-form-urlencoded conent type identifies
+ * the body as contain form data. If there are duplicates then they
+ * both are available from the query that is built.
+ *
+ * @author Niall Gallagher
+ */
+class QueryBuilder {
+
+ /**
+ * This is the request that is used to acquire the data.
+ */
+ private final Request request;
+
+ /**
+ * This is the header that is used to acquire the data.
+ */
+ private final Header header;
+
+ /**
+ * Constructor for the <code>QueryBuilder</code> object. This will
+ * create an object that can be used to construct a single query
+ * from the multiple sources of data within the request entity.
+ *
+ * @param request this is the request to build a query for
+ * @param entity this is the entity that contains the data
+ */
+ public QueryBuilder(Request request, Entity entity) {
+ this.header = entity.getHeader();
+ this.request = request;
+ }
+
+ /**
+ * This method is used to acquire the query part from the HTTP
+ * request URI target and a form post if it exists. Both the
+ * query and the form post are merge together in a single query.
+ *
+ * @return the query associated with the HTTP target URI
+ */
+ public Query build() {;
+ Query query = header.getQuery();
+
+ if(!isFormPost()) {
+ return query;
+ }
+ return getQuery(query);
+ }
+
+ /**
+ * This method is used to acquire the query part from the HTTP
+ * request URI target and a form post if it exists. Both the
+ * query and the form post are merge together in a single query.
+ *
+ * @param query this is the URI query string to be used
+ *
+ * @return the query associated with the HTTP target URI
+ */
+ private Query getQuery(Query query) {
+ String body = getContent();
+
+ if(body == null) {
+ return query;
+ }
+ return new QueryCombiner(query, body);
+ }
+
+ /**
+ * This method attempts to acquire the content of the request
+ * body. If there is an <code>IOException</code> acquiring the
+ * content of the body then this will simply return a null
+ * value without reporting the exception.
+ *
+ * @return the content of the body, or null on error
+ */
+ private String getContent() {
+ try {
+ return request.getContent();
+ } catch(Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * This is used to determine if the content type is a form POST
+ * of type application/x-www-form-urlencoded. Such a type is
+ * used when a HTML form is used to post data to the server.
+ *
+ * @return this returns true if content type is a form post
+ */
+ private boolean isFormPost() {
+ ContentType type = request.getContentType();
+
+ if(type == null) {
+ return false;
+ }
+ return isFormPost(type);
+ }
+
+ /**
+ * This is used to determine if the content type is a form POST
+ * of type application/x-www-form-urlencoded. Such a type is
+ * used when a HTML form is used to post data to the server.
+ *
+ * @param type the type to determine if its a form post
+ *
+ * @return this returns true if content type is a form post
+ */
+ private boolean isFormPost(ContentType type) {
+ String primary = type.getPrimary();
+ String secondary = type.getSecondary();
+
+ if(!primary.equals(APPLICATION)) {
+ return false;
+ }
+ return secondary.equals(URL_ENCODED);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/QueryCombiner.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/QueryCombiner.java
new file mode 100644
index 0000000..ed4c92e
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/QueryCombiner.java
@@ -0,0 +1,148 @@
+/*
+ * QueryCombiner.java May 2003
+ *
+ * Copyright (C) 2003, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.util.List;
+import java.util.Set;
+
+import org.simpleframework.http.Query;
+import org.simpleframework.http.parse.QueryParser;
+
+/**
+ * The <code>QueryCombimer</code> is used to parse several strings
+ * as a complete URL encoded parameter string. This will do the
+ * following concatenations.
+ *
+ * <pre>
+ * null + "a=b&amp;c=d&amp;e=f" = "a=b&amp;c=d&amp;e=f"
+ * "a=b" + "e=f&amp;g=h" = "a=b&amp;e=f&amp;g=h";
+ * "a=b&amp;c=d&amp;e=f" + "" = "a=b&amp;c=d&amp;e=f"
+ * </pre>
+ *
+ * This ensures that the <code>QueryForm</code> can parse the list
+ * of strings as a single URL encoded parameter string. This can
+ * parse any number of parameter strings.
+ *
+ * @author Niall Gallagher
+ */
+class QueryCombiner extends QueryParser {
+
+ /**
+ * Constructor that allows a list of string objects to be
+ * parsed as a single parameter string. This will check
+ * each string to see if it is empty, that is, is either
+ * null or the zero length string.
+ *
+ * @param list this is a list of query values to be used
+ */
+ public QueryCombiner(String... list) {
+ this.parse(list);
+ }
+
+ /**
+ * Constructor that allows an array of string objects to
+ * be parsed as a single parameter string. This will check
+ * each string to see if it is empty, that is, is either
+ * null or the zero length string.
+ *
+ * @param query this is the query from the HTTP header
+ * @param list this is the list of strings to be parsed
+ */
+ public QueryCombiner(Query query, String... list) {
+ this.add(query);
+ this.parse(list);
+ }
+
+ /**
+ * Constructor that allows an array of string objects to
+ * be parsed as a single parameter string. This will check
+ * each string to see if it is empty, that is, is either
+ * null or the zero length string.
+ *
+ * @param query this is the query from the HTTP header
+ * @param post this is the query from the HTTP post body
+ */
+ public QueryCombiner(Query query, Query post) {
+ this.add(query);
+ this.add(post);
+ }
+
+ /**
+ * This will concatenate the list of parameter strings as a
+ * single parameter string, before handing it to be parsed
+ * by the <code>parse(String)</code> method. This method
+ * will ignore any null or zero length strings in the array.
+ *
+ * @param list this is the list of strings to be parsed
+ */
+ public void parse(String[] list) {
+ StringBuilder text = new StringBuilder();
+
+ for(int i = 0; i < list.length; i++) {
+ if(list[i] == null) {
+ continue;
+ } else if(list[i].length()==0){
+ continue;
+ } else if(text.length() > 0){
+ text.append("&");
+ }
+ text.append(list[i]);
+ }
+ parse(text);
+ }
+
+ /**
+ * This is used to perform a parse of the form data that is in
+ * the provided string builder. This will simply convert the
+ * data in to a string and parse it in the normal fashion.
+ *
+ * @param text this is the buffer to be converted to a string
+ */
+ private void parse(StringBuilder text) {
+ if(text != null){
+ ensureCapacity(text.length());
+ count = text.length();
+ text.getChars(0, count, buf,0);
+ parse();
+ }
+ }
+
+ /**
+ * This method is used to insert a collection of tokens into
+ * the parsers map. This is used when another source of tokens
+ * is required to populate the connection currently maintained
+ * within this parsers internal map. Any tokens that currently
+ * exist with similar names will be overwritten by this.
+ *
+ * @param query this is the collection of tokens to be added
+ */
+ private void add(Query query) {
+ Set<String> keySet = query.keySet();
+
+ for(String key : keySet) {
+ List<String> list = query.getAll(key);
+ String first = query.get(key);
+
+ if(first != null) {
+ all.put(key, list);
+ map.put(key, first);
+ }
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCertificate.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCertificate.java
new file mode 100644
index 0000000..1da8b54
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCertificate.java
@@ -0,0 +1,183 @@
+/*
+ * RequestCertificate.java June 2013
+ *
+ * Copyright (C) 2013, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.util.concurrent.Future;
+
+import javax.security.cert.X509Certificate;
+
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.CertificateChallenge;
+import org.simpleframework.transport.Channel;
+
+/**
+ * The <code>RequestCertificate</code> represents a certificate for
+ * an HTTP request. It basically wraps the raw SSL certificate that
+ * comes with the <code>Channel</code>. Wrapping the raw certificate
+ * allows us to enforce the HTTPS workflow for SSL renegotiation,
+ * which requires some rather weird behaviour. Most importantly
+ * we only allow a challenge when the response has not been sent.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.transport.CertificateChallenge
+ */
+class RequestCertificate implements Certificate {
+
+ /**
+ * This is used to challenge the client for an X509 certificate.
+ */
+ private final CertificateChallenge challenge;
+
+ /**
+ * This is the raw underlying certificate for the SSL channel.
+ */
+ private final Certificate certificate;
+
+ /**
+ * This is the channel representing the client connection.
+ */
+ private final Channel channel;
+
+ /**
+ * Constructor for the <code>RequestCertificate</code>. This is
+ * used to create a wrapper for the raw SSL certificate that
+ * is provided by the underlying SSL session.
+ *
+ * @param observer the observer used to observe the transaction
+ * @param entity the request entity containing the data
+ */
+ public RequestCertificate(BodyObserver observer, Entity entity) {
+ this.challenge = new Challenge(observer, entity);
+ this.channel = entity.getChannel();
+ this.certificate = channel.getCertificate();
+ }
+
+ /**
+ * This will return the X509 certificate chain, if any, that
+ * has been sent by the client. A certificate chain is typically
+ * only send when the server explicitly requests the certificate
+ * on the initial connection or when it is challenged for.
+ *
+ * @return this returns the clients X509 certificate chain
+ */
+ public X509Certificate[] getChain() throws Exception {
+ return certificate.getChain();
+ }
+
+ /**
+ * This returns a challenge for the certificate. A challenge is
+ * issued by providing a <code>Runnable</code> task which is to
+ * be executed when the challenge has completed. Typically this
+ * task should be used to drive completion of an HTTPS request.
+ *
+ * @return this returns a challenge for the client certificate
+ */
+ public CertificateChallenge getChallenge() throws Exception {
+ return challenge;
+ }
+
+ /**
+ * This is used to determine if the X509 certificate chain is
+ * present for the request. If it is not present then a challenge
+ * can be used to request the certificate.
+ *
+ * @return true if the certificate chain is present
+ */
+ public boolean isChainPresent() throws Exception {
+ return certificate.isChainPresent();
+ }
+
+ /**
+ * The <code>Challenge</code> provides a basic wrapper around the
+ * challenge provided by the SSL connection. It is used to enforce
+ * the workflow required by HTTP, this workflow requires that the
+ * SSL renegotiation be issued before the response is sent. This
+ * will also throw an exception if a challenge is issued for
+ * a request that already has a client certificate.
+ */
+ private static class Challenge implements CertificateChallenge {
+
+ /**
+ * This is the observer used to keep track of the HTTP transaction.
+ */
+ private final BodyObserver observer;
+
+ /**
+ * This is the certificate associated with the SSL connection.
+ */
+ private final Certificate certificate;
+
+ /**
+ * This is the channel representing the underlying TCP stream.
+ */
+ private final Channel channel;
+
+ /**
+ * Constructor for the <code>Challenge</code> object. This is
+ * basically a wrapper for the raw certificate challenge that
+ * will enforce some of the workflow required by HTTPS.
+ *
+ * @param observer this observer used to track the transaction
+ * @param entity this entity containing the request data
+ */
+ public Challenge(BodyObserver observer, Entity entity) {
+ this.channel = entity.getChannel();
+ this.certificate = channel.getCertificate();
+ this.observer = observer;
+ }
+
+ /**
+ * This method will challenge the client for their certificate.
+ * It does so by performing an SSL renegotiation. Successful
+ * completion of the SSL renegotiation results in the client
+ * providing their certificate, and execution of the task.
+ *
+ * @param completion task to be run on successful challenge
+ */
+ public Future<Certificate> challenge() throws Exception {
+ return challenge(null);
+ }
+
+ /**
+ * This method will challenge the client for their certificate.
+ * It does so by performing an SSL renegotiation. Successful
+ * completion of the SSL renegotiation results in the client
+ * providing their certificate, and execution of the task.
+ *
+ * @param completion task to be run on successful challenge
+ */
+ public Future<Certificate> challenge(Runnable completion) throws Exception {
+ if(certificate == null) {
+ throw new IOException("Challenging must be done on a secure connection");
+ }
+ CertificateChallenge challenge = certificate.getChallenge();
+
+ if(certificate.isChainPresent()) {
+ throw new IOException("Certificate is already present");
+ }
+ if(observer.isCommitted()) {
+ throw new IOException("Response has already been committed");
+ }
+ return challenge.challenge(completion);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCollector.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCollector.java
new file mode 100644
index 0000000..027318d
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestCollector.java
@@ -0,0 +1,184 @@
+/*
+ * RequestCollector.java October 2002
+ *
+ * Copyright (C) 2002, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.simpleframework.http.core.ContainerEvent.REQUEST_READY;
+import static org.simpleframework.transport.TransportEvent.READ_WAIT;
+
+import java.io.IOException;
+import java.nio.channels.SocketChannel;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.http.message.Body;
+import org.simpleframework.http.message.EntityConsumer;
+import org.simpleframework.http.message.Header;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>RequestCollector</code> object is used to collect all of
+ * the data used to form a request entity. This will collect the data
+ * fragment by fragment from the underlying transport. When all of
+ * the data is consumed and the entity is created and then it is sent
+ * to the <code>Selector</code> object for processing. If the request
+ * has completed the next request can be collected from the
+ * underlying transport using a new collector object.
+ *
+ * @author Niall Gallagher
+ */
+class RequestCollector implements Collector {
+
+ /**
+ * This is used to consume the request entity from the channel.
+ */
+ private final EntityConsumer entity;
+
+ /**
+ * This is the cursor used to read and reset the data.
+ */
+ private final ByteCursor cursor;
+
+ /**
+ * This is the channel used to acquire the underlying data.
+ */
+ private final Channel channel;
+
+ /**
+ * This is the trace used to listen for various collect events.
+ */
+ private final Trace trace;
+
+ /**
+ * This represents the time the request collection began at.
+ */
+ private final Timer timer;
+
+ /**
+ * The <code>RequestCollector</code> object used to collect the data
+ * from the underlying transport. In order to collect a body this
+ * must be given an <code>Allocator</code> which is used to create
+ * an internal buffer to store the consumed body.
+ *
+ * @param allocator this is the allocator used to buffer data
+ * @param tracker this is the tracker used to create sessions
+ * @param channel this is the channel used to read the data
+ */
+ public RequestCollector(Allocator allocator, Channel channel) {
+ this.entity = new EntityConsumer(allocator, channel);
+ this.timer = new Timer(MILLISECONDS);
+ this.cursor = channel.getCursor();
+ this.trace = channel.getTrace();
+ this.channel = channel;
+ }
+
+ /**
+ * This is used to collect the data from a <code>Channel</code>
+ * which is used to compose the entity. If at any stage there
+ * are no ready bytes on the socket the controller provided can
+ * be used to queue the collector until such time as the socket
+ * is ready to read. Also, should the entity have completed reading
+ * all required content it is handed to the controller as ready,
+ * which processes the entity as a new client HTTP request.
+ *
+ * @param controller this is the controller used to queue this
+ */
+ public void collect(Controller controller) throws IOException {
+ while(cursor.isReady()) {
+ if(entity.isFinished()) {
+ break;
+ } else {
+ timer.set();
+ entity.consume(cursor);
+ }
+ }
+ if(cursor.isOpen()) {
+ if(entity.isFinished()) {
+ trace.trace(REQUEST_READY);
+ controller.ready(this);
+ } else {
+ trace.trace(READ_WAIT);
+ controller.select(this);
+ }
+ }
+ }
+
+ /**
+ * This is the time in milliseconds when the request was first
+ * read from the underlying channel. The time represented here
+ * represents the time collection of this request began. This
+ * does not necessarily represent the time the bytes arrived on
+ * the receive buffers as some data may have been buffered.
+ *
+ * @return this represents the time the request was ready at
+ */
+ public long getTime() {
+ return timer.get();
+ }
+
+ /**
+ * This provides the HTTP request header for the entity. This is
+ * always populated and provides the details sent by the client
+ * such as the target URI and the query if specified. Also this
+ * can be used to determine the method and protocol version used.
+ *
+ * @return the header provided by the HTTP request message
+ */
+ public Header getHeader() {
+ return entity.getHeader();
+ }
+
+ /**
+ * This is used to acquire the body for this HTTP entity. This
+ * will return a body which can be used to read the content of
+ * the message, also if the request is multipart upload then all
+ * of the parts are provided as <code>Part</code> objects. Each
+ * part can then be read as an individual message.
+ *
+ * @return the body provided by the HTTP request message
+ */
+ public Body getBody() {
+ return entity.getBody();
+ }
+
+ /**
+ * This provides the connected channel for the client. This is
+ * used to send and receive bytes to and from an transport layer.
+ * Each channel provided with an entity contains an attribute
+ * map which contains information about the connection.
+ *
+ * @return the connected channel for this HTTP entity
+ */
+ public Channel getChannel() {
+ return channel;
+ }
+
+ /**
+ * This returns the socket channel that is used by the collector
+ * to read content from. This is a selectable socket, in that
+ * it can be registered with a Java NIO selector. This ensures
+ * that the system can be notified when the socket is ready.
+ *
+ * @return the socket channel used by this collector object
+ */
+ public SocketChannel getSocket() {
+ return channel.getSocket();
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestDispatcher.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestDispatcher.java
new file mode 100644
index 0000000..6b1531a
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestDispatcher.java
@@ -0,0 +1,128 @@
+/*
+ * RequestDispatcher.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.core.ContainerEvent.DISPATCH_FINISHED;
+import static org.simpleframework.http.core.ContainerEvent.DISPATCH_REQUEST;
+import static org.simpleframework.http.core.ContainerEvent.ERROR;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>RequestDispatcher</code> object is used to dispatch a
+ * request and response to the container. This is the root task that
+ * executes all transactions. A transaction is dispatched to the
+ * container which can deal with it asynchronously, however as a
+ * safeguard the dispatcher will catch any exceptions thrown and close
+ * the connection if required. Closing the connection if an exception
+ * is thrown ensures that CLOSE_WAIT issues do not arise with open
+ * connections that can not be closed within the container.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.Container
+ */
+class RequestDispatcher implements Runnable {
+
+ /**
+ * This is the observer object used to signal completion events.
+ */
+ private final ResponseObserver observer;
+
+ /**
+ * This is the container that is used to handle the transactions.
+ */
+ private final Container container;
+
+ /**
+ * This is the response object used to response to the request.
+ */
+ private final Response response;
+
+ /**
+ * This is the request object which contains the request entity.
+ */
+ private final Request request;
+
+ /**
+ * This is the channel associated with the request to dispatch.
+ */
+ private final Channel channel;
+
+ /**
+ * This is the trace that is used to track the request dispatch.
+ */
+ private final Trace trace;
+
+ /**
+ * Constructor for the <code>RequestDispatcher</code> object. This
+ * creates a request and response object using the provided entity,
+ * these can then be passed to the container to handle it.
+ *
+ * @param container this is the container to handle the request
+ * @param controller the controller used to handle the next request
+ * @param entity this contains the current request entity
+ */
+ public RequestDispatcher(Container container, Controller controller, Entity entity) {
+ this.observer = new ResponseObserver(controller, entity);
+ this.request = new RequestEntity(observer, entity);
+ this.response = new ResponseEntity(observer, request, entity);
+ this.channel = entity.getChannel();
+ this.trace = channel.getTrace();
+ this.container = container;
+ }
+
+ /**
+ * This <code>run</code> method will dispatch the created request
+ * and response objects to the container. This will interpret the
+ * target and semantics from the request object and compose a
+ * response for the request which is sent to the connected client.
+ */
+ public void run() {
+ try {
+ dispatch();
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ } finally {
+ trace.trace(DISPATCH_FINISHED);
+ }
+ }
+
+ /**
+ * This <code>dispatch</code> method will dispatch the request
+ * and response objects to the container. This will interpret the
+ * target and semantics from the request object and compose a
+ * response for the request which is sent to the connected client.
+ * If there is an exception this will close the socket channel.
+ */
+ private void dispatch() throws Exception {
+ try {
+ trace.trace(DISPATCH_REQUEST);
+ container.handle(request, response);
+ } catch(Throwable cause) {
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+}
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestEntity.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestEntity.java
new file mode 100644
index 0000000..6060a4f
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestEntity.java
@@ -0,0 +1,398 @@
+/*
+ * RequestEntity.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.Protocol.CLOSE;
+import static org.simpleframework.http.Protocol.CONNECTION;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SocketChannel;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Part;
+import org.simpleframework.http.Query;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.message.Body;
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+
+/**
+ * This object is used to represent a HTTP request. This defines the
+ * attributes that a HTTP request has such as a request line and the
+ * headers that come with the message header.
+ * <p>
+ * The <code>Request</code> is used to provide an interface to the
+ * HTTP <code>InputStream</code> and message header. The stream can
+ * have certain characteristics, these characteristics are available
+ * by this object. The <code>Request</code> provides methods that
+ * allow the <code>InputStream</code>'s semantics to be known, for
+ * example if the stream is keep-alive or if the stream has a length.
+ * <p>
+ * The <code>Request</code> origin is also retrievable from the
+ * <code>Request</code> as is the attributes <code>Map</code> object
+ * which defines specific connection attributes. And acts as a
+ * simple model for the request transaction.
+ * <p>
+ * It is important to note that the <code>Request</code> controls
+ * the processing of the HTTP pipeline. The next HTTP request is
+ * not processed until the request has read all of the content body
+ * within the <code>InputStream</code>. The stream must be fully
+ * read or closed for the next request to be processed.
+ *
+ * @author Niall Gallagher
+ */
+class RequestEntity extends RequestMessage implements Request {
+
+ /**
+ * This is the certificate associated with the connection.
+ */
+ private Certificate certificate;
+
+ /**
+ * This will create the form object using the query and body.
+ */
+ private QueryBuilder builder;
+
+ /**
+ * This channel represents the connected pipeline used.
+ */
+ private Channel channel;
+
+ /**
+ * The query contains all the parameters for the request.
+ */
+ private Query query;
+
+ /**
+ * The body contains the message content sent by the client.
+ */
+ private Body body;
+
+ /**
+ * This is used to contain the values for this request.
+ */
+ private Map map;
+
+ /**
+ * This is the time at which the request is ready to be used.
+ */
+ private long time;
+
+ /**
+ * Constructor for the <code>RequestEntity</code> object. This is
+ * used to create a request that contains all the parts sent by
+ * the client, including the headers and the request body. Each of
+ * the request elements are accessible through this object in a
+ * convenient manner, all parts and parameters, as well as cookies
+ * can be accessed and used without much effort.
+ *
+ * @param observer this is the observer used to monitor events
+ * @param entity this is the entity that was sent by the client
+ */
+ public RequestEntity(ResponseObserver observer, Entity entity) {
+ this.certificate = new RequestCertificate(observer, entity);
+ this.builder = new QueryBuilder(this, entity);
+ this.channel = entity.getChannel();
+ this.header = entity.getHeader();
+ this.body = entity.getBody();
+ this.time = entity.getTime();
+ }
+
+ /**
+ * This is used to determine if the request has been transferred
+ * over a secure connection. If the protocol is HTTPS and the
+ * content is delivered over SSL then the request is considered
+ * to be secure. Also the associated response will be secure.
+ *
+ * @return true if the request is transferred securely
+ */
+ public boolean isSecure() {
+ return channel.isSecure();
+ }
+
+ /**
+ * This is a convenience method that is used to determine whether
+ * or not this message has the Connection header with the close
+ * token. If the close token is present then this stream is not a
+ * keep-alive connection. However if this has no Connection header
+ * then the keep alive status is determined by the HTTP version,
+ * that is HTTP/1.1 is keep alive by default, HTTP/1.0 has the
+ * connection close by default.
+ *
+ * @return returns true if this is keep alive connection
+ */
+ public boolean isKeepAlive(){
+ String value = getValue(CONNECTION);
+
+ if(value == null) {
+ int major = getMajor();
+ int minor = getMinor();
+
+ if(major > 1) {
+ return true;
+ }
+ if(major == 1) {
+ return minor > 0;
+ }
+ return false;
+ }
+ return !value.equalsIgnoreCase(CLOSE);
+ }
+
+ /**
+ * This is the time in milliseconds when the request was first
+ * read from the underlying socket. The time represented here
+ * represents the time collection of this request began. This
+ * does not necessarily represent the time the bytes arrived on
+ * the receive buffers as some data may have been buffered.
+ *
+ * @return this represents the time the request arrived at
+ */
+ public long getRequestTime() {
+ return time;
+ }
+
+ /**
+ * This provides the underlying channel for the request. It
+ * contains the TCP socket channel and various other low level
+ * components. Typically this will only ever be needed when
+ * there is a need to switch protocols.
+ *
+ * @return the underlying channel for this request
+ */
+ public Channel getChannel() {
+ return channel;
+ }
+
+ /**
+ * This is used to acquire the SSL certificate used when the
+ * server is using a HTTPS connection. For plain text connections
+ * or connections that use a security mechanism other than SSL
+ * this will be null. This is only available when the connection
+ * makes specific use of an SSL engine to secure the connection.
+ *
+ * @return this returns the associated SSL certificate if any
+ */
+ public Certificate getClientCertificate() {
+ if(channel.isSecure()) {
+ return certificate;
+ }
+ return null;
+ }
+
+ /**
+ * This is used to acquire the remote client address. This can
+ * be used to acquire both the port and the I.P address for the
+ * client. It allows the connected clients to be logged and if
+ * require it can be used to perform course grained security.
+ *
+ * @return this returns the client address for this request
+ */
+ public InetSocketAddress getClientAddress() {
+ SocketChannel socket = channel.getSocket();
+ Socket client = socket.socket();
+
+ return getClientAddress(client);
+ }
+
+ /**
+ * This is used to acquire the remote client address. This can
+ * be used to acquire both the port and the I.P address for the
+ * client. It allows the connected clients to be logged and if
+ * require it can be used to perform course grained security.
+ *
+ * @param socket this is the socket to get the address for
+ *
+ * @return this returns the client address for this request
+ */
+ private InetSocketAddress getClientAddress(Socket socket) {
+ InetAddress address = socket.getInetAddress();
+ int port = socket.getPort();
+
+ return new InetSocketAddress(address, port);
+ }
+
+ /**
+ * This is used to get the content body. This will essentially get
+ * the content from the body and present it as a single string.
+ * The encoding of the string is determined from the content type
+ * charset value. If the charset is not supported this will throw
+ * an exception. Typically only text values should be extracted
+ * using this method if there is a need to parse that content.
+ *
+ * @return the body content containing the message body
+ */
+ public String getContent() throws IOException {
+ ContentType type = getContentType();
+
+ if(type == null) {
+ return body.getContent("ISO-8859-1");
+ }
+ return getContent(type);
+ }
+
+ /**
+ * This is used to get the content body. This will essentially get
+ * the content from the body and present it as a single string.
+ * The encoding of the string is determined from the content type
+ * charset value. If the charset is not supported this will throw
+ * an exception. Typically only text values should be extracted
+ * using this method if there is a need to parse that content.
+ *
+ * @param type this is the content type used with the request
+ *
+ * @return the input stream containing the message body
+ */
+ public String getContent(ContentType type) throws IOException {
+ String charset = type.getCharset();
+
+ if(charset == null) {
+ charset = "ISO-8859-1";
+ }
+ return body.getContent(charset);
+ }
+
+ /**
+ * This is used to read the content body. The specifics of the data
+ * that is read from this <code>InputStream</code> can be determined
+ * by the <code>getContentLength</code> method. If the data sent by
+ * the client is chunked then it is decoded, see RFC 2616 section
+ * 3.6. Also multipart data is available as <code>Part</code> objects
+ * however the raw content of the multipart body is still available.
+ *
+ * @return the input stream containing the message body
+ */
+ public InputStream getInputStream() throws IOException {
+ return body.getInputStream();
+ }
+
+ /**
+ * This is used to read the content body. The specifics of the data
+ * that is read from this <code>ReadableByteChannel</code> can be
+ * determined by the <code>getContentLength</code> method. If the
+ * data sent by the client is chunked then it is decoded, see RFC
+ * 2616 section 3.6. This stream will never provide empty reads as
+ * the content is internally buffered, so this can do a full read.
+ *
+ * @return this returns the byte channel used to read the content
+ */
+ public ReadableByteChannel getByteChannel() throws IOException {
+ InputStream source = getInputStream();
+
+ if(source != null) {
+ return Channels.newChannel(source);
+ }
+ return null;
+ }
+
+ /**
+ * This can be used to retrieve the response attributes. These can
+ * be used to keep state with the response when it is passed to
+ * other systems for processing. Attributes act as a convenient
+ * model for storing objects associated with the response. This
+ * also inherits attributes associated with the client connection.
+ *
+ * @return the attributes that have been added to this request
+ */
+ public Map getAttributes() {
+ Map common = channel.getAttributes();
+
+ if(map == null) {
+ map = new HashMap(common);
+ }
+ return map;
+ }
+
+ /**
+ * This is used as a shortcut for acquiring attributes for the
+ * response. This avoids acquiring the attribute <code>Map</code>
+ * in order to retrieve the attribute directly from that object.
+ * The attributes contain data specific to the response.
+ *
+ * @param key this is the key of the attribute to acquire
+ *
+ * @return this returns the attribute for the specified name
+ */
+ public Object getAttribute(Object key) {
+ return getAttributes().get(key);
+ }
+
+ /**
+ * This method is used to acquire the query part from the HTTP
+ * request URI target and a form post if it exists. Both the
+ * query and the form post are merge together in a single query.
+ *
+ * @return the query associated with the HTTP target URI
+ */
+ public Query getQuery() {
+ if(query == null) {
+ query = builder.build();
+ }
+ return query;
+ }
+
+ /**
+ * This is used to provide quick access to the parameters. This
+ * avoids having to acquire the request <code>Form</code> object.
+ * This basically acquires the parameters object and invokes
+ * the <code>getParameters</code> method with the given name.
+ *
+ * @param name this is the name of the parameter value
+ */
+ public String getParameter(String name) {
+ return getQuery().get(name);
+ }
+
+ /**
+ * This method is used to acquire a <code>Part</code> from the
+ * HTTP request using a known name for the part. This is typically
+ * used when there is a file upload with a multipart POST request.
+ * All parts that are not files can be acquired as string values
+ * from the attachment object.
+ *
+ * @param name this is the name of the part object to acquire
+ *
+ * @return the named part or null if the part does not exist
+ */
+ public Part getPart(String name) {
+ return body.getPart(name);
+ }
+
+ /**
+ * This method is used to get all <code>Part</code> objects that
+ * are associated with the request. Each attachment contains the
+ * body and headers associated with it. If the request is not a
+ * multipart POST request then this will return an empty list.
+ *
+ * @return the list of parts associated with this request
+ */
+ public List<Part> getParts() {
+ return body.getParts();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestMessage.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestMessage.java
new file mode 100644
index 0000000..b22c74e
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestMessage.java
@@ -0,0 +1,341 @@
+/*
+ * RequestMessage.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.util.List;
+import java.util.Locale;
+
+import org.simpleframework.http.Address;
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Query;
+import org.simpleframework.http.RequestHeader;
+import org.simpleframework.http.message.Header;
+
+/**
+ * The <code>RequestMessage</code> object is used to create a HTTP
+ * request header representation. All requests for details within a
+ * request message delegates to an underlying header, which contains
+ * all of the header names and values sent by the client. The header
+ * names are case insensitively mapped as required by RFC 2616.
+ *
+ * @author Niall Gallagher
+ */
+class RequestMessage implements RequestHeader {
+
+ /**
+ * This is the underlying header used to house the headers.
+ */
+ protected Header header;
+
+ /**
+ * Constructor for the <code>RequestMessage</code> object. This
+ * is used to create a request message without an underlying
+ * header. In such an event it is up to the subclass to provide
+ * the instance, this is useful for testing the request.
+ */
+ public RequestMessage() {
+ super();
+ }
+
+ /**
+ * Constructor for the <code>RequestMessage</code> object. This
+ * is used to create a request with a header instance. In such
+ * a case the header provided will be queried for headers and is
+ * used to store headers added to this message instance.
+ *
+ * @param header this is the backing header for the message
+ */
+ public RequestMessage(Header header) {
+ this.header = header;
+ }
+
+ /**
+ * This can be used to get the URI specified for this HTTP
+ * request. This corresponds to the /index part of a
+ * http://www.domain.com/index URL but may contain the full
+ * URL. This is a read only value for the request.
+ *
+ * @return the URI that this HTTP request is targeting
+ */
+ public String getTarget() {
+ return header.getTarget();
+ }
+
+ /**
+ * This is used to acquire the address from the request line.
+ * An address is the full URI including the scheme, domain, port
+ * and the query parts. This allows various parameters to be
+ * acquired without having to parse the raw request target URI.
+ *
+ * @return this returns the address of the request line
+ */
+ public Address getAddress() {
+ return header.getAddress();
+ }
+
+ /**
+ * This is used to acquire the path as extracted from the HTTP
+ * request URI. The <code>Path</code> object that is provided by
+ * this method is immutable, it represents the normalized path
+ * only part from the request uniform resource identifier.
+ *
+ * @return this returns the normalized path for the request
+ */
+ public Path getPath() {
+ return header.getPath();
+ }
+
+ /**
+ * This method is used to acquire the query part from the
+ * HTTP request URI target. This will return only the values
+ * that have been extracted from the request URI target.
+ *
+ * @return the query associated with the HTTP target URI
+ */
+ public Query getQuery() {
+ return header.getQuery();
+ }
+
+ /**
+ * This can be used to get the HTTP method for this request. The
+ * HTTP specification RFC 2616 specifies the HTTP request methods
+ * in section 9, Method Definitions. Typically this will be a
+ * GET, POST or a HEAD method, although any string is possible.
+ *
+ * @return the request method for this request message
+ */
+ public String getMethod() {
+ return header.getMethod();
+ }
+
+ /**
+ * This can be used to get the major number from a HTTP version.
+ * The major version corresponds to the major type that is the 1
+ * of a HTTP/1.0 version string.
+ *
+ * @return the major version number for the request message
+ */
+ public int getMajor() {
+ return header.getMajor();
+ }
+
+ /**
+ * This can be used to get the major number from a HTTP version.
+ * The major version corresponds to the major type that is the 0
+ * of a HTTP/1.0 version string. This is used to determine if
+ * the request message has keep alive semantics.
+ *
+ * @return the major version number for the request message
+ */
+ public int getMinor() {
+ return header.getMinor();
+ }
+
+ /**
+ * This method is used to get a <code>List</code> of the names
+ * for the headers. This will provide the original names for the
+ * HTTP headers for the message. Modifications to the provided
+ * list will not affect the header, the list is a simple copy.
+ *
+ * @return this returns a list of the names within the header
+ */
+ public List<String> getNames() {
+ return header.getNames();
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. The value provided from this will
+ * be trimmed so there is no need to modify the value, also if
+ * the header name specified refers to a comma seperated list of
+ * values the value returned is the first value in that list.
+ * This returns null if theres no HTTP message header.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public String getValue(String name) {
+ return header.getValue(name);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. The value provided from this will
+ * be trimmed so there is no need to modify the value, also if
+ * the header name specified refers to a comma separated list of
+ * values the value returned is the first value in that list.
+ * This returns null if theres no HTTP message header.
+ *
+ * @param name the HTTP message header to get the value from
+ * @param index if there are multiple values this selects one
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public String getValue(String name, int index) {
+ return header.getValue(name, index);
+ }
+
+ /**
+ * This can be used to get the integer of the first message header
+ * that has the specified name. This is a convenience method that
+ * avoids having to deal with parsing the value of the requested
+ * HTTP message header. This returns -1 if theres no HTTP header
+ * value for the specified name.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the date as a long from the header value
+ */
+ public int getInteger(String name) {
+ return header.getInteger(name);
+ }
+
+ /**
+ * This can be used to get the date of the first message header
+ * that has the specified name. This is a convenience method that
+ * avoids having to deal with parsing the value of the requested
+ * HTTP message header. This returns -1 if theres no HTTP header
+ * value for the specified name.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the date as a long from the header value
+ */
+ public long getDate(String name) {
+ return header.getDate(name);
+ }
+
+ /**
+ * This can be used to get the values of HTTP message headers
+ * that have the specified name. This is a convenience method that
+ * will present that values as tokens extracted from the header.
+ * This has obvious performance benifits as it avoids having to
+ * deal with <code>substring</code> and <code>trim</code> calls.
+ * <p>
+ * The tokens returned by this method are ordered according to
+ * there HTTP quality values, or "q" values, see RFC 2616 section
+ * 3.9. This also strips out the quality parameter from tokens
+ * returned. So "image/html; q=0.9" results in "image/html". If
+ * there are no "q" values present then order is by appearence.
+ * <p>
+ * The result from this is either the trimmed header value, that
+ * is, the header value with no leading or trailing whitespace
+ * or an array of trimmed tokens ordered with the most preferred
+ * in the lower indexes, so index 0 is has higest preference.
+ *
+ * @param name the name of the headers that are to be retrieved
+ *
+ * @return ordered array of tokens extracted from the header(s)
+ */
+ public List<String> getValues(String name) {
+ return header.getValues(name);
+ }
+
+ /**
+ * This is used to acquire the locales from the request header. The
+ * locales are provided in the <code>Accept-Language</code> header.
+ * This provides an indication as to the languages that the client
+ * accepts. It provides the locales in preference order.
+ *
+ * @return this returns the locales preferred by the client
+ */
+ public List<Locale> getLocales() {
+ return header.getLocales();
+ }
+
+ /**
+ * This is used to acquire a cookie usiing the name of that cookie.
+ * If the cookie exists within the HTTP header then it is returned
+ * as a <code>Cookie</code> object. Otherwise this method will
+ * return null. Each cookie object will contain the name, value
+ * and path of the cookie as well as the optional domain part.
+ *
+ * @param name this is the name of the cookie object to acquire
+ *
+ * @return this returns a cookie object from the header or null
+ */
+ public Cookie getCookie(String name) {
+ return header.getCookie(name);
+ }
+
+ /**
+ * This is used to acquire all cookies that were sent in the header.
+ * If any cookies exists within the HTTP header they are returned
+ * as <code>Cookie</code> objects. Otherwise this method will an
+ * empty list. Each cookie object will contain the name, value and
+ * path of the cookie as well as the optional domain part.
+ *
+ * @return this returns all cookie objects from the HTTP header
+ */
+ public List<Cookie> getCookies() {
+ return header.getCookies();
+ }
+
+ /**
+ * This is a convenience method that can be used to determine the
+ * content type of the message body. This will determine whether
+ * there is a <code>Content-Type</code> header, if there is then
+ * this will parse that header and represent it as a typed object
+ * which will expose the various parts of the HTTP header.
+ *
+ * @return this returns the content type value if it exists
+ */
+ public ContentType getContentType() {
+ return header.getContentType();
+ }
+
+ /**
+ * This is a convenience method that can be used to determine
+ * the length of the message body. This will determine if there
+ * is a <code>Content-Length</code> header, if it does then the
+ * length can be determined, if not then this returns -1.
+ *
+ * @return the content length, or -1 if it cannot be determined
+ */
+ public long getContentLength() {
+ return header.getContentLength();
+ }
+
+ /**
+ * This method returns a <code>CharSequence</code> holding the header
+ * consumed for the request. A character sequence is returned as it
+ * can provide a much more efficient means of representing the header
+ * data by just wrapping the consumed byte array.
+ *
+ * @return this returns the characters consumed for the header
+ */
+ public CharSequence getHeader() {
+ return header.getHeader();
+ }
+
+ /**
+ * This is used to provide a string representation of the header
+ * read. Providing a string representation of the header is used
+ * so that on debugging the contents of the delivered header can
+ * be inspected in order to determine a cause of error.
+ *
+ * @return this returns a string representation of the header
+ */
+ public String toString() {
+ return header.toString();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestReader.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestReader.java
new file mode 100644
index 0000000..6f8cbaa
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/RequestReader.java
@@ -0,0 +1,131 @@
+/*
+ * RequestReader.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.core.ContainerEvent.ERROR;
+
+import java.nio.channels.SocketChannel;
+
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.reactor.Operation;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>RequestReader</code> object is used to read the bytes
+ * that form the request entity. In order to execute a read operation
+ * the socket must be read ready. This is determined using the socket
+ * object, which is registered with a controller. If at any point the
+ * reading results in an error the operation is cancelled and the
+ * collector is closed, which shuts down the connection.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.transport.reactor.Reactor
+ */
+class RequestReader implements Operation {
+
+ /**
+ * This is the selector used to process the collection events.
+ */
+ private final Controller controller;
+
+ /**
+ * This is the collector used to consume the entity bytes.
+ */
+ private final Collector collector;
+
+ /**
+ * This is the channel object associated with the collector.
+ */
+ private final Channel channel;
+
+ /**
+ * This is used to collect any trace information.
+ */
+ private final Trace trace;
+
+ /**
+ * Constructor for the <code>RequestReader</code> object. This is
+ * used to collect the data required to compose a HTTP request.
+ * Once all the data has been read by this it is dispatched.
+ *
+ * @param controller the controller object used to process events
+ * @param collector this is the task used to collect the entity
+ */
+ public RequestReader(Controller controller, Collector collector){
+ this.channel = collector.getChannel();
+ this.trace = channel.getTrace();
+ this.collector = collector;
+ this.controller = controller;
+ }
+
+ /**
+ * This is used to acquire the trace object that is associated
+ * with the operation. A trace object is used to collection details
+ * on what operations are being performed. For instance it may
+ * contain information relating to I/O events or errors.
+ *
+ * @return this returns the trace associated with this operation
+ */
+ public Trace getTrace() {
+ return trace;
+ }
+
+ /**
+ * This is the <code>SocketChannel</code> used to determine if the
+ * connection has some bytes that can be read. If it contains any
+ * data then that data is read from and is used to compose the
+ * request entity, which consists of a HTTP header and body.
+ *
+ * @return this returns the socket for the connected pipeline
+ */
+ public SocketChannel getChannel() {
+ return channel.getSocket();
+ }
+
+ /**
+ * This <code>run</code> method is used to collect the bytes from
+ * the connected channel. If a sufficient amount of data is read
+ * from the socket to form a HTTP entity then the collector uses
+ * the <code>Selector</code> object to dispatch the request. This
+ * is sequence of events that occur for each transaction.
+ */
+ public void run() {
+ try {
+ collector.collect(controller);
+ }catch(Throwable cause){
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+
+ /**
+ * This is used to cancel the operation if it has timed out. If
+ * the retry is waiting too long to read content from the socket
+ * then the retry is cancelled and the underlying transport is
+ * closed. This helps to clean up occupied resources.
+ */
+ public void cancel() {
+ try {
+ channel.close();
+ } catch(Throwable cause) {
+ trace.trace(ERROR, cause);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseBuffer.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseBuffer.java
new file mode 100644
index 0000000..d2a9f80
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseBuffer.java
@@ -0,0 +1,303 @@
+/*
+ * ResponseBuffer.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+
+import org.simpleframework.http.Response;
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.transport.Channel;
+
+/**
+ * The <code>ResponseBuffer</code> object is an output stream that can
+ * buffer bytes written up to a given size. This is used if a buffer
+ * is requested for the response output. Such a mechanism allows the
+ * response to be written without committing the response. Also it
+ * enables content that has been written to be reset, by simply
+ * clearing the response buffer. If the response buffer overflows
+ * then the response is committed.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.ResponseEncoder
+ */
+class ResponseBuffer extends OutputStream implements WritableByteChannel {
+
+ /**
+ * This is the transfer object used to transfer the response.
+ */
+ private ResponseEncoder encoder;
+
+ /**
+ * This is the buffer used to accumulate the response bytes.
+ */
+ private byte[] buffer;
+
+ /**
+ * This is used to determine if the accumulate was flushed.
+ */
+ private boolean flushed;
+
+ /**
+ * This is used to determine if the accumulator was closed.
+ */
+ private boolean closed;
+
+ /**
+ * This counts the number of bytes that have been accumulated.
+ */
+ private int count;
+
+ /**
+ * Constructor for the <code>ResponseBuffer</code> object. This will
+ * create a buffering output stream which will flush data to the
+ * underlying transport provided with the entity. All I/O events
+ * are reported to the monitor so the server can process other
+ * requests within the pipeline when the current one is finished.
+ *
+ * @param observer this is used to notify of response completion
+ * @param response this is the response header for this buffer
+ * @param support this is used to determine the response semantics
+ * @param entity this is used to acquire the underlying transport
+ */
+ public ResponseBuffer(BodyObserver observer, Response response, Conversation support, Entity entity) {
+ this(observer, response, support, entity.getChannel());
+ }
+
+ /**
+ * Constructor for the <code>ResponseBuffer</code> object. This will
+ * create a buffering output stream which will flush data to the
+ * underlying transport provided with the channel. All I/O events
+ * are reported to the monitor so the server can process other
+ * requests within the pipeline when the current one is finished.
+ *
+ * @param observer this is used to notify of response completion
+ * @param response this is the response header for this buffer
+ * @param support this is used to determine the response semantics
+ * @param channel this is the channel used to write the data to
+ */
+ public ResponseBuffer(BodyObserver observer, Response response, Conversation support, Channel channel) {
+ this.encoder = new ResponseEncoder(observer, response, support, channel);
+ this.buffer = new byte[] {};
+ }
+
+ /**
+ * This is used to determine if the accumulator is still open. If
+ * the accumulator is still open then data can still be written to
+ * it and this transmitted to the client. When the accumulator is
+ * closed the data is committed and this can not be used.
+ *
+ * @return this returns true if the accumulator object is open
+ */
+ public boolean isOpen() {
+ return !closed;
+ }
+
+ /**
+ * This is used to reset the buffer so that it can be written to
+ * again. If the accumulator has already been flushed then the
+ * stream can not be reset. Resetting the stream is typically
+ * done if there is an error in writing the response and an error
+ * message is generated to replaced the partial response.
+ */
+ public void reset() throws IOException {
+ if(flushed) {
+ throw new IOException("Response has been flushed");
+ }
+ count = 0;
+ }
+
+ /**
+ * This is used to write the provided octet to the buffer. If the
+ * buffer is full it will be flushed and the octet is appended to
+ * the start of the buffer. If however the buffer is zero length
+ * then this will write directly to the underlying transport.
+ *
+ * @param octet this is the octet that is to be written
+ */
+ public void write(int octet) throws IOException {
+ byte value = (byte) octet;
+
+ if(closed) {
+ throw new IOException("Response has been transferred");
+ }
+ write(new byte[] { value });
+ }
+
+ /**
+ * This is used to write the provided array to the buffer. If the
+ * buffer is full it will be flushed and the array is appended to
+ * the start of the buffer. If however the buffer is zero length
+ * then this will write directly to the underlying transport.
+ *
+ * @param array this is the array of bytes to send to the client
+ * @param off this is the offset within the array to send from
+ * @param size this is the number of bytes that are to be sent
+ */
+ public void write(byte[] array, int off, int size) throws IOException {
+ ByteBuffer buffer = ByteBuffer.wrap(array, off, size);
+
+ if(size > 0) {
+ write(buffer);
+ }
+ }
+
+ /**
+ * This is used to write the provided buffer to the buffer. If the
+ * buffer is full it will be flushed and the buffer is appended to
+ * the start of the buffer. If however the buffer is zero length
+ * then this will write directly to the underlying transport.
+ *
+ * @param source this is the byte buffer to send to the client
+ *
+ * @return this returns the number of bytes that have been sent
+ */
+ public int write(ByteBuffer source) throws IOException {
+ int mark = source.position();
+ int size = source.limit();
+
+ if(mark > size) {
+ throw new ResponseException("Buffer position greater than limit");
+ }
+ return write(source, 0, size - mark);
+ }
+
+ /**
+ * This is used to write the provided buffer to the buffer. If the
+ * buffer is full it will be flushed and the buffer is appended to
+ * the start of the buffer. If however the buffer is zero length
+ * then this will write directly to the underlying transport.
+ *
+ * @param source this is the byte buffer to send to the client
+ * @param off this is the offset within the array to send from
+ * @param size this is the number of bytes that are to be sent
+ *
+ * @return this returns the number of bytes that have been sent
+ */
+ public int write(ByteBuffer source, int off, int size) throws IOException {
+ if(closed) {
+ throw new IOException("Response has been transferred");
+ }
+ int mark = source.position();
+ int limit = source.limit();
+
+ if(limit - mark < size) { // not enough data
+ size = limit - mark; // reduce expectation
+ }
+ if(count + size > buffer.length) {
+ flush(false);
+ }
+ if(size > buffer.length){
+ encoder.write(source);
+ } else {
+ source.get(buffer, count, size);
+ count += size;
+ }
+ return size;
+ }
+
+ /**
+ * This is used to expand the capacity of the internal buffer. If
+ * there is already content that has been appended to the buffer
+ * this will copy that data to the newly created buffer. This
+ * will not decrease the size of the buffer if it is larger than
+ * the requested capacity.
+ *
+ * @param capacity this is the capacity to expand the buffer to
+ */
+ public void expand(int capacity) throws IOException {
+ if(buffer.length < capacity) {
+ int size = buffer.length * 2;
+ int resize = Math.max(capacity, size);
+ byte[] temp = new byte[resize];
+
+ System.arraycopy(buffer, 0, temp, 0, count);
+ buffer = temp;
+ }
+ }
+
+ /**
+ * This is used to flush the contents of the buffer to the
+ * underlying transport. Once the accumulator is flushed the HTTP
+ * headers are written such that the semantics of the connection
+ * match the protocol version and the existing response headers.
+ */
+ public void flush() throws IOException {
+ flush(true);
+ }
+
+ /**
+ * This is used to flush the contents of the buffer to the
+ * underlying transport. Once the accumulator is flushed the HTTP
+ * headers are written such that the semantics of the connection
+ * match the protocol version and the existing response headers.
+ *
+ * @param flush indicates whether the transport should be flushed
+ */
+ private void flush(boolean flush) throws IOException {
+ if(!flushed) {
+ encoder.start();
+ }
+ if(count > 0) {
+ encoder.write(buffer, 0, count);
+ }
+ if(flush) {
+ encoder.flush();
+ }
+ flushed = true;
+ count = 0;
+ }
+
+ /**
+ * This will flush the buffer to the underlying transport and
+ * close the stream. Once the accumulator is flushed the HTTP
+ * headers are written such that the semantics of the connection
+ * match the protocol version and the existing response headers.
+ * Closing this stream does not mean the connection is closed.
+ */
+ public void close() throws IOException {
+ if(!closed) {
+ commit();
+ }
+ flushed = true;
+ closed = true;
+ }
+
+ /**
+ * This will close the underlying transfer object which will
+ * notify the server kernel that the next request is read to be
+ * processed. If the accumulator is unflushed then this will set
+ * a Content-Length header such that it matches the number of
+ * bytes that are buffered within the internal buffer.
+ */
+ private void commit() throws IOException {
+ if(!flushed) {
+ encoder.start(count);
+ }
+ if(count > 0) {
+ encoder.write(buffer, 0, count);
+ }
+ encoder.close();
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEncoder.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEncoder.java
new file mode 100644
index 0000000..0fbe39b
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEncoder.java
@@ -0,0 +1,324 @@
+/*
+ * ResponseEncoder.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.core.ContainerEvent.WRITE_BODY;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import org.simpleframework.http.Response;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>ResponseEncoder</code> object acts as a means to determine
+ * the transfer encoding for the response body. This will ensure that
+ * the correct HTTP headers are used when the transfer of the body begins.
+ * In order to determine what headers to use this can be provided
+ * with a content length value. If the <code>start</code> method is
+ * provided with the content length then the HTTP headers will use a
+ * Content-Length header as the message delimiter. If there is no
+ * content length provided then the chunked encoding is used for
+ * HTTP/1.1 and connection close is used for HTTP/1.0.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.BodyEncoder
+ */
+class ResponseEncoder {
+
+ /**
+ * This is used to create a encoder based on the HTTP headers.
+ */
+ private BodyEncoderFactory factory;
+
+ /**
+ * This is used to determine the type of transfer required.
+ */
+ private Conversation support;
+
+ /**
+ * This is the response message that is to be committed.
+ */
+ private Response response;
+
+ /**
+ * Once the header is committed this is used to produce data.
+ */
+ private BodyEncoder encoder;
+
+ /**
+ * This is the trace used to monitor events in the data transfer.
+ */
+ private Trace trace;
+
+ /**
+ * Constructor for the <code>ResponseEncoder</code> object, this is
+ * used to create an object used to transfer a response body. This
+ * must be given a <code>Conversation</code> that can be used to set
+ * and get information regarding the type of transfer required.
+ *
+ * @param observer this is used to signal for response completion
+ * @param response this is the actual response message
+ * @param support this is used to determine the semantics
+ * @param channel this is the connected TCP channel for the response
+ */
+ public ResponseEncoder(BodyObserver observer, Response response, Conversation support, Channel channel) {
+ this.factory = new BodyEncoderFactory(observer, support, channel);
+ this.trace = channel.getTrace();
+ this.response = response;
+ this.support = support;
+ }
+
+ /**
+ * This is used to determine if the transfer has started. It has
+ * started when a encoder is created and the HTTP headers have
+ * been sent, or at least handed to the underlying transport.
+ * Once started the semantics of the connection can not change.
+ *
+ * @return this returns whether the transfer has started
+ */
+ public boolean isStarted() {
+ return encoder != null;
+ }
+
+ /**
+ * This starts the transfer with no specific content length set.
+ * This is typically used when dynamic data is emitted ans will
+ * require chunked encoding for HTTP/1.1 and connection close
+ * for HTTP/1.0. Once invoked the HTTP headers are committed.
+ */
+ public void start() throws IOException {
+ if(encoder != null) {
+ throw new ResponseException("Transfer has already started");
+ }
+ clear();
+ configure();
+ commit();
+ }
+
+ /**
+ * This starts the transfer with a known content length. This is
+ * used when there is a Content-Length header set. This will not
+ * encode the content for HTTP/1.1 however, HTTP/1.0 may need
+ * a connection close if it does not have keep alive semantics.
+ *
+ * @param length this is the length of the response body
+ */
+ public void start(int length) throws IOException {
+ if(encoder != null) {
+ throw new ResponseException("Transfer has already started");
+ }
+ clear();
+ configure(length);
+ commit();
+ }
+
+ /**
+ * This method is used to write content to the underlying socket.
+ * This will make use of the <code>Producer</code> object to
+ * encode the response body as required. If the encoder has not
+ * been created then this will throw an exception.
+ *
+ * @param array this is the array of bytes to send to the client
+ */
+ public void write(byte[] array) throws IOException {
+ write(array, 0, array.length);
+ }
+
+ /**
+ * This method is used to write content to the underlying socket.
+ * This will make use of the <code>Producer</code> object to
+ * encode the response body as required. If the encoder has not
+ * been created then this will throw an exception.
+ *
+ * @param array this is the array of bytes to send to the client
+ * @param off this is the offset within the array to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void write(byte[] array, int off, int len) throws IOException {
+ if(encoder == null) {
+ throw new ResponseException("Conversation details not ready");
+ }
+ trace.trace(WRITE_BODY, len);
+ encoder.encode(array, off, len);
+ }
+
+ /**
+ * This method is used to write content to the underlying socket.
+ * This will make use of the <code>Producer</code> object to
+ * encode the response body as required. If the encoder has not
+ * been created then this will throw an exception.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ */
+ public void write(ByteBuffer buffer) throws IOException {
+ int mark = buffer.position();
+ int size = buffer.limit();
+
+ if(mark > size) {
+ throw new ResponseException("Buffer position greater than limit");
+ }
+ write(buffer, 0, size - mark);
+ }
+
+ /**
+ * This method is used to write content to the underlying socket.
+ * This will make use of the <code>Producer</code> object to
+ * encode the response body as required. If the encoder has not
+ * been created then this will throw an exception.
+ *
+ * @param buffer this is the buffer of bytes to send to the client
+ * @param off this is the offset within the buffer to send from
+ * @param len this is the number of bytes that are to be sent
+ */
+ public void write(ByteBuffer buffer, int off, int len) throws IOException {
+ if(encoder == null) {
+ throw new ResponseException("Conversation details not ready");
+ }
+ trace.trace(WRITE_BODY, len);
+ encoder.encode(buffer, off, len);
+ }
+
+ /**
+ * This method is used to flush the contents of the buffer to
+ * the client. This method will block until such time as all of
+ * the data has been sent to the client. If at any point there
+ * is an error sending the content an exception is thrown.
+ */
+ public void flush() throws IOException {
+ if(encoder == null) {
+ throw new ResponseException("Conversation details not ready");
+ }
+ encoder.flush();
+ }
+
+ /**
+ * This is used to signal to the encoder that all content has
+ * been written and the user no longer needs to write. This will
+ * either close the underlying transport or it will notify the
+ * monitor that the response has completed and the next request
+ * can begin. This ensures the content is flushed to the client.
+ */
+ public void close() throws IOException {
+ if(encoder == null) {
+ throw new ResponseException("Conversation details not ready");
+ }
+ encoder.close();
+ }
+
+ /**
+ * This method is used to set the required HTTP headers on the
+ * response. This will check the existing HTTP headers, and if
+ * there is insufficient data chunked encoding will be used for
+ * HTTP/1.1 and connection close will be used for HTTP/1.0.
+ */
+ private void configure() throws IOException {
+ long length = support.getContentLength();
+ boolean empty = support.isEmpty();
+ boolean tunnel = support.isTunnel();
+
+ if(tunnel) {
+ support.setConnectionUpgrade();
+ } else if(empty) {
+ support.setContentLength(0);
+ } else if(length >= 0) {
+ support.setContentLength(length);
+ } else {
+ support.setChunkedEncoded();
+ }
+ encoder = factory.getInstance();
+ }
+
+ /**
+ * This method is used to set the required HTTP headers on the
+ * response. This will check the existing HTTP headers, and if
+ * there is insufficient data chunked encoding will be used for
+ * HTTP/1.1 and connection close will be used for HTTP/1.0.
+ *
+ * @param count this is the number of bytes to be transferred
+ */
+ private void configure(long count) throws IOException {
+ long length = support.getContentLength();
+
+ if(support.isHead()) {
+ if(count > 0) {
+ configure(count, count);
+ } else {
+ configure(count, length);
+ }
+ } else {
+ configure(count, count);
+ }
+ }
+
+ /**
+ * This method is used to set the required HTTP headers on the
+ * response. This will check the existing HTTP headers, and if
+ * there is insufficient data chunked encoding will be used for
+ * HTTP/1.1 and connection close will be used for HTTP/1.0.
+ *
+ * @param count this is the number of bytes to be transferred
+ * @param length this is the actual length value to be used
+ */
+ private void configure(long count, long length) throws IOException {
+ boolean empty = support.isEmpty();
+ boolean tunnel = support.isTunnel();
+
+ if(tunnel) {
+ support.setConnectionUpgrade();
+ } else if(empty) {
+ support.setContentLength(0);
+ } else if(length >= 0) {
+ support.setContentLength(length);
+ } else {
+ support.setChunkedEncoded();
+ }
+ encoder = factory.getInstance();
+ }
+
+ /**
+ * This is used to clear any previous encoding that has been set
+ * in the event that content length may be used instead. This is
+ * used so that an override can be made to the transfer encoding
+ * such that content length can be used instead.
+ */
+ private void clear() throws IOException {
+ support.setIdentityEncoded();
+
+ }
+
+ /**
+ * This is used to compose the HTTP header and send it over the
+ * transport to the client. Once done the response is committed
+ * and no more headers can be set, also the semantics of the
+ * response have been committed and the encoder is created.
+ */
+ private void commit() throws IOException {
+ try {
+ response.commit();
+ } catch(Exception cause) {
+ throw new ResponseException("Unable to commit", cause);
+ }
+ }
+
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEntity.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEntity.java
new file mode 100644
index 0000000..ae7aed9
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseEntity.java
@@ -0,0 +1,437 @@
+/*
+ * ResponseEntity.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.Protocol.CONTENT_LENGTH;
+import static org.simpleframework.http.Protocol.CONTENT_TYPE;
+import static org.simpleframework.http.core.ContainerEvent.WRITE_HEADER;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.channels.WritableByteChannel;
+import java.util.Map;
+
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * This is used to represent the HTTP response. This provides methods
+ * that can be used to set various characteristics of the response.
+ * The <code>OutputStream</code> of the <code>Response</code> can be
+ * retrieved from this interface as can the I.P address of the client
+ * that will be receiving the <code>Response</code>. The attributes
+ * of the connection can be retrieved also. This provides a set of
+ * methods that can be used to set the attributes of the stream so
+ * the <code>Response</code> can be transported properly. The headers
+ * can be set and will be sent once a commit is made, or when there
+ * is content sent over the output stream.
+ * <p>
+ * This should never allow the message body be sent if it should not
+ * be sent with the headers as of RFC 2616 rules for the presence of
+ * a message body. A message body must not be included with a HEAD
+ * request or with a 304 or a 204 response. A proper implementation
+ * of this will prevent a message body being sent if the response
+ * is to a HEAD request of if there is a 304 or 204 response code.
+ * <p>
+ * It is important to note that the <code>Response</code> controls
+ * the processing of the HTTP pipeline. The next HTTP request is
+ * not processed until the response has committed. The response is
+ * committed once the <code>commit</code> method is invoked if there
+ * is NO content body. Committing with a content body is done only if
+ * correct content is given. The <code>OutputStream</code> acts as
+ * a client and commits the response once the specified content has
+ * been written to the issued <code>OutputStream</code>.
+ *
+ * @author Niall Gallagher
+ */
+class ResponseEntity extends ResponseMessage implements Response {
+
+ /**
+ * This is the observer that is used to monitor the response.
+ */
+ private BodyObserver observer;
+
+ /**
+ * This is used to buffer the bytes that are sent to the client.
+ */
+ private ResponseBuffer buffer;
+
+ /**
+ * This is the conversation used to determine connection type.
+ */
+ private Conversation support;
+
+ /**
+ * This is the underlying channel for the connected pipeline.
+ */
+ private Channel channel;
+
+ /**
+ * This is the sender object used to deliver to response data.
+ */
+ private ByteWriter sender;
+
+ /**
+ * This is used to trace events that occur with the response
+ */
+ private Trace trace;
+
+ /**
+ * Constructor for the <code>ResponseEntity</code> object. This is
+ * used to create a response instance using the provided request,
+ * entity, and monitor object. To ensure that the response is
+ * compatible with client the <code>Request</code> is used. Also
+ * to ensure the next request can be processed the provided monitor
+ * is used to signal response events to the server kernel.
+ *
+ * @param observer this is the observer used to signal events
+ * @param request this is the request that was sent by the client
+ * @param entity this is the entity that contains the channel
+ */
+ public ResponseEntity(BodyObserver observer, Request request, Entity entity) {
+ this.support = new Conversation(request, this);
+ this.buffer = new ResponseBuffer(observer, this, support, entity);
+ this.channel = entity.getChannel();
+ this.sender = channel.getWriter();
+ this.trace = channel.getTrace();
+ this.observer = observer;
+ }
+
+ /**
+ * This represents the time at which the response has fully written.
+ * Because the response is delivered asynchronously to the client
+ * this response time does not represent the time to last byte.
+ * It simply represents the time at which the response has been
+ * fully generated and written to the output buffer or queue. This
+ * returns zero if the response has not finished.
+ *
+ * @return this is the time taken to complete the response
+ */
+ public long getResponseTime() {
+ return observer.getTime();
+ }
+
+ /**
+ * This is used as a shortcut for acquiring attributes for the
+ * response. This avoids acquiring the <code>Attributes</code>
+ * in order to retrieve the attribute directly from that object.
+ * The attributes contain data specific to the response.
+ *
+ * @param name this is the name of the attribute to acquire
+ *
+ * @return this returns the attribute for the specified name
+ */
+ public Object getAttribute(Object name) {
+ return getAttributes().get(name);
+ }
+
+ /**
+ * This can be used to retrieve certain attributes about
+ * this <code>Response</code>. The attributes contains certain
+ * properties about the <code>Response</code>. For example if
+ * this Response goes over a secure line then there may be any
+ * arbitrary attributes.
+ *
+ * @return the response attributes of that have been set
+ */
+ public Map getAttributes() {
+ return channel.getAttributes();
+ }
+
+ /**
+ * This should be used when the size of the message body is known. For
+ * performance reasons this should be used so the length of the output
+ * is known. This ensures that Persistent HTTP (PHTTP) connections
+ * can be maintained for both HTTP/1.0 and HTTP/1.1 clients. If the
+ * length of the output is not known HTTP/1.0 clients will require a
+ * connection close, which reduces performance (see RFC 2616).
+ * <p>
+ * This removes any previous Content-Length headers from the message
+ * header. This will then set the appropriate Content-Length header with
+ * the correct length. If a the Connection header is set with the close
+ * token then the semantics of the connection are such that the server
+ * will close it once the <code>OutputStream.close</code> is used.
+ *
+ * @param length this is the length of the HTTP message body
+ */
+ public void setContentLength(long length) {
+ setLong(CONTENT_LENGTH, length);
+ }
+
+ /**
+ * This is used to set the content type for the response. Typically
+ * a response will contain a message body of some sort. This is used
+ * to conveniently set the type for that response. Setting the
+ * content type can also be done explicitly if desired.
+ *
+ * @param type this is the type that is to be set in the response
+ */
+ public void setContentType(String type) {
+ setValue(CONTENT_TYPE, type);
+ }
+
+ /**
+ * This determines the charset for <code>PrintStream</code> objects
+ * returned from the <code>getPrintStream</code> method. This will
+ * return a valid charset regardless of whether the Content-Type
+ * header has been set, set without a charset, or not set at all.
+ * If unspecified, the charset returned is <code>ISO-8859-1</code>,
+ * as suggested by RFC 2616, section 3.7.1.
+ *
+ * @return returns the charset used by this response object
+ */
+ private String getCharset() {
+ ContentType type = getContentType();
+
+ if(type == null) {
+ return "ISO-8859-1";
+ }
+ if(type.getCharset()==null){
+ return "ISO-8859-1";
+ }
+ return type.getCharset();
+ }
+
+ /**
+ * Used to write a message body with the <code>Response</code>. The
+ * semantics of this <code>OutputStream</code> will be determined
+ * by the HTTP version of the client, and whether or not the content
+ * length has been set, through the <code>setContentLength</code>
+ * method. If the length of the output is not known then the output
+ * is chunked for HTTP/1.1 clients and closed for HTTP/1.0 clients.
+ *
+ * @return an output stream object used to write the message body
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return buffer;
+ }
+
+ /**
+ * Used to write a message body with the <code>Response</code>. The
+ * semantics of this <code>OutputStream</code> will be determined
+ * by the HTTP version of the client, and whether or not the content
+ * length has been set, through the <code>setContentLength</code>
+ * method. If the length of the output is not known then the output
+ * is chunked for HTTP/1.1 clients and closed for HTTP/1.0 clients.
+ * <p>
+ * This will ensure that there is buffering done so that the output
+ * can be reset using the <code>reset</code> method. This will
+ * enable the specified number of bytes to be written without
+ * committing the response. This specified size is the minimum size
+ * that the response buffer must be.
+ *
+ * @param size the minimum size that the response buffer must be
+ *
+ * @return an output stream object used to write the message body
+ */
+ public OutputStream getOutputStream(int size) throws IOException {
+ if(size > 0) {
+ buffer.expand(size);
+ }
+ return buffer;
+ }
+
+ /**
+ * This method is provided for convenience so that the HTTP content
+ * can be written using the <code>print</code> methods provided by
+ * the <code>PrintStream</code>. This will basically wrap the
+ * <code>getOutputStream</code> with a buffer size of zero.
+ * <p>
+ * The retrieved <code>PrintStream</code> uses the charset used to
+ * describe the content, with the Content-Type header. This will
+ * check the charset parameter of the contents MIME type. So if
+ * the Content-Type was <code>text/plain; charset=UTF-8</code> the
+ * resulting <code>PrintStream</code> would encode the written data
+ * using the UTF-8 encoding scheme. Care must be taken to ensure
+ * that bytes written to the stream are correctly encoded.
+ *
+ * @return a print stream object used to write the message body
+ */
+ public PrintStream getPrintStream() throws IOException {
+ return getPrintStream(0, getCharset());
+ }
+
+ /**
+ * This method is provided for convenience so that the HTTP content
+ * can be written using the <code>print</code> methods provided by
+ * the <code>PrintStream</code>. This will basically wrap the
+ * <code>getOutputStream</code> with a specified buffer size.
+ * <p>
+ * The retrieved <code>PrintStream</code> uses the charset used to
+ * describe the content, with the Content-Type header. This will
+ * check the charset parameter of the contents MIME type. So if
+ * the Content-Type was <code>text/plain; charset=UTF-8</code> the
+ * resulting <code>PrintStream</code> would encode the written data
+ * using the UTF-8 encoding scheme. Care must be taken to ensure
+ * that bytes written to the stream are correctly encoded.
+ *
+ * @param size the minimum size that the response buffer must be
+ *
+ * @return a print stream object used to write the message body
+ */
+ public PrintStream getPrintStream(int size) throws IOException {
+ return getPrintStream(size, getCharset());
+ }
+
+ /**
+ * This is used to wrap the <code>getOutputStream</code> object in
+ * a <code>PrintStream</code>, which will write content using a
+ * specified charset. The <code>PrintStream</code> created will not
+ * buffer the content, it will write directly to the underlying
+ * <code>OutputStream</code> where it is buffered (if there is a
+ * buffer size greater than zero specified). In future the buffer
+ * of the <code>PrintStream</code> may be usable.
+ *
+ * @param size the minimum size that the response buffer must be
+ * @param charset this is the charset used by the resulting stream
+ *
+ * @return a print stream that encodes in the given charset
+ */
+ private PrintStream getPrintStream(int size, String charset) throws IOException {
+ if(size > 0) {
+ buffer.expand(size);
+ }
+ return new PrintStream(buffer, false, charset);
+ }
+
+ /**
+ * Used to write a message body with the <code>Response</code>. The
+ * semantics of this <code>WritableByteChannel</code> are determined
+ * by the HTTP version of the client, and whether or not the content
+ * length has been set, through the <code>setContentLength</code>
+ * method. If the length of the output is not known then the output
+ * is chunked for HTTP/1.1 clients and closed for HTTP/1.0 clients.
+ *
+ * @return a writable byte channel used to write the message body
+ */
+ public WritableByteChannel getByteChannel() throws IOException {
+ return buffer;
+ }
+
+ /**
+ * Used to write a message body with the <code>Response</code>. The
+ * semantics of this <code>WritableByteChannel</code> are determined
+ * by the HTTP version of the client, and whether or not the content
+ * length has been set, through the <code>setContentLength</code>
+ * method. If the length of the output is not known then the output
+ * is chunked for HTTP/1.1 clients and closed for HTTP/1.0 clients.
+ * <p>
+ * This will ensure that there is buffering done so that the output
+ * can be reset using the <code>reset</code> method. This will
+ * enable the specified number of bytes to be written without
+ * committing the response. This specified size is the minimum size
+ * that the response buffer must be.
+ *
+ * @param size the minimum size that the response buffer must be
+ *
+ * @return a writable byte channel used to write the message body
+ */
+ public WritableByteChannel getByteChannel(int size) throws IOException {
+ if(size > 0) {
+ buffer.expand(size);
+ }
+ return buffer;
+ }
+
+ /**
+ * This is used to determine if the HTTP response message is a
+ * keep alive message or if the underlying socket was closed. Even
+ * if the client requests a connection keep alive and supports
+ * persistent connections, the response can still be closed by
+ * the server. This can be explicitly indicated by the presence
+ * of the <code>Connection</code> HTTP header, it can also be
+ * implicitly indicated by using version HTTP/1.0.
+ *
+ * @return this returns true if the connection was closed
+ */
+ public boolean isKeepAlive() {
+ return support.isKeepAlive();
+ }
+
+ /**
+ * This can be used to determine whether the <code>Response</code>
+ * has been committed. This is true if the <code>Response</code>
+ * was committed, either due to an explicit invocation of the
+ * <code>commit</code> method or due to the writing of content. If
+ * the <code>Response</code> has committed the <code>reset</code>
+ * method will not work in resetting content already written.
+ *
+ * @return true if the response has been fully committed
+ */
+ public boolean isCommitted() {
+ return observer.isCommitted();
+ }
+
+ /**
+ * This is used to write the headers that where given to the
+ * <code>Response</code>. Any further attempts to give headers
+ * to the <code>Response</code> will be futile as only the headers
+ * that were given at the time of the first commit will be used
+ * in the message header.
+ * <p>
+ * This also performs some final checks on the headers submitted.
+ * This is done to determine the optimal performance of the
+ * output. If no specific Connection header has been specified
+ * this will set the connection so that HTTP/1.0 closes by default.
+ *
+ * @exception IOException thrown if there was a problem writing
+ */
+ public void commit() throws IOException {
+ if(!observer.isCommitted()) {
+ String header = toString();
+ byte[] message = header.getBytes("UTF-8");
+
+ trace.trace(WRITE_HEADER, header);
+ sender.write(message);
+ observer.commit(sender);
+ }
+ }
+
+ /**
+ * This can be used to determine whether the <code>Response</code>
+ * has been committed. This is true if the <code>Response</code>
+ * was committed, either due to an explicit invocation of the
+ * <code>commit</code> method or due to the writing of content. If
+ * the <code>Response</code> has committed the <code>reset</code>
+ * method will not work in resetting content already written.
+ *
+ * @throws IOException thrown if there is a problem resetting
+ */
+ public void reset() throws IOException {
+ buffer.reset();
+ }
+
+ /**
+ * This is used to close the connection and commit the request.
+ * This provides the same semantics as closing the output stream
+ * and ensures that the HTTP response is committed. This will
+ * throw an exception if the response can not be committed.
+ *
+ * @throws IOException thrown if there is a problem writing
+ */
+ public void close() throws IOException {
+ buffer.close();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseException.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseException.java
new file mode 100644
index 0000000..a02fe78
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseException.java
@@ -0,0 +1,58 @@
+/*
+ * ResponseException.java February 2007
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import java.io.IOException;
+
+/**
+ * The <code>ResponseException</code> object is used to represent an
+ * exception that is thrown when there is a problem producing the
+ * response body. This can be used to wrap <code>IOException</code>
+ * objects that are thrown from the underlying transport.
+ *
+ * @author Niall Gallagher
+ */
+class ResponseException extends IOException {
+
+ /**
+ * Constructor for the <code>ResponseException</code> object. This
+ * is used to represent an exception that is thrown when producing
+ * the response body. The producer exception is an I/O exception
+ * and thus exceptions can propagate out of stream methods.
+ *
+ * @param message this is the message describing the exception
+ */
+ public ResponseException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor for the <code>ResponseException</code> object. This
+ * is used to represent an exception that is thrown when producing
+ * the response body. The producer exception is an I/O exception
+ * and thus exceptions can propagate out of stream methods.
+ *
+ * @param message this is the message describing the exception
+ * @param cause this is the cause of the producer exception
+ */
+ public ResponseException(String message, Throwable cause) {
+ super(message);
+ initCause(cause);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseMessage.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseMessage.java
new file mode 100644
index 0000000..4dcfb08
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseMessage.java
@@ -0,0 +1,283 @@
+/*
+ * ResponseMessage.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static org.simpleframework.http.Protocol.CONTENT_LENGTH;
+import static org.simpleframework.http.Protocol.CONTENT_TYPE;
+import static org.simpleframework.http.Protocol.SET_COOKIE;
+import static org.simpleframework.http.Protocol.TRANSFER_ENCODING;
+
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.ResponseHeader;
+import org.simpleframework.http.Status;
+import org.simpleframework.http.message.MessageHeader;
+import org.simpleframework.http.parse.ContentTypeParser;
+
+/**
+ * The <code>ResponseMessage</code> object represents the header used
+ * for a response. This is used to get and set the headers in a case
+ * insensitive manner. It is also used to manage the cookies that are
+ * send and received. Also, the status code and description can also
+ * be set through this object as well as the protocol version.
+ *
+ * @author Niall Gallagher
+ */
+class ResponseMessage extends MessageHeader implements ResponseHeader {
+
+ /**
+ * This is the text description used for the response status.
+ */
+ private String text;
+
+ /**
+ * This is the major protocol version used for the response.
+ */
+ private int major;
+
+ /**
+ * This is the minor protocol version used for the response.
+ */
+ private int minor;
+
+ /**
+ * This is the status code used to identify the response type.
+ */
+ private int code;
+
+ /**
+ * Constructor for the <code>ResponseMessage</code> object. This
+ * is used to create a response message with a default status code
+ * of 200 and a a protocol version of HTTP/1.1. If the response is
+ * a different status code or version these can be modified.
+ */
+ public ResponseMessage() {
+ this.text = "OK";
+ this.code = 200;
+ this.major = 1;
+ this.minor = 1;
+ }
+
+ /**
+ * This represents the status code of the HTTP response.
+ * The response code represents the type of message that is
+ * being sent to the client. For a description of the codes
+ * see RFC 2616 section 10, Status Code Definitions.
+ *
+ * @return the status code that this HTTP response has
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * This method allows the status for the response to be
+ * changed. This MUST be reflected the the response content
+ * given to the client. For a description of the codes see
+ * RFC 2616 section 10, Status Code Definitions.
+ *
+ * @param code the new status code for the HTTP response
+ */
+ public void setCode(int code) {
+ this.code = code;
+ }
+
+ /**
+ * This can be used to retrieve the text of a HTTP status
+ * line. This is the text description for the status code.
+ * This should match the status code specified by the RFC.
+ *
+ * @return the message description of the response
+ */
+ public String getDescription() {
+ return text;
+ }
+
+ /**
+ * This is used to set the text of the HTTP status line.
+ * This should match the status code specified by the RFC.
+ *
+ * @param text the descriptive text message of the status
+ */
+ public void setDescription(String text) {
+ this.text = text;
+ }
+
+ /**
+ * This is used to acquire the status from the response.
+ * The <code>Status</code> object returns represents the
+ * code that has been set on the response, it does not
+ * necessarily represent the description in the response.
+ *
+ * @return this is the response for this status line
+ */
+ public Status getStatus() {
+ return Status.getStatus(code);
+ }
+
+ /**
+ * This is used to set the status code and description
+ * for this response. Setting the code and description in
+ * this manner provides a much more convenient way to set
+ * the response status line details.
+ *
+ * @param status this is the status to set on the response
+ */
+ public void setStatus(Status status) {
+ setCode(status.code);
+ setDescription(status.description);
+ }
+
+ /**
+ * This can be used to get the major number from a HTTP version.
+ * The major version corresponds to the major type that is the 1
+ * of a HTTP/1.0 version string.
+ *
+ * @return the major version number for the request message
+ */
+ public int getMajor() {
+ return major;
+ }
+
+ /**
+ * This can be used to set the major number from a HTTP version.
+ * The major version corresponds to the major type that is the 1
+ * of a HTTP/1.0 version string.
+ *
+ * @param major the major version number for the request message
+ */
+ public void setMajor(int major) {
+ this.major = major;
+ }
+
+ /**
+ * This can be used to get the minor number from a HTTP version.
+ * The minor version corresponds to the major type that is the 0
+ * of a HTTP/1.0 version string. This is used to determine if
+ * the request message has keep alive semantics.
+ *
+ * @return the minor version number for the request message
+ */
+ public int getMinor() {
+ return minor;
+ }
+
+ /**
+ * This can be used to get the minor number from a HTTP version.
+ * The minor version corresponds to the major type that is the 0
+ * of a HTTP/1.0 version string. This is used to determine if
+ * the request message has keep alive semantics.
+ *
+ * @param minor the minor version number for the request message
+ */
+ public void setMinor(int minor) {
+ this.minor = minor;
+ }
+
+ /**
+ * This is a convenience method that can be used to determine the
+ * content type of the message body. This will determine whether
+ * there is a <code>Content-Type</code> header, if there is then
+ * this will parse that header and represent it as a typed object
+ * which will expose the various parts of the HTTP header.
+ *
+ * @return this returns the content type value if it exists
+ */
+ public ContentType getContentType() {
+ String value = getValue(CONTENT_TYPE);
+
+ if(value == null) {
+ return null;
+ }
+ return new ContentTypeParser(value);
+ }
+
+ /**
+ * This is a convenience method that can be used to determine
+ * the length of the message body. This will determine if there
+ * is a <code>Content-Length</code> header, if it does then the
+ * length can be determined, if not then this returns -1.
+ *
+ * @return content length, or -1 if it cannot be determined
+ */
+ public long getContentLength() {
+ return getLong(CONTENT_LENGTH);
+ }
+
+ /**
+ * This is a convenience method that can be used to determine the
+ * content type of the message body. This will determine whether
+ * there is a <code>Transfer-Encoding</code> header, if there is
+ * then this will parse that header and return the first token in
+ * the comma separated list of values, which is the primary value.
+ *
+ * @return this returns the transfer encoding value if it exists
+ */
+ public String getTransferEncoding() {
+ return getValue(TRANSFER_ENCODING);
+ }
+
+ /**
+ * This is used to compose the HTTP response header. All of the
+ * headers added to the response are added, as well as the cookies
+ * to form the response message header. To ensure that the text
+ * produces is as required the header names are in the same case
+ * as they were added to the response message.
+ *
+ * @return a string representation of the response message
+ */
+ public CharSequence getHeader() {
+ return toString();
+ }
+
+ /**
+ * This is used to compose the HTTP response header. All of the
+ * headers added to the response are added, as well as the cookies
+ * to form the response message header. To ensure that the text
+ * produces is as required the header names are in the same case
+ * as they were added to the response message.
+ *
+ * @return a string representation of the response message
+ */
+ public String toString() {
+ StringBuilder head = new StringBuilder(256);
+
+ head.append("HTTP/").append(major);
+ head.append('.').append(minor);
+ head.append(' ').append(code);
+ head.append(' ').append(text);
+ head.append("\r\n");
+
+ for(String name : getNames()) {
+ for(String value : getAll(name)) {
+ head.append(name);
+ head.append(": ");
+ head.append(value);
+ head.append("\r\n");
+ }
+ }
+ for(Cookie cookie : getCookies()) {
+ head.append(SET_COOKIE);
+ head.append(": ");
+ head.append(cookie);
+ head.append("\r\n");
+ }
+ return head.append("\r\n").toString();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseObserver.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseObserver.java
new file mode 100644
index 0000000..4a4b2c7
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/ResponseObserver.java
@@ -0,0 +1,238 @@
+/*
+ * ResponseObserver.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.simpleframework.http.core.ContainerEvent.ERROR;
+import static org.simpleframework.http.core.ContainerEvent.RESPONSE_FINISHED;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.simpleframework.http.message.Entity;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>ResponseObserver</code> is used to observe the response
+ * streams. If there is an error or a close requested this will
+ * close the underlying transport. If however there is a successful
+ * response then this will flush the transport and hand the channel
+ * for the pipeline back to the server kernel. This ensures that
+ * the next HTTP request can be consumed from the transport.
+ *
+ * @author Niall Gallagher
+ */
+class ResponseObserver implements BodyObserver {
+
+ /**
+ * This is used to determine if the response has committed.
+ */
+ private AtomicBoolean committed;
+
+ /**
+ * This flag determines whether the connection was closed.
+ */
+ private AtomicBoolean closed;
+
+ /**
+ * This flag determines whether the was a response error.
+ */
+ private AtomicBoolean error;
+
+ /**
+ * This is the controller used to initiate a new request.
+ */
+ private Controller controller;
+
+ /**
+ * This is the channel associated with the client connection.
+ */
+ private Channel channel;
+
+ /**
+ * This is the trace used to observe the state of the stream.
+ */
+ private Trace trace;
+
+ /**
+ * This represents a time stamp that records the finish time.
+ */
+ private Timer timer;
+
+ /**
+ * Constructor for the <code>ResponseObserver</code> object. This
+ * is used to create an observer using a HTTP request entity and an
+ * initiator which is used to reprocess a channel if there was a
+ * successful deliver of a response.
+ *
+ * @param controller the controller used to process channels
+ * @param entity this is the entity associated with the channel
+ */
+ public ResponseObserver(Controller controller, Entity entity) {
+ this.timer = new Timer(MILLISECONDS);
+ this.committed = new AtomicBoolean();
+ this.closed = new AtomicBoolean();
+ this.error = new AtomicBoolean();
+ this.channel = entity.getChannel();
+ this.trace = channel.getTrace();
+ this.controller = controller;
+ }
+
+ /**
+ * This is used to close the underlying transport. A closure is
+ * typically done when the response is to a HTTP/1.0 client
+ * that does not require a keep alive connection. Also, if the
+ * container requests an explicit closure this is used when all
+ * of the content for the response has been sent.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ public void close(ByteWriter writer) {
+ try {
+ if(!isClosed()) {
+ closed.set(true);
+ timer.set();
+ trace.trace(RESPONSE_FINISHED);
+ writer.close();
+ }
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ fail(writer);
+ }
+ }
+
+ /**
+ * This is used when there is an error sending the response. On
+ * error RFC 2616 suggests a connection closure is the best
+ * means to handle the condition, and the one clients should be
+ * expecting and support. All errors result in closure of the
+ * underlying transport and no more requests are processed.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ public void error(ByteWriter writer) {
+ try {
+ if(!isClosed()) {
+ error.set(true);
+ timer.set();
+ trace.trace(RESPONSE_FINISHED);
+ writer.close();
+ }
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ fail(writer);
+ }
+ }
+
+ /**
+ * This is used when the response has been sent correctly and
+ * the connection supports persisted HTTP. When ready the channel
+ * is handed back in to the server kernel where the next request
+ * on the pipeline is read and used to compose the next entity.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ public void ready(ByteWriter writer) {
+ try {
+ if(!isClosed()) {
+ closed.set(true);
+ writer.flush();
+ timer.set();
+ trace.trace(RESPONSE_FINISHED);
+ controller.start(channel);
+ }
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ fail(writer);
+ }
+ }
+
+ /**
+ * This is used to purge the writer so that it closes the socket
+ * ensuring there is no connection leak on shutdown. This is used
+ * when there is an exception signalling the state of the writer.
+ *
+ * @param writer this is the writer that is to be purged
+ */
+ private void fail(ByteWriter writer) {
+ try {
+ writer.close();
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ }
+ }
+
+ /**
+ * This is used to notify the observer that the HTTP response is
+ * committed and that the header can no longer be changed. It
+ * is also used to indicate whether the response can be reset.
+ *
+ * @param writer this is the writer used to send the response
+ */
+ public void commit(ByteWriter writer) {
+ committed.set(true);
+ }
+
+ /**
+ * This can be used to determine whether the response has been
+ * committed. If the response is committed then the header can
+ * no longer be manipulated and the response has been partially
+ * send to the client.
+ *
+ * @return true if the response headers have been committed
+ */
+ public boolean isCommitted() {
+ return committed.get();
+ }
+
+ /**
+ * This is used to determine if the response has completed or
+ * if there has been an error. This basically allows the writer
+ * of the response to take action on certain I/O events.
+ *
+ * @return this returns true if there was an error or close
+ */
+ public boolean isClosed() {
+ return closed.get() || error.get();
+ }
+
+ /**
+ * This is used to determine if the response was in error. If
+ * the response was in error this allows the writer to throw an
+ * exception indicating that there was a problem responding.
+ *
+ * @return this returns true if there was a response error
+ */
+ public boolean isError(){
+ return error.get();
+ }
+
+ /**
+ * This represents the time at which the response was either
+ * ready, closed or in error. Providing a time here is useful
+ * as it allows the time taken to generate a response to be
+ * determined even if the response is written asynchronously.
+ *
+ * @return the time when the response completed or failed
+ */
+ public long getTime() {
+ return timer.get();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/core/Timer.java b/simple/simple-http/src/main/java/org/simpleframework/http/core/Timer.java
new file mode 100644
index 0000000..c07d49b
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/core/Timer.java
@@ -0,0 +1,94 @@
+/*
+ * Timer.java November 2012
+ *
+ * Copyright (C) 2012, Niall Gallagher <niallg@users.sf.net>
+ *
+ * Licensed 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.
+ */
+
+package org.simpleframework.http.core;
+
+import static java.lang.System.currentTimeMillis;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>Timer<code> object is used to set the time a specific
+ * event occurred at. The time can be set only once from that point
+ * on all attempts to set the time are ignored. This makes this
+ * timer useful when there is a desire to record when a certain
+ * scenario was first encountered, for example when a request is
+ * first read from the underlying transport.
+ *
+ * @author Niall Gallagher
+ */
+class Timer {
+
+ /**
+ * This is the time unit that this timer provides the time in.
+ */
+ private TimeUnit unit;
+
+ /**
+ * This is the time in milliseconds used to record the event.
+ */
+ private volatile long time;
+
+ /**
+ * Constructor for the <code>Timer</code> object. This is used
+ * to record when a specific event occurs. The provided time
+ * unit is used to determine how the time is retrieved.
+ *
+ * @param unit this time unit this timer will be using
+ */
+ public Timer(TimeUnit unit) {
+ this.unit = unit;
+ this.time = -1L;
+ }
+
+ /**
+ * This is used to determine if the timer has been set. If
+ * the <code>set</code> method has been called on this instance
+ * before then this will return true, otherwise false.
+ *
+ * @return this returns true if the timer has been set
+ */
+ public boolean isSet() {
+ return time > 0;
+ }
+
+ /**
+ * This is used to set the time for a specific event. Invoking
+ * this method multiple times will have no effect as the time
+ * is set for the first invocation only. Setting the time in
+ * this manner enables start times to be recorded effectively.
+ */
+ public void set() {
+ if(time < 0) {
+ time = currentTimeMillis();
+ }
+ }
+
+ /**
+ * This is used to get the time for a specific event. The time
+ * returned by this method is given in the time unit specified
+ * on construction of the instance.
+ *
+ * @return this returns the time recorded by the timer
+ */
+ public long get() {
+ return unit.convert(time, MILLISECONDS);
+ }
+}
+ \ No newline at end of file