summaryrefslogtreecommitdiffstats
path: root/simple/simple-http/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'simple/simple-http/src/main')
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Address.java157
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/ContentDisposition.java59
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/ContentType.java142
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Cookie.java527
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Method.java70
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Part.java107
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Path.java166
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Principal.java48
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Protocol.java370
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Query.java99
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Request.java210
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/RequestHeader.java201
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/RequestLine.java98
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/RequestWrapper.java520
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Response.java262
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/ResponseHeader.java304
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/ResponseWrapper.java747
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Scheme.java136
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/Status.java320
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/StatusLine.java122
-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
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/ArrayConsumer.java184
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/Body.java95
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/BodyConsumer.java43
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/BoundaryConsumer.java206
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/BufferBody.java166
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/BufferPart.java160
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/ByteConsumer.java64
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/ChunkedConsumer.java258
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/ConsumerFactory.java201
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/ContentConsumer.java226
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/ContinueDispatcher.java88
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyConsumer.java69
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyInputStream.java44
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/Entity.java75
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/EntityConsumer.java184
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/FileUploadConsumer.java272
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/FixedLengthConsumer.java128
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/Header.java213
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/HeaderConsumer.java114
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/Message.java273
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/MessageHeader.java477
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartBodyConsumer.java129
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartConsumer.java135
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartData.java101
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryConsumer.java112
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryFactory.java84
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartFactory.java78
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartHeaderConsumer.java85
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeries.java68
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeriesConsumer.java165
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/RequestConsumer.java457
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/Segment.java163
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/SegmentConsumer.java750
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/TokenConsumer.java113
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/message/UpdateConsumer.java143
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/AddressParser.java1347
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentDispositionParser.java296
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentTypeParser.java556
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/CookieParser.java589
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java642
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/LanguageParser.java156
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/ListParser.java456
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/PathParser.java726
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/PrincipalParser.java362
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/QueryParser.java636
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/parse/ValueParser.java108
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/BinaryData.java75
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/CloseCode.java150
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/Data.java51
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/DataConverter.java111
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/DataFrame.java212
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/Frame.java85
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameChannel.java117
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameListener.java64
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameType.java142
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/Reason.java97
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/Session.java91
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/TextData.java75
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/AcceptToken.java127
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/DirectRouter.java107
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameBuilder.java118
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameCollector.java179
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConnection.java214
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConsumer.java162
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameEncoder.java229
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeader.java80
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeaderConsumer.java235
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameProcessor.java255
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/OutputBarrier.java99
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/PathRouter.java111
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ProtocolRouter.java105
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ReasonExtractor.java114
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RequestValidator.java137
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java159
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Router.java59
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RouterContainer.java109
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Service.java44
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceChannel.java149
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceDispatcher.java101
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceEvent.java97
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceSession.java139
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionBuilder.java93
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionDispatcher.java111
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusChecker.java220
-rw-r--r--simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusResultListener.java93
136 files changed, 27186 insertions, 0 deletions
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Address.java b/simple/simple-http/src/main/java/org/simpleframework/http/Address.java
new file mode 100644
index 0000000..cd05280
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Address.java
@@ -0,0 +1,157 @@
+/*
+ * Address.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;
+
+import org.simpleframework.common.KeyMap;
+
+/**
+ * The <code>Address</code> interface is used to represent a generic
+ * uniform resource identifier. This interface allows each section
+ * of the uniform resource identifier to be represented. A generic
+ * uniform resource identifier syntax is represented in RFC 2616
+ * section 3.2.2 for the HTTP protocol, this allows similar URI's
+ * for example ftp, http, https, tftp. The syntax is
+ * <code><pre>
+ *
+ * URI = [scheme "://"] host [ ":" port ] [ path [ "?" query ]]
+ *
+ * </pre></code>
+ * This interface represents the host, port, path and query part
+ * of the uniform resource identifier. The parameters are also
+ * represented by the URI. The parameters in a URI consist of name
+ * and value pairs in the path segment of the URI.
+ * <p>
+ * This will normalize the path part of the uniform resource
+ * identifier. A normalized path is one that contains no back
+ * references like "./" and "../". The normalized path will not
+ * contain the path parameters.
+ *
+ * @author Niall Gallagher
+ */
+public interface Address {
+
+ /**
+ * This allows the scheme of the URL given to be returned.
+ * If the URI does not contain a scheme then this will
+ * return null. The scheme of the URI is the part that
+ * specifies the type of protocol that the URI is used
+ * for, an example <code>http://domain/path</code> is
+ * a URI that is intended for the http protocol. The
+ * scheme is the string <code>http</code>.
+ *
+ * @return the scheme tag for the address if available
+ */
+ String getScheme();
+
+ /**
+ * This is used to retrieve the domain of this URI. The
+ * domain part in the URI is an optional part, an example
+ * <code>http://domain/path?querypart</code>. This will
+ * return the value of the domain part. If there is no
+ * domain part then this will return null otherwise the
+ * domain value found in the uniform resource identifier.
+ *
+ * @return the domain part of the address if available
+ */
+ String getDomain();
+
+ /**
+ * This is used to retrieve the port of the uniform resource
+ * identifier. The port part in this is an optional part, an
+ * example <code>http://host:port/path?querypart</code>. This
+ * will return the value of the port. If there is no port then
+ * this will return <code>-1</code> because this represents
+ * an impossible uniform resource identifier port. The port
+ * is an optional part.
+ *
+ * @return this returns the port part if it is available
+ */
+ int getPort();
+
+ /**
+ * This is used to retrieve the path of this URI. The path part
+ * is the most fundamental part of the URI. This will return
+ * the value of the path. If there is no path part then this
+ * will return a Path implementation that represents the root
+ * path represented by <code>/</code>.
+ *
+ * @return the path part of the uniform resource identifier
+ */
+ Path getPath();
+
+ /**
+ * This is used to retrieve the query of this URI. The query part
+ * in the URI is an optional part. This will return the value
+ * of the query part. If there is no query part then this will
+ * return an empty <code>Query</code> object. The query is
+ * an optional member of a URI and comes after the path part, it
+ * is preceded by a question mark, <code>?</code> character.
+ * For example the following URI contains <code>query</code> for
+ * its query part, <code>http://host:port/path?query</code>.
+ * <p>
+ * This returns a <code>org.simpleframework.http.Query</code>
+ * object that can be used to interact directly with the query
+ * values. The <code>Query</code> object is a read-only interface
+ * to the query parameters, and so will not affect the URI.
+ *
+ * @return a <code>Query</code> object for the query part
+ */
+ Query getQuery();
+
+ /**
+ * This extracts the parameter values from the uniform resource
+ * identifier represented by this object. The parameters that a
+ * uniform resource identifier contains are embedded in the path
+ * part of the URI. If the path contains no parameters then this
+ * will return an empty <code>Map</code> instance.
+ * <p>
+ * This will produce unique name and value parameters. Thus if the
+ * URI contains several path segments with similar parameter names
+ * this will return the deepest parameter. For example if the URI
+ * represented was <code>http://domain/path1;x=y/path2;x=z</code>
+ * the value for the parameter named <code>x</code> would be
+ * <code>z</code>.
+ *
+ * @return this will return the parameter names found in the URI
+ */
+ KeyMap<String> getParameters();
+
+ /**
+ * This is used to convert this URI object into a <code>String</code>
+ * object. This will only convert the parts of the URI that exist, so
+ * the URI may not contain the domain or the query part and it will
+ * not contain the path parameters. If the URI contains all these
+ * parts then it will return something like
+ * <pre>
+ * scheme://host:port/path/path?querypart
+ * </pre>
+ * <p>
+ * It can return <code>/path/path?querypart</code> style relative
+ * URI's. If any of the parts are set to null then that part will be
+ * missing, for example if only the path is available then this will
+ * omit the domain, port and scheme. Showing a relative address.
+ * <pre>
+ * scheme://host:port/?querypart
+ * </pre>
+ *
+ * @return the URI address with the optional parts if available
+ */
+ String toString();
+}
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/ContentDisposition.java b/simple/simple-http/src/main/java/org/simpleframework/http/ContentDisposition.java
new file mode 100644
index 0000000..579cb91
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/ContentDisposition.java
@@ -0,0 +1,59 @@
+/*
+ * ContentDisposition.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;
+
+/**
+ * The <code>ContentDisposition</code> object represents the HTTP
+ * Content-Disposition header of a request. A content disposition
+ * contains the name of the part and whether that part contains
+ * the contents of a file. If the part represents a parameter then
+ * the <code>getName</code> can be used to determine the name, if
+ * it represents a file then <code>getFileName</code> is preferred.
+ *
+ * @author Niall Gallagher
+ */
+public interface ContentDisposition {
+
+ /**
+ * This method is used to acquire the name of the part. Typically
+ * this is used when the part represents a text parameter rather
+ * than a file. However, this can also be used with a file part.
+ *
+ * @return this returns the name of the associated part
+ */
+ String getName();
+
+ /**
+ * This method is used to acquire the file name of the part. This
+ * is used when the part represents a text parameter rather than
+ * a file. However, this can also be used with a file part.
+ *
+ * @return this returns the file name of the associated part
+ */
+ String getFileName();
+
+ /**
+ * This method is used to determine the type of a part. Typically
+ * a part is either a text parameter or a file. If this is true
+ * then the content represented by the associated part is a file.
+ *
+ * @return this returns true if the associated part is a file
+ */
+ boolean isFile();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/ContentType.java b/simple/simple-http/src/main/java/org/simpleframework/http/ContentType.java
new file mode 100644
index 0000000..c62687d
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/ContentType.java
@@ -0,0 +1,142 @@
+/*
+ * ContentType.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;
+
+/**
+ * This provides access to the MIME type parts, that is the primary
+ * type, the secondary type and an optional character set parameter.
+ * The <code>charset</code> parameter is one of many parameters that
+ * can be associated with a MIME type. This however this exposes this
+ * parameter with a typed method.
+ * <p>
+ * The <code>getCharset</code> will return the character encoding the
+ * content type is encoded within. This allows the user of the content
+ * to decode it correctly. Other parameters can be acquired from this
+ * by simply providing the name of the parameter.
+ *
+ * @author Niall Gallagher
+ */
+public interface ContentType {
+
+ /**
+ * This method is used to get the primary and secondary parts
+ * joined together with a "/". This is typically how a content
+ * type is examined. Here convenience is most important, we can
+ * easily compare content types without any parameters.
+ *
+ * @return this returns the primary and secondary types
+ */
+ String getType();
+
+ /**
+ * This sets the primary type to whatever value is in the string
+ * provided is. If the string is null then this will contain a
+ * null string for the primary type of the parameter, which is
+ * likely invalid in most cases.
+ *
+ * @param type the type to set for the primary type of this
+ */
+ void setPrimary(String type);
+
+ /**
+ * This is used to retrieve the primary type of this MIME type. The
+ * primary type part within the MIME type defines the generic type.
+ * For example <code>text/plain; charset=UTF-8</code>. This will
+ * return the text value. If there is no primary type then this
+ * will return <code>null</code> otherwise the string value.
+ *
+ * @return the primary type part of this MIME type
+ */
+ String getPrimary();
+
+ /**
+ * This sets the secondary type to whatever value is in the string
+ * provided is. If the string is null then this will contain a
+ * null string for the secondary type of the parameter, which is
+ * likely invalid in most cases.
+ *
+ * @param type the type to set for the primary type of this
+ */
+ void setSecondary(String type);
+
+ /**
+ * This is used to retrieve the secondary type of this MIME type.
+ * The secondary type part within the MIME type defines the generic
+ * type. For example <code>text/html; charset=UTF-8</code>. This
+ * will return the HTML value. If there is no secondary type then
+ * this will return <code>null</code> otherwise the string value.
+ *
+ * @return the primary type part of this MIME type
+ */
+ String getSecondary();
+
+ /**
+ * This will set the <code>charset</code> to whatever value the
+ * string contains. If the string is null then this will not set
+ * the parameter to any value and the <code>toString</code> method
+ * will not contain any details of the parameter.
+ *
+ * @param charset parameter value to add to the MIME type
+ */
+ void setCharset(String charset);
+
+ /**
+ * This is used to retrieve the <code>charset</code> of this MIME
+ * type. This is a special parameter associated with the type, if
+ * the parameter is not contained within the type then this will
+ * return null, which typically means the default of ISO-8859-1.
+ *
+ * @return the value that this parameter contains
+ */
+ String getCharset();
+
+ /**
+ * This is used to retrieve an arbitrary parameter from the MIME
+ * type header. This ensures that values for <code>boundary</code>
+ * or other such parameters are not lost when the header is parsed.
+ * This will return the value, unquoted if required, as a string.
+ *
+ * @param name this is the name of the parameter to be retrieved
+ *
+ * @return this is the value for the parameter, or null if empty
+ */
+ String getParameter(String name);
+
+ /**
+ * This will add a named parameter to the content type header. If
+ * a parameter of the specified name has already been added to the
+ * header then that value will be replaced by the new value given.
+ * Parameters such as the <code>boundary</code> as well as other
+ * common parameters can be set with this method.
+ *
+ * @param name this is the name of the parameter to be added
+ * @param value this is the value to associate with the name
+ */
+ void setParameter(String name, String value);
+
+ /**
+ * This will return the value of the MIME type as a string. This
+ * will concatenate the primary and secondary type values and
+ * add the <code>charset</code> parameter to the type which will
+ * recreate the content type.
+ *
+ * @return this returns the string representation of the type
+ */
+ String toString();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Cookie.java b/simple/simple-http/src/main/java/org/simpleframework/http/Cookie.java
new file mode 100644
index 0000000..a9ab422
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Cookie.java
@@ -0,0 +1,527 @@
+/*
+ * Cookie.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;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
+
+/**
+ * This class is used to represent a generic cookie. This exposes
+ * the fields that a cookie can have. By default the version of the
+ * <code>Cookie</code> is set to 1. The version can be configured
+ * using the <code>setVersion</code> method. The domain, path,
+ * security, and expiry of the cookie can also be set using their
+ * respective set methods.
+ * <p>
+ * The <code>toString</code> method allows the <code>Cookie</code>
+ * to be converted back into text form. This text form converts the
+ * cookie according to the Set-Cookie header form. This is done so
+ * that a created <code>Cookie</code> instance can be converted
+ * to a string which can be used as a a HTTP header.
+ *
+ * @author Niall Gallagher
+ */
+public class Cookie {
+
+ /**
+ * This is used to set the expiry date for the cookie.
+ */
+ private CookieDate date;
+
+ /**
+ * The name attribute of this cookie instance.
+ */
+ private String name;
+
+ /**
+ * The value attribute of this cookie instance.
+ */
+ private String value;
+
+ /**
+ * Represents the value of the path for this cookie.
+ */
+ private String path;
+
+ /**
+ * Represents the value of the domain attribute.
+ */
+ private String domain;
+
+ /**
+ * Determines whether the cookie should be secure.
+ */
+ private boolean secure;
+
+ /**
+ * Determines whether the cookie should be protected.
+ */
+ private boolean protect;
+
+ /**
+ * This is used to determine the the cookie is new.
+ */
+ private boolean created;
+
+ /**
+ * Represents the value of the version attribute.
+ */
+ private int version;
+
+ /**
+ * Represents the duration in seconds of the cookie.
+ */
+ private int expiry;
+
+ /**
+ * Constructor of the <code>Cookie</code> that uses a default
+ * version of 1, which is used by RFC 2109. This contains none
+ * of the optional attributes, such as domain and path. These
+ * optional attributes can be set using the set methods.
+ * <p>
+ * The name must conform to RFC 2109, which means that it can
+ * contain only ASCII alphanumeric characters and cannot have
+ * commas, white space, or semicolon characters.
+ *
+ * @param name this is the name of this cookie instance
+ * @param value this is the value of this cookie instance
+ */
+ public Cookie(String name, String value) {
+ this(name, value, "/");
+ }
+
+ /**
+ * Constructor of the <code>Cookie</code> that uses a default
+ * version of 1, which is used by RFC 2109. This contains none
+ * of the optional attributes, such as domain and path. These
+ * optional attributes can be set using the set methods.
+ * <p>
+ * The name must conform to RFC 2109, which means that it can
+ * contain only ASCII alphanumeric characters and cannot have
+ * commas, white space, or semicolon characters.
+ *
+ * @param name this is the name of this cookie instance
+ * @param value this is the value of this cookie instance
+ * @param created this determines if the cookie is new
+ */
+ public Cookie(String name, String value, boolean created) {
+ this(name, value, "/", created);
+ }
+
+ /**
+ * Constructor of the <code>Cookie</code> that uses a default
+ * version of 1, which is used by RFC 2109. This allows the
+ * path attribute to be specified for on construction. Other
+ * attributes can be set using the set methods provided.
+ * <p>
+ * The name must conform to RFC 2109, which means that it can
+ * contain only ASCII alphanumeric characters and cannot have
+ * commas, white space, or semicolon characters.
+ *
+ * @param name this is the name of this cookie instance
+ * @param value this is the value of this cookie instance
+ * @param path the path attribute of this cookie instance
+ */
+ public Cookie(String name, String value, String path) {
+ this(name, value, path, false);
+ }
+
+ /**
+ * Constructor of the <code>Cookie</code> that uses a default
+ * version of 1, which is used by RFC 2109. This allows the
+ * path attribute to be specified for on construction. Other
+ * attributes can be set using the set methods provided.
+ * <p>
+ * The name must conform to RFC 2109, which means that it can
+ * contain only ASCII alphanumeric characters and cannot have
+ * commas, white space, or semicolon characters.
+ *
+ * @param name this is the name of this cookie instance
+ * @param value this is the value of this cookie instance
+ * @param path the path attribute of this cookie instance
+ * @param created this determines if the cookie is new
+ */
+ public Cookie(String name, String value, String path, boolean created) {
+ this.date = new CookieDate();
+ this.created = created;
+ this.value = value;
+ this.name = name;
+ this.path = path;
+ this.version = 1;
+ this.expiry = -1;
+ }
+
+ /**
+ * This is used to determine if the cookie is new. A cookie is
+ * considered new if it has just been created on the server. A
+ * cookie is considered not new if it has been received by the
+ * client in a request. This allows the server to determine if
+ * the cookie needs to be delivered to the client.
+ *
+ * @return this returns true if the cookie was just created
+ */
+ public boolean isNew() {
+ return created;
+ }
+
+ /**
+ * This returns the version for this cookie. The version is
+ * not optional and so will always return the version this
+ * cookie uses. If no version number is specified this will
+ * return a version of 1, to comply with RFC 2109.
+ *
+ * @return the version value from this cookie instance
+ */
+ public int getVersion() {
+ return version;
+ }
+
+ /**
+ * This enables the version of the <code>Cookie</code> to be
+ * set. By default the version of the <code>Cookie</code> is
+ * set to 1. It is not advisable to set the version higher
+ * than 1, unless it is known that the client will accept it.
+ * <p>
+ * Some old browsers can only handle cookie version 0. This
+ * can be used to comply with the original Netscape cookie
+ * specification. Version 1 complies with RFC 2109.
+ *
+ * @param version this is the version number for the cookie
+ */
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ /**
+ * This returns the name for this cookie. The name and value
+ * attributes of a cookie define what the <code>Cookie</code>
+ * is for, these values will always be present. These are
+ * mandatory for both the Cookie and Set-Cookie headers.
+ * <p>
+ * Because the cookie may be stored by name, the cookie name
+ * cannot be modified after the creation of the cookie object.
+ *
+ * @return the name from this cookie instance object
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * This returns the value for this cookie. The name and value
+ * attributes of a cookie define what the <code>Cookie</code>
+ * is for, these values will always be present. These are
+ * mandatory for both the Cookie and Set-Cookie headers.
+ *
+ * @return the value from this cookie instance object
+ */
+ public String getValue() {
+ return value;
+ }
+
+ /**
+ * This enables the value of the cookie to be changed. This
+ * can be set to any value the server wishes to send. Cookie
+ * values can contain space characters as they are transmitted
+ * in quotes. For example a value of <code>some value</code>
+ * is perfectly legal. However for maximum compatibility
+ * across the different plaforms such as PHP, JavaScript and
+ * others, quotations should be avoided. If quotations are
+ * required they must be added to the string. For example a
+ * quoted value could be created as <code>"some value"</code>.
+ *
+ * @param value this is the new value of this cookie object
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * This determines whether the cookie is secure. The cookie
+ * is secure if it has the "secure" token set, as defined
+ * by RFC 2109. If this token is set then the cookie is only
+ * sent over secure channels such as SSL and TLS and ensures
+ * that a third party cannot intercept and spoof the cookie.
+ *
+ * @return this returns true if the "secure" token is set
+ */
+ public boolean isSecure() {
+ return secure;
+ }
+
+ /**
+ * This is used to determine if the client browser should send
+ * this cookie over a secure protocol. If this is true then
+ * the client browser should only send the cookie over secure
+ * channels such as SSL and TLS. This ensures that the value
+ * of the cookie cannot be intercepted by a third party.
+ *
+ * @param secure if true then the cookie should be secure
+ */
+ public void setSecure(boolean secure) {
+ this.secure = secure;
+ }
+
+ /**
+ * This is used to determine if the cookie is protected against
+ * cross site scripting. It sets the <code>HttpOnly</code> value
+ * for the cookie. Setting this value ensures that the cookie
+ * is not available to some scripting attacks.
+ *
+ * @return this returns true if the cookie is protected
+ */
+ public boolean isProtected() {
+ return protect;
+ }
+
+ /**
+ * This is used to protect the cookie from cross site scripting
+ * vulnerabilities. If this is set to true the cookie will be
+ * protected by setting the <code>HttpOnly</code> value for the
+ * cookie. See RFC 6265 for more details on this value.
+ *
+ * @param protect this determines if the cookie is protected
+ */
+ public void setProtected(boolean protect) {
+ this.protect = protect;
+ }
+
+ /**
+ * This returns the number of seconds a cookie lives for. This
+ * determines how long the cookie will live on the client side.
+ * If the expiry is less than zero the cookie lifetime is the
+ * duration of the client browser session, if it is zero then
+ * the cookie will be deleted from the client browser.
+ *
+ * @return returns the duration in seconds the cookie lives
+ */
+ public int getExpiry() {
+ return expiry;
+ }
+
+ /**
+ * This allows a lifetime to be specified for the cookie. This
+ * will make use of the "max-age" token specified by RFC 2109
+ * the specifies the number of seconds a browser should keep
+ * a cookie for. This is useful if the cookie is to be kept
+ * beyond the lifetime of the client session. If the value of
+ * this is zero then this will remove the client cookie, if
+ * it is less than zero then the "max-age" field is ignored.
+ *
+ * @param expiry the duration in seconds the cookie lives
+ */
+ public void setExpiry(int expiry){
+ this.expiry = expiry;
+ }
+
+ /**
+ * This returns the path for this cookie. The path is in both
+ * the Cookie and Set-Cookie headers and so may return null
+ * if there is no domain value. If the <code>toString</code>
+ * or <code>toClientString</code> is invoked the path will
+ * not be present if the path attribute is null.
+ *
+ * @return this returns the path value from this cookie
+ */
+ public String getPath() {
+ return path;
+ }
+
+ /**
+ * This is used to set the cookie path for this cookie. This
+ * is set so that the cookie can specify the directories that
+ * the cookie is sent with. For example if the path attribute
+ * is set to <code>/pub/bin</code>, then requests for the
+ * resource <code>http://hostname:port/pub/bin/README</code>
+ * will be issued with this cookie. The cookie is issued for
+ * all resources in the path and all subdirectories.
+ *
+ * @param path this is the path value for this cookie object
+ */
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ /**
+ * This returns the domain for this cookie. The domain is in
+ * both the Cookie and Set-Cookie headers and so may return
+ * null if there is no domain value. If either the
+ * <code>toString</code> or <code>toClientString</code> is
+ * invoked the domain will not be present if this is null.
+ *
+ * @return this returns the domain value from this cookie
+ */
+ public String getDomain() {
+ return domain;
+ }
+
+ /**
+ * This enables the domain for this <code>Cookie</code> to be
+ * set. The form of the domain is specified by RFC 2109. The
+ * value can begin with a dot, like <code>.host.com</code>.
+ * This means that the cookie is visible within a specific
+ * DNS zone like <code>www.host.com</code>. By default this
+ * value is null which means it is sent back to its origin.
+ *
+ * @param domain this is the domain value for this cookie
+ */
+ public void setDomain(String domain) {
+ this.domain = domain;
+ }
+
+ /**
+ * This will give the correct string value of this cookie. This
+ * will generate the cookie text with only the values that were
+ * given with this cookie. If there are no optional attributes
+ * like $Path or $Domain these are left blank. This returns the
+ * encoding as it would be for the HTTP Cookie header.
+ *
+ * @return this returns the Cookie header encoding of this
+ */
+ public String toClientString(){
+ return "$Version="+version+"; "+name+"="+
+ value+ (path==null?"":"; $Path="+
+ path)+ (domain==null? "":"; $Domain="+
+ domain);
+ }
+
+ /**
+ * The <code>toString</code> method converts the cookie to the
+ * Set-Cookie value. This can be used to send the HTTP header
+ * to a client browser. This uses a format that has been tested
+ * with various browsers. This is required as some browsers
+ * do not perform flexible parsing of the Set-Cookie value.
+ * <p>
+ * Netscape and IE-5.0 can't or wont handle <code>Path</code>
+ * it must be <code>path</code> also Netscape can not handle
+ * the path in quotations such as <code>"/path"</code> it must
+ * be <code>/path</code>. This value is never in quotations.
+ * <p>
+ * For maximum compatibility cookie values are not transmitted
+ * in quotations. This is done to ensure that platforms like
+ * PHP, JavaScript and various others that don't comply with
+ * RFC 2109 can transparently access the sent cookies.
+ * <p>
+ * When setting the expiry time for the cookie it is important
+ * to set the <code>max-age</code> and <code>expires</code>
+ * attributes so that IE-5.0 and up can understand them. Old
+ * versions of IE do not understand <code>max-age</code>.
+ *
+ * @return this returns a Set-Cookie encoding of the cookie
+ */
+ public String toString(){
+ return name+"="+value+"; version="+
+ version +(path ==null ?"":"; path="+path)+
+ (domain ==null ?"": "; domain="+domain)+
+ (expiry< 0?"":"; expires="+date.format(expiry))+
+ (expiry < 0 ? "" : "; max-age="+expiry)+
+ (secure ? "; secure" : "") +
+ (protect ? "; httponly" : "");
+
+ }
+
+ /**
+ * The <code>CookieDate</code> complies with the date format
+ * used by older browsers such as Internet Explorer and
+ * Netscape Navigator. The format of the date is not the same
+ * as other HTTP date headers. It takes the form.
+ * <pre>
+ *
+ * DAY, DD-MMM-YYYY HH:MM:SS GMT
+ *
+ * </pre>
+ * Support for this format is required as many browsers do
+ * not support <code>max-age</code> and so cookies will not
+ * expire for these browsers.
+ */
+ private static class CookieDate {
+
+ /**
+ * This is the format that is required for the date.
+ */
+ private static final String FORMAT = "EEE, dd-MMM-yyyy HH:mm:ss z";
+
+ /**
+ * The cookie date must be returned in the GMT zone.
+ */
+ private static final String ZONE = "GMT";
+
+ /**
+ * This is the date formatter used to build the string.
+ */
+ private final DateFormat format;
+
+ /**
+ * This is the GMT time zone which must be used.
+ */
+ private final TimeZone zone;
+
+ /**
+ * Constructor for the <code>CookieDate</code> formatter.
+ * This creates the time zone and date formatting tools
+ * that are need to convert the expiry in seconds to the
+ * correct text format for older browsers to understand.
+ */
+ public CookieDate() {
+ this.format = new SimpleDateFormat(FORMAT);
+ this.zone = new SimpleTimeZone(0, ZONE);
+ }
+
+ /**
+ * This takes the number of seconds the cookie will live
+ * for. In order for this to be respected by older browsers
+ * such as IE-5.0 to IE-9.0 this must return a string in
+ * the original cookie specification by Netscape.
+ *
+ * @param seconds the number of seconds from now
+ *
+ * @return a date formatted for used with old browsers
+ */
+ public String format(int seconds) {
+ Calendar calendar = Calendar.getInstance(zone);
+ Date date = convert(seconds);
+
+ calendar.setTime(date);
+ format.setCalendar(calendar);
+
+ return format.format(date);
+ }
+
+ /**
+ * This method is used to convert the provided time to
+ * a date that can be formatted. The time returned is the
+ * current time plus the number of seconds provided.
+ *
+ * @param seconds the number of seconds from now
+ *
+ * @return a date representing some time in the future
+ */
+ private Date convert(int seconds) {
+ long now = System.currentTimeMillis();
+ long duration = seconds * 1000L;
+ long time = now + duration;
+
+ return new Date(time);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Method.java b/simple/simple-http/src/main/java/org/simpleframework/http/Method.java
new file mode 100644
index 0000000..5bb5027
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Method.java
@@ -0,0 +1,70 @@
+/*
+ * Method.java May 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;
+
+/**
+ * The <code>Method</code> interface contains the common HTTP methods
+ * that are sent with a request. This only contains those methods
+ * that have been defined within the RFC 2616 specification. These
+ * are defined here for convenience and informational purposes.
+ *
+ * @author Niall Gallagher
+ */
+public interface Method {
+
+ /**
+ * For use with a proxy that can dynamically switch to being a tunnel.
+ */
+ String CONNECT = "CONNECT";
+
+ /**
+ * Requests that the origin server delete the resource identified.
+ */
+ String DELETE = "DELETE";
+
+ /**
+ * Retrieve whatever information is identified by the request.
+ */
+ String GET = "GET";
+
+ /**
+ * Retrieve only the headers for the resource that is requested.
+ */
+ String HEAD = "HEAD";
+
+ /**
+ * Represents a request for the communication options available.
+ */
+ String OPTIONS = "OPTIONS";
+
+ /**
+ * Request that the origin server accept the entity in the request.
+ */
+ String POST = "POST";
+
+ /**
+ * Requests that the entity be stored as the resource specified
+ */
+ String PUT = "PUT";
+
+ /**
+ * Invoke a remote application layer loop back of the request.
+ */
+ String TRACE = "TRACE";
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Part.java b/simple/simple-http/src/main/java/org/simpleframework/http/Part.java
new file mode 100644
index 0000000..75660ea
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Part.java
@@ -0,0 +1,107 @@
+/*
+ * Part.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The <code>Part</code> object is used to represent a part within
+ * a request message. Typically a part represents either a text
+ * parameter or a file, with associated headers. The contents of
+ * the part can be acquire as an <code>InputStream</code> or as a
+ * string encoded in the default HTTP encoding ISO-8859-1 or in
+ * the encoding specified with the Content-Type header.
+ *
+ * @author Niall Gallagher
+ */
+public interface Part {
+
+ /**
+ * This method is used to determine the type of a part. Typically
+ * a part is either a text parameter or a file. If this is true
+ * then the content represented by the associated part is a file.
+ *
+ * @return this returns true if the associated part is a file
+ */
+ boolean isFile();
+
+ /**
+ * This method is used to acquire the name of the part. Typically
+ * this is used when the part represents a text parameter rather
+ * than a file. However, this can also be used with a file part.
+ *
+ * @return this returns the name of the associated part
+ */
+ String getName();
+
+ /**
+ * This method is used to acquire the file name of the part. This
+ * is used when the part represents a text parameter rather than
+ * a file. However, this can also be used with a file part.
+ *
+ * @return this returns the file name of the associated part
+ */
+ String getFileName();
+
+ /**
+ * This is used to acquire the header value for the specified
+ * header name. Providing the header values through this method
+ * ensures any special processing for a know content type can be
+ * handled by an application.
+ *
+ * @param name the name of the header to get the value for
+ *
+ * @return value of the header mapped to the specified name
+ */
+ String getHeader(String name);
+
+ /**
+ * This is used to acquire the content of the part as a string.
+ * The encoding of the string is taken from the content type.
+ * If no content type is sent the content is decoded in the
+ * standard default of ISO-8859-1.
+ *
+ * @return this returns a string representing the content
+ *
+ * @throws IOException thrown if the content can not be created
+ */
+ String getContent() throws IOException;
+
+ /**
+ * This is used to acquire an <code>InputStream</code> for the
+ * part. Acquiring the stream allows the content of the part to
+ * be consumed by reading the stream. Each invocation of this
+ * method will produce a new stream starting from the first byte.
+ *
+ * @return this returns the stream for this part object
+ *
+ * @throws IOException thrown if the stream can not be created
+ */
+ InputStream getInputStream() throws IOException;
+
+ /**
+ * This is used to acquire the content type for this part. This
+ * is typically the type of content for a file part, as provided
+ * by a MIME type from the HTTP "Content-Type" header.
+ *
+ * @return this returns the content type for the part object
+ */
+ ContentType getContentType();
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Path.java b/simple/simple-http/src/main/java/org/simpleframework/http/Path.java
new file mode 100644
index 0000000..fb07ef0
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Path.java
@@ -0,0 +1,166 @@
+/*
+ * Path.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;
+
+/**
+ * The <code>Path</code> represents the path part of a URI. This provides
+ * the various components of the URI path to the user. The normalization
+ * of the path is the conversion of the path given into it's actual path by
+ * removing the references to the parent directories and to the current dir.
+ * <p>
+ * If the path that this represents is <code>/usr/bin/../etc/./README</code>
+ * then the actual path, normalized, is <code>/usr/etc/README</code>. Once
+ * the path has been normalized it is possible to acquire the segments as
+ * an array of strings, which allows simple manipulation of the path.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.parse.PathParser
+ */
+public interface Path {
+
+ /**
+ * This will return the extension that the file name contains.
+ * For example a file name <code>file.en_US.extension</code>
+ * will produce an extension of <code>extension</code>. This
+ * will return null if the path contains no file extension.
+ *
+ * @return this will return the extension this path contains
+ */
+ String getExtension();
+
+ /**
+ * This will return the full name of the file without the path.
+ * As regargs the definition of the path in RFC 2396 the name
+ * would be considered the last path segment. So if the path
+ * was <code>/usr/README</code> the name is <code>README</code>.
+ * Also for directorys the name of the directory in the last
+ * path segment is returned. This returns the name without any
+ * of the path parameters. As RFC 2396 defines the path to have
+ * path parameters after the path segments.
+ *
+ * @return this will return the name of the file in the path
+ */
+ String getName();
+
+ /**
+ * This will return the normalized path. The normalized path is
+ * the path without any references to its parent or itself. So
+ * if the path to be parsed is <code>/usr/../etc/./</code> the
+ * path is <code>/etc/</code>. If the path that this represents
+ * is a path with an immediate back reference then this will
+ * return null. This is the path with all its information even
+ * the parameter information if it was defined in the path.
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ String getPath();
+
+ /**
+ * This will return the normalized path from the specified path
+ * segment. This allows various path parts to be acquired in an
+ * efficient means what does not require copy operations of the
+ * use of <code>substring</code> invocations. Of particular
+ * interest is the extraction of context based paths. This is
+ * the path with all its information even the parameter
+ * information if it was defined in the path.
+ *
+ * @param from this is the segment offset to get the path for
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ String getPath(int from);
+
+ /**
+ * This will return the normalized path from the specified path
+ * segment. This allows various path parts to be acquired in an
+ * efficient means what does not require copy operations of the
+ * use of <code>substring</code> invocations. Of particular
+ * interest is the extraction of context based paths. This is
+ * the path with all its information even the parameter
+ * information if it was defined in the path.
+ *
+ * @param from this is the segment offset to get the path for
+ * @param count this is the number of path segments to include
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ String getPath(int from, int count);
+
+ /**
+ * This method is used to break the path into individual parts
+ * called segments, see RFC 2396. This can be used as an easy
+ * way to compare paths and to examine the directory tree that
+ * the path points to. For example, if an path was broken from
+ * the string <code>/usr/bin/../etc</code> then the segments
+ * returned would be <code>usr</code> and <code>etc</code> as
+ * the path is normalized before the segments are extracted.
+ *
+ * @return return all the path segments within the directory
+ */
+ String[] getSegments();
+
+ /**
+ * This will return the highest directory that exists within
+ * the path. This is used to that files within the same path
+ * can be acquired. An example of that this would do given
+ * the path <code>/pub/./bin/README</code> would be to return
+ * the highest directory path <code>/pub/bin/</code>. The "/"
+ * character will allways be the last character in the path.
+ *
+ * @return this method will return the highest directory
+ */
+ String getDirectory();
+
+ /**
+ * This will return the path as it is relative to the issued
+ * path. This in effect will chop the start of this path if
+ * it's start matches the highest directory of the given path
+ * as of <code>getDirectory</code>. This is useful if paths
+ * that are relative to a specific location are required. To
+ * illustrate what this method will do the following example
+ * is provided. If this object represented the path string
+ * <code>/usr/share/rfc/rfc2396.txt</code> and the issued
+ * path was <code>/usr/share/text.txt</code> then this will
+ * return the path string <code>/rfc/rfc2396.txt</code>.
+ *
+ * @param path the path prefix to acquire a relative path
+ *
+ * @return returns a path relative to the one it is given
+ * otherwize this method will return null
+ */
+ String getRelative(String path);
+
+ /**
+ * This will return the normalized path. The normalized path is
+ * the path without any references to its parent or itself. So
+ * if the path to be parsed is <code>/usr/../etc/./</code> the
+ * path is <code>/etc/</code>. If the path that this represents
+ * is a path with an immediate back reference then this will
+ * return null. This is the path with all its information even
+ * the parameter information if it was defined in the path.
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ String toString();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Principal.java b/simple/simple-http/src/main/java/org/simpleframework/http/Principal.java
new file mode 100644
index 0000000..361e4c1
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Principal.java
@@ -0,0 +1,48 @@
+/*
+ * Principal.java November 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;
+
+/**
+ * The <code>Principal</code> interface is used to describe a
+ * user that has a name and password. This should not be
+ * confused with the <code>java.security.Principal</code>
+ * interface which does not provide <code>getPassword</code>.
+ *
+ * @author Niall Gallagher
+ */
+public interface Principal {
+
+ /**
+ * The <code>getPassword</code> method is used to retrieve
+ * the password of the principal. This is the password
+ * tag in the RFC 2616 Authorization credentials expression.
+ *
+ * @return this returns the password for this principal
+ */
+ String getPassword();
+
+ /**
+ * The <code>getName</code> method is used to retreive
+ * the name of the principal. This is the name tag in
+ * the RFC 2616 Authorization credentials expression.
+ *
+ * @return this returns the name of this principal
+ */
+ String getName();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Protocol.java b/simple/simple-http/src/main/java/org/simpleframework/http/Protocol.java
new file mode 100644
index 0000000..295b6c6
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Protocol.java
@@ -0,0 +1,370 @@
+/*
+ * Protocol.java May 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;
+
+/**
+ * This represents the HTTP header names defined in RFC 2616. It can be
+ * used to set and get headers safely from the <code>Request</code> and
+ * <code>Response</code> objects. This is used internally by the HTTP
+ * server to parse the incoming requests and also to submit response
+ * values for each conversation.
+ * <p>
+ * In addition to the header names this also contains some common
+ * HTTP header value tokens. These are provided for convenience and
+ * can be used to ensure that response values comply with RFC 2616.
+ *
+ * @author Niall Gallagher
+ */
+public interface Protocol {
+
+ /**
+ * Specifies media types which are acceptable for the response.
+ */
+ String ACCEPT = "Accept";
+
+ /**
+ * Indicates what character sets are acceptable for the response.
+ */
+ String ACCEPT_CHARSET = "Accept-Charset";
+
+ /**
+ * Restricts the content codings that are acceptable in the response.
+ */
+ String ACCEPT_ENCODING = "Accept-Encoding";
+
+ /**
+ * Restricts the set of languages that are preferred as a response.
+ */
+ String ACCEPT_LANGUAGE = "Accept-Language";
+
+ /**
+ * Indicates a servers acceptance of range requests for a resource.
+ */
+ String ACCEPT_RANGES = "Accept-Ranges";
+
+ /**
+ * Estimates the amount of time since the response was generated.
+ */
+ String AGE = "Age";
+
+ /**
+ * Lists the set of methods supported by the resource identified.
+ */
+ String ALLOW = "Allow";
+
+ /**
+ * Sent by a client that wishes to authenticate itself with a server.
+ */
+ String AUTHORIZATION = "Authorization";
+
+ /**
+ * Specifies directives that must be obeyed by all caching mechanisms.
+ */
+ String CACHE_CONTROL = "Cache-Control";
+
+ /**
+ * Specifies options that are desired for that particular connection.
+ */
+ String CONNECTION = "Connection";
+
+ /**
+ * Specifies a tag indicating of its desired presentation semantics.
+ */
+ String CONTENT_DISPOSITION = "Content-Disposition";
+
+ /**
+ * Indicates additional content codings have been applied to the body.
+ */
+ String CONTENT_ENCODING = "Content-Encoding";
+
+ /**
+ * Describes the languages of the intended audience for the body.
+ */
+ String CONTENT_LANGUAGE = "Content-Language";
+
+ /**
+ * Indicates the size of the entity body in decimal number of octets.
+ */
+ String CONTENT_LENGTH = "Content-Length";
+
+ /**
+ * Used to supply the resource location for the entity enclosed.
+ */
+ String CONTENT_LOCATION = "Content-Location";
+
+ /**
+ * An MD5 digest of the body for the purpose of checking integrity.
+ */
+ String CONTENT_MD5 = "Content-MD5";
+
+ /**
+ * Specifies where in the full body a partial body should be applied.
+ */
+ String CONTENT_RANGE = "Content-Range";
+
+ /**
+ * Indicates the media type of the body sent to the recipient.
+ */
+ String CONTENT_TYPE = "Content-Type";
+
+ /**
+ * Represents a cookie that contains some information from the client.
+ */
+ String COOKIE = "Cookie";
+
+ /**
+ * Represents the date and time at which the message was originated.
+ */
+ String DATE = "Date";
+
+ /**
+ * Provides the value of the entity tag for the requested variant.
+ */
+ String ETAG = "ETag";
+
+ /**
+ * Indicate that particular server behaviors are required by the client.
+ */
+ String EXPECT = "Expect";
+
+ /**
+ * Gives the time after which the response is considered stale.
+ */
+ String EXPIRES = "Expires";
+
+ /**
+ * Address for the human user who controls the requesting user agent.
+ */
+ String FROM = "From";
+
+ /**
+ * Specifies the host and port number of the resource being requested.
+ */
+ String HOST = "Host";
+
+ /**
+ * Specifies the entity tag for a request to make it conditional.
+ */
+ String IF_MATCH = "If-Match";
+
+ /**
+ * If variant has not been modified since the time specified.
+ */
+ String IF_MODIFIED_SINCE = "If-Modified-Since";
+
+ /**
+ * Verify that none of those entities is current by including a list.
+ */
+ String IF_NONE_MATCH = "If-None-Match";
+
+ /**
+ * If the entity is unchanged send me the part that I am missing.
+ */
+ String IF_RANGE = "If-Range";
+
+ /**
+ * If the requested resource has not been modified since this time.
+ */
+ String IF_UNMODIFIED_SINCE = "If-Unmodified-Since";
+
+ /**
+ * Indicates the date and time at which the variant was last modified.
+ */
+ String LAST_MODIFIED = "Last-Modified";
+
+ /**
+ * Used to redirect the recipient to a location other than the URI.
+ */
+ String LOCATION = "Location";
+
+ /**
+ * Limit the number of proxies or gateways that can forward the request.
+ */
+ String MAX_FORWARDS = "Max-Forwards";
+
+ /**
+ * Include implementation specific directives that might apply.
+ */
+ String PRAGMA = "Pragma";
+
+ /**
+ * Challenge indicating the authentication applicable to the proxy.
+ */
+ String PROXY_AUTHENTICATE = "Proxy-Authenticate";
+
+ /**
+ * Allows client identification for a proxy requiring authentication.
+ */
+ String PROXY_AUTHORIZATION = "Proxy-Authorization";
+
+ /**
+ * Specifies a range of bytes within a resource to be sent by a server.
+ */
+ String RANGE = "Range";
+
+ /**
+ * Allows the client to specify the source address to the server.
+ */
+ String REFERER = "Referer";
+
+ /**
+ * Response to indicate how long the service will be unavailable.
+ */
+ String RETRY_AFTER = "Retry-After";
+
+ /**
+ * Represents the globally unique identifier sent by the client.
+ */
+ String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";
+
+ /**
+ * Represents the SHA-1 digest of the clients globally unique identifier.
+ */
+ String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept";
+
+ /**
+ * Specifies the protocol that should be used by the connected parties.
+ */
+ String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
+
+ /**
+ * Represents the version of the protocol that should be used.
+ */
+ String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
+
+ /**
+ * Contains information about the software used by the origin server.
+ */
+ String SERVER = "Server";
+
+ /**
+ * Represents some value from the server that the client should keep.
+ */
+ String SET_COOKIE = "Set-Cookie";
+
+ /**
+ * Indicates what extension transfer codings it is willing to accept.
+ */
+ String TE = "TE";
+
+ /**
+ * Indicates that these header fields is present in the trailer.
+ */
+ String TRAILER = "Trailer";
+
+ /**
+ * Indicates the transformation has been applied to the message body.
+ */
+ String TRANSFER_ENCODING = "Transfer-Encoding";
+
+ /**
+ * Specifies additional communication protocols the client supports.
+ */
+ String UPGRADE = "Upgrade";
+
+ /**
+ * Contains information about the user agent originating the request.
+ */
+ String USER_AGENT = "User-Agent";
+
+ /**
+ * Indicates the headers that can make a cached resource stale.
+ */
+ String VARY = "Vary";
+
+ /**
+ * Used by gateways and proxies to indicate the intermediate protocols.
+ */
+ String VIA = "Via";
+
+ /**
+ * Used to carry additional information about the status or body.
+ */
+ String WARNING = "Warning";
+
+ /**
+ * Uses to challenge a client for authentication for a resource.
+ */
+ String WWW_AUTHENTICATE = "WWW-Authenticate";
+
+ /**
+ * Represents a class of data representing an executable application.
+ */
+ String APPLICATION = "application";
+
+ /**
+ * Represents the token used to identify a multipart boundary.
+ */
+ String BOUNDARY = "boundary";
+
+ /**
+ * Represents the token used to identify the encoding of a message.
+ */
+ String CHARSET = "charset";
+
+ /**
+ * Represents the name of a self delimiting transfer encoding.
+ */
+ String CHUNKED = "chunked";
+
+ /**
+ * Specifies that the server will terminate the connection.
+ */
+ String CLOSE = "close";
+
+ /**
+ * Represents a message type for an image such as a PNG or JPEG.
+ */
+ String IMAGE = "image";
+
+ /**
+ * Specifies that the server wishes to keep the connection open.
+ */
+ String KEEP_ALIVE = "keep-alive";
+
+ /**
+ * Represents a message type that contains multiple parts.
+ */
+ String MULTIPART = "multipart";
+
+ /**
+ * Specifies that the message should not be cached by anything.
+ */
+ String NO_CACHE = "no-cache";
+
+ /**
+ * Represents the default content type if none is specified.
+ */
+ String OCTET_STREAM = "octet-stream";
+
+ /**
+ * Represents a message type containing human readable text.
+ */
+ String TEXT = "text";
+
+ /**
+ * Represents a message type that contains HTML form posted data.
+ */
+ String URL_ENCODED = "x-www-form-urlencoded";
+
+ /**
+ * This is the protocol token that is used when upgrading.
+ */
+ String WEBSOCKET = "websocket";
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Query.java b/simple/simple-http/src/main/java/org/simpleframework/http/Query.java
new file mode 100644
index 0000000..5ab8afa
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Query.java
@@ -0,0 +1,99 @@
+/*
+ * Query.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;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The <code>Query</code> object is used to represent HTTP query
+ * parameters. Parameters are acquired by name and can be either a
+ * string, float, int, or boolean value. This ensures that data can
+ * be conveniently extracted in the correct type. This stores the
+ * parameters in a map of key value pairs. Each parameter can be
+ * acquired using the name of the parameter, if the parameter is
+ * named twice then all values can be acquired.
+ *
+ * @author Niall Gallagher
+ */
+public interface Query extends Map<String, String> {
+
+ /**
+ * This method is used to acquire a <code>List</code> for all of
+ * the parameter values associated with the specified name. Using
+ * this method allows the query to expose many values taken from
+ * the query or HTTP form posting. Typically the first value in
+ * the list is the value from the <code>get(String)</code> method
+ * as this is the primary value from the ordered list of values.
+ *
+ * @param name this is the name used to search for the value
+ *
+ * @return this is the list of values associated with the key
+ */
+ List<String> getAll(Object name);
+
+ /**
+ * This extracts an integer parameter for the named value. If the
+ * named parameter does not exist this will return a zero value.
+ * If however the parameter exists but is not in the format of a
+ * decimal integer value then this will throw an exception.
+ *
+ * @param name the name of the parameter value to retrieve
+ *
+ * @return this returns the named parameter value as an integer
+ */
+ int getInteger(Object name);
+
+ /**
+ * This extracts a float parameter for the named value. If the
+ * named parameter does not exist this will return a zero value.
+ * If however the parameter exists but is not in the format of a
+ * floating point number then this will throw an exception.
+ *
+ * @param name the name of the parameter value to retrieve
+ *
+ * @return this returns the named parameter value as a float
+ */
+ float getFloat(Object name);
+
+ /**
+ * This extracts a boolean parameter for the named value. If the
+ * named parameter does not exist this will return false otherwise
+ * the value is evaluated. If it is either <code>true</code> or
+ * <code>false</code> then those boolean values are returned.
+ *
+ * @param name the name of the parameter value to retrieve
+ *
+ * @return this returns the named parameter value as an float
+ */
+ boolean getBoolean(Object name);
+
+ /**
+ * This will return all parameters represented using the HTTP
+ * URL query format. The <code>x-www-form-urlencoded</code>
+ * format is used to encode the attributes, see RFC 2616.
+ * <p>
+ * This will also encode any special characters that appear
+ * within the name and value pairs as an escaped sequence.
+ * If there are no parameters an empty string is returned.
+ *
+ * @return returns an empty string if the is no parameters
+ */
+ String toString();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Request.java b/simple/simple-http/src/main/java/org/simpleframework/http/Request.java
new file mode 100644
index 0000000..2c83c28
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Request.java
@@ -0,0 +1,210 @@
+/*
+ * Request.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.nio.channels.ReadableByteChannel;
+import java.util.List;
+import java.util.Map;
+
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+
+/**
+ * The <code>Request</code> is used to provide an interface to the
+ * HTTP entity body and message header. This provides methods that
+ * allow the entity body to be acquired as a stream, string, or if
+ * the message is a multipart encoded body, then the individual
+ * parts of the request body can be acquired.
+ * <p>
+ * This can also maintain data during the request lifecycle as well
+ * as the session lifecycle. A <code>Session</code> is made available
+ * for convenience. It provides a means for the services to associate
+ * data with a given client session, which can be retrieved when
+ * there are subsequent requests sent to the server.
+ * <p>
+ * It is important to note that the entity body can be read multiple
+ * times from the request. Calling <code>getInputStream</code> will
+ * start reading from the first byte in the body regardless of the
+ * number of times it is called. This allows POST parameters as well
+ * as multipart bodies to be read from the stream if desired.
+ *
+ * @author Niall Gallagher
+ */
+public interface Request extends RequestHeader {
+
+ /**
+ * 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
+ */
+ boolean isSecure();
+
+ /**
+ * This is a convenience method that is used to determine whether
+ * or not this message has the <code>Connection: close</code>
+ * header. If the close token is present then this stream is not
+ * a keep-alive connection. If this has no <code>Connection</code>
+ * 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
+ * is not keep-alive by default.
+ *
+ * @return returns true if this has a keep-alive stream
+ */
+ boolean isKeepAlive();
+
+ /**
+ * 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 as
+ * as some data may have been buffered before it was parsed.
+ *
+ * @return this represents the time the request arrived at
+ */
+ long getRequestTime();
+
+ /**
+ * 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
+ */
+ Channel getChannel();
+
+ /**
+ * 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
+ */
+ Certificate getClientCertificate();
+
+ /**
+ * 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
+ */
+ InetSocketAddress getClientAddress();
+
+ /**
+ * 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 of that have been set on the request
+ */
+ Map getAttributes();
+
+ /**
+ * 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
+ */
+ Object getAttribute(Object key);
+
+ /**
+ * 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
+ */
+ String getParameter(String 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
+ */
+ Part getPart(String 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
+ */
+ List<Part> getParts();
+
+ /**
+ * 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 this returns the message bytes as an encoded string
+ */
+ String getContent() throws IOException;
+
+ /**
+ * 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 this returns an input stream containing the message body
+ */
+ InputStream getInputStream() throws IOException;
+
+ /**
+ * 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
+ */
+ ReadableByteChannel getByteChannel() throws IOException;
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/RequestHeader.java b/simple/simple-http/src/main/java/org/simpleframework/http/RequestHeader.java
new file mode 100644
index 0000000..d1ca7d0
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/RequestHeader.java
@@ -0,0 +1,201 @@
+/*
+ * RequestHeader.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;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * This is a <code>Header</code> object that is used to represent a
+ * basic form for the HTTP request message. This is used to extract
+ * values such as the request line and header values from the request
+ * message. Access to header values is done case insensitively.
+ * <p>
+ * As well as providing the header values and request line values
+ * this will also provide convenience methods which enable the user
+ * to determine the length of the body this message header prefixes.
+ *
+ * @author Niall Gallagher
+ */
+public interface RequestHeader extends RequestLine {
+
+ /**
+ * 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
+ */
+ List<String> getNames();
+
+ /**
+ * 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
+ */
+ int getInteger(String 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
+ */
+ long getDate(String name);
+
+ /**
+ * This is used to acquire a cookie using 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
+ */
+ Cookie getCookie(String 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
+ */
+ List<Cookie> getCookies();
+
+ /**
+ * 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
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ String getValue(String 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
+ */
+ String getValue(String name, int index);
+
+ /**
+ * 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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param name the name of the headers that are to be retrieved
+ *
+ * @return ordered array of tokens extracted from the header(s)
+ */
+ List<String> getValues(String 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
+ */
+ List<Locale> getLocales();
+
+ /**
+ * 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
+ */
+ ContentType 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
+ */
+ long 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
+ */
+ CharSequence getHeader();
+
+ /**
+ * This method returns a string representing the header that was
+ * consumed for this request. For performance reasons it is better
+ * to acquire the character sequence representing the header as it
+ * does not require the allocation on new memory.
+ *
+ * @return this returns a string representation of this request
+ */
+ String toString();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/RequestLine.java b/simple/simple-http/src/main/java/org/simpleframework/http/RequestLine.java
new file mode 100644
index 0000000..b5b1abc
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/RequestLine.java
@@ -0,0 +1,98 @@
+/*
+ * RequestLine.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;
+
+/**
+ * The <code>RequestLine</code> is used to represent a HTTP request
+ * line. The methods provided for this can be used to provide easy
+ * access to the components of a HTTP request line. For the syntax
+ * of a HTTP request line see RFC 2616.
+ *
+ * @author Niall Gallagher
+ */
+public interface RequestLine {
+
+ /**
+ * 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
+ */
+ String getMethod();
+
+ /**
+ * 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
+ */
+ String 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
+ */
+ Address 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
+ */
+ Path 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
+ */
+ Query getQuery();
+
+ /**
+ * 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
+ */
+ int 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
+ */
+ int getMinor();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/RequestWrapper.java b/simple/simple-http/src/main/java/org/simpleframework/http/RequestWrapper.java
new file mode 100644
index 0000000..be81f5e
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/RequestWrapper.java
@@ -0,0 +1,520 @@
+/*
+ * RequestWrapper.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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.nio.channels.ReadableByteChannel;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+import org.simpleframework.transport.Certificate;
+import org.simpleframework.transport.Channel;
+
+/**
+ * The <code>RequestWrapper</code> object is used so that the original
+ * <code>Request</code> object can be wrapped in a filtering proxy
+ * object. This allows a <code>Container</code> that interacts with
+ * a modified request object. To add functionality to the request it
+ * can be wrapped in a subclass of this and the overridden methods
+ * can provide modified functionality to the standard request.
+ *
+ * @author Niall Gallagher
+ */
+public class RequestWrapper implements Request {
+
+ /**
+ * This is the request instance that is being wrapped.
+ */
+ protected Request request;
+
+ /**
+ * Constructor for <code>RequestWrapper</code> object. This allows
+ * the original <code>Request</code> object to be wrapped so that
+ * adjustments to the behaviour of a request object handed to the
+ * container can be provided by a subclass implementation.
+ *
+ * @param request the request object that is being wrapped
+ */
+ public RequestWrapper(Request request){
+ this.request = request;
+ }
+
+ /**
+ * 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 request.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 request.getMinor();
+ }
+
+ /**
+ * 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 request.getMethod();
+ }
+
+ /**
+ * This can be used to get the URI specified for this HTTP request.
+ * This corresponds to the either the full HTTP URI or the path
+ * part of the URI depending on how the client sends the request.
+ *
+ * @return the URI address that this HTTP request is targeting
+ */
+ public String getTarget() {
+ return request.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 request.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 request.getPath();
+ }
+
+ /**
+ * 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() {
+ return request.getQuery();
+ }
+
+ /**
+ * 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 request.getNames();
+ }
+
+ /**
+ * 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 request.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 request.getDate(name);
+ }
+
+ /**
+ * 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 request.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 request.getCookies();
+ }
+
+ /**
+ * 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 request.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 request.getValue(name, index);
+ }
+
+ /**
+ * 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 request.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 request.getLocales();
+ }
+
+ /**
+ * 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 request.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 request.getContentLength();
+ }
+
+ /**
+ * 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 request.isSecure();
+ }
+
+ /**
+ * This is a convenience method that is used to determine whether
+ * or not this message has the <code>Connection: close</code>
+ * header. If the close token is present then this stream is not
+ * a keep-alive connection. If this has no <code>Connection</code>
+ * 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
+ * is not keep-alive by default.
+ *
+ * @return returns true if this has a keep-alive stream
+ */
+ public boolean isKeepAlive() {
+ return request.isKeepAlive();
+ }
+
+ /**
+ * 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 as
+ * as some data may have been buffered before it was parsed.
+ *
+ * @return this represents the time the request arrived at
+ */
+ public long getRequestTime() {
+ return request.getRequestTime();
+ }
+
+ /**
+ * 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 request.getChannel();
+ }
+
+ /**
+ * 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() {
+ return request.getClientCertificate();
+ }
+
+ /**
+ * 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 set on this response
+ */
+ public Map getAttributes() {
+ return request.getAttributes();
+ }
+
+ /**
+ * 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 request.getAttribute(key);
+ }
+
+ /**
+ * 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() {
+ return request.getClientAddress();
+ }
+
+ /**
+ * 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 request.getHeader();
+ }
+
+ /**
+ * 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.
+ *
+ * @exception IOException signifies that there is an I/O problem
+ *
+ * @return the body content as an encoded string value
+ */
+ public String getContent() throws IOException {
+ return request.getContent();
+ }
+
+ /**
+ * 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.
+ *
+ * @exception Exception signifies that there is an I/O problem
+ *
+ * @return returns the input stream containing the message body
+ */
+ public InputStream getInputStream() throws IOException {
+ return request.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 {
+ return request.getByteChannel();
+ }
+
+ /**
+ * 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 request.getParameter(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 request.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 request.getParts();
+ }
+
+ /**
+ * This method returns a string representing the header that was
+ * consumed for this request. For performance reasons it is better
+ * to acquire the character sequence representing the header as it
+ * does not require the allocation on new memory.
+ *
+ * @return this returns a string representation of this request
+ */
+ public String toString() {
+ return request.toString();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Response.java b/simple/simple-http/src/main/java/org/simpleframework/http/Response.java
new file mode 100644
index 0000000..e9e54da
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Response.java
@@ -0,0 +1,262 @@
+/*
+ * Response.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;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.channels.WritableByteChannel;
+
+/**
+ * This is used to represent the HTTP response. This provides methods
+ * that can be used to set various characteristics of the response.
+ * An <code>OutputStream</code> can be acquired via this interface
+ * which can be used to write the response body. A buffer size can be
+ * specified when acquiring the output stream which allows data to
+ * be buffered until it over flows or is flushed explicitly. This
+ * buffering allows a partially written response body to be reset.
+ * <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 been sent. To ensure that
+ * the response is sent the <code>close</code> method of the response
+ * or the output stream should be used. This will notify the server
+ * to dispatch the next request in the pipeline for processing.
+ *
+ * @author Niall Gallagher
+ */
+public interface Response extends ResponseHeader {
+
+ /**
+ * This should be used when the size of the message body 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 output stream or request is closed.
+ *
+ * @param length this is the length of the HTTP message body
+ */
+ void setContentLength(long 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
+ */
+ void setContentType(String type);
+
+ /**
+ * 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 with the specified semantics
+ */
+ OutputStream getOutputStream() throws IOException;
+
+ /**
+ * 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.
+ *
+ * @return an output stream object with the specified semantics
+ */
+ OutputStream getOutputStream(int size) throws IOException;
+
+ /**
+ * 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.
+ * <p>
+ * Implementations of the <code>Response</code> must guarantee
+ * that this can be invoked repeatedly without effecting any issued
+ * <code>OutputStream</code> or <code>PrintStream</code> object.
+ *
+ * @return a print stream that provides convenience writing
+ */
+ PrintStream getPrintStream() throws IOException;
+
+ /**
+ * 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.
+ * <p>
+ * Implementations of the <code>Response</code> must guarantee
+ * that this can be invoked repeatedly without effecting any issued
+ * <code>OutputStream</code> or <code>PrintStream</code> object.
+ *
+ * @param size the minimum size that the response buffer must be
+ *
+ * @return a print stream that provides convenience writing
+ */
+ PrintStream getPrintStream(int size) throws IOException;
+
+ /**
+ * 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
+ */
+ WritableByteChannel getByteChannel() throws IOException;
+
+ /**
+ * 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
+ */
+ WritableByteChannel getByteChannel(int size) throws IOException;
+
+ /**
+ * 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
+ */
+ long getResponseTime();
+
+ /**
+ * 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
+ */
+ boolean 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 headers have been committed
+ */
+ boolean 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
+ */
+ void commit() throws IOException;
+
+ /**
+ * 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
+ */
+ void reset() throws IOException;
+
+ /**
+ * 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
+ */
+ void close() throws IOException;
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/ResponseHeader.java b/simple/simple-http/src/main/java/org/simpleframework/http/ResponseHeader.java
new file mode 100644
index 0000000..5b36994
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/ResponseHeader.java
@@ -0,0 +1,304 @@
+/*
+ * Response.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;
+
+import java.util.List;
+
+/**
+ * The <code>ResponseHeader</code> object is used to manipulate the
+ * header information for a given response. Headers are stored and
+ * retrieved from this object in a case insensitive manner. This
+ * implements the <code>StatusLine</code> object, which exposes the
+ * protocol version and response status code.
+ * <p>
+ * All cookies set on the response header will be delivered as a
+ * Set-Cookie header in the response message. The Content-Length and
+ * Transfer-Encoding headers can be set to configure how the message
+ * body is delivered to the connected client.
+ *
+ * @author Niall Gallagher
+ */
+public interface ResponseHeader extends StatusLine {
+
+ /**
+ * This is used to acquire the names of the of the headers that
+ * have been set in the response. This can be used to acquire all
+ * header values by name that have been set within the response.
+ * If no headers have been set this will return an empty list.
+ *
+ * @return a list of strings representing the set header names
+ */
+ List<String> getNames();
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void addValue(String name, String value);
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getInteger</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void addInteger(String name, int value);
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTPdate string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ void addDate(String name, long date);
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void setValue(String name, String value);
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void setInteger(String name, int value);
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void setLong(String name, long value);
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTP date string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ void setDate(String name, long date);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ String getValue(String name);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @param name the HTTP message header to get the value from
+ * @param index used if there are multiple headers present
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ String getValue(String name, int index);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the integer
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ int getInteger(String name);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the long value
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ long getDate(String 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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param name the name of the headers that are to be retrieved
+ *
+ * @return ordered list of tokens extracted from the header(s)
+ */
+ List<String> getValues(String name);
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ *
+ * @param cookie this is the cookie to be added to the response
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ Cookie setCookie(Cookie cookie);
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ * This is a convenience method that avoids cookie creation.
+ *
+ * @param name this is the cookie to be added to the response
+ * @param value this is the cookie value that is to be used
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ Cookie setCookie(String name, String value);
+
+ /**
+ * This returns the <code>Cookie</code> object stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If the cookie does
+ * not exist under the specified name this will return null.
+ *
+ * @param name this is the name of the cookie to be retrieved
+ *
+ * @return returns the <code>Cookie</code> by the given name
+ */
+ Cookie getCookie(String name);
+
+ /**
+ * This returns all <code>Cookie</code> objects stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If there are no
+ * cookies then this will return an empty list.
+ *
+ * @return returns all the <code>Cookie</code> in the response
+ */
+ List<Cookie> 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
+ */
+ ContentType getContentType();
+
+ /**
+ * 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
+ */
+ String getTransferEncoding();
+
+ /**
+ * 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
+ */
+ long getContentLength();
+
+ /**
+ * This method returns a <code>CharSequence</code> holding the header
+ * created 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 the data generated.
+ *
+ * @return this returns the characters generated for the header
+ */
+ CharSequence getHeader();
+
+ /**
+ * This method returns a string representing the header that was
+ * generated for this header. For performance reasons it is better
+ * to acquire the character sequence representing the header as it
+ * does not require the allocation on new memory.
+ *
+ * @return this returns a string representation of this response
+ */
+ String toString();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/ResponseWrapper.java b/simple/simple-http/src/main/java/org/simpleframework/http/ResponseWrapper.java
new file mode 100644
index 0000000..240384c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/ResponseWrapper.java
@@ -0,0 +1,747 @@
+/*
+ * ResponseWrapper.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;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.nio.channels.WritableByteChannel;
+import java.util.List;
+
+/**
+ * The <code>ResponseWrapper</code> object is used so that the original
+ * <code>Response</code> object can be wrapped in a filtering proxy
+ * object. This allows a container to interact with an implementation
+ * of this with overridden methods providing specific functionality.
+ * the <code>Response</code> object in a concurrent environment.
+ * <pre>
+ *
+ * public void handle(Request req, Response resp) {
+ * handler.handle(req, new ZipResponse(resp));
+ * }
+ *
+ * </pre>
+ * The above is an example of how the <code>ResponseWrapper</code> can
+ * be used to provide extra functionality to a <code>Response</code>
+ * in a transparent manner. Such an implementation could apply a
+ * Content-Encoding header and compress the response for performance
+ * over a slow network. Filtering can be applied with the use of
+ * layered <code>Container</code> objects.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.core.Container
+ */
+public class ResponseWrapper implements Response {
+
+ /**
+ * This is the response instance that is being wrapped.
+ */
+ protected Response response;
+
+ /**
+ * Constructor for <code>ResponseWrapper</code> object. This allows
+ * the original <code>Response</code> object to be wrapped so that
+ * adjustments to the behavior of a request object handed to the
+ * container can be provided by a subclass implementation.
+ *
+ * @param response the response object that is being wrapped
+ */
+ public ResponseWrapper(Response response){
+ this.response = response;
+ }
+
+ /**
+ * 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 response.getCode();
+ }
+
+ /**
+ * 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) {
+ response.setCode(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 response.getDescription();
+ }
+
+ /**
+ * 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) {
+ response.setDescription(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 response.getStatus();
+ }
+
+ /**
+ * 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) {
+ response.setStatus(status);
+ }
+
+ /**
+ * 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 response.getMajor();
+ }
+
+ /**
+ * 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) {
+ response.setMajor(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 response.getMinor();
+ }
+
+ /**
+ * 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) {
+ response.setMinor(minor);
+ }
+
+ /**
+ * 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 response.getResponseTime();
+ }
+
+ /**
+ * This is used to acquire the names of the of the headers that
+ * have been set in the response. This can be used to acquire all
+ * header values by name that have been set within the response.
+ * If no headers have been set this will return an empty list.
+ *
+ * @return a list of strings representing the set header names
+ */
+ public List<String> getNames() {
+ return response.getNames();
+ }
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void addValue(String name, String value) {
+ response.addValue(name, value);
+ }
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getInteger</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void addInteger(String name, int value) {
+ response.addInteger(name, value);
+ }
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTPdate string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ public void addDate(String name, long date) {
+ response.addDate(name, date);
+ }
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void setValue(String name, String value) {
+ response.setValue(name, value);
+ }
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void setInteger(String name, int value) {
+ response.setInteger(name, value);
+ }
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void setLong(String name, long value) {
+ response.setLong(name, value);
+ }
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTP date string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ public void setDate(String name, long date) {
+ response.setDate(name, date);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @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 response.getValue(name);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @param name the HTTP message header to get the value from
+ * @param index used if there are multiple headers present
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public String getValue(String name, int index) {
+ return response.getValue(name, index);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the integer
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public int getInteger(String name) {
+ return response.getInteger(name);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the long value
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public long getDate(String name) {
+ return response.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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param name the name of the headers that are to be retrieved
+ *
+ * @return ordered list of tokens extracted from the header(s)
+ */
+ public List<String> getValues(String name) {
+ return response.getValues(name);
+ }
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ *
+ * @param cookie this is the cookie to be added to the response
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ public Cookie setCookie(Cookie cookie) {
+ return response.setCookie(cookie);
+ }
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ * This is a convenience method that avoids cookie creation.
+ *
+ * @param name this is the cookie to be added to the response
+ * @param value this is the cookie value that is to be used
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ public Cookie setCookie(String name, String value) {
+ return response.setCookie(name, value);
+ }
+
+ /**
+ * This returns the <code>Cookie</code> object stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If the cookie does
+ * not exist under the specified name this will return null.
+ *
+ * @param name this is the name of the cookie to be retrieved
+ *
+ * @return returns the cookie object send with the request
+ */
+ public Cookie getCookie(String name) {
+ return response.getCookie(name);
+ }
+
+ /**
+ * This returns all <code>Cookie</code> objects stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If there are no
+ * cookies then this will return an empty list.
+ *
+ * @return returns all the cookie objects for this response
+ */
+ public List<Cookie> getCookies() {
+ return response.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 response.getContentType();
+ }
+
+ /**
+ * 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 response.getTransferEncoding();
+ }
+
+ /**
+ * 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 response.getContentLength();
+ }
+
+ /**
+ * 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) {
+ response.setContentLength(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) {
+ response.setContentType(type);
+ }
+
+ /**
+ * This method returns a <code>CharSequence</code> holding the header
+ * created 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 the data generated.
+ *
+ * @return this returns the characters generated for the header
+ */
+ public CharSequence getHeader() {
+ return response.getHeader();
+ }
+
+ /**
+ * 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.
+ * The <code>OutputStream</code> issued must be thread safe so that
+ * it can be used in a concurrent environment.
+ *
+ * @exception IOException this is thrown if there was an I/O error
+ *
+ * @return an output stream used to write the response body
+ */
+ public OutputStream getOutputStream() throws IOException {
+ return response.getOutputStream();
+ }
+
+ /**
+ * 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.
+ * The <code>OutputStream</code> issued must be thread safe so that
+ * it can be used in a concurrent environment.
+ * <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 used to write the response body
+ *
+ * @exception IOException this is thrown if there was an I/O error
+ */
+ public OutputStream getOutputStream(int size) throws IOException {
+ return response.getOutputStream(size);
+ }
+
+ /**
+ * 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.
+ * <p>
+ * Implementations of the <code>Response</code> must guarantee
+ * that this can be invoked repeatedly without effecting any issued
+ * <code>OutputStream</code> or <code>PrintStream</code> object.
+ *
+ * @return a print stream used for writing the response body
+ *
+ * @exception IOException this is thrown if there was an I/O error
+ */
+ public PrintStream getPrintStream() throws IOException {
+ return response.getPrintStream();
+ }
+
+ /**
+ * 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.
+ * <p>
+ * Implementations of the <code>Response</code> must guarantee
+ * that this can be invoked repeatedly without effecting any issued
+ * <code>OutputStream</code> or <code>PrintStream</code> object.
+ *
+ * @param size the minimum size that the response buffer must be
+ *
+ * @return a print stream used for writing the response body
+ *
+ * @exception IOException this is thrown if there was an I/O error
+ */
+ public PrintStream getPrintStream(int size) throws IOException {
+ return response.getPrintStream(size);
+ }
+
+ /**
+ * 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 response.getByteChannel();
+ }
+
+ /**
+ * 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 {
+ return response.getByteChannel(size);
+ }
+
+ /**
+ * 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 response.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 response.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 {
+ response.commit();
+ }
+
+ /**
+ * 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 {
+ response.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 {
+ response.close();
+ }
+
+ /**
+ * This method returns a string representing the header that was
+ * generated for this header. For performance reasons it is better
+ * to acquire the character sequence representing the header as it
+ * does not require the allocation on new memory.
+ *
+ * @return this returns a string representation of this response
+ */
+ public String toString() {
+ return response.toString();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Scheme.java b/simple/simple-http/src/main/java/org/simpleframework/http/Scheme.java
new file mode 100644
index 0000000..b6df799
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Scheme.java
@@ -0,0 +1,136 @@
+/*
+* Scheme.java February 2014
+*
+* Copyright (C) 2014, 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;
+
+import java.net.URI;
+
+/**
+* The <code>Scheme</code> represents a scheme used for a URI. Here
+ * only schemes that directly relate to HTTP are provided, which
+ * includes HTTP/1.1 schemes and WebSocket 1.0 schemes.
+ *
+ * @author Niall Gallagher
+*/
+public enum Scheme {
+
+ /**
+ * This represents the scheme for a plaintext HTTP connection.
+ */
+ HTTP("http", false),
+
+ /**
+ * This represents the scheme for a HTTP over TLS connection.
+ */
+ HTTPS("https", true),
+
+ /**
+ * This represents the scheme for a plaintext WebSocket connection.
+ */
+ WS("ws", false),
+
+ /**
+ * This represents the scheme for WebSocket over TLS connection.
+ */
+ WSS("wss", true);
+
+ /**
+ * This is the actual scheme token that is to be used in the URI.
+ */
+ public final String scheme;
+
+ /**
+ * This is used to determine if the connection is secure or not.
+ */
+ public final boolean secure;
+
+ /**
+ * Constructor for the <code>Scheme</code> object. This is used
+ * create an entry using the specific scheme token and a boolean
+ * indicating if the scheme is secure or not.
+ *
+ * @param scheme this is the scheme token to be used
+ * @param secure this determines if the scheme is secure or not
+ */
+ private Scheme(String scheme, boolean secure) {
+ this.scheme = scheme;
+ this.secure = secure;
+ }
+
+ /**
+ * This is used to determine if the scheme is secure or not. In
+ * general a secure scheme is one sent over a SSL/TLS connection.
+ *
+ * @return this returns true if the scheme is a secure one
+ */
+ public boolean isSecure() {
+ return secure;
+ }
+
+ /**
+ * This is used to acquire the scheme token for this. The scheme
+ * token can be used to prefix a absolute fully qualified URI.
+ *
+ * @return the scheme token representing this scheme
+ */
+ public String getScheme() {
+ return scheme;
+ }
+
+ /**
+ * This is used to resolve the scheme given a token. If there is
+ * no matching scheme for the provided token a default of HTTP
+ * is provided.
+ *
+ * @param token this is the token used to determine the scheme
+ *
+ * @return this returns the match or HTTP if none matched
+ */
+ public static Scheme resolveScheme(String token) {
+ if(token != null) {
+ for(Scheme scheme : values()) {
+ if(token.equalsIgnoreCase(scheme.scheme)) {
+ return scheme;
+ }
+ }
+ }
+ return HTTP;
+ }
+
+ /**
+ * This is used to resolve the scheme given a <code>URI</code>. If
+ * there is no matching scheme for the provided instance then this
+ * will return null.
+ *
+ * @param token this is the object to resolve a scheme for
+ *
+ * @return this returns the match or null if none matched
+ */
+ public static Scheme resolveScheme(URI target) {
+ if(target != null) {
+ String scheme = target.getScheme();
+
+ for(Scheme option : values()) {
+ if(option.scheme.equalsIgnoreCase(scheme)) {
+ return option;
+ }
+ }
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/Status.java b/simple/simple-http/src/main/java/org/simpleframework/http/Status.java
new file mode 100644
index 0000000..7fa3b6f
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/Status.java
@@ -0,0 +1,320 @@
+/*
+ * Status.java February 2008
+ *
+ * Copyright (C) 2008, 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;
+
+/**
+ * The <code>Status</code> enumeration is used to specify status codes
+ * and the descriptions of those status codes. This is a convenience
+ * enumeration that allows users to acquire the descriptions of codes
+ * by simply providing the code. Also if the response state is known
+ * the code and description can be provided to the client.
+ * <p>
+ * The official HTTP status codes are defined in RFC 2616 section 10.
+ * Each set of status codes belongs to a specific family. Each family
+ * describes a specific scenario. Although it is possible to use other
+ * status codes it is recommended that servers restrict their status
+ * code responses to those specified in this enumeration.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.StatusLine
+ */
+public enum Status {
+
+ /**
+ * This is used as an intermediate response to a request.
+ */
+ CONTINUE(100, "Continue"),
+
+ /**
+ * This represents a change in the protocol the client is using.
+ */
+ SWITCHING_PROTOCOLS(101, "Switching Protocols"),
+
+ /**
+ * This represents a successful response of a targeted request.
+ */
+ OK(200, "OK"),
+
+ /**
+ * This is used to signify that a resource was created successfully.
+ */
+ CREATED(201, "Created"),
+
+ /**
+ * This is used to signify that the request has been accepted.
+ */
+ ACCEPTED(202, "Accepted"),
+
+ /**
+ * This represents a response that contains no response content.
+ */
+ NO_CONTENT(204, "No Content"),
+
+ /**
+ * This is used to represent a response that resets the content.
+ */
+ RESET_CONTENT(205, "Reset Content"),
+
+ /**
+ * This is used to represent a response that has partial content.
+ */
+ PARTIAL_CONTENT(206, "Partial Content"),
+
+ /**
+ * This is used to represent a response where there are choices.
+ */
+ MULTIPLE_CHOICES(300, "Multiple Choices"),
+
+ /**
+ * This is used to represent a target resource that has moved.
+ */
+ MOVED_PERMANENTLY(301, "Moved Permanently"),
+
+ /**
+ * This is used to represent a resource that has been found.
+ */
+ FOUND(302, "Found"),
+
+ /**
+ * This is used to tell the client to see another HTTP resource.
+ */
+ SEE_OTHER(303, "See Other"),
+
+ /**
+ * This is used in response to a target that has not been modified.
+ */
+ NOT_MODIFIED(304, "Not Modified"),
+
+ /**
+ * This is used to tell the client that it should use a proxy.
+ */
+ USE_PROXY(305, "Use Proxy"),
+
+ /**
+ * This is used to redirect the client to a resource that has moved.
+ */
+ TEMPORARY_REDIRECT(307, "Temporary Redirect"),
+
+ /**
+ * This is used to tell the client they have send an invalid request.
+ */
+ BAD_REQUEST(400, "Bad Request"),
+
+ /**
+ * This is used to tell the client that authorization is required.
+ */
+ UNAUTHORIZED(401, "Unauthorized"),
+
+ /**
+ * This is used to tell the client that payment is required.
+ */
+ PAYMENT_REQUIRED(402, "Payment Required"),
+
+ /**
+ * This is used to tell the client that the resource is forbidden.
+ */
+ FORBIDDEN(403, "Forbidden"),
+
+ /**
+ * This is used to tell the client that the resource is not found.
+ */
+ NOT_FOUND(404, "Not Found"),
+
+ /**
+ * This is used to tell the client that the method is not allowed.
+ */
+ METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
+
+ /**
+ * This is used to tell the client the request is not acceptable.
+ */
+ NOT_ACCEPTABLE(406, "Not Acceptable"),
+
+ /**
+ * This is used to tell the client that authentication is required.
+ */
+ PROXY_AUTHENTICATION_REQUIRED(407, "Proxy Authentication Required"),
+
+ /**
+ * This is used to tell the client that the request has timed out.
+ */
+ REQUEST_TIMEOUT(408, "Request Timeout"),
+
+ /**
+ * This is used to tell the client that there has been a conflict.
+ */
+ CONFLICT(409, "Conflict"),
+
+ /**
+ * This is used to tell the client that the resource has gone.
+ */
+ GONE(410, "Gone"),
+
+ /**
+ * This is used to tell the client that a request length is needed.
+ */
+ LENGTH_REQUIRED(411, "Length Required"),
+
+ /**
+ * This is used to tell the client that a precondition has failed.
+ */
+ PRECONDITION_FAILED(412, "Precondition Failed"),
+
+ /**
+ * This is used to tell the client that the request body is too big.
+ */
+ REQUEST_ENTITY_TOO_LARGE(413, "Request Entity Too Large"),
+
+ /**
+ * This is used to tell the client that the request URI is too long.
+ */
+ REQUEST_URI_TOO_LONG(414, "Request-URI Too Long"),
+
+ /**
+ * This is used to tell the client that the content type is invalid.
+ */
+ UNSUPPORTED_MEDIA_TYPE(415, "Unsupported Media Type"),
+
+ /**
+ * This is used to tell the client that the range is invalid.
+ */
+ REQUESTED_RANGE_NOT_SATISFIABLE(416, "Requested Range Not Satisfiable"),
+
+ /**
+ * This is used to tell the client that the expectation has failed.
+ */
+ EXPECTATION_FAILED(417, "Expectation Failed"),
+
+ /**
+ * This is sent when the request has caused an internal server error.
+ */
+ INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
+
+ /**
+ * This is used to tell the client the resource is not implemented.
+ */
+ NOT_IMPLEMENTED(501, "Not Implemented"),
+
+ /**
+ * This is used to tell the client that the gateway is invalid.
+ */
+ BAD_GATEWAY(502, "Bad Gateway"),
+
+ /**
+ * This is used to tell the client the resource is unavailable.
+ */
+ SERVICE_UNAVAILABLE(503, "Service Unavailable"),
+
+ /**
+ * This is used to tell the client there was a gateway timeout.
+ */
+ GATEWAY_TIMEOUT(504, "Gateway Timeout"),
+
+ /**
+ * This is used to tell the client the request version is invalid.
+ */
+ VERSION_NOT_SUPPORTED(505, "Version Not Supported");
+
+ /**
+ * This is the description of the status this instance represents.
+ */
+ public final String description;
+
+ /**
+ * This is the code for the status that this instance represents.
+ */
+ public final int code;
+
+ /**
+ * Constructor for the <code>Status</code> object. This will create
+ * a status object that is used to represent a response state. It
+ * contains a status code and a description of that code.
+ *
+ * @param code this is the code that is used for this status
+ * @param description this is the description used for the status
+ */
+ private Status(int code, String description) {
+ this.description = description;
+ this.code = code;
+ }
+
+ /**
+ * This is used to acquire the code of the status object. This is
+ * used in the HTTP response message to tell the client what kind
+ * of response this represents. Typically this is used to get a
+ * code for a known response state for convenience.
+ *
+ * @return the code associated by this status instance
+ */
+ public int getCode() {
+ return code;
+ }
+
+ /**
+ * This is used to provide the status description. The description
+ * is the textual description of the response state. It is used
+ * so that the response can be interpreted and is a required part
+ * of the HTTP response combined with the status code.
+ *
+ * @return the description associated by this status instance
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * This is used to provide the status description. The description
+ * is the textual description of the response state. It is used
+ * so that the response can be interpreted and is a required part
+ * of the HTTP response combined with the status code.
+ *
+ * @param code this is the code to resolve the description for
+ *
+ * @return the description associated by this status code
+ */
+ public static String getDescription(int code) {
+ Status[] list = values();
+
+ for(Status status : list) {
+ if(status.code == code)
+ return status.description;
+ }
+ return "Unknown";
+ }
+
+ /**
+ * This is used to provide the status value. If the specified
+ * code can not be matched this will return the default HTTP/1.1
+ * status code of OK, which may not match the intended status.
+ *
+ * @param code this is the code to resolve the status for
+ *
+ * @return the status value associated by this status code
+ */
+ public static Status getStatus(int code) {
+ Status[] list = values();
+
+ for(Status status : list) {
+ if(status.code == code)
+ return status;
+ }
+ return OK;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/StatusLine.java b/simple/simple-http/src/main/java/org/simpleframework/http/StatusLine.java
new file mode 100644
index 0000000..3ea0f16
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/StatusLine.java
@@ -0,0 +1,122 @@
+/*
+ * StatusLine.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;
+
+/**
+ * The <code>StatusLine</code> is used to represent a HTTP status
+ * line. This provides several convenience methods that can be used
+ * to manipulate a HTTP status line. see the RFC (RFC 2616) for the
+ * syntax of a status line.
+ *
+ * @author Niall Gallagher
+ */
+public interface StatusLine {
+
+ /**
+ * 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
+ */
+ int getCode();
+
+ /**
+ * 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
+ */
+ void setCode(int 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
+ */
+ String getDescription();
+
+ /**
+ * 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
+ */
+ void setDescription(String 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
+ */
+ Status getStatus();
+
+ /**
+ * 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
+ */
+ void setStatus(Status status);
+
+ /**
+ * 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 response
+ */
+ int getMajor();
+
+ /**
+ * This can be used to specify the major version. This
+ * should be the major version of the HTTP request.
+ *
+ * @param major this is the major number desired
+ */
+ void setMajor(int major);
+
+ /**
+ * This can be used to get the minor number from a HTTP
+ * version. The major version corresponds to the minor
+ * type that is the 0 of a HTTP/1.0 version string.
+ *
+ * @return the major version number for the response
+ */
+ int getMinor();
+
+ /**
+ * This can be used to specify the minor version. This
+ * should not be set to zero if the HTTP request was
+ * for HTTP/1.1. The response must be equal or higher.
+ *
+ * @param minor this is the minor number desired
+ */
+ void setMinor(int minor);
+}
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
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/ArrayConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/ArrayConsumer.java
new file mode 100644
index 0000000..ef5db66
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/ArrayConsumer.java
@@ -0,0 +1,184 @@
+/*
+ * ArrayConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>ArrayConsumer</code> object is a consumer that consumes
+ * bytes in to an internal array before processing. This consumes
+ * all bytes read in to an internal array. Each read is met with an
+ * invocation of the <code>scan</code> method, which searches for
+ * the terminal token within the read chunk. Once the terminal token
+ * has been read the excess bytes are reset and the data can be
+ * processed by the subclass implementation. The internal array is
+ * expanded if the number of consumed bytes exceeds its capacity.
+ *
+ * @author Niall Gallagher
+ */
+public abstract class ArrayConsumer implements ByteConsumer {
+
+ /**
+ * This is the array that is used to contain the read bytes.
+ */
+ protected byte[] array;
+
+ /**
+ * This is the number of bytes that have been consumed so far.
+ */
+ protected int count;
+
+ /**
+ * This is the size of the chunk of bytes to read each time.
+ */
+ protected int chunk;
+
+ /**
+ * This determines whether the terminal token has been read.
+ */
+ protected boolean done;
+
+ /**
+ * Constructor for the <code>ArrayConsumer</code> object. This is
+ * used to create a consumer that will consume all bytes in to an
+ * internal array until a terminal token has been read. If excess
+ * bytes are read by this consumer they are reset in the cursor.
+ */
+ public ArrayConsumer() {
+ this(1024);
+ }
+
+ /**
+ * Constructor for the <code>ArrayConsumer</code> object. This is
+ * used to create a consumer that will consume all bytes in to an
+ * internal array until a terminal token has been read. If excess
+ * bytes are read by this consumer they are reset in the cursor.
+ *
+ * @param size this is the initial array and chunk size to use
+ */
+ public ArrayConsumer(int size) {
+ this(size, 512);
+ }
+
+ /**
+ * Constructor for the <code>ArrayConsumer</code> object. This is
+ * used to create a consumer that will consume all bytes in to an
+ * internal array until a terminal token has been read. If excess
+ * bytes are read by this consumer they are reset in the cursor.
+ *
+ * @param size this is the initial array size that is to be used
+ * @param chunk this is the chunk size to read bytes as
+ */
+ public ArrayConsumer(int size, int chunk) {
+ this.array = new byte[size];
+ this.chunk = chunk;
+ }
+
+ /**
+ * This method is used to consume bytes from the provided cursor.
+ * Each read performed is done in a specific chunk size to ensure
+ * that a sufficiently large or small amount of data is read from
+ * the <code>ByteCursor</code> object. After each read the byte
+ * array is scanned for the terminal token. When the terminal
+ * token is found the bytes are processed by the implementation.
+ *
+ * @param cursor this is the cursor to consume the bytes from
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ if(!done) {
+ int ready = cursor.ready();
+
+ while(ready > 0) {
+ int size = Math.min(ready, chunk);
+
+ if(count + size > array.length) {
+ resize(count + size);
+ }
+ size = cursor.read(array, count, size);
+ count += size;
+
+ if(size > 0) {
+ int reset = scan();
+
+ if(reset > 0) {
+ cursor.reset(reset);
+ }
+ if(done) {
+ process();
+ break;
+ }
+ }
+ ready = cursor.ready();
+ }
+ }
+ }
+
+ /**
+ * This method is used to add an additional chunk size to the
+ * internal array. Resizing of the internal array is required as
+ * the consumed bytes may exceed the initial size of the array.
+ * In such a scenario the array is expanded the chunk size.
+ *
+ * @param size this is the minimum size to expand the array to
+ */
+ protected void resize(int size) throws IOException {
+ if(array.length < size) {
+ int expand = array.length + chunk;
+ int max = Math.max(expand, size);
+ byte[] temp = new byte[max];
+
+ System.arraycopy(array, 0, temp, 0, count);
+ array = temp;
+ }
+ }
+
+ /**
+ * When the terminal token is read from the cursor this will be
+ * true. The <code>scan</code> method is used to determine the
+ * terminal token. It is invoked after each read, when the scan
+ * method returns a non-zero value then excess bytes are reset
+ * and the consumer has finished.
+ *
+ * @return this returns true when the terminal token is read
+ */
+ public boolean isFinished() {
+ return done;
+ }
+
+ /**
+ * This method is invoked after the terminal token has been read.
+ * It is used to process the consumed data and is typically used to
+ * parse the input such that it can be used by the subclass for
+ * some useful purpose. This is called only once by the consumer.
+ */
+ protected abstract void process() throws IOException;
+
+ /**
+ * This method is used to scan for the terminal token. It searches
+ * for the token and returns the number of bytes in the buffer
+ * after the terminal token. Returning the excess bytes allows the
+ * consumer to reset the bytes within the consumer object.
+ *
+ * @return this returns the number of excess bytes consumed
+ */
+ protected abstract int scan() throws IOException;
+
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/Body.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/Body.java
new file mode 100644
index 0000000..6b3599a
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/Body.java
@@ -0,0 +1,95 @@
+/*
+ * Body.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.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+
+import org.simpleframework.http.Part;
+
+/**
+ * The <code>Body</code> interface is used to represent the body of
+ * a HTTP entity. It contains the information that is delivered with
+ * the request. The body is represented by a stream of bytes. In
+ * order to access the entity body this interface provides a stream
+ * which can be used to read it. Also, should the message be encoded
+ * as a multipart message the individual parts can be read using the
+ * <code>Attachment</code> instance for it.
+ *
+ * @author Niall Gallagher
+ */
+public interface Body {
+
+ /**
+ * This will acquire the contents of the body in UTF-8. If there
+ * is no content encoding and the user of the request wants to
+ * deal with the body as a string then this method can be used.
+ * It will simply create a UTF-8 string using the body bytes.
+ *
+ * @return returns a UTF-8 string representation of the body
+ */
+ String getContent() throws IOException;
+
+ /**
+ * This will acquire the contents of the body in the specified
+ * charset. Typically this will be given the charset as taken
+ * from the HTTP Content-Type header. Although any encoding can
+ * be specified to convert the body to a string representation.
+ *
+ * @return returns an encoded string representation of the body
+ */
+ String getContent(String charset) throws IOException;
+
+ /**
+ * This is used to acquire the contents of the body as a stream.
+ * Each time this method is invoked a new stream is created that
+ * will read the contents of the body from the first byte. This
+ * ensures that the stream can be acquired several times without
+ * any issues arising from previous reads.
+ *
+ * @return this returns a new string used to read the body
+ */
+ InputStream getInputStream() throws IOException;
+
+ /**
+ * 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
+ */
+ Part getPart(String 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
+ */
+ List<Part> getParts();
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/BodyConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/BodyConsumer.java
new file mode 100644
index 0000000..d84d6ed
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/BodyConsumer.java
@@ -0,0 +1,43 @@
+/*
+ * BodyConsumer.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.message;
+
+/**
+ * The <code>BodyConsumer</code> is used to consume the body of an
+ * HTTP message. Implementations of this consumer must provide the
+ * <code>Body</code> that has been consumed. If there is no body
+ * associated with the consumer then an empty body is returned.
+ *
+ * @author Niall Gallagher
+ */
+public interface BodyConsumer extends ByteConsumer {
+
+ /**
+ * This is used to acquire the body that has been consumed. 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 that has been consumed by this instance
+ */
+ Body getBody();
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/BoundaryConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/BoundaryConsumer.java
new file mode 100644
index 0000000..f519ce2
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/BoundaryConsumer.java
@@ -0,0 +1,206 @@
+/*
+ * BoundaryConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.Buffer;
+
+/**
+ * The <code>BoundaryConsumer</code> is used to consume a boundary
+ * for a multipart message. This ensures that the boundary complies
+ * with the multipart specification in that it ends with a carriage
+ * return and line feed. This consumer implementation can be used
+ * multiple times as its internal buffer can be cleared and reset.
+ *
+ * @author Niall Gallagher
+ */
+class BoundaryConsumer extends ArrayConsumer {
+
+ /**
+ * This is the terminal token for a multipart boundary entity.
+ */
+ private static final byte[] LAST = { '-', '-', '\r', '\n', };
+
+ /**
+ * This is the terminal token for a multipart boundary line.
+ */
+ private static final byte[] LINE = { '\r', '\n' };
+
+ /**
+ * This represents the start of the boundary line for the part.
+ */
+ private static final byte[] TOKEN = { '-', '-' };
+
+ /**
+ * This is used to allocate a buffer for for the boundary.
+ */
+ private Allocator allocator;
+
+ /**
+ * This is used to consume the contents of the consumed buffer.
+ */
+ private Buffer buffer;
+
+ /**
+ * This is the actual boundary value that is to be consumed.
+ */
+ private byte[] boundary;
+
+ /**
+ * This counts the number of characters read from the start.
+ */
+ private int seek;
+
+ /**
+ * Constructor for the <code>BoundaryConsumer</code> object. This
+ * is used to create a boundary consumer for validating boundaries
+ * and consuming them from a provided source. This is used to help
+ * in reading multipart messages by removing boundaries from the
+ * stream.
+ *
+ * @param boundary this is the boundary value to be consumed
+ */
+ public BoundaryConsumer(Allocator allocator, byte[] boundary) {
+ this.chunk = boundary.length + LAST.length + TOKEN.length;
+ this.allocator = allocator;
+ this.boundary = boundary;
+ }
+
+ /**
+ * This does not perform any processing after the boundary has
+ * been consumed. Because the boundary consumer is used only as a
+ * means to remove the boundary from the underlying stream there
+ * is no need to perform any processing of the value consumed.
+ */
+ @Override
+ protected void process() throws IOException {
+ if(count < boundary.length + 4) {
+ throw new IOException("Invalid boundary processed");
+ }
+ }
+
+ /**
+ * This method is used to scan for the terminal token. It searches
+ * for the token and returns the number of bytes in the buffer
+ * after the terminal token. Returning the excess bytes allows the
+ * consumer to reset the bytes within the consumer object.
+ *
+ * @return this returns the number of excess bytes consumed
+ */
+ @Override
+ protected int scan() throws IOException {
+ int size = boundary.length;
+
+ if(count >= 2 && seek < 2) {
+ if(scan(TOKEN)) {
+ append(TOKEN);
+ }
+ }
+ if(count >= 2 + size && seek < 2 + size) {
+ if(scan(boundary)) {
+ append(boundary);
+ }
+ }
+ if(count >= 4 + size && seek < 4 + size) {
+ if(array[size + 2] == TOKEN[0]) {
+ if(scan(TOKEN)) {
+ append(TOKEN);
+ }
+ } else if(array[size + 2] == LINE[0]) {
+ if(scan(LINE)) {
+ append(LINE);
+ }
+ done = true;
+ return count - seek;
+ }
+ }
+ if(count >= 6 + size && seek < 6 + size) {
+ if(scan(LINE)) {
+ append(LINE);
+ }
+ done = true;
+ return count - seek;
+ }
+ return 0;
+ }
+
+ /**
+ * This is used to append a token to the underlying buffer. Adding
+ * various tokens ensures that the whole message is reconstructed
+ * and can be forwarded to any connected service if used as a proxy.
+ *
+ * @param token this is the token that is to be appended
+ */
+ private void append(byte[] token) throws IOException {
+ if(buffer == null) {
+ buffer = allocator.allocate(chunk);
+ }
+ buffer.append(token);
+ }
+
+ /**
+ * This is used to scan the specified token from the consumed bytes.
+ * If the data scanned does not match the token provided then this
+ * will throw an exception to signify a bad boundary. This will
+ * return true only when the whole boundary has been consumed.
+ *
+ * @param data this is the token to scan from the consumed bytes
+ *
+ * @return this returns true of the token has been read
+ */
+ private boolean scan(byte[] data) throws IOException {
+ int size = data.length;
+ int pos = 0;
+
+ while(seek < count) {
+ if(array[seek++] != data[pos++]) {
+ throw new IOException("Invalid boundary");
+ }
+ if(pos == data.length) {
+ return true;
+ }
+ }
+ return pos == size;
+ }
+
+ /**
+ * This is used to determine whether the boundary has been read
+ * from the underlying stream. This is true only when the very
+ * last boundary has been read. This will be the boundary value
+ * that ends with the two <code>-</code> characters.
+ *
+ * @return this returns true with the terminal boundary is read
+ */
+ public boolean isEnd() {
+ return seek == chunk;
+ }
+
+ /**
+ * This is used to clear the state of the of boundary consumer
+ * such that it can be reused. This is required as the multipart
+ * body may contain many parts, all delimited with the same
+ * boundary. Clearing allows the next boundary to be consumed.
+ */
+ public void clear() {
+ done = false;
+ count = seek = 0;
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/BufferBody.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/BufferBody.java
new file mode 100644
index 0000000..e0e8a75
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/BufferBody.java
@@ -0,0 +1,166 @@
+/*
+ * BufferBody.java February 2012
+ *
+ * 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.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.List;
+
+import org.simpleframework.common.buffer.Buffer;
+import org.simpleframework.http.Part;
+
+/**
+ * The <code>Body</code> interface is used to represent the body of
+ * a HTTP entity. It contains the information that is delivered with
+ * the request. The body is represented by a stream of bytes. In
+ * order to access the entity body this interface provides a stream
+ * which can be used to read it. Also, should the message be encoded
+ * as a multipart message the individual parts can be read using the
+ * <code>Attachment</code> instance for it.
+ *
+ * @author Niall Gallagher
+ */
+class BufferBody implements Body {
+
+ /**
+ * This is used to hold the attachments for the HTTP body.
+ */
+ private final PartSeries series;
+
+ /**
+ * This is usd to hold the bytes representing the HTTP body.
+ */
+ private final Buffer buffer;
+
+ /**
+ * Constructor for the <code>BufferBody</code> object. This is
+ * used to create a body that represents a HTTP payload. The
+ * body enables the payload to be either read in a stream or
+ * as an encoded string. Also the attachments are available.
+ */
+ public BufferBody() {
+ this(null);
+ }
+
+ /**
+ * Constructor for the <code>BufferBody</code> object. This is
+ * used to create a body that represents a HTTP payload. The
+ * body enables the payload to be either read in a stream or
+ * as an encoded string. Also the attachments are available.
+ *
+ * @param buffer this is the buffer representing the body
+ */
+ public BufferBody(Buffer buffer) {
+ this(buffer, null);
+ }
+
+ /**
+ * Constructor for the <code>BufferBody</code> object. This is
+ * used to create a body that represents a HTTP payload. The
+ * body enables the payload to be either read in a stream or
+ * as an encoded string. Also the attachments are available.
+ *
+ * @param buffer this is the buffer representing the body
+ * @param series this is the list of parts for this body
+ */
+ public BufferBody(Buffer buffer, PartSeries series) {
+ this.buffer = buffer;
+ this.series = series;
+ }
+
+ /**
+ * 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) {
+ if(series != null) {
+ return series.getPart(name);
+ }
+ return null;
+ }
+
+ /**
+ * 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() {
+ if(series != null) {
+ return series.getParts();
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * This will acquire the contents of the body in UTF-8. If there
+ * is no content encoding and the user of the request wants to
+ * deal with the body as a string then this method can be used.
+ * It will simply create a UTF-8 string using the body bytes.
+ *
+ * @return returns a UTF-8 string representation of the body
+ */
+ public String getContent() throws IOException {
+ if(buffer == null) {
+ return new String();
+ }
+ return buffer.encode();
+ }
+
+ /**
+ * This will acquire the contents of the body in the specified
+ * charset. Typically this will be given the charset as taken
+ * from the HTTP Content-Type header. Although any encoding can
+ * be specified to convert the body to a string representation.
+ *
+ * @return returns an encoded string representation of the body
+ */
+ public String getContent(String charset) throws IOException {
+ if(buffer == null) {
+ return new String();
+ }
+ return buffer.encode(charset);
+ }
+
+ /**
+ * This is used to acquire the contents of the body as a stream.
+ * Each time this method is invoked a new stream is created that
+ * will read the contents of the body from the first byte. This
+ * ensures that the stream can be acquired several times without
+ * any issues arising from previous reads.
+ *
+ * @return this returns a new string used to read the body
+ */
+ public InputStream getInputStream() throws IOException {
+ if(buffer == null) {
+ return new EmptyInputStream();
+ }
+ return buffer.open();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/BufferPart.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/BufferPart.java
new file mode 100644
index 0000000..73a5ea0
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/BufferPart.java
@@ -0,0 +1,160 @@
+/*
+ * BufferPart.java February 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.message;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.simpleframework.common.buffer.Buffer;
+import org.simpleframework.http.ContentDisposition;
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Part;
+
+/**
+ * The <code>BufferPart</code> is used to represent a part within
+ * a request message. Typically a part represents either a text
+ * parameter or a file, with associated headers. The contents of
+ * the part can be acquire as an <code>InputStream</code> or as a
+ * string encoded in the default HTTP encoding ISO-8859-1 or in
+ * the encoding specified with the Content-Type header.
+ *
+ * @author Niall Gallagher
+ */
+class BufferPart implements Part {
+
+ /**
+ * This is the segment representing the headers for the part.
+ */
+ private final Segment segment;
+
+ /**
+ * This is the body that forms the payload for the part.
+ */
+ private final Body body;
+
+ /**
+ * Constructor for the <code>BufferPart</code> object. This is
+ * used to create a part from a multipart body. Each part will
+ * contain the headers associated with it as well as the body.
+ *
+ * @param segment this holds the headers for the part
+ * @param buffer this represents the body for the part
+ */
+ public BufferPart(Segment segment, Buffer buffer) {
+ this.body = new BufferBody(buffer);
+ this.segment = segment;
+ }
+
+ /**
+ * This method is used to determine the type of a part. Typically
+ * a part is either a text parameter or a file. If this is true
+ * then the content represented by the associated part is a file.
+ *
+ * @return this returns true if the associated part is a file
+ */
+ public boolean isFile() {
+ return getDisposition().isFile();
+ }
+
+ /**
+ * This method is used to acquire the name of the part. Typically
+ * this is used when the part represents a text parameter rather
+ * than a file. However, this can also be used with a file part.
+ *
+ * @return this returns the name of the associated part
+ */
+ public String getName() {
+ return getDisposition().getName();
+ }
+
+ /**
+ * This method is used to acquire the file name of the part. This
+ * is used when the part represents a text parameter rather than
+ * a file. However, this can also be used with a file part.
+ *
+ * @return this returns the file name of the associated part
+ */
+ public String getFileName() {
+ return getDisposition().getFileName();
+ }
+
+ /**
+ * This is used to acquire the content of the part as a string.
+ * The encoding of the string is taken from the content type.
+ * If no content type is sent the content is decoded in the
+ * standard default of ISO-8859-1.
+ *
+ * @return this returns a string representing the content
+ *
+ * @throws IOException thrown if the content can not be created
+ */
+ public String getContent() throws IOException {
+ return body.getContent();
+ }
+
+ /**
+ * This is used to acquire an <code>InputStream</code> for the
+ * part. Acquiring the stream allows the content of the part to
+ * be consumed by reading the stream. Each invocation of this
+ * method will produce a new stream starting from the first byte.
+ *
+ * @return this returns the stream for this part object
+ *
+ * @throws IOException thrown if the stream can not be created
+ */
+ public InputStream getInputStream() throws IOException {
+ return body.getInputStream();
+ }
+
+ /**
+ * This is used to acquire the content type for this part. This
+ * is typically the type of content for a file part, as provided
+ * by a MIME type from the HTTP "Content-Type" header.
+ *
+ * @return this returns the content type for the part object
+ */
+ public ContentType getContentType() {
+ return segment.getContentType();
+ }
+
+ /**
+ * This is used to acquire the content disposition for the part.
+ * The content disposition contains the Content-Disposition header
+ * details sent with the part in the multipart request body.
+ *
+ * @return value of the header mapped to the specified name
+ */
+ public ContentDisposition getDisposition() {
+ return segment.getDisposition();
+ }
+
+ /**
+ * This is used to acquire the header value for the specified
+ * header name. Providing the header values through this method
+ * ensures any special processing for a know content type can be
+ * handled by an application.
+ *
+ * @param name the name of the header to get the value for
+ *
+ * @return value of the header mapped to the specified name
+ */
+ public String getHeader(String name) {
+ return segment.getValue(name);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/ByteConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/ByteConsumer.java
new file mode 100644
index 0000000..886434d
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/ByteConsumer.java
@@ -0,0 +1,64 @@
+/*
+ * ByteConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>ByteConsumer</code> object is used to consume and process
+ * bytes from a cursor. This is used to consume bytes from a pipeline
+ * and process the content in order to produce a valid HTTP message.
+ * Using a consumer allows the server to gather and process the data
+ * from the stream bit by bit without blocking.
+ * <p>
+ * A consumer has completed its task when it has either exhausted its
+ * stream, or when it has consume a terminal token. For instance a
+ * consumer for a HTTP header will have two <code>CRLF</code> bytes
+ * tokens to identify the end of the header, once this has been read
+ * any excess bytes are reset on the cursor and it has finished.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.transport.ByteCursor
+ */
+public interface ByteConsumer {
+
+ /**
+ * This method is used to consume bytes from the provided cursor.
+ * Consuming of bytes from the cursor should be done in such a
+ * way that it does not block. So typically only the number of
+ * ready bytes in the <code>ByteCursor</code> object should be
+ * read. If there are no ready bytes then this method return.
+ *
+ * @param cursor used to consume the bytes from the cursor
+ */
+ void consume(ByteCursor cursor) throws IOException;
+
+ /**
+ * This is used to determine whether the consumer has finished
+ * reading. The consumer is considered finished if it has read a
+ * terminal token or if it has exhausted the stream and can not
+ * read any more. Once finished the consumed bytes can be parsed.
+ *
+ * @return true if the consumer has finished reading its content
+ */
+ boolean isFinished();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/ChunkedConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/ChunkedConsumer.java
new file mode 100644
index 0000000..41549d6
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/ChunkedConsumer.java
@@ -0,0 +1,258 @@
+/*
+ * ChunkedConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.Buffer;
+
+/**
+ * The <code>ChunkedConsumer</code> is reads an decodes a stream
+ * using the chunked transfer coding. This is used so that any data
+ * sent in the chunked transfer coding can be decoded. All bytes are
+ * appended to an internal buffer so that they can be read without
+ * having to parse the encoding.
+ * <pre>
+ *
+ * length := 0
+ * read chunk-size, chunk-extension (if any) and CRLF
+ * while (chunk-size &gt; 0) {
+ * read chunk-data and CRLF
+ * append chunk-data to entity-body
+ * length := length + chunk-size
+ * read chunk-size and CRLF
+ * }
+ * read entity-header
+ * while (entity-header not empty) {
+ * append entity-header to existing header fields
+ * read entity-header
+ * }
+ *
+ * </pre>
+ * The above algorithm is taken from RFC 2616 section 19.4.6. This
+ * coding scheme is used in HTTP pipelines so that dynamic content,
+ * that is, content with which a length cannot be determined does
+ * not require a connection close to delimit the message body.
+ *
+ * @author Niall Gallagher
+ */
+public class ChunkedConsumer extends UpdateConsumer {
+
+ /**
+ * This is used to create the internal buffer for the body.
+ */
+ private Allocator allocator;
+
+ /**
+ * This is the internal buffer used to capture the body read.
+ */
+ private Buffer buffer;
+
+ /**
+ * This is used to determine whether a full chunk has been read.
+ */
+ private boolean terminal;
+
+ /**
+ * This is used to determine if the zero length chunk was read.
+ */
+ private boolean last;
+
+ /**
+ * This is used to accumulate the bytes of the chunk size line.
+ */
+ private byte line[];
+
+ /**
+ * This is the number of bytes appended to the line buffer.
+ */
+ private int count;
+
+ /**
+ * This is the number of bytes left in the current chunk.
+ */
+ private int chunk;
+
+ /**
+ * Constructor for the <code>ChunkedConsumer</code> object. This
+ * is used to create a consumer that reads chunked encoded data and
+ * appended that data in decoded form to an internal buffer so that
+ * it can be read in a clean decoded fromat.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ */
+ public ChunkedConsumer(Allocator allocator) {
+ this(allocator, 1024);
+ }
+
+ /**
+ * Constructor for the <code>ChunkedConsumer</code> object. This
+ * is used to create a consumer that reads chunked encoded data and
+ * appended that data in decoded form to an internal buffer so that
+ * it can be read in a clean decoded fromat.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param chunk this is the maximum size line allowed
+ */
+ private ChunkedConsumer(Allocator allocator, int chunk) {
+ this.line = new byte[chunk];
+ this.allocator = allocator;
+ }
+
+ /**
+ * This is used to acquire the body that has been consumed. 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>Attachment</code> objects.
+ * Each part can then be read as an individual message.
+ *
+ * @return the body that has been consumed by this instance
+ */
+ public Body getBody() {
+ return new BufferBody(buffer);
+ }
+
+ /**
+ * This method is used to append the contents of the array to the
+ * internal buffer. The appended bytes can be acquired from the
+ * internal buffer using an <code>InputStream</code>, or the text
+ * of the appended bytes can be acquired by encoding the bytes.
+ *
+ * @param array this is the array of bytes to be appended
+ * @param off this is the start offset in the array to read from
+ * @param len this is the number of bytes to write to the buffer
+ */
+ private void append(byte[] array, int off, int len) throws IOException {
+ if(buffer == null) {
+ buffer = allocator.allocate();
+ }
+ buffer.append(array, off, len);
+ }
+
+ /**
+ * This is used to process the bytes that have been read from the
+ * cursor. This will keep reading bytes from the stream until such
+ * time as the zero length chunk has been read from the stream. If
+ * the zero length chunk is encountered then the overflow count is
+ * returned so it can be used to reset the cursor.
+ *
+ * @param array this is a chunk read from the cursor
+ * @param off this is the offset within the array the chunk starts
+ * @param size this is the number of bytes within the array
+ *
+ * @return this returns the number of bytes overflow that is read
+ */
+ @Override
+ protected int update(byte[] array, int off, int size) throws IOException {
+ int mark = off + size;
+
+ while(off < mark){
+ if(terminal || last) {
+ while(off < mark) {
+ if(array[off++] == '\n') { // CR[LF]
+ if(last) { // 0; CRLFCR[LF]
+ finished = true;
+ return mark - off;
+ }
+ terminal = false;
+ break;
+ }
+ }
+ } else if(chunk == 0) {
+ while(chunk == 0) {
+ if(off >= mark) {
+ break;
+ } else if(array[off++] == '\n') { // CR[LF]
+ parse();
+
+ if(chunk == 0) { // 0; CR[LF]CRLF
+ last = true;
+ break;
+ }
+ } else {
+ line[count++] = array[off-1];
+ }
+ }
+ } else {
+ int write = Math.min(mark - off, chunk);
+
+ append(array, off, write);
+ chunk -= write;
+ off += write;
+
+ if(chunk == 0) { // []CRLF
+ terminal = true;
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * This method is used to convert the size in hexidecimal to a
+ * decimal <code>int</code>. This will use the specified number
+ * of bytes from the internal buffer and parse each character
+ * read as a hexidecimal character. This stops interpreting the
+ * size line when a non-hexidecimal character is encountered.
+ */
+ private void parse() throws IOException {
+ int off = 0;
+
+ while(off < count) {
+ int octet = toDecimal(line[off]);
+
+ if(octet < 0){
+ if(off < 1) {
+ throw new IOException("Invalid chunk size line");
+ }
+ break;
+ }
+ chunk <<= 4;
+ chunk ^= octet;
+ off++;
+ }
+ count = 0;
+ }
+
+ /**
+ * This performs a conversion from a character to an integer. If
+ * the character given, as a <code>byte</code>, is a hexidecimal
+ * char this will convert it into its integer equivelant. So a
+ * char of <code>A</code> is converted into <code>10</code>.
+ *
+ * @param octet this is an ISO 8869-1 hexidecimal character
+ *
+ * @return returns the hex character into its decinal value
+ */
+ private int toDecimal(byte octet){
+ if(octet >= 'A' && octet <= 'Z') {
+ return (octet - 'A') + 10;
+ }
+ if(octet >= '0' && octet <= '9') {
+ return octet - '0';
+ }
+ if(octet >= 'a' && octet <= 'f') {
+ return (octet - 'a') + 10;
+ }
+ return -1;
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/ConsumerFactory.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/ConsumerFactory.java
new file mode 100644
index 0000000..b3e5dc0
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/ConsumerFactory.java
@@ -0,0 +1,201 @@
+/*
+ * ConsumerFactory.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.message;
+
+import static org.simpleframework.http.Protocol.BOUNDARY;
+import static org.simpleframework.http.Protocol.CHUNKED;
+import static org.simpleframework.http.Protocol.MULTIPART;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.http.ContentType;
+
+/**
+ * The <code>ConsumerFactory</code> object is used to create a factory
+ * for creating consumers. This allows the request to determine the
+ * type of content sent and allows consumption of the request body in
+ * a the manner specified by the HTTP header. This will allow multipart
+ * and chunked content to be consumed from the pipeline.
+ *
+ * @author Niall Gallagher
+ */
+class ConsumerFactory {
+
+ /**
+ * This is used to allocate the memory associated with the body.
+ */
+ protected Allocator allocator;
+
+ /**
+ * This is the header associated with the request body consumed.
+ */
+ protected Segment segment;
+
+ /**
+ * Constructor for the <code>ConsumerFactory</code> object. This
+ * will create a factory that makes use of the HTTP header in order
+ * to determine the type of the body that is to be consumed.
+ *
+ * @param allocator this is the allocator used to allocate memory
+ * @param segment this is the HTTP header used to determine type
+ */
+ public ConsumerFactory(Allocator allocator, Segment segment) {
+ this.allocator = allocator;
+ this.segment = segment;
+ }
+
+ /**
+ * This method is used to create a body consumer to read the body
+ * from the pipeline. This will examine the HTTP header associated
+ * with the body to determine how to consume the data. This will
+ * provide an empty consumer if no specific delimiter was provided.
+ *
+ * @return this returns the consumer used to consume the body
+ */
+ public BodyConsumer getInstance() {
+ long length = getContentLength();
+
+ if(length < 0) {
+ return getInstance(8192);
+ }
+ return getInstance(length);
+ }
+
+ /**
+ * This method is used to create a body consumer to read the body
+ * from the pipeline. This will examine the HTTP header associated
+ * with the body to determine how to consume the data. This will
+ * provide an empty consumer if no specific delimiter was provided.
+ *
+ * @param length this is the length of the body to be consumed
+ *
+ * @return this returns the consumer used to consume the body
+ */
+ public BodyConsumer getInstance(long length) {
+ byte[] boundary = getBoundary(segment);
+
+ if(isUpload(segment)) {
+ return new FileUploadConsumer(allocator, boundary, length);
+ }
+ if(isChunked(segment)) {
+ return new ChunkedConsumer(allocator);
+ }
+ if(isFixed(segment)) {
+ return new FixedLengthConsumer(allocator, length);
+ }
+ return new EmptyConsumer();
+ }
+
+ /**
+ * This is used to extract information from the HTTP header that
+ * can be used to determine the type of the body. This will look
+ * at the HTTP headers provided to find a specific token which
+ * enables it to determine how to consume the body.
+ *
+ * @param header this is the header associated with the body
+ *
+ * @return the boundary for a multipart upload body
+ */
+ protected byte[] getBoundary(Segment header) {
+ ContentType type = header.getContentType();
+
+ if(type != null) {
+ String token = type.getParameter(BOUNDARY);
+
+ if(token != null) {
+ return token.getBytes();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This is used to extract information from the HTTP header that
+ * can be used to determine the type of the body. This will look
+ * at the HTTP headers provided to find a specific token which
+ * enables it to determine how to consume the body.
+ *
+ * @param segment this is the header associated with the body
+ *
+ * @return true if the content type is that of a multipart body
+ */
+ protected boolean isUpload(Segment segment) {
+ ContentType type = segment.getContentType();
+
+ if(type != null) {
+ String token = type.getPrimary();
+
+ if(token.equals(MULTIPART)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This is used to extract information from the HTTP header that
+ * can be used to determine the type of the body. This will look
+ * at the HTTP headers provided to find a specific token which
+ * enables it to determine how to consume the body.
+ *
+ * @param segment this is the header associated with the body
+ *
+ * @return true if the body is to be consumed as a chunked body
+ */
+ protected boolean isChunked(Segment segment) {
+ String encoding = segment.getTransferEncoding();
+
+ if(encoding != null) {
+ if(encoding.equals(CHUNKED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This is used to extract information from the HTTP header that
+ * can be used to determine the type of the body. This will look
+ * at the HTTP headers provided to find a specific token which
+ * enables it to determine how to consume the body.
+ *
+ * @param segment this is the header associated with the body
+ *
+ * @return true if there was a content length in the header
+ */
+ protected boolean isFixed(Segment segment) {
+ long length = segment.getContentLength();
+
+ if(length > 0) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 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
+ */
+ protected long getContentLength() {
+ return segment.getContentLength();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/ContentConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/ContentConsumer.java
new file mode 100644
index 0000000..76742c2
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/ContentConsumer.java
@@ -0,0 +1,226 @@
+/*
+ * ContentConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.Buffer;
+import org.simpleframework.http.Part;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>ContentConsumer</code> object represents a consumer for
+ * a multipart body part. This will read the contents of the cursor
+ * until such time as it reads the terminal boundary token, which is
+ * used to frame the content. Once the boundary token has been read
+ * this will add itself as a part to a part list. This part list can
+ * then be used with the HTTP request to examine and use the part.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.message.PartConsumer
+ */
+class ContentConsumer extends UpdateConsumer {
+
+ /**
+ * This represents the start of the boundary token for the body.
+ */
+ private static final byte[] START = { '\r', '\n', '-', '-' };
+
+ /**
+ * This is the part list that this part is to be added to.
+ */
+ private PartSeries series;
+
+ /**
+ * This is used to allocate the internal buffer when required.
+ */
+ private Allocator allocator;
+
+ /**
+ * Represents the HTTP headers that were provided for the part.
+ */
+ private Segment segment;
+
+ /**
+ * This is the internal buffer used to house the part body.
+ */
+ private Buffer buffer;
+
+ /**
+ * Represents the message boundary that terminates the part body.
+ */
+ private byte[] boundary;
+
+ /**
+ * This is used to determine if the start token had been read.
+ */
+ private int start;
+
+ /**
+ * This is used to determine how many boundary tokens are read.
+ */
+ private int seek;
+
+ /**
+ * Constructor for the <code>ContentConsumer</code> object. This
+ * is used to create a consumer that reads the body of a part in
+ * a multipart request body. The terminal token must be provided
+ * so that the end of the part body can be determined.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param segment this represents the headers for the part body
+ * @param series this is the part list that this body belongs in
+ * @param boundary this is the message boundary for the body part
+ */
+ public ContentConsumer(Allocator allocator, Segment segment, PartSeries series, byte[] boundary) {
+ this.allocator = allocator;
+ this.boundary = boundary;
+ this.segment = segment;
+ this.series = series;
+ }
+
+ /**
+ * 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 new BufferBody(buffer);
+ }
+
+ /**
+ * This is used to acquire the part for this HTTP entity. This
+ * will return a part which can be used to read the content of
+ * the message, the part created contains the contents of the
+ * body and the headers associated with it.
+ *
+ * @return the part provided by the HTTP request message
+ */
+ public Part getPart() {
+ return new BufferPart(segment, buffer);
+ }
+
+ /**
+ * This method is used to append the contents of the array to the
+ * internal buffer. The appended bytes can be acquired from the
+ * internal buffer using an <code>InputStream</code>, or the text
+ * of the appended bytes can be acquired by encoding the bytes.
+ *
+ * @param array this is the array of bytes to be appended
+ * @param off this is the start offset in the array to read from
+ * @param len this is the number of bytes to write to the buffer
+ */
+ private void append(byte[] array, int off, int len) throws IOException {
+ if(buffer == null) {
+ buffer = allocator.allocate();
+ }
+ buffer.append(array, off, len);
+ }
+
+ /**
+ * This is used to push the start and boundary back on to the
+ * cursor. Pushing the boundary back on to the cursor is required
+ * to ensure that the next consumer will have valid data to
+ * read from it. Simply resetting the boundary is not enough as
+ * this can cause an infinite loop if the connection is bad.
+ *
+ * @param cursor this is the cursor used by this consumer
+ */
+ @Override
+ protected void commit(ByteCursor cursor) throws IOException {
+ cursor.push(boundary);
+ cursor.push(START);
+ }
+
+ /**
+ * This is used to process the bytes that have been read from the
+ * cursor. This will search for the boundary token within the body
+ * of the message part, when it is found this will returns the
+ * number of bytes that represent the overflow.
+ *
+ * @param array this is a chunk read from the cursor
+ * @param off this is the offset within the array the chunk starts
+ * @param size this is the number of bytes within the array
+ *
+ * @return this returns the number of bytes overflow that is read
+ */
+ @Override
+ protected int update(byte[] array, int off, int size) throws IOException {
+ int skip = start + seek; // did we skip previously
+ int last = off + size;
+ int next = start;
+ int mark = off;
+
+ while(off < last) {
+ if(start == START.length) { // search for boundary
+ if(array[off++] != boundary[seek++]) { // boundary not found
+ if(skip > 0) {
+ append(START, 0, next); // write skipped start
+ append(boundary, 0, skip - next); // write skipped boundary
+ }
+ skip = start = seek = 0; // reset scan position
+ }
+ if(seek == boundary.length) { // boundary found
+ int excess = seek + start; // boundary bytes read
+ int total = off - mark; // total bytes read
+ int valid = total - excess; // body bytes read
+
+ finished = true;
+
+ if(valid > 0) {
+ append(array, mark, valid);
+ }
+ Part part = getPart();
+
+ if(part != null) {
+ series.addPart(part);
+ }
+ return size - total; // remaining excluding boundary
+ }
+ } else {
+ byte octet = array[off++]; // current
+
+ if(octet != START[start++]) {
+ if(skip > 0) {
+ append(START, 0, next); // write skipped start
+ }
+ skip = start = 0; // reset
+
+ if(octet == START[0]) { // is previous byte the start
+ start++;
+ }
+ }
+ }
+ }
+ int excess = seek + start; // boundary bytes read
+ int total = off - mark; // total bytes read
+ int valid = total - excess; // body bytes read
+
+ if(valid > 0) { // can we append processed data
+ append(array, mark, valid);
+ }
+ return 0;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/ContinueDispatcher.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/ContinueDispatcher.java
new file mode 100644
index 0000000..56f6472
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/ContinueDispatcher.java
@@ -0,0 +1,88 @@
+/*
+ * ContinueDispatcher.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.message;
+
+import static org.simpleframework.http.core.ContainerEvent.DISPATCH_CONTINUE;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>ContinueDispatcher</code> object is used to send the HTTP
+ * 100 continue status if required. This is delivered to the client
+ * to tell the client that the server is willing to accept the request
+ * body. Once this is sent the transport will likely wait until there
+ * is a read ready event.
+ *
+ * @author Niall Gallagher
+ */
+class ContinueDispatcher {
+
+ /**
+ * This is the status code that is sent to prompt the client.
+ */
+ private static final byte[] STATUS = { 'H', 'T','T', 'P', '/','1','.', '1',' ', '1','0','0',' '};
+
+ /**
+ * This is the optional description for the expect status code.
+ */
+ private static final byte[] MESSAGE = {'C','o','n','t','i','n','u','e', '\r','\n','\r','\n'};
+
+ /**
+ * This is the writer that is used to deliver the continue.
+ */
+ private final ByteWriter writer;
+
+ /**
+ * This is the trace used to capture a continue response if any.
+ */
+ private final Trace trace;
+
+ /**
+ * Constructor for the <code>ContinueDispatcher</code> object. This
+ * will create an object that will deliver the continue status code.
+ * Because the transport performs an asynchronous write this will
+ * not block the execution of this method and delay execution.
+ *
+ * @param channel this is the channel used to deliver the prompt
+ */
+ public ContinueDispatcher(Channel channel) {
+ this.writer = channel.getWriter();
+ this.trace = channel.getTrace();
+ }
+
+ /**
+ * This will execute the continue if the header contains the
+ * expectation header. If there is no expectation then this will
+ * return without sending anything back to the connected client.
+ *
+ * @param header this is the header read from the channel
+ */
+ public void execute(Header header) throws IOException {
+ if(header.isExpectContinue()) {
+ trace.trace(DISPATCH_CONTINUE);
+ writer.write(STATUS);
+ writer.write(MESSAGE);
+ writer.flush();
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyConsumer.java
new file mode 100644
index 0000000..9fb8145
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyConsumer.java
@@ -0,0 +1,69 @@
+/*
+ * EmptyConsumer.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.message;
+
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>EmptyConsumer</code> object is used to represent a body
+ * of zero length. This is the most common body consumer created as
+ * it represents the body for GET messages that have nothing within
+ * the body part.
+ *
+ * @author Niall Gallagher
+ */
+public class EmptyConsumer implements BodyConsumer {
+
+ /**
+ * This is used to acquire the body that has been consumed. 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>Attachment</code> objects.
+ * Each part can then be read as an individual message.
+ *
+ * @return the body that has been consumed by this instance
+ */
+ public Body getBody() {
+ return new BufferBody();
+ }
+
+ /**
+ * This method will not consume any bytes from the cursor. This
+ * ensures that the next byte read from the stream is the first
+ * character of the next HTTP message within the pipeline.
+ *
+ * @param cursor this is the cursor which will not be read from
+ */
+ public void consume(ByteCursor cursor) {
+ return;
+ }
+
+ /**
+ * This will return true immediately. Because the empty consumer
+ * represents a zero length body and no bytes are read from the
+ * cursor, this should not be processed and return finished.
+ *
+ * @return this will always return true for the zero length body
+ */
+ public boolean isFinished() {
+ return true;
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyInputStream.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyInputStream.java
new file mode 100644
index 0000000..2d1f9ff
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/EmptyInputStream.java
@@ -0,0 +1,44 @@
+/*
+ * EmptyInputStream.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.message;
+
+import java.io.InputStream;
+
+/**
+ * The <code>EmptyInputStream</code> object provides a stream that
+ * is immediately empty. Each read method with this input stream
+ * will return a -1 value indicating that the stream has come to an
+ * end and no more data can be read from it.
+ *
+ * @author Niall Gallagher
+ */
+class EmptyInputStream extends InputStream {
+
+ /**
+ * This is used to provide a -1 value when an attempt is made to
+ * read from the stream. Implementing this method as so also
+ * ensures that all the other read methods return a -1 value.
+ *
+ * @return this returns a -1 when an attempt is made to read
+ */
+ public int read() {
+ return -1;
+ }
+
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/Entity.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/Entity.java
new file mode 100644
index 0000000..6668eeb
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/Entity.java
@@ -0,0 +1,75 @@
+/*
+ * Entity.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.message;
+
+import org.simpleframework.transport.Channel;
+
+/**
+ * The <code>Entity</code> object is used to represent the HTTP entity
+ * received from the client. The entity contains a header and body as
+ * well as the underlying <code>Channel</code> for the connection. If
+ * there is no body with the entity this will provide an empty body
+ * object which provides a zero length sequence of bytes.
+ *
+ * @author Niall Gallagher
+ */
+public interface Entity {
+
+ /**
+ * 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
+ */
+ long getTime();
+
+ /**
+ * 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
+ */
+ Body getBody();
+
+ /**
+ * 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
+ */
+ Header getHeader();
+
+ /**
+ * 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
+ */
+ Channel getChannel();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/EntityConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/EntityConsumer.java
new file mode 100644
index 0000000..9cf12fb
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/EntityConsumer.java
@@ -0,0 +1,184 @@
+/*
+ * EntityConsumer.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.message;
+
+import static org.simpleframework.http.core.ContainerEvent.BODY_FINISHED;
+import static org.simpleframework.http.core.ContainerEvent.HEADER_FINISHED;
+import static org.simpleframework.http.core.ContainerEvent.READ_BODY;
+import static org.simpleframework.http.core.ContainerEvent.READ_HEADER;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>EntityConsumer</code> object is used to consume data
+ * from a cursor and build a request entity. Each constituent part of
+ * the entity is consumed from the pipeline and can be acquired from
+ * this consumer object. The <code>Header</code> and <code>Body</code>
+ * can be used to extract the individual parts of the entity.
+ *
+ * @author Niall Gallagher
+ */
+public class EntityConsumer implements ByteConsumer {
+
+ /**
+ * This is used to determine if there a continue is expected.
+ */
+ protected ContinueDispatcher dispatcher;
+
+ /**
+ * This is used to create a body consumer for the entity.
+ */
+ protected ConsumerFactory factory;
+
+ /**
+ * This is used to consume the header for the request entity.
+ */
+ protected RequestConsumer header;
+
+ /**
+ * This is used to consume the body for the request entity.
+ */
+ protected BodyConsumer body;
+
+ /**
+ * This is used to trace the progress of the request consumption.
+ */
+ protected Trace trace;
+
+ /**
+ * Constructor for the <code>EntityConsumer</code> object. This
+ * is used to build an entity from the constituent parts. Once
+ * all of the parts have been consumed they are available from
+ * the exposed methods of this consumed instance.
+ *
+ * @param allocator this is used to allocate the memory used
+ * @param channel this is the channel used to send a response
+ */
+ public EntityConsumer(Allocator allocator, Channel channel) {
+ this.header = new RequestConsumer();
+ this.dispatcher = new ContinueDispatcher(channel);
+ this.factory = new ConsumerFactory(allocator, header);
+ this.trace = channel.getTrace();
+ }
+
+ /**
+ * 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>Attachment</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 body.getBody();
+ }
+
+ /**
+ * 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 header;
+ }
+
+ /**
+ * This consumes the header and body from the cursor. The header
+ * is consumed first followed by the body if there is any. There
+ * is a body of there is a Content-Length or a Transfer-Encoding
+ * header present. If there is no body then a substitute body
+ * is given which has an empty input stream.
+ *
+ * @param cursor used to consumed the bytes for the entity
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ while(cursor.isReady()) {
+ if(header.isFinished()) {
+ if(body == null) {
+ CharSequence sequence = header.getHeader();
+
+ trace.trace(HEADER_FINISHED, sequence);
+ body = factory.getInstance();
+ }
+ trace.trace(READ_BODY);
+ body.consume(cursor);
+
+ if(body.isFinished()) {
+ trace.trace(BODY_FINISHED);
+ break;
+ }
+ } else {
+ trace.trace(READ_HEADER);
+ header.consume(cursor);
+ }
+ }
+ if(header.isFinished()) {
+ if(body == null) {
+ CharSequence sequence = header.getHeader();
+
+ trace.trace(HEADER_FINISHED, sequence);
+ dispatcher.execute(header);
+ body = factory.getInstance();
+ }
+ }
+ }
+
+ /**
+ * This is determined finished when the body has been consumed.
+ * If only the header has been consumed then the body will be
+ * created using the header information, the body is then read
+ * from the cursor, which may read nothing for an empty body.
+ *
+ * @return this returns true if the entity has been built
+ */
+ public boolean isFinished() {
+ if(header.isFinished()) {
+ if(body == null) {
+ CharSequence sequence = header.getHeader();
+
+ trace.trace(HEADER_FINISHED, sequence);
+ body = factory.getInstance();
+ }
+ return body.isFinished();
+ }
+ return false;
+
+ }
+
+ /**
+ * This is used to determine if the header has finished. Exposing
+ * this method ensures the entity consumer can be used to determine
+ * if the header for the entity can be consumed before fully
+ * processing the entity body of the request message.
+ *
+ * @return determines if the header has been fully consumed
+ */
+ public boolean isHeaderFinished() {
+ return header.isFinished();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/FileUploadConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/FileUploadConsumer.java
new file mode 100644
index 0000000..8318013
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/FileUploadConsumer.java
@@ -0,0 +1,272 @@
+/*
+ * FileUploadConsumer.java February 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.message;
+
+import java.io.IOException;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>FileUploadConsumer</code> object is used to consume a
+ * list of parts encoded in the multipart format. This is can consume
+ * any number of parts from a cursor. Each part consumed is added to an
+ * internal part list which can be used to acquire the contents of the
+ * upload and inspect the headers provided for each uploaded part. To
+ * ensure that only a fixed number of bytes are consumed this wraps
+ * the provided cursor with a counter to ensure reads a limited amount.
+ *
+ * @author Niall Gallagher
+ */
+public class FileUploadConsumer implements BodyConsumer {
+
+ /**
+ * This is used to read and parse the contents of the part series.
+ */
+ private final BodyConsumer consumer;
+
+ /**
+ * This counts the number of bytes remaining the the part series.
+ */
+ private final AtomicLong count;
+
+ /**
+ * Constructor for the <code>FileUploadConsumer</code> object.
+ * This is used to create an object that read a series of parts
+ * from a fixed length body. When consuming the body this will not
+ * read any more than the content length from the cursor.
+ *
+ * @param allocator this is the allocator used to allocate buffers
+ * @param boundary this is the boundary that is used by this
+ * @param length this is the number of bytes for this part series
+ */
+ public FileUploadConsumer(Allocator allocator, byte[] boundary, long length) {
+ this.consumer = new PartSeriesConsumer(allocator, boundary, length);
+ this.count = new AtomicLong(length);
+ }
+
+ /**
+ * This is used to acquire the body that has been consumed. 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 that has been consumed by this instance
+ */
+ public Body getBody() {
+ return consumer.getBody();
+ }
+
+ /**
+ * This method is used to consume bytes from the provided cursor.
+ * Consuming of bytes from the cursor should be done in such a
+ * way that it does not block. So typically only the number of
+ * ready bytes in the <code>ByteCursor</code> object should be
+ * read. If there are no ready bytes then this will return.
+ *
+ * @param cursor used to consume the bytes from the HTTP pipeline
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ ByteCounter counter = new ByteCounter(cursor);
+
+ while(counter.isReady()) {
+ if(consumer.isFinished()) {
+ break;
+ }
+ consumer.consume(counter);
+ }
+ }
+
+ /**
+ * This is used to determine whether the consumer has finished
+ * reading. The consumer is considered finished if it has read a
+ * terminal token or if it has exhausted the stream and can not
+ * read any more. Once finished the consumed bytes can be parsed.
+ *
+ * @return true if the consumer has finished reading its content
+ */
+ public boolean isFinished() {
+ long remaining = count.get();
+
+ if(consumer.isFinished()) {
+ return true;
+ }
+ return remaining <= 0;
+ }
+
+ /**
+ * The <code>ByteCounter</code> is a wrapper for a cursor that can
+ * be used to restrict the number of bytes consumed. This will
+ * count the bytes consumed and ensure that any requested data is
+ * restricted to a chunk less than or equal to the remaining bytes.
+ */
+ private class ByteCounter implements ByteCursor {
+
+ /**
+ * This is the cursor that this counter will delegate to.
+ */
+ private final ByteCursor cursor;
+
+ /**
+ * Constructor for the <code>Counter</code> object. This is used
+ * to create a special cursor that counts the bytes read and
+ * limits reads to the remaining bytes left in the part series.
+ *
+ * @param cursor this is the cursor that is delegated to
+ */
+ public ByteCounter(ByteCursor cursor) {
+ this.cursor = cursor;
+ }
+
+ /**
+ * Determines whether the cursor is still open. The cursor is
+ * considered open if there are still bytes to read. If there is
+ * still bytes buffered and the underlying transport is closed
+ * then the cursor is still considered open.
+ *
+ * @return true if the read method does not return a -1 value
+ */
+ public boolean isOpen() throws IOException {
+ return cursor.isOpen();
+ }
+
+ /**
+ * Determines whether the cursor is ready for reading. When the
+ * cursor is ready then it guarantees that some amount of bytes
+ * can be read from the underlying stream without blocking.
+ *
+ * @return true if some data can be read without blocking
+ */
+ public boolean isReady() throws IOException {
+ long limit = count.get();
+
+ if(limit > 0) {
+ return cursor.isReady();
+ }
+ return false;
+ }
+
+ /**
+ * Provides the number of bytes that can be read from the stream
+ * without blocking. This is typically the number of buffered or
+ * available bytes within the stream. When this reaches zero then
+ * the cursor may perform a blocking read.
+ *
+ * @return the number of bytes that can be read without blocking
+ */
+ public int ready() throws IOException {
+ int limit = (int)count.get();
+ int ready = cursor.ready();
+
+ if(ready > limit) {
+ return limit;
+ }
+ return ready;
+ }
+
+ /**
+ * Reads a block of bytes from the underlying stream. This will
+ * read up to the requested number of bytes from the underlying
+ * stream. If there are no ready bytes on the stream this can
+ * return zero, representing the fact that nothing was read.
+ *
+ * @param data this is the array to read the bytes in to
+ *
+ * @return this returns the number of bytes read from the stream
+ */
+ public int read(byte[] data) throws IOException {
+ return read(data, 0, data.length);
+ }
+
+ /**
+ * Reads a block of bytes from the underlying stream. This will
+ * read up to the requested number of bytes from the underlying
+ * stream. If there are no ready bytes on the stream this can
+ * return zero, representing the fact that nothing was read.
+ *
+ * @param data this is the array to read the bytes in to
+ * @param off this is the offset to begin writing the bytes to
+ * @param len this is the number of bytes that are requested
+ *
+ * @return this returns the number of bytes read from the stream
+ */
+ public int read(byte[] data, int off, int len) throws IOException {
+ int limit = (int)count.get();
+ int size = Math.min(limit, len);
+ int chunk = cursor.read(data, off, size);
+
+ if(chunk > 0) {
+ count.addAndGet(-chunk);
+ }
+ return chunk;
+ }
+
+ /**
+ * Pushes the provided data on to the cursor. Data pushed on to
+ * the cursor will be the next data read from the cursor. This
+ * complements the <code>reset</code> method which will reset
+ * the cursors position on a stream. Allowing data to be pushed
+ * on to the cursor allows more flexibility.
+ *
+ * @param data this is the data to be pushed on to the cursor
+ */
+ public void push(byte[] data) throws IOException {
+ push(data, 0, data.length);
+ }
+
+ /**
+ * Pushes the provided data on to the cursor. Data pushed on to
+ * the cursor will be the next data read from the cursor. This
+ * complements the <code>reset</code> method which will reset
+ * the cursors position on a stream. Allowing data to be pushed
+ * on to the cursor allows more flexibility.
+ *
+ * @param data this is the data to be pushed on to the cursor
+ * @param off this is the offset to begin reading the bytes
+ * @param len this is the number of bytes that are to be used
+ */
+ public void push(byte[] data, int off, int len) throws IOException {
+ if(len > 0) {
+ count.addAndGet(len);
+ }
+ cursor.push(data, off, len);
+ }
+
+ /**
+ * Moves the cursor backward within the stream. This ensures
+ * that any bytes read from the last read can be pushed back
+ * in to the stream so that they can be read again. This will
+ * throw an exception if the reset can not be performed.
+ *
+ * @param len this is the number of bytes to reset back
+ *
+ * @return this is the number of bytes that have been reset
+ */
+ public int reset(int len) throws IOException {
+ int reset = cursor.reset(len);
+
+ if(reset > 0) {
+ count.addAndGet(reset);
+ }
+ return reset;
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/FixedLengthConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/FixedLengthConsumer.java
new file mode 100644
index 0000000..5358d4b
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/FixedLengthConsumer.java
@@ -0,0 +1,128 @@
+/*
+ * FixedLengthConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.Buffer;
+
+/**
+ * The <code>FixedLengthConsumer</code> object reads a fixed number of
+ * bytes from a cursor. This is typically used when the Content-Length
+ * header is used as the body delimiter. In order to determine when
+ * the full body has been consumed this counts the bytes read. Once
+ * all the bytes have been read any overflow will be reset. All of the
+ * bytes read are appended to the internal buffer so they can be read.
+ *
+ * @author Niall Gallagher
+ */
+public class FixedLengthConsumer extends UpdateConsumer {
+
+ /**
+ * This is the allocator used to allocate the buffer used.
+ */
+ private Allocator allocator;
+
+ /**
+ * This is the internal buffer used to accumulate the body.
+ */
+ private Buffer buffer;
+
+ /**
+ * This is the number of bytes to be consumed from the cursor.
+ */
+ private long limit;
+
+ /**
+ * Constructor for the <code>FixedLengthConsumer</code> object. This
+ * is used to create a consumer that reads a fixed number of bytes
+ * from a cursor and accumulates those bytes in an internal buffer
+ * so that it can be read at a later stage.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param limit this is the number of bytes that are to be read
+ */
+ public FixedLengthConsumer(Allocator allocator, long limit) {
+ this.allocator = allocator;
+ this.limit = limit;
+ }
+
+
+ /**
+ * This is used to acquire the body that has been consumed. 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>Attachment</code> objects.
+ * Each part can then be read as an individual message.
+ *
+ * @return the body that has been consumed by this instance
+ */
+ public Body getBody() {
+ return new BufferBody(buffer);
+ }
+
+ /**
+ * This method is used to append the contents of the array to the
+ * internal buffer. The appended bytes can be acquired from the
+ * internal buffer using an <code>InputStream</code>, or the text
+ * of the appended bytes can be acquired by encoding the bytes.
+ *
+ * @param array this is the array of bytes to be appended
+ * @param off this is the start offset in the array to read from
+ * @param len this is the number of bytes to write to the buffer
+ */
+ private void append(byte[] array, int off, int len) throws IOException {
+ if(buffer == null) {
+ buffer = allocator.allocate(limit);
+ }
+ buffer.append(array, off, len);
+ }
+
+ /**
+ * This is used to process the bytes that have been read from the
+ * cursor. This will count the number of bytes read, once all of
+ * the bytes that form the body have been read this returns the
+ * number of bytes that represent the overflow.
+ *
+ * @param array this is a chunk read from the cursor
+ * @param off this is the offset within the array the chunk starts
+ * @param count this is the number of bytes within the array
+ *
+ * @return this returns the number of bytes overflow that is read
+ */
+ @Override
+ protected int update(byte[] array, int off, int count) throws IOException {
+ int mark = (int)limit;
+
+ if(count >= limit) {
+ append(array, off, mark);
+ finished = true;
+ limit = 0;
+ return count - mark;
+ }
+ if(count > 0) {
+ append(array, off, count);
+ limit -= count;
+ }
+ return 0;
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/Header.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/Header.java
new file mode 100644
index 0000000..4b79716
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/Header.java
@@ -0,0 +1,213 @@
+/*
+ * Header.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.message;
+
+import java.util.List;
+import java.util.Locale;
+
+import org.simpleframework.http.Address;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Query;
+
+/**
+ * This is a <code>Header</code> object that is used to represent a
+ * basic form for the HTTP request message. This is used to extract
+ * values such as the request line and header values from the request
+ * message. Access to header values is done case insensitively.
+ * <p>
+ * As well as providing the header values and request line values
+ * this will also provide convenience methods which enable the user
+ * to determine the length of the body this message header prefixes.
+ *
+ * @author Niall Gallagher
+ */
+public interface Header extends Segment {
+
+ /**
+ * This can be used to get the target specified for this HTTP
+ * request. This corresponds to the URI sent in the request
+ * line. Typically this will be the path part of the URI, but
+ * can be the full URI if the request is a proxy request.
+ *
+ * @return the target URI that this HTTP request specifies
+ */
+ String getTarget();
+
+ /**
+ * This method returns a <code>CharSequence</code> holding the data
+ * 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
+ */
+ CharSequence getHeader();
+
+ /**
+ * 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 target.
+ *
+ * @return this returns the address of the request line
+ */
+ Address getAddress();
+
+ /**
+ * This is used to acquire the path as extracted from the
+ * 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 URI.
+ *
+ * @return this returns the normalized path for the request
+ */
+ Path 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
+ */
+ Query 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 or POST method, but can be any valid alphabetic token.
+ *
+ * @return the HTTP method that this request has specified
+ */
+ String getMethod();
+
+ /**
+ * This can be used to get the major number from a HTTP version.
+ * The major version corresponds to the major protocol type, that
+ * is the 1 of a HTTP/1.1 version string. Typically the major
+ * type is 1, by can be 0 for HTTP/0.9 clients.
+ *
+ * @return the major version number for the HTTP message
+ */
+ int getMajor();
+
+ /**
+ * This can be used to get the minor number from a HTTP version.
+ * The minor version corresponds to the minor protocol type, that
+ * is the 0 of a HTTP/1.0 version string. This number is typically
+ * used to determine whether persistent connections are supported.
+ *
+ * @return the minor version number for the HTTP message
+ */
+ int 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
+ */
+ List<String> getNames();
+
+ /**
+ * 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
+ */
+ int getInteger(String 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
+ */
+ long getDate(String name);
+
+ /**
+ * 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
+ */
+ Cookie getCookie(String 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
+ */
+ List<Cookie> getCookies();
+
+ /**
+ * 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
+ */
+ List<Locale> getLocales();
+
+ /**
+ * This is used to determine if the header represents one that
+ * requires the HTTP/1.1 continue expectation. If the request
+ * does require this expectation then it should be send the
+ * 100 status code which prompts delivery of the message body.
+ *
+ * @return this returns true if a continue expectation exists
+ */
+ boolean isExpectContinue();
+
+ /**
+ * This method returns a string representing the header that was
+ * consumed by this consumer. For performance reasons it is better
+ * to acquire the character sequence representing the header as it
+ * does not require the allocation on new memory.
+ *
+ * @return this returns a string representation of this request
+ */
+ String toString();
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/HeaderConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/HeaderConsumer.java
new file mode 100644
index 0000000..55dab40
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/HeaderConsumer.java
@@ -0,0 +1,114 @@
+/*
+ * HeaderConsumer.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.message;
+
+import java.util.List;
+
+import org.simpleframework.http.Cookie;
+
+/**
+ * The <code>HeaderConsumer</code> object is used to consume a HTTP
+ * header from the cursor. This extends the segment consumer with
+ * methods specific to the header. Also this enables session cookies
+ * to be created using the cookies extracted from the header.
+ *
+ * @author Niall Gallagher
+ */
+public abstract class HeaderConsumer extends SegmentConsumer implements Header {
+
+ /**
+ * Constructor for the <code>HeaderConsumer</code> object. This
+ * is used to create a consumer capable of reading a header from
+ * a provided cursor. All methods of the <code>Header</coder>
+ * interface are implemented in this object.
+ */
+ protected HeaderConsumer() {
+ super();
+ }
+
+ /**
+ * 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 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 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 is used to acquire a cookie using 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();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/Message.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/Message.java
new file mode 100644
index 0000000..ac01dff
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/Message.java
@@ -0,0 +1,273 @@
+/*
+ * Message.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.message;
+
+import java.util.List;
+
+import org.simpleframework.http.Cookie;
+
+/**
+ * The <code>Message</code> object is used to store an retrieve the
+ * headers for both a request and response. Headers are stored and
+ * retrieved in a case insensitive manner according to RFC 2616.
+ * The message also allows multiple header values to be added to a
+ * single header name, headers such as Cookie and Set-Cookie can be
+ * added multiple times with different values.
+ *
+ * @author Niall Gallagher
+ */
+public interface Message {
+
+ /**
+ * This is used to acquire the names of the of the headers that
+ * have been set in the response. This can be used to acquire all
+ * header values by name that have been set within the response.
+ * If no headers have been set this will return an empty list.
+ *
+ * @return a list of strings representing the set header names
+ */
+ List<String> getNames();
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void setValue(String name, String value);
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void setInteger(String name, int value);
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTP date string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ void setDate(String name, long date);
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void addValue(String name, String value);
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getInteger</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ void addInteger(String name, int value);
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTPdate string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ void addDate(String name, long date);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ String getValue(String name);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @param name the HTTP message header to get the value from
+ * @param index gets the value at the index if there are multiple
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ String getValue(String name, int index);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the integer
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ int getInteger(String name);
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the long value
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ long getDate(String name);
+
+ /**
+ * This returns the <code>Cookie</code> object stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If the cookie does
+ * not exist under the specified name this will return null.
+ *
+ * @param name this is the name of the cookie to be retrieved
+ *
+ * @return returns the <code>Cookie</code> by the given name
+ */
+ Cookie getCookie(String name);
+
+ /**
+ * This returns all <code>Cookie</code> objects stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If there are no
+ * cookies then this will return an empty list.
+ *
+ * @return returns all the <code>Cookie</code> in the response
+ */
+ List<Cookie> getCookies();
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ * This is a convenience method that avoids cookie creation.
+ *
+ * @param name this is the cookie to be added to the response
+ * @param value this is the cookie value that is to be used
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ Cookie setCookie(String name, String value);
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ *
+ * @param cookie this is the cookie to be added to the response
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ Cookie setCookie(Cookie cookie);
+
+ /**
+ * 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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param name the name of the headers that are to be retrieved
+ *
+ * @return ordered list of tokens extracted from the header(s)
+ */
+ List<String> getValues(String 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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param list this is the list of individual header values
+ *
+ * @return ordered list of tokens extracted from the header(s)
+ */
+ List<String> getValues(List<String> list);
+
+ /**
+ * This is used to acquire all the individual header values from
+ * the message. The header values provided by this are unparsed
+ * and represent the actual string values that have been added to
+ * the message keyed by a given header name.
+ *
+ * @param name the name of the header to get the values for
+ *
+ * @return this returns a list of the values for the header name
+ */
+ List<String> getAll(String name);
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/MessageHeader.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/MessageHeader.java
new file mode 100644
index 0000000..b809efe
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/MessageHeader.java
@@ -0,0 +1,477 @@
+/*
+ * Message.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.message;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.simpleframework.common.KeyMap;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.parse.DateParser;
+import org.simpleframework.http.parse.ValueParser;
+
+/**
+ * The <code>Message</code> object is used to store an retrieve the
+ * headers for both a request and response. Headers are stored and
+ * retrieved in a case insensitive manner according to RFC 2616.
+ * The message also allows multiple header values to be added to a
+ * single header name, headers such as Cookie and Set-Cookie can be
+ * added multiple times with different values.
+ *
+ * @author Niall Gallagher
+ */
+public class MessageHeader implements Message {
+
+ /**
+ * This is used to store the cookies added to the HTTP header.
+ */
+ private final KeyMap<Cookie> cookies;
+
+ /**
+ * This is used to store multiple header values for a name.
+ */
+ private final KeyMap<Series> values;
+
+ /**
+ * This is used to store the individual names for the header.
+ */
+ private final KeyMap<String> names;
+
+ /**
+ * This is used to parse all date headers added to the message.
+ */
+ private final DateParser parser;
+
+ /**
+ * Constructor for the <code>Message</code> object. This is used
+ * to create a case insensitive means for storing HTTP header
+ * names and values. Dates can also be added to message as a
+ * long value and is converted to RFC 1123 compliant date string.
+ */
+ public MessageHeader() {
+ this.cookies = new KeyMap<Cookie>();
+ this.values = new KeyMap<Series>();
+ this.names = new KeyMap<String>();
+ this.parser = new DateParser();
+ }
+
+ /**
+ * This is used to acquire the names of the of the headers that
+ * have been set in the response. This can be used to acquire all
+ * header values by name that have been set within the response.
+ * If no headers have been set this will return an empty list.
+ *
+ * @return a list of strings representing the set header names
+ */
+ public List<String> getNames() {
+ return names.getValues();
+ }
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void setValue(String name, String value) {
+ List<String> list = getAll(name);
+
+ if(value == null) {
+ String token = name.toLowerCase();
+
+ values.remove(token);
+ names.remove(token);
+ } else {
+ list.clear();
+ list.add(value);
+ }
+ }
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void setInteger(String name, int value) {
+ setValue(name, String.valueOf(value));
+ }
+
+ /**
+ * This can be used to set a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void setLong(String name, long value) {
+ setValue(name, String.valueOf(value));
+ }
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTP date string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ * This will perform a <code>remove</code> using the issued header
+ * name before the header value is set.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ public void setDate(String name, long date) {
+ setValue(name, parser.convert(date));
+ }
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getValue</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void addValue(String name, String value) {
+ List<String> list = getAll(name);
+
+ if(value != null) {
+ list.add(value);
+ }
+ }
+
+ /**
+ * This can be used to add a HTTP message header to this object.
+ * The name and value of the HTTP message header will be used to
+ * create a HTTP message header object which can be retrieved using
+ * the <code>getInteger</code> in combination with the get methods.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param value the value the HTTP message header will have
+ */
+ public void addInteger(String name, int value) {
+ addValue(name, String.valueOf(value));
+ }
+
+ /**
+ * This is used as a convenience method for adding a header that
+ * needs to be parsed into a HTTPdate string. This will convert
+ * the date given into a date string defined in RFC 2616 sec 3.3.1.
+ *
+ * @param name the name of the HTTP message header to be added
+ * @param date the value constructed as an RFC 1123 date string
+ */
+ public void addDate(String name, long date) {
+ addValue(name, parser.convert(date));
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @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 getValue(name, 0);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the full string
+ * representing the named header value. If the named header does
+ * not exist then this will return a null value.
+ *
+ * @param name the HTTP message header to get the value from
+ * @param index this is the index to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public String getValue(String name, int index) {
+ List<String> list = getAll(name);
+
+ if(list.size() > index) {
+ return list.get(index);
+ }
+ return null;
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the integer
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public int getInteger(String name) {
+ String value = getValue(name);
+
+ if(value == null) {
+ return -1;
+ }
+ return Integer.parseInt(value);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the long
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public long getLong(String name) {
+ String value = getValue(name);
+
+ if(value == null) {
+ return -1L;
+ }
+ return Long.parseLong(value);
+ }
+
+ /**
+ * This can be used to get the value of the first message header
+ * that has the specified name. This will return the long value
+ * representing the named header value. If the named header does
+ * not exist then this will return a value of minus one, -1.
+ *
+ * @param name the HTTP message header to get the value from
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public long getDate(String name) {
+ String value = getValue(name);
+
+ if(value == null) {
+ return -1;
+ }
+ return parser.convert(value);
+ }
+
+ /**
+ * This returns the <code>Cookie</code> object stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If the cookie does
+ * not exist under the specified name this will return null.
+ *
+ * @param name this is the name of the cookie to be retrieved
+ *
+ * @return returns the <code>Cookie</code> by the given name
+ */
+ public Cookie getCookie(String name) {
+ return cookies.get(name);
+ }
+
+ /**
+ * This returns all <code>Cookie</code> objects stored under the
+ * specified name. This is used to retrieve cookies that have been
+ * set with the <code>setCookie</code> methods. If there are no
+ * cookies then this will return an empty list.
+ *
+ * @return returns all the <code>Cookie</code> in the response
+ */
+ public List<Cookie> getCookies() {
+ return cookies.getValues();
+ }
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ * This is a convenience method that avoids cookie creation.
+ *
+ * @param name this is the cookie to be added to the response
+ * @param value this is the cookie value that is to be used
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ public Cookie setCookie(String name, String value) {
+ return setCookie(new Cookie(name, value, true));
+ }
+
+ /**
+ * The <code>setCookie</code> method is used to set a cookie value
+ * with the cookie name. This will add a cookie to the response
+ * stored under the name of the cookie, when this is committed it
+ * will be added as a Set-Cookie header to the resulting response.
+ *
+ * @param cookie this is the cookie to be added to the response
+ *
+ * @return returns the cookie that has been set in the response
+ */
+ public Cookie setCookie(Cookie cookie) {
+ String name = cookie.getName();
+
+ if(name != null) {
+ cookies.put(name, cookie);
+ }
+ return cookie;
+ }
+
+ /**
+ * 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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param name the name of the headers that are to be retrieved
+ *
+ * @return ordered list of tokens extracted from the header(s)
+ */
+ public List<String> getValues(String name) {
+ return getValues(getAll(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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param list this is the list of individual header values
+ *
+ * @return ordered list of tokens extracted from the header(s)
+ */
+ public List<String> getValues(List<String> list) {
+ return new ValueParser(list).list();
+ }
+
+ /**
+ * This is used to acquire all the individual header values from
+ * the message. The header values provided by this are unparsed
+ * and represent the actual string values that have been added to
+ * the message keyed by a given header name.
+ *
+ * @param name the name of the header to get the values for
+ *
+ * @return this returns a list of the values for the header name
+ */
+ public List<String> getAll(String name) {
+ String token = name.toLowerCase();
+ Series series = values.get(token);
+
+ if(series == null) {
+ return getAll(name, token);
+ }
+ return series.getValues();
+ }
+
+ /**
+ * This is used to acquire all the individual header values from
+ * the message. The header values provided by this are unparsed
+ * and represent the actual string values that have been added to
+ * the message keyed by a given header name.
+ *
+ * @param name the name of the header to get the values for
+ * @param token this provides a lower case version of the header
+ *
+ * @return this returns a list of the values for the header name
+ */
+ private List<String> getAll(String name, String token) {
+ Series series = new Series();
+ String value = names.get(token);
+
+ if(value == null) {
+ names.put(token, name);
+ }
+ values.put(token, series);
+
+ return series.getValues();
+ }
+
+ /**
+ * The <code>Series</code> object is used to represent a list of
+ * HTTP header value for a given name. It allows multiple values
+ * to exist for a given header, such as the Cookie header. Most
+ * entries will contain a single value.
+ */
+ private class Series {
+
+ /**
+ * Contains the header values that belong to the entry name.
+ */
+ private List<String> value;
+
+ /**
+ * Constructor for the <code>Entry</code> object. The entry is
+ * created using the name of the HTTP header. Values can be
+ * added to the entry list in order to build up the header.
+ */
+ public Series() {
+ this.value = new LinkedList<String>();
+ }
+
+ /**
+ * This returns the list of header values associated with the
+ * header name. Each value is added as an individual header
+ * prefixed by the header name and a semicolon character.
+ *
+ * @return this returns the list of values for the header
+ */
+ public List<String> getValues() {
+ return value;
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartBodyConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartBodyConsumer.java
new file mode 100644
index 0000000..b30f28f
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartBodyConsumer.java
@@ -0,0 +1,129 @@
+/*
+ * PartBodyConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>PartBodyConsumer</code> object is used to consume a part
+ * the contents of a multipart body. This will consume the part and
+ * add it to a part list, once the part has been consumed and added
+ * to the part list a terminal token is consumed, which is a carriage
+ * return and line feed.
+ *
+ * @author Niall Gallagher
+ */
+class PartBodyConsumer implements BodyConsumer {
+
+ /**
+ * This is the token that is consumed after the content body.
+ */
+ private static final byte[] LINE = { '\r', '\n' };
+
+ /**
+ * This is used to consume the content from the multipart upload.
+ */
+ private ContentConsumer content;
+
+ /**
+ * This is used to consume the final terminal token from the part.
+ */
+ private ByteConsumer token;
+
+ /**
+ * Constructor for the <code>PartBodyConsumer</code> object. This
+ * is used to create a consumer that reads the body of a part in
+ * a multipart request body. The terminal token must be provided
+ * so that the end of the part body can be determined.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param segment this represents the headers for the part body
+ * @param boundary this is the message boundary for the body part
+ */
+ public PartBodyConsumer(Allocator allocator, Segment segment, byte[] boundary) {
+ this(allocator, segment, new PartData(), boundary);
+ }
+
+ /**
+ * Constructor for the <code>PartBodyConsumer</code> object. This
+ * is used to create a consumer that reads the body of a part in
+ * a multipart request body. The terminal token must be provided
+ * so that the end of the part body can be determined.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param segment this represents the headers for the part body
+ * @param series this is the part list that this body belongs in
+ * @param boundary this is the message boundary for the body part
+ */
+ public PartBodyConsumer(Allocator allocator, Segment segment, PartSeries series, byte[] boundary) {
+ this.content = new ContentConsumer(allocator, segment, series, boundary);
+ this.token = new TokenConsumer(allocator, LINE);
+ }
+
+ /**
+ * This is used to acquire the body that has been consumed. 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>Attachment</code> objects.
+ * Each part can then be read as an individual message.
+ *
+ * @return the body that has been consumed by this instance
+ */
+ public Body getBody() {
+ return content.getBody();
+ }
+
+ /**
+ * This is used to consume the part body from the cursor. This
+ * initially reads the body of the part, which represents the
+ * actual payload exposed via the <code>Part</code> interface
+ * once the payload has been consumed the terminal is consumed.
+ *
+ * @param cursor this is the cursor to consume the body from
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ while(cursor.isReady()) {
+ if(content.isFinished()) {
+ if(token.isFinished()) {
+ break;
+ }
+ token.consume(cursor);
+ } else {
+ content.consume(cursor);
+ }
+ }
+ }
+
+ /**
+ * This is used to determine whether the part body has been read
+ * from the cursor successfully. In order to determine if all of
+ * the bytes have been read successfully this will check to see
+ * of the terminal token had been consumed.
+ *
+ * @return true if the part body and terminal have been read
+ */
+ public boolean isFinished() {
+ return token.isFinished();
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartConsumer.java
new file mode 100644
index 0000000..cc54558
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartConsumer.java
@@ -0,0 +1,135 @@
+/*
+ * PartConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>PartConsumer</code> object is used to consume a part
+ * from a part list. A part consists of a header and a body, which
+ * can be either a simple chunk of data or another part list. This
+ * must be able to cope with either a simple body or a part list.
+ *
+ * @author Niall Gallagher
+ */
+class PartConsumer implements ByteConsumer {
+
+ /**
+ * This is used to consume the header message of the part.
+ */
+ private SegmentConsumer header;
+
+ /**
+ * This is used to consume the body data from the part.
+ */
+ private BodyConsumer body;
+
+ /**
+ * This is used to determine what type the body data is.
+ */
+ private PartFactory factory;
+
+ /**
+ * This is used to add the consumed parts to when finished.
+ */
+ private PartSeries series;
+
+ /**
+ * This is the current consumer used to read from the cursor.
+ */
+ private ByteConsumer current;
+
+ /**
+ * This is the terminal token that ends the part payload.
+ */
+ private byte[] terminal;
+
+ /**
+ * Constructor for the <code>PartConsumer</code> object. This is
+ * used to create a consumer used to read the contents of a part
+ * and the boundary that terminates the content. Any parts that
+ * are created by this are added to the provided part list.
+ *
+ * @param allocator this is the allocator used to creat buffers
+ * @param series this is the part list used to store the parts
+ * @param terminal this is the terminal token for the part
+ * @param length this is the length of the parent part series
+ */
+ public PartConsumer(Allocator allocator, PartSeries series, byte[] terminal, long length) {
+ this.header = new PartHeaderConsumer(allocator);
+ this.factory = new PartFactory(allocator, header, length);
+ this.terminal = terminal;
+ this.current = header;
+ this.series = series;
+ }
+
+ /**
+ * This is used to create a new body consumer used to consume the
+ * part body from for the list. This will ensure that the part
+ * data is created based on the part header consumed. The types
+ * of part supported are part lists and part body.
+ *
+ * @return this returns a consumed for the part content
+ */
+ private BodyConsumer getConsumer() {
+ return factory.getInstance(series, terminal);
+ }
+
+ /**
+ * This is used to consume the part body from the cursor. This
+ * initially reads the body of the part, which represents the
+ * actual payload exposed via the <code>Part</code> interface
+ * once the payload has been consumed the terminal is consumed.
+ *
+ * @param cursor this is the cursor to consume the body from
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ while(cursor.isReady()) {
+ if(header.isFinished()) {
+ if(body == null) {
+ body = getConsumer();
+ current = body;
+ } else {
+ if(body.isFinished())
+ break;
+ }
+ }
+ current.consume(cursor);
+ }
+ }
+
+ /**
+ * This is used to determine whether the part body has been read
+ * from the cursor successfully. In order to determine if all of
+ * the bytes have been read successfully this will check to see
+ * of the terminal token had been consumed.
+ *
+ * @return true if the part body and terminal have been read
+ */
+ public boolean isFinished() {
+ if(body != null) {
+ return body.isFinished();
+ }
+ return false;
+ }
+}
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartData.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartData.java
new file mode 100644
index 0000000..cf7a90a
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartData.java
@@ -0,0 +1,101 @@
+/*
+ * PartData.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.message;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.simpleframework.common.KeyMap;
+import org.simpleframework.http.Part;
+
+/**
+ * The <code>PartData</code> object represents an ordered list of
+ * parts that were uploaded within a HTTP entity body. This allows
+ * the parts to be iterated over, or if required accessed by name.
+ * In order to access the <code>Part</code> object by name it must
+ * have had a name within the Content-Disposition header.
+ *
+ * @author Niall Gallagher
+ */
+class PartData implements PartSeries {
+
+ /**
+ * This is the key map that is used to store the part objects.
+ */
+ private final KeyMap<Part> map;
+
+ /**
+ * This is the list of attachments for this part list object.
+ */
+ private final List<Part> list;
+
+ /**
+ * Constructor for the <code>PartData</code> object. This is used
+ * to create an order list of parts that is used by the request
+ * to access the individual parts uploaded with a HTTP body.
+ */
+ public PartData() {
+ this.list = new ArrayList<Part>();
+ this.map = new KeyMap<Part>();
+ }
+
+ /**
+ * This is used to acquire the attachments associated with this
+ * list. If no parts have been collected by this list then it
+ * will return an empty list. The order of the parts in the list
+ * are the insertion order for consistency.
+ *
+ * @return this returns the parts collected in iteration order
+ */
+ public List<Part> getParts() {
+ return list;
+ }
+
+ /**
+ * This is used to add a part to the list. The order the parts are
+ * added to the list is the iteration order. If the part has a name
+ * that is not null then it is added to an internal map using that
+ * name. This allows it to be accesses by name at a later time.
+ *
+ * @param part this is the part that is to be added to the list
+ *
+ * @return returns true if the list has changed due to the add
+ */
+ public boolean addPart(Part part) {
+ String name = part.getName();
+
+ if(name != null) {
+ map.put(name, part);
+ }
+ return list.add(part);
+ }
+
+ /**
+ * This method is used to acquire a <code>Part</code> from the list
+ * using a known name for the part. This is a convenient way to
+ * access a part when the name for the part is known.
+ *
+ * @param name this is the name of the part to acquire
+ *
+ * @return the named part or null if the part does not exist
+ */
+ public Part getPart(String name) {
+ return map.get(name);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryConsumer.java
new file mode 100644
index 0000000..8e1a38c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryConsumer.java
@@ -0,0 +1,112 @@
+/*
+ * PartEntryConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>PartEntryConsumer</code> object is used to consume each
+ * part from the part list. This is combines the task of consuming
+ * the part, which consists of a header and a body, and a boundary
+ * which identifies the end of the message content.
+ *
+ * @author Niall Gallagher
+ */
+class PartEntryConsumer implements ByteConsumer {
+
+ /**
+ * This is used to consume the boundary at the end of a part.
+ */
+ private final BoundaryConsumer boundary;
+
+ /**
+ * This is used to consume the actual part from the list.
+ */
+ private final ByteConsumer consumer;
+
+ /**
+ * Constructor for the <code>PartEntryConsumer</code> object. This
+ * is used to create a consumer that will read the message part
+ * and the boundary that terminates the part. All contents that
+ * are read are appended to an internal buffer.
+ *
+ * @param allocator this is the allocator used for the buffer
+ * @param series this is the list used to accumulate the parts
+ * @param terminal this is the terminal token for the part list
+ * @param length this is the length of the parent part series
+ */
+ public PartEntryConsumer(Allocator allocator, PartSeries series, byte[] terminal, long length) {
+ this.consumer = new PartConsumer(allocator, series, terminal, length);
+ this.boundary = new BoundaryConsumer(allocator, terminal);
+ }
+
+ /**
+ * This is used to consume the part body from the cursor. This
+ * initially reads the body of the part, which represents the
+ * actual content exposed via the <code>Part</code> interface
+ * once the content has been consumed the terminal is consumed.
+ *
+ * @param cursor this is the cursor to consume the body from
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ while(cursor.isReady()) {
+ if(!boundary.isFinished()) {
+ boundary.consume(cursor);
+ } else {
+ if(consumer.isFinished()) {
+ break;
+ }
+ if(boundary.isEnd()) {
+ break;
+ }
+ consumer.consume(cursor);
+ }
+ }
+ }
+
+ /**
+ * This is used to determine whether the part body has been read
+ * from the cursor successfully. In order to determine if all of
+ * the bytes have been read successfully this will check to see
+ * of the terminal token had been consumed.
+ *
+ * @return true if the part body and terminal have been read
+ */
+ public boolean isFinished() {
+ if(boundary.isEnd()) {
+ return true;
+ }
+ return consumer.isFinished();
+ }
+
+ /**
+ * This is used to determine whether the terminal token read is
+ * the final terminal token. The final terminal token is a
+ * normal terminal token, however it ends with two hyphens and
+ * a carriage return line feed, this ends the part list.
+ *
+ * @return true if this was the last part within the list
+ */
+ public boolean isEnd() {
+ return boundary.isEnd();
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryFactory.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryFactory.java
new file mode 100644
index 0000000..aba5738
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartEntryFactory.java
@@ -0,0 +1,84 @@
+/*
+ * PartEntryFactory.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.message;
+
+import org.simpleframework.common.buffer.Allocator;
+
+/**
+ * This <code>PartEntryFactory</code> object provides a factory for
+ * creating part entry consumers. The part entry consumers created
+ * read individual entries from a list of parts within a stream.
+ * This is basically a convenience factory for the list consumer.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.message.PartSeriesConsumer
+ */
+class PartEntryFactory {
+
+ /**
+ * This is used to accumulate all the parts of the upload.
+ */
+ private final PartSeries series;
+
+ /**
+ * This is used to allocate the buffers used by the entry.
+ */
+ private final Allocator allocator;
+
+ /**
+ * This is the terminal token used to delimiter the upload.
+ */
+ private final byte[] terminal;
+
+ /**
+ * This is the length of the parent part series body.
+ */
+ private final long length;
+
+ /**
+ * Constructor for the <code>PartEntryFactory</code> object.
+ * This is used to create a factory for entry consumers that
+ * can be used to read an entry from a part list.
+ *
+ * @param allocator this is the allocator used for buffers
+ * @param series this is the list of parts that are extracted
+ * @param terminal this is the terminal buffer to be used
+ * @param length this is the length of the parent part series
+ */
+ public PartEntryFactory(Allocator allocator, PartSeries series, byte[] terminal, long length) {
+ this.allocator = allocator;
+ this.terminal = terminal;
+ this.series = series;
+ this.length = length;
+ }
+
+
+ /**
+ * This creates a new part entry consumer that can be used to
+ * read the next part from the list. The consumer instantiated
+ * by this factory acquires the allocator, list and boundary
+ * from the enclosing part list consumer instance.
+ *
+ * @return a part entry consumer for this part list consumer
+ */
+ public PartEntryConsumer getInstance() {
+ return new PartEntryConsumer(allocator, series, terminal, length);
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartFactory.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartFactory.java
new file mode 100644
index 0000000..f394ba6
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartFactory.java
@@ -0,0 +1,78 @@
+/*
+ * PartFactory.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.message;
+
+import org.simpleframework.common.buffer.Allocator;
+
+/**
+ * The <code>PartFactory</code> represents a factory for creating the
+ * consumers that are used to read a multipart upload message. This
+ * supports two types of consumers for the multipart upload, lists
+ * and bodies. A part list is basically a collection of parts and or
+ * part lists. The part type is determined from the part header.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.message.PartSeriesConsumer
+ * @see org.simpleframework.http.message.PartBodyConsumer
+ */
+class PartFactory extends ConsumerFactory {
+
+ /**
+ * This is the overall length of the parent part series.
+ */
+ private final long length;
+
+ /**
+ * Constructor for the <code>PartFactory</code> object. This is
+ * used to create a factory using a buffer allocator, which will
+ * create a buffer for accumulating the entire message body,
+ * also to ensure the correct part type is created this requires
+ * the header information for the part.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param header this is used to determine the part type
+ * @param length this is the length of the parent part series
+ */
+ public PartFactory(Allocator allocator, Segment header, long length) {
+ super(allocator, header);
+ this.length = length;
+ }
+
+ /**
+ * This method is used to create the consumer given the list and
+ * boundary for the part. In order to determine the part type
+ * this will consult the header consumed for the part. Depending
+ * on whether it is a list or body a suitable consumer is created.
+ *
+ * @param series this is the list used to collect the parts
+ * @param boundary this is the boundary used to terminate the part
+ *
+ * @return this will return the consumer for the part body
+ */
+ public BodyConsumer getInstance(PartSeries series, byte[] boundary) {
+ byte[] terminal = getBoundary(segment);
+
+ if(isUpload(segment)) {
+ return new PartSeriesConsumer(allocator, series, terminal, length);
+ }
+ return new PartBodyConsumer(allocator, segment, series, boundary);
+ }
+}
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartHeaderConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartHeaderConsumer.java
new file mode 100644
index 0000000..7612d8d
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartHeaderConsumer.java
@@ -0,0 +1,85 @@
+/*
+ * PartHeaderConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.Buffer;
+
+/**
+ * The <code>PartHeaderConsumer</code> object is used to consume the
+ * header for a multipart message. This performs a parse of the
+ * HTTP headers within the message up to the terminal carriage return
+ * and line feed token. Once this had been read the contents of the
+ * header are appended to a buffer so they can be read later.
+ *
+ * @author Niall Gallagher
+ */
+class PartHeaderConsumer extends SegmentConsumer {
+
+ /**
+ * This is used to allocate the internal buffer for the header.
+ */
+ private Allocator allocator;
+
+ /**
+ * This is the internal buffer used to store the header.
+ */
+ private Buffer buffer;
+
+ /**
+ * Constructor for the <code>PartHeaderConsumer</code> object. An
+ * allocator is required so that the header consumer can create a
+ * buffer to store the contents of the consumed message.
+ *
+ * @param allocator this is the allocator used to create a buffer
+ */
+ public PartHeaderConsumer(Allocator allocator) {
+ this.allocator = allocator;
+ }
+
+ /**
+ * This is used to process the header consumer once all of the
+ * headers have been read. This will simply parse all of the
+ * headers and append the consumed bytes to the internal buffer.
+ * Appending the bytes ensures that the whole upload can be
+ * put back together as a single byte stream if required.
+ */
+ @Override
+ protected void process() throws IOException {
+ headers();
+ append();
+ }
+
+ /**
+ * This is used to allocate the internal buffer and append the
+ * consumed bytes to the buffer. Once the header is added to
+ * the internal buffer this is finished and the next part of
+ * the upload can be consumed.
+ */
+ private void append() throws IOException {
+ if(buffer == null) {
+ buffer = allocator.allocate(count);
+ }
+ buffer.append(array, 0, count);
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeries.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeries.java
new file mode 100644
index 0000000..c971d97
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeries.java
@@ -0,0 +1,68 @@
+/*
+ * PartSeries.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.message;
+
+import java.util.List;
+
+import org.simpleframework.http.Part;
+
+/**
+ * The <code>PartSeries</code> object represents an ordered list of
+ * parts that were uploaded within a HTTP entity body. This allows
+ * the parts to be iterated over, or if required accessed by name.
+ * In order to access the <code>Part</code> object by name it must
+ * have had a name within the Content-Disposition header.
+ *
+ * @author Niall Gallagher
+ */
+interface PartSeries {
+
+ /**
+ * This is used to acquire the attachments associated with this
+ * list. If no parts have been collected by this list then it
+ * will return an empty list. The order of the parts in the list
+ * are the insertion order for consistency.
+ *
+ * @return this returns the parts collected in iteration order
+ */
+ List<Part> getParts();
+
+ /**
+ * This is used to add a part to the list. The order the parts are
+ * added to the list is the iteration order. If the part has a name
+ * that is not null then it is added to an internal map using that
+ * name. This allows it to be accesses by name at a later time.
+ *
+ * @param part this is the part that is to be added to the list
+ *
+ * @return returns true if the list has changed due to the add
+ */
+ boolean addPart(Part part);
+
+ /**
+ * This method is used to acquire a <code>Part</code> from the list
+ * using a known name for the part. This is a convenient way to
+ * access a part when the name for the part is known.
+ *
+ * @param name this is the name of the part to acquire
+ *
+ * @return the named part or null if the part does not exist
+ */
+ Part getPart(String name);
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeriesConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeriesConsumer.java
new file mode 100644
index 0000000..c3a0417
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/PartSeriesConsumer.java
@@ -0,0 +1,165 @@
+/*
+ * PartSeriesConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.BufferAllocator;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>PartSeriesConsumer</code> object is used to consume a list
+ * of parts encoded in the multipart format. This is can consume any
+ * number of parts from a cursor. Each part consumed is added to an
+ * internal part list which can be used to acquire the contents of the
+ * upload and inspect the headers provided for each uploaded part. To
+ * ensure that only a fixed number of bytes are consumed this uses a
+ * content length for an internal buffer.
+ *
+ * @author Niall Gallagher
+ */
+class PartSeriesConsumer implements BodyConsumer {
+
+ /**
+ * This is used to consume individual parts from the part list.
+ */
+ private PartEntryConsumer consumer;
+
+ /**
+ * This is the factory that is used to create the consumers used.
+ */
+ private PartEntryFactory factory;
+
+ /**
+ * This is used to both allocate and buffer the part list body.
+ */
+ private BufferAllocator buffer;
+
+ /**
+ * This is used to accumulate all the parts of the upload.
+ */
+ private PartSeries series;
+
+ /**
+ * Constructor for the <code>PartSeriesConsumer</code> object. This
+ * will create a consumer that is capable of breaking an upload in
+ * to individual parts so that they can be accessed and used by
+ * the receiver of the HTTP request message.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param boundary this is the boundary used for the upload
+ */
+ public PartSeriesConsumer(Allocator allocator, byte[] boundary) {
+ this(allocator, boundary, 8192);
+ }
+
+ /**
+ * Constructor for the <code>PartSeriesConsumer</code> object. This
+ * will create a consumer that is capable of breaking an upload in
+ * to individual parts so that they can be accessed and used by
+ * the receiver of the HTTP request message.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param boundary this is the boundary used for the upload
+ * @param length this is the number of bytes the upload should be
+ */
+ public PartSeriesConsumer(Allocator allocator, byte[] boundary, long length) {
+ this(allocator, new PartData(), boundary, length);
+ }
+
+ /**
+ * Constructor for the <code>PartSeriesConsumer</code> object. This
+ * will create a consumer that is capable of breaking an upload in
+ * to individual parts so that they can be accessed and used by
+ * the receiver of the HTTP request message.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param boundary this is the boundary used for the upload
+ * @param series this is the part list used to accumulate the parts
+ */
+ public PartSeriesConsumer(Allocator allocator, PartSeries series, byte[] boundary) {
+ this(allocator, series, boundary, 8192);
+ }
+
+ /**
+ * Constructor for the <code>PartSeriesConsumer</code> object. This
+ * will create a consumer that is capable of breaking an upload in
+ * to individual parts so that they can be accessed and used by
+ * the receiver of the HTTP request message.
+ *
+ * @param allocator this is used to allocate the internal buffer
+ * @param series this is the part list used to accumulate the parts
+ * @param boundary this is the boundary used for the upload
+ * @param length this is the number of bytes the upload should be
+ */
+ public PartSeriesConsumer(Allocator allocator, PartSeries series, byte[] boundary, long length) {
+ this.buffer = new BufferAllocator(allocator, length);
+ this.consumer = new PartEntryConsumer(buffer, series, boundary, length);
+ this.factory = new PartEntryFactory(buffer, series, boundary, length);
+ this.series = series;
+ }
+
+ /**
+ * This is used to acquire the body that has been consumed. 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>Attachment</code> objects.
+ * Each part can then be read as an individual message.
+ *
+ * @return the body that has been consumed by this instance
+ */
+ public Body getBody() {
+ return new BufferBody(buffer, series);
+ }
+
+ /**
+ * This is used to consume the part list from the cursor. This
+ * initially reads the list of parts, which represents the
+ * actual content exposed via the <code>PartSeries</code> object,
+ * once the content has been consumed the terminal is consumed.
+ *
+ * @param cursor this is the cursor to consume the list from
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ while(cursor.isReady()) {
+ if(!consumer.isFinished()) {
+ consumer.consume(cursor);
+ } else {
+ if(!consumer.isEnd()) {
+ consumer = factory.getInstance();
+ } else {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This is used to determine whether the part body has been read
+ * from the cursor successfully. In order to determine if all of
+ * the bytes have been read successfully this will check to see
+ * of the terminal token had been consumed.
+ *
+ * @return true if the part body and terminal have been read
+ */
+ public boolean isFinished() {
+ return consumer.isEnd();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/RequestConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/RequestConsumer.java
new file mode 100644
index 0000000..0687271
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/RequestConsumer.java
@@ -0,0 +1,457 @@
+/*
+ * RequestConsumer.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.message;
+
+import java.util.List;
+
+import org.simpleframework.http.Address;
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Query;
+import org.simpleframework.http.parse.AddressParser;
+
+/**
+ * The <code>RequestConsumer</code> object is used to parse the HTTP
+ * request line followed by the HTTP message headers. This parses the
+ * request URI such that the query parameters and path are extracted
+ * and normalized. It performs this using external parsers, which
+ * will remove and escaped characters and normalize the path segments.
+ * Finally this exposes the HTTP version used using the major and
+ * minor numbers sent with the HTTP request.
+ *
+ * @author Niall Gallagher
+ */
+public class RequestConsumer extends HeaderConsumer {
+
+ /**
+ * This is the address parser used to parse the request URI.
+ */
+ protected AddressParser parser;
+
+ /**
+ * This is the method token send with the HTTP request header.
+ */
+ protected String method;
+
+ /**
+ * This represents the raw request URI in an unparsed form.
+ */
+ protected String target;
+
+ /**
+ * This is the major version number of the HTTP request header.
+ */
+ protected int major;
+
+ /**
+ * This is the minor version number of the HTTP request header.
+ */
+ protected int minor;
+
+ /**
+ * Constructor for the <code>RequestConsumer</code> object. This
+ * is used to create a consumer which can consume a HTTP request
+ * header and provide the consumed contents via a known interface.
+ * This also further breaks down the request URI for convenience.
+ */
+ public RequestConsumer() {
+ super();
+ }
+
+ /**
+ * This can be used to get the target specified for this HTTP
+ * request. This corresponds to the URI sent in the request
+ * line. Typically this will be the path part of the URI, but
+ * can be the full URI if the request is a proxy request.
+ *
+ * @return the target URI that this HTTP request specifies
+ */
+ public String getTarget() {
+ return target;
+ }
+
+ /**
+ * 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 target.
+ *
+ * @return this returns the address of the request line
+ */
+ public Address getAddress() {
+ if(parser == null) {
+ parser = new AddressParser(target);
+ }
+ return parser;
+ }
+
+ /**
+ * 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 getAddress().getQuery();
+ }
+
+ /**
+ * This is used to acquire the path as extracted from the
+ * 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 URI.
+ *
+ * @return this returns the normalized path for the request
+ */
+ public Path getPath() {
+ return getAddress().getPath();
+ }
+
+ /**
+ * 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 or POST method, but can be any valid alphabetic token.
+ *
+ * @return the HTTP method that this request has specified
+ */
+ public String getMethod() {
+ return method;
+ }
+
+ /**
+ * This can be used to get the major number from a HTTP version.
+ * The major version corrosponds to the major protocol type, that
+ * is the 1 of a HTTP/1.0 version string. Typically the major
+ * type is 1, by can be 0 for HTTP/0.9 clients.
+ *
+ * @return the major version number for the HTTP message
+ */
+ public int getMajor() {
+ return major;
+ }
+
+ /**
+ * This can be used to get the minor number from a HTTP version.
+ * The minor version corrosponds to the minor protocol type, that
+ * is the 0 of a HTTP/1.0 version string. This number is typically
+ * used to determine whether persistent connections are supported.
+ *
+ * @return the minor version number for the HTTP message
+ */
+ public int getMinor() {
+ return minor;
+ }
+
+ /**
+ * 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 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 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 method is invoked after the terminal token has been read.
+ * It is used to process the consumed data and is typically used to
+ * parse the input such that it can be used by the subclass for
+ * some useful puropse. This is called only once by the consumer.
+ */
+ @Override
+ protected void process() {
+ method();
+ target();
+ version();
+ end();
+ headers();
+ }
+
+ /**
+ * This will parse URI target from the first line of the header
+ * and store the parsed string internally. The target token is
+ * used to create an <code>Address</code> object which provides
+ * all the details of the target including the query part.
+ */
+ private void target() {
+ Token token = new Token(array, pos, 0);
+
+ while(pos < count){
+ if(white(array[pos])){
+ pos++;
+ break;
+ }
+ token.size++;
+ pos++;
+ }
+ target = token.toString();
+ }
+
+ /**
+ * This will parse HTTP method from the first line of the header
+ * and store the parsed string internally. The method is used to
+ * determine what action to take with the request, it also acts
+ * as a means to determine the semantics of the request.
+ */
+ private void method() {
+ Token token = new Token(array, pos, 0);
+
+ while(pos < count){
+ if(white(array[pos])){
+ pos++;
+ break;
+ }
+ token.size++;
+ pos++;
+ }
+ method = token.toString();
+ }
+
+ /**
+ * This will parse HTTP version from the first line of the header
+ * and store the parsed string internally. The method is used to
+ * determine what version of HTTP is being used. Typically this
+ * will be HTTP/1.1 however HTTP/1.0 must be supported and this
+ * has different connection semantics with regards to pipelines.
+ */
+ protected void version() {
+ pos += 5; /* "HTTP/" */
+ major(); /* "1" */
+ pos++; /* "." */
+ minor(); /* "1" */
+ }
+
+ /**
+ * This will parse the header from the current offset and convert
+ * the bytes found into an int as it parses the digits it comes
+ * accross. This will cease to parse bytes when it encounters a
+ * non digit byte or the end of the readable bytes.
+ */
+ private void major() {
+ while(pos < count){
+ if(!digit(array[pos])){
+ break;
+ }
+ major *= 10;
+ major += array[pos];
+ major -= '0';
+ pos++;
+ }
+ }
+
+ /**
+ * This will parse the header from the current offset and convert
+ * the bytes found into an int as it parses the digits it comes
+ * accross. This will cease to parse bytes when it encounters a
+ * non digit byte or the end of the readable bytes.
+ */
+ private void minor() {
+ while(pos < count){
+ if(!digit(array[pos])){
+ break;
+ }
+ minor *= 10;
+ minor += array[pos];
+ minor -= '0';
+ pos++;
+ }
+ }
+
+ /**
+ * This is used to determine if a given ISO-8859-1 byte is a digit
+ * character, between an ISO-8859-1 0 and 9. If it is, this will
+ * return true otherwise it returns false.
+ *
+ * @param octet this is to be checked to see if it is a digit
+ *
+ * @return true if the byte is a digit character, false otherwise
+ */
+ protected boolean digit(byte octet) {
+ return octet >= '0' && octet <= '9';
+ }
+
+ /**
+ * This method returns a <code>CharSequence</code> holding the data
+ * 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 new Token(array, 0, count);
+ }
+
+ /**
+ * This is used to convert the byte range to a string. This
+ * will use UTF-8 encoding for the string which is compatible
+ * with the HTTP default header encoding of ISO-8859-1.
+ *
+ * @return the encoded string representing the token
+ */
+ public String toString() {
+ return getHeader().toString();
+ }
+
+ /**
+ * This is a sequence of characters representing the header data
+ * consumed. Here the internal byte buffer is simply wrapped so
+ * that it can be a represented as a <code>CharSequence</code>.
+ * Wrapping the consumed array in this manner ensures that no
+ * further memory allocation is required.
+ */
+ private static class Token implements CharSequence {
+
+ /**
+ * This is the array that contains the header bytes.
+ */
+ public byte[] array;
+
+ /**
+ * This is the number of bytes to use from the array.
+ */
+ public int size;
+
+ /**
+ * This is the offset in the array the token begins at.
+ */
+ public int off;
+
+ /**
+ * Constructor for the <code>ByteSequence</code> object. This
+ * is used to represent the data that has been consumed by
+ * the header. It acts as a light weight wrapper for the data
+ * and avoids having to create new strings for each event.
+ *
+ * @param array this is the array representing the header
+ * @param off the starting offset for the token range
+ * @param size the number of bytes used for the token
+ */
+ private Token(byte[] array, int off, int size) {
+ this.array = array;
+ this.size = size;
+ this.off = off;
+ }
+
+ /**
+ * This returns the length of the header in bytes. The length
+ * includes the request line and all of the control characters
+ * including the carriage return and line feed at the end of
+ * the request header.
+ *
+ * @return this returns the number of bytes for the header
+ */
+ public int length() {
+ return size;
+ }
+
+ /**
+ * This is used to acquire the character at the specified index.
+ * Characters returned from this method are simply the bytes
+ * casted to a character. This may not convert the character
+ * correctly and a more sensible method should be used.
+ *
+ * @param index the index to extract the character from
+ *
+ * @return this returns the character found at the index
+ */
+ public char charAt(int index) {
+ return (char) array[index];
+ }
+
+ /**
+ * This returns a section of characters within the specified
+ * range. Acquiring a section in this manner is simply done by
+ * setting a start and end offset within the internal array.
+ *
+ * @param start this is the start index to be used
+ * @param end this is the end index to be used
+ *
+ * @return this returns a new sequence within the original
+ */
+ public CharSequence subSequence(int start, int end) {
+ return new Token(array, start, end - start);
+ }
+
+ /**
+ * This is used to create a string from the header bytes. This
+ * converts the header bytes to a string using a compatible
+ * encoding. This may produce different results depending on
+ * the time it is invoked, as the header consumes more data.
+ *
+ * @return this returns an encoded version of the header
+ */
+ public String toString() {
+ return toString("UTF-8");
+ }
+
+ /**
+ * This is used to create a string from the header bytes. This
+ * converts the header bytes to a string using a compatible
+ * encoding. This may produce different results depending on
+ * the time it is invoked, as the header consumes more data.
+ *
+ * @param charset this is the encoding to use for the header
+ *
+ * @return this returns an encoded version of the header
+ */
+ public String toString(String charset) {
+ try {
+ return new String(array, off, size, charset);
+ } catch(Exception e) {
+ return null;
+ }
+ }
+ }
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/Segment.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/Segment.java
new file mode 100644
index 0000000..915b231
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/Segment.java
@@ -0,0 +1,163 @@
+/*
+ * Segment.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.message;
+
+import org.simpleframework.http.ContentDisposition;
+import org.simpleframework.http.ContentType;
+
+import java.util.List;
+
+/**
+ * The <code>Segment</code> object represents a collection of header
+ * values that is followed by a body. This is used to represent the
+ * header of a multipart upload part. The raw value of each header
+ * for the part can be acquired using this interface, also the type
+ * and the disposition of the body can be determined from this.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.Part
+ */
+public interface Segment {
+
+ /**
+ * This method is used to determine the type of a part. Typically
+ * a part is either a text parameter or a file. If this is true
+ * then the content represented by the associated part is a file.
+ *
+ * @return this returns true if the associated part is a file
+ */
+ boolean isFile();
+
+ /**
+ * This method is used to acquire the name of the part. Typically
+ * this is used when the part represents a text parameter rather
+ * than a file. However, this can also be used with a file part.
+ *
+ * @return this returns the name of the associated part
+ */
+ String getName();
+
+ /**
+ * This method is used to acquire the file name of the part. This
+ * is used when the part represents a text parameter rather than
+ * a file. However, this can also be used with a file part.
+ *
+ * @return this returns the file name of the associated part
+ */
+ String getFileName();
+
+ /**
+ * 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 there is 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
+ */
+ String getValue(String 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 there is no HTTP message header.
+ *
+ * @param name the HTTP message header to get the value from
+ * @param index acquires a specific header value from multiple
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ String getValue(String name, int index);
+
+ /**
+ * 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 benefits 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 appearance.
+ * <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 highest preference.
+ *
+ * @param name the name of the headers that are to be retrieved
+ *
+ * @return ordered array of tokens extracted from the header(s)
+ */
+ List<String> getValues(String name);
+
+ /**
+ * 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
+ */
+ ContentType getContentType();
+
+ /**
+ * 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-Disposition</code> header, if there is
+ * 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 disposition value if it exists
+ */
+ ContentDisposition getDisposition();
+
+ /**
+ * 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
+ */
+ String getTransferEncoding();
+
+ /**
+ * 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
+ */
+ long getContentLength();
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/SegmentConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/SegmentConsumer.java
new file mode 100644
index 0000000..5c994ce
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/SegmentConsumer.java
@@ -0,0 +1,750 @@
+/*
+ * SegmentConsumer.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.message;
+
+import static org.simpleframework.http.Protocol.ACCEPT_LANGUAGE;
+import static org.simpleframework.http.Protocol.CONTENT_DISPOSITION;
+import static org.simpleframework.http.Protocol.CONTENT_LENGTH;
+import static org.simpleframework.http.Protocol.CONTENT_TYPE;
+import static org.simpleframework.http.Protocol.COOKIE;
+import static org.simpleframework.http.Protocol.EXPECT;
+import static org.simpleframework.http.Protocol.TRANSFER_ENCODING;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+import org.simpleframework.http.ContentDisposition;
+import org.simpleframework.http.ContentType;
+import org.simpleframework.http.Cookie;
+import org.simpleframework.http.parse.ContentDispositionParser;
+import org.simpleframework.http.parse.ContentTypeParser;
+import org.simpleframework.http.parse.CookieParser;
+import org.simpleframework.http.parse.LanguageParser;
+
+/**
+ * The <code>SegmentConsumer</code> object provides a consumer that is
+ * used to consume a HTTP header. This will read all headers within a
+ * HTTP header message until the carriage return line feed empty line
+ * is encountered. Once all headers are consumed they are available
+ * using the case insensitive header name. This will remove leading
+ * and trailing whitespace from the names and values parsed.
+ *
+ * @author Niall Gallagher
+ */
+public class SegmentConsumer extends ArrayConsumer implements Segment {
+
+ /**
+ * This is the terminal carriage return and line feed end line.
+ */
+ private static final byte[] TERMINAL = { 13, 10, 13, 10 };
+
+ /**
+ * This is used to represent the content disposition header.
+ */
+ protected ContentDisposition disposition;
+
+ /**
+ * This is used to parse the languages accepted in the request.
+ */
+ protected LanguageParser language;
+
+ /**
+ * This is used to parse the cookie headers that are consumed.
+ */
+ protected CookieParser cookies;
+
+ /**
+ * This is used to store all consumed headers by the header name.
+ */
+ protected MessageHeader header;
+
+ /**
+ * This is used to parse the content type header consumed.
+ */
+ protected ContentType type;
+
+ /**
+ * This represents the transfer encoding value of the body.
+ */
+ protected String encoding;
+
+ /**
+ * During parsing this is used to store the parsed header name,
+ */
+ protected String name;
+
+ /**
+ * During parsing this is used to store the parsed header value.
+ */
+ protected String value;
+
+ /**
+ * This is used to determine if there is a continue expected.
+ */
+ protected boolean expect;
+
+ /**
+ * Represents the length of the body from the content length.
+ */
+ protected long length;
+
+ /**
+ * This represents the length limit of the HTTP header cosumed.
+ */
+ protected long limit;
+
+ /**
+ * This is used to track the read offset within the header.
+ */
+ protected int pos;
+
+ /**
+ * This is used to track how much of the terminal is read.
+ */
+ protected int scan;
+
+ /**
+ * Constructor for the <code>SegmentConsumer</code> object. This
+ * is used to create a segment consumer used to consume and parse
+ * a HTTP message header. This delegates parsing of headers if
+ * they represent special headers, like content type or cookies.
+ */
+ public SegmentConsumer() {
+ this(1048576);
+ }
+
+ /**
+ * Constructor for the <code>SegmentConsumer</code> object. This
+ * is used to create a segment consumer used to consume and parse
+ * a HTTP message header. This delegates parsing of headers if
+ * they represent special headers, like content type or cookies.
+ *
+ * @param limit this is the length limit for a HTTP header
+ */
+ public SegmentConsumer(int limit) {
+ this.language = new LanguageParser();
+ this.cookies = new CookieParser();
+ this.header = new MessageHeader();
+ this.limit = limit;
+ this.length = -1;
+ }
+
+ /**
+ * This method is used to determine the type of a part. Typically
+ * a part is either a text parameter or a file. If this is true
+ * then the content represented by the associated part is a file.
+ *
+ * @return this returns true if the associated part is a file
+ */
+ public boolean isFile() {
+ if(disposition == null) {
+ return false;
+ }
+ return disposition.isFile();
+ }
+
+ /**
+ * This method is used to acquire the name of the part. Typically
+ * this is used when the part represents a text parameter rather
+ * than a file. However, this can also be used with a file part.
+ *
+ * @return this returns the name of the associated part
+ */
+ public String getName() {
+ if(disposition == null) {
+ return null;
+ }
+ return disposition.getName();
+ }
+
+ /**
+ * This method is used to acquire the file name of the part. This
+ * is used when the part represents a text parameter rather than
+ * a file. However, this can also be used with a file part.
+ *
+ * @return this returns the file name of the associated part
+ */
+ public String getFileName() {
+ if(disposition == null) {
+ return null;
+ }
+ return disposition.getFileName();
+ }
+
+ /**
+ * 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 type;
+ }
+
+ /**
+ * 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 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 encoding;
+ }
+
+ /**
+ * 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-Disposition</code> header, if there is
+ * 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 disposition value if it exists
+ */
+ public ContentDisposition getDisposition() {
+ return disposition;
+ }
+
+ /**
+ * 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() {
+ if(language != null) {
+ return language.list();
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * 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 benefits 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 appearance.
+ * <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 highest 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 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
+ *
+ * @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 there is no HTTP message header.
+ *
+ * @param name the HTTP message header to get the value from
+ * @param index acquires a specific header value from multiple
+ *
+ * @return this returns the value that the HTTP message header
+ */
+ public String getValue(String name, int index) {
+ return header.getValue(name, index);
+ }
+
+ /**
+ * This is used to determine if the header represents one that
+ * requires the HTTP/1.1 continue expectation. If the request
+ * does require this expectation then it should be send the
+ * 100 status code which prompts delivery of the message body.
+ *
+ * @return this returns true if a continue expectation exists
+ */
+ public boolean isExpectContinue() {
+ return expect;
+ }
+
+ /**
+ * This method is used to add an additional chunk size to the
+ * internal array. Resizing of the internal array is required as
+ * the consumed bytes may exceed the initial size of the array.
+ * In such a scenario the array is expanded the chunk size.
+ *
+ * @param size this is the minimum size to expand the array to
+ */
+ @Override
+ protected void resize(int size) throws IOException {
+ if(size > limit) {
+ throw new IOException("Header has exceeded maximum size");
+ }
+ super.resize(size);
+ }
+
+ /**
+ * This is used to process the headers when the terminal token
+ * has been fully read from the consumed bytes. Processing will
+ * extract all headers from the HTTP header message and further
+ * parse those values if required.
+ */
+ @Override
+ protected void process() throws IOException {
+ headers();
+ }
+
+ /**
+ * This is used to parse the headers from the consumed HTTP header
+ * and add them to the segment. Once added they are available via
+ * the header name in a case insensitive manner. If the header has
+ * a special value, that is, if further information is required it
+ * will be extracted and exposed in the segment interface.
+ */
+ protected void headers() {
+ while(pos < count) {
+ header();
+ add(name, value);
+ }
+ }
+
+ /**
+ * This is used to parse a header from the consumed HTTP message
+ * and add them to the segment. Once added it is available via
+ * the header name in a case insensitive manner. If the header has
+ * a special value, that is, if further information is required it
+ * will be extracted and exposed in the segment interface.
+ */
+ private void header() {
+ adjust();
+ name();
+ adjust();
+ value();
+ end();
+ }
+
+ /**
+ * This is used to add the name and value specified as a special
+ * header within the segment. Special headers are those where
+ * there are values of interest to the segment. For instance the
+ * Content-Length, Content-Type, and Cookie headers are parsed
+ * using an external parser to extract the values.
+ *
+ * @param name this is the name of the header to be added
+ * @param value this is the value of the header to be added
+ */
+ protected void add(String name, String value) {
+ if(equal(ACCEPT_LANGUAGE, name)) {
+ language(value);
+ }else if(equal(CONTENT_LENGTH, name)) {
+ length(value);
+ } else if(equal(CONTENT_TYPE, name)) {
+ type(value);
+ } else if(equal(CONTENT_DISPOSITION, name)) {
+ disposition(value);
+ } else if(equal(TRANSFER_ENCODING, name)) {
+ encoding(value);
+ } else if(equal(EXPECT, name)) {
+ expect(value);
+ } else if(equal(COOKIE, name)) {
+ cookie(value);
+ }
+ header.addValue(name, value);
+ }
+
+ /**
+ * This is used to determine if the expect continue header is
+ * present and thus there is a requirement to send the continue
+ * status before the client sends the request body. This will
+ * basically assume the expectation is always continue.
+ *
+ * @param value the value in the expect continue header
+ */
+ protected void expect(String value) {
+ expect = true;
+ }
+
+ /**
+ * This will accept any cookie header and parse it such that all
+ * cookies within it are converted to <code>Cookie</code> objects
+ * and made available as typed objects. If the value can not be
+ * parsed this will not add the cookie value.
+ *
+ * @param value this is the value of the cookie to be parsed
+ */
+ protected void cookie(String value) {
+ cookies.parse(value);
+
+ for(Cookie cookie : cookies) {
+ header.setCookie(cookie);
+ }
+ }
+
+ /**
+ * This is used to parse the <code>Accept-Language</code> header
+ * value. This allows the locales the client is interested in to
+ * be provided in preference order and allows the client do alter
+ * and response based on the locale the client has provided.
+ *
+ * @param value this is the value that is to be parsed
+ */
+ protected void language(String value) {
+ language = new LanguageParser(value);
+ }
+
+ /**
+ * This is used to parse the content type header header so that
+ * the MIME type is available to the segment. This provides an
+ * instance of the <code>ContentType</code> object to represent
+ * the content type header, which exposes the charset value.
+ *
+ * @param value this is the content type value to parse
+ */
+ protected void type(String value) {
+ type = new ContentTypeParser(value);
+ }
+
+ /**
+ * This is used to parse the content disposition header header so
+ * that the MIME type is available to the segment. This provides
+ * an instance of the <code>Disposition<code> object to represent
+ * the content disposition, this exposes the upload type.
+ *
+ * @param value this is the content type value to parse
+ */
+ protected void disposition(String value) {
+ disposition = new ContentDispositionParser(value);
+ }
+
+ /**
+ * This is used to store the transfer encoding header value. This
+ * is used to determine the encoding of the body this segment
+ * represents. Typically this will be the chunked encoding.
+ *
+ * @param value this is the value representing the encoding
+ */
+ protected void encoding(String value) {
+ encoding = value;
+ }
+
+ /**
+ * This is used to parse a provided header value for the content
+ * length. If the string provided is not an integer value this will
+ * throw a number format exception, by default length is -1.
+ *
+ * @param value this is the header value of the content length
+ */
+ protected void length(String value) {
+ try {
+ length = Long.parseLong(value);
+ }catch(Exception e) {
+ length = -1;
+ }
+ }
+
+ /**
+ * This updates the token for the header name. The name is parsed
+ * according to the presence of a colon ':'. Once a colon character
+ * is encountered then this header name is considered to be read
+ * from the buffer and is used to key the value after the colon.
+ */
+ private void name() {
+ Token token = new Token(pos, 0);
+
+ while(pos < count){
+ if(array[pos] == ':') {
+ pos++;
+ break;
+ }
+ token.size++;
+ pos++;
+ }
+ name = token.text();
+ }
+
+
+ /**
+ * This is used to parse the HTTP header value. This will parse it
+ * in such a way that the line can be folded over several lines
+ * see RFC 2616 for the syntax of a folded line. The folded line
+ * is basically a way to wrap a single HTTP header into several
+ * lines using a tab at the start of the following line to indicate
+ * that the header flows onto the next line.
+ */
+ private void value() {
+ Token token = new Token(pos, 0);
+
+ scan: for(int mark = 0; pos < count;){
+ if(terminal(array[pos])) { /* CR or LF */
+ for(int i = 0; pos < count; i++){
+ if(array[pos++] == 10) { /* skip the LF */
+ if(pos < array.length) {
+ if(space(array[pos])) {
+ mark += i + 1; /* account for bytes examined */
+ break; /* folding line */
+ }
+ }
+ break scan; /* not a folding line */
+ }
+ }
+ } else {
+ if(!space(array[pos])){
+ token.size = ++mark;
+ } else {
+ mark++;
+ }
+ pos++;
+ }
+ }
+ value = token.text();
+ }
+
+ /**
+ * This will update the offset variable so that the next read will
+ * be of a non whitespace character. According to RFC 2616 a white
+ * space character is a tab or a space. This will remove multiple
+ * occurrences of whitespace characters until an non-whitespace
+ * character is encountered.
+ */
+ protected void adjust() {
+ while(pos < count) {
+ if(!space(array[pos])){
+ break;
+ }
+ pos++;
+ }
+ }
+
+ /**
+ * This will update the offset variable so that the next read will
+ * be a non whitespace character or terminal character. According to
+ * RFC 2616 a white space character is a tab or a space. This will
+ * remove multiple occurrences of whitespace characters until an
+ * non-whitespace character or a non-terminal is encountered. This
+ * is basically used to follow through to the end of a header line.
+ */
+ protected void end() {
+ while(pos < count) {
+ if(!white(array[pos])){
+ break;
+ }
+ pos++;
+ }
+ }
+
+ /**
+ * This method is used to scan for the terminal token. It searches
+ * for the token and returns the number of bytes in the buffer
+ * after the terminal token. Returning the excess bytes allows the
+ * consumer to reset the bytes within the consumer object.
+ *
+ * @return this returns the number of excess bytes consumed
+ */
+ @Override
+ protected int scan() {
+ int length = count;
+
+ while(pos < count) {
+ if(array[pos++] != TERMINAL[scan++]) {
+ scan = 0;
+ }
+ if(scan == TERMINAL.length) {
+ done = true;
+ count = pos;
+ pos = 0;
+ return length - count;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * This is used to determine if two header names are equal, this is
+ * done to ensure that the case insensitivity of HTTP header names
+ * is observed. Special headers are processed using this consumer
+ * and this is used to ensure the correct header is always matched.
+ *
+ * @param name this is the name to compare the parsed token with
+ * @param token this is the header name token to examine
+ *
+ * @return true of the header name token is equal to the name
+ */
+ protected boolean equal(String name, String token) {
+ return name.equalsIgnoreCase(token);
+ }
+
+ /**
+ * This identifies a given ISO-8859-1 byte as a space character. A
+ * space is either a space or a tab character in ISO-8859-1.
+ *
+ * @param octet the byte to determine whether it is a space
+ *
+ * @return true if it is a space character, false otherwise
+ */
+ protected boolean space(byte octet) {
+ return octet == ' ' || octet == '\t';
+ }
+
+ /**
+ * This determines if an ISO-8859-1 byte is a terminal character. A
+ * terminal character is a carriage return or a line feed character.
+ *
+ * @param octet the byte to determine whether it is a terminal
+ *
+ * @return true if it is a terminal character, false otherwise
+ */
+ protected boolean terminal(byte octet){
+ return octet == 13 || octet == 10;
+ }
+
+
+ /**
+ * This is used to determine if a given ISO-8859-1 byte is a white
+ * space character, such as a tab or space or a terminal character,
+ * such as a carriage return or a new line. If it is, this will
+ * return true otherwise it returns false.
+ *
+ * @param octet this is to be checked to see if it is a space
+ *
+ * @return true if the byte is a space character, false otherwise
+ */
+ protected boolean white(byte octet) {
+ switch(octet) {
+ case ' ': case '\r':
+ case '\n': case '\t':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * 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
+ */
+ @Override
+ public String toString() {
+ return new String(array, 0, count);
+ }
+
+ /**
+ * This is used to track the boundaries of a token so that it can
+ * be converted in to a usable string. This will track the length
+ * and offset within the consumed array of the token. When the
+ * token is to be used it can be converted in to a string.
+ */
+ private class Token {
+
+ /**
+ * This is used to track the number of bytes within the array.
+ */
+ public int size;
+
+ /**
+ * This is used to mark the start offset within the array.
+ */
+ public int off;
+
+ /**
+ * Constructor for the <code>Token</code> object. This is used
+ * to create a new token to track the range of bytes that will
+ * be used to create a string representing the parsed value.
+ *
+ * @param off the starting offset for the token range
+ * @param size the number of bytes used for the token
+ */
+ public Token(int off, int size) {
+ this.off = off;
+ this.size = size;
+ }
+
+ /**
+ * This is used to convert the byte range to a string. This
+ * will use UTF-8 encoding for the string which is compatible
+ * with the HTTP default header encoding of ISO-8859-1.
+ *
+ * @return the encoded string representing the token
+ */
+ public String text() {
+ return text("UTF-8");
+ }
+
+ /**
+ * This is used to convert the byte range to a string. This
+ * will use specified encoding, if that encoding is not
+ * supported then this will return null for the token value.
+ *
+ * @return the encoded string representing the token
+ */
+ public String text(String charset) {
+ try {
+ return new String(array, off, size, charset);
+ } catch(IOException e) {
+ return null;
+ }
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/TokenConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/TokenConsumer.java
new file mode 100644
index 0000000..2b48b7c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/TokenConsumer.java
@@ -0,0 +1,113 @@
+/*
+ * TokenConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.common.buffer.Allocator;
+import org.simpleframework.common.buffer.Buffer;
+
+/**
+ * The <code>TokenConsumer</code> object is used to consume a token
+ * from the cursor. Once the token has been consumed the consumer
+ * is finished and the contents of the consumed token is appended
+ * to an allocated buffer so that it can be extracted.
+ *
+ * @author Niall Gallagher
+ */
+class TokenConsumer extends ArrayConsumer {
+
+ /**
+ * This is used to allocate a buffer to append the contents.
+ */
+ private Allocator allocator;
+
+ /**
+ * This is used to append the contents of consumed token.
+ */
+ private Buffer buffer;
+
+ /**
+ * This is the token that is to be consumed from the cursor.
+ */
+ private byte[] token;
+
+ /**
+ * This tracks the number of bytes that are read from the token.
+ */
+ private int seek;
+
+ /**
+ * This is the length of the token that is to be consumed.
+ */
+ private int length;
+
+ /**
+ * The <code>TokenConsumer</code> object is used to read a token
+ * from the cursor. This tracks the bytes read from the cursor,
+ * when it has fully read the token bytes correctly it will
+ * finish and append the consumed bytes to a buffer.
+ *
+ * @param allocator the allocator used to create a buffer
+ * @param token this is the token that is to be consumed
+ */
+ public TokenConsumer(Allocator allocator, byte[] token) {
+ this.allocator = allocator;
+ this.length = token.length;
+ this.token = token;
+ this.chunk = length;
+ }
+
+ /**
+ * This is used to append the consumed bytes to a created buffer
+ * so that it can be used when he is finished. This allows the
+ * contents to be read from an input stream or as a string.
+ */
+ @Override
+ protected void process() throws IOException {
+ if(buffer == null) {
+ buffer = allocator.allocate(length);
+ }
+ buffer.append(token);
+ }
+
+ /**
+ * This is used to scan the token from the array. Once the bytes
+ * have been read from the consumed bytes this will return the
+ * number of bytes that need to be reset within the buffer.
+ *
+ * @return this returns the number of bytes to be reset
+ */
+ @Override
+ protected int scan() throws IOException {
+ int size = token.length;
+ int pos = 0;
+
+ if(count >= size) {
+ while(seek < count) {
+ if(array[seek++] != token[pos++]) {
+ throw new IOException("Invalid token");
+ }
+ }
+ done = true;
+ return count - seek;
+ }
+ return 0;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/message/UpdateConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/message/UpdateConsumer.java
new file mode 100644
index 0000000..5d514c9
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/message/UpdateConsumer.java
@@ -0,0 +1,143 @@
+/*
+ * UpdateConsumer.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.message;
+
+import java.io.IOException;
+
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>UpdateConsumer</code> object is used to create a consumer
+ * that is used to consume and process large bodies. Typically a large
+ * body will be one that is delivered as part of a multipart upload
+ * or as a large form POST. The task of the large consumer is to
+ * consume all the bytes for the body, and reset the cursor after the
+ * last byte that has been send with the body. This ensures that the
+ * next character read from the cursor is the first character of a
+ * HTTP header within the pipeline.
+ *
+ * @author Niall Gallagher
+ */
+public abstract class UpdateConsumer implements BodyConsumer {
+
+ /**
+ * This is an external array used to copy data between buffers.
+ */
+ protected byte[] array;
+
+ /**
+ * This is used to determine whether the consumer has finished.
+ */
+ protected boolean finished;
+
+ /**
+ * Constructor for the <code>UpdateConsumer</code> object. This is
+ * used to create a consumer with a one kilobyte buffer used to
+ * read the contents from the cursor and transfer it to the buffer.
+ */
+ protected UpdateConsumer() {
+ this(2048);
+ }
+
+ /**
+ * Constructor for the <code>UpdateConsumer</code> object. This is
+ * used to create a consumer with a variable size buffer used to
+ * read the contents from the cursor and transfer it to the buffer.
+ *
+ * @param chunk this is the size of the buffer used to read bytes
+ */
+ protected UpdateConsumer(int chunk) {
+ this.array = new byte[chunk];
+ }
+
+ /**
+ * This is used to determine whether the consumer has finished
+ * reading. The consumer is considered finished if it has read a
+ * terminal token or if it has exhausted the stream and can not
+ * read any more. Once finished the consumed bytes can be parsed.
+ *
+ * @return true if the consumer has finished reading its content
+ */
+ public boolean isFinished() {
+ return finished;
+ }
+
+ /**
+ * This method is used to consume bytes from the provided cursor.
+ * Consuming of bytes from the cursor should be done in such a
+ * way that it does not block. So typically only the number of
+ * ready bytes in the <code>ByteCursor</code> object should be
+ * read. If there are no ready bytes then this will return.
+ *
+ * @param cursor used to consume the bytes from the HTTP pipeline
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ int ready = cursor.ready();
+
+ while(ready > 0) {
+ int size = Math.min(ready, array.length);
+ int count = cursor.read(array, 0, size);
+
+ if(count > 0) {
+ int reset = update(array, 0, count);
+
+ if(reset > 0) {
+ cursor.reset(reset);
+ }
+ }
+ if(finished) {
+ commit(cursor);
+ break;
+ }
+ ready = cursor.ready();
+ }
+ }
+
+ /**
+ * This method can be used to commit the consumer when all data
+ * has been consumed. It is often used to push back some data on
+ * to the cursor so that the next consumer can read valid tokens
+ * from the stream of bytes. If no commit is required then the
+ * default implementation of this will simply return quietly.
+ *
+ * @param cursor this is the cursor used by this consumer
+ */
+ protected void commit(ByteCursor cursor) throws IOException {
+ if(!finished) {
+ throw new IOException("Consumer not finished");
+ }
+ }
+
+ /**
+ * This is used to process the bytes that have been read from the
+ * cursor. Depending on the delimiter used this knows when the
+ * end of the body has been encountered. If the end is encountered
+ * this method must return the number of bytes overflow, and set
+ * the state of the consumer to finished.
+ *
+ * @param array this is a chunk read from the cursor
+ * @param off this is the offset within the array the chunk starts
+ * @param count this is the number of bytes within the array
+ *
+ * @return this returns the number of bytes overflow that is read
+ */
+ protected abstract int update(byte[] array, int off, int count) throws IOException;
+}
+
+
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/AddressParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/AddressParser.java
new file mode 100644
index 0000000..06d5509
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/AddressParser.java
@@ -0,0 +1,1347 @@
+/*
+ * AddressParser.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.parse;
+
+import org.simpleframework.common.KeyMap;
+import org.simpleframework.common.parse.Parser;
+import org.simpleframework.http.Address;
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Query;
+
+/**
+ * This parser is used to parse uniform resource identifiers.
+ * The uniform resource identifier syntax is given in RFC 2396.
+ * This parser can parse relative and absolute URI's. The
+ * uniform resource identifier syntax that this parser will
+ * parse are based on the generic web based URL similar to
+ * the syntax represented in RFC 2616 section 3.2.2. The syntax
+ * used to parse this URI is a modified version of RFC 2396
+ * <pre>
+ *
+ * URI = (absoluteURI | relativeURI)
+ * absoluteURI = scheme ":" ("//" netpath | relativeURI)
+ * relativeURI = path ["?" querypart]
+ * netpath = domain [":" port] relativeURI
+ * path = *("/" segment)
+ * segment = *pchar *( ";" param )
+ *
+ * </pre>
+ * This implements the <code>Address</code> interface and provides
+ * methods that access the various parts of the URI. The parameters
+ * in the path segments of the uniform resource identifier are
+ * stored in name value pairs. If parameter names are not unique
+ * across the path segments then only the deepest parameter will be
+ * stored from the path segment. For example if the URI represented
+ * was <code>http://domain/path1;x=y/path2;x=z</code> the value for
+ * the parameter named <code>x</code> would be <code>z</code>.
+ * <p>
+ * This will normalize the path part of the uniform resource
+ * identifier. A normalized path is one that contains no back
+ * references like "./" and "../". The normalized path will not
+ * contain the path parameters.
+ * <p>
+ * The <code>setPath</code> method is used to reset the path this
+ * uniform resource identifier has, it also resets the parameters.
+ * The parameters are extracted from the new path given.
+ *
+ * @author Niall Gallagher
+ */
+public class AddressParser extends Parser implements Address {
+
+ /**
+ * Parameters are stored so that the can be viewed.
+ */
+ private ParameterMap param;
+
+ /**
+ * This is the path used to represent the address path.
+ */
+ private Path normal;
+
+ /**
+ * This contains the query parameters for the address.
+ */
+ private Query data;
+
+ /**
+ * Used to track the characters that form the path.
+ */
+ private Token path;
+
+ /**
+ * Used to track the characters that form the domain.
+ */
+ private Token domain;
+
+ /**
+ * Used to track the characters that form the query.
+ */
+ private Token query;
+
+ /**
+ * Used to track the name characters of a parameter.
+ */
+ private Token name;
+
+ /**
+ * Used to track the value characters of a parameter.
+ */
+ private Token value;
+
+ /**
+ * References the scheme that this URI contains.
+ */
+ private Token scheme;
+
+ /**
+ * Contains the port number if it was specified.
+ */
+ private int port;
+
+ /**
+ * Default constructor will create a <code>AddressParser</code>
+ * that contains no specifics. The instance will return
+ * <code>null</code> for all the get methods. The parsers
+ * get methods are populated by using the <code>parse</code>
+ * method.
+ */
+ public AddressParser(){
+ this.param = new ParameterMap();
+ this.path = new Token();
+ this.domain = new Token();
+ this.query = new Token();
+ this.scheme = new Token();
+ this.name = new Token();
+ this.value = new Token();
+ }
+
+ /**
+ * This is primarily a convenience constructor. This will parse
+ * the <code>String</code> given to extract the specifics. This
+ * could be achieved by calling the default no-arg constructor
+ * and then using the instance to invoke the <code>parse</code>
+ * method on that <code>String</code> to extract the parts.
+ *
+ * @param text a <code>String</code> containing a URI value
+ */
+ public AddressParser(String text){
+ this();
+ parse(text);
+ }
+
+ /**
+ * This allows the scheme of the URL given to be returned.
+ * If the URI does not contain a scheme then this will
+ * return null. The scheme of the URI is the part that
+ * specifies the type of protocol that the URI is used
+ * for, an example <code>gopher://domain/path</code> is
+ * a URI that is intended for the gopher protocol. The
+ * scheme is the string <code>gopher</code>.
+ *
+ * @return this returns the scheme tag for the URI if
+ * there is one specified for it
+ */
+ public String getScheme(){
+ return scheme.toString();
+ }
+
+ /**
+ * This is used to retrieve the domain of this URI. The
+ * domain part in the URI is an optional part, an example
+ * <code>http://domain/path?querypart</code>. This will
+ * return the value of the domain part. If there is no
+ * domain part then this will return null otherwise the
+ * domain value found in the uniform resource identifier.
+ *
+ * @return the domain part of this uniform resource
+ * identifier this represents
+ */
+ public String getDomain(){
+ return domain.toString();
+ }
+
+ /**
+ * This is used to retrieve the path of this URI. The path part
+ * is the most fundamental part of the URI. This will return
+ * the value of the path. If there is no path part then this
+ * will return <code>/</code> to indicate the root.
+ * <p>
+ * The <code>Path</code> object returned by this will contain
+ * no path parameters. The path parameters are available using
+ * the <code>Address</code> methods. The reason that this does not
+ * contain any of the path parameters is so that if the path is
+ * needed to be converted into an OS specific path then the path
+ * parameters will not need to be separately parsed out.
+ *
+ * @return the path that this URI contains, this value will not
+ * contain any back references such as "./" and "../" or any
+ * path parameters
+ */
+ public Path getPath(){
+ if(normal == null) {
+ String text = path.toString();
+
+ if(text == null) {
+ normal = new PathParser("/");
+ }
+ if(normal == null){
+ normal = new PathParser(text);
+ }
+ }
+ return normal;
+ }
+
+ /**
+ * This is used to retrieve the query of this URI. The query part
+ * in the URI is an optional part. This will return the value
+ * of the query part. If there is no query part then this will
+ * return an empty <code>Query</code> object. The query is
+ * an optional member of a URI and comes after the path part, it
+ * is preceded by a question mark, <code>?</code> character.
+ * For example the following URI contains <code>query</code> for
+ * its query part, <code>http://host:port/path?query</code>.
+ * <p>
+ * This returns a <code>org.simpleframework.http.Query</code>
+ * object that can be used to interact directly with the query
+ * values. The <code>Query</code> object is a read-only interface
+ * to the query parameters, and so will not affect the URI.
+ *
+ * @return a <code>Query</code> object for the query part
+ */
+ public Query getQuery(){
+ if(data == null) {
+ String text = query.toString();
+
+ if(text == null) {
+ data = new QueryParser();
+ }
+ if(data == null){
+ data = new QueryParser(text);
+ }
+ }
+ return data;
+ }
+
+ /**
+ * This is used to retrieve the port of the uniform resource
+ * identifier. The port part in this is an optional part, an
+ * example <code>http://host:port/path?querypart</code>. This
+ * will return the value of the port. If there is no port then
+ * this will return <code>-1</code> because this represents
+ * an impossible uniform resource identifier port. The port
+ * is an optional part.
+ *
+ * @return this returns the port of the uniform resource
+ * identifier
+ */
+ public int getPort(){
+ return port <= 0? -1 : port;
+ }
+
+ /**
+ * This extracts the parameter values from the uniform resource
+ * identifier represented by this object. The parameters that a
+ * uniform resource identifier contains are embedded in the path
+ * part of the URI. If the path contains no parameters then this
+ * will return an empty <code>Map</code> instance.
+ * <p>
+ * This will produce unique name and value parameters. Thus if the
+ * URI contains several path segments with similar parameter names
+ * this will return the deepest parameter. For example if the URI
+ * represented was <code>http://domain/path1;x=y/path2;x=z</code>
+ * the value for the parameter named <code>x</code> would be
+ * <code>z</code>.
+ *
+ * @return this will return the parameter names found in the URI
+ */
+ public KeyMap<String> getParameters(){
+ return param;
+ }
+
+ /**
+ * This allows the scheme for the URI to be specified.
+ * If the URI does not contain a scheme then this will
+ * attach the scheme and the <code>://</code> identifier
+ * to ensure that the <code>Address.toString</code> will
+ * produce the correct syntax.
+ * <p>
+ * Caution must be taken to ensure that the port and
+ * the scheme are consistent. So if the original URI
+ * was <code>http://domain:80/path</code> and the scheme
+ * was changed to <code>ftp</code> the port number that
+ * remains is the standard HTTP port not the FTP port.
+ *
+ * @param value this specifies the protocol this URI
+ * is intended for
+ */
+ public void setScheme(String value){
+ scheme.value = value;
+ }
+
+ /**
+ * This will set the domain to whatever value is in the
+ * string parameter. If the string is null then this URI
+ * objects <code>toString</code> method will not contain
+ * the domain. The result of the <code>toString</code>
+ * method will be <code>/path/path?query</code>. If the
+ * path is non-null this URI will contain the path.
+ *
+ * @param value this will be the new domain of this
+ * uniform resource identifier, if it is not null
+ */
+ public void setDomain(String value){
+ path.toString();
+ query.toString();
+ scheme.toString();
+ domain.clear();
+ parseDomain(value);
+ }
+
+ /**
+ * This will set the domain to whatever value is in the
+ * string parameter. If the string is null then this URI
+ * objects <code>toString</code> method will not contain
+ * the domain. The result of the <code>toString</code>
+ * method will be <code>/path/path?query</code>. If the
+ * path is non-null this URI will contain the path.
+ *
+ * @param value this will be the new domain of this
+ * uniform resource identifier, if it is not null
+ */
+ private void parseDomain(String value){
+ count = value.length();
+ ensureCapacity(count);
+ value.getChars(0, count, buf, 0);
+ normal = null;
+ off = 0;
+ hostPort();
+ }
+
+ /**
+ * This will set the port to whatever value it is given. If
+ * the value is 0 or less then the <code>toString</code> will
+ * will not contain the optional port. If port number is above
+ * 0 then the <code>toString</code> method will produce a URI
+ * like <code>http://host:123/path</code> but only if there is
+ * a valid domain.
+ *
+ * @param port the port value that this URI is to have
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * This will set the path to whatever value it is given. If the
+ * value is null then this <code>Address.toString</code> method will
+ * not contain the path, that is if path is null then it will be
+ * interpreted as <code>/</code>.
+ * <p>
+ * This will reset the parameters this URI has. If the value
+ * given to this method has embedded parameters these will form
+ * the parameters of this URI. The value given may not be the
+ * same value that the <code>getPath</code> produces. The path
+ * will have all back references and parameters stripped.
+ *
+ * @param text the path that this URI is to be set with
+ */
+ public void setPath(String text) {
+ if(!text.startsWith("/")){
+ text = "/" + text;
+ }
+ domain.toString();
+ query.toString();
+ scheme.toString();
+ param.clear();
+ path.clear();
+ parsePath(text); /*extract params*/
+ }
+
+ /**
+ * This will set the path to whatever value it is given. If the
+ * value is null then this <code>Address.toString</code> method
+ * will not contain the path, that is if path is null then it will
+ * be interpreted as <code>/</code>.
+ * <p>
+ * This will reset the parameters this URI has. If the value
+ * given to this method has embedded parameters these will form
+ * the parameters of this URI. The value given may not be the
+ * same value that the <code>getPath</code> produces. The path
+ * will have all back references and parameters stripped.
+ *
+ * @param path the path that this URI is to be set with
+ */
+ public void setPath(Path path) {
+ if(path != null){
+ normal = path;
+ }else {
+ setPath("/");
+ }
+ }
+
+ /**
+ * This is used to parse the path given with the <code>setPath</code>
+ * method. The path contains name and value pairs. These parameters
+ * are embedded into the path segments using a semicolon character,
+ * ';'. Since the parameters to not form part of the actual path
+ * mapping they are removed from the path and stored. Each parameter
+ * can then be extracted from this parser using the methods provided
+ * by the <code>Address</code> interface.
+ *
+ * @param path this is the path that is to be parsed and have the
+ * parameter values extracted
+ */
+ private void parsePath(String path){
+ count = path.length();
+ ensureCapacity(count);
+ path.getChars(0, count, buf, 0);
+ normal = null;
+ off = 0;
+ path();
+ }
+
+ /**
+ * This will set the query to whatever value it is given. If the
+ * value is null then this <code>Address.toString</code> method
+ * will not contain the query. If the query was <code>abc</code>
+ * then the <code>toString</code> method would produce a string
+ * like <code>http://host:port/path?abc</code>. If the query is
+ * null this URI would have no query part. The query must not
+ * contain the <code>?</code> character.
+ *
+ * @param value the query that this uniform resource identifier
+ * is to be set to if it is non-null
+ */
+ public void setQuery(String value) {
+ query.value = value;
+ data = null;
+ }
+
+ /**
+ * This will set the query to whatever value it is given. If the
+ * value is null then this <code>Address.toString</code> method
+ * will not contain the query. If the <code>Query.toString</code>
+ * returns null then the query will be empty. This is basically
+ * the <code>setQuery(String)</code> method with the string value
+ * from the issued <code>Query.toString</code> method.
+ *
+ * @param query a <code>Query</code> object that contains
+ * the name value parameters for the query
+ */
+ public void setQuery(Query query) {
+ if(value != null) {
+ data = query;
+ }else {
+ setQuery("");
+ }
+ }
+
+ /**
+ * This will check to see what type of URI this is if it is an
+ * <code>absoluteURI</code> or a <code>relativeURI</code>. To
+ * see the definition of a URI see RFC 2616 for the definition
+ * of a URL and for more specifics see RFC 2396 for the
+ * expressions.
+ */
+ protected void parse(){
+ if(count > 0){
+ if(buf[0] == '/'){
+ relativeURI();
+ }else{
+ absoluteURI();
+ }
+ }
+ }
+
+ /**
+ * This will empty each tokens cache. A tokens cache is used
+ * to represent a token once the token's <code>toString</code>
+ * method has been called. Thus when the <code>toString</code>
+ * method is called then the token depends on the value of the
+ * cache alone in further calls to <code>toString</code>.
+ * However if a URI has just been parsed and that method has
+ * not been invoked then the cache is created from the buf if
+ * its length is greater than zero.
+ */
+ protected void init(){
+ param.clear();
+ domain.clear();
+ path.clear();
+ query.clear();
+ scheme.clear();
+ off =port = 0;
+ normal = null;
+ data = null;
+ }
+
+ /**
+ * This is a specific definition of a type of URI. An absolute
+ * URI is a URI that contains a host and port. It is the most
+ * frequently used type of URI. This will define the host and
+ * the optional port part. As well as the relative URI part.
+ * This uses a simpler syntax than the one specified in RFC 2396
+ * <code><pre>
+ *
+ * absoluteURI = scheme ":" ("//" netpath | relativeURI)
+ * relativeURI = path ["?" querypart]
+ * netpath = domain [":" port] relativeURI
+ * path = *("/" segment)
+ * segment = *pchar *( ";" param )
+ *
+ * </pre></code>
+ * This syntax is sufficient to handle HTTP style URI's as well
+ * as GOPHER and FTP and various other 'simple' schemes. See
+ * RFC 2396 for the syntax of an <code>absoluteURI</code>.
+ */
+ private void absoluteURI(){
+ scheme();
+ netPath();
+ }
+
+ /**
+ * This will check to see if there is a scheme in the URI. If
+ * there is a scheme found in the URI this returns true and
+ * removes that scheme tag of the form "ftp:" or "http:"
+ * or whatever the protocol scheme tag may be for the URI.
+ * <p>
+ * The syntax for the scheme is given in RFC 2396 as follows
+ * <code><pre>
+ *
+ * scheme = alpha *( alpha | digit | "+" | "-" | "." )
+ *
+ * </pre></code>
+ * This will however also skips the "://" from the tag
+ * so of the URI was <code>gopher://domain/path</code> then
+ * the URI would be <code>domain/path</code> afterwards.
+ */
+ private void scheme(){
+ int mark = off;
+ int pos = off;
+
+ if(alpha(buf[off])){
+ while(off < count){
+ char next = buf[off++];
+
+ if(schemeChar(next)){
+ pos++;
+ }else if(next == ':'){
+ if(!skip("//")) {
+ off = mark;
+ pos = mark;
+ }
+ break;
+ }else{
+ off = mark;
+ pos = mark;
+ break;
+ }
+ }
+ scheme.len = pos - mark;
+ scheme.off = mark;
+ }
+ }
+
+ /**
+ * This method is used to assist the scheme method. This will
+ * check to see if the type of the character is the same as
+ * those described in RFC 2396 for a scheme character. The
+ * scheme tag can contain an alphanumeric of the following
+ * <code>"+", "-", "."</code>.
+ *
+ * @param c this is the character that is being checked
+ *
+ * @return this returns true if the character is a valid
+ * scheme character
+ */
+ private boolean schemeChar(char c){
+ switch(c){
+ case '+': case '-':
+ case '.':
+ return true;
+ default:
+ return alphanum(c);
+ }
+ }
+
+ /**
+ * The network path is the path that contains the network
+ * address of the host that this URI is targeted at. This
+ * will parse the domain name of the host and also a port
+ * number before parsing a relativeURI
+ * <code><pre>
+ *
+ * netpath = domain [":" port] relativeURI
+ *
+ * </pre></code>
+ * This syntax is modified from the URI specification on
+ * RFC 2396.
+ */
+ private void netPath(){
+ hostPort();
+ relativeURI();
+ }
+
+ /**
+ * This is used to extract the host and port combination.
+ * Typically a URI will not explicitly specify a port, however
+ * if there is a semicolon at the end of the domain it should
+ * be interpreted as the port part of the URI.
+ */
+ private void hostPort() {
+ domain();
+ if(skip(":")){
+ port();
+ }
+ }
+
+ /**
+ * This is a specific definition of a type of URI. A relative
+ * URI is a URI that contains no host or port. It is basically
+ * the resource within the host. This will extract the path and
+ * the optional query part of the URI. Rfc2396 has the proper
+ * definition of a <code>relativeURI</code>.
+ */
+ private void relativeURI(){
+ path();
+ if(skip("?")){
+ query();
+ }
+ }
+
+ /**
+ * This is used to extract the optional port from a given URI.
+ * This will read a sequence of digit characters and convert
+ * the <code>String</code> of digit characters into a decimal
+ * number. The digits will be added to the port variable. If
+ * there is no port number this will not update the read offset.
+ */
+ private void port() {
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ port *= 10;
+ port += buf[off];
+ port -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to extract the domain from the given URI. This
+ * will firstly initialize the token object that represents the
+ * domain. This allows the token's <code>toString</code> method to
+ * return the extracted value of the token rather than getting
+ * confused with previous values set by a previous parse method.
+ * <p>
+ * This uses the following delimiters to determine the end of the
+ * domain <code>?</code>,<code>:</code> and <code>/<code>. This
+ * ensures that the read offset does not go out of bounds and
+ * consequently throw an <code>IndexOutOfBoundsException</code>.
+ */
+ private void domain(){
+ int mark = off;
+
+ loop: while(off < count){
+ switch(buf[off]){
+ case '/': case ':':
+ case '?':
+ break loop;
+ default:
+ off++;
+ }
+ }
+ domain.len = off - mark;
+ domain.off = mark;
+ }
+
+ /**
+ * This is used to extract the segments from the given URI. This
+ * will firstly initialize the token object that represents the
+ * path. This allows the token's <code>toString</code> method to
+ * return the extracted value of the token rather than getting
+ * confused with previous values set by a previous parse method.
+ * <p>
+ * This is slightly different from RFC 2396 in that it defines a
+ * pchar as the RFC 2396 definition of a pchar without the escaped
+ * chars. So this method has to ensure that no escaped chars go
+ * unchecked. This ensures that the read offset does not go out
+ * of bounds and throw an <code>IndexOutOfBoundsException</code>.
+ */
+ private void path(){
+ int mark = off;
+ int pos = off;
+
+ while(skip("/")) {
+ buf[pos++] = '/';
+
+ while(off < count){
+ if(buf[off]==';'){
+ while(skip(";")){
+ param();
+ insert();
+ }
+ break;
+ }
+ if(buf[off]=='%'){
+ escape();
+ }else if(!pchar(buf[off])){
+ break;
+ }
+ buf[pos++]=buf[off++];
+ }
+ }
+ path.len = pos -mark;
+ path.off = mark;
+ }
+
+ /**
+ * This is used to extract the query from the given URI. This
+ * will firstly initialize the token object that represents the
+ * query. This allows the token's <code>toString</code> method
+ * to return the extracted value of the token rather than getting
+ * confused with previous values set by a previous parse method.
+ * The calculation of the query part of a URI is basically the
+ * end of the URI.
+ */
+ private void query() {
+ query.len = count - off;
+ query.off = off;
+ }
+
+ /**
+ * This is an expression that is defined by RFC 2396 it is used
+ * in the definition of a segment expression. This is basically
+ * a list of pchars.
+ * <p>
+ * This method has to ensure that no escaped chars go unchecked.
+ * This ensures that the read offset does not goe out of bounds
+ * and consequently throw an out of bounds exception.
+ */
+ private void param() {
+ name();
+ if(skip("=")){ /* in case of error*/
+ value();
+ }
+ }
+
+ /**
+ * This extracts the name of the parameter from the character
+ * buffer. The name of a parameter is defined as a set of
+ * pchars including escape sequences. This will extract the
+ * parameter name and buffer the chars. The name ends when a
+ * equals character, "=", is encountered or in the case of a
+ * malformed parameter when the next character is not a pchar.
+ */
+ private void name(){
+ int mark = off;
+ int pos = off;
+
+ while(off < count){
+ if(buf[off]=='%'){ /* escaped */
+ escape();
+ }else if(buf[off]=='=') {
+ break;
+ }else if(!pchar(buf[off])){
+ break;
+ }
+ buf[pos++] = buf[off++];
+ }
+ name.len = pos - mark;
+ name.off = mark;
+ }
+
+ /**
+ * This extracts a parameter value from a path segment. The
+ * parameter value consists of a sequence of pchars and some
+ * escape sequences. The parameter value is buffered so that
+ * the name and values can be paired. The end of the value
+ * is determined as the end of the buffer or the last pchar.
+ */
+ private void value(){
+ int mark = off;
+ int pos = off;
+
+ while(off < count){
+ if(buf[off]=='%'){ /* escaped */
+ escape();
+ }else if(!pchar(buf[off])) {
+ break;
+ }
+ buf[pos++] = buf[off++];
+ }
+ value.len = pos - mark;
+ value.off = mark;
+ }
+
+ /**
+ * This method adds the name and value to a map so that the next
+ * name and value can be collected. The name and value are added
+ * to the map as string objects. Once added to the map the
+ * <code>Token</code> objects are set to have zero length so they
+ * can be reused to collect further values. This will add the
+ * values to the map as an array of type string. This is done so
+ * that if there are multiple values that they can be stored.
+ */
+ private void insert(){
+ if(value.length() > 0){
+ if(name.length() > 0)
+ insert(name,value);
+ }
+ name.clear();
+ value.clear();
+ }
+
+ /**
+ * This will add the given name and value to the parameters map.
+ * This will only store a single value per parameter name, so
+ * only the parameter that was latest encountered will be saved.
+ * The <code>getQuery</code> method can be used to collect
+ * the parameter values using the parameter name.
+ *
+ * @param name this is the name of the value to be inserted
+ * @param value this is the value of a that is to be inserted
+ */
+ private void insert(Token name, Token value){
+ insert(name.toString(), value.toString());
+ }
+
+ /**
+ * This will add the given name and value to the parameters map.
+ * This will only store a single value per parameter name, so
+ * only the parameter that was latest encountered will be saved.
+ * The <code>getQuery</code> method can be used to collect
+ * the parameter values using the parameter name.
+ *
+ * @param name this is the name of the value to be inserted
+ * @param value this is the value of a that is to be inserted
+ */
+ private void insert(String name, String value) {
+ param.put(name, value);
+ }
+
+ /**
+ * This converts an encountered escaped sequence, that is all
+ * embedded hexidecimal characters into a native UCS character
+ * value. This does not take any characters from the stream it
+ * just prepares the buffer with the correct byte. The escaped
+ * sequence within the URI will be interpreded as UTF-8.
+ * <p>
+ * This will leave the next character to read from the buffer
+ * as the character encoded from the URI. If there is a fully
+ * valid escaped sequence, that is <code>"%" HEX HEX</code>.
+ * This decodes the escaped sequence using UTF-8 encoding, all
+ * encoded sequences should be in UCS-2 to fit in a Java char.
+ */
+ private void escape() {
+ int peek = peek(off);
+
+ if(!unicode(peek)) {
+ binary(peek);
+ }
+ }
+
+ /**
+ * This method determines, using a peek character, whether the
+ * sequence of escaped characters within the URI is binary data.
+ * If the data within the escaped sequence is binary then this
+ * will ensure that the next character read from the URI is the
+ * binary octet. This is used strictly for backward compatible
+ * parsing of URI strings, binary data should never appear.
+ *
+ * @param peek this is the first escaped character from the URI
+ *
+ * @return currently this implementation always returns true
+ */
+ private boolean binary(int peek) {
+ if(off + 2 < count) {
+ off += 2;
+ buf[off]= bits(peek);
+ }
+ return true;
+ }
+
+ /**
+ * This method determines, using a peek character, whether the
+ * sequence of escaped characters within the URI is in UTF-8. If
+ * a UTF-8 character can be successfully decoded from the URI it
+ * will be the next character read from the buffer. This can
+ * check for both UCS-2 and UCS-4 characters. However, because
+ * the Java <code>char</code> can only hold UCS-2, the UCS-4
+ * characters will have only the low order octets stored.
+ * <p>
+ * The WWW Consortium provides a reference implementation of a
+ * UTF-8 decoding for Java, in this the low order octets in the
+ * UCS-4 sequence are used for the character. So, in the
+ * absence of a defined behaviour, the W3C behaviour is assumed.
+ *
+ * @param peek this is the first escaped character from the URI
+ *
+ * @return this returns true if a UTF-8 character is decoded
+ */
+ private boolean unicode(int peek) {
+ if((peek & 0x80) == 0x00){
+ return unicode(peek, 0);
+ }
+ if((peek & 0xe0) == 0xc0){
+ return unicode(peek & 0x1f, 1);
+ }
+ if((peek & 0xf0) == 0xe0){
+ return unicode(peek & 0x0f, 2);
+ }
+ if((peek & 0xf8) == 0xf0){
+ return unicode(peek & 0x07, 3);
+ }
+ if((peek & 0xfc) == 0xf8){
+ return unicode(peek & 0x03, 4);
+ }
+ if((peek & 0xfe) == 0xfc){
+ return unicode(peek & 0x01, 5);
+ }
+ return false;
+ }
+
+ /**
+ * This method will decode the specified amount of escaped
+ * characters from the URI and convert them into a single Java
+ * UCS-2 character. If there are not enough characters within
+ * the URI then this will return false and leave the URI alone.
+ * <p>
+ * The number of characters left is determined from the first
+ * UTF-8 octet, as specified in RFC 2279, and because this is
+ * a URI there must that number of <code>"%" HEX HEX</code>
+ * sequences left. If successful the next character read is
+ * the UTF-8 sequence decoded into a native UCS-2 character.
+ *
+ * @param peek contains the bits read from the first UTF octet
+ * @param more this specifies the number of UTF octets left
+ *
+ * @return this returns true if a UTF-8 character is decoded
+ */
+ private boolean unicode(int peek, int more) {
+ if(off + more * 3 >= count) {
+ return false;
+ }
+ return unicode(peek,more,off);
+ }
+
+ /**
+ * This will decode the specified amount of trailing UTF-8 bits
+ * from the URI. The trailing bits are those following the first
+ * UTF-8 octet, which specifies the length, in octets, of the
+ * sequence. The trailing octets are if the form 10xxxxxx, for
+ * each of these octets only the last six bits are valid UCS
+ * bits. So a conversion is basically an accumulation of these.
+ * <p>
+ * If at any point during the accumulation of the UTF-8 bits
+ * there is a parsing error, then parsing is aborted an false
+ * is returned, as a result the URI is left unchanged.
+ *
+ * @param peek bytes that have been accumulated from the URI
+ * @param more this specifies the number of UTF octets left
+ * @param pos this specifies the position the parsing begins
+ *
+ * @return this returns true if a UTF-8 character is decoded
+ */
+ private boolean unicode(int peek, int more, int pos) {
+ while(more-- > 0) {
+ if(buf[pos] == '%'){
+ int next = pos + 3;
+ int hex = peek(next);
+
+ if((hex & 0xc0) == 0x80){
+ peek = (peek<<6)|(hex&0x3f);
+ pos = next;
+ continue;
+ }
+ }
+ return false;
+ }
+ if(pos + 2 < count) {
+ off = pos + 2;
+ buf[off]= bits(peek);
+ }
+ return true;
+ }
+
+ /**
+ * Defines behaviour for UCS-2 versus UCS-4 conversion from four
+ * octets. The UTF-8 encoding scheme enables UCS-4 characters to
+ * be encoded and decodeded. However, Java supports the 16-bit
+ * UCS-2 character set, and so the 32-bit UCS-4 character set is
+ * not compatable. This basically decides what to do with UCS-4.
+ *
+ * @param data up to four octets to be converted to UCS-2 format
+ *
+ * @return this returns a native UCS-2 character from the int
+ */
+ private char bits(int data) {
+ return (char)data;
+ }
+
+ /**
+ * This will return the escape expression specified from the URI
+ * as an integer value of the hexidecimal sequence. This does
+ * not make any changes to the buffer it simply checks to see if
+ * the characters at the position specified are an escaped set
+ * characters of the form <code>"%" HEX HEX</code>, if so, then
+ * it will convert that hexidecimal string in to an integer
+ * value, or -1 if the expression is not hexidecimal.
+ *
+ * @param pos this is the position the expression starts from
+ *
+ * @return the integer value of the hexidecimal expression
+ */
+ private int peek(int pos) {
+ if(buf[pos] == '%'){
+ if(count <= pos + 2) {
+ return -1;
+ }
+ char high = buf[pos + 1];
+ char low = buf[pos + 2];
+
+ return convert(high, low);
+ }
+ return -1;
+ }
+
+ /**
+ * This will convert the two hexidecimal characters to a real
+ * integer value, which is returned. This requires characters
+ * within the range of 'A' to 'F' and 'a' to 'f', and also
+ * the digits '0' to '9'. The characters encoded using the
+ * ISO-8859-1 encoding scheme, if the characters are not with
+ * in the range specified then this returns -1.
+ *
+ * @param high this is the high four bits within the integer
+ * @param low this is the low four bits within the integer
+ *
+ * @return this returns the indeger value of the conversion
+ */
+ private int convert(char high, char low) {
+ int hex = 0x00;
+
+ if(hex(high) && hex(low)){
+ if('A' <= high && high <= 'F'){
+ high -= 'A' - 'a';
+ }
+ if(high >= 'a') {
+ hex ^= (high-'a')+10;
+ } else {
+ hex ^= high -'0';
+ }
+ hex <<= 4;
+
+ if('A' <= low && low <= 'F') {
+ low -= 'A' - 'a';
+ }
+ if(low >= 'a') {
+ hex ^= (low-'a')+10;
+ } else {
+ hex ^= low-'0';
+ }
+ return hex;
+ }
+ return -1;
+ }
+
+ /**
+ * This is used to determine wheather a char is a hexidecimal
+ * <code>char</code> or not. A hexidecimal character is consdered
+ * to be a character within the range of <code>0 - 9</code> and
+ * between <code>a - f</code> and <code>A - F</code>. This will
+ * return <code>true</code> if the character is in this range.
+ *
+ * @param ch this is the character which is to be determined here
+ *
+ * @return true if the character given has a hexidecimal value
+ */
+ private boolean hex(char ch) {
+ if(ch >= '0' && ch <= '9') {
+ return true;
+ } else if(ch >='a' && ch <= 'f') {
+ return true;
+ } else if(ch >= 'A' && ch <= 'F') {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This is a character set defined by RFC 2396 it is used to
+ * determine the valididity of certain <code>chars</code>
+ * within a Uniform Resource Identifier. RFC 2396 defines
+ * an unreserved char as <code>alphanum | mark</code>.
+ *
+ * @param c the character value that is being checked
+ *
+ * @return true if the character has an unreserved value
+ */
+ private boolean unreserved(char c){
+ return mark(c) || alphanum(c);
+ }
+
+ /**
+ * This is used to determine wheather or not a given unicode
+ * character is an alphabetic character or a digit character.
+ * That is withing the range <code>0 - 9</code> and between
+ * <code>a - z</code> it uses <code>iso-8859-1</code> to
+ * compare the character.
+ *
+ * @param c the character value that is being checked
+ *
+ * @return true if the character has an alphanumeric value
+ */
+ private boolean alphanum(char c){
+ return digit(c) || alpha(c);
+ }
+
+ /**
+ * This is used to determine wheather or not a given unicode
+ * character is an alphabetic character. This uses encoding
+ * <code>iso-8859-1</code> to compare the characters.
+ *
+ * @param c the character value that is being checked
+ *
+ * @return true if the character has an alphabetic value
+ */
+ private boolean alpha(char c){
+ return (c <= 'z' && 'a' <= c) ||
+ (c <= 'Z' && 'A' <= c);
+ }
+
+ /**
+ * This is a character set defined by RFC 2396 it checks
+ * the valididity of cetain chars within a uniform resource
+ * identifier. The RFC 2396 defines a mark char as <code>"-",
+ * "_", ".", "!", "~", "*", "'", "(", ")"</code>.
+ *
+ * @param c the character value that is being checked
+ *
+ * @return true if the character is a mark character
+ */
+ private boolean mark(char c){
+ switch(c){
+ case '-': case '_': case '.':
+ case '!': case '~': case '*':
+ case '\'': case '(': case ')':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * This is a character set defined by RFC 2396 it is used to check
+ * the valididity of cetain chars within a generic uniform resource
+ * identifier. The RFC 2396 defines a pchar char as unreserved or
+ * escaped or one of the following characters <code>":", "@", "=",
+ * "&amp;", "+", "$", ","</code> this will not check to see if the
+ * char is an escaped char, that is <code>% HEX HEX</code>. Because
+ * this takes 3 chars.
+ *
+ * @param c the character value that is being checked
+ *
+ * @return true if the character is a pchar character
+ */
+ private boolean pchar(char c){
+ switch(c){
+ case '@': case '&': case '=':
+ case '+': case '$': case ',':
+ case ':':
+ return true;
+ default:
+ return unreserved(c);
+ }
+ }
+
+ /**
+ * This is a character set defined by RFC 2396, it checks the
+ * valididity of certain chars in a uniform resource identifier.
+ * The RFC 2396 defines a reserved char as <code>";", "/", "?",
+ * ":", "@", "&amp;", "=", "+", "$", ","</code>.
+ *
+ * @param c the character value that is being checked
+ *
+ * @return true if the character is a reserved character
+ */
+ private boolean reserved(char c){
+ switch(c){
+ case ';': case '/': case '?':
+ case '@': case '&': case ':':
+ case '=': case '+': case '$':
+ case ',':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * This is used to convert this URI object into a <code>String</code>
+ * object. This will only convert the parts of the URI that exist, so
+ * the URI may not contain the domain or the query part and it will
+ * not contain the path parameters. If the URI contains all these
+ * parts then it will return somthing like
+ * <pre>
+ * scheme://host:port/path/path?querypart
+ * </pre>
+ * <p>
+ * It can return <code>/path/path?querypart</code> style relative
+ * URI's. If any of the parts are set to null then that part will be
+ * missing, for example if <code>setDomain</code> method is invoked
+ * with a null parameter then the domain and port will be missing
+ * from the resulting URI. If the path part is set to null using the
+ * <code>setPath</code> then the path will be <code>/</code>. An
+ * example URI with the path part of null would be
+ * <pre>
+ * scheme://host:port/?querypart
+ * </pre>
+ *
+ * @return the URI with only the path part and the non-null optional
+ * parts of the uniform resource identifier
+ */
+ public String toString() {
+ return (scheme.length() > 0 ? scheme +"://": "") +
+ (domain.length() > 0 ? domain +
+ (port > 0 ? ":"+port : "") : "")+ getPath() +
+ (param.size() > 0 ? param : "")+
+ (query.length()>0?"?"+query :"");
+ }
+
+ /**
+ * The <code>ParameterMap</code> is uses to store the parameters
+ * that are to be encoded in to the address. This will append all
+ * of the parameters to the end of the path. These can later be
+ * extracted by parsing the address.
+ *
+ * @author Niall Gallagher
+ */
+ private class ParameterMap extends KeyMap<String> {
+
+ /**
+ * This will return the parameters encoded in such a way that
+ * it can be appended to the end of the path. These parameters
+ * can be added to the address such that they do not form a
+ * query parameter. Values such as session identifiers are
+ * often added as the path parameters to the address.
+ *
+ * @return this returns the representation of the parameters
+ */
+ private String encode() {
+ StringBuilder text = new StringBuilder();
+
+ for(String name : param) {
+ String value = param.get(name);
+
+ text.append(";");
+ text.append(name);
+
+ if(value != null) {
+ text.append("=");
+ text.append(value);;
+ }
+ }
+ return text.toString();
+ }
+
+ /**
+ * This will return the parameters encoded in such a way that
+ * it can be appended to the end of the path. These parameters
+ * can be added to the address such that they do not form a
+ * query parameter. Values such as session identifiers are
+ * often added as the path parameters to the address.
+ *
+ * @return this returns the representation of the parameters
+ */
+ public String toString() {
+ return encode();
+ }
+ }
+
+ /**
+ * This is used as an alternative to the <code>ParseBuffer</code>
+ * for extracting tokens from the URI without allocating memory.
+ * This will basically mark out regions within the buffer which are
+ * used to represent the token. When the token value is required
+ * the region is used to create a <code>String</code> object.
+ */
+ private class Token {
+
+ /**
+ * This can be used to override the value for this token.
+ */
+ public String value;
+
+ /**
+ * This represents the start offset within the buffer.
+ */
+ public int off;
+
+ /**
+ * This represents the number of charters in the token.
+ */
+ public int len;
+
+ /**
+ * If the <code>Token</code> is to be reused this will clear
+ * all previous data. Clearing the buffer allows it to be
+ * reused if there is a new URI to be parsed. This ensures
+ * that a null is returned if the token length is zero.
+ */
+ public void clear() {
+ value = null;
+ len = 0;
+ }
+
+ /**
+ * This is used to determine the number of characters this
+ * token contains. This is used rather than accessing the
+ * length directly so that the value the token represents
+ * can be overridden easily without upsetting the token.
+ *
+ * @return this returns the number of characters this uses
+ */
+ public int length() {
+ if(value == null){
+ return len;
+ }
+ return value.length();
+ }
+
+ /**
+ * This method will convert the <code>Token</code> into it's
+ * <code>String</code> equivelant. This will firstly check
+ * to see if there is a value, for the string representation,
+ * if there is the value is returned, otherwise the region
+ * is converted into a <code>String</code> and returned.
+ *
+ * @return this returns a value representing the token
+ */
+ public String toString() {
+ if(value != null) {
+ return value;
+ }
+ if(len > 0) {
+ value = new String(buf,off,len);
+ }
+ return value;
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentDispositionParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentDispositionParser.java
new file mode 100644
index 0000000..b7fe6c2
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentDispositionParser.java
@@ -0,0 +1,296 @@
+/*
+ * ContentDispositionParser.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.parse;
+
+import org.simpleframework.common.parse.ParseBuffer;
+import org.simpleframework.common.parse.Parser;
+import org.simpleframework.http.ContentDisposition;
+
+/**
+ * The <code>ContentDispositionParser</code> object is used to represent
+ * a parser used to parse the Content-Disposition header. Its used when
+ * there is a multipart form upload to the server and allows the
+ * server to determine the individual part types.
+ *
+ * @author Niall Gallagher
+ */
+public class ContentDispositionParser extends Parser implements ContentDisposition {
+
+ /**
+ * This is the buffer used to acquire values from the header.
+ */
+ private ParseBuffer skip;
+
+ /**
+ * This is used to capture the name of the file if it is provided.
+ */
+ private ParseBuffer file;
+
+ /**
+ * This is used to capture the name of the part if it is provided.
+ */
+ private ParseBuffer name;
+
+ /**
+ * This is used to determine if the disposition is a file or form.
+ */
+ private boolean form;
+
+ /**
+ * Constructor for the <code>ContentDispositionParser</code> object.
+ * This is used to create a parser that can parse a disposition
+ * header which is typically sent as part of a multipart upload. It
+ * can be used to determine the type of the upload.
+ */
+ public ContentDispositionParser() {
+ this.file = new ParseBuffer();
+ this.name = new ParseBuffer();
+ this.skip = new ParseBuffer();
+ }
+
+ /**
+ * Constructor for the <code>ContentDispositionParser</code> object.
+ * This is used to create a parser that can parse a disposition header
+ * which is typically sent as part of a multipart upload. It can
+ * be used to determine the type of the upload.
+ *
+ * @param text this is the header value that is to be parsed
+ */
+ public ContentDispositionParser(String text) {
+ this();
+ parse(text);
+ }
+
+ /**
+ * This method is used to acquire the file name of the part. This
+ * is used when the part represents a text parameter rather than
+ * a file. However, this can also be used with a file part.
+ *
+ * @return this returns the file name of the associated part
+ */
+ public String getFileName() {
+ return file.toString();
+ }
+
+ /**
+ * This method is used to acquire the name of the part. Typically
+ * this is used when the part represents a text parameter rather
+ * than a file. However, this can also be used with a file part.
+ *
+ * @return this returns the name of the associated part
+ */
+ public String getName() {
+ return name.toString();
+ }
+
+ /**
+ * This method is used to determine the type of a part. Typically
+ * a part is either a text parameter or a file. If this is true
+ * then the content represented by the associated part is a file.
+ *
+ * @return this returns true if the associated part is a file
+ */
+ public boolean isFile() {
+ return !form || file.length() > 0;
+ }
+
+ /**
+ * This will initialize the <code>Parser</code> when it is ready
+ * to parse a new <code>String</code>. This will reset the
+ * parser to a ready state. This method is invoked by the parser
+ * before the parse method is invoked, it is used to pack the
+ * contents of the header and clear any previous tokens used.
+ */
+ protected void init() {
+ if(count > 0) {
+ pack();
+ }
+ clear();
+ }
+ /**
+ * This is used to clear all previously collected tokens. This
+ * allows the parser to be reused when there are multiple source
+ * strings to be parsed. Clearing of the tokens is performed
+ * when the parser is initialized.
+ */
+ protected void clear() {
+ file.clear();
+ name.clear();
+ form = false;
+ off = 0;
+ }
+
+ /**
+ * This is the method that should be implemented to read the
+ * buffer. This method will extract the type from the header and
+ * the tries to extract the optional parameters if they are in
+ * the header. The optional parts are the file name and name.
+ */
+ protected void parse() {
+ type();
+ parameters();
+ }
+
+ /**
+ * This is used to remove all whitespace characters from the
+ * <code>String</code> excluding the whitespace within literals.
+ * The definition of a literal can be found in RFC 2616.
+ * <p>
+ * The definition of a literal for RFC 2616 is anything between 2
+ * quotes but excuding quotes that are prefixed with the backward
+ * slash character.
+ */
+ private void pack() {
+ char old = buf[0];
+ int len = count;
+ int seek = 0;
+ int pos = 0;
+
+ while(seek < len){
+ char ch = buf[seek++];
+
+ if(ch == '"' && old != '\\'){ /* qd-text*/
+ buf[pos++] = ch;
+
+ while(seek < len){
+ old = buf[seek-1];
+ ch = buf[seek++];
+ buf[pos++] = ch;
+
+ if(ch =='"'&& old!='\\'){ /*qd-text*/
+ break;
+ }
+ }
+ }else if(!space(ch)){
+ old = buf[seek - 1];
+ buf[pos++] = old;
+ }
+ }
+ count = pos;
+ }
+
+ /**
+ * This is used to determine the type of the disposition header. This
+ * will allow the parser to determine it the header represents form
+ * data or a file upload. Once it determines the type of the upload
+ * header it sets an internal flag which can be used.
+ */
+ private void type() {
+ if(skip("form-data")) {
+ form = true;
+ } else if(skip("file")) {
+ form = false;
+ }
+ }
+
+ /**
+ * This will read the parameters from the header value. This will search
+ * for the <code>filename</code> parameter within the set of parameters
+ * which are given to the type. The <code>filename</code> param and the
+ * the <code>name</code> are tokenized by this method.
+ */
+ private void parameters(){
+ while(skip(";")){
+ if(skip("filename=")){
+ value(file);
+ } else {
+ if(skip("name=")) {
+ value(name);
+ } else {
+ parameter();
+ }
+ }
+ }
+ }
+
+ /**
+ * This will read the parameters from the header value. This will search
+ * for the <code>filename</code> parameter within the set of parameters
+ * which are given to the type. The <code>filename</code> param and the
+ * the <code>name</code> are tokenized by this method.
+ */
+ private void parameter() {
+ name();
+ off++;
+ value(skip);
+ }
+
+ /**
+ * This will simply read all characters from the buffer before the first '='
+ * character. This represents a parameter name (see RFC 2616 for token). The
+ * parameter name is not buffered it is simply read from the buffer. This will
+ * not cause an <code>IndexOutOfBoundsException</code> as each offset
+ * is checked before it is acccessed.
+ */
+ private void name(){
+ while(off < count){
+ if(buf[off] =='='){
+ break;
+ }
+ off++;
+ }
+ }
+
+ /**
+ * This is used to read a parameters value from the buf. This will read all
+ * <code>char</code>'s upto but excluding the first terminal <code>char</code>
+ * encountered from the off within the buf, or if the value is a literal
+ * it will read a literal from the buffer (literal is any data between
+ * quotes except if the quote is prefixed with a backward slash character).
+ *
+ * @param value this is the parse buffer to append the value to
+ */
+ private void value(ParseBuffer value) {
+ if(quote(buf[off])) {
+ char quote = buf[off];
+
+ for(off++; off < count;) {
+ if(quote == buf[off]) {
+ if(buf[++off - 2] != '\\') {
+ break;
+ }
+ }
+
+ value.append(buf[off++]);
+ }
+ } else {
+ while(off < count) {
+ if(buf[off] == ';') {
+ break;
+ }
+
+ value.append(buf[off]);
+ off++;
+ }
+ }
+ }
+
+ /**
+ * This method is used to determine if the specified character is a quote
+ * character. The quote character is typically used as a boundary for the
+ * values within the header. This accepts a single or double quote.
+ *
+ * @param ch the character to determine if it is a quotation
+ *
+ * @return true if the character provided is a quotation character
+ */
+ private boolean quote(char ch) {
+ return ch == '\'' || ch == '"';
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentTypeParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentTypeParser.java
new file mode 100644
index 0000000..f42c073
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ContentTypeParser.java
@@ -0,0 +1,556 @@
+/*
+ * ContentTypeParser.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.parse;
+
+import org.simpleframework.common.KeyMap;
+import org.simpleframework.common.parse.ParseBuffer;
+import org.simpleframework.common.parse.Parser;
+import org.simpleframework.http.ContentType;
+
+/**
+ * This provides access to the MIME type parts, that is the primary
+ * type, the secondary type and an optional character set parameter.
+ * The <code>charset</code> parameter is one of many parameters that
+ * can be associated with a MIME type. This however this exposes this
+ * parameter with a typed method.
+ * <p>
+ * The <code>getCharset</code> will return the character encoding the
+ * content type is encoded within. This allows the user of the content
+ * to decode it correctly. Other parameters can be acquired from this
+ * by simply providing the name of the parameter.
+ *
+ * @author Niall Gallagher
+ */
+public class ContentTypeParser extends Parser implements ContentType {
+
+ /**
+ * Used to store the characters consumed for the secondary type.
+ */
+ private ParseBuffer secondary;
+
+ /**
+ * Used to store the characters consumed for the primary type.
+ */
+ private ParseBuffer primary;
+
+ /**
+ * Used to store the characters for the charset parameter.
+ */
+ private ParseBuffer charset;
+
+ /**
+ * Used to store the characters consumed for the type.
+ */
+ private ParseBuffer type;
+
+ /**
+ * Used to collect the name of a content type parameter.
+ */
+ private ParseBuffer name;
+
+ /**
+ * Used to collect the value of the content type parameter.
+ */
+ private ParseBuffer value;
+
+ /**
+ * Used to store the name value pairs of the parameters.
+ */
+ private KeyMap<String> map;
+
+ /**
+ * The default constructor will create a <code>ContentParser</code>
+ * that contains no charset, primary or secondary. This can be used
+ * to extract the primary, secondary and the optional charset
+ * parameter by using the parser's <code>parse(String)</code>
+ * method.
+ */
+ public ContentTypeParser(){
+ this.secondary = new ParseBuffer();
+ this.primary = new ParseBuffer();
+ this.charset = new ParseBuffer();
+ this.value = new ParseBuffer();
+ this.type = new ParseBuffer();
+ this.name = new ParseBuffer();
+ this.map = new KeyMap<String>();
+ }
+
+ /**
+ * This is primarily a convenience constructor. This will parse
+ * the <code>String</code> given to extract the MIME type. This
+ * could be achieved by calling the default no-arg constructor
+ * and then using the instance to invoke the <code>parse</code>
+ * method on that <code>String</code>.
+ *
+ * @param header <code>String</code> containing a MIME type value
+ */
+ public ContentTypeParser(String header){
+ this();
+ parse(header);
+ }
+
+ /**
+ * This method is used to get the primary and secondary parts
+ * joined together with a "/". This is typically how a content
+ * type is examined. Here convenience is most important, we can
+ * easily compare content types without any parameters.
+ *
+ * @return this returns the primary and secondary types
+ */
+ public String getType() {
+ return type.toString();
+ }
+
+ /**
+ * This sets the primary type to whatever value is in the string
+ * provided is. If the string is null then this will contain a
+ * null string for the primary type of the parameter, which is
+ * likely invalid in most cases.
+ *
+ * @param value the type to set for the primary type of this
+ */
+ public void setPrimary(String value) {
+ type.reset(value);
+ type.append('/');
+ type.append(secondary);
+ primary.reset(value);
+ }
+
+ /**
+ * This is used to retrieve the primary type of this MIME type. The
+ * primary type part within the MIME type defines the generic type.
+ * For example <code>text/plain; charset=UTF-8</code>. This will
+ * return the text value. If there is no primary type then this
+ * will return <code>null</code> otherwise the string value.
+ *
+ * @return the primary type part of this MIME type
+ */
+ public String getPrimary() {
+ return primary.toString();
+ }
+
+ /**
+ * This sets the secondary type to whatever value is in the string
+ * provided is. If the string is null then this will contain a
+ * null string for the secondary type of the parameter, which is
+ * likely invalid in most cases.
+ *
+ * @param value the type to set for the primary type of this
+ */
+ public void setSecondary(String value) {
+ type.reset(primary);
+ type.append('/');
+ type.append(value);
+ secondary.reset(value);
+ }
+
+ /**
+ * This is used to retrieve the secondary type of this MIME type.
+ * The secondary type part within the MIME type defines the generic
+ * type. For example <code>text/html; charset=UTF-8</code>. This
+ * will return the HTML value. If there is no secondary type then
+ * this will return <code>null</code> otherwise the string value.
+ *
+ * @return the primary type part of this MIME type
+ */
+ public String getSecondary(){
+ return secondary.toString();
+ }
+
+ /**
+ * This will set the <code>charset</code> to whatever value the
+ * string contains. If the string is null then this will not set
+ * the parameter to any value and the <code>toString</code> method
+ * will not contain any details of the parameter.
+ *
+ * @param enc parameter value to add to the MIME type
+ */
+ public void setCharset(String enc) {
+ charset.reset(enc);
+ }
+
+ /**
+ * This is used to retrieve the <code>charset</code> of this MIME
+ * type. This is a special parameter associated with the type, if
+ * the parameter is not contained within the type then this will
+ * return null, which typically means the default of ISO-8859-1.
+ *
+ * @return the value that this parameter contains
+ */
+ public String getCharset() {
+ return charset.toString();
+ }
+
+ /**
+ * This is used to retrieve an arbitrary parameter from the MIME
+ * type header. This ensures that values for <code>boundary</code>
+ * or other such parameters are not lost when the header is parsed.
+ * This will return the value, unquoted if required, as a string.
+ *
+ * @param name this is the name of the parameter to be retrieved
+ *
+ * @return this is the value for the parameter, or null if empty
+ */
+ public String getParameter(String name) {
+ return map.get(name);
+ }
+
+ /**
+ * This will add a named parameter to the content type header. If
+ * a parameter of the specified name has already been added to the
+ * header then that value will be replaced by the new value given.
+ * Parameters such as the <code>boundary</code> as well as other
+ * common parameters can be set with this method.
+ *
+ * @param name this is the name of the parameter to be added
+ * @param value this is the value to associate with the name
+ */
+ public void setParameter(String name, String value) {
+ map.put(name, value);
+ }
+
+ /**
+ * This will initialize the parser when it is ready to parse
+ * a new <code>String</code>. This will reset the parser to a
+ * ready state. The init method is invoked by the parser when
+ * the <code>Parser.parse</code> method is invoked.
+ */
+ protected void init(){
+ if(count > 0) {
+ pack();
+ }
+ clear();
+ }
+
+ /**
+ * This is used to clear all previously collected tokens. This
+ * allows the parser to be reused when there are multiple source
+ * strings to be parsed. Clearing of the tokens is performed
+ * when the parser is initialized.
+ */
+ private void clear() {
+ primary.clear();
+ secondary.clear();
+ charset.clear();
+ name.clear();
+ value.clear();
+ type.clear();
+ map.clear();
+ off = 0;
+ }
+
+ /**
+ * Reads and parses the MIME type from the given <code>String</code>
+ * object. This uses the syntax defined by RFC 2616 for the media-type
+ * syntax. This parser is only concerned with one parameter, the
+ * <code>charset</code> parameter. The syntax for the media type is
+ * <pre>
+ * media-type = token "/" token *( ";" parameter )
+ * parameter = token | literal
+ * </pre>
+ */
+ protected void parse(){
+ primary();
+ off++;
+ secondary();
+ parameters();
+ }
+
+ /**
+ * This is used to remove all whitespace characters from the
+ * <code>String</code> excluding the whitespace within literals.
+ * The definition of a literal can be found in RFC 2616.
+ * <p>
+ * The definition of a literal for RFC 2616 is anything between 2
+ * quotes but excluding quotes that are prefixed with the backward
+ * slash character.
+ */
+ private void pack() {
+ char old = buf[0];
+ int len = count;
+ int seek = 0;
+ int pos = 0;
+
+ while(seek < len){
+ char ch = buf[seek++];
+
+ if(ch == '"' && old != '\\'){ /* qd-text*/
+ buf[pos++] = ch;
+
+ while(seek < len){
+ old = buf[seek-1];
+ ch = buf[seek++];
+ buf[pos++] = ch;
+
+ if(ch =='"'&& old!='\\'){ /*qd-text*/
+ break;
+ }
+ }
+ }else if(!space(ch)){
+ old = buf[seek - 1];
+ buf[pos++] = old;
+ }
+ }
+ count = pos;
+ }
+
+ /**
+ * This reads the type from the MIME type. This will fill the
+ * type <code>ParseBuffer</code>. This will read all chars
+ * upto but not including the first instance of a '/'. The type
+ * of a media-type as defined by RFC 2616 is
+ * <code>type/subtype;param=val;param2=val</code>.
+ */
+ private void primary(){
+ while(off < count){
+ if(buf[off] =='/'){
+ type.append('/');
+ break;
+ }
+ type.append(buf[off]);
+ primary.append(buf[off]);
+ off++;
+ }
+ }
+
+ /**
+ * This reads the subtype from the MIME type. This will fill the
+ * subtype <code>ParseBuffer</code>. This will read all chars
+ * upto but not including the first instance of a ';'. The subtype
+ * of a media-type as defined by RFC 2616 is
+ * <code>type/subtype;param=val;param2=val</code>.
+ */
+ private void secondary(){
+ while(off < count){
+ if(buf[off] ==';'){
+ break;
+ }
+ type.append(buf[off]);
+ secondary.append(buf[off]);
+ off++;
+ }
+ }
+
+ /**
+ * This will read the parameters from the MIME type. This will search
+ * for the <code>charset</code> parameter within the set of parameters
+ * which are given to the type. The <code>charset</code> param is the
+ * only parameter that this parser will tokenize.
+ * <p>
+ * This will remove any parameters that preceed the charset parameter.
+ * Once the <code>charset</code> is retrived the MIME type is considered
+ * to be parsed.
+ */
+ private void parameters(){
+ while(skip(";")){
+ if(skip("charset=")){
+ charset();
+ break;
+ }else{
+ parameter();
+ insert();
+ }
+ }
+ }
+
+ /**
+ * This will add the name and value tokens to the parameters map.
+ * If any previous value of the given name has been inserted
+ * into the map then this will overwrite that value. This is
+ * used to ensure that the string value is inserted to the map.
+ */
+ private void insert() {
+ insert(name, value);
+ name.clear();
+ value.clear();
+ }
+
+ /**
+ * This will add the given name and value to the parameters map.
+ * If any previous value of the given name has been inserted
+ * into the map then this will overwrite that value. This is
+ * used to ensure that the string value is inserted to the map.
+ *
+ * @param name this is the name of the value to be inserted
+ * @param value this is the value of a that is to be inserted
+ */
+ private void insert(ParseBuffer name, ParseBuffer value) {
+ map.put(name.toString(), value.toString());
+ }
+
+ /**
+ * This is a parameter as defined by RFC 2616. The parameter is added to a
+ * MIME type e.g. <code>type/subtype;param=val</code> etc. The parameter
+ * name and value are not stored. This is used to simply update the read
+ * offset past the parameter. The reason for reading the parameters is to
+ * search for the <code>charset</code> parameter which will indicate the
+ * encoding.
+ */
+ private void parameter(){
+ name();
+ off++; /* = */
+ value();
+ }
+
+ /**
+ * This will simply read all characters from the buffer before the first '='
+ * character. This represents a parameter name (see RFC 2616 for token). The
+ * parameter name is not buffered it is simply read from the buffer. This will
+ * not cause an <code>IndexOutOfBoundsException</code> as each offset
+ * is checked before it is acccessed.
+ */
+ private void name(){
+ while(off < count){
+ if(buf[off] =='='){
+ break;
+ }
+ name.append(buf[off]);
+ off++;
+ }
+ }
+
+ /**
+ * This is used to read a parameters value from the buf. This will read all
+ * <code>char</code>'s upto but excluding the first terminal <code>char</code>
+ * encountered from the off within the buf, or if the value is a literal
+ * it will read a literal from the buffer (literal is any data between
+ * quotes except if the quote is prefixed with a backward slash character).
+ */
+ private void value(){
+ if(quote(buf[off])){
+ for(off++; off < count;){
+ if(quote(buf[off])){
+ if(buf[++off-2]!='\\'){
+ break;
+ }
+ }
+ value.append(buf[off++]);
+ }
+ }else{
+ while(off < count){
+ if(buf[off] ==';') {
+ break;
+ }
+ value.append(buf[off]);
+ off++;
+ }
+ }
+ }
+
+ /**
+ * This method is used to determine if the specified character is a quote
+ * character. The quote character is typically used as a boundary for the
+ * values within the header. This accepts a single or double quote.
+ *
+ * @param ch the character to determine if it is a quotation
+ *
+ * @return true if the character provided is a quotation character
+ */
+ private boolean quote(char ch) {
+ return ch == '\'' || ch == '"';
+ }
+
+ /**
+ * This is used to read the value from the <code>charset</code> param.
+ * This will fill the <code>charset</code> <code>ParseBuffer</code> and with
+ * the <code>charset</code> value. This will read a literal or a token as
+ * the <code>charset</code> value. If the <code>charset</code> is a literal
+ * then the quotes will be read as part of the charset.
+ */
+ private void charset(){
+ if(buf[off] == '"'){
+ charset.append('"');
+ for(off++; off < count;){
+ charset.append(buf[off]);
+ if(buf[off++]=='"')
+ if(buf[off-2]!='\\'){
+ break;
+ }
+ }
+ }else{
+ while(off < count){
+ if(buf[off]==';') {
+ break;
+ }
+ charset.append(buf[off]);
+ off++;
+ }
+ }
+ }
+
+ /**
+ * This will return the value of the MIME type as a string. This
+ * will concatenate the primary and secondary type values and
+ * add the <code>charset</code> parameter to the type which will
+ * recreate the content type.
+ *
+ * @return this returns the string representation of the type
+ */
+ private String encode() {
+ StringBuilder text = new StringBuilder();
+
+ if(primary != null) {
+ text.append(primary);
+ text.append("/");
+ text.append(secondary);
+ }
+ if(charset.length() > 0) {
+ text.append("; charset=");
+ text.append(charset);
+ }
+ return encode(text);
+ }
+
+ /**
+ * This will return the value of the MIME type as a string. This
+ * will concatenate the primary and secondary type values and
+ * add the <code>charset</code> parameter to the type which will
+ * recreate the content type.
+ *
+ * @param text this is the buffer to encode the parameters to
+ *
+ * @return this returns the string representation of the type
+ */
+ private String encode(StringBuilder text) {
+ for(String name : map) {
+ String value = map.get(name);
+
+ text.append("; ");
+ text.append(name);
+
+ if(value != null) {
+ text.append("=");
+ text.append(value);;
+ }
+ }
+ return text.toString();
+ }
+
+ /**
+ * This will return the value of the MIME type as a string. This
+ * will concatenate the primary and secondary type values and
+ * add the <code>charset</code> parameter to the type which will
+ * recreate the content type.
+ *
+ * @return this returns the string representation of the type
+ */
+ public String toString() {
+ return encode();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/CookieParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/CookieParser.java
new file mode 100644
index 0000000..1d07c04
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/CookieParser.java
@@ -0,0 +1,589 @@
+/*
+ * CookieParser.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.parse;
+
+import org.simpleframework.common.parse.Parser;
+import org.simpleframework.http.Cookie;
+
+import java.util.Iterator;
+
+/**
+ * CookieParser is used to parse the cookie header. The cookie header is
+ * one of the headers that is used by the HTTP state management mechanism.
+ * The Cookie header is the header that is sent from the client to the
+ * server in response to a Set-Cookie header. The syntax of the Cookie
+ * header as taken from RFC 2109, HTTP State Management Mechanism.
+ * <pre>
+ *
+ * cookie = "Cookie:" cookie-version
+ * 1*((";" | ",") cookie-value)
+ * cookie-value = NAME "=" VALUE [";" path] [";" domain]
+ * cookie-version = "$Version" "=" value
+ * NAME = attr
+ * VALUE = value
+ * path = "$Path" "=" value
+ * domain = "$Domain" "=" value
+ *
+ * </pre>
+ * The cookie header may consist of several cookies. Each cookie can be
+ * extracted from the header by examining the it syntax of the cookie
+ * header. The syntax of the cookie header is defined in RFC 2109.
+ * <p>
+ * Each cookie has a <code>$Version</code> attribute followed by multiple
+ * cookies. Each contains a name and a value, followed by an optional
+ * <code>$Path</code> and <code>$Domain</code> attribute. This will parse
+ * a given cookie header and return each cookie extracted as a
+ * <code>Cookie</code> object.
+ *
+ * @author Niall Gallagher
+ */
+public class CookieParser extends Parser implements Iterable<Cookie> {
+
+ /**
+ * Determines when the <code>Parser</code> has finished.
+ */
+ private boolean finished;
+
+ /**
+ * Used so the <code>Parser</code> does not parse twice.
+ */
+ private boolean parsed;
+
+ /**
+ * Version of the <code>Cookie</code> being parsed.
+ */
+ private int version;
+
+ /**
+ * Used to store the name of the <code>Cookie</code>.
+ */
+ private Token name;
+
+ /**
+ * Used to store the value of the <code>Cookie</code>.
+ */
+ private Token value;
+
+ /**
+ * Used to store the <code>$Path</code> values.
+ */
+ private Token path;
+
+ /**
+ * Used to store the <code>$Domain</code> values.
+ */
+ private Token domain;
+
+ /**
+ * Create a <code>CookieParser</code> that contains no cookies.
+ * the instance will return <code>false</code> for the
+ * <code>hasNext</code> method. cookies may be parsed using
+ * this instance by using the <code>parse</code> method.
+ */
+ public CookieParser(){
+ this.path = new Token();
+ this.domain = new Token();
+ this.name = new Token();
+ this.value = new Token();
+ this.finished = true;
+ }
+
+ /**
+ * This is primarily a convineance constructor. This will parse the
+ * <code>String</code> given to extract the cookies. This could be
+ * achived by calling the default no-arg constructor and then using
+ * the instance to invoke the <code>parse</code> method on that
+ * <code>String</code>.
+ *
+ * @param header a <code>String</code> containing a cookie value
+ */
+ public CookieParser(String header){
+ this();
+ parse(header);
+ }
+
+ /**
+ * Resets the cookie and the buffer variables for this
+ * <code>CookieParser</code>. It is used to set the
+ * state of the parser to start parsing a new cookie.
+ */
+ protected void init() {
+ finished = false;
+ parsed =false;
+ version = 0;
+ off = 0;
+ version();
+ }
+
+ /**
+ * This will extract the next <code>Cookie</code> from the
+ * buffer. If all the characters in the buffer have already
+ * been examined then this method will simply do nothing.
+ * Otherwise this will parse the remainder of the buffer
+ * and (if it follows RFC 2109) produce a <code>Cookie</code>.
+ */
+ protected void parse() {
+ if(!finished){
+ cookie();
+ parsed=true;
+ }
+ }
+
+ /**
+ * This is used to skip an arbitrary <code>String</code> within the
+ * <code>char</code> buf. It checks the length of the <code>String</code>
+ * first to ensure that it will not go out of bounds. A comparison
+ * is then made with the buffers contents and the <code>String</code>
+ * if the reigon in the buffer matched the <code>String</code> then the
+ * offset within the buffer is increased by the <code>String</code>'s
+ * length so that it has effectively skipped it.
+ * <p>
+ * This <code>skip</code> method will ignore all of the whitespace text.
+ * This will also skip trailing spaces within the the input text and
+ * all spaces within the source text. For example if the input was
+ * the string "s omete xt" and the source was "some text to skip" then
+ * the result of a skip ignoring spaces would be "to skip" in the
+ * source string, as the trailing spaces are also eaten by this.
+ *
+ * @param text this is the <code>String</code> value to be skipped
+ *
+ * @return true if the <code>String</code> was skipped
+ */
+ protected boolean skip(String text){
+ int size = text.length();
+ int seek = off;
+ int read = 0;
+
+ if(off + size > count){
+ return false;
+ }
+ while(read < size) {
+ char a = text.charAt(read);
+ char b = buf[seek];
+
+ if(space(b)){
+ if(++seek >= count){
+ return false;
+ }
+ }else if(space(a)){
+ if(++read >= size) {
+ continue;
+ }
+ }else {
+ if(toLower(a) != toLower(b)){
+ return false;
+ }
+ read++;
+ seek++;
+ }
+ }
+ for(off = seek; off < count; off++){
+ if(!space(buf[off]))
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * This is used to acquire the cookie values from the provided
+ * the provided source text. This allows the cookie parser to be
+ * used within a for each loop to parse out the values of a
+ * cookie one by one so that they may be used or stored.
+ *
+ * @return this returns an iterator for extracting cookie value
+ */
+ public Iterator<Cookie> iterator() {
+ return new Sequence();
+ }
+
+ /**
+ * This is used so that the collection of <code>Cookies</code>
+ * can be reiterated. This allows the collection to be reused.
+ * The <code>reset</code> method will invoke the super classes
+ * <code>init</code> method. This will reinitialize this
+ * <code>Parser</code> so the cookie will be reparsed.
+ */
+ public void reset() {
+ init();
+ parse();
+ }
+
+ /**
+ * Creates the <code>Cookie</code> from the token objects. It is
+ * assumed that the <code>Cookie</code> <code>String</code> has
+ * been parsed when this is called. This should only be used after
+ * the <code>parse</code> method has been called.
+ * <p>
+ * If there is no <code>$Domain</code> or <code>$Path</code>
+ * within the <code>Cookie</code> <code>String</code> then the
+ * <code>getDomain</code> and <code>getPath</code> are null.
+ *
+ * @return the <code>Cookie</code> that was just parsed
+ */
+ private Cookie getCookie() {
+ return getCookie(name.toString(),
+ value.toString());
+ }
+
+ /**
+ * Creates the <code>Cookie</code> from the token objects. It is
+ * assumed that the <code>Cookie</code> <code>String</code> has
+ * been parsed when this is called. This should only be used after
+ * the <code>parse</code> method has been called.
+ * <p>
+ * If there is no <code>$Domain</code> or <code>$Path</code>
+ * within the <code>Cookie</code> <code>String</code> then the
+ * <code>getDomain</code> and <code>getPath</code> are null.
+ *
+ * @param name the name that the <code>Cookie</code> contains
+ * @param value the value that the <code>Cookie</code> contains
+ *
+ * @return the <code>Cookie</code> that was just parsed
+ */
+ private Cookie getCookie(String name, String value) {
+ Cookie cookie = new Cookie(name, value, false);
+
+ if(domain.len > 0) {
+ cookie.setDomain(domain.toString());
+ }
+ if(path.len > 0) {
+ cookie.setPath(path.toString());
+ }
+ cookie.setVersion(version);
+ return cookie;
+ }
+
+ /**
+ * This is used to parse a <code>Cookie</code> from the buffer
+ * that contains the <code>Cookie</code> values. This will first
+ * try to remove any trailing value after the version/prev
+ * <code>Cookie</code> once this is removed it will extract the
+ * name/value pair from the <code>Cookie</code>. The name and
+ * value of the <code>Cookie</code> will be saved by the name
+ * and value tokens.
+ */
+ private void cookie(){
+ if(!skip(",")){ /* ,|; */
+ skip(";");
+ }
+ name();
+ skip("="); /* = */
+ value();
+ }
+
+ /**
+ * This initializes the name token and extracts the name of this
+ * <code>Cookie</code>. The offset and length of the name will be
+ * saved in the name token. This will read all <code>char</code>'s
+ * upto but excluding the first '=' <code>char</code> encountered
+ * from the <code>off</code> within the buffer.
+ */
+ private void name() {
+ name.off = off;
+ name.len = 0;
+ while(off < count){
+ if(buf[off] == '='){
+ break;
+ }
+ name.len++;
+ off++;
+ }
+ }
+
+ /**
+ * Used to extract everything found after the <code>NAME '='</code>
+ * within a <code>Cookie</code>. This extracts the <code>Cookie</code>
+ * value the <code>$Path</code> and <code>$Domain</code> attributes
+ * if they exist (i.e. <code>$Path</code> and <code>$Domain</code>
+ * are optional in a cookie see RFC 2109).
+ * <p>
+ * The path method reads the terminal found before it as does the
+ * <code>domain</code> method that is ";$Path" is read as the first
+ * part of the path method. This is because if there is no path the
+ * parser should not read data it does not know belongs to a specific
+ * part of the <code>Cookie</code>.
+ */
+ private void value() {
+ data();
+ path();
+ domain();
+ }
+
+ /**
+ * This initializes the value token and extracts the value of this
+ * <code>Cookie</code>. The offset and length of the value will be
+ * saved in the value token. This will read all <code>char</code>'s
+ * upto but excluding the first terminal char encountered from the
+ * off within the buffer, or if the value is a literal it will read
+ * a literal from the buffer (literal is any data between quotes
+ * except if the quote is prefixed with a backward slash character
+ * that is '\').
+ */
+ private void data() {
+ value.off = off;
+ value.len = 0;
+ if(off < count && buf[off] == '"'){
+ value.len++;
+ for(off++; off < count;){
+ value.len++;
+ if(buf[off++]=='"')
+ if(buf[off-2]!='\\'){
+ break;
+ }
+ }
+ value.len-=2; /* remove " */
+ value.off++; /* remove " */
+ }else {
+ while(off < count){
+ if(terminal(buf[off]))
+ break;
+ value.len++;
+ off++;
+ }
+ }
+ }
+
+ /**
+ * This initializes the path token and extracts the <code>$Path</code>
+ * of this <code>Cookie</code>. The offset and length of the path will
+ * be saved in the path token. This will read all <code>char</code>'s
+ * up to but excluding the first terminal <code>char</code> encountered
+ * from the <code>off</code> within the buffer, or if the value is a
+ * literal it will read a literal from the buffer (literal is any data
+ * between quotes except if the quote is prefixed with a backward slash
+ * character, that is '\').
+ * <p>
+ * This reads the terminal before the <code>$Path</code> so that if
+ * there is no <code>$Path</code> for the <code>Cookie</code> then
+ * the character before it will not be read needlessly.
+ */
+ private void path() {
+ path.len = 0; /* reset */
+ if(skip(";$Path=")){
+ path.off = off;
+ if(buf[off] == '"'){
+ path.len++;
+ for(off++; off < count;){
+ path.len++;
+ if(buf[off++]=='"')
+ if(buf[off-2]!='\\'){
+ break;
+ }
+ }
+ path.len-=2; /* remove " */
+ path.off++; /* remove " */
+ }else{
+ while(off < count){
+ if(terminal(buf[off]))
+ break;
+ path.len++;
+ off++;
+ }
+ }
+ }
+ }
+
+ /**
+ * Initializes the domain token and extracts the <code>$Domain</code>
+ * of this <code>Cookie</code>. The offset and length of the domain
+ * will be saved in the path token. This will read all characters up
+ * to but excluding the first terminal <code>char</code> encountered
+ * from the off within the buffer, or if the value is a literal it
+ * will read a literal from the buffer (literal is any data between
+ * quotes except if the quote is prefixed with a backward slash
+ * character, that is '\').
+ * <p>
+ * This reads the terminal before the <code>$Domain</code> so that
+ * if there is no <code>$Domain</code> for the <code>Cookie</code>
+ * then the character before it will not be read needlessly.
+ */
+ private void domain(){
+ domain.len = 0; /* reset */
+ if(skip(";$Domain=")) {
+ domain.off = off;
+ if(buf[off] == '"'){
+ domain.len++;
+ for(off++; off < count;){
+ domain.len++;
+ if(buf[off++]=='"')
+ if(buf[off-2]!='\\'){
+ break;
+ }
+ }
+ domain.len-=2; /* remove " */
+ domain.off++; /* remove " */
+ }else{
+ while(off < count){
+ if(terminal(buf[off]))
+ break;
+ domain.len++;
+ off++;
+ }
+ }
+ }
+ }
+
+ /**
+ * This extracts the <code>$Version</code> of this <code>Cookie</code>.
+ * The version is parsed and converted into a decimal int from the digit
+ * characters that make up a version.
+ * <p>
+ * This will read all digit <code>char</code>'s up to but excluding the
+ * first non digit <code>char</code> that it encounters from the offset
+ * within the buffer, or if the value is a literal it will read a literal
+ * from the buffer (literal is any data between quotes except if the quote
+ * is prefixed with a backward slash character i.e. '\').
+ */
+ private void version(){
+ if(skip("$Version=")) {
+ if(buf[off] == '"'){
+ off++;
+ }
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ version *= 10;
+ version += buf[off];
+ version -= '0';
+ off++;
+ }
+ if(buf[off] == '"'){
+ off++;
+ }
+ }else{
+ version = 1;
+ }
+ }
+
+ /**
+ * This is used to determine if a given iso8859-1 character is
+ * a terminal character. That is either the ';' or ','
+ * characters. Although the RFC 2109 says the terminal can be
+ * either a comma, it is not used by any browsers.
+ *
+ * @param ch the character that is to be compared
+ *
+ * @return true if this is a semicolon character
+ */
+ private boolean terminal(char ch) {
+ return ch == ';';
+ }
+
+ /**
+ * This is used to represent an <code>Iterator</code> that will
+ * iterate over the available cookies within the provided source
+ * text. This allows the cookie parser to be used as an iterable
+ * with for each loops. Cookies can not be removed with this.
+ */
+ private class Sequence implements Iterator<Cookie> {
+
+ /**
+ * Extracts the next <code>Cookie</code> object from the string
+ * given. This will return <code>null</code> when there are no
+ * more cookies left in the <code>String</code> being parsed.
+ * <p>
+ * To find out when there are no more cookies left use the
+ * <code>hasNext</code> method. This will only set the name,
+ * value, path, domain name version of the <code>cookie</code>
+ * because as of RFC 2109 these are the only attributes a
+ * <code>Cookie</code> may have, the path and domain are
+ * optional.
+ *
+ * @return an initialized <code>Cookie</code> object
+ */
+ public Cookie next(){
+ if(!hasNext()) {
+ return null;
+ }
+ parsed = false;
+ return getCookie();
+ }
+
+
+ /**
+ * Determine whether or not there are any <code>Cookie</code>s
+ * left in the <code>String</code>. This will attempt to extract
+ * another <code>Cookie</code> from the <code>String</code> and
+ * cache the result so the <code>next</code> method will produce
+ * this <code>Cookie</code>. If another <code>Cookie</code> cannot
+ * be parsed from the remainder of the <code>String</code> then
+ * this will return <code>false</code> otherwise it will return
+ * <code>true</code>.
+ *
+ * @return true if there are more cookies false otherwise
+ */
+ public boolean hasNext(){
+ if(finished) {
+ return false;
+ }
+ if(parsed) {
+ return true;
+ }
+ parse();
+
+ if(name.len <=0){
+ finished = true;
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * This method is used to remove items from the iterator. This
+ * however performs no action as the act of parsing should not
+ * modify the underlying source text value so that it can be
+ * reset with the <code>reset</code> method and used again.
+ */
+ public void remove() {
+ return;
+ }
+ }
+
+ /**
+ * This is a token object that is used to store the offset and
+ * length of a region of chars in the <code>CookieParser.buf</code>
+ * array. The <code>toString</code> method of this token will
+ * produce the <code>String</code> value of the region it
+ * represents.
+ */
+ private class Token {
+
+ /**
+ * The numer of characters that were consumed by this token.
+ */
+ public int len;
+
+ /**
+ * The offset within the buffer that this token starts from.
+ */
+ public int off;
+
+ /**
+ * This converts region within the buffer to a <code>String</code>.
+ * This converts the region only if there is a sufficient length.
+ *
+ * @return the <code>String</code> value of the region
+ */
+ public String toString(){
+ return new String(buf,off,len);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java
new file mode 100644
index 0000000..7efea9c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/DateParser.java
@@ -0,0 +1,642 @@
+/*
+ * DateParser.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.parse;
+
+import static java.util.Calendar.DAY_OF_MONTH;
+import static java.util.Calendar.DAY_OF_WEEK;
+import static java.util.Calendar.HOUR_OF_DAY;
+import static java.util.Calendar.MILLISECOND;
+import static java.util.Calendar.MINUTE;
+import static java.util.Calendar.MONTH;
+import static java.util.Calendar.SECOND;
+import static java.util.Calendar.YEAR;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import org.simpleframework.common.parse.Parser;
+
+/**
+ * This is used to create a <code>Parser</code> for the HTTP date format.
+ * This will parse the 3 formats that are acceptable for the HTTP/1.1 date.
+ * The three formats that are acceptable for the HTTP-date are
+ * <pre>
+ * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
+ * Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+ * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
+ * </pre>
+ * <p>
+ * This can also parse the date in ms as retrived from the <code>System</code>'s
+ * <code>System.currentTimeMillis</code> method. This has a parse method for a
+ * <code>long</code> which will do the same as the <code>parse(String)</code>.
+ * Once the date has been parsed there are two methods that allow the date
+ * to be represented, the <code>toLong</code> method converts the date to a
+ * <code>long</code> and the <code>toString</code> method will convert the date
+ * into a <code>String</code>.
+ * <p>
+ * This produces the same string as the <code>SimpleDateFormat.format</code>
+ * using the pattern <code>"EEE, dd MMM yyyy hh:mm:ss 'GMT'"</code>. This will
+ * however do the job faster as it does not take arbitrary inputs.
+ *
+ * @author Niall Gallagher
+ */
+public class DateParser extends Parser {
+
+ /**
+ * Ensure that the time zone for dates if set to GMT.
+ */
+ private static final TimeZone ZONE = TimeZone.getTimeZone("GMT");
+
+ /**
+ * Contains the possible days of the week for RFC 1123.
+ */
+ private static final String WKDAYS[] = { "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" };
+
+ /**
+ * Contains the possible days of the week for RFC 850.
+ */
+ private static final String WEEKDAYS[] = { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
+
+ /**
+ * Contains the possible months in the year for HTTP-date.
+ */
+ private static final String MONTHS[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+ /**
+ * Used as an index into the months array to get the month.
+ */
+ private int month;
+
+ /**
+ * Represents the decimal value of the date such as 1977.
+ */
+ private int year;
+
+ /**
+ * Represents the decimal value of the date such as 18.
+ */
+ private int day;
+
+ /**
+ * Used as an index into the weekdays array to get the weekday.
+ */
+ private int weekday;
+
+ /**
+ * Represents the decimal value of the hour such as 24.
+ */
+ private int hour;
+
+ /**
+ * Represents the decimal value of the minute.
+ */
+ private int mins;
+
+ /**
+ * Represents the decimal value for the second.
+ */
+ private int secs;
+
+ /**
+ * The default constructor will create a parser that can parse
+ * <code>String</code>s that contain dates in the form of RFC 1123,
+ * RFC 850 or asctime. If the dates that are to be parsed are not in
+ * the form of one of these date encodings the results of this
+ * parser will be random.
+ */
+ public DateParser(){
+ this.init();
+ }
+
+ /**
+ * This constructor will conveniently parse the <code>long</code> argument
+ * in the constructor. This can also be done by first calling the no-arg
+ * constructor and then using the parse method.
+ * <p>
+ * This will then set this object to one that uses the RFC 1123 format
+ * for a date.
+ *
+ * @param date the date to be parsed
+ */
+ public DateParser(long date){
+ this();
+ parse(date);
+ }
+
+ /** This constructor will conveniently parse the <code>String</code>
+ * argument in the constructor. This can also be done by first calling
+ * the no-arg constructor and then using the parse method.
+ * <p>
+ * This will then set this object to one that uses the RFC 1123 format
+ * for a date.
+ *
+ * @param date the date to be parsed
+ */
+ public DateParser(String date) {
+ this();
+ parse(date);
+ }
+
+ /**
+ * This is used to extract the date from a <code>long</code>. If this
+ * method is given the value of the date as a <code>long</code> it will
+ * construct the RFC 1123 date as required by RFC 2616 sec 3.3.
+ * <p>
+ * This saves time on parsing a <code>String</code> that is encoded in
+ * the HTTP-date format. The date given must be positive, if the date
+ * given is not a positive '<code>long</code>' then the results
+ * of this method is random/unknown.
+ *
+ * @param date the date to be parsed
+ */
+ public void parse(long date){
+ Calendar calendar = Calendar.getInstance(ZONE);
+ calendar.setTimeInMillis(date);
+
+ weekday = calendar.get(DAY_OF_WEEK);
+ year = calendar.get(YEAR);
+ month = calendar.get(MONTH);
+ day = calendar.get(DAY_OF_MONTH);
+ hour = calendar.get(HOUR_OF_DAY);
+ mins = calendar.get(MINUTE);
+ secs = calendar.get(SECOND);
+ month = month > 11 ? 11: month;
+ weekday = (weekday+5) % 7;
+ }
+
+ /**
+ * Convenience method used to convert the specified HTTP date in to a
+ * long representing the time. This is used when a single method is
+ * required to convert a HTTP date format to a usable long value for
+ * use in creating <code>Date</code> objects.
+ *
+ * @param date the date specified in on of the HTTP date formats
+ *
+ * @return the date value as a long value in milliseconds
+ */
+ public long convert(String date) {
+ parse(date);
+ return toLong();
+
+ }
+
+ /**
+ * Convenience method used to convert the specified long date in to a
+ * HTTP date format. This is used when a single method is required to
+ * convert a long data value in milliseconds to a HTTP date value.
+ *
+ * @param date the date specified as a long of milliseconds
+ *
+ * @return the date represented in the HTTP date format RFC 1123
+ */
+ public String convert(long date) {
+ parse(date);
+ return toString();
+ }
+
+ /**
+ * This is used to reset the date and the buffer variables
+ * for this <code>DateParser</code>. Every in is set to the
+ * value of 0.
+ */
+ protected void init() {
+ month = year = day =
+ weekday = hour = mins =
+ secs = off = 0;
+ }
+
+ /**
+ * This is used to parse the contents of the <code>buf</code>. This
+ * checks the fourth char of the buffer to see what it contains. Invariably
+ * a date format belonging to RFC 1123 will have a ',' character in position 4,
+ * a date format belonging to asctime will have a ' ' character in position 4
+ * and if neither of these characters are found at position 4 then it is
+ * assumed that the date is in the RFC 850 fromat, however it may not be.
+ */
+ protected void parse(){
+ if(buf.length<4)return;
+ if(buf[3]==','){
+ rfc1123();
+ }else if(buf[3]==' '){
+ asctime();
+ }else{
+ rfc850();
+ }
+ }
+
+ /**
+ * This will parse a date that is in the form of an RFC 1123 date. This
+ * date format is the date format that is to be used with all applications
+ * that are HTTP/1.1 compliant. The RFC 1123 date format is
+ * <pre>
+ * rfc1123 = 'wkday "," SP date1 SP time SP GMT'.
+ * date1 = '2DIGIT SP month SP 4DIGIT' and finally
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'.
+ * </pre>
+ */
+ private void rfc1123(){
+ wkday();
+ off+=2;
+ date1();
+ off++;
+ time();
+ }
+
+ /**
+ * This will parse a date that is in the form of an RFC 850 date. This date
+ * format is the date format that is to be used with some applications that
+ * are HTTP/1.0 compliant. The RFC 1123 date format is
+ * <pre>
+ * rfc850 = 'weekday "," SP date2 SP time SP GMT'.
+ * date2 = '2DIGIT "-" month "-" 2DIGIT' and finally
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'.
+ * </pre>
+ */
+ private void rfc850() {
+ weekday();
+ off+=2;
+ date2();
+ off++;
+ time();
+ }
+
+ /**
+ * This will parse a date that is in the form of an asctime date. This date
+ * format is the date format that is to be used with some applications that
+ * are HTTP/1.0 compliant. The RFC 1123 date format is
+ * <pre>
+ * asctime = 'weekday SP date3 SP time SP 4DIGIT'.
+ * date3 = 'month SP (2DIGIT | (SP 1DIGIT))' and
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'.
+ * </pre>
+ */
+ private void asctime(){
+ wkday();
+ off++;
+ date3();
+ off++;
+ time();
+ off++;
+ year4();
+ }
+
+ /**
+ * This is the date1 format of a date that is used by the RFC 1123
+ * date format. This date is
+ * <pre>
+ * date1 = '2DIGIT SP month SP 4DIGIT'.
+ * example '02 Jun 1982'.
+ * </pre>
+ */
+ private void date1(){
+ day();
+ off++;
+ month();
+ off++;
+ year4();
+ }
+
+ /**
+ * This is the date2 format of a date that is used by the RFC 850
+ * date format. This date is
+ * <pre>
+ * date2 = '2DIGIT "-" month "-" 2DIGIT'
+ * example '02-Jun-82'.
+ * </pre>
+ */
+ private void date2(){
+ day();
+ off++;
+ month();
+ off++;
+ year2();
+ }
+
+ /**
+ * This is the date3 format of a date that is used by the asctime
+ * date format. This date is
+ * <pre>
+ * date3 = 'month SP (2DIGIT | (SP 1DIGIT))'
+ * example 'Jun 2'.
+ * <pre>
+ */
+ private void date3(){
+ month();
+ off++;
+ day();
+ }
+
+ /**
+ * This is used to parse a consecutive set of digit characters to create
+ * the day of the week. This will tolerate a space on front of the digits
+ * thiswill allow all date formats including asctime to use this to get
+ * the day. This may parse more than 2 digits, however if there are more
+ * than 2 digits the date format is incorrect anyway.
+ */
+ private void day(){
+ if(space(buf[off])){
+ off++;
+ }
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ day *= 10;
+ day += buf[off];
+ day -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to get the year from a set of digit characters. This is
+ * used to parse years that are of the form of 2 digits (e.g 82) however
+ * this will assume that any dates that are in 2 digit format are dates
+ * for the 2000 th milleneum so 01 will be 2001.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2 digits
+ * in a row then the date format is incorrect anyway.
+ */
+ private void year2(){
+ int mill = 2000; /* milleneum */
+ int cent = 0; /* century */
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ cent *= 10;
+ cent += buf[off];
+ cent -= '0';
+ off++;
+ }
+ year= mill+cent; /* result 4 digits*/
+ }
+
+ /**
+ * This is used to get the year from a set of digit characters. This
+ * is used to parse years that are of the form of 4 digits (e.g 1982).
+ * <p>
+ * This may parse more than 4 digits but if there are more than 2
+ * digits in a row then the date format is incorrect anyway.
+ */
+ private void year4() {
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ year *= 10;
+ year += buf[off];
+ year -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to parse the time for a HTTP-date. The time for a
+ * HTTP-date is in the form <code>00:00:00</code> that is
+ * <pre>
+ * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT' so this will
+ * read only a time of that form, although this will
+ * parse time = '2DIGIT CHAR 2DIGIT CHAR 2DIGIT'.
+ * </pre>
+ */
+ private void time(){
+ hours();
+ off++;
+ mins();
+ off++;
+ secs();
+ }
+
+ /**
+ * This is used to initialize the hour. This will read a consecutive
+ * sequence of digit characters and convert them into a decimal number
+ * to represent the hour that this date represents.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2
+ * digits the date is already incorrect.
+ */
+ private void hours(){
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ hour *= 10;
+ hour += buf[off];
+ hour -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to initialize the mins. This will read a consecutive
+ * sequence of digit characters and convert them into a decimal number
+ * to represent the mins that this date represents.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2
+ * digits the date is already incorrect.
+ */
+ private void mins(){
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ mins *= 10;
+ mins += buf[off];
+ mins -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to initialize the secs. This will read a consecutive
+ * sequence of digit characters and convert them into a decimal
+ * number to represent the secs that this date represents.
+ * <p>
+ * This may parse more than 2 digits but if there are more than 2
+ * digits the date is already incorrect
+ */
+ private void secs(){
+ while(off < count){
+ if(!digit(buf[off])){
+ break;
+ }
+ secs *= 10;
+ secs += buf[off];
+ secs -= '0';
+ off++;
+ }
+ }
+
+ /**
+ * This is used to read the week day of HTTP-date. The shorthand day
+ * (e.g Mon for Monday) is used by the RFC 1123 and asctime date formats.
+ * This will simply try to read each day from the buffer, when the day
+ * is read successfully then the index of that day is saved.
+ */
+ private void wkday(){
+ for(int i =0; i < WKDAYS.length;i++){
+ if(skip(WKDAYS[i])){
+ weekday = i;
+ return;
+ }
+ }
+ }
+
+ /**
+ * This is used to read the week day of HTTP-date. This format is used
+ * by the RFC 850 date format. This will simply try to read each day from
+ * the buffer, when the day is read successfully then the index of that
+ * day is saved.
+ */
+ private void weekday(){
+ for(int i =0; i < WKDAYS.length;i++){
+ if(skip(WEEKDAYS[i])){
+ weekday = i;
+ return;
+ }
+ }
+ }
+
+ /**
+ * This is used to read the month of HTTP-date. This will simply
+ * try to read each month from the buffer, when the month is read
+ * successfully then the index of that month is saved.
+ */
+ private void month(){
+ for(int i =0; i < MONTHS.length;i++){
+ if(skip(MONTHS[i])){
+ month = i;
+ return;
+ }
+ }
+ }
+
+ /**
+ * This is used to append the date in RFC 1123 format to the given
+ * string builder. This will append the date and a trailing space
+ * character to the buffer. Dates like the following are appended.
+ * <pre>
+ * Tue, 02 Jun 1982
+ * </pre>.
+ * For performance reasons a string builder is used. This avoids
+ * an unneeded synchronization caused by the string buffers.
+ *
+ * @param builder this is the builder to append the date to
+ */
+ private void date(StringBuilder builder) {
+ builder.append(WKDAYS[weekday]);
+ builder.append(", ");
+
+ if(day <= 9) {
+ builder.append('0');
+ }
+ builder.append(day);
+ builder.append(' ');
+ builder.append(MONTHS[month]);
+ builder.append(' ');
+ builder.append(year);
+ builder.append(' ');
+ }
+
+ /**
+ * This is used to append the time in RFC 1123 format to the given
+ * string builder. This will append the time and a trailing space
+ * character to the buffer. Times like the following are appended.
+ * <pre>
+ * 23:59:59
+ * </pre>.
+ * For performance reasons a string builder is used. This avoids
+ * an unneeded synchronization caused by the string buffers.
+ *
+ * @param builder this is the builder to write the time to
+ */
+ private void time(StringBuilder builder) {
+ if(hour <= 9) {
+ builder.append('0');
+ }
+ builder.append(hour);
+ builder.append(':');
+
+ if(mins <= 9) {
+ builder.append('0');
+ }
+ builder.append(mins);
+ builder.append(':');
+
+ if(secs <= 9) {
+ builder.append('0');
+ }
+ builder.append(secs);
+ builder.append(' ');
+ }
+
+ /**
+ * This is used to append the time zone to the provided appender.
+ * For HTTP the dates should always be in GMT format. So this will
+ * simply append the "GMT" string to the end of the builder.
+ *
+ * @param builder this builder to append the time zone to
+ */
+ private void zone(StringBuilder builder) {
+ builder.append("GMT");
+ }
+
+ /**
+ * This returns the date in as a <code>long</code>, given the exact
+ * time this will use the <code>java.util.Date</code> to parse this date
+ * into a <code>long</code>. The <code>GregorianCalendar</code> uses
+ * the method <code>getTime</code> which produces the <code>Date</code>
+ * object from this the <code>getTime</code> returns the <code>long</code>
+ *
+ * @return the date parsed as a <code>long</code>
+ */
+ public long toLong() {
+ Calendar calendar = Calendar.getInstance(ZONE); /* GMT*/
+ calendar.set(year,month, day, hour, mins, secs);
+ calendar.set(MILLISECOND, 0);
+
+ return calendar.getTime().getTime();
+ }
+
+ /**
+ * This prints the date in the format of a RFC 1123 date. Example
+ * <pre>
+ * Tue, 02 Jun 1982 23:59:59 GMT
+ * </pre>.
+ * This uses a <code>StringBuffer</code> to accumulate the various
+ * <code>String</code>s/<code>int</code>s to form the resulting date
+ * value. The resulting date value is the one required by RFC 2616.
+ * <p>
+ * The HTTP date must be in the form of RFC 1123. The hours, minutes
+ * and seconds are appended with the 0 character if they are less than
+ * 9 i.e. if they do not have two digits.
+ *
+ * @return the date in RFC 1123 format
+ */
+ public String toString(){
+ StringBuilder builder = new StringBuilder(30);
+
+ date(builder);
+ time(builder);
+ zone(builder);
+
+ return builder.toString();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/LanguageParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/LanguageParser.java
new file mode 100644
index 0000000..0a2d215
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/LanguageParser.java
@@ -0,0 +1,156 @@
+/*
+ * LanguageParser.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.parse;
+
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * LanguageParser is used to parse the HTTP <code>Accept-Language</code>
+ * header. This takes in an <code>Accept-Language</code> header and parses
+ * it according the RFC 2616 BNF for the <code>Accept-Language</code> header.
+ * This also has the ability to sequence the language tokens in terms of
+ * the most preferred and the least preferred.
+ * <p>
+ * This uses the qvalues outlined by RFC 2616 to order the language tokens
+ * by preference. Typically the language tokens will not have qvalues with
+ * the language. However when a language tag has the qvalue parameter then
+ * this tag will be ordered based on the value of that parameter. A language
+ * tag without the qvalue parameter is considered to have a qvalue of 1 and
+ * is ordered accordingly.
+ *
+ * @author Niall Gallagher
+ */
+public class LanguageParser extends ListParser<Locale> {
+
+ /**
+ * This is used to create a <code>LanguageParser</code> for the
+ * <code>Accept-Language</code> HTTP header value. This will
+ * parse a set of language tokens and there parameters. The
+ * languages will be ordered on preference. This constructor
+ * will parse the value given using <code>parse(String)</code>.
+ */
+ public LanguageParser() {
+ super();
+ }
+
+ /**
+ * This is used to create a <code>LanguageParser</code> for the
+ * <code>Accept-Language</code> HTTP header value. This will
+ * parse a set of language tokens and there parameters. The
+ * languages will be ordered on preference. This constructor
+ * will parse the value given using <code>parse(String)</code>.
+ *
+ * @param text value of a <code>Accept-Language</code> header
+ */
+ public LanguageParser(String text) {
+ super(text);
+ }
+
+ /**
+ * This is used to create a <code>LanguageParser</code> for the
+ * <code>Accept-Language</code> HTTP header value. This will
+ * parse a set of language tokens and there parameters. The
+ * languages will be ordered on preference. This constructor
+ * will parse the value given using <code>parse(String)</code>.
+ *
+ * @param list value of a <code>Accept-Language</code> header
+ */
+ public LanguageParser(List<String> list) {
+ super(list);
+ }
+
+ /**
+ * This creates a locale object using an offset and a length.
+ * The locale is created from the extracted token and the offset
+ * and length ensure that no leading or trailing whitespace are
+ * within the created locale object.
+ *
+ * @param text this is the text buffer to acquire the value from
+ * @param start the offset within the array to take characters
+ * @param len this is the number of characters within the token
+ */
+ @Override
+ protected Locale create(char[] text, int start, int len){
+ String language = language(text, start, len);
+ String country = country(text, start, len);
+
+ return new Locale(language, country);
+ }
+
+ /**
+ * This will extract the primary language tag from the header.
+ * This token is used to represent the language that will be
+ * available in the <code>Locale</code> object created.
+ *
+ * @param text this is the text buffer to acquire the value from
+ * @param start the offset within the array to take characters
+ * @param len this is the number of characters within the token
+ */
+ private String language(char[] text, int start, int len) {
+ int mark = start;
+ int size = 0;
+
+ while(start < len) {
+ char next = text[start];
+
+ if(terminal(next)) {
+ return new String(text, mark, size);
+ }
+ size++;
+ start++;
+ }
+ return new String(text, mark, len);
+ }
+
+ /**
+ * This will extract the primary country tag from the header.
+ * This token is used to represent the country that will be
+ * available in the <code>Locale</code> object created.
+ *
+ * @param text this is the text buffer to acquire the value from
+ * @param start the offset within the array to take characters
+ * @param len this is the number of characters within the token
+ */
+ private String country(char[] text, int start, int len) {
+ int size = len;
+
+ while(start < len) {
+ if(text[start++] == '-') {
+ return new String(text, start, --size);
+ }
+ size--;
+ }
+ return "";
+ }
+
+ /**
+ * This is used to determine whether the character provided is
+ * a terminal character. The terminal token is the value that is
+ * used to separate the country from the language and also any
+ * character the marks the end of the language token.
+ *
+ * @param ch this is the character that is to be evaluated
+ *
+ * @return true if the character represents a terminal token
+ */
+ private boolean terminal(char ch) {
+ return ch ==' ' || ch == '-' || ch == ';';
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/ListParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ListParser.java
new file mode 100644
index 0000000..166a2aa
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ListParser.java
@@ -0,0 +1,456 @@
+/*
+ * ListParser.java September 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.parse;
+
+import static java.lang.Long.MAX_VALUE;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.PriorityQueue;
+
+import org.simpleframework.common.parse.Parser;
+
+/**
+ * The <code>ListParser</code> is used to extract a comma separated
+ * list of HTTP header values. This will extract values without
+ * any leading or trailing spaces, which enables the values to be
+ * used. Listing the values that appear in the header also requires
+ * that the values are ordered. This orders the values using the
+ * values that appear with any quality parameter associated with it.
+ * The quality value is a special parameter that often found in a
+ * comma separated value list to specify the client preference.
+ * <pre>
+ *
+ * image/gif, image/jpeg, text/html
+ * image/gif;q=1.0, image/jpeg;q=0.8, image/png; q=1.0,*;q=0.1
+ * gzip;q=1.0, identity; q=0.5, *;q=0
+ *
+ * </pre>
+ * The above lists taken from RFC 2616 provides an example of the
+ * common form comma separated values take. The first illustrates
+ * a simple comma delimited list, here the ordering of values is
+ * determined from left to right. The second and third list have
+ * quality values associated with them, these are used to specify
+ * a preference and thus order.
+ * <p>
+ * Each value within a list has an implicit quality value of 1.0.
+ * If the value is explicitly set with a the "q" parameter, then
+ * the values can range from 1.0 to 0.001. This parser ensures
+ * that the order of values returned from the <code>list</code>
+ * method adheres to the optional quality parameters and ensures
+ * that the quality parameters a removed from the resulting text.
+ *
+ * @author Niall Gallagher
+ */
+public abstract class ListParser<T> extends Parser {
+
+ /**
+ * Provides a quick means of sorting the values extracted.
+ */
+ private PriorityQueue<Entry> order;
+
+ /**
+ * Contains all the values extracted from the header(s).
+ */
+ private List<T> list;
+
+ /**
+ * This is used as a working space to parse the value.
+ */
+ private char[] text;
+
+ /**
+ * The quality associated with an individual value.
+ */
+ private long qvalue;
+
+ /**
+ * Used to index into the write offset for the value.
+ */
+ private int pos;
+
+ /**
+ * This is used to determine whether to gather tokens.
+ */
+ private boolean build;
+
+ /**
+ * Constructor for the <code>ListParser</code>. This creates a
+ * parser with no initial parse data, if there are headers to
+ * be parsed then the <code>parse(String)</code> method or
+ * <code>parse(List)</code> method can be used. This will
+ * parse a delimited list according so RFC 2616 section 4.2.
+ */
+ public ListParser(){
+ this.order = new PriorityQueue<Entry>();
+ this.list = new ArrayList<T>();
+ this.text = new char[0];
+ }
+
+ /**
+ * Constructor for the <code>ListParser</code>. This creates a
+ * parser with the text supplied. This will parse the comma
+ * separated list according to RFC 2616 section 2.1 and 4.2.
+ * The tokens can be extracted using the <code>list</code>
+ * method, which will also sort and trim the tokens.
+ *
+ * @param text this is the comma separated list to be parsed
+ */
+ public ListParser(String text) {
+ this();
+ parse(text);
+ }
+
+ /**
+ * Constructor for the <code>ListParser</code>. This creates a
+ * parser with the text supplied. This will parse the comma
+ * separated list according to RFC 2616 section 2.1 and 4.2.
+ * The tokens can be extracted using the <code>list</code>
+ * method, which will also sort and trim the tokens.
+ *
+ * @param list a list of comma separated lists to be parsed
+ */
+ public ListParser(List<String> list) {
+ this();
+ parse(list);
+ }
+
+ /**
+ * This allows multiple header values to be represented as one
+ * single comma separated list. RFC 2616 states that multiple
+ * message header fields with the same field name may be present
+ * in a message if and only if the entire field value for that
+ * header field is defined as a comma separated list. This means
+ * that if there are multiple header values with the same name
+ * they can be combined into a single comma separated list.
+ *
+ * @param list this is a list of header values to be combined
+ */
+ public void parse(List<String> list) {
+ for(String value : list) {
+ parse(value);
+ build = true;
+ }
+ build = false;
+ }
+
+ /**
+ * This will build an ordered list of values extracted from the
+ * comma separated header value. This enables the most preferred
+ * token, to be taken from the first index of the array and the
+ * least preferred token to be taken from the last index.
+ *
+ * @return tokens parsed from the list ordered by preference
+ */
+ public List<T> list() {
+ return list;
+ }
+
+ /**
+ * This is used to remove the <code>String</code> tokens from
+ * the priority queue and place those tokens in an array. The
+ * The <code>String</code> tokens are placed into the array
+ * in an ordered manner so that the most preferred token is
+ * inserted into the start of the list.
+ */
+ private void build() {
+ while(!order.isEmpty()) {
+ Entry entry = order.remove();
+ T value = entry.getValue();
+
+ list.add(value);
+ }
+ }
+
+ /**
+ * This ensures that tokens are taken from the comma separated
+ * list as long as there bytes left to be examined within the
+ * source text. This also makes sure that the implicit qvalue
+ * is decreased each time a token is extracted from the list.
+ */
+ protected void parse() {
+ while(off < count) {
+ clear();
+ value();
+ save();
+ }
+ build();
+ }
+
+ /**
+ * Initializes the parser so that tokens can be extracted from
+ * the list. This creates a write buffer so that a if there is
+ * only one token as long as the source text, then that token
+ * can be accommodated, also this starts of the initial qvalue
+ * implicit to tokens within the list as the maximum long value.
+ * <p>
+ * One thing that should be noted is that this will not empty
+ * the priority queue on each string parsed. This ensures that
+ * if there are multiple strings they can be parsed quickly
+ * and also contribute to the final result.
+ */
+ protected void init(){
+ if(text.length < count){
+ text = new char[count];
+ }
+ if(!build) {
+ list.clear();
+ }
+ pos = off = 0;
+ order.clear();
+ }
+
+ /**
+ * This is used to return the parser to a semi-initialized state.
+ * After extracting a token from the list the buffer will have
+ * accumulated bytes, this ensures that bytes previously written
+ * to the buffer do not interfere with the next token extracted.
+ * <p>
+ * This also ensures the implicit qvalue is reset to the maximum
+ * long value, so that the next token parsed without a qvalue
+ * will have the highest priority and be placed at the top of
+ * the list. This ensures order is always maintained.
+ */
+ private void clear() {
+ qvalue = MAX_VALUE;
+ pos = 0;
+ }
+
+ /**
+ * This method will extract a token from a comma separated list
+ * and write it to a buffer. This performs the extraction in such
+ * a way that it can tolerate literals, parameters, and quality
+ * value parameters. The only alterations made to the token by
+ * this method is the removal of quality values, that is, qvalue
+ * parameters which have the name "q". Below is an example of
+ * some of the lists that this can parse.
+ * <pre>
+ *
+ * token; quantity=1;q=0.001, token; text="a, b, c, d";q=0
+ * image/gif, , image/jpeg, image/png;q=0.8, *
+ * token="\"a, b, c, d\", a, b, c, d", token="a";q=0.9,,
+ *
+ * </pre>
+ * This will only interpret a comma delimiter outside quotes of
+ * a literal. So if there are comma separated tokens that have
+ * quoted strings, then commas within those quoted strings will
+ * not upset the extraction of the token. Also escaped strings
+ * are tolerated according to RFC 2616 section 2.
+ */
+ private void value() {
+ parse: while(off < count) {
+ if(buf[off++] == '"'){ /* "[t]ext" */
+ text[pos++] = buf[off-1]; /* ["]text"*/
+ while(++off < count){ /* "text"[] */
+ if(buf[off -1] =='"'){ /* "text["] */
+ if(buf[off -2] !='\\')
+ break;
+ }
+ text[pos++] = buf[off-1]; /* "tex[t]"*/
+ }
+ } else if(buf[off -1] == ';'){ /* [;] q=0.1 */
+ for(int seek = off; seek+1 < count;){/* ;[ ]q=0.1 */
+ if(!space(buf[seek])){ /* ;[ ]q=0.1*/
+ if(buf[seek] =='q'){ /* ; [q]=0.1*/
+ if(buf[seek+1] =='='){ /* ; q[=]0.1*/
+ off = seek;
+ qvalue();
+ continue parse;
+ }
+ }
+ break;
+ }
+ seek++;
+ }
+ }
+ if(buf[off-1] ==','){
+ break;
+ }
+ text[pos++] = buf[off-1];
+ }
+ }
+
+ /**
+ * This method will trim whitespace from the extracted token and
+ * store that token within the <code>PriorityQueue</code>. This
+ * ensures that the tokens parsed from the comma separated list
+ * can be used. Trimming the whitespace is something that will be
+ * done to the tokens so that they can be examined, so this
+ * ensures that the overhead of the <code>String.trim</code>
+ * method is not required to remove trailing or leading spaces.
+ * This also ensures that empty tokens are not saved.
+ */
+ private void save() {
+ int size = pos;
+ int start = 0;
+
+ while(size > 0){
+ if(!space(text[size-1])){
+ break;
+ }
+ size--;
+ }
+ while(start < pos){
+ if(space(text[start])){
+ start++;
+ size--;
+ }else {
+ break;
+ }
+ }
+ if(size > 0) {
+ T value = create(text, start, size);
+
+ if(value != null) {
+ save(value);
+ }
+ }
+ }
+
+ /**
+ * This stores the string in the <code>PriorityQueue</code>. If
+ * the qvalue extracted from the header value is less that 0.001
+ * then this will not store the token. This ensures that client
+ * applications can specify tokens that are unacceptable to it.
+ *
+ * @param value this is the token to be enqueued into the queue
+ */
+ private void save(T value) {
+ int size = order.size();
+
+ if(qvalue > 0) {
+ order.offer(new Entry(value, qvalue, size));
+ }
+ }
+
+ /**
+ * This is used to extract the qvalue parameter from the header.
+ * The qvalue parameter is identified by a parameter with the
+ * name "q" and a numeric floating point number. The number can
+ * be in the range of 0.000 to 1.000. The <code>qvalue</code>
+ * is parsed byte bit shifting a byte in to a value in to a
+ * long, this may cause problems with varying accuracy.
+ */
+ private void qvalue() {
+ if(skip("q=")){
+ char digit = 0;
+
+ for(qvalue = 0; off < count;){
+ if(buf[off] == '.'){
+ off++;
+ continue;
+ }
+ if(!digit(buf[off])){
+ break;
+ }
+ digit = buf[off];
+ digit -= '0';
+ qvalue |= digit;
+ qvalue <<= 4;
+ off++;
+ }
+ }
+ }
+
+ /**
+ * This creates an value object using the range of characters
+ * that have been parsed as an item within the list of values. It
+ * is up to the implementation to create a value to insert in to
+ * the list. A null value will be ignored if returned.
+ *
+ * @param text this is the text buffer to acquire the value from
+ * @param start the offset within the array to take characters
+ * @param len this is the number of characters within the token
+ */
+ protected abstract T create(char[] text, int start, int len);
+
+ /**
+ * The <code>Entry</code> object provides a comparable object to
+ * insert in to a priority queue. This will sort the value using
+ * the quality value parameter parsed from the list. If there
+ * are values with the same quality value this this will sort
+ * the values by a secondary order parameter.
+ */
+ private class Entry implements Comparable<Entry> {
+
+ /**
+ * This is the value that is represented by this entry.
+ */
+ private final T value;
+
+ /**
+ * This is the priority value that is used to sort entries.
+ */
+ private final long priority;
+
+ /**
+ * This is the secondary order value used to sort entries.
+ */
+ private final int order;
+
+ /**
+ * Constructor for the <code>Entry</code> object. This is used
+ * to create a comparable value that can be inserted in to a
+ * priority queue and extracted in order of the priority value.
+ *
+ * @param value this is the value that is represented by this
+ * @param priority this is the priority value for sorting
+ * @param order this is the secondary priority value used
+ */
+ public Entry(T value, long priority, int order) {
+ this.priority = priority;
+ this.order = order;
+ this.value = value;
+ }
+
+ /**
+ * This acquires the value represented by this entry. This is
+ * can be used to place the value within a list as it is taken
+ * from the priority queue. Acquiring the values in this way
+ * facilitates a priority ordered list of values.
+ *
+ * @return this returns the value represented by this
+ */
+ public T getValue() {
+ return value;
+ }
+
+ /**
+ * This is used to sort the entries within the priority queue
+ * using the provided priority of specified. If the entries
+ * have the same priority value then they are sorted using a
+ * secondary order value, which is the insertion index.
+ *
+ * @param entry this is the entry to be compared to
+ *
+ * @return this returns the result of the entry comparison
+ */
+ public int compareTo(Entry entry) {
+ long value = entry.priority - priority;
+
+ if(value > 0) {
+ return 1;
+ }
+ if(value < 0) {
+ return -1;
+ }
+ return order - entry.order;
+ }
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/PathParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/PathParser.java
new file mode 100644
index 0000000..7055e4e
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/PathParser.java
@@ -0,0 +1,726 @@
+/*
+ * PathParser.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.parse;
+
+import org.simpleframework.common.parse.Parser;
+import org.simpleframework.http.Path;
+
+/**
+ * This is used to parse a path given as part of a URI. This will read the
+ * path, normalize it, and break it up into its components. The normalization
+ * of the path is the conversion of the path given into it's actual path by
+ * removing the references to the parent directories and to the current dir.
+ * <p>
+ * If the path that this represents is <code>/usr/bin/../etc/./README</code>
+ * then the actual path, normalized, is <code>/usr/etc/README</code>. Once
+ * the path has been normalized it is possible to acquire the segments as
+ * an array of strings, which allows simple manipulation of the path.
+ * <p>
+ * Although RFC 2396 defines the path within a URI to have parameters this
+ * does not extract those parameters this will simply normalize the path and
+ * include the path parameters in the path. If the path is to be converted
+ * into a OS specific file system path that has the parameters extracted
+ * then the <code>AddressParser</code> should be used.
+ *
+ * @author Niall Gallagher
+ */
+public class PathParser extends Parser implements Path{
+
+ /**
+ * Used to store the individual path segments.
+ */
+ private TokenList list;
+
+ /**
+ * Used to store consumed name characters.
+ */
+ private Token name;
+
+ /**
+ * Used to store consumed file extension.
+ */
+ private Token ext;
+
+ /**
+ * Used to store the highest directory path.
+ */
+ private Token dir;
+
+ /**
+ * Used to store consumed normalized path name.
+ */
+ private Token path;
+
+ /**
+ * The default constructor will create a <code>PathParser</code> that
+ * contains no specifics. The instance will return <code>null</code>
+ * for all the get methods. The <code>PathParser</code>'s get methods
+ * may be populated by using the parse method.
+ */
+ public PathParser() {
+ this.list = new TokenList();
+ this.ext = new Token();
+ this.dir = new Token();
+ this.path = new Token();
+ this.name = new Token();
+ }
+
+ /**
+ * This is primarily a convineance constructor. This will parse the
+ * <code>String</code> given to extract the specifics. This could be
+ * achived by calling the default no-arg constructor and then using
+ * the instance to invoke the <code>parse</code> method on that
+ * <code>String</code> to extract the parts.
+ *
+ * @param path a <code>String</code> containing a path value
+ */
+ public PathParser(String path){
+ this();
+ parse(path);
+ }
+
+ /**
+ * This will parse the path in such a way that it ensures that at no
+ * stage there are trailing back references, using path normalization.
+ * The need to remove the back references is so that this
+ * <code>PathParser</code> will create the same <code>String</code>
+ * path given a set of paths that have different back references. For
+ * example the paths <code>/path/../path</code> and <code>/path</code>
+ * are the same path but different <code>String</code>'s.
+ * <p>
+ * This will NOT parse an immediate back reference as this signifies
+ * a path that cannot exist. So a path such as <code>/../</code> will
+ * result in a null for all methods. Paths such as <code>../bin</code>
+ * will not be allowed.
+ */
+ protected void parse() {
+ normalize();
+ path();
+ segments();
+ name();
+ extension();
+ }
+
+ /**
+ * This will initialize the parser so that it is in a ready state.
+ * This allows the parser to be used to parse many paths. This will
+ * clear the parse buffer objects and reset the offset to point to
+ * the start of the char buffer. The count variable is reset by the
+ * <code>Parser.parse</code> method.
+ */
+ protected void init() {
+ list.clear();
+ ext.clear();
+ dir.clear();
+ name.clear();
+ path.clear();
+ off = 0;
+ }
+
+ /**
+ * This will return the extension that the file name contains.
+ * For example a file name <code>file.en_US.extension</code>
+ * will produce an extension of <code>extension</code>. This
+ * will return null if the path contains no file extension.
+ *
+ * @return this will return the extension this path contains
+ */
+ public String getExtension() {
+ return ext.toString();
+ }
+
+ /**
+ * This will return the full name of the file without the path.
+ * As regargs the definition of the path in RFC 2396 the name
+ * would be considered the last path segment. So if the path
+ * was <code>/usr/README</code> the name is <code>README</code>.
+ * Also for directorys the name of the directory in the last
+ * path segment is returned. This returns the name without any
+ * of the path parameters. As RFC 2396 defines the path to have
+ * path parameters after the path segments.
+ *
+ * @return this will return the name of the file in the path
+ */
+ public String getName(){
+ return name.toString();
+ }
+
+ /**
+ * This will return the normalized path. The normalized path is
+ * the path without any references to its parent or itself. So
+ * if the path to be parsed is <code>/usr/../etc/./</code> the
+ * path is <code>/etc/</code>. If the path that this represents
+ * is a path with an immediate back reference then this will
+ * return null. This is the path with all its information even
+ * the parameter information if it was defined in the path.
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ public String getPath() {
+ return path.toString();
+ }
+
+ /**
+ * This will return the normalized path from the specified path
+ * segment. This allows various path parts to be acquired in an
+ * efficient means what does not require copy operations of the
+ * use of <code>substring</code> invocations. Of particular
+ * interest is the extraction of context based paths. This is
+ * the path with all its information even the parameter
+ * information if it was defined in the path.
+ *
+ * @param from this is the segment offset to get the path for
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ public String getPath(int from) {
+ return list.segment(from);
+ }
+
+ /**
+ * This will return the normalized path from the specified path
+ * segment. This allows various path parts to be acquired in an
+ * efficient means what does not require copy operations of the
+ * use of <code>substring</code> invocations. Of particular
+ * interest is the extraction of context based paths. This is
+ * the path with all its information even the parameter
+ * information if it was defined in the path.
+ *
+ * @param from this is the segment offset to get the path for
+ * @param count this is the number of path segments to include
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ public String getPath(int from, int count) {
+ return list.segment(from, count);
+ }
+
+ /**
+ * This will return the highest directory that exists within
+ * the path. This is used to that files within the same path
+ * can be acquired. An example of that this would do given
+ * the path <code>/pub/./bin/README</code> would be to return
+ * the highest directory path <code>/pub/bin/</code>. The "/"
+ * character will allways be the last character in the path.
+ *
+ * @return this method will return the highest directory
+ */
+ public String getDirectory(){
+ return dir.toString();
+ }
+
+ /**
+ * This method is used to break the path into individual parts
+ * called segments, see RFC 2396. This can be used as an easy
+ * way to compare paths and to examine the directory tree that
+ * the path points to. For example, if an path was broken from
+ * the string <code>/usr/bin/../etc</code> then the segments
+ * returned would be <code>usr</code> and <code>etc</code> as
+ * the path is normalized before the segments are extracted.
+ *
+ * @return return all the path segments within the directory
+ */
+ public String[] getSegments(){
+ return list.list();
+ }
+
+ /**
+ * This will return the path as it is relative to the issued
+ * path. This in effect will chop the start of this path if
+ * it's start matches the highest directory of the given path
+ * as of <code>getDirectory</code>. This is useful if paths
+ * that are relative to a specific location are required. To
+ * illustrate what this method will do the following example
+ * is provided. If this object represented the path string
+ * <code>/usr/share/rfc/rfc2396.txt</code> and the issued
+ * path was <code>/usr/share/text.txt</code> then this will
+ * return the path string <code>/rfc/rfc2396.txt</code>.
+ *
+ * @param path the path prefix to acquire a relative path
+ *
+ * @return returns a path relative to the one it is given
+ * otherwize this method will return null
+ */
+ public String getRelative(String path){
+ return getRelative(new PathParser(path));
+ }
+
+ /**
+ * This is used by the <code>getRelative(String)</code> to
+ * normalize the path string and determine if it contains a
+ * highest directory which is shared with the path that is
+ * represented by this object. If the path has leading back
+ * references, such as <code>../</code>, then the result of
+ * this is null. The returned path begins with a '/'.
+ *
+ * @param path the path prefix to acquire a relative path
+ *
+ * @return returns a path relative to the one it is given
+ * otherwize this method will return null
+ */
+ private String getRelative(PathParser path){
+ char[] text = path.buf;
+ int off = path.dir.off;
+ int len = path.dir.len;
+
+ return getRelative(text, off, len);
+ }
+
+ /**
+ * This will return the path as it is relative to the issued
+ * path. This in effect will chop the start of this path if
+ * it's start matches the highest directory of the given path
+ * as of <code>getDirectory</code>. This is useful if paths
+ * that are relative to a specific location are required. To
+ * illustrate what this method will do the following example
+ * is provided. If this object represented the path string
+ * <code>/usr/share/rfc/rfc2396.txt</code> and the issued
+ * path was <code>/usr/share/text.txt</code> then this will
+ * return the path string <code>/rfc/rfc2396.txt</code>.
+ *
+ * @param text the path prefix to acquire a relative path
+ * @param off this is the offset within the text to read
+ * @param len this is the number of characters in the path
+ *
+ * @return returns a path relative to the one it is given
+ * otherwize this method will return null
+ */
+ private String getRelative(char[] text, int off, int len){
+ if (len > path.len) {
+ return null;
+ }
+ int size = path.len - len + 1; /* '/' */
+ int pos = path.off + len - 1;
+
+ for(int i = 0; i < len; i++){
+ if(text[off++] != buf[path.off+i]){
+ return null;
+ }
+ }
+ if(pos < 0) { /* ../ */
+ return null;
+ }
+ return new String(buf,pos,size);
+ }
+
+ /**
+ * This will extract the path of the given <code>String</code>
+ * after it has been normalized. If the path can not be normalized
+ * then the count is set to -1 and the path cannot be extracted.
+ * When this happens then the path parameter is <code>null</code>.
+ */
+ private void path() {
+ if(count > 0){
+ path.len = count;
+ path.off = 0;
+ }
+ }
+
+ /**
+ * This will simply read the characters from the end of the
+ * buffer until it encounters the first peroid character. When
+ * this is read it will store the file extension and remove the
+ * characters from the buffer.
+ */
+ private void extension() {
+ int pos = off + count; /* index.html[]*/
+ int len = 0;
+
+ while(pos-1 >= off) { /* index.htm[l]*/
+ if(buf[--pos]=='.'){ /* index[.]html*/
+ ext.off = pos+1;
+ ext.len = len;
+ count = pos;
+ break;
+ }
+ len++;
+ }
+ }
+
+ /**
+ * This wil extract each individual segment from the path and
+ * also extract the highest directory. The path segments are
+ * basically the strings delimited by the '/' character of a
+ * normalized path. As well as extracting the path segments
+ * this will also extract the directory of path, that is, the
+ * the path up to the last occurance of the '/' character.
+ */
+ private void segments() {
+ int pos = count - 1;
+ int len = 1;
+
+ if(count > 0){
+ if(buf[pos] == '/'){ /* /pub/bin[/] */
+ dir.len = pos+1;
+ dir.off = 0;
+ pos--; /* /pub/bi[n]/ */
+ }
+ while(pos >= off){
+ if(buf[pos] == '/'){ /* /pub[/]bin/*/
+ if(dir.len == 0){
+ dir.len = pos+1; /* [/] is 0*/
+ dir.off = 0;
+ }
+ list.add(pos+1,len-1);
+ len = 0;
+ }
+ len++;
+ pos--;
+ }
+ }
+ }
+
+ /**
+ * The normalization of the path is the conversion of the path
+ * given into it's actual path by removing the references to
+ * the parent directorys and to the current dir. So if the path
+ * given was <code>/usr/bin/../etc/./README</code> then the actual
+ * path, the normalized path, is <code>/usr/etc/README</code>.
+ * <p>
+ * This method ensures the if there are an illegal number of back
+ * references that the path will be evaluated as empty. This can
+ * evaluate any path configuration, this includes any references
+ * like <code>../</code> or <code>/..</code> within the path.
+ */
+ private void normalize(){
+ int size = count + off;
+ int pos = off;
+
+ for(off = count = 0; pos < size; pos++) {
+ buf[count++] = buf[pos];
+
+ if(buf[pos] == '.') { /* //[.]/path/ */
+ if(count -1 > 0) { /* /[/]./path/ */
+ if(buf[count - 2] !='/') /* /[/]./path./ */
+ continue; /* /path.[/] */
+ }
+ if(pos + 2 > size){ /* /path/[.] */
+ count--;
+ } else {
+ if(buf[pos + 1] =='/'){ /* /.[/]path */
+ pos++;/* /[/]. */
+ count--; /* /.[/]path */
+ }
+ if(buf[pos] !='.'){ /* /.[/]path */
+ continue;
+ }
+ if(pos + 2< size){
+ if(buf[pos + 2]!='/') /* /..[p]ath */
+ continue; /* /[.].path */
+ }
+ if(count - 2 > 0) {
+ for(count -= 2; count - 1 > 0;){ /* /path[/]..*/
+ if(buf[count - 1]=='/') { /* [/]path/..*/
+ break;
+ }
+ count--;
+ }
+ }else { /* /../ */
+ count = 0;
+ off = 0;
+ break;
+ }
+ pos += 2; /* /path/.[.]/ */
+ }
+ }
+ }
+ }
+
+ /**
+ * This will extract the full name of the file without the path.
+ * As regards the definition of the path in RFC 2396 the name
+ * would be considered the last path segment. So if the path
+ * was <code>/usr/README</code> the name is <code>README</code>.
+ * Also for directorys the name of the directory in the last
+ * path segment is returned. This returns the name without any
+ * of the path parameters. As RFC 2396 defines the path to have
+ * path parameters after the path segments. So the path for the
+ * directory "/usr/bin;param=value/;param=value" would result
+ * in the name "bin". If the path given was "/" then there will
+ * be nothing in the buffer because <code>extract</code> will
+ * have removed it.
+ */
+ private void name(){
+ int pos = count;
+ int len = 0;
+
+ while(pos-- > off) { /* /usr/bin/;para[m] */
+ if(buf[pos]==';'){ /* /usr/bin/[;]param */
+ if(buf[pos-1]=='/'){ /* /usr/bin[/];param */
+ pos--; /* /usr/bin[/];param */
+ }
+ len = 0; /* /usr/bin[/]*/
+ }else if(buf[pos]=='/'){ /* /usr[/]bin*/
+ off = pos + 1; /* /usr/[b]in*/
+ count = len; /* [b]in */
+ break;
+ }else{
+ len++;
+ }
+ }
+ name.len = count;
+ name.off = off;
+ }
+
+ /**
+ * This will return the normalized path. The normalized path is
+ * the path without any references to its parent or itself. So
+ * if the path to be parsed is <code>/usr/../etc/./</code> the
+ * path is <code>/etc/</code>. If the path that this represents
+ * is a path with an immediate back reference then this will
+ * return null. This is the path with all its information even
+ * the parameter information if it was defined in the path.
+ *
+ * @return this returns the normalize path without
+ * <code>../</code> or <code>./</code>
+ */
+ public String toString(){
+ return getPath();
+ }
+
+ /**
+ * This is used so that the <code>PathParser</code> can speed
+ * up the parsing of the data. Rather than using a buffer like
+ * a <code>ParseBuffer</code> or worse a <code>StringBuffer</code>
+ * this just keeps an index into the character array from the
+ * start and end of the token. Also this enables a cache to be
+ * kept so that a <code>String</code> does not need to be made
+ * again after the first time it is created.
+ */
+ private class Token {
+
+ /**
+ * Provides a quick retrieval of the token value.
+ */
+ public String value;
+
+ /**
+ * Offset within the buffer that the token starts.
+ */
+ public int off;
+
+ /**
+ * Length of the region that the token consumes.
+ */
+ public int len;
+
+ /**
+ * If the <code>Token</code> is to be reused this will clear
+ * all previous data. Clearing the buffer allows it to be
+ * reused if there is a new URI to be parsed. This ensures
+ * that a null is returned if the token length is zero.
+ */
+ public void clear() {
+ value = null;
+ len = 0;
+ }
+
+ /**
+ * This method will convert the <code>Token</code> into it's
+ * <code>String</code> equivelant. This will firstly check
+ * to see if there is a value, for the string representation,
+ * if there is the value is returned, otherwise the region
+ * is converted into a <code>String</code> and returned.
+ *
+ * @return this returns a value representing the token
+ */
+ public String toString() {
+ if(value != null) {
+ return value;
+ }
+ if(len > 0) {
+ value = new String(buf,off,len);
+ }
+ return value;
+ }
+ }
+
+ /**
+ * The <code>TokenList</code> class is used to store a list of
+ * tokens. This provides an <code>add</code> method which can
+ * be used to store an offset and length of a token within
+ * the buffer. Once the tokens have been added to they can be
+ * examined, in the order they were added, using the provided
+ * <code>list</code> method. This has a scalable capacity.
+ */
+ private class TokenList {
+
+ /**
+ * This is used to cache the segments that are created.
+ */
+ private String[] cache;
+
+ /**
+ * Contains the offsets and lengths of the tokens.
+ */
+ private int[] list;
+
+ /**
+ * Determines the write offset into the array.
+ */
+ private int count;
+
+ /**
+ * Constructor for the <code>TokenList</code> is used to
+ * create a scalable list to store tokens. The initial
+ * list is created with an array of sixteen ints, which
+ * is enough to store eight tokens.
+ */
+ private TokenList(){
+ list = new int[16];
+ }
+
+ /**
+ * This is used to acquire the path from the segment that
+ * is specified. This provides an efficient means to get
+ * the path without having to perform expensive copy of
+ * substring operations.
+ *
+ * @param from this is the path segment to get the path
+ *
+ * @return the string that is the path segment created
+ */
+ public String segment(int from) {
+ int total = count / 2;
+ int left = total - from;
+
+ return segment(from, left);
+ }
+
+ /**
+ * This is used to acquire the path from the segment that
+ * is specified. This provides an efficient means to get
+ * the path without having to perform expensive copy of
+ * substring operations.
+ *
+ * @param from this is the path segment to get the path
+ * @param total this is the number of segments to use
+ *
+ * @return the string that is the path segment created
+ */
+ public String segment(int from, int total) {
+ int last = list[0] + list[1] + 1;
+
+ if(from + total < count / 2) {
+ last = offset(from + total);
+ }
+ int start = offset(from);
+ int length = last - start;
+
+ return new String(buf, start-1, length);
+ }
+
+ /**
+ * This is used to acquire the offset within the buffer
+ * of the specified segment. This allows a path to be
+ * created that is constructed from a given segment.
+ *
+ * @param segment this is the segment offset to use
+ *
+ * @return this returns the offset start for the segment
+ */
+ private int offset(int segment) {
+ int last = count - 2;
+ int shift = segment * 2;
+ int index = last - shift;
+
+ return list[index];
+ }
+
+ /**
+ * This is used to add a new token to the list. Tokens
+ * will be available from the <code>list</code> method in
+ * the order it was added, so the first to be added will
+ * at index zero and the last with be in the last index.
+ *
+ * @param off this is the read offset within the buffer
+ * @param len the number of characters within the token
+ */
+ public void add(int off, int len){
+ if(count+1 > list.length) {
+ resize(count *2);
+ }
+ list[count++] = off;
+ list[count++] = len;
+ }
+
+ /**
+ * This is used to retrieve the list of tokens inserted
+ * to this list using the <code>add</code> method. The
+ * indexes of the tokens represents the order that the
+ * tokens were added to the list.
+ *
+ * @return returns an ordered list of token strings
+ */
+ public String[] list(){
+ if(cache == null) {
+ cache = build();
+ }
+ return cache;
+ }
+
+ /**
+ * This is used to retrieve the list of tokens inserted
+ * to this list using the <code>add</code> method. The
+ * indexes of the tokens represents the order that the
+ * tokens were added to the list.
+ *
+ * @return returns an ordered list of token strings
+ */
+ private String[] build(){
+ String[] value = new String[count/2];
+
+ for(int i =0, j = count/2; i< count; i+=2){
+ int index = j - (i/2) - 1;
+ int off = list[i];
+ int size = list[i + 1];
+
+ value[index] = new String(buf, off, size);
+ }
+ return value;
+ }
+
+ /**
+ * This is used to clear all tokens previously stored
+ * in the list. This is required so that initialization
+ * of the parser with the <code>init</code> method can
+ * ensure that there are no tokens from previous data.
+ */
+ public void clear(){
+ cache =null;
+ count =0;
+ }
+
+ /**
+ * Scales the internal array used should the number of
+ * tokens exceed the initial capacity. This will just
+ * copy across the ints used to represent the token.
+ *
+ * @param size length the capacity is to increase to
+ */
+ private void resize(int size){
+ int[] copy = new int[size];
+ System.arraycopy(list,0,copy,0,count);
+ list = copy;
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/PrincipalParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/PrincipalParser.java
new file mode 100644
index 0000000..52aeff8
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/PrincipalParser.java
@@ -0,0 +1,362 @@
+/*
+ * PrincipalParser.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.parse;
+
+import org.simpleframework.common.parse.ParseBuffer;
+import org.simpleframework.common.parse.Parser;
+import org.simpleframework.http.Principal;
+
+/**
+ * PrincipalParser is a parser class for the HTTP basic authorization
+ * header. It decodes the <code>base64</code> encoding of the user and
+ * password pair.
+ * <p>
+ * This follows the parsing tree of RFC 2617. The goal of this parser
+ * is to decode the <code>base64</code> encoding of the user name and
+ * password. After the string has been decoded then the user name and
+ * password are extracted. This will only parse headers that are from
+ * the <code>Basic</code> authorization scheme. The format of the basic
+ * scheme can be found in RFC 2617 and is of the form
+ * <pre>
+ * Basic SP base64-encoding.
+ * </pre>
+ *
+ * @author Niall Gallagher
+ */
+public class PrincipalParser extends Parser implements Principal {
+
+ /**
+ * Keeps the characters consumed for the password token.
+ */
+ private ParseBuffer password;
+
+ /**
+ * Keeps the characters consumed for the user name token.
+ */
+ private ParseBuffer user;
+
+ /**
+ * Keeps the <code>bytes</code> used for decoding base64.
+ */
+ private byte[] four;
+
+ /**
+ * Tracks the write offset for the buffer.
+ */
+ private int write;
+
+ /**
+ * Tracks the ready offset for the four buffer.
+ */
+ private int ready;
+
+ /**
+ * Tracks the read offset for the buffer.
+ */
+ private int read;
+
+ /**
+ * Creates a <code>Parser</code> for the basic authorization
+ * scheme. This allows headers that are of this scheme to be
+ * broken into its component parts i.e. user name and password.
+ */
+ public PrincipalParser() {
+ this.password = new ParseBuffer();
+ this.user = new ParseBuffer();
+ this.four = new byte[4];
+ }
+
+ /**
+ * Creates a <code>Parser</code> for the basic authorization
+ * scheme. This allows headers that are of this scheme to be
+ * broken into its component parts i.e. user name and password.
+ * This constructor will parse the <code>String</code> given as
+ * the header.
+ *
+ * @param header this is a header value from the basic scheme
+ */
+ public PrincipalParser(String header){
+ this();
+ parse(header);
+ }
+
+ /**
+ * Gets the users password parsed from the Authorization
+ * header value. If there was not password parsed from the
+ * base64 value of the header this returns <code>null</code>
+ *
+ * @return the password for the user or <code>null</code>
+ */
+ public String getPassword(){
+ if(password.length() == 0){
+ return null;
+ }
+ return password.toString();
+ }
+
+ /**
+ * Gets the users name from the Authorization header value.
+ * This will return <code>null</code> if there is no user
+ * name extracted from the base64 header value.
+ *
+ * @return this returns the name of the user
+ */
+ public String getName(){
+ if(user.length() == 0){
+ return null;
+ }
+ return user.toString();
+ }
+
+ /**
+ * Used to parse the actual header data. This will attempt to
+ * read the "Basic" token from the set of characters given, if
+ * this is successful then the username and password is
+ * extracted.
+ */
+ protected void parse(){
+ if(skip("Basic ")){
+ decode();
+ userpass();
+ }
+ }
+
+ /**
+ * This will initialize the <code>Parser</code> when it is ready
+ * to parse a new <code>String</code>. This will reset the
+ * <code>Parser</code> to a ready state. The <code>init</code> method
+ * is invoked by the <code>Parser</code> when the <code>parse</code>
+ * method is invoked.
+ */
+ protected void init() {
+ password.clear();
+ user.clear();
+ write = ready =
+ read = off = 0;
+ pack();
+ }
+
+ /**
+ * This is used to remove all whitespace characters from the
+ * <code>String</code> excluding the whitespace within literals.
+ * The definition of a literal can be found in RFC 2616.
+ * <p>
+ * The definition of a literal for RFC 2616 is anything between 2
+ * quotes but excuding quotes that are prefixed with the backward
+ * slash character.
+ */
+ private void pack() {
+ int len = count;
+ int seek = 0; /* read */
+ int pos = 0; /* write */
+ char ch = 0;
+
+ while(seek <len){ /* trim start*/
+ if(!space(buf[seek])){
+ break;
+ }
+ seek++;
+ }
+ while(seek < len){
+ ch = buf[seek++];
+ if(space(ch)){
+ while(seek < len){ /* skip spaces */
+ if(!space(buf[seek])){
+ break;
+ }
+ seek++;
+ }
+ }
+ buf[pos++] = ch;
+ }
+ if(space(ch)){ /* trim end */
+ pos--;
+ }
+ count = pos;
+ }
+
+ /**
+ * Extracts the name and password of the user from the
+ * <code>name : password</code> pair that was given. This
+ * will take all data up to the first occurence of a
+ * ':' character as the users name and all data after the
+ * colon as the users password.
+ */
+ private void userpass(){
+ userid();
+ off++;
+ password();
+ }
+
+ /**
+ * Extracts the user name from the buffer. This will read up to
+ * the first occurence of a colon, ':', character as the user
+ * name. For the BNF syntax of this see RFC 2617.
+ */
+ private void userid(){
+ while(off < count){
+ char ch = buf[off];
+ if(!text(ch) || ch ==':'){
+ break;
+ }
+ user.append(ch);
+ off++;
+ }
+
+ }
+
+ /**
+ * Extracts the password from the buffer. This will all characters
+ * from the current offset to the first non text character as the
+ * password. For the BNF syntax of this see RFC 2617.
+ */
+ private void password() {
+ while(off < count){
+ char ch = buf[off];
+ if(!text(ch)){
+ break;
+ }
+ password.append(ch);
+ off++;
+ }
+ }
+
+ /**
+ * This is used to remove decode the <code>base64</code> encoding of
+ * the user name and password. This uses a standart <code>base64</code>
+ * decoding scheme.
+ * <p>
+ * For information on the decoding scheme used for <code>base64</code>
+ * see the RFC 2045 on MIME, Multipurpose Internet Mail Extensions.
+ */
+ private void decode() {
+ for(write = read = off; read + 3 < count;) {
+ while(ready < 4) {
+ int ch = translate(buf[read++]);
+ if(ch >= 0) {
+ four[ready++] = (byte)ch;
+ }
+ }
+ if(four[2] == 65) {
+ buf[write++] = first(four);
+ break;
+ } else if(four[3] == 65) {
+ buf[write++] = first(four);
+ buf[write++] = second(four);
+ break;
+ } else {
+ buf[write++] = first(four);
+ buf[write++] = second(four);
+ buf[write++] = third(four);
+ }
+ ready = 0;
+ }
+ count = write;
+ }
+
+ /**
+ * This uses a basic translation from the <code>byte</code> character to the
+ * <code>byte</code> number.
+ * <p>
+ * The table for translation the data can be found in RFC 2045 on
+ * MIME, Multipurpose Internet Mail Extensions.
+ *
+ * @param octet this is the octet ttat is to be translated
+ *
+ * @return this returns the translated octet
+ */
+ private int translate(int octet) {
+ if(octet >= 'A' && octet <= 'Z') {
+ octet = octet - 'A';
+ } else if(octet >= 'a' && octet <= 'z') {
+ octet = (octet - 'a') + 26;
+ } else if(octet >= '0' && octet <= '9') {
+ octet = (octet - '0') + 52;
+ } else if(octet == '+') {
+ octet = 62;
+ } else if(octet == '/') {
+ octet = 63;
+ } else if(octet == '=') {
+ octet = 65;
+ } else {
+ octet = -1;
+ }
+ return octet;
+ }
+
+ /**
+ * This is used to extract the <code>byte</code> from the set of four
+ * <code>bytes</code> given. This method is used to isolate the correct
+ * bits that corrospond to an actual character withing the
+ * <code>base64</code> data.
+ *
+ * @param four this is the four <code>bytes</code> that the character
+ * is to be extracted from
+ *
+ * @return this returns the character extracted
+ */
+ private char first(byte[] four) {
+ return (char)(((four[0] & 0x3f) << 2) | ((four[1] & 0x30) >>> 4));
+ }
+
+ /**
+ * This is used to extract the <code>byte</code> from the set of four
+ * <code>bytes</code> given. This method is used to isolate the correct
+ * bits that corrospond to an actual character withing the
+ * <code>base64</code> data.
+ *
+ * @param four this is the four <code>bytes</code> that the character
+ * is to be extracted from
+ *
+ * @return this returns the character extracted
+
+ */
+ private char second(byte[] four) {
+ return (char)(((four[1] & 0x0f) << 4) | ((four[2] &0x3c) >>> 2));
+ }
+
+ /**
+ * This is used to extract the <code>byte</code> from the set of four
+ * <code>bytes</code> given. This method is used to isolate the correct
+ * bits that corrospond to an actual character withing the
+ * <code>base64</code> data.
+ *
+ * @param four this is the four <code>bytes</code> that the character
+ * is to be extracted from
+ *
+ * @return this returns the character extracted
+ */
+ private char third(byte[] four) {
+ return (char)(((four[2] & 0x03) << 6) | (four[3] & 0x3f));
+ }
+
+ /**
+ * This is used to determine wheather or not a character is a
+ * <code>TEXT</code> character according to the HTTP specification,
+ * that is RFC 2616 specifies a <code>TEXT</code> character as one
+ * that is any octet except those less than 32 and not 127.
+ *
+ * @param c this is the character that is to be determined
+ *
+ * @return this returns true if the character is a <code>TEXT</code>
+ */
+ private boolean text(char c){
+ return c > 31 && c != 127 && c <= 0xffff;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/QueryParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/QueryParser.java
new file mode 100644
index 0000000..56b6788
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/QueryParser.java
@@ -0,0 +1,636 @@
+/*
+ * QueryParser.java December 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.parse;
+
+import org.simpleframework.common.parse.MapParser;
+import org.simpleframework.http.Query;
+
+import java.net.URLEncoder;
+import java.util.Set;
+
+/**
+ * The <code>ParameterParser</code> is used to parse data encoded in
+ * the <code>application/x-www-form-urlencoded</code> MIME type. It
+ * is also used to parse a query string from a HTTP URL, see RFC 2616.
+ * The parsed parameters are available through the various methods of
+ * the <code>org.simpleframework.http.net.Query</code> interface. The
+ * syntax of the parsed parameters is described below in BNF.
+ * <pre>
+ *
+ * params = *(pair [ "&amp;" params])
+ * pair = name "=" value
+ * name = *(text | escaped)
+ * value = *(text | escaped)
+ * escaped = % HEX HEX
+ *
+ * </pre>
+ * This will consume all data found as a name or value, if the data
+ * is a "+" character then it is replaced with a space character.
+ * This regards only "=", "&amp;", and "%" as having special values.
+ * The "=" character delimits the name from the value and the "&amp;"
+ * delimits the name value pair. The "%" character represents the
+ * start of an escaped sequence, which consists of two hex digits.
+ * All escaped sequences are converted to its character value.
+ *
+ * @author Niall Gallagher
+ */
+public class QueryParser extends MapParser<String> implements Query {
+
+ /**
+ * Used to accumulate the characters for the parameter name.
+ */
+ private Token name;
+
+ /**
+ * Used to accumulate the characters for the parameter value.
+ */
+ private Token value;
+
+ /**
+ * Constructor for the <code>ParameterParser</code>. This creates
+ * an instance that can be use to parse HTML form data and URL
+ * query strings encoded as application/x-www-form-urlencoded.
+ * The parsed parameters are made available through the interface
+ * <code>org.simpleframework.util.net.Query</code>.
+ */
+ public QueryParser(){
+ this.name = new Token();
+ this.value = new Token();
+ }
+
+ /**
+ * Constructor for the <code>ParameterParser</code>. This creates
+ * an instance that can be use to parse HTML form data and URL
+ * query strings encoded as application/x-www-form-urlencoded.
+ * The parsed parameters are made available through the interface
+ * <code>org.simpleframework.util.net.Query</code>.
+ *
+ * @param text this is the text to parse for the parameters
+ */
+ public QueryParser(String text){
+ this();
+ parse(text);
+ }
+
+ /**
+ * This extracts an integer parameter for the named value. If the
+ * named parameter does not exist this will return a zero value.
+ * If however the parameter exists but is not in the format of a
+ * decimal integer value then this will throw an exception.
+ *
+ * @param name the name of the parameter value to retrieve
+ *
+ * @return this returns the named parameter value as an integer
+ */
+ public int getInteger(Object name) {
+ String value = get(name);
+
+ if(value != null) {
+ return Integer.parseInt(value);
+ }
+ return 0;
+ }
+
+ /**
+ * This extracts a float parameter for the named value. If the
+ * named parameter does not exist this will return a zero value.
+ * If however the parameter exists but is not in the format of a
+ * floating point number then this will throw an exception.
+ *
+ * @param name the name of the parameter value to retrieve
+ *
+ * @return this returns the named parameter value as a float
+ */
+ public float getFloat(Object name) {
+ String value = get(name);
+
+ if(value != null) {
+ return Float.parseFloat(value);
+ }
+ return 0.0f;
+ }
+
+ /**
+ * This extracts a boolean parameter for the named value. If the
+ * named parameter does not exist this will return false otherwise
+ * the value is evaluated. If it is either <code>true</code> or
+ * <code>false</code> then those boolean values are returned.
+ *
+ * @param name the name of the parameter value to retrieve
+ *
+ * @return this returns the named parameter value as an float
+ */
+ public boolean getBoolean(Object name) {
+ Boolean flag = Boolean.FALSE;
+ String value = get(name);
+
+ if(value != null) {
+ flag = Boolean.valueOf(value);
+ }
+ return flag.booleanValue();
+ }
+
+
+ /**
+ * This initializes the parser so that it can be used several
+ * times. This clears any previous parameters extracted. This
+ * ensures that when the next <code>parse(String)</code> is
+ * invoked the status of the <code>Query</code> is empty.
+ */
+ protected void init(){
+ all.clear();
+ map.clear();
+ name.len = 0;
+ value.len = 0;
+ off = 0;
+ }
+
+ /**
+ * This performs the actual parsing of the parameter text. The
+ * parameters parsed from this are taken as "name=value" pairs.
+ * Multiple pairs within the text are separated by an "&amp;".
+ * This will parse and insert all parameters into a hashtable.
+ */
+ protected void parse() {
+ param();
+ while(skip("&")){
+ param();
+ }
+ }
+
+ /**
+ * This method adds the name and value to a map so that the next
+ * name and value can be collected. The name and value are added
+ * to the map as string objects. Once added to the map the
+ * <code>Token</code> objects are set to have zero length so they
+ * can be reused to collect further values. This will add the
+ * values to the map as an array of type string. This is done so
+ * that if there are multiple values that they can be stored.
+ */
+ private void insert(){
+ if(name.len > 0){
+ insert(name,value);
+ }
+ name.len = 0;
+ value.len = 0;
+ }
+
+ /**
+ * This will add the given name and value to the parameters map.
+ * If any previous value of the given name has been inserted
+ * into the map then this will overwrite that value. This is
+ * used to ensure that the string value is inserted to the map.
+ *
+ * @param name this is the name of the value to be inserted
+ * @param value this is the value of a that is to be inserted
+ */
+ private void insert(Token name, Token value){
+ put(name.toString(), value.toString());
+ }
+
+ /**
+ * This is an expression that is defined by RFC 2396 it is used
+ * in the definition of a segment expression. This is basically
+ * a list of chars with escaped sequences.
+ * <p>
+ * This method has to ensure that no escaped chars go unchecked.
+ * This ensures that the read offset does not go out of bounds
+ * and consequently throw an out of bounds exception.
+ */
+ private void param() {
+ name();
+ if(skip("=")){ /* in case of error*/
+ value();
+ }
+ insert();
+ }
+
+ /**
+ * This extracts the name of the parameter from the character
+ * buffer. The name of a parameter is defined as a set of
+ * chars including escape sequences. This will extract the
+ * parameter name and buffer the chars. The name ends when a
+ * equals character, "=", is encountered.
+ */
+ private void name(){
+ int mark = off;
+ int pos = off;
+
+ while(off < count){
+ if(buf[off]=='%'){ /* escaped */
+ escape();
+ }else if(buf[off]=='=') {
+ break;
+ }else if(buf[off]=='+'){
+ buf[off] = ' ';
+ }
+ buf[pos++] = buf[off++];
+ }
+ name.len = pos - mark;
+ name.off = mark;
+ }
+
+ /**
+ * This extracts a parameter value from a path segment. The
+ * parameter value consists of a sequence of chars and some
+ * escape sequences. The parameter value is buffered so that
+ * the name and values can be paired. The end of the value
+ * is determined as the end of the buffer or an ampersand.
+ */
+ private void value(){
+ int mark = off;
+ int pos = off;
+
+ while(off < count){
+ if(buf[off]=='%'){ /* escaped */
+ escape();
+ }else if(buf[off]=='+'){
+ buf[off] = ' ';
+ }else if(buf[off]=='&'){
+ break;
+ }
+ buf[pos++] = buf[off++];
+ }
+ value.len = pos - mark;
+ value.off = mark;
+ }
+
+ /**
+ * This converts an encountered escaped sequence, that is all
+ * embedded hexidecimal characters into a native UCS character
+ * value. This does not take any characters from the stream it
+ * just prepares the buffer with the correct byte. The escaped
+ * sequence within the URI will be interpreded as UTF-8.
+ * <p>
+ * This will leave the next character to read from the buffer
+ * as the character encoded from the URI. If there is a fully
+ * valid escaped sequence, that is <code>"%" HEX HEX</code>.
+ * This decodes the escaped sequence using UTF-8 encoding, all
+ * encoded sequences should be in UCS-2 to fit in a Java char.
+ */
+ private void escape() {
+ int peek = peek(off);
+
+ if(!unicode(peek)) {
+ binary(peek);
+ }
+ }
+
+ /**
+ * This method determines, using a peek character, whether the
+ * sequence of escaped characters within the URI is binary data.
+ * If the data within the escaped sequence is binary then this
+ * will ensure that the next character read from the URI is the
+ * binary octet. This is used strictly for backward compatible
+ * parsing of URI strings, binary data should never appear.
+ *
+ * @param peek this is the first escaped character from the URI
+ *
+ * @return currently this implementation always returns true
+ */
+ private boolean binary(int peek) {
+ if(off + 2 < count) {
+ off += 2;
+ buf[off] =bits(peek);
+ }
+ return true;
+ }
+
+ /**
+ * This method determines, using a peek character, whether the
+ * sequence of escaped characters within the URI is in UTF-8. If
+ * a UTF-8 character can be successfully decoded from the URI it
+ * will be the next character read from the buffer. This can
+ * check for both UCS-2 and UCS-4 characters. However, because
+ * the Java <code>char</code> can only hold UCS-2, the UCS-4
+ * characters will have only the low order octets stored.
+ * <p>
+ * The WWW Consortium provides a reference implementation of a
+ * UTF-8 decoding for Java, in this the low order octets in the
+ * UCS-4 sequence are used for the character. So, in the
+ * absence of a defined behaviour, the W3C behaviour is assumed.
+ *
+ * @param peek this is the first escaped character from the URI
+ *
+ * @return this returns true if a UTF-8 character is decoded
+ */
+ private boolean unicode(int peek) {
+ if((peek & 0x80) == 0x00){
+ return unicode(peek, 0);
+ }
+ if((peek & 0xe0) == 0xc0){
+ return unicode(peek & 0x1f, 1);
+ }
+ if((peek & 0xf0) == 0xe0){
+ return unicode(peek & 0x0f, 2);
+ }
+ if((peek & 0xf8) == 0xf0){
+ return unicode(peek & 0x07, 3);
+ }
+ if((peek & 0xfc) == 0xf8){
+ return unicode(peek & 0x03, 4);
+ }
+ if((peek & 0xfe) == 0xfc){
+ return unicode(peek & 0x01, 5);
+ }
+ return false;
+ }
+
+ /**
+ * This method will decode the specified amount of escaped
+ * characters from the URI and convert them into a single Java
+ * UCS-2 character. If there are not enough characters within
+ * the URI then this will return false and leave the URI alone.
+ * <p>
+ * The number of characters left is determined from the first
+ * UTF-8 octet, as specified in RFC 2279, and because this is
+ * a URI there must that number of <code>"%" HEX HEX</code>
+ * sequences left. If successful the next character read is
+ * the UTF-8 sequence decoded into a native UCS-2 character.
+ *
+ * @param peek contains the bits read from the first UTF octet
+ * @param more this specifies the number of UTF octets left
+ *
+ * @return this returns true if a UTF-8 character is decoded
+ */
+ private boolean unicode(int peek, int more) {
+ if(off + more * 3 >= count) {
+ return false;
+ }
+ return unicode(peek,more,off);
+ }
+
+ /**
+ * This will decode the specified amount of trailing UTF-8 bits
+ * from the URI. The trailing bits are those following the first
+ * UTF-8 octet, which specifies the length, in octets, of the
+ * sequence. The trailing octets are of the form 10xxxxxx, for
+ * each of these octets only the last six bits are valid UCS
+ * bits. So a conversion is basically an accumulation of these.
+ * <p>
+ * If at any point during the accumulation of the UTF-8 bits
+ * there is a parsing error, then parsing is aborted an false
+ * is returned, as a result the URI is left unchanged.
+ *
+ * @param peek bytes that have been accumulated fron the URI
+ * @param more this specifies the number of UTF octets left
+ * @param pos this specifies the position the parsing begins
+ *
+ * @return this returns true if a UTF-8 character is decoded
+ */
+ private boolean unicode(int peek, int more, int pos) {
+ while(more-- > 0) {
+ if(buf[pos] == '%'){
+ int next = pos + 3;
+ int hex = peek(next);
+
+ if((hex & 0xc0) == 0x80){
+ peek = (peek<<6)|(hex&0x3f);
+ pos = next;
+ continue;
+ }
+ }
+ return false;
+ }
+ if(pos + 2 < count) {
+ off = pos + 2;
+ buf[off]= bits(peek);
+ }
+ return true;
+ }
+
+ /**
+ * Defines behaviour for UCS-2 versus UCS-4 conversion from four
+ * octets. The UTF-8 encoding scheme enables UCS-4 characters to
+ * be encoded and decodeded. However, Java supports the 16-bit
+ * UCS-2 character set, and so the 32-bit UCS-4 character set is
+ * not compatable. This basically decides what to do with UCS-4.
+ *
+ * @param data up to four octets to be converted to UCS-2 format
+ *
+ * @return this returns a native UCS-2 character from the int
+ */
+ private char bits(int data) {
+ return (char)data;
+ }
+
+ /**
+ * This will return the escape expression specified from the URI
+ * as an integer value of the hexadecimal sequence. This does
+ * not make any changes to the buffer it simply checks to see if
+ * the characters at the position specified are an escaped set
+ * characters of the form <code>"%" HEX HEX</code>, if so, then
+ * it will convert that hexadecimal string in to an integer
+ * value, or -1 if the expression is not hexadecimal.
+ *
+ * @param pos this is the position the expression starts from
+ *
+ * @return the integer value of the hexadecimal expression
+ */
+ private int peek(int pos) {
+ if(buf[pos] == '%'){
+ if(count <= pos + 2) {
+ return -1;
+ }
+ char high = buf[pos + 1];
+ char low = buf[pos + 2];
+
+ return convert(high, low);
+ }
+ return -1;
+ }
+
+ /**
+ * This will convert the two hexidecimal characters to a real
+ * integer value, which is returned. This requires characters
+ * within the range of 'A' to 'F' and 'a' to 'f', and also
+ * the digits '0' to '9'. The characters encoded using the
+ * ISO-8859-1 encoding scheme, if the characters are not with
+ * in the range specified then this returns -1.
+ *
+ * @param high this is the high four bits within the integer
+ * @param low this is the low four bits within the integer
+ *
+ * @return this returns the indeger value of the conversion
+ */
+ private int convert(char high, char low) {
+ int hex = 0x00;
+
+ if(hex(high) && hex(low)){
+ if('A' <= high && high <= 'F'){
+ high -= 'A' - 'a';
+ }
+ if(high >= 'a') {
+ hex ^= (high-'a')+10;
+ } else {
+ hex ^= high -'0';
+ }
+ hex <<= 4;
+
+ if('A' <= low && low <= 'F') {
+ low -= 'A' - 'a';
+ }
+ if(low >= 'a') {
+ hex ^= (low-'a')+10;
+ } else {
+ hex ^= low-'0';
+ }
+ return hex;
+ }
+ return -1;
+ }
+
+ /**
+ * This is used to determine whether a char is a hexadecimal
+ * <code>char</code> or not. A hexadecimal character is considered
+ * to be a character within the range of <code>0 - 9</code> and
+ * between <code>a - f</code> and <code>A - F</code>. This will
+ * return <code>true</code> if the character is in this range.
+ *
+ * @param ch this is the character which is to be determined here
+ *
+ * @return true if the character given has a hexadecimal value
+ */
+ private boolean hex(char ch) {
+ if(ch >= '0' && ch <= '9') {
+ return true;
+ } else if(ch >='a' && ch <= 'f') {
+ return true;
+ } else if(ch >= 'A' && ch <= 'F') {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This <code>encode</code> method will escape the text that
+ * is provided. This is used to that the parameter pairs can
+ * be encoded in such a way that it can be transferred over
+ * HTTP/1.1 using the ISO-8859-1 character set.
+ *
+ * @param text this is the text that is to be escaped
+ *
+ * @return the text with % HEX HEX UTF-8 escape sequences
+ */
+ private String encode(String text) {
+ try {
+ return URLEncoder.encode(text, "UTF-8");
+ }catch(Exception e){
+ return text;
+ }
+ }
+
+ /**
+ * This <code>encode</code> method will escape the name=value
+ * pair provided using the UTF-8 character set. This method
+ * will ensure that the parameters are encoded in such a way
+ * that they can be transferred via HTTP in ISO-8859-1.
+ *
+ * @param name this is the name of that is to be escaped
+ * @param value this is the value that is to be escaped
+ *
+ * @return the pair with % HEX HEX UTF-8 escape sequences
+ */
+ private String encode(String name, String value) {
+ return encode(name) + "=" + encode(value);
+ }
+
+ /**
+ * This <code>toString</code> method is used to compose an string
+ * in the <code>application/x-www-form-urlencoded</code> MIME type.
+ * This will encode the tokens specified in the <code>Set</code>.
+ * Each name=value pair acquired is converted into a UTF-8 escape
+ * sequence so that the parameters can be sent in the IS0-8859-1
+ * format required via the HTTP/1.1 specification RFC 2616.
+ *
+ * @param set this is the set of parameters to be encoded
+ *
+ * @return returns a HTTP parameter encoding for the pairs
+ */
+ public String toString(Set set) {
+ Object[] list = set.toArray();
+ String text = "";
+
+ for(int i = 0; i < list.length; i++){
+ String name = list[i].toString();
+ String value = get(name);
+
+ if(i > 0) {
+ text += "&";
+ }
+ text += encode(name, value);
+ }
+ return text;
+ }
+
+ /**
+ * This <code>toString</code> method is used to compose an string
+ * in the <code>application/x-www-form-urlencoded</code> MIME type.
+ * This will iterate over all tokens that have been added to this
+ * object, either during parsing, or during use of the instance.
+ * Each name=value pair acquired is converted into a UTF-8 escape
+ * sequence so that the parameters can be sent in the IS0-8859-1
+ * format required via the HTTP/1.1 specification RFC 2616.
+ *
+ * @return returns a HTTP parameter encoding for the pairs
+ */
+ public String toString() {
+ Set set = map.keySet();
+
+ if(map.size() > 0) {
+ return toString(set);
+ }
+ return "";
+ }
+
+ /**
+ * This is used to mark regions within the buffer that represent
+ * a valid token for either the name of a parameter or its value.
+ * This is used as an alternative to the <code>ParseBuffer</code>
+ * which requires memory to be allocated for storing the data
+ * read from the buffer. This requires only two integer values.
+ */
+ private class Token {
+
+ /**
+ * This represents the number of characters in the token.
+ */
+ public int len;
+
+ /**
+ * This represents the start offset within the buffer.
+ */
+ public int off;
+
+ /**
+ * In order to represent the <code>Token</code> as a value
+ * that can be used this converts it to a <code>String</code>.
+ * If the length of the token is less than or equal to zero
+ * this will return and empty string for the value.
+ *
+ * @return this returns a value representing the token
+ */
+ public String toString() {
+ if(len <= 0) {
+ return "";
+ }
+ return new String(buf,off,len);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/parse/ValueParser.java b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ValueParser.java
new file mode 100644
index 0000000..99b16b9
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/parse/ValueParser.java
@@ -0,0 +1,108 @@
+/*
+ * ValueParser.java September 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.parse;
+
+import java.util.List;
+
+/**
+ * The <code>ValueParser</code> is used to extract a comma separated
+ * list of HTTP header values. This will extract values without
+ * any leading or trailing spaces, which enables the values to be
+ * used. Listing the values that appear in the header also requires
+ * that the values are ordered. This orders the values using the
+ * values that appear with any quality parameter associated with it.
+ * The quality value is a special parameter that often found in a
+ * comma separated value list to specify the client preference.
+ * <pre>
+ *
+ * image/gif, image/jpeg, text/html
+ * image/gif;q=1.0, image/jpeg;q=0.8, image/png; q=1.0,*;q=0.1
+ * gzip;q=1.0, identity; q=0.5, *;q=0
+ *
+ * </pre>
+ * The above lists taken from RFC 2616 provides an example of the
+ * common form comma separated values take. The first illustrates
+ * a simple comma delimited list, here the ordering of values is
+ * determined from left to right. The second and third list have
+ * quality values associated with them, these are used to specify
+ * a preference and thus order.
+ * <p>
+ * Each value within a list has an implicit quality value of 1.0.
+ * If the value is explicitly set with a the "q" parameter, then
+ * the values can range from 1.0 to 0.001. This parser ensures
+ * that the order of values returned from the <code>list</code>
+ * method adheres to the optional quality parameters and ensures
+ * that the quality parameters a removed from the resulting text.
+ *
+ * @author Niall Gallagher
+ */
+public class ValueParser extends ListParser<String> {
+
+ /**
+ * Constructor for the <code>ValueParser</code>. This creates
+ * a parser with no initial parse data, if there are headers to
+ * be parsed then the <code>parse(String)</code> method or
+ * <code>parse(List)</code> method can be used. This will
+ * parse a delimited list according so RFC 2616 section 4.2.
+ */
+ public ValueParser(){
+ super();
+ }
+
+ /**
+ * Constructor for the <code>ValueParser</code>. This creates
+ * a parser with the text supplied. This will parse the comma
+ * separated list according to RFC 2616 section 2.1 and 4.2.
+ * The tokens can be extracted using the <code>list</code>
+ * method, which will also sort and trim the tokens.
+ *
+ * @param text this is the comma separated list to be parsed
+ */
+ public ValueParser(String text) {
+ super(text);
+ }
+
+ /**
+ * Constructor for the <code>ValueParser</code>. This creates
+ * a parser with the text supplied. This will parse the comma
+ * separated list according to RFC 2616 section 2.1 and 4.2.
+ * The tokens can be extracted using the <code>list</code>
+ * method, which will also sort and trim the tokens.
+ *
+ * @param list a list of comma separated lists to be parsed
+ */
+ public ValueParser(List<String> list) {
+ super(list);
+ }
+
+ /**
+ * This creates a string object using an offset and a length.
+ * The string is created from the extracted token and the offset
+ * and length ensure that no leading or trailing whitespace are
+ * within the created string object.
+ *
+ * @param text this is the text buffer to acquire the value from
+ * @param start the offset within the buffer to take characters
+ * @param len this is the number of characters within the token
+ */
+ @Override
+ protected String create(char[] text, int start, int len){
+ return new String(text, start, len);
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/BinaryData.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/BinaryData.java
new file mode 100644
index 0000000..bea3c63
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/BinaryData.java
@@ -0,0 +1,75 @@
+/*
+ * BinaryData.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>BinaryData</code> object represents a binary payload for
+ * a WebScoket frame. This can be used to send any type of data. If
+ * however it is used to send text data then it is decoded as UTF-8.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.DataFrame
+ */
+public class BinaryData implements Data {
+
+ /**
+ * This is used to convert the binary payload to text.
+ */
+ private final DataConverter converter;
+
+ /**
+ * This is the byte array that represents the binary payload.
+ */
+ private final byte[] data;
+
+ /**
+ * Constructor for the <code>BinaryData</code> object. It requires
+ * an array of binary data that will be send within a frame.
+ *
+ * @param data the byte array representing the frame payload
+ */
+ public BinaryData(byte[] data) {
+ this.converter = new DataConverter();
+ this.data = data;
+ }
+
+ /**
+ * This returns the binary payload that is to be sent with a frame.
+ * It contains no headers or other meta data. If the original data
+ * was text this converts it to UTF-8.
+ *
+ * @return the binary payload to be sent with the frame
+ */
+ public byte[] getBinary() {
+ return data;
+ }
+
+ /**
+ * This returns the text payload that is to be sent with a frame.
+ * It contains no header information or meta data. Caution should
+ * be used with this method as binary payloads will encode to
+ * garbage when decoded as UTF-8.
+ *
+ * @return the text payload to be sent with the frame
+ */
+ public String getText() {
+ return converter.convert(data);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/CloseCode.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/CloseCode.java
new file mode 100644
index 0000000..c64c605
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/CloseCode.java
@@ -0,0 +1,150 @@
+/*
+ * CloseCode.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>CloseCode</code> enumerates the closure codes specified in
+ * RFC 6455. When closing an established connection an endpoint may
+ * indicate a reason for closure. The interpretation of this reason by
+ * an endpoint, and the action an endpoint should take given this reason,
+ * are left undefined by RFC 6455. The specification defines a set of
+ * status codes and specifies which ranges may be used by extensions,
+ * frameworks, and end applications. The status code and any associated
+ * textual message are optional components of a Close frame.
+ *
+ * @author niall.gallagher
+ */
+public enum CloseCode {
+
+ /**
+ * Indicates the purpose for the connection has been fulfilled.
+ */
+ NORMAL_CLOSURE(1000),
+
+ /**
+ * Indicates that the server is going down or the client browsed away.
+ */
+ GOING_AWAY(1001),
+
+ /**
+ * Indicates the connection is terminating due to a protocol error.
+ */
+ PROTOCOL_ERROR(1002),
+
+ /**
+ * Indicates the connection received a data type it cannot accept.
+ */
+ UNSUPPORTED_DATA(1003),
+
+ /**
+ * According to RFC 6455 this has been reserved for future use.
+ */
+ RESERVED(1004),
+
+ /**
+ * Indicates that no status code was present and should not be used.
+ */
+ NO_STATUS_CODE(1005),
+
+ /**
+ * Indicates an abnormal closure and should not be used.
+ */
+ ABNORMAL_CLOSURE(1006),
+
+ /**
+ * Indicates that a payload was not consistent with the message type.
+ */
+ INVALID_FRAME_DATA(1007),
+
+ /**
+ * Indicates an endpoint received a message that violates its policy.
+ */
+ POLICY_VIOLATION(1008),
+
+ /**
+ * Indicates that a payload is too big to be processed.
+ */
+ TOO_BIG(1009),
+
+ /**
+ * Indicates that the server did not negotiate an extension properly.
+ */
+ NO_EXTENSION(1010),
+
+ /**
+ * Indicates an unexpected error within the server.
+ */
+ INTERNAL_SERVER_ERROR(1011),
+
+ /**
+ * Indicates a validation failure for TLS and should not be used.
+ */
+ TLS_HANDSHAKE_FAILURE(1015);
+
+ /**
+ * This is the actual integer value representing the code.
+ */
+ public final int code;
+
+ /**
+ * This is the high order byte for the closure code.
+ */
+ public final int high;
+
+ /**
+ * This is the low order byte for the closure code.
+ */
+ public final int low;
+
+ /**
+ * Constructor for the <code>CloseCode</code> object. This is used
+ * to create a closure code using one of the pre-defined values
+ * within RFC 6455.
+ *
+ * @param code this is the code that is to be used
+ */
+ private CloseCode(int code) {
+ this.high = code & 0x0f;
+ this.low = code & 0xf0;
+ this.code = code;
+ }
+
+ /**
+ * This is the data that represents the closure code. The array
+ * contains the high order byte and the low order byte as taken
+ * from the pre-defined closure code.
+ *
+ * @return a byte array representing the closure code
+ */
+ public byte[] getData() {
+ return new byte[] { (byte)high, (byte)low };
+ }
+
+
+ public static CloseCode resolveCode(int high, int low) {
+ for(CloseCode code : values()) {
+ if(code.high == high) {
+ if(code.low == low) {
+ return code;
+ }
+ }
+ }
+ return NO_STATUS_CODE;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/Data.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Data.java
new file mode 100644
index 0000000..bb79830
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Data.java
@@ -0,0 +1,51 @@
+/*
+ * Data.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>Data</code> interface represents a payload for a WebScoket
+ * frame. It can hold either binary data or text data. For performance
+ * binary frames are a better choice as all text frames need to be
+ * encoded as UTF-8 from the native UCS2 format.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.DataFrame
+ */
+public interface Data {
+
+ /**
+ * This returns the binary payload that is to be sent with a frame.
+ * It contains no headers or other meta data. If the original data
+ * was text this converts it to UTF-8.
+ *
+ * @return the binary payload to be sent with the frame
+ */
+ byte[] getBinary();
+
+ /**
+ * This returns the text payload that is to be sent with a frame.
+ * It contains no header information or meta data. Caution should
+ * be used with this method as binary payloads will encode to
+ * garbage when decoded as UTF-8.
+ *
+ * @return the text payload to be sent with the frame
+ */
+ String getText();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/DataConverter.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/DataConverter.java
new file mode 100644
index 0000000..5713fd6
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/DataConverter.java
@@ -0,0 +1,111 @@
+/*
+ * DataConverter.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>DataConverter</code> object is used to convert binary data
+ * to text data and vice versa. According to RFC 6455 a particular text
+ * frame might include a partial UTF-8 sequence; however, the whole
+ * message MUST contain valid UTF-8.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.DataFrame
+ */
+public class DataConverter {
+
+ /**
+ * This is the character encoding used to convert the text data.
+ */
+ private final String charset;
+
+ /**
+ * Constructor for the <code>DataConverter</code> object. By default
+ * this uses UTF-8 character encoding to convert text data as this
+ * is what is required for RFC 6455 section 5.6.
+ */
+ public DataConverter() {
+ this("UTF-8");
+ }
+
+ /**
+ * Constructor for the <code>DataConverter</code> object. This can be
+ * used to specific a character encoding other than UTF-8. However it
+ * is not recommended as RFC 6455 section 5.6 suggests the frame must
+ * contain valid UTF-8 data.
+ *
+ * @param charset the character encoding to be used
+ */
+ public DataConverter(String charset) {
+ this.charset = charset;
+ }
+
+ /**
+ * This method is used to convert text using the character encoding
+ * specified when constructing the converter. Typically this will use
+ * UTF-8 as required by RFC 6455.
+ *
+ * @param text this is the string to convert to a byte array
+ *
+ * @return a byte array decoded using the specified encoding
+ */
+ public byte[] convert(String text) {
+ try {
+ return text.getBytes(charset);
+ } catch(Exception e) {
+ throw new IllegalStateException("Could not encode text as " + charset, e);
+ }
+ }
+
+ /**
+ * This method is used to convert data using the character encoding
+ * specified when constructing the converter. Typically this will use
+ * UTF-8 as required by RFC 6455.
+ *
+ * @param text this is the byte array to convert to a string
+ *
+ * @return a string encoded using the specified encoding
+ */
+ public String convert(byte[] binary) {
+ try {
+ return new String(binary, charset);
+ } catch(Exception e) {
+ throw new IllegalStateException("Could not decode data as " + charset, e);
+ }
+ }
+
+ /**
+ * This method is used to convert data using the character encoding
+ * specified when constructing the converter. Typically this will use
+ * UTF-8 as required by RFC 6455.
+ *
+ * @param text this is the byte array to convert to a string
+ * @param offset the is the offset to read the bytes from
+ * @param size this is the number of bytes to be used
+ *
+ * @return a string encoded using the specified encoding
+ */
+ public String convert(byte[] binary, int offset, int size) {
+ try {
+ return new String(binary, offset, size, charset);
+ } catch(Exception e) {
+ throw new IllegalStateException("Could not decode data as " + charset, e);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/DataFrame.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/DataFrame.java
new file mode 100644
index 0000000..b51cd2b
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/DataFrame.java
@@ -0,0 +1,212 @@
+/*
+ * DataFrame.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>DataFrame</code> object represents a frame as defined in
+ * RFC 6455. A frame is a very lightweight envelope used to send
+ * control information and either text or binary user data. Typically
+ * a frame will represent a single message however, it is possible
+ * to fragment a single frame up in to several frames. A fragmented
+ * frame has a specific <code>FrameType</code> indicating that it
+ * is a continuation frame.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.Data
+ */
+public class DataFrame implements Frame {
+
+ /**
+ * This is the type used to determine the intent of the frame.
+ */
+ private final FrameType type;
+
+ /**
+ * This contains the payload to be sent with the frame.
+ */
+ private final Data data;
+
+ /**
+ * This determines if the frame is the last of a sequence.
+ */
+ private final boolean last;
+
+ /**
+ * Constructor for the <code>DataFrame</code> object. This is used
+ * to create a frame using the specified data and frame type. A
+ * zero payload is created using this constructor and is suitable
+ * only for specific control frames such as connection termination.
+ *
+ * @param type this is the frame type used for this instance
+ */
+ public DataFrame(FrameType type) {
+ this(type, new byte[0]);
+ }
+
+ /**
+ * Constructor for the <code>DataFrame</code> object. This is used
+ * to create a frame using the specified data and frame type. In
+ * some cases a control frame may require a zero length payload.
+ *
+ * @param type this is the frame type used for this instance
+ * @param data this is the payload for this frame
+ */
+ public DataFrame(FrameType type, byte[] data) {
+ this(type, data, true);
+ }
+
+ /**
+ * Constructor for the <code>DataFrame</code> object. This is used
+ * to create a frame using the specified data and frame type. In
+ * some cases a control frame may require a zero length payload.
+ *
+ * @param type this is the frame type used for this instance
+ * @param data this is the payload for this frame
+ * @param last true if this is not a fragment in a sequence
+ */
+ public DataFrame(FrameType type, byte[] data, boolean last) {
+ this(type, new BinaryData(data), last);
+ }
+
+ /**
+ * Constructor for the <code>DataFrame</code> object. This is used
+ * to create a frame using the specified data and frame type. In
+ * some cases a control frame may require a zero length payload.
+ *
+ * @param type this is the frame type used for this instance
+ * @param data this is the payload for this frame
+ */
+ public DataFrame(FrameType type, String text) {
+ this(type, text, true);
+ }
+
+ /**
+ * Constructor for the <code>DataFrame</code> object. This is used
+ * to create a frame using the specified data and frame type. In
+ * some cases a control frame may require a zero length payload.
+ *
+ * @param type this is the frame type used for this instance
+ * @param data this is the payload for this frame
+ * @param last true if this is not a fragment in a sequence
+ */
+ public DataFrame(FrameType type, String text, boolean last) {
+ this(type, new TextData(text), last);
+ }
+
+ /**
+ * Constructor for the <code>DataFrame</code> object. This is used
+ * to create a frame using the specified data and frame type. In
+ * some cases a control frame may require a zero length payload.
+ *
+ * @param type this is the frame type used for this instance
+ * @param data this is the payload for this frame
+ */
+ public DataFrame(FrameType type, Data data) {
+ this(type, data, true);
+ }
+
+ /**
+ * Constructor for the <code>DataFrame</code> object. This is used
+ * to create a frame using the specified data and frame type. In
+ * some cases a control frame may require a zero length payload.
+ *
+ * @param type this is the frame type used for this instance
+ * @param data this is the payload for this frame
+ * @param last true if this is not a fragment in a sequence
+ */
+ public DataFrame(FrameType type, Data data, boolean last) {
+ this.data = data;
+ this.type = type;
+ this.last = last;
+ }
+
+ /**
+ * This is used to determine if the frame is the final frame in
+ * a sequence of fragments or a whole frame. If this returns false
+ * then the frame is a continuation from from a sequence of
+ * fragments, otherwise it is a whole frame or the last fragment.
+ *
+ * @return this returns false if the frame is a fragment
+ */
+ public boolean isFinal() {
+ return last;
+ }
+
+ /**
+ * This returns the binary payload that is to be sent with the frame.
+ * It contains no headers or other meta data. If the original data
+ * was text this converts it to UTF-8.
+ *
+ * @return the binary payload to be sent with the frame
+ */
+ public byte[] getBinary() {
+ return data.getBinary();
+ }
+
+ /**
+ * This returns the text payload that is to be sent with the frame.
+ * It contains no header information or meta data. Caution should
+ * be used with this method as binary payloads will encode to
+ * garbage when decoded as UTF-8.
+ *
+ * @return the text payload to be sent with the frame
+ */
+ public String getText(){
+ return data.getText();
+ }
+
+ /**
+ * This method is used to convert from one frame type to another.
+ * Converting a frame type is useful in scenarios such as when a
+ * ping needs to respond to a pong or when it is more convenient
+ * to send a text frame as binary.
+ *
+ * @param type this is the frame type to convert to
+ *
+ * @return a new frame using the specified frame type
+ */
+ public Frame getFrame(FrameType type) {
+ return new DataFrame(type, data, last);
+ }
+
+ /**
+ * This is used to determine the type of frame. Interpretation of
+ * this type is outlined in RFC 6455 and can be loosely categorised
+ * as control frames and either data or binary frames.
+ *
+ * @return this returns the type of frame that this represents
+ */
+ public FrameType getType(){
+ return type;
+ }
+
+ /**
+ * This returns the text payload that is to be sent with the frame.
+ * It contains no header information or meta data. Caution should
+ * be used with this method as binary payloads will encode to
+ * garbage when decoded as UTF-8.
+ *
+ * @return the text payload to be sent with the frame
+ */
+ @Override
+ public String toString() {
+ return getText();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/Frame.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Frame.java
new file mode 100644
index 0000000..7f5ad0f
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Frame.java
@@ -0,0 +1,85 @@
+/*
+ * Frame.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>Frame</code> interface represents a frame as defined in
+ * RFC 6455. A frame is a very lightweight envelope used to send
+ * control information and either text or binary user data. Typically
+ * a frame will represent a single message however, it is possible
+ * to fragment a single frame up in to several frames. A fragmented
+ * frame has a specific <code>FrameType</code> indicating that it
+ * is a continuation frame.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.DataFrame
+ */
+public interface Frame {
+
+ /**
+ * This is used to determine if the frame is the final frame in
+ * a sequence of fragments or a whole frame. If this returns false
+ * then the frame is a continuation from from a sequence of
+ * fragments, otherwise it is a whole frame or the last fragment.
+ *
+ * @return this returns false if the frame is a fragment
+ */
+ boolean isFinal();
+
+ /**
+ * This returns the binary payload that is to be sent with the frame.
+ * It contains no headers or other meta data. If the original data
+ * was text this converts it to UTF-8.
+ *
+ * @return the binary payload to be sent with the frame
+ */
+ byte[] getBinary();
+
+ /**
+ * This returns the text payload that is to be sent with the frame.
+ * It contains no header information or meta data. Caution should
+ * be used with this method as binary payloads will encode to
+ * garbage when decoded as UTF-8.
+ *
+ * @return the text payload to be sent with the frame
+ */
+ String getText();
+
+ /**
+ * This method is used to convert from one frame type to another.
+ * Converting a frame type is useful in scenarios such as when a
+ * ping needs to respond to a pong or when it is more convenient
+ * to send a text frame as binary.
+ *
+ * @param type this is the frame type to convert to
+ *
+ * @return a new frame using the specified frame type
+ */
+ Frame getFrame(FrameType type);
+
+ /**
+ * This is used to determine the type of frame. Interpretation of
+ * this type is outlined in RFC 6455 and can be loosely categorised
+ * as control frames and either data or binary frames.
+ *
+ * @return this returns the type of frame that this represents
+ */
+ FrameType getType();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameChannel.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameChannel.java
new file mode 100644
index 0000000..bcacc43
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameChannel.java
@@ -0,0 +1,117 @@
+/*
+ * FrameChannel.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+import java.io.IOException;
+
+/**
+ * The <code>FrameChannel</code> represents a full duplex communication
+ * channel as defined by RFC 6455. Any instance of this will provide
+ * a means to perform asynchronous writes and reads to a remote client
+ * using a lightweight framing protocol. A frame is a finite length
+ * sequence of bytes that can hold either text or binary data. Also,
+ * control frames are used to perform heartbeat monitoring and closure.
+ * <p>
+ * For convenience frames can be consumed from the socket via a
+ * callback to a registered listener. This avoids having to poll each
+ * socket for data and provides a asynchronous event driven model of
+ * communication, which greatly reduces overhead and complication.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.FrameListener
+ * @see org.simpleframework.http.socket.Frame
+ */
+public interface FrameChannel {
+
+ /**
+ * This is used to send data to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param data this is the data that is to be sent
+ */
+ void send(byte[] data) throws IOException;
+
+ /**
+ * This is used to send text to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param text this is the text that is to be sent
+ */
+ void send(String text) throws IOException;
+
+ /**
+ * This is used to send data to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param frame this is the frame that is to be sent
+ */
+ void send(Frame frame) throws IOException;
+
+ /**
+ * This is used to register a <code>FrameListener</code> to this
+ * instance. The registered listener will receive all user frames
+ * and control frames sent from the client. Also, when the frame
+ * is closed or when an unexpected error occurs the listener is
+ * notified. Any number of listeners can be registered at any time.
+ *
+ * @param listener this is the listener that is to be registered
+ */
+ void register(FrameListener listener) throws IOException;
+
+ /**
+ * This is used to remove a <code>FrameListener</code> from this
+ * instance. After removal the listener will no longer receive
+ * any user frames or control messages from this specific instance.
+ *
+ * @param listener this is the listener to be removed
+ */
+ void remove(FrameListener listener) throws IOException;
+
+ /**
+ * This is used to close the connection with a specific reason.
+ * The close reason will be sent as a control frame before the
+ * TCP connection is terminated.
+ *
+ * @param reason the reason for closing the connection
+ */
+ void close(Reason reason) throws IOException;
+
+ /**
+ * This is used to close the connection without a specific reason.
+ * The close reason will be sent as a control frame before the
+ * TCP connection is terminated.
+ */
+ void close() throws IOException;
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameListener.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameListener.java
new file mode 100644
index 0000000..6892e9c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameListener.java
@@ -0,0 +1,64 @@
+/*
+ * FrameListener.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>FrameListener</code> is used to listen for incoming frames
+ * on a <code>WebSocket</code>. Any number of listeners can listen on
+ * a single web socket and it will receive all incoming events. For
+ * consistency this interface is modelled on the WebSocket API as
+ * defined by W3C Candidate Recommendation as of 20 September 2012.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.FrameChannel
+ */
+public interface FrameListener {
+
+ /**
+ * This is called when a new frame arrives on the WebSocket. It
+ * will receive control frames as well as binary and text user
+ * frames. Control frames should not be acted on or responded
+ * to as they are provided for informational purposes only.
+ *
+ * @param session this is the associated session
+ * @param frame this is the frame that has been received
+ */
+ void onFrame(Session session, Frame frame);
+
+ /**
+ * This is called when an error occurs on the WebSocket. After
+ * an error the connection it is closed with an opcode indicating
+ * an internal server error.
+ *
+ * @param session this is the associated session
+ * @param frame this is the exception that has been thrown
+ */
+ void onError(Session session, Exception cause);
+
+ /**
+ * This is called when the connection is closed from the other
+ * side. Typically a frame with an opcode of close is sent
+ * before the close callback is issued.
+ *
+ * @param session this is the associated session
+ * @param reason this is the reason the connection was closed
+ */
+ void onClose(Session session, Reason reason);
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameType.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameType.java
new file mode 100644
index 0000000..8237701
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/FrameType.java
@@ -0,0 +1,142 @@
+/*
+ * FrameType.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>FrameType</code> represents the set of opcodes defined
+ * in RFC 6455. The base framing protocol uses a opcode to define the
+ * interpretation of the payload data for the frame.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.Frame
+ */
+public enum FrameType {
+
+ /**
+ * A continuation frame identifies a fragment from a larger message.
+ */
+ CONTINUATION(0x00),
+
+ /**
+ * A text frame identifies a message that contains UTF-8 text data.
+ */
+ TEXT(0x01),
+
+ /**
+ * A binary frame identifies a message that contains binary data.
+ */
+ BINARY(0x02),
+
+ /**
+ * A close frame identifies a frame used to terminate a connection.
+ */
+ CLOSE(0x08),
+
+ /**
+ * A ping frame is a heartbeat used to determine connection health.
+ */
+ PING(0x09),
+
+ /**
+ * A pong frame is sent is sent in response to a ping frame.
+ */
+ PONG(0x0a);
+
+ /**
+ * This is the integer value for the opcode.
+ */
+ public final int code;
+
+ /**
+ * Constructor for the <code>Frame</code> type enumeration. This is
+ * given the opcode that is used to identify a specific frame type.
+ *
+ * @param code this is the opcode representing the frame type
+ */
+ private FrameType(int code) {
+ this.code = code;
+ }
+
+ /**
+ * This is used to determine if a frame is a text frame. It can be
+ * useful to know if a frame is a user based frame as it reduces
+ * the need to convert from or to certain character sets.
+ *
+ * @return this returns true if the frame represents a text frame
+ */
+ public boolean isText() {
+ return this == TEXT;
+ }
+
+ /**
+ * This is used to determine if a frame is a close frame. A close
+ * frame contains an optional payload, which if present contains
+ * an error code in network byte order in the first two bytes,
+ * followed by an optional UTF-8 text reason of the closure.
+ *
+ * @return this returns true if the frame represents a close frame
+ */
+ public boolean isClose() {
+ return this == CLOSE;
+ }
+
+ /**
+ * This is used to determine if a frame is a pong frame. A pong
+ * frame is sent in response to a ping and is used to determine if
+ * a WebSocket connection is still active and healthy.
+ *
+ * @return this returns true if the frame represents a pong frame
+ */
+ public boolean isPong() {
+ return this == PONG;
+ }
+
+ /**
+ * This is used to determine if a frame is a ping frame. A ping
+ * frame is sent to check if a WebSocket connection is still healthy.
+ * A connection is determined healthy if it responds with a pong
+ * frame is a reasonable length of time.
+ *
+ * @return this returns true if the frame represents a ping frame
+ */
+ public boolean isPing() {
+ return this == PING;
+ }
+
+ /**
+ * This is used to acquire the frame type given an opcode. If no
+ * frame type can be determined from the opcode provided then this
+ * will return a null value.
+ *
+ * @param octet this is the octet representing the opcode
+ *
+ * @return this returns the frame type from the opcode
+ */
+ public static FrameType resolveType(int octet) {
+ int value = octet & 0xff;
+
+ for(FrameType code : values()) {
+ if(code.code == value) {
+ return code;
+ }
+ }
+ return null;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/Reason.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Reason.java
new file mode 100644
index 0000000..c7438e5
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Reason.java
@@ -0,0 +1,97 @@
+/*
+ * Reason.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>Reason</code> object is used to hold a textual reason
+ * for connection closure and an RFC 6455 defined code. When a
+ * connection is to be closed a control frame with an opcode of
+ * close is sent with the text reason, if one is provided.
+ *
+ * @author Niall Gallagher
+ */
+public class Reason {
+
+ /**
+ * This is the close code to be sent with a control frame.
+ */
+ private final CloseCode code;
+
+ /**
+ * This is the textual description of the close reason.
+ */
+ private final String text;
+
+ /**
+ * Constructor for the <code>Reason</code> object. This is used
+ * to create a reason and a textual description of that reason
+ * to be delivered as a control frame.
+ *
+ * @param code this is the code to be sent with the frame
+ */
+ public Reason(CloseCode code) {
+ this(code, null);
+ }
+
+ /**
+ * Constructor for the <code>Reason</code> object. This is used
+ * to create a reason and a textual description of that reason
+ * to be delivered as a control frame.
+ *
+ * @param code this is the code to be sent with the frame
+ * @param text this is textual description of the close reason
+ */
+ public Reason(CloseCode code, String text) {
+ this.code = code;
+ this.text = text;
+ }
+
+ /**
+ * This is used to get the RFC 6455 code describing the type
+ * of close event. It is the code that should be used by
+ * applications to determine why the connection was terminated.
+ *
+ * @return returns the close code for the connection
+ */
+ public CloseCode getCode() {
+ return code;
+ }
+
+ /**
+ * This is used to get the textual description for the closure.
+ * In many scenarios there will be no textual reason as it is
+ * an optional attribute.
+ *
+ * @return this returns the description for the closure
+ */
+ public String getText() {
+ return text;
+ }
+
+ /**
+ * This is used to provide a textual representation of the reason.
+ * For consistency this will only return the enumerated value for
+ * the close code, or if none exists a "null" text string.
+ *
+ * @return this returns a string representation of the reason
+ */
+ public String toString() {
+ return String.valueOf(code);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/Session.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Session.java
new file mode 100644
index 0000000..7c9a7db
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/Session.java
@@ -0,0 +1,91 @@
+/*
+ * Session.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+import java.util.Map;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+/**
+ * The <code>Session</code> object represents a simple WebSocket session
+ * that contains the connection handshake details and the actual socket.
+ * In order to determine how the session should be interacted with the
+ * protocol is conveniently exposed, however all attributes of the
+ * original HTTP request are available.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.FrameChannel
+ */
+public interface Session {
+
+ /**
+ * 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 of that have been set on the request
+ */
+ Map getAttributes();
+
+ /**
+ * 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
+ */
+ Object getAttribute(Object key);
+
+ /**
+ * Provides a <code>FrameChannel</code> that can be used to communicate
+ * with the connected client. Communication is full duplex and also
+ * asynchronous through the use of a <code>FrameListener</code> that
+ * can be registered with the channel.
+ *
+ * @return a web socket for full duplex communication
+ */
+ FrameChannel getChannel();
+
+ /**
+ * Provides the <code>Request</code> used to initiate the session.
+ * This is useful in establishing the identity of the user, acquiring
+ * an security information and also for determining the request path
+ * that was used, which be used to establish context.
+ *
+ * @return the request used to initiate the session
+ */
+ Request getRequest();
+
+ /**
+ * Provides the <code>Response</code> used to establish the session
+ * with the remote client. This is useful in establishing the protocol
+ * used to create the session and also for determining various other
+ * useful contextual information.
+ *
+ * @return the response used to establish the session
+ */
+ Response getResponse();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/TextData.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/TextData.java
new file mode 100644
index 0000000..24ee97d
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/TextData.java
@@ -0,0 +1,75 @@
+/*
+ * TextData.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket;
+
+/**
+ * The <code>TextData</code> object represents a text payload for
+ * a WebScoket frame. This can be used to send any type of data. If
+ * however it is used to send binary data then it is encoded as UTF-8.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.DataFrame
+ */
+public class TextData implements Data {
+
+ /**
+ * This is used to convert the text payload to a byte array.
+ */
+ private final DataConverter converter;
+
+ /**
+ * This is the text string representing a frame payload.
+ */
+ private final String data;
+
+ /**
+ * Constructor for the <code>TextData</code> object. It requires
+ * an text string that will be sent as UTF-8 within a frame.
+ *
+ * @param data the text string representing the frame payload
+ */
+ public TextData(String data) {
+ this.converter = new DataConverter();
+ this.data = data;
+ }
+
+ /**
+ * This returns the binary payload that is to be sent with a frame.
+ * It contains no headers or other meta data. If the original data
+ * was text this converts it to UTF-8.
+ *
+ * @return the binary payload to be sent with the frame
+ */
+ public byte[] getBinary() {
+ return converter.convert(data);
+ }
+
+ /**
+ * This returns the text payload that is to be sent with a frame.
+ * It contains no header information or meta data. Caution should
+ * be used with this method as binary payloads will encode to
+ * garbage when decoded as UTF-8.
+ *
+ * @return the text payload to be sent with the frame
+ */
+ public String getText() {
+ return data;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/AcceptToken.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/AcceptToken.java
new file mode 100644
index 0000000..2fe2521
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/AcceptToken.java
@@ -0,0 +1,127 @@
+/*
+ * AcceptToken.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_KEY;
+
+import java.io.IOException;
+import java.security.MessageDigest;
+
+import org.simpleframework.common.encode.Base64Encoder;
+import org.simpleframework.http.Request;
+
+/**
+ * The <code>AcceptToken</code> is used to create a unique token based
+ * on a random key sent by the client. This is used to prove that the
+ * handshake was received, the server has to take two pieces of
+ * information and combine them to form a response. The first piece
+ * of information comes from the <code>Sec-WebSocket-Key</code> header
+ * field in the client handshake, the second is the globally unique
+ * identifier <code>258EAFA5-E914-47DA-95CA-C5AB0DC85B11</code>. Both
+ * are concatenated and an SHA-1 has is generated and used in the
+ * session initiating response.
+ *
+ * @author Niall Gallagher
+ */
+class AcceptToken {
+
+ /**
+ * This is the globally unique identifier used in the handshake.
+ */
+ private static final byte[] MAGIC = {
+ '2', '5', '8', 'E', 'A', 'F', 'A', '5', '-',
+ 'E', '9', '1', '4', '-', '4', '7', 'D', 'A',
+ '-', '9', '5', 'C', 'A', '-', 'C', '5', 'A',
+ 'B', '0', 'D', 'C', '8', '5', 'B', '1', '1' };
+
+ /**
+ * This is used to generate the SHA-1 has from the user key.
+ */
+ private final MessageDigest digest;
+
+ /**
+ * This is the original request used to initiate the session.
+ */
+ private final Request request;
+
+ /**
+ * This is the character encoding to decode the key with.
+ */
+ private final String charset;
+
+ /**
+ * Constructor for the <code>AcceptToken</code> object. This is
+ * to create an object that can generate a token from the client
+ * key available from the <code>Sec-WebSocket-Key</code> header.
+ *
+ * @param request this is the session initiating request
+ */
+ public AcceptToken(Request request) throws Exception {
+ this(request, "SHA-1");
+ }
+
+ /**
+ * Constructor for the <code>AcceptToken</code> object. This is
+ * to create an object that can generate a token from the client
+ * key available from the <code>Sec-WebSocket-Key</code> header.
+ *
+ * @param request this is the session initiating request
+ * @param algorithm the algorithm used to create the token
+ */
+ public AcceptToken(Request request, String algorithm) throws Exception {
+ this(request, algorithm, "UTF-8");
+ }
+
+ /**
+ * Constructor for the <code>AcceptToken</code> object. This is
+ * to create an object that can generate a token from the client
+ * key available from the <code>Sec-WebSocket-Key</code> header.
+ *
+ * @param request this is the session initiating request
+ * @param algorithm the algorithm used to create the token
+ * @param charset the encoding used to decode the client key
+ */
+ public AcceptToken(Request request, String algorithm, String charset) throws Exception {
+ this.digest = MessageDigest.getInstance(algorithm);
+ this.request = request;
+ this.charset = charset;
+ }
+
+ /**
+ * This is used to create the required accept token for the session
+ * initiating response. The resulting token is a SHA-1 digest of
+ * the <code>Sec-WebSocket-Key</code> a globally unique identifier
+ * defined in RFC 6455 all encoded in base64.
+ *
+ * @return the accept token for the session initiating response
+ */
+ public String create() throws IOException {
+ String value = request.getValue(SEC_WEBSOCKET_KEY);
+ byte[] data = value.getBytes(charset);
+
+ if (data.length > 0) {
+ digest.update(data);
+ digest.update(MAGIC);
+ }
+ byte[] digested = digest.digest();
+ char[] text = Base64Encoder.encode(digested);
+
+ return new String(text);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/DirectRouter.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/DirectRouter.java
new file mode 100644
index 0000000..0c09063
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/DirectRouter.java
@@ -0,0 +1,107 @@
+/*
+ * DirectRouter.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_PROTOCOL;
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_VERSION;
+import static org.simpleframework.http.Protocol.UPGRADE;
+import static org.simpleframework.http.Protocol.WEBSOCKET;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+/**
+ * The <code>DirectRouter</code> object is used to create a router
+ * that uses a single service. Typically this is used by simpler
+ * servers that wish to expose a single sub-protocol to clients.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.RouterContainer
+ */
+public class DirectRouter implements Router {
+
+ /**
+ * The service used by this router instance.
+ */
+ private final Service service;
+
+ /**
+ * The protocol used or null if none was specified.
+ */
+ private final String protocol;
+
+ /**
+ * Constructor for the <code>DirectRouter</code> object. This
+ * is used to create an object that will select a single service.
+ * Creating an instance with this constructor means that the
+ * protocol header will not be set.
+ *
+ * @param service this is the service used by this instance
+ * @param protocol the protocol used by this router or null
+ */
+ public DirectRouter(Service service) {
+ this(service, null);
+ }
+
+ /**
+ * Constructor for the <code>DirectRouter</code> object. This
+ * is used to create an object that will select a single service.
+ * If the protocol specified is null then the response to the
+ * session initiation will contain null for the protocol header.
+ *
+ * @param service this is the service used by this instance
+ * @param protocol the protocol used by this router or null
+ */
+ public DirectRouter(Service service, String protocol) {
+ this.protocol = protocol;
+ this.service = service;
+ }
+
+ /**
+ * This is used to route an incoming request to a service if
+ * the request represents a WebSocket handshake as defined by
+ * RFC 6455. If the request is not a session initiating handshake
+ * then this will return a null value to allow it to be processed
+ * by some other part of the server.
+ *
+ * @param request this is the request to use for routing
+ * @param response this is the response to establish the session
+ *
+ * @return a service that can be used to process the session
+ */
+ public Service route(Request request, Response response) {
+ String token = request.getValue(UPGRADE);
+
+ if(token != null) {
+ if(token.equalsIgnoreCase(WEBSOCKET)) {
+ String version = request.getValue(SEC_WEBSOCKET_VERSION);
+
+ if(version != null) {
+ response.setValue(SEC_WEBSOCKET_VERSION, version);
+ }
+ if(protocol != null) {
+ response.setValue(SEC_WEBSOCKET_PROTOCOL, protocol);
+ }
+ return service;
+ }
+ }
+ return null;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameBuilder.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameBuilder.java
new file mode 100644
index 0000000..6ab224a
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameBuilder.java
@@ -0,0 +1,118 @@
+/*
+ * FrameBuilder.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.util.Arrays;
+
+import org.simpleframework.http.socket.DataConverter;
+import org.simpleframework.http.socket.DataFrame;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameType;
+
+/**
+ * The <code>FrameBuilder</code> object is used to create an object
+ * that interprets a frame header to produce frame objects. For
+ * efficiency this converts binary data to the native frame data
+ * type, which avoids memory churn.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.FrameConsumer
+ */
+class FrameBuilder {
+
+ /**
+ * This converts binary data to a UTF-8 string for text frames.
+ */
+ private final DataConverter converter;
+
+ /**
+ * This is used to determine the type of frames to create.
+ */
+ private final FrameHeader header;
+
+ /**
+ * Constructor for the <code>FrameBuilder</code> object. This acts
+ * as a factory for frame objects by using the provided header to
+ * determine the frame type to be created.
+ *
+ * @param header the header used to determine the frame type
+ */
+ public FrameBuilder(FrameHeader header) {
+ this.converter = new DataConverter();
+ this.header = header;
+ }
+
+ /**
+ * This is used to create a frame object to represent the data that
+ * has been consumed. The frame created will contain either a copy of
+ * the provided byte buffer or a text string encoded in UTF-8. To
+ * avoid memory churn this method should be used sparingly.
+ *
+ * @return this returns a frame created from the consumed bytes
+ */
+ public Frame create(byte[] data, int count) {
+ FrameType type = header.getType();
+
+ if(type.isText()) {
+ return createText(data, count);
+ }
+ return createBinary(data, count);
+ }
+
+ /**
+ * This is used to create a frame object from the provided data.
+ * The resulting frame will contain a UTF-8 encoding of the data
+ * to ensure that data conversion needs to be performed only once.
+ *
+ * @param data this is the data to convert to a new frame
+ * @param count this is the number of bytes in the frame
+ *
+ * @return a new frame containing the text
+ */
+ private Frame createText(byte[] data, int count) {
+ FrameType type = header.getType();
+ String text = converter.convert(data, 0, count);
+
+ if(header.isFinal()) {
+ return new DataFrame(type, text, true);
+ }
+ return new DataFrame(type, text, false);
+ }
+
+ /**
+ * This is used to create a frame object from the provided data.
+ * The resulting frame will contain a copy of the data to ensure
+ * that the frame is immutable.
+ *
+ * @param data this is the data to convert to a new frame
+ * @param count this is the number of bytes in the frame
+ *
+ * @return a new frame containing a copy of the provided data
+ */
+ private Frame createBinary(byte[] data, int count) {
+ FrameType type = header.getType();
+ byte[] copy = Arrays.copyOf(data, count);
+
+ if(header.isFinal()) {
+ return new DataFrame(type, copy, true);
+ }
+ return new DataFrame(type, copy, false);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameCollector.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameCollector.java
new file mode 100644
index 0000000..8987620
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameCollector.java
@@ -0,0 +1,179 @@
+/*
+ * FrameCollector.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.socket.service.ServiceEvent.ERROR;
+
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.reactor.Operation;
+import org.simpleframework.transport.reactor.Reactor;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>FrameCollector</code> operation is used to collect frames
+ * from a channel and dispatch them to a <code>FrameListener</code>.
+ * To ensure that stale connections do not linger any connection that
+ * does not send a control ping or pong frame within two minutes will
+ * be terminated and the close control frame will be sent.
+ *
+ * @author Niall Gallagher
+ */
+class FrameCollector implements Operation {
+
+ /**
+ * This decodes the frame bytes from the channel and processes it.
+ */
+ private final FrameProcessor processor;
+
+ /**
+ * This is the cursor used to maintain a stream seek position.
+ */
+ private final ByteCursor cursor;
+
+ /**
+ * This is the underlying channel for this frame collector.
+ */
+ private final Channel channel;
+
+ /**
+ * This is the reactor used to schedule this operation for reads.
+ */
+ private final Reactor reactor;
+
+ /**
+ * This is the tracer that is used to trace the frame collection.
+ */
+ private final Trace trace;
+
+ /**
+ * Constructor for the <code>FrameCollector</code> object. This is
+ * used to create a collector that will process and dispatch web
+ * socket frames as defined by RFC 6455.
+ *
+ * @param encoder this is the encoder used to send messages
+ * @param session this is the web socket session
+ * @param channel this is the underlying TCP communication channel
+ * @param reactor this is the reactor used for read notifications
+ */
+ public FrameCollector(FrameEncoder encoder, Session session, Request request, Reactor reactor) {
+ this.processor = new FrameProcessor(encoder, session, request);
+ this.channel = request.getChannel();
+ this.cursor = channel.getCursor();
+ this.trace = channel.getTrace();
+ this.reactor = reactor;
+ }
+
+ /**
+ * 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 channel associated with this collector. This is used
+ * to register for notification of read events. If at any time the
+ * remote endpoint is closed then this will cause the collector
+ * to perform a final execution before closing.
+ *
+ * @return this returns the selectable TCP channel
+ */
+ public SelectableChannel getChannel() {
+ return channel.getSocket();
+ }
+
+ /**
+ * This is used to register a <code>FrameListener</code> to this
+ * instance. The registered listener will receive all user frames
+ * and control frames sent from the client. Also, when the frame
+ * is closed or when an unexpected error occurs the listener is
+ * notified. Any number of listeners can be registered at any time.
+ *
+ * @param listener this is the listener that is to be registered
+ */
+ public void register(FrameListener listener) {
+ processor.register(listener);
+ }
+
+ /**
+ * This is used to remove a <code>FrameListener</code> from this
+ * instance. After removal the listener will no longer receive
+ * any user frames or control messages from this specific instance.
+ *
+ * @param listener this is the listener to be removed
+ */
+ public void remove(FrameListener listener) {
+ processor.remove(listener);
+ }
+
+ /**
+ * This is used to execute the collection operation. Collection is
+ * done by reading the frame header from the incoming data, once
+ * consumed the remainder of the frame is collected until such
+ * time as it has been fully consumed. When consumed it will be
+ * dispatched to the registered frame listeners.
+ */
+ public void run() {
+ try {
+ processor.process();
+
+ if(cursor.isOpen()) {
+ reactor.process(this, SelectionKey.OP_READ);
+ } else {
+ processor.close();
+ }
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+
+ try {
+ processor.failure(cause);
+ } catch(Exception fatal) {
+ trace.trace(ERROR, fatal);
+ } finally {
+ channel.close();
+ }
+ }
+ }
+
+ /**
+ * This is called when a read operation has timed out. To ensure
+ * that stale channels do not remain registered they are cleared
+ * out with this method and a close frame is sent if possible.
+ */
+ public void cancel() {
+ try{
+ processor.close();
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConnection.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConnection.java
new file mode 100644
index 0000000..b904130
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConnection.java
@@ -0,0 +1,214 @@
+/*
+ * FrameConnection.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.socket.CloseCode.NORMAL_CLOSURE;
+import static org.simpleframework.http.socket.service.ServiceEvent.OPEN_SOCKET;
+
+import java.io.IOException;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.http.socket.FrameChannel;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.reactor.Reactor;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>FrameConnection</code> represents a connection that can
+ * send and receivd WebSocket frames. Any instance of this will provide
+ * a means to perform asynchronous writes and reads to a remote client
+ * using a lightweight framing protocol. A frame is a finite length
+ * sequence of bytes that can hold either text or binary data. Also,
+ * control frames are used to perform heartbeat monitoring and closure.
+ * <p>
+ * For convenience frames can be consumed from the socket via a
+ * callback to a registered listener. This avoids having to poll each
+ * socket for data and provides a asynchronous event driven model of
+ * communication, which greatly reduces overhead and complication.
+ *
+ * @author Niall Gallagher
+ */
+class FrameConnection implements FrameChannel {
+
+ /**
+ * The collector is used to collect frames from the TCP channel.
+ */
+ private final FrameCollector operation;
+
+ /**
+ * This encoder is used to encode data as RFC 6455 frames.
+ */
+ private final FrameEncoder encoder;
+
+ /**
+ * This is the sender used to send frames over the channel.
+ */
+ private final ByteWriter writer;
+
+ /**
+ * This is the session object that has a synchronized channel.
+ */
+ private final Session session;
+
+ /**
+ * This is the underlying TCP channel that frames are sent over.
+ */
+ private final Channel channel;
+
+ /**
+ * The reason that is sent if at any time the channel is closed.
+ */
+ private final Reason reason;
+
+ /**
+ * This is used to trace all events that occur on the channel.
+ */
+ private final Trace trace;
+
+ /**
+ * Constructor for the <code>FrameConnection</code> object. This is used
+ * to create a channel that can read and write frames over a TCP
+ * channel. For asynchronous read and dispatch operations this will
+ * produce an operation to collect and process RFC 6455 frames.
+ *
+ * @param request this is the initiating request for the WebSocket
+ * @param response this is the initiating response for the WebSocket
+ * @param reactor this is the reactor used to process frames
+ */
+ public FrameConnection(Request request, Response response, Reactor reactor) {
+ this.encoder = new FrameEncoder(request);
+ this.session = new ServiceSession(this, request, response);
+ this.operation = new FrameCollector(encoder, session, request, reactor);
+ this.reason = new Reason(NORMAL_CLOSURE);
+ this.channel = request.getChannel();
+ this.writer = channel.getWriter();
+ this.trace = channel.getTrace();
+ }
+
+ /**
+ * This is used to open the channel and begin consuming frames. This
+ * will also return the session that contains the details for the
+ * created WebSocket such as the initiating request and response as
+ * well as the <code>FrameChannel</code> object.
+ *
+ * @return the session associated with the WebSocket
+ */
+ public Session open() throws IOException {
+ trace.trace(OPEN_SOCKET);
+ operation.run();
+ return session;
+ }
+
+ /**
+ * This is used to register a <code>FrameListener</code> to this
+ * instance. The registered listener will receive all user frames
+ * and control frames sent from the client. Also, when the frame
+ * is closed or when an unexpected error occurs the listener is
+ * notified. Any number of listeners can be registered at any time.
+ *
+ * @param listener this is the listener that is to be registered
+ */
+ public void register(FrameListener listener) throws IOException {
+ operation.register(listener);
+ }
+
+ /**
+ * This is used to remove a <code>FrameListener</code> from this
+ * instance. After removal the listener will no longer receive
+ * any user frames or control messages from this specific instance.
+ *
+ * @param listener this is the listener to be removed
+ */
+ public void remove(FrameListener listener) throws IOException {
+ operation.remove(listener);
+ }
+
+ /**
+ * This is used to send data to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param data this is the data that is to be sent
+ */
+ public void send(byte[] data) throws IOException {
+ encoder.encode(data);
+ }
+
+ /**
+ * This is used to send text to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param text this is the text that is to be sent
+ */
+ public void send(String text) throws IOException {
+ encoder.encode(text);
+ }
+
+ /**
+ * This is used to send data to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param frame this is the frame that is to be sent
+ */
+ public void send(Frame frame) throws IOException {
+ encoder.encode(frame);
+ }
+
+ /**
+ * This is used to close the connection with a specific reason.
+ * The close reason will be sent as a control frame before the
+ * TCP connection is terminated.
+ *
+ * @param reason the reason for closing the connection
+ */
+ public void close(Reason reason) throws IOException {
+ encoder.encode(reason);
+ writer.close();
+ }
+
+ /**
+ * This is used to close the connection without a specific reason.
+ * The close reason will be sent as a control frame before the
+ * TCP connection is terminated.
+ */
+ public void close() throws IOException {
+ encoder.encode(reason);
+ writer.close();
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConsumer.java
new file mode 100644
index 0000000..579d6ef
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameConsumer.java
@@ -0,0 +1,162 @@
+/*
+ * FrameConsumer.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.io.IOException;
+
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>FrameConsumer</code> object is used to read a WebSocket
+ * frame as defined by RFC 6455. This is a state machine that can read
+ * the data one byte at a time until the entire frame has been consumed.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.FrameCollector
+ */
+class FrameConsumer {
+
+ /**
+ * This is used to consume the header part of the frame.
+ */
+ private FrameHeaderConsumer header;
+
+ /**
+ * This is used to interpret the header and create a frame.
+ */
+ private FrameBuilder builder;
+
+ /**
+ * This is used to buffer the bytes that form the frame.
+ */
+ private byte[] buffer;
+
+ /**
+ * This is a count of the payload bytes currently consumed.
+ */
+ private int count;
+
+ /**
+ * Constructor for the <code>FrameConsumer</code> object. This is
+ * used to create a consumer to read the bytes that form the frame
+ * from an underlying TCP connection. Internally a buffer is created
+ * to allow bytes to be consumed and collected in chunks.
+ */
+ public FrameConsumer() {
+ this.header = new FrameHeaderConsumer();
+ this.builder = new FrameBuilder(header);
+ this.buffer = new byte[2048];
+ }
+
+ /**
+ * This is used to determine the type of frame. Interpretation of
+ * this type is outlined in RFC 6455 and can be loosely categorised
+ * as control frames and either data or binary frames.
+ *
+ * @return this returns the type of frame that this represents
+ */
+ public FrameType getType() {
+ return header.getType();
+ }
+
+ /**
+ * This is used to create a frame object to represent the data that
+ * has been consumed. The frame created will make a copy of the
+ * internal byte buffer so this method should be used sparingly.
+ *
+ * @return this returns a frame created from the consumed bytes
+ */
+ public Frame getFrame() {
+ return builder.create(buffer, count);
+ }
+
+ /**
+ * This consumes frame bytes using the provided cursor. The consumer
+ * acts as a state machine by consuming the data as that data
+ * becomes available, this allows it to consume data asynchronously
+ * and dispatch once the whole frame has been consumed.
+ *
+ * @param cursor the cursor to consume the frame data from
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ while (cursor.isReady()) {
+ if(!header.isFinished()) {
+ header.consume(cursor);
+ }
+ if(header.isFinished()) {
+ int length = header.getLength();
+
+ if(count <= length) {
+ if(buffer.length < length) {
+ buffer = new byte[length];
+ }
+ if(count < length) {
+ int size = cursor.read(buffer, count, length - count);
+
+ if(size == -1) {
+ throw new IOException("Could only read " + count + " of length " + length);
+ }
+ count += size;
+ }
+ if(count == length) {
+ if(header.isMasked()) {
+ byte[] mask = header.getMask();
+
+ for (int i = 0; i < count; i++) {
+ buffer[i] ^= mask[i % 4];
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This is used to determine if the collector has finished. If it
+ * is not finished the collector will be registered to listen for
+ * an I/O interrupt to read further bytes of the frame.
+ *
+ * @return true if the collector has finished consuming
+ */
+ public boolean isFinished() {
+ if(header.isFinished()) {
+ int length = header.getLength();
+
+ if(count == length) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This resets the collector to its original state so that it can
+ * be reused. Reusing the collector has obvious benefits as it will
+ * reduce the amount of memory churn for the server.
+ */
+ public void clear() {
+ header.clear();
+ count = 0;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameEncoder.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameEncoder.java
new file mode 100644
index 0000000..1a99d30
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameEncoder.java
@@ -0,0 +1,229 @@
+/*
+ * FrameEncoder.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.socket.FrameType.BINARY;
+import static org.simpleframework.http.socket.FrameType.CLOSE;
+import static org.simpleframework.http.socket.FrameType.TEXT;
+import static org.simpleframework.http.socket.service.ServiceEvent.WRITE_FRAME;
+
+import java.io.IOException;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.socket.CloseCode;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>FrameEncoder</code> is used to encode data as frames as
+ * defined by RFC 6455. This can encode binary, and text frames as
+ * well as control frames. All frames generated are written to the
+ * underlying channel but are not flushed so that multiple frames
+ * can be buffered before the final flush is made.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.FrameConnection
+ */
+class FrameEncoder {
+
+ /**
+ * This is the underlying sender used to send the frames.
+ */
+ private final OutputBarrier barrier;
+
+ /**
+ * This is the TCP channel the frames are delivered over.
+ */
+ private final Channel channel;
+
+ /**
+ * This is used to trace the traffic on the channel.
+ */
+ private final Trace trace;
+
+ /**
+ * This is the charset used to encode the text frames with.
+ */
+ private final String charset;
+
+ /**
+ * Constructor for the <code>FrameEncoder</code> object. This is
+ * used to create an encoder to sending frames over the provided
+ * channel. Frames send remain unflushed so they can be batched
+ * on a single output buffer.
+ *
+ * @param request contains the opening handshake information
+ */
+ public FrameEncoder(Request request) {
+ this(request, "UTF-8");
+ }
+
+ /**
+ * Constructor for the <code>FrameEncoder</code> object. This is
+ * used to create an encoder to sending frames over the provided
+ * channel. Frames send remain unflushed so they can be batched
+ * on a single output buffer.
+ *
+ * @param request contains the opening handshake information
+ * @param charset this is the character encoding to encode with
+ */
+ public FrameEncoder(Request request, String charset) {
+ this.barrier = new OutputBarrier(request, 5000);
+ this.channel = request.getChannel();
+ this.trace = channel.getTrace();
+ this.charset = charset;
+ }
+
+ /**
+ * This is used to encode the provided data as a WebSocket frame as
+ * of RFC 6455. The encoded data is written to the underlying socket
+ * and the number of bytes generated is returned.
+ *
+ * @param text this is the data used to encode the frame
+ *
+ * @return the size of the generated frame including the header
+ */
+ public int encode(String text) throws IOException {
+ byte[] data = text.getBytes(charset);
+ return encode(TEXT, data, true);
+ }
+
+ /**
+ * This is used to encode the provided data as a WebSocket frame as
+ * of RFC 6455. The encoded data is written to the underlying socket
+ * and the number of bytes generated is returned.
+ *
+ * @param data this is the data used to encode the frame
+ *
+ * @return the size of the generated frame including the header
+ */
+ public int encode(byte[] data) throws IOException {
+ return encode(BINARY, data, true);
+ }
+
+ /**
+ * This is used to encode the provided data as a WebSocket frame as
+ * of RFC 6455. The encoded data is written to the underlying socket
+ * and the number of bytes generated is returned. A close frame with
+ * a reason is similar to a text frame with the exception that the
+ * first two bytes of the frame payload contains the close code as
+ * a two byte integer in network byte order. The body of the close
+ * frame may contain UTF-8 encoded data with a reason, the
+ * interpretation of which is not defined by RFC 6455.
+ *
+ * @param reason this is the data used to encode the frame
+ *
+ * @return the size of the generated frame including the header
+ */
+ public int encode(Reason reason) throws IOException {
+ CloseCode code = reason.getCode();
+ String text = reason.getText();
+ byte[] header = code.getData();
+
+ if(text != null) {
+ byte[] data = text.getBytes(charset);
+ byte[] message = new byte[data.length + 2];
+
+ message[0] = header[0];
+ message[1] = header[1];
+
+ for(int i = 0; i < data.length; i++) {
+ message[i + 2] = data[i];
+ }
+ return encode(CLOSE, message, true);
+ }
+ return encode(CLOSE, header, true);
+ }
+
+ /**
+ * This is used to encode the provided frame as a WebSocket frame as
+ * of RFC 6455. The encoded data is written to the underlying socket
+ * and the number of bytes generated is returned.
+ *
+ * @param frame this is frame that is to be send over the channel
+ *
+ * @return the size of the generated frame including the header
+ */
+ public int encode(Frame frame) throws IOException {
+ FrameType code = frame.getType();
+ byte[] data = frame.getBinary();
+ boolean last = frame.isFinal();
+
+ return encode(code, data, last);
+ }
+
+ /**
+ * This is used to encode the provided frame as a WebSocket frame as
+ * of RFC 6455. The encoded data is written to the underlying socket
+ * and the number of bytes generated is returned.
+ *
+ * @param type this is the type of frame that is to be encoded
+ * @param data this is the data used to create the frame
+ * @param last determines if the is the last frame in a sequence
+ *
+ * @return the size of the generated frame including the header
+ */
+ private int encode(FrameType type, byte[] data, boolean last) throws IOException {
+ byte[] header = new byte[10];
+ long length = data.length;
+ int count = 0;
+
+ if (last) {
+ header[0] |= 1 << 7;
+ }
+ header[0] |= type.code % 128;
+
+ if (length <= 125) {
+ header[1] = (byte) length;
+ count = 2;
+ } else if (length >= 126 && length <= 65535) {
+ header[1] = (byte) 126;
+ header[2] = (byte) ((length >>> 8) & 0xff);
+ header[3] = (byte) (length & 0xff);
+ count = 4;
+ } else {
+ header[1] = (byte) 127;
+ header[2] = (byte) ((length >>> 56) & 0xff);
+ header[3] = (byte) ((length >>> 48) & 0xff);
+ header[4] = (byte) ((length >>> 40) & 0xff);
+ header[5] = (byte) ((length >>> 32) & 0xff);
+ header[6] = (byte) ((length >>> 24) & 0xff);
+ header[7] = (byte) ((length >>> 16) & 0xff);
+ header[8] = (byte) ((length >>> 8) & 0xff);
+ header[9] = (byte) (length & 0xff);
+ count = 10;
+ }
+ byte[] reply = new byte[count + data.length];
+
+ for (int i = 0; i < count; i++) {
+ reply[i] = header[i];
+ }
+ for (int i = 0; i < length; i++) {
+ reply[i + count] = data[i];
+ }
+ trace.trace(WRITE_FRAME, type);
+ barrier.send(reply);
+
+ return reply.length;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeader.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeader.java
new file mode 100644
index 0000000..a246451
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeader.java
@@ -0,0 +1,80 @@
+/*
+ * FrameHeader.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import org.simpleframework.http.socket.FrameType;
+
+/**
+ * The <code>FrameHeader</code> represents the variable length header
+ * used for a WebSocket frame. It is used to determine the number of
+ * bytes that need to be consumed to successfully process a frame
+ * from the connected client.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.FrameConsumer
+ */
+interface FrameHeader {
+
+ /**
+ * This is used to determine the type of frame. Interpretation of
+ * this type is outlined in RFC 6455 and can be loosely categorised
+ * as control frames and either data or binary frames.
+ *
+ * @return this returns the type of frame that this represents
+ */
+ FrameType getType();
+
+ /**
+ * This provides the client mask send with the request. The mask is
+ * a 32 bit value that is used as an XOR bitmask of the client
+ * payload. Masking applies only in the client to server direction.
+ *
+ * @return this returns the 32 bit mask used for this frame
+ */
+ byte[] getMask();
+
+ /**
+ * This provides the length of the payload within the frame. It
+ * is used to determine how much data to consume from the underlying
+ * TCP stream in order to recreate the frame to dispatch.
+ *
+ * @return the number of bytes used in the frame
+ */
+ int getLength();
+
+ /**
+ * This is used to determine if the frame is masked. All client
+ * frames should be masked according to RFC 6455. If masked the
+ * payload will have its contents bitmasked with a 32 bit value.
+ *
+ * @return this returns true if the payload has been masked
+ */
+ boolean isMasked();
+
+ /**
+ * This is used to determine if the frame is the final frame in
+ * a sequence of fragments or a whole frame. If this returns false
+ * then the frame is a continuation from from a sequence of
+ * fragments, otherwise it is a whole frame or the last fragment.
+ *
+ * @return this returns false if the frame is a fragment
+ */
+ boolean isFinal();
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeaderConsumer.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeaderConsumer.java
new file mode 100644
index 0000000..d651ea9
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameHeaderConsumer.java
@@ -0,0 +1,235 @@
+/*
+ * FrameHeaderConsumer.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.io.IOException;
+
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.transport.ByteCursor;
+
+/**
+ * The <code>FrameHeaderConsumer</code> is used to consume frames from
+ * a connected TCP channel. This is a state machine that can consume
+ * the data one byte at a time until the entire header has been consumed.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.FrameConsumer
+ */
+class FrameHeaderConsumer implements FrameHeader {
+
+ /**
+ * This is the frame type which represents the opcode.
+ */
+ private FrameType type;
+
+ /**
+ * If header consumed was from a client frame the data is masked.
+ */
+ private boolean masked;
+
+ /**
+ * Determines if this frame is part of a larger sequence.
+ */
+ private boolean last;
+
+ /**
+ * This is the mask that is used to obfuscate client frames.
+ */
+ private byte[] mask;
+
+ /**
+ * This is the octet that is used to read one byte at a time.
+ */
+ private byte[] octet;
+
+ /**
+ * Required number of bytes within the frame header.
+ */
+ private int required;
+
+ /**
+ * This represents the length of the frame payload.
+ */
+ private int length;
+
+ /**
+ * This determines the count of the mask bytes read.
+ */
+ private int count;
+
+ /**
+ * Constructor for the <code>FrameHeaderConsumer</code> object. This
+ * is used to create a consumer to read the bytes that form the
+ * frame header from an underlying TCP connection.
+ */
+ public FrameHeaderConsumer() {
+ this.octet = new byte[1];
+ this.mask = new byte[4];
+ this.length = -1;
+ }
+
+ /**
+ * This provides the length of the payload within the frame. It
+ * is used to determine how much data to consume from the underlying
+ * TCP stream in order to recreate the frame to dispatch.
+ *
+ * @return the number of bytes used in the frame
+ */
+ public int getLength() {
+ return length;
+ }
+
+ /**
+ * This provides the client mask send with the request. The mask is
+ * a 32 bit value that is used as an XOR bitmask of the client
+ * payload. Masking applies only in the client to server direction.
+ *
+ * @return this returns the 32 bit mask used for this frame
+ */
+ public byte[] getMask() {
+ return mask;
+ }
+
+ /**
+ * This is used to determine the type of frame. Interpretation of
+ * this type is outlined in RFC 6455 and can be loosely categorised
+ * as control frames and either data or binary frames.
+ *
+ * @return this returns the type of frame that this represents
+ */
+ public FrameType getType() {
+ return type;
+ }
+
+ /**
+ * This is used to determine if the frame is masked. All client
+ * frames should be masked according to RFC 6455. If masked the
+ * payload will have its contents bitmasked with a 32 bit value.
+ *
+ * @return this returns true if the payload has been masked
+ */
+ public boolean isMasked() {
+ return masked;
+ }
+
+ /**
+ * This is used to determine if the frame is the final frame in
+ * a sequence of fragments or a whole frame. If this returns false
+ * then the frame is a continuation from from a sequence of
+ * fragments, otherwise it is a whole frame or the last fragment.
+ *
+ * @return this returns false if the frame is a fragment
+ */
+ public boolean isFinal() {
+ return last;
+ }
+
+ /**
+ * This consumes frame bytes using the provided cursor. The consumer
+ * acts as a state machine by consuming the data as that data
+ * becomes available, this allows it to consume data asynchronously
+ * and dispatch once the whole frame has been consumed.
+ *
+ * @param cursor the cursor to consume the frame data from
+ */
+ public void consume(ByteCursor cursor) throws IOException {
+ if (cursor.isReady()) {
+ if (type == null) {
+ int count = cursor.read(octet);
+
+ if (count <= 0) {
+ throw new IOException("Ready cursor produced no data");
+ }
+ type = FrameType.resolveType(octet[0] & 0x0f);
+
+ if(type == null) {
+ throw new IOException("Frame type code not supported");
+ }
+ last = (octet[0] & 0x80) != 0;
+ } else {
+ if (length < 0) {
+ int count = cursor.read(octet);
+
+ if (count <= 0) {
+ throw new IOException("Ready cursor produced no data");
+ }
+ masked = (octet[0] & 0x80) != 0;
+ length = (octet[0] & 0x7F);
+
+ if (length == 0x7F) { // 8 byte extended payload length
+ required = 8;
+ length = 0;
+ } else if (length == 0x7E) { // 2 bytes extended payload length
+ required = 2;
+ length = 0;
+ }
+ } else if (required > 0) {
+ int count = cursor.read(octet);
+
+ if (count == -1) {
+ throw new IOException("Could not read length");
+ }
+ length |= (octet[0] & 0xFF) << (8 * --required);
+ } else {
+ if (masked && count < mask.length) {
+ int size = cursor.read(mask, count, mask.length - count);
+
+ if (size == -1) {
+ throw new IOException("Could not read mask");
+ }
+ count += size;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This is used to determine if the collector has finished. If it
+ * is not finished the collector will be registered to listen for
+ * an I/O intrrupt to read further bytes of the frame.
+ *
+ * @return true if the collector has finished consuming
+ */
+ public boolean isFinished() {
+ if(type != null) {
+ if(length >= 0 && required == 0) {
+ if(masked) {
+ return count == mask.length;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This resets the collector to its original state so that it can
+ * be reused. Reusing the collector has obvious benefits as it will
+ * reduce the amount of memory churn for the server.
+ */
+ public void clear() {
+ type = null;
+ length = -1;
+ required = 0;
+ masked = false;
+ count = 0;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameProcessor.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameProcessor.java
new file mode 100644
index 0000000..c7528d4
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/FrameProcessor.java
@@ -0,0 +1,255 @@
+/*
+ * FrameProcessor.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.socket.CloseCode.NORMAL_CLOSURE;
+import static org.simpleframework.http.socket.service.ServiceEvent.ERROR;
+import static org.simpleframework.http.socket.service.ServiceEvent.READ_FRAME;
+import static org.simpleframework.http.socket.service.ServiceEvent.READ_PING;
+import static org.simpleframework.http.socket.service.ServiceEvent.READ_PONG;
+import static org.simpleframework.http.socket.service.ServiceEvent.WRITE_PONG;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteCursor;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>FrameProcessor</code> object is used to process incoming
+ * data and dispatch that data as WebSocket frames. Dispatching of the
+ * frames is done by making a callback to <code>FrameListener</code>
+ * objects registered. In addition to frames this will also notify of
+ * any errors that occur or on connection closure.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.FrameConsumer
+ */
+class FrameProcessor {
+
+ /**
+ * This is the set of listeners to dispatch frames to.
+ */
+ private final Set<FrameListener> listeners;
+
+ /**
+ * This is used to extract the reason description from a frame.
+ */
+ private final ReasonExtractor extractor;
+
+ /**
+ * This is used to consume the frames from the underling channel.
+ */
+ private final FrameConsumer consumer;
+
+ /**
+ * This is the encoder that is used to send control messages.
+ */
+ private final FrameEncoder encoder;
+
+ /**
+ * This is used to determine if a close notification was sent.
+ */
+ private final AtomicBoolean closed;
+
+ /**
+ * This is the cursor used to maintain a read seek position.
+ */
+ private final ByteCursor cursor;
+
+ /**
+ * This is the session associated with the WebSocket connection.
+ */
+ private final Session session;
+
+ /**
+ * This is the underlying TCP channel this reads frames from.
+ */
+ private final Channel channel;
+
+ /**
+ * This is the reason message used for a normal closure.
+ */
+ private final Reason normal;
+
+ /**
+ * This is used to trace the events that occur on the channel.
+ */
+ private final Trace trace;
+
+ /**
+ * Constructor for the <code>FrameProcessor</code> object. This is
+ * used to create a processor that can consume and dispatch frames
+ * as defined by RFC 6455 to a set of registered listeners.
+ *
+ * @param encoder this is the encoder used to send control frames
+ * @param session this is the session associated with the channel
+ * @param channel this is the channel to read frames from
+ */
+ public FrameProcessor(FrameEncoder encoder, Session session, Request request) {
+ this.listeners = new CopyOnWriteArraySet<FrameListener>();
+ this.normal = new Reason(NORMAL_CLOSURE);
+ this.extractor = new ReasonExtractor();
+ this.consumer = new FrameConsumer();
+ this.closed = new AtomicBoolean();
+ this.channel = request.getChannel();
+ this.cursor = channel.getCursor();
+ this.trace = channel.getTrace();
+ this.encoder = encoder;
+ this.session = session;
+ }
+
+ /**
+ * This is used to register a <code>FrameListener</code> to this
+ * instance. The registered listener will receive all user frames
+ * and control frames sent from the client. Also, when the frame
+ * is closed or when an unexpected error occurs the listener is
+ * notified. Any number of listeners can be registered at any time.
+ *
+ * @param listener this is the listener that is to be registered
+ */
+ public void register(FrameListener listener) {
+ listeners.add(listener);
+ }
+
+ /**
+ * This is used to remove a <code>FrameListener</code> from this
+ * instance. After removal the listener will no longer receive
+ * any user frames or control messages from this specific instance.
+ *
+ * @param listener this is the listener to be removed
+ */
+ public void remove(FrameListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * This is used to process frames consumed from the underlying TCP
+ * connection. It will respond to control frames such as pings and
+ * will also handle close frames. Each frame, regardless of its
+ * type will be dispatched to any <code>FrameListener</code> objects
+ * that are registered with the processor. If an a close frame is
+ * received it will echo that close frame, with the same close code
+ * and back to the sender as suggested by RFC 6455 section 5.5.1.
+ */
+ public void process() throws IOException {
+ if(cursor.isReady()) {
+ consumer.consume(cursor);
+
+ if(consumer.isFinished()) {
+ Frame frame = consumer.getFrame();
+ FrameType type = frame.getType();
+
+ trace.trace(READ_FRAME, type);
+
+ if(type.isPong()) {
+ trace.trace(READ_PONG);
+ }
+ if(type.isPing()){
+ Frame response = frame.getFrame(FrameType.PONG);
+
+ trace.trace(READ_PING);
+ encoder.encode(response);
+ trace.trace(WRITE_PONG);
+ }
+ for(FrameListener listener : listeners) {
+ listener.onFrame(session, frame);
+ }
+ if(type.isClose()){
+ Reason reason = extractor.extract(frame);
+
+ if(reason != null) {
+ close(reason);
+ } else {
+ close();
+ }
+ }
+ consumer.clear();
+ }
+ }
+ }
+
+ /**
+ * This is used to report failures back to the client. Any I/O
+ * or frame processing exception is reported back to all of the
+ * registered listeners so that they can take action. The
+ * underlying TCP connection is closed after any failure.
+ *
+ * @param reason this is the cause of the failure
+ */
+ public void failure(Exception reason) throws IOException {
+ if(!closed.getAndSet(true)) {
+ for(FrameListener listener : listeners) {
+ try {
+ listener.onError(session, reason);
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ }
+ }
+ }
+ }
+
+ /**
+ * This is used to close the connection without a specific reason.
+ * The close reason will be sent as a control frame before the
+ * TCP connection is terminated. All registered listeners will be
+ * notified of the close event.
+ *
+ * @param reason this is the reason for the connection closure
+ */
+ public void close(Reason reason) throws IOException{
+ if(!closed.getAndSet(true)) {
+ for(FrameListener listener : listeners) {
+ try {
+ listener.onClose(session, reason);
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ }
+ }
+ }
+ }
+
+ /**
+ * This is used to close the connection when it has not responded
+ * to any activity for a configured period of time. It may be
+ * possible to send up a control frame, however if the TCP channel
+ * is closed this will just notify the listeners.
+ */
+ public void close() throws IOException{
+ if(!closed.getAndSet(true)) {
+ try {
+ for(FrameListener listener : listeners) {
+ listener.onClose(session, normal);
+ }
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ }
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/OutputBarrier.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/OutputBarrier.java
new file mode 100644
index 0000000..3da2635
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/OutputBarrier.java
@@ -0,0 +1,99 @@
+/*
+ * OutputBarrier.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.io.IOException;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteWriter;
+
+/**
+ * The <code>OutputBarrier</code> is used to ensure that control
+ * frames and data frames do not get sent at the same time. Sending
+ * both at the same time could lead to the status checking thread
+ * being blocked and this could eventually exhaust the thread pool.
+ *
+ * @author Niall Gallagher
+ */
+class OutputBarrier {
+
+ /**
+ * This is used to check if there is an operation in progress.
+ */
+ private final ReentrantLock lock;
+
+ /**
+ * This is the underlying sender used to send the frames.
+ */
+ private final ByteWriter writer;
+
+ /**
+ * This is the TCP channel the frames are delivered over.
+ */
+ private final Channel channel;
+
+ /**
+ * This is the length of time to wait before failing to lock.
+ */
+ private final long duration;
+
+ /**
+ * Constructor for the <code>OutputBarrier</code> object. This
+ * is used to ensure that if there is currently a blocking write
+ * in place that the <code>SessionChecker</code> will not end up
+ * being blocked if it attempts to send a control frame.
+ *
+ * @param request this is the request to get the TCP channel from
+ * @param duration this is the length of time to wait for the lock
+ */
+ public OutputBarrier(Request request, long duration) {
+ this.lock = new ReentrantLock();
+ this.channel = request.getChannel();
+ this.writer = channel.getWriter();
+ this.duration = duration;
+ }
+
+ /**
+ * This method is used to send all frames. It is important that
+ * a lock is used to protect this so that if there is an attempt
+ * to send out a control frame while the connection is blocked
+ * there is an exception thrown.
+ *
+ * @param frame this is the frame to send over the TCP channel
+ */
+ public void send(byte[] frame) throws IOException {
+ try {
+ if(!lock.tryLock(duration, MILLISECONDS)) {
+ throw new IOException("Transport lock could not be acquired");
+ }
+ try {
+ writer.write(frame);
+ writer.flush(); // less throughput, better latency
+ } finally {
+ lock.unlock();
+ }
+ } catch(Exception e) {
+ throw new IOException("Error writing to transport", e);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/PathRouter.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/PathRouter.java
new file mode 100644
index 0000000..9deb66a
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/PathRouter.java
@@ -0,0 +1,111 @@
+/*
+ * PathRouter.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_PROTOCOL;
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_VERSION;
+import static org.simpleframework.http.Protocol.UPGRADE;
+import static org.simpleframework.http.Protocol.WEBSOCKET;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.simpleframework.http.Path;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+/**
+ * The <code>PathRouter</code> is used when there are multiple
+ * services that can be used. Each service is selected based on the
+ * path sent in the initiating request. If a match cannot be made
+ * based on the request then a default service us chosen.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.RouterContainer
+ */
+public class PathRouter implements Router {
+
+ /**
+ * This is the set of services that can be selected.
+ */
+ private final Map<String, Service> registry;
+
+ /**
+ * This is the default service chosen if there is no match.
+ */
+ private final Service primary;
+
+ /**
+ * Constructor for the <code>PathRouter</code> object. This is used
+ * to create a router using a selection of services that can be
+ * selected using the path provided in the initiating request.
+ *
+ * @param registry this is the registry of available services
+ * @param primary this is the default service to use
+ */
+ public PathRouter(Map<String, Service> registry, Service primary) throws IOException {
+ this.registry = registry;
+ this.primary = primary;
+ }
+
+ /**
+ * This is used to route an incoming request to a service if
+ * the request represents a WebSocket handshake as defined by
+ * RFC 6455. If the request is not a session initiating handshake
+ * then this will return a null value to allow it to be processed
+ * by some other part of the server.
+ *
+ * @param request this is the request to use for routing
+ * @param response this is the response to establish the session
+ *
+ * @return a service that can be used to process the session
+ */
+ public Service route(Request request, Response response) {
+ String token = request.getValue(UPGRADE);
+
+ if(token != null) {
+ if(token.equalsIgnoreCase(WEBSOCKET)) {
+ List<String> protocols = request.getValues(SEC_WEBSOCKET_PROTOCOL);
+ String version = request.getValue(SEC_WEBSOCKET_VERSION);
+ Path path = request.getPath();
+ String normal = path.getPath();
+
+ if(version != null) {
+ response.setValue(SEC_WEBSOCKET_VERSION, version);
+ }
+ for(String protocol : protocols) {
+ String original = response.getValue(SEC_WEBSOCKET_PROTOCOL);
+
+ if(original == null) {
+ response.setValue(SEC_WEBSOCKET_PROTOCOL, protocol);
+ }
+ }
+ Service service = registry.get(normal);
+
+ if(service != null) {
+ return service;
+ }
+ return primary;
+ }
+ }
+ return null;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ProtocolRouter.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ProtocolRouter.java
new file mode 100644
index 0000000..54060c9
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ProtocolRouter.java
@@ -0,0 +1,105 @@
+/*
+ * ProtocolRouter.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_PROTOCOL;
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_VERSION;
+import static org.simpleframework.http.Protocol.UPGRADE;
+import static org.simpleframework.http.Protocol.WEBSOCKET;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+/**
+ * The <code>ProtocolRouter</code> is used when there are multiple
+ * services that can be used. Each service is selected based on the
+ * protocol sent in the initiating request. If a match cannot be
+ * made based on the request then a default service us chosen.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.RouterContainer
+ */
+public class ProtocolRouter implements Router {
+
+ /**
+ * This is the set of services that can be selected.
+ */
+ private final Map<String, Service> registry;
+
+ /**
+ * This is the default service chosen if there is no match.
+ */
+ private final Service primary;
+
+ /**
+ * Constructor for the <code>ProtocolRouter</code> object. This is
+ * used to create a router using a selection of services that can
+ * be selected using the <code>Sec-WebSocket-Protocol</code> header
+ * sent in the initiating request by the client.
+ *
+ * @param registry this is the registry of available services
+ * @param primary this is the default service to use
+ */
+ public ProtocolRouter(Map<String, Service> registry, Service primary) throws IOException {
+ this.registry = registry;
+ this.primary = primary;
+ }
+
+ /**
+ * This is used to route an incoming request to a service if
+ * the request represents a WebSocket handshake as defined by
+ * RFC 6455. If the request is not a session initiating handshake
+ * then this will return a null value to allow it to be processed
+ * by some other part of the server.
+ *
+ * @param request this is the request to use for routing
+ * @param response this is the response to establish the session
+ *
+ * @return a service that can be used to process the session
+ */
+ public Service route(Request request, Response response) {
+ String token = request.getValue(UPGRADE);
+
+ if(token != null) {
+ if(token.equalsIgnoreCase(WEBSOCKET)) {
+ List<String> protocols = request.getValues(SEC_WEBSOCKET_PROTOCOL);
+ String version = request.getValue(SEC_WEBSOCKET_VERSION);
+
+ if(version != null) {
+ response.setValue(SEC_WEBSOCKET_VERSION, version);
+ }
+ for(String protocol : protocols) {
+ Service service = registry.get(protocol);
+
+ if(service != null) {
+ response.setValue(SEC_WEBSOCKET_PROTOCOL, protocol);
+ return service;
+ }
+ }
+ return primary;
+ }
+ }
+ return null;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ReasonExtractor.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ReasonExtractor.java
new file mode 100644
index 0000000..fb6ce88
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ReasonExtractor.java
@@ -0,0 +1,114 @@
+/*
+ * ReasonExtractor.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.socket.CloseCode.NO_STATUS_CODE;
+
+import org.simpleframework.http.socket.CloseCode;
+import org.simpleframework.http.socket.DataConverter;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.Reason;
+
+/**
+ * The <code>ReasonExtractor</code> object is used to extract the close
+ * reason from a frame payload. If their is no close reason this will
+ * return a <code>Reason</code> with just the close code. Finally in
+ * the event of a botched frame being sent with no close code then the
+ * close code 1005 is used to indicate no reason.
+ *
+ * @author Niall Gallagher
+ */
+class ReasonExtractor {
+
+ /**
+ * This is the data converter object used to convert data.
+ */
+ private final DataConverter converter;
+
+ /**
+ * Constructor for the <code>ReasonExtractor</code> object. This
+ * is used to create an extractor for close code and the close
+ * reason descriptions. All descriptions are decoded using the
+ * UTF-8 character encoding.
+ */
+ public ReasonExtractor() {
+ this.converter = new DataConverter();
+ }
+
+ /**
+ * This is used to extract a reason from the provided frame. The
+ * close reason is taken from the first two bytes of the frame
+ * payload and the UTF-8 string that follows is the description.
+ *
+ * @param frame this is the frame to extract the reason from
+ *
+ * @return a reason containing the close code and reason
+ */
+ public Reason extract(Frame frame) {
+ byte[] data = frame.getBinary();
+
+ if(data.length > 0) {
+ CloseCode code = extractCode(data);
+ String text = extractText(data);
+
+ return new Reason(code, text);
+ }
+ return new Reason(NO_STATUS_CODE);
+ }
+
+ /**
+ * This method is used to extract the UTF-8 description from the
+ * frame payload. If there are only two bytes within the payload
+ * then this will return null for the reason.
+ *
+ * @param data the frame payload to extract the description from
+ *
+ * @return returns the description within the payload
+ */
+ private String extractText(byte[] data) {
+ int length = data.length - 2;
+
+ if(length > 0) {
+ return converter.convert(data, 2, length);
+ }
+ return null;
+ }
+
+ /**
+ * This method is used to extract the close code. The close code
+ * is an two byte integer in network byte order at the start
+ * of the close frame payload. This code is required by RFC 6455
+ * however if not code is available code 1005 is returned.
+ *
+ * @param data the frame payload to extract the description from
+ *
+ * @return returns the description within the payload
+ */
+ private CloseCode extractCode(byte[] data) {
+ int length = data.length;
+
+ if(length > 0) {
+ int high = data[0];
+ int low = data[1];
+
+ return CloseCode.resolveCode(high, low);
+ }
+ return NO_STATUS_CODE;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RequestValidator.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RequestValidator.java
new file mode 100644
index 0000000..7e47dc3
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RequestValidator.java
@@ -0,0 +1,137 @@
+/*
+ * RequestValidator.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.Protocol.CONNECTION;
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_KEY;
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_VERSION;
+import static org.simpleframework.http.Protocol.UPGRADE;
+import static org.simpleframework.http.Protocol.WEBSOCKET;
+
+import java.util.List;
+
+import org.simpleframework.http.Request;
+
+/**
+ * The <code>RequestValidator</code> object is used to ensure requests
+ * for confirm to RFC 6455 section 4.2.1. The client opening handshake
+ * must consist of several parts, including a version of 13 referring
+ * to RFC 6455, a WebSocket key, and the required HTTP connection
+ * details. If any of these are missing the server is obliged to
+ * respond with a HTTP 400 response indicating a bad request.
+ *
+ * @author Niall Gallagher
+ */
+class RequestValidator {
+
+ /**
+ * This is the request forming the client part of the handshake.
+ */
+ private final Request request;
+
+ /**
+ * This is the version referring to the required client version.
+ */
+ private final String version;
+
+ /**
+ * Constructor for the <code>RequestValidator</code> object. This
+ * is used to create a plain vanilla validator that uses version
+ * 13 as dictated by RFC 6455 section 4.2.1.
+ *
+ * @param request this is the handshake request from the client
+ */
+ public RequestValidator(Request request) {
+ this(request, "13");
+ }
+
+ /**
+ * Constructor for the <code>RequestValidator</code> object. This
+ * is used to create a plain vanilla validator that uses version
+ * 13 as dictated by RFC 6455 section 4.2.1.
+ *
+ * @param request this is the handshake request from the client
+ * @param version a version other than 13 if desired
+ */
+ public RequestValidator(Request request, String version) {
+ this.request = request;
+ this.version = version;
+ }
+
+ /**
+ * This is used to determine if the client handshake request had
+ * all the required headers as dictated by RFC 6455 section 4.2.1.
+ * If the request does not contain any of these parts then this
+ * will return false, indicating a HTTP 400 response should be
+ * sent to the client.
+ *
+ * @return true if the request was a valid handshake
+ */
+ public boolean isValid() {
+ if(!isProtocol()) {
+ return false;
+ }
+ if(!isUpgrade()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * This is used to determine if the request is a valid WebSocket
+ * handshake of the correct version. This also checks to see if
+ * the request contained the required handshake token.
+ *
+ * @return this returns true if the request is a valid handshake
+ */
+ private boolean isProtocol() {
+ String protocol = request.getValue(SEC_WEBSOCKET_VERSION);
+ String token = request.getValue(SEC_WEBSOCKET_KEY);
+
+ if(token != null) {
+ return version.equals(protocol);
+ }
+ return false;
+ }
+
+ /**
+ * Here we check to ensure that there is a HTTP connection header
+ * with the required upgrade token. The upgrade token may be
+ * one of many, so all must be checked. Finally to ensure that
+ * the upgrade is for a WebSocket the upgrade header is checked.
+ *
+ * @return this returns true if the request is an upgrade
+ */
+ private boolean isUpgrade() {
+ List<String> tokens = request.getValues(CONNECTION);
+
+ for(String token : tokens) {
+ if(token.equalsIgnoreCase(UPGRADE)) {
+ String upgrade = request.getValue(UPGRADE);
+
+ if(upgrade != null) {
+ return upgrade.equalsIgnoreCase(WEBSOCKET);
+ }
+ return false;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java
new file mode 100644
index 0000000..0ba780e
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ResponseBuilder.java
@@ -0,0 +1,159 @@
+/*
+ * ResponseBuilder.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.Protocol.CLOSE;
+import static org.simpleframework.http.Protocol.CONNECTION;
+import static org.simpleframework.http.Protocol.DATE;
+import static org.simpleframework.http.Protocol.SEC_WEBSOCKET_ACCEPT;
+import static org.simpleframework.http.Protocol.UPGRADE;
+import static org.simpleframework.http.Protocol.WEBSOCKET;
+import static org.simpleframework.http.socket.service.ServiceEvent.WRITE_HEADER;
+
+import java.io.IOException;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.Status;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.ByteWriter;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>ResponseBuilder</code> object is used to build a response
+ * to a WebSocket handshake. In order for a successful handshake to
+ * complete a HTTP request must have a version of 13 referring
+ * to RFC 6455, a WebSocket key, and the required HTTP connection
+ * details. If any of these are missing the server is obliged to
+ * respond with a HTTP 400 response indicating a bad request.
+ *
+ * @author Niall Gallagher
+ */
+class ResponseBuilder {
+
+ /**
+ * This is used to validate the initiating WebSocket request.
+ */
+ private final RequestValidator validator;
+
+ /**
+ * This is the accept token generated for the request.
+ */
+ private final AcceptToken token;
+
+ /**
+ * This is the sender used to send the WebSocket response.
+ */
+ private final ByteWriter writer;
+
+ /**
+ * This is the response to the WebSocket handshake.
+ */
+ private final Response response;
+
+ /**
+ * This is the underlying TCP channel for the request.
+ */
+ private final Channel channel;
+
+ /**
+ * This is used to trace the activity for the handshake.
+ */
+ private final Trace trace;
+
+ /**
+ * Constructor for the <code>ResponseBuilder</code> object. In order
+ * to process the WebSocket handshake this requires the original
+ * request and the response as well as the underlying TCP channel
+ * which forms the basis of the WebSocket connection.
+ *
+ * @param request this is the request that initiated the handshake
+ * @param response this is the response for the handshake
+ */
+ public ResponseBuilder(Request request, Response response) throws Exception {
+ this.validator = new RequestValidator(request);
+ this.token = new AcceptToken(request);
+ this.channel = request.getChannel();
+ this.writer = channel.getWriter();
+ this.trace = channel.getTrace();
+ this.response = response;
+ }
+
+ /**
+ * This is used to determine if the client handshake request had
+ * all the required headers as dictated by RFC 6455 section 4.2.1.
+ * If the request does not contain any of these parts then this
+ * will return false, indicating a HTTP 400 response is sent to
+ * the client, otherwise a HTTP 101 response is sent.
+ */
+ public void commit() throws IOException {
+ if(validator.isValid()) {
+ accept();
+ } else {
+ reject();
+ }
+ }
+
+ /**
+ * This is used to respond to the client with a HTTP 400 response
+ * indicating the WebSocket handshake failed. No response body is
+ * sent with the rejection message and the underlying TCP channel
+ * is closed to prevent further use of the connection.
+ */
+ private void reject() throws IOException {
+ long time = System.currentTimeMillis();
+
+ response.setStatus(Status.BAD_REQUEST);
+ response.setValue(CONNECTION, CLOSE);
+ response.setDate(DATE, time);
+
+ String header = response.toString();
+ byte[] message = header.getBytes("UTF-8");
+
+ trace.trace(WRITE_HEADER, header);
+ writer.write(message);
+ writer.flush();
+ writer.close();
+ }
+
+ /**
+ * This is used to respond to the client with a HTTP 101 response
+ * to indicate that the WebSocket handshake succeeeded. Once this
+ * response has been sent all traffic between the client and
+ * server will be with WebSocket frames as defined by RFC 6455.
+ */
+ private void accept() throws IOException {
+ long time = System.currentTimeMillis();
+ String accept = token.create();
+
+ response.setStatus(Status.SWITCHING_PROTOCOLS);
+ response.setDescription(UPGRADE);
+ response.setValue(CONNECTION, UPGRADE);
+ response.setDate(DATE, time);
+ response.setValue(SEC_WEBSOCKET_ACCEPT, accept);
+ response.setValue(UPGRADE, WEBSOCKET);
+
+ String header = response.toString();
+ byte[] message = header.getBytes("UTF-8");
+
+ trace.trace(WRITE_HEADER, header);
+ writer.write(message);
+ writer.flush();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Router.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Router.java
new file mode 100644
index 0000000..3b466f5
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Router.java
@@ -0,0 +1,59 @@
+/*
+ * Router.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+
+/**
+ * The <code>Router</code> interface represents a means of routing
+ * a session initiating request to the correct service. Typically
+ * a service is chosen based on the sub-protocol provided in the
+ * initiating request, however it can be chosen on any criteria
+ * available in the request. An initiating request must contain
+ * a <code>Connection</code> header with the <code>websocket</code>
+ * token according to RFC 6455 section 4.2.1. If the request does
+ * not contain this token it is treated as a normal request and
+ * a <code>Service</code> will not be resolved.
+ * <p>
+ * If a service has been successfully chosen from the initiating
+ * request the the value of <code>Sec-WebSocket-Protocol</code> will
+ * contain either the chosen protocol if a match was made with the
+ * initiating request or null to indicate a default choice.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.RouterContainer
+ */
+public interface Router {
+
+ /**
+ * This is used to route an incoming request to a service if
+ * the request represents a WebSocket handshake as defined by
+ * RFC 6455. If the request is not a session initiating handshake
+ * then this must return a null value to allow it to be processed
+ * by some other part of the server.
+ *
+ * @param request this is the request to use for routing
+ * @param response this is the response to establish the session
+ *
+ * @return a service that can be used to process the session
+ */
+ Service route(Request request, Response response);
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RouterContainer.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RouterContainer.java
new file mode 100644
index 0000000..3b018a9
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/RouterContainer.java
@@ -0,0 +1,109 @@
+/*
+ * RouterContainer.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.io.IOException;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.core.Container;
+
+/**
+ * The <code>RouterContainer</code> is used to route requests that
+ * satisfy a WebSocket opening handshake to a specific service. Each
+ * request intercepted by this <code>Container</code> implementation
+ * is examined for opening handshake criteria as specified by RFC 6455,
+ * and if it contains the required information it is router to a
+ * specific service using a <code>Router</code> implementation. If the
+ * request does not contain the required criteria it is handled by
+ * an internal container delegate.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.service.Router
+ */
+public class RouterContainer implements Container {
+
+ /**
+ * This is the service dispatcher used to dispatch requests.
+ */
+ private final ServiceDispatcher dispatcher;
+
+ /**
+ * This is the container used to handle traditional requests.
+ */
+ private final Container container;
+
+ /**
+ * This is the router used to select specific services.
+ */
+ private final Router router;
+
+ /**
+ * Constructor for the <code>RouterContainer</code> object. This
+ * requires a container to delegate traditional requests to and
+ * a <code>Router</code> implementation which can be used to
+ * select a service to dispatch a WebSocket session to.
+ *
+ * @param container this is the container to delegate to
+ * @param router this is the router used to select services
+ * @param threads this contains the number of threads to use
+ */
+ public RouterContainer(Container container, Router router, int threads) throws IOException {
+ this(container, router, threads, 10000);
+ }
+
+ /**
+ * Constructor for the <code>RouterContainer</code> object. This
+ * requires a container to delegate traditional requests to and
+ * a <code>Router</code> implementation which can be used to
+ * select a service to dispatch a WebSocket session to.
+ *
+ * @param container this is the container to delegate to
+ * @param router this is the router used to select services
+ * @param threads this contains the number of threads to use
+ * @param ping this is the frequency to send ping frames with
+ */
+ public RouterContainer(Container container, Router router, int threads, long ping) throws IOException {
+ this.dispatcher = new ServiceDispatcher(router, threads, ping);
+ this.container = container;
+ this.router = router;
+ }
+
+ /**
+ * This method is used to create a dispatch a <code>Session</code> to
+ * a specific service selected by a router. If the session initiating
+ * handshake fails for any reason this will close the underlying TCP
+ * connection and send a HTTP 400 response back to the client. All
+ * traditional requests that do not represent an WebSocket opening
+ * handshake are dispatched to the internal container.
+ *
+ * @param req the request that contains the client HTTP message
+ * @param resp the response used to deliver the server response
+ */
+ public void handle(Request req, Response resp) {
+ Service service = router.route(req, resp);
+
+ if(service != null) {
+ dispatcher.dispatch(req, resp);
+ } else {
+ container.handle(req, resp);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Service.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Service.java
new file mode 100644
index 0000000..d95c01f
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/Service.java
@@ -0,0 +1,44 @@
+/*
+ * Service.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import org.simpleframework.http.socket.Session;
+
+/**
+ * The <code>Service</code> interface represents a service that can be
+ * used to communicate with the WebSocket protocol defined in RFC 6455.
+ * Typically a service will implement a sub-protocol negotiated from
+ * the initiating HTTP request. The service should be considered a
+ * hand off point rather than an place to implement business logic.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.FrameChannel
+ */
+public interface Service {
+
+ /**
+ * This method connects a new session with a service implementation.
+ * Connecting a session with a service in this way should not block
+ * as it could cause starvation of the servicing thread pool.
+ *
+ * @param session the new session to connect to the service
+ */
+ void connect(Session session);
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceChannel.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceChannel.java
new file mode 100644
index 0000000..ad5325c
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceChannel.java
@@ -0,0 +1,149 @@
+/*
+ * ServiceChannel.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.io.IOException;
+
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.FrameChannel;
+
+/**
+ * The <code>ServiceChannel</code> represents a full duplex communication
+ * channel as defined by RFC 6455. Any instance of this will provide
+ * a means to perform asynchronous writes and reads to a remote client
+ * using a lightweight framing protocol. A frame is a finite length
+ * sequence of bytes that can hold either text or binary data. Also,
+ * control frames are used to perform heartbeat monitoring and closure.
+ * <p>
+ * For convenience frames can be consumed from the socket via a
+ * callback to a registered listener. This avoids having to poll each
+ * socket for data and provides a asynchronous event driven model of
+ * communication, which greatly reduces overhead and complication.
+ *
+ * @author Niall Gallagher
+ */
+class ServiceChannel implements FrameChannel {
+
+ /**
+ * This is the internal channel for full duplex communication.
+ */
+ private final FrameChannel channel;
+
+ /**
+ * Constructor for the <code>ServiceChannel</code> object. This is
+ * used to create a channel that is given to the application. This
+ * is synchronized so only one frame can be dispatched at a time.
+ *
+ * @param channel this is the channel to delegate to
+ */
+ public ServiceChannel(FrameChannel channel) {
+ this.channel = channel;
+ }
+
+ /**
+ * This is used to send data to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param data this is the data that is to be sent
+ */
+ public synchronized void send(byte[] data) throws IOException {
+ channel.send(data);
+ }
+
+ /**
+ * This is used to send text to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param text this is the text that is to be sent
+ */
+ public synchronized void send(String text) throws IOException {
+ channel.send(text);
+ }
+
+ /**
+ * This is used to send data to the connected client. To prevent
+ * an application code from causing resource issues this will block
+ * as soon as a configured linked list of mapped memory buffers has
+ * been exhausted. Caution should be taken when writing a broadcast
+ * implementation that can write to multiple sockets as a badly
+ * behaving socket that has filled its output buffering capacity
+ * can cause congestion.
+ *
+ * @param frame this is the frame that is to be sent
+ */
+ public synchronized void send(Frame frame) throws IOException {
+ channel.send(frame);
+ }
+
+ /**
+ * This is used to register a <code>FrameListener</code> to this
+ * instance. The registered listener will receive all user frames
+ * and control frames sent from the client. Also, when the frame
+ * is closed or when an unexpected error occurs the listener is
+ * notified. Any number of listeners can be registered at any time.
+ *
+ * @param listener this is the listener that is to be registered
+ */
+ public synchronized void register(FrameListener listener) throws IOException {
+ channel.register(listener);
+ }
+
+ /**
+ * This is used to remove a <code>FrameListener</code> from this
+ * instance. After removal the listener will no longer receive
+ * any user frames or control messages from this specific instance.
+ *
+ * @param listener this is the listener to be removed
+ */
+ public synchronized void remove(FrameListener listener) throws IOException {
+ channel.remove(listener);
+ }
+
+ /**
+ * This is used to close the connection with a specific reason.
+ * The close reason will be sent as a control frame before the
+ * TCP connection is terminated.
+ *
+ * @param reason the reason for closing the connection
+ */
+ public synchronized void close(Reason reason) throws IOException {
+ channel.close(reason);
+ }
+
+ /**
+ * This is used to close the connection without a specific reason.
+ * The close reason will be sent as a control frame before the
+ * TCP connection is terminated.
+ */
+ public void close() throws IOException {
+ channel.close();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceDispatcher.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceDispatcher.java
new file mode 100644
index 0000000..509495f
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceDispatcher.java
@@ -0,0 +1,101 @@
+/*
+ * ServiceDispatcher.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.io.IOException;
+
+import org.simpleframework.common.thread.ConcurrentScheduler;
+import org.simpleframework.common.thread.Scheduler;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.transport.reactor.ExecutorReactor;
+import org.simpleframework.transport.reactor.Reactor;
+
+/**
+ * The <code>ServiceDispatcher</code> object is used to perform the
+ * opening handshake for a WebSocket session. Once the session has been
+ * established it is connected to a <code>Service</code> where frames
+ * can be sent and received. If for any reason the handshake fails
+ * this will terminated the connection with a HTTP 400 response.
+ *
+ * @author Niall Gallagher
+ */
+class ServiceDispatcher {
+
+ /**
+ * This is the session dispatcher used to dispatch the session.
+ */
+ private final SessionDispatcher dispatcher;
+
+ /**
+ * This is used to build the sessions from the handshake request.
+ */
+ private final SessionBuilder builder;
+
+ /**
+ * This is used asynchronously read frames from the TCP channel.
+ */
+ private final Scheduler scheduler;
+
+ /**
+ * This is used to notify of read events on the TCP channel.
+ */
+ private final Reactor reactor;
+
+ /**
+ * Constructor for the <code>ServiceDispatcher</code> object. The
+ * dispatcher created will dispatch WebSocket sessions to a service
+ * using the provided <code>Router</code> instance.
+ *
+ * @param router this is the router used to select a service
+ * @param threads this is the number of threads to use
+ */
+ public ServiceDispatcher(Router router, int threads) throws IOException {
+ this(router, threads, 10000);
+ }
+
+ /**
+ * Constructor for the <code>ServiceDispatcher</code> object. The
+ * dispatcher created will dispatch WebSocket sessions to a service
+ * using the provided <code>Router</code> instance.
+ *
+ * @param router this is the router used to select a service
+ * @param threads this is the number of threads to use
+ * @param ping this is the frequency used to send ping frames
+ */
+ public ServiceDispatcher(Router router, int threads, long ping) throws IOException {
+ this.scheduler = new ConcurrentScheduler(FrameCollector.class, threads);
+ this.reactor = new ExecutorReactor(scheduler);
+ this.builder = new SessionBuilder(scheduler, reactor, ping);
+ this.dispatcher = new SessionDispatcher(builder, router);
+ }
+
+ /**
+ * This method is used to create a dispatch a <code>Session</code> to
+ * a specific service selected by a router. If the session initiating
+ * handshake fails for any reason this will close the underlying TCP
+ * connection and send a HTTP 400 response back to the client.
+ *
+ * @param request this is the session initiating request
+ * @param response this is the session initiating response
+ */
+ public void dispatch(Request request, Response response) {
+ dispatcher.dispatch(request, response);
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceEvent.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceEvent.java
new file mode 100644
index 0000000..a5d0079
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceEvent.java
@@ -0,0 +1,97 @@
+/*
+ * ServiceEvent.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+/**
+ * The <code>ServiceEvent</code> enumeration contains the events that
+ * are dispatched processing a WebSocket. To see how a WebSocket is
+ * behaving and to gather performance statistics the service events
+ * can be intercepted using a custom <code>TraceAnalyzer</code> object.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.transport.trace.TraceAnalyzer
+ */
+public enum ServiceEvent {
+
+ /**
+ * This event is dispatched when a WebSocket is connected.
+ */
+ OPEN_SOCKET,
+
+ /**
+ * This event is dispatched when a WebSocket is dispatched.
+ */
+ DISPATCH_SOCKET,
+
+ /**
+ * This event is dispatched when a WebSocket channel is closed.
+ */
+ TERMINATE_SOCKET,
+
+ /**
+ * This event is dispatched when the response handshake is sent.
+ */
+ WRITE_HEADER,
+
+ /**
+ * This event is dispatched when the WebSocket receives a ping.
+ */
+ READ_PING,
+
+ /**
+ * This event is dispatched when a ping is sent over a WebSocket.
+ */
+ WRITE_PING,
+
+ /**
+ * This event is dispatched when the WebSocket receives a pong.
+ */
+ READ_PONG,
+
+ /**
+ * This event is dispatched when a pong is sent over a WebSocket.
+ */
+ WRITE_PONG,
+
+ /**
+ * This event is dispatched when a frame is read from a WebSocket.
+ */
+ READ_FRAME,
+
+ /**
+ * This event is dispatched when a frame is sent over a WebSocket.
+ */
+ WRITE_FRAME,
+
+ /**
+ * This indicates that there has been no response to a ping.
+ */
+ PING_EXPIRED,
+
+ /**
+ * This indicates that there has been no response to a ping.
+ */
+ PONG_RECEIVED,
+
+ /**
+ * This event is dispatched when an error occurs with a WebSocket.
+ */
+ ERROR;
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceSession.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceSession.java
new file mode 100644
index 0000000..b8fc083
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/ServiceSession.java
@@ -0,0 +1,139 @@
+/*
+ * ServiceSession.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.util.Map;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.socket.FrameChannel;
+import org.simpleframework.http.socket.Session;
+
+/**
+ * The <code>ServiceSession</code> represents a simple WebSocket session
+ * that contains the connection handshake details and the actual socket.
+ * In order to determine how the session should be interacted with the
+ * protocol is conveniently exposed, however all attributes of the
+ * original HTTP request are available.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.http.socket.FrameChannel
+ */
+class ServiceSession implements Session {
+
+ /**
+ * The WebSocket used for asynchronous full duplex communication.
+ */
+ private final FrameChannel channel;
+
+ /**
+ * This is the initiating response associated with the session.
+ */
+ private final Response response;
+
+ /**
+ * This is the initiating request associated with the session.
+ */
+ private final Request request;
+
+ /**
+ * This is the bag of attributes used by this session.
+ */
+ private final Map attributes;
+
+ /**
+ * Constructor for the <code>ServiceSession</code> object. This is used
+ * to create the session that will be used by a <code>Service</code> to
+ * send and receive WebSocket frames.
+ *
+ * @param channel this is the actual WebSocket for the session
+ * @param request this is the session initiating request
+ * @param response this is the session initiating response
+ */
+ public ServiceSession(FrameChannel channel, Request request, Response response) {
+ this.channel = new ServiceChannel(channel);
+ this.attributes = request.getAttributes();
+ this.response = response;
+ this.request = request;
+ }
+
+ /**
+ * 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 of that have been set on the request
+ */
+ public Map getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * 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 attributes.get(key);
+ }
+
+ /**
+ * Provides a <code>WebSocket</code> that can be used to communicate
+ * with the connected client. Communication is full duplex and also
+ * asynchronous through the use of a <code>FrameListener</code> that
+ * can be registered with the socket.
+ *
+ * @return a web socket for full duplex communication
+ */
+ public FrameChannel getChannel() {
+ return channel;
+ }
+
+ /**
+ * Provides the <code>Request</code> used to initiate the session.
+ * This is useful in establishing the identity of the user, acquiring
+ * an security information and also for determining the request path
+ * that was used, which be used to establish context.
+ *
+ * @return the request used to initiate the session
+ */
+ public Request getRequest() {
+ return request;
+ }
+
+ /**
+ * Provides the <code>Response</code> used to establish the session
+ * with the remote client. This is useful in establishing the protocol
+ * used to create the session and also for determining various other
+ * useful contextual information.
+ *
+ * @return the response used to establish the session
+ */
+ public Response getResponse() {
+ return response;
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionBuilder.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionBuilder.java
new file mode 100644
index 0000000..59864ee
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionBuilder.java
@@ -0,0 +1,93 @@
+/*
+ * SessionBuilder.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import java.io.IOException;
+
+import org.simpleframework.common.thread.Scheduler;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.transport.reactor.Reactor;
+
+/**
+ * The <code>SessionBuilder</code> object is used to create sessions
+ * for connected WebSockets. Before the session is created a response
+ * is sent back to the connected client. If for some reason the session
+ * is not valid or does not conform to the requirements of RFC 6455
+ * then a HTTP 400 response code is sent and the TCP channel is closed.
+ *
+ * @author Niall Gallagher
+ */
+class SessionBuilder {
+
+ /**
+ * This is the scheduler that is used to ping WebSocket sessions.
+ */
+ private final Scheduler scheduler;
+
+ /**
+ * This is the reactor used to register for I/O notifications.
+ */
+ private final Reactor reactor;
+
+ /**
+ * This is the frequency the server should send out ping frames.
+ */
+ private final long ping;
+
+ /**
+ * Constructor for the <code>SessionBuilder</code> object. This is
+ * used to create sessions using the request and response associated
+ * with the WebSocket opening handshake.
+ *
+ * @param scheduler this is the shared thread pool used for pinging
+ * @param reactor this is used to check for I/O notifications
+ * @param ping this is the frequency to send out ping frames
+ */
+ public SessionBuilder(Scheduler scheduler, Reactor reactor, long ping) {
+ this.scheduler = scheduler;
+ this.reactor = reactor;
+ this.ping = ping;
+ }
+
+ /**
+ * This is used to create a WebSocket session. If at any point there
+ * is an error creating the session the underlying TCP connection is
+ * closed and a <code>Session</code> is returned regardless.
+ *
+ * @param request this is the request associated with this session
+ * @param response this is the response associated with this session
+ *
+ * @return this returns the session associated with the WebSocket
+ */
+ public Session create(Request request, Response response) throws Exception {
+ FrameConnection connection = new FrameConnection(request, response, reactor);
+ ResponseBuilder builder = new ResponseBuilder(request, response);
+ StatusChecker checker = new StatusChecker(connection, request, scheduler, ping);
+
+ try {
+ builder.commit();
+ checker.start();
+ } catch(Exception e) {
+ throw new IOException("Could not send response", e);
+ }
+ return connection.open();
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionDispatcher.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionDispatcher.java
new file mode 100644
index 0000000..6543be0
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/SessionDispatcher.java
@@ -0,0 +1,111 @@
+/*
+ * SessionDispatcher.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.socket.service.ServiceEvent.DISPATCH_SOCKET;
+import static org.simpleframework.http.socket.service.ServiceEvent.ERROR;
+import static org.simpleframework.http.socket.service.ServiceEvent.TERMINATE_SOCKET;
+
+import org.simpleframework.http.Request;
+import org.simpleframework.http.Response;
+import org.simpleframework.http.socket.Session;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>SessionDispatcher</code> object is used to perform the
+ * opening handshake for a WebSocket session. Once the session has been
+ * established it is connected to a <code>Service</code> where frames
+ * can be sent and received. If for any reason the handshake fails
+ * this will terminated the connection with a HTTP 400 response.
+ *
+ * @author Niall Gallagher
+ */
+class SessionDispatcher {
+
+ /**
+ * This is used to create the session for the WebSocket.
+ */
+ private final SessionBuilder builder;
+
+ /**
+ * This is used to select the service to dispatch to.
+ */
+ private final Router router;
+
+ /**
+ * Constructor for the <code>SessionDispatcher</code> object. The
+ * dispatcher created will dispatch WebSocket sessions to a service
+ * using the provided <code>Router</code> instance.
+ *
+ * @param builder this is used to build the WebSocket session
+ * @param router this is used to select the service
+ */
+ public SessionDispatcher(SessionBuilder builder, Router router) {
+ this.builder = builder;
+ this.router = router;
+ }
+
+ /**
+ * This method is used to create a dispatch a <code>Session</code> to
+ * a specific service selected by a router. If the session initiating
+ * handshake fails for any reason this will close the underlying TCP
+ * connection and send a HTTP 400 response back to the client.
+ *
+ * @param request this is the session initiating request
+ * @param response this is the session initiating response
+ */
+ public void dispatch(Request request, Response response) {
+ Channel channel = request.getChannel();
+ Trace trace = channel.getTrace();
+
+ try {
+ Service service = router.route(request, response);
+ Session session = builder.create(request, response);
+
+ trace.trace(DISPATCH_SOCKET);
+ service.connect(session);
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ terminate(request, response);
+ }
+ }
+
+ /**
+ * This method is used to terminate the connection and commit the
+ * response. Terminating the session before it has been dispatched
+ * is done when there is a protocol or an unexpected I/O error with
+ * the underlying TCP channel.
+ *
+ * @param request this is the session initiating request
+ * @param response this is the session initiating response
+ */
+ public void terminate(Request request, Response response) {
+ Channel channel = request.getChannel();
+ Trace trace = channel.getTrace();
+
+ try {
+ response.close();
+ channel.close();
+ trace.trace(TERMINATE_SOCKET);
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ }
+ }
+}
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusChecker.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusChecker.java
new file mode 100644
index 0000000..1f4f0d7
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusChecker.java
@@ -0,0 +1,220 @@
+/*
+ * StatusChecker.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import static org.simpleframework.http.socket.CloseCode.INTERNAL_SERVER_ERROR;
+import static org.simpleframework.http.socket.CloseCode.NORMAL_CLOSURE;
+import static org.simpleframework.http.socket.FrameType.PING;
+import static org.simpleframework.http.socket.service.ServiceEvent.ERROR;
+import static org.simpleframework.http.socket.service.ServiceEvent.PING_EXPIRED;
+import static org.simpleframework.http.socket.service.ServiceEvent.PONG_RECEIVED;
+import static org.simpleframework.http.socket.service.ServiceEvent.WRITE_PING;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.simpleframework.common.thread.Scheduler;
+import org.simpleframework.http.Request;
+import org.simpleframework.http.socket.DataFrame;
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.transport.Channel;
+import org.simpleframework.transport.trace.Trace;
+
+/**
+ * The <code>StatusChecker</code> object is used to perform health
+ * checks on connected sessions. Health is determined using the ping
+ * pong protocol defined in RFC 6455. The ping pong protocol requires
+ * that any endpoint must respond to a ping control frame with a pong
+ * control frame containing the same payload. This session checker
+ * will send out out ping controls frames and wait for a pong frame.
+ * If it does not receive a pong frame after a configured expiry time
+ * then it will close the associated session.
+ *
+ * @author Niall Gallagher
+ */
+class StatusChecker implements Runnable{
+
+ /**
+ * This is used to perform the monitoring of the sessions.
+ */
+ private final StatusResultListener listener;
+
+ /**
+ * This is the WebSocket this this pinger will be monitoring.
+ */
+ private final FrameConnection connection;
+
+ /**
+ * This is the shared scheduler used to execute this checker.
+ */
+ private final Scheduler scheduler;
+
+ /**
+ * This is a count of the number of unacknowledged ping frames.
+ */
+ private final AtomicLong counter;
+
+ /**
+ * This is the underling TCP channel that is being checked.
+ */
+ private final Channel channel;
+
+ /**
+ * The only reason for a close is for an unexpected error.
+ */
+ private final Reason normal;
+
+ /**
+ * The only reason for a close is for an unexpected error.
+ */
+ private final Reason error;
+
+ /**
+ * This is used to trace various events for this pinger.
+ */
+ private final Trace trace;
+
+ /**
+ * This is the frame that contains the ping to send.
+ */
+ private final Frame frame;
+
+ /**
+ * This is the frequency with which the checker should run.
+ */
+ private final long frequency;
+
+ /**
+ * Constructor for the <code>StatusChecker</code> object. This
+ * is used to create a pinger that will send out ping frames at
+ * a specified interval. If a session does not respond within
+ * three times the duration of the ping the connection is reset.
+ *
+ * @param connection this is the WebSocket to send the frames
+ * @param request this is the associated request
+ * @param scheduler this is the scheduler used to execute this
+ * @param frequency this is the frequency with which to ping
+ */
+ public StatusChecker(FrameConnection connection, Request request, Scheduler scheduler, long frequency) {
+ this.listener = new StatusResultListener(this);
+ this.error = new Reason(INTERNAL_SERVER_ERROR);
+ this.normal = new Reason(NORMAL_CLOSURE);
+ this.frame = new DataFrame(PING);
+ this.counter = new AtomicLong();
+ this.channel = request.getChannel();
+ this.trace = channel.getTrace();
+ this.connection = connection;
+ this.scheduler = scheduler;
+ this.frequency = frequency;
+ }
+
+ /**
+ * This is used to kick of the status checking. Here an initial
+ * ping is sent over the socket and the task is then scheduled to
+ * check the result after the frequency period has expired. If
+ * this method fails for any reason the TCP channel is closed.
+ */
+ public void start() {
+ try {
+ connection.register(listener);
+ trace.trace(WRITE_PING);
+ connection.send(frame);
+ counter.getAndIncrement();
+ scheduler.execute(this, frequency);
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+
+ /**
+ * This method is used to check to see if a session has expired.
+ * If there have been three unacknowledged ping events then this
+ * will force a closure of the WebSocket connection. This is done
+ * to ensure only healthy connections are maintained within the
+ * server, also RFC 6455 recommends using the ping pong protocol.
+ */
+ public void run() {
+ long count = counter.get();
+
+ try {
+ if(count < 3) {
+ trace.trace(WRITE_PING);
+ connection.send(frame);
+ counter.getAndIncrement();
+ scheduler.execute(this, frequency); // schedule the next one
+ } else {
+ trace.trace(PING_EXPIRED);
+ connection.close(normal);
+ }
+ } catch (Exception cause) {
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+
+ /**
+ * If the connection gets a response to its ping message then this
+ * will reset the internal counter. This ensure that the connection
+ * does not time out. If after three pings there is not response
+ * from the other side then the connection will be terminated.
+ */
+ public void refresh() {
+ try {
+ trace.trace(PONG_RECEIVED);
+ counter.set(0);
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+
+ /**
+ * This is used to close the session and send a 1011 close code
+ * to the client indicating an internal server error. Closing
+ * of the session in this manner only occurs if there is an
+ * expiry of the session or an I/O error, both of which are
+ * unexpected and violate the behaviour as defined in RFC 6455.
+ */
+ public void failure() {
+ try {
+ connection.close(error);
+ channel.close();
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+
+ /**
+ * This is used to close the session and send a 1000 close code
+ * to the client indicating a normal closure. This will be called
+ * when there is a close notification dispatched to the status
+ * listener. Typically here a graceful closure is best.
+ */
+ public void close() {
+ try {
+ connection.close(normal);
+ channel.close();
+ } catch(Exception cause) {
+ trace.trace(ERROR, cause);
+ channel.close();
+ }
+ }
+} \ No newline at end of file
diff --git a/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusResultListener.java b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusResultListener.java
new file mode 100644
index 0000000..2b2a049
--- /dev/null
+++ b/simple/simple-http/src/main/java/org/simpleframework/http/socket/service/StatusResultListener.java
@@ -0,0 +1,93 @@
+/*
+ * StatusResultListener.java February 2014
+ *
+ * Copyright (C) 2014, 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.socket.service;
+
+import org.simpleframework.http.socket.Frame;
+import org.simpleframework.http.socket.FrameListener;
+import org.simpleframework.http.socket.FrameType;
+import org.simpleframework.http.socket.Reason;
+import org.simpleframework.http.socket.Session;
+
+/**
+ * The <code>StatusResultListener</code> is used to listen for responses
+ * to ping frames sent out by the server. A response to the ping frame
+ * is a pong frame. When a pong is received it allows the session to
+ * be scheduled to receive another ping.
+ *
+ * @author Niall Gallagher
+ */
+class StatusResultListener implements FrameListener {
+
+ /**
+ * This is used to ping sessions to check for health.
+ */
+ private final StatusChecker checker;
+
+ /**
+ * Constructor for the <code>StatusResultListener</code> object.
+ * This requires the session health checker that performs the pings
+ * so that it can reschedule the session for multiple pings if
+ * the connection responds with a pong.
+ *
+ * @param checker this is the session health checker
+ */
+ public StatusResultListener(StatusChecker checker) {
+ this.checker = checker;
+ }
+
+ /**
+ * This is called when a new frame arrives on the WebSocket. If
+ * the frame is a pong then this will reschedule the the session
+ * to receive another ping frame.
+ *
+ * @param session this is the associated session
+ * @param frame this is the frame that has been received
+ */
+ public void onFrame(Session session, Frame frame) {
+ FrameType type = frame.getType();
+
+ if(type.isPong()) {
+ checker.refresh();
+ }
+ }
+
+ /**
+ * This is called when there is an error with the connection.
+ * When called the session is removed from the checker and no
+ * more ping frames are sent.
+ *
+ * @param session this is the associated session
+ * @param cause this is the cause of the error
+ */
+ public void onError(Session session, Exception cause) {
+ checker.failure();
+ }
+
+ /**
+ * This is called when the connection is closed from the other
+ * side. When called the session is removed from the checker
+ * and no more ping frames are sent.
+ *
+ * @param session this is the associated session
+ * @param reason this is the reason the connection was closed
+ */
+ public void onClose(Session session, Reason reason) {
+ checker.close();
+ }
+} \ No newline at end of file