simple-http-4.1.21/0000775000175000017500000000000011767603362014534 5ustar jamespagejamespagesimple-http-4.1.21/src/0000755000175000017500000000000011417313372015310 5ustar jamespagejamespagesimple-http-4.1.21/src/org/0000755000175000017500000000000011417313372016077 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/0000755000175000017500000000000011417313373021307 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/http/0000755000175000017500000000000011767603362022276 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/http/Form.java0000644000175000017500000000400011417313373024026 0ustar jamespagejamespage/* * Form.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Form interface is used to represent the details * submitted with a request. Typically this will be parameters given * by a HTML form, however a form can also contain parts. Each part * can represent either a file or a parameter. All parts can be * acquired as Part objects from this Form. * * @author Niall Gallagher */ public interface Form extends Query { /** * This method is used to acquire a Part from the * form 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 are added to the query values * as strings so that they can be used in a convenient way. * * @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); /** * This method provides all parts for this Form. The * parts for a form can contain text parameters or files. Each file * part can contain headers, which take the form of HTTP headers to * describe the payload. Typically headers describe the content. * * @return this returns a list of parts for this form */ public List getParts(); }simple-http-4.1.21/src/org/simpleframework/http/Request.java0000644000175000017500000002112711417313373024564 0ustar jamespagejamespage/* * Request.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.Map; import org.simpleframework.http.session.Session; import org.simpleframework.util.lease.LeaseException; /** * The Request 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. *

* This can also maintain data during the request lifecycle as well * as the session lifecycle. A Session 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. *

* It is important to note that the entity body can be read multiple * times from the request. Calling getInputStream 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 */ public boolean isSecure(); /** * This is a convenience method that is used to determine whether * or not this message has the Connection: close * header. If the close token is present then this stream is not * a keep-alive connection. 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 * is not keep-alive by default. * * @return returns true if this has a keep-alive stream */ public boolean isKeepAlive(); /** * This is used to acquire all the form parameters from the * HTTP request. This includes the query and POST data values * as well as the parts of a multipart request. The form is * a convenience object enabling easy access to state. * * @return this returns the form containing the state */ public Form getForm() throws IOException; /** * This is used to provide quick access to the parameters. This * avoids having to acquire the request Form object. * This basically acquires the parameters object and invokes * the getParameters method with the given name. * * @param name this is the name of the parameter value */ public String getParameter(String name) throws IOException; /** * This method is used to acquire a Part from the * form 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 are added to the query values * as strings so that they can be used in a convenient way. * * @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) throws IOException; /** * 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(); /** * This is used as a shortcut for acquiring attributes for the * response. This avoids acquiring the attribute Map * 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); /** * 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(); /** * 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 */ public String getContent() throws IOException; /** * This is used to read the content body. The specifics of the data * that is read from this InputStream can be determined * by the getContentLength 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 Part objects * however the raw content of the multipart body is still available. * * @return this returns an input stream containing the message body */ public InputStream getInputStream() throws IOException; /** * This is used to read the content body. The specifics of the data * that is read from this ReadableByteChannel can be * determined by the getContentLength 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; /** * This method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @return returns an active session for the associated client */ public Session getSession() throws LeaseException; /** * This method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @param create creates the session if it does not exist * * @return returns an active session for the associated client */ public Session getSession(boolean create) throws LeaseException; } simple-http-4.1.21/src/org/simpleframework/http/Address.java0000644000175000017500000001430011417313373024514 0ustar jamespagejamespage/* * Address.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.KeyMap; /** * The Address 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 *

 *
 *    URI = [scheme "://"] host [ ":" port ] [ path [ "?" query ]]
 *
 * 
* 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. *

* 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 http://domain/path is * a URI that is intended for the http protocol. The * scheme is the string http. * * @return the scheme tag for the address if available */ public String getScheme(); /** * This is used to retrieve the domain of this URI. The * domain part in the URI is an optional part, an example * http://domain/path?querypart. 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 */ public 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 http://host:port/path?querypart. This * will return the value of the port. If there is no port then * this will return -1 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 */ public 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 /. * * @return the path part of the uniform resource identifier */ public 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 Query object. The query is * an optional member of a URI and comes after the path part, it * is preceded by a question mark, ? character. * For example the following URI contains query for * its query part, http://host:port/path?query. *

* This returns a org.simpleframework.http.Query * object that can be used to interact directly with the query * values. The Query object is a read-only interface * to the query parameters, and so will not affect the URI. * * @return a Query object for the query part */ public 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 Map instance. *

* 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 http://domain/path1;x=y/path2;x=z * the value for the parameter named x would be * z. * * @return this will return the parameter names found in the URI */ public KeyMap getParameters(); /** * This is used to convert this URI object into a String * 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 *

    * scheme://host:port/path/path?querypart
    * 
*

* It can return /path/path?querypart 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. *

    * scheme://host:port/?querypart
    * 
* * @return the URI address with the optional parts if available */ public String toString(); } simple-http-4.1.21/src/org/simpleframework/http/ResponseHeader.java0000644000175000017500000002646311417313373026053 0ustar jamespagejamespage/* * Response.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 ResponseHeader 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 StatusLine object, which exposes the * protocol version and response status code. *

* 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 */ public List 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 getValue 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 add(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 getInteger 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 add(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 */ public 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 getValue in combination with the get methods. * This will perform a remove 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 set(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 getValue in combination with the get methods. * This will perform a remove 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 set(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 remove 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); /** * This is used to remove the named header from the response. This * removes all header values assigned to the specified name. If it * does not exist then this will return without modifying the HTTP * response. Headers names removed are case insensitive. * * @param name the HTTP message header to remove from the response */ public void remove(String name); /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(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 * * @return this returns the value that the HTTP message header */ public 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 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); /** * 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); /** * 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 substring and trim calls. *

* 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. *

* 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 getValues(String name); /** * The setCookie 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); /** * The setCookie 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); /** * This returns the Cookie object stored under the * specified name. This is used to retrieve cookies that have been * set with the setCookie 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 by the given name */ public Cookie getCookie(String name); /** * This returns all Cookie objects stored under the * specified name. This is used to retrieve cookies that have been * set with the setCookie methods. If there are no * cookies then this will return an empty list. * * @return returns all the Cookie in the response */ public List 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 Content-Type 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(); /** * 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 Transfer-Encoding 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(); /** * This is a convenience method that can be used to determine * the length of the message body. This will determine if there * is a Content-Length 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 int getContentLength(); } simple-http-4.1.21/src/org/simpleframework/http/RequestLine.java0000644000175000017500000000657511417313373025406 0ustar jamespagejamespage/* * RequestLine.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 RequestLine 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 */ public 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 */ public 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 */ public Address getAddress(); /** * This is used to acquire the path as extracted from the HTTP * request URI. The Path 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(); /** * 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(); /** * 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(); /** * 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(); } simple-http-4.1.21/src/org/simpleframework/http/Status.java0000644000175000017500000002045411417313373024421 0ustar jamespagejamespage/* * Status.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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 Status 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. *

* 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 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. */ private final String description; /** * This is the code for the status that this instance represents. */ private final int code; /** * Constructor for the Status 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 instance */ public static String getDescription(int code) { Status[] list = values(); for(Status status : list) { if(status.code == code) return status.description; } return "Unknown"; } } simple-http-4.1.21/src/org/simpleframework/http/StatusLine.java0000644000175000017500000000635111417313373025231 0ustar jamespagejamespage/* * StatusLine.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 StatusLine 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 */ public 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 */ public 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 */ public String getText(); /** * 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 setText(String text); /** * 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 */ public 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 */ public 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 */ public 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 */ public void setMinor(int minor); } simple-http-4.1.21/src/org/simpleframework/http/ResponseWrapper.java0000644000175000017500000006253511417313373026303 0ustar jamespagejamespage/* * ResponseWrapper.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 ResponseWrapper object is used so that the original * Response 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 Response object in a concurrent environment. *

 *
 *    public void handle(Request req, Response resp) {
 *       handler.handle(req, new ZipResponse(resp));
 *    }
 *
 * 
* The above is an example of how the ResponseWrapper can * be used to provide extra functionality to a Response * 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 Container 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 ResponseWrapper object. This allows * the original Response 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 getText() { return response.getText(); } /** * 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 setText(String text) { response.setText(text); } /** * 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 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 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 getValue 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 add(String name, String value) { response.add(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 getInteger 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 add(String name, int value) { response.add(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 getValue in combination with the get methods. * This will perform a remove 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 set(String name, String value) { response.set(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 getValue in combination with the get methods. * This will perform a remove 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 set(String name, int value) { response.set(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 remove 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 is used to remove the named header from the response. This * removes all header values assigned to the specified name. If it * does not exist then this will return without modifying the HTTP * response. Headers names removed are case insensitive. * * @param name the HTTP message header to remove from the response */ public void remove(String name) { response.remove(name); } /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(String name) { return response.contains(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 * * @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 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 substring and trim calls. *

* 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. *

* 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 getValues(String name) { return response.getValues(name); } /** * The setCookie 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 setCookie 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 Cookie object stored under the * specified name. This is used to retrieve cookies that have been * set with the setCookie 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 Cookie objects stored under the * specified name. This is used to retrieve cookies that have been * set with the setCookie methods. If there are no * cookies then this will return an empty list. * * @return returns all the cookie objects for this response */ public List 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 Content-Type 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 Transfer-Encoding 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 Content-Length 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 int 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). *

* 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 OutputStream.close is used. * * @param length this is the length of the HTTP message body */ public void setContentLength(int length) { response.setContentLength(length); } /** * Used to write a message body with the Response. The * semantics of this OutputStream will be determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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 OutputStream 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 Response. The * semantics of this OutputStream will be determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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 OutputStream issued must be thread safe so that * it can be used in a concurrent environment. *

* This will ensure that there is buffering done so that the output * can be reset using the reset 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 print methods provided by * the PrintStream. This will basically wrap the * getOutputStream with a buffer size of zero. *

* The retrieved PrintStream 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 text/plain; charset=UTF-8 the * resulting PrintStream 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. *

* Implementations of the Response must guarantee * that this can be invoked repeatedly without effecting any issued * OutputStream or PrintStream 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 print methods provided by * the PrintStream. This will basically wrap the * getOutputStream with a specified buffer size. *

* The retrieved PrintStream 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 text/plain; charset=UTF-8 the * resulting PrintStream 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. *

* Implementations of the Response must guarantee * that this can be invoked repeatedly without effecting any issued * OutputStream or PrintStream 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 Response. The * semantics of this WritableByteChannel are determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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 Response. The * semantics of this WritableByteChannel are determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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. *

* This will ensure that there is buffering done so that the output * can be reset using the reset 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 can be used to determine whether the Response * has been committed. This is true if the Response * was committed, either due to an explicit invocation of the * commit method or due to the writing of content. If * the Response has committed the reset * 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 * Response. Any further attempts to give headers * to the Response will be futile as only the headers * that were given at the time of the first commit will be used * in the message header. *

* 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 Response * has been committed. This is true if the Response * was committed, either due to an explicit invocation of the * commit method or due to the writing of content. If * the Response has committed the reset * 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(); } } simple-http-4.1.21/src/org/simpleframework/http/Principal.java0000644000175000017500000000306511417313373025056 0ustar jamespagejamespage/* * Principal.java November 2002 * * Copyright (C) 2002, Niall Gallagher * * 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 Principal interface is used to describe a * user that has a name and password. This should not be * confused with the java.security.Principal * interface which does not provide getPassword. * * @author Niall Gallagher */ public interface Principal { /** * The getPassword 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 */ public String getPassword(); /** * The getName 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 */ public String getName(); } simple-http-4.1.21/src/org/simpleframework/http/resource/0000755000175000017500000000000011767603362024125 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/http/resource/Context.java0000644000175000017500000002500611417313373026407 0ustar jamespagejamespage/* * Context.java March 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.resource; import java.io.File; import org.simpleframework.http.Path; /** * The Context interface is used to give a view of the * file system to the ResourceEngine. This provides the * information to the ResourceEngine that it needs in * order to serve content to the client browser. This provides the * path translations for the HTTP request URI. *

* This object essentially provides a mechanism that allows the file * engine to convert the HTTP request URI into OS system paths and * system objects such as the File object. A context * is rooted a a certain directory in the system. This directory is * where the resources are gathered from. For example suppose that * a Context implementation is rooted at the directory * "c:\web\html\" on a DOS system. Now if the target of the browser * was "http://some.host/web/pub/README". The context needs to be * consulted to convert "/web/pub/README" into the real path within * the system. So Context.getRealPath is invoked with * the path "/web/pub/README", which responds with the system path * "c:\web\html\web\pub\README". Also if this was a UNIX system * with the same context rooted at "/home/user/html" then the same * URL would result in "/home/user/html/web/pub/README". *

* The meaning of HTTP URI in this instance is the request URI * from a HTTP/x.x request, as RFC 2616 and RFC 2396 defines it * *

 
 * Request-Line = Method SP Request-URI SP HTTP-Version CRLF
 *
 * Request-URI = "*" | absoluteURI | abs_path | authority
 * absoluteURI = "http:" "//" host [":" port] [abs_path ["?" query]] 
 * abs_path = "/" path_segments         
 * path_segments = segment *( "/" segment )
 * 
* * So the Context object must be prepared to accept * the request URI that come in the form outlined above. These can * include formats like * *
 
 * http://some.host/pub;param=value/bin/index.html?name=value
 * http://some.host:8080/index.en_US.html
 * some.host:8080/index.html
 * /usr/bin;param=value/README.txt
 * /usr/bin/compress.tar.gz
 * 
* * The Context implementation should be able to * directly take a Request-URI as defined in RFC 2616 and translate * this into a path compatible with the OS specific file system. * This keeps the objects semantics simple and explicit, although * at the expense of performance. * * @author Niall Gallagher * * @see org.simpleframework.http.parse.AddressParser * @see org.simpleframework.http.parse.PathParser */ public interface Context { /** * This is used to retrieve the base path of the context. The * base path of the context is that path that that this will * retrieve system information from. This represents a base * that the request URI paths are served from on the system. * For instance a base of "c:\path" would translate a URI * path of "/index.html" into "c:\path\index.html". Every * resource request must be relative to the context path * this allows the ResourceEngine to map the URIs * onto the specific OS. The base path is the OS file system * specific path. So on UNIX it could be "/home/user/" and * on a DOS system it could be "c:\web\html" for example. * * @return this returns the base path of the context */ public String getBasePath(); /** * This is used to translate the HTTP request URI into the OS * specific path that it represents. This will convert the * URI to a format that the system can use and also represents * the resource path on that system. So if for example the * context path was "c:\path" on a DOS system and the HTTP URI * given was "/index.html" this returns "c:\path\index.html". * If a UNIX system was running the VM and the context base * was for example "/home/" then this would return the UNIX * path "/home/index.html" for the same request URI. * * @param target this is the HTTP request URI path that is to * be translated into the OS specific path * * @return this returns the OS specific path name for the * translate request URI */ public String getRealPath(String target); /** * This is used to translate the HTTP request URI into the URI * path normalized and without query or parameter parts. This * is used so that the resource requested by the client can be * discovered. For example this will convert the HTTP request * URI "http://hostname/bin;param=value/../index.html?query" * into the relative URI path /index.html. This is useful if * a logging mechanism requires the name of the resource that * was requested, it can also be used help find the resource. * * @param target this is the HTTP request URI that is to be * converted into a normalized relative URI path * * @return the HTTP request URI as a normalized relative path */ public String getRequestPath(String target); /** * This is used to translate the HTTP request URI into the * Path object that it represents. This enables the * HTTP request URI to be examined thoroughly an allows various * other files to be examined relative to it. For example if the * URI referenced a path "/usr/bin/file" and some resource * in the same directory is required then the Path * can be used to acquire the relative path. This is useful if * links within a HTML page are to be dynamically generated. The * Path.getRelative provides this functionality. * * @param target this is the HTTP request URI path that is used * to retrieve the Path object * * @return returns the Path for the given path */ public Path getPath(String target); /** * This is used to translate the HTTP request URI into the * File object that it represents. This will convert * the URI to a format that the system can use and then create * the File object for that path. So if for example * the context path was "c:\path" on a DOS system and the HTTP * URI given was "/index.html" this returns the File * "c:\path\index.html". This is basically for convenience as the * same could be achieved using the getRealPath and * then creating the File from that OS specific path. * * @param target this is the HTTP request URI path that is used * to retrieve the File object * * @return returns the File for the given path */ public File getFile(String target); /** * This is used to translate the HTTP request URI into the * File object that it represent the parent directory * of the URI. This will convert the URI to a format that the host * system can use and then create the File object for * that path. So if for example the context path was "c:\path" on * a DOS system and the HTTP URI given was "/index.html" this * returns the File "c:\path\". This is basically * for convenience as the same could be achieved using the file * retrieved from getFile and acquiring the parent. * * @param target this is the HTTP request URI path that is used * to retrieve the File object * * @return returns the File for the directory */ public File getDirectory(String target); /** * This method will extract the type attribute of this URI. The * MIME type of the request URI is extracted from the name of the * target. The name for the Context is the last path * segment is the token defined by RFC 2396 as path_segments. So * for example if the target was "some.host:8080/bin/index.html" * then the name for that resource would be "index.html". Once * the name has been extracted the MIME is defined by the file * extension which, for the example is text/html. *

* Implementations of the Context may also choose to * implement a method that consults the underlying resource and * inspect its contents to determine its MIME type. Or for a MAC * it may contain its MIME type. If the MIME type cannot be found * by any of the above methods RFC 2616 suggests that the resource * be given the MIME type application/octetstream. This should also * make not predictions as to how the file will be served. * * @param target the request URI to be parsed for its type * * @return the type of the file this path refers to */ public String getContentType(String target); /** * This will parse and return the file name that this request URI * references. The name for the Context is the last * path segment is the token defined by RFC 2396 as path_segments. * So for example if the target was "some.host:8080/home/user/" * then the name for that resource would be "user". If the path * references the root path "/" then null should be returned. * * @param target the request URI to be parsed for its name * * @return this will return the name that this references */ public String getName(String target); /** * This is an all in one method that allows all the information * on the target URI to be gathered at once. The motivation for * this method is primarily convenience. However it is also used * to increase the performance of the ResourceEngine * when the Context implementation is synchronized. * This will enable the ResourceEngine to gather the * information on the target by acquiring the lock for the object * instance only once. * * @param target this is the request URI that is to be parsed */ public Index getIndex(String target); } simple-http-4.1.21/src/org/simpleframework/http/resource/ResourceEngine.java0000644000175000017500000000523111417313373027676 0ustar jamespagejamespage/* * ResourceEngine.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.resource; import org.simpleframework.http.Address; /** * The ResourceEngine is used to create implementations * of the Resource interface that suit the targeted * resource. Different Resource objects may be needed to * handle different files/directories or even applications. The request * URI specified must be a HTTP request URI as of RFC 2616. *

* The meaning of HTTP URI in this instance is the request URI * from a HTTP/x.x request, as RFC 2616 and RFC 2396 defines it * *

 
 * Request-Line = Method SP Request-URI SP HTTP-Version CRLF
 *
 * Request-URI = "*" | absoluteURI | abs_path | authority
 * absoluteURI = "http:" "//" host [":" port] [abs_path ["?" query]] 
 * abs_path = "/" path_segments         
 * path_segments = segment *( "/" segment )
 * 
* * The ResourceEngine object must be prepared to accept * the request URI that come in the form outlined above. These can * include formats like * *
 
 * http://some.host/pub;param=value/bin/index.html?name=value
 * http://some.host:8080/index.en_US.html
 * some.host:8080/index.html
 * /usr/bin;param=value/README.txt
 * /usr/bin/compress.tar.gz
 * 
* * The ResourceEngine implementation should be able to * directly take a Request-URI as defined in RFC 2616 and translate * this into a Resource. This keeps the objects semantics * simple and explicit, although at the expense of performance. * * @author Niall Gallagher */ public interface ResourceEngine { /** * This will look for and retrieve the requested resource. The * target given must be in the form of a request URI. This will * locate the resource and return the Resource * implementation that will handle the target. * * @param target the address used to identify the resource * * @return this returns the resource used to handle the request */ public Resource resolve(Address target); } simple-http-4.1.21/src/org/simpleframework/http/resource/Resource.java0000644000175000017500000000400011417313373026541 0ustar jamespagejamespage/* * Resource.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.resource; import org.simpleframework.http.Response; import org.simpleframework.http.Request; /** * A Resource provides an abstraction of any given * object that can be retrieved using a HTTP request. The reason * for having this abstraction is to simplify the interface with * any given Resource. *

* This is similar in design to a Container, however * this is intended to handle a single resource. At any time a * container may manage many resources all of which are resolved * using a ResourceEngine implementation. So in * essence this is used to identify a component that can handle * a HTTP request routed by a resource engine. * * @author Niall Gallagher */ public interface Resource { /** * This acts as the main processing method for the resources. * Implementations are required to provide the functions that * will process the Request and generate a suitable * response for that request. This method is also responsible * for closing and comitting the Response unless * handed (chained) to another Resource. * * @param req the Request to be processed * @param resp the Response to be processed */ public void handle(Request req, Response resp); } simple-http-4.1.21/src/org/simpleframework/http/resource/FileContext.java0000644000175000017500000002371211417313373027211 0ustar jamespagejamespage/* * FileContext.java March 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.resource; import java.io.File; import org.simpleframework.http.Path; /** * The FileContext provides an implementation of the * Context object that provides a direct mapping from * a request URI as defined in RFC 2616 to an OS specific target. * This uses a File object to define the mapping * for the request URI paths. Using a File object * allows the FileContext to be easily used with both * DOS and UNIX systems. *

* This Indexer implementation uses an MIME database * to obtain mappings for the getContentType method. * The file used is acquired from the class path as a mapping from * file extension to MIME type. This file can be modified if any * additional types are required. However it is more advisable to * simple extend this object and override the content type method. * * @author Niall Gallagher * * @see org.simpleframework.http.resource.FileIndexer */ public class FileContext implements Context { /** * This is used to extract any user specified MIME types. */ private final FileIndexer indexer; /** * This will be used to fetch the real OS system paths. */ private final File base; /** * Constructor for creating an instance that operates from * the given current working path. This instance will use * the current path to translate the HTTP request URIs * into the OS specific path. This will load configuration * files from the current working directory. */ public FileContext() { this(new File(".")); } /** * Constructor for creating an instance that operates from * the given OS specific base path. This instance will use * the given base path to translate the HTTP request URIs * into the OS specific path. This will load configuration * files from the specified directory path. * * @param base this is the OS specific base path for this */ public FileContext(File base) { this.indexer = new FileIndexer(base); this.base = base; } /** * This is used to retrieve the base path of the context. The * base path of the context is that path that that this will * retrieve system information from. This represents a base * that the request URI paths are served from on the system. * For instance a base of "c:\path" would translate a URI * path of "/index.html" into "c:\path\index.html". Every * resource request must be relative to the context path * this allows the FileEngine to map the URIs * onto the specific OS. The base path is the OS file system * specific path. So on UNIX it could be "/home/user/" and * on a DOS system it could be "c:\web\html" for example. * * @return this returns the base path of the context */ public String getBasePath() { return base.getAbsolutePath(); } /** * This is used to translate the HTTP request URI into the OS * specific path that it represents. This will convert the * URI to a format that the system can use and also represents * the resource path on that system. So if for example the * context path was "c:\path" on a DOS system and the HTTP URI * given was "/index.html" this returns "c:\path\index.html". * If a UNIX system was running the VM and the context base * was for example "/home/" then this would return the UNIX * path "/home/index.html" for the same request URI. * * @param target this is the HTTP request URI path that is to * be translated into the OS specific path * * @return this returns the OS specific path name for the * translate request URI */ public String getRealPath(String target){ return getIndex(target).getRealPath(); } /** * This is used to translate the HTTP request URI into the URI * path normalized and without query or parameter parts. This * is used so that the resource requested by the client can be * discovered. For example this will convert the HTTP request * URI "http://hostname/bin;param=value/../index.html?query" * into the relative URI path /index.html. This is useful if * a logging mechanism requires the name of the resource that * was requested, it can also be used help find the resource. * * @param target this is the HTTP request URI that is to be * converted into a normalized relative URI path * * @return the HTTP request URI as a normalized relative path */ public String getRequestPath(String target){ return getIndex(target).getRequestPath(); } /** * This is used to translate the HTTP request URI into the * File object that it represents. This will convert * the URI to a format that the system can use and then create * the File object for that path. So if for example * the context path was "c:\path" on a DOS system and the HTTP * URI given was "/index.html" this returns the File * "c:\path\index.html". This is basically for convenience as the * same could be achieved using the getRealPath and * then creating the File from that OS specific path. * * @param target this is the HTTP request URI path that is used * to retrieve the File object * * @return returns the File for the given path */ public File getFile(String target) { return getIndex(target).getFile(); } /** * This is used to translate the HTTP request URI into the * File object that it represent the parent directory * of the URI. This will convert the URI to a format that the host * system can use and then create the File object for * that path. So if for example the context path was "c:\path" on * a DOS system and the HTTP URI given was "/index.html" this * returns the File "c:\path\". This is basically * for convenience as the same could be achieved using the file * retrieved from getFile and acquiring the parent. * * @param target this is the HTTP request URI path that is used * to retrieve the File object * * @return returns the File for the directory */ public File getDirectory(String target) { return getIndex(target).getDirectory(); } /** * This is used to translate the HTTP request URI into the * Path object that it represents. This enables the * HTTP request URI to be examined thoroughly an allows various * other files to be examined relative to it. For example if the * URI referenced a path "/usr/bin/file" and some resource * in the same directory is required then the Path * can be used to acquire the relative path. This is useful if * links within a HTML page are to be dynamically generated. The * Path.getRelative provides this functionality. * * @param target this is the HTTP request URI path that is used * to retrieve the Path object * * @return returns the Path for the given path */ public Path getPath(String target){ return getIndex(target).getPath(); } /** * This method will extract the type attribute of this URI. The * MIME type of the request URI is extracted from the name of the * target. The name for the Context is the last path * segment in the token defined by RFC 2396 as path_segments. So * for example if the target was "some.host:8080/bin/index.html" * then the name for that resource would be "index.html". Once * the name has been extracted the MIME is defined by the file * extension, which for the example is text/html. * * @param target the request URI to be parsed for its type * * @return the type of the given request URI path refers to */ public String getContentType(String target){ return getIndex(target).getContentType(); } /** * This will parse and return the file name that this request URI * references. The name for the Context is the last * path segment is the token defined by RFC 2396 as path_segments. * So for example if the target was "some.host:8080/home/user/" * then the name for that resource would be "user". If the path * references the root path "/" then null should be returned. * * @param target the request URI to be parsed for its name * * @return this will return the name that this references */ public String getName(String target){ return getIndex(target).getName(); } /** * This is an all in one method that allows all the information * on the target URI to be gathered at once. The motivation for * this method is primarily convenience. However it is also used * to increase the performance of the FileEngine * when the Context implementation is synchronized. * This will enable the FileEngine to gather the * information on the target by acquiring the lock for the object * instance only once. * * @param target this is the request URI that is to be parsed */ public Index getIndex(String target){ return indexer.getIndex(target); } } simple-http-4.1.21/src/org/simpleframework/http/resource/ResourceContainer.java0000644000175000017500000000507211417313372030415 0ustar jamespagejamespage/* * ResourceProcessor.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.resource; import org.simpleframework.http.Response; import org.simpleframework.http.Request; import org.simpleframework.http.core.Container; /** * The ResourceContainer is an implementation of the * Container interface for handling an arbitrary set * of resources. This container will accept any HTTP transaction * and delegate the processing of that transation to a resource. * This resolves the resource to use using an implementation of * the ResourceEngine interface. *

* This provides a very simple means to manage individual targets * using separate resource implementations. It also provides an * ideal location to introduce path mapping functionality. * * @author Niall Gallagher */ public class ResourceContainer implements Container { /** * The engine that resolves the resources to be used. */ private final ResourceEngine engine; /** * Constructor for the ResourceContainer object. * This requires a resource engine which it uses to map the * request targets to a given implementation or instance. * This is essentially a router to the Resource * objects that have been mapped to a given request path. * * @param engine the engine used to resolve resources */ public ResourceContainer(ResourceEngine engine){ this.engine = engine; } /** * This method is where most of the work is done to retrieve * the Resource and process the HTTP request. This * will basically use the resolve method of the * issued ResourceEngine to acquire resources. * * @param req the Request to be processed * @param resp the Response to be processed */ public void handle(Request req, Response resp){ engine.resolve(req.getAddress()).handle(req,resp); } } simple-http-4.1.21/src/org/simpleframework/http/resource/FileIndexer.java0000644000175000017500000002716011417313373027164 0ustar jamespagejamespage/* * FileIndexer.java December 2005 * * Copyright (C) 2005, Niall Gallagher * * 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.resource; import java.io.File; import java.util.MissingResourceException; import java.util.ResourceBundle; import java.util.concurrent.ConcurrentHashMap; import org.simpleframework.http.Address; import org.simpleframework.http.Path; /** * The FileIndexer provides an implementation of the * Indexer object that provides a direct mapping from * a request URI as defined in RFC 2616 to the resources meta data. * This uses a File object to define the mapping * for the request URI paths. Using a File object * allows the FileIndexer to be easily used with both * DOS and UNIX systems. *

* This Indexer implementation uses a MIME database * to obtain mappings for the getContentType method. * The file used is FileIndexer.properties, which is * packaged within org.simpleframework.http.resource. * This determines the MIME type of the request URI by matching file * extension of the resource with the MIME type as defined in the * "FileIndexer.properties" file. * * @author Niall Gallagher * * @see org.simpleframework.http.parse.AddressParser * @see org.simpleframework.http.parse.PathParser */ class FileIndexer implements Indexer { /** * This is used to extract any user specified MIME types. */ private ResourceBundle resolver; /** * This is used to cache the meta information acquired. */ private Cache cache; /** * This will be used to fetch the real OS system paths. */ private File base; /** * Constructor for the FileIndexer object. This is * used to create a centralized store for meta data. The meta * data created by this is acquired from the context frequently, * so in order to improve performance all indexes are cached, * except those URI targets that contain query parameters. * * @param base this is the root of the context that is used */ public FileIndexer(File base) { this.cache = new Cache(); this.base = base; } /** * This is an all in one method that allows all the information * on the target URI to be gathered at once. The motivation for * this method is primarily convenience. However it is also used * to increase the performance of the FileIndexer * by using a cache of the most recently used indexes. This will * help to reduce the amount or parsing and memory required. * * @param target this is the request URI that is to be parsed * * @return this is the index of meta data for the URI target */ public Index getIndex(String target) { Index index = cache.get(target); if(index == null) { index = getIndex(this, target); } return index; } /** * This is an all in one method that allows all the information * on the target URI to be gathered at once. The motivation for * this method is primarily convenience. However it is also used * to increase the performance of the FileIndexer * by using a cache of the most recently used indexes. This will * help to reduce the amount or parsing and memory required. * This is used as a convinience method for caching indexes. * * @param indexer this is typically the current indexer object * @param target this is the request URI that is to be parsed * * @return this is the index of meta data for the URI target */ public Index getIndex(Indexer indexer, String target) { Index index = new FileIndex(indexer, target); if(target.indexOf('?') < 0) { cache.put(target, index); } return index; } /** * This is used to translate the HTTP request URI into the * File object that it represents. This will convert * the URI to a format that the system can use and then create * the File object for that path. So if for example * the context path was "c:\path" on a DOS system and the HTTP * URI given was "/index.html" this returns the File * "c:\path\index.html". This is basically for convenience as the * same could be achieved using the getRealPath and * then creating the File from that OS specific path. * * @param target this is the HTTP request URI path that is used * to retrieve the File object * * @return returns the File for the given path */ public File getFile(Address target) { return getFile(target.getPath()); } /** * This is used to translate the HTTP request URI into the * Path object that it represents. This enables the * HTTP request URI to be examined thoroughly an allows various * other files to be examined relative to it. For example if the * URI referenced a path "/usr/bin/file" and some resource * in the same directory is required then the Path * can be used to acquire the relative path. This is useful if * links within a HTML page are to be dynamically generated. The * Path.getRelative provides this functionality. * * @param target this is the HTTP request URI path that is used * to retrieve the Path object * * @return returns the Path for the given path */ public Path getPath(Address target){ return target.getPath(); } /** * This is used to translate the request URI path into the * File object that it represents. This will convert * the path to a format that the system can use and then create * the File object for that path. So if for example * the context path was "c:\path" on a DOS system and the request * URI given was "/index.html" this returns the File * "c:\path\index.html". This is basically for convenience as the * same could be achieved using the getRealPath and * then creating the File from that OS specific path. * * @param path this is the URI path that is used to retrieve the * File object * * @return returns the File for the given path */ private File getFile(Path path) { String file = path.toString(); if(file != null) { file = file.replace('/', File.separatorChar); } return new File(base, file); } /** * This method will extract the type attribute of this URI. The * MIME type of the request URI is extracted from the name of the * target. The name for the Context is the last path * segment in the token defined by RFC 2396 as path_segments. So * for example if the target was "some.host:8080/bin/index.html" * then the name for that resource would be "index.html". Once * the name has been extracted the MIME is defined by the file * extension, which for the example is text/html. * * @param target the request URI to be parsed for its type * * @return the type of the given request URI path refers to */ public String getContentType(Address target){ return getContentType(target.getPath()); } /** * This method will extract the type attribute of this path. The * MIME type of the request path is extracted from the name of the * target. The name for the Context is the last path * segment in the token defined by RFC 2396 as path_segments. So * for example if the target was "some.host:8080/bin/index.html" * then the name for that resource would be "index.html". Once * the name has been extracted the MIME is defined by the file * extension, which for the example is text/html. * * @param path path that is to have its MIME type determined * * @return the type of the given resource path refers to */ private String getContentType(Path path){ String ext = path.getExtension(); String target = path.getPath(); return getContentType(target, ext); } /** * This method will extract the type attribute of this path. The * MIME type of the request path is extracted from the name of the * target. The name for the Context is the last path * segment is the token defined by RFC 2396 as path_segments. So * for example if the target was "some.host:8080/bin/index.html" * then the name for that resource would be "index.html". Once * the name has been extracted the MIME is defined by the file * extension, which for the example is text/html. * * @param path path that is to have its MIME type determined * @param ext this is the file extension for the given path * * @return the type of the given resource path refers to */ private String getContentType(String path, String ext) { try { ResourceBundle bundle = getBundle(); if(bundle != null) { return bundle.getString(ext); } }catch(MissingResourceException e){ } return "application/octetstream"; } /** * This is used to acquire the resource bundle used to map all of * the MIME types for the indexer. By default this will acquire * a properties file named FileIndexer.properties. If * this file is not found then every content type will be the * default application/octetstream type. * * @return the resource bundle to used for this file indexer */ private ResourceBundle getBundle() { if(resolver == null) { resolver = getClassBundle(); } return resolver; } /** * This is used to acquire the resource bundle used to map all of * the MIME types for the indexer. By default this will acquire * a properties file named FileIndexer.properties. If * this file is not found then every content type will be the * default application/octetstream type. * * @return the resource bundle to used for this file indexer */ private ResourceBundle getClassBundle() { Class type = FileIndexer.class; String name = type.getName(); return ResourceBundle.getBundle(name); } /** * The Cache object essentially acts as a convenient * typedef for caching addresses to their index. This allows the * index to be retrieved much quicker when requested again. Each * address key contains the query parameters and path parameters. * * @author Niall Gallagher */ private class Cache extends ConcurrentHashMap { /** * Constructor for the Cache object. This acts * as a quick means to lookup data on a resource by taking * an unparsed address and mapping that to an index object. */ public Cache() { super(); } } } simple-http-4.1.21/src/org/simpleframework/http/resource/Index.java0000644000175000017500000000743311417313373026036 0ustar jamespagejamespage/* * Index.java December 2005 * * Copyright (C) 2005, Niall Gallagher * * 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.resource; import java.io.File; import org.simpleframework.http.Path; /** * The Index object is used to represent the properties * a URI can contain. This is used so that properties relating to a * file can be quickly extracted from an Indexer. This * will contain all necessary meta data for a file or resource. With * this the File reference to a resource as well as the * locale, MIME type, name and other such data can be accessed. * * @author Niall Gallagher * * @see org.simpleframework.http.resource.Indexer */ public interface Index { /** * This allows the name for this object to be acquired. The * name usually refers to the last entry in the path. So if * the index target path was "/usr/bin/" the name is "bin". * * @return this returns the name of this index target */ public String getName(); /** * This allows the MIME type of this Index to * be acquired. The MIME type of a file is retrieved by the * Context.getContentType method for a specific * request URI. This should have a value and perhaps some * parameters like the charset, "text/html; charset=UTF-8". * * @return the MIME type this object has been set to */ public String getContentType(); /** * This is used to get the path that this object refers to. * This should be the fully qualified normalized path. This * refers to the OS system specific path that this represents. * * @return this returns the OS specific path for the target */ public String getRealPath(); /** * This is used to acquire the normalized URI style path for * the index target. This allows the path to be used within * the Mapper and other such objects that need * a normalized URI style path to resolve resources. * * @return this returns the normalized path for the target */ public String getRequestPath(); /** * This is used to acquire the File directory * for the index target. This is typically rooted at a * base path, for instance the Context root * is typically used. This allows resources within the * same directory to be acquired easily. * * @return this returns the OS file for the directory */ public File getDirectory(); /** * This is used to acquire the File reference * for the index target. This is typically rooted at a * base path, for instance the Context root * is typically used. This allows the file to be opened, * deleted, or read should the need arise in a service. * * @return this returns the OS file for the resource */ public File getFile(); /** * This is used to acquire the Path object that * exposes various parts of the URI path. This can be used * to extract the individual path segments as strings as * well as the file extension and various other details. * * @return this returns a path object with various details */ public Path getPath(); } simple-http-4.1.21/src/org/simpleframework/http/resource/FileIndexer.properties0000644000175000017500000003526711417313373030446 0ustar jamespagejamespage3dm=x-world/x-3dmf 3dmf=x-world/x-3dmf a=application/octet-stream aab=application/x-authorware-bin aam=application/x-authorware-map aas=application/x-authorware-seg abc=text/vnd.abc acgi=text/html afl=video/animaflex ai=application/postscript aif=audio/aiff aif=audio/x-aiff aifc=audio/aiff aifc=audio/x-aiff aiff=audio/aiff aiff=audio/x-aiff aim=application/x-aim aip=text/x-audiosoft-intra ani=application/x-navi-animation aos=application/x-nokia-9000-communicator-add-on-software aps=application/mime arc=application/octet-stream arj=application/arj arj=application/octet-stream art=image/x-jg asf=video/x-ms-asf asm=text/x-asm asp=text/asp asx=application/x-mplayer2 asx=video/x-ms-asf asx=video/x-ms-asf-plugin au=audio/basic au=audio/x-au avi=application/x-troff-msvideo avi=video/avi avi=video/msvideo avi=video/x-msvideo avs=video/avs-video bcpio=application/x-bcpio bin=application/mac-binary bin=application/macbinary bin=application/octet-stream bin=application/x-binary bin=application/x-macbinary bm=image/bmp bmp=image/bmp bmp=image/x-windows-bmp boo=application/book book=application/book boz=application/x-bzip2 bsh=application/x-bsh bz=application/x-bzip bz2=application/x-bzip2 c=text/plain c=text/x-c c++=text/plain cat=application/vnd.ms-pki.seccat cc=text/plain cc=text/x-c ccad=application/clariscad cco=application/x-cocoa cdf=application/cdf cdf=application/x-cdf cdf=application/x-netcdf cer=application/pkix-cert cer=application/x-x509-ca-cert cha=application/x-chat chat=application/x-chat class=application/java class=application/java-byte-code class=application/x-java-class com=application/octet-stream com=text/plain conf=text/plain cpio=application/x-cpio cpp=text/x-c cpt=application/mac-compactpro cpt=application/x-compactpro cpt=application/x-cpt crl=application/pkcs-crl crl=application/pkix-crl crt=application/pkix-cert crt=application/x-x509-ca-cert crt=application/x-x509-user-cert csh=application/x-csh csh=text/x-script.csh css=application/x-pointplus css=text/css cxx=text/plain dcr=application/x-director deepv=application/x-deepv def=text/plain der=application/x-x509-ca-cert dif=video/x-dv dir=application/x-director dl=video/dl dl=video/x-dl doc=application/msword dot=application/msword dp=application/commonground drw=application/drafting dump=application/octet-stream dv=video/x-dv dvi=application/x-dvi dwf=drawing/x-dwf (old) dwf=model/vnd.dwf dwg=application/acad dwg=image/vnd.dwg dwg=image/x-dwg dxf=application/dxf dxf=image/vnd.dwg dxf=image/x-dwg dxr=application/x-director el=text/x-script.elisp elc=application/x-bytecode.elisp (compile elisp) elc=application/x-elc env=application/x-envoy eps=application/postscript es=application/x-esrehber etx=text/x-setext evy=application/envoy evy=application/x-envoy exe=application/octet-stream f=text/plain f=text/x-fortran f77=text/x-fortran f90=text/plain f90=text/x-fortran fdf=application/vnd.fdf fif=application/fractals fif=image/fif fli=video/fli fli=video/x-fli flo=image/florian flx=text/vnd.fmi.flexstor fmf=video/x-atomic3d-feature for=text/plain for=text/x-fortran fpx=image/vnd.fpx fpx=image/vnd.net-fpx frl=application/freeloader funk=audio/make g=text/plain g3=image/g3fax gif=image/gif gl=video/gl gl=video/x-gl gsd=audio/x-gsm gsm=audio/x-gsm gsp=application/x-gsp gss=application/x-gss gtar=application/x-gtar gz=application/x-compressed gz=application/x-gzip gzip=application/x-gzip gzip=multipart/x-gzip h=text/plain h=text/x-h hdf=application/x-hdf help=application/x-helpfile hgl=application/vnd.hp-hpgl hh=text/plain hh=text/x-h hlb=text/x-script hlp=application/hlp hlp=application/x-helpfile hlp=application/x-winhelp hpg=application/vnd.hp-hpgl hpgl=application/vnd.hp-hpgl hqx=application/binhex hqx=application/binhex4 hqx=application/mac-binhex hqx=application/mac-binhex40 hqx=application/x-binhex40 hqx=application/x-mac-binhex40 hta=application/hta htc=text/x-component htm=text/html html=text/html htmls=text/html htt=text/webviewhtml htx=text/html ice=x-conference/x-cooltalk ico=image/x-icon idc=text/plain ief=image/ief iefs=image/ief iges=application/iges iges=model/iges igs=application/iges igs=model/iges ima=application/x-ima imap=application/x-httpd-imap inf=application/inf ins=application/x-internett-signup ip=application/x-ip2 isu=video/x-isvideo it=audio/it iv=application/x-inventor ivr=i-world/i-vrml ivy=application/x-livescreen jam=audio/x-jam jav=text/plain jav=text/x-java-source java=text/plain java=text/x-java-source jcm=application/x-java-commerce jfif=image/jpeg jfif=image/pjpeg jfif-tbnl=image/jpeg jpe=image/jpeg jpe=image/pjpeg jpeg=image/jpeg jpeg=image/pjpeg jpg=image/jpeg jpg=image/pjpeg jps=image/x-jps js=application/x-javascript jut=image/jutvision kar=audio/midi kar=music/x-karaoke ksh=application/x-ksh ksh=text/x-script.ksh la=audio/nspaudio la=audio/x-nspaudio lam=audio/x-liveaudio latex=application/x-latex lha=application/lha lha=application/octet-stream lha=application/x-lha lhx=application/octet-stream list=text/plain lma=audio/nspaudio lma=audio/x-nspaudio log=text/plain lsp=application/x-lisp lsp=text/x-script.lisp lst=text/plain lsx=text/x-la-asf ltx=application/x-latex lzh=application/octet-stream lzh=application/x-lzh lzx=application/lzx lzx=application/octet-stream lzx=application/x-lzx m=text/plain m=text/x-m m1v=video/mpeg m2a=audio/mpeg m2v=video/mpeg m3u=audio/x-mpequrl man=application/x-troff-man map=application/x-navimap mar=text/plain mbd=application/mbedlet mc$=application/x-magic-cap-package-1.0 mcd=application/mcad mcd=application/x-mathcad mcf=image/vasa mcf=text/mcf mcp=application/netmc me=application/x-troff-me mht=message/rfc822 mhtml=message/rfc822 mid=application/x-midi mid=audio/midi mid=audio/x-mid mid=audio/x-midi mid=music/crescendo mid=x-music/x-midi midi=application/x-midi midi=audio/midi midi=audio/x-mid midi=audio/x-midi midi=music/crescendo midi=x-music/x-midi mif=application/x-frame mif=application/x-mif mime=message/rfc822 mime=www/mime mjf=audio/x-vnd.audioexplosion.mjuicemediafile mjpg=video/x-motion-jpeg mm=application/base64 mm=application/x-meme mme=application/base64 mod=audio/mod mod=audio/x-mod moov=video/quicktime mov=video/quicktime movie=video/x-sgi-movie mp2=audio/mpeg mp2=audio/x-mpeg mp2=video/mpeg mp2=video/x-mpeg mp2=video/x-mpeq2a mp3=audio/mpeg3 mp3=audio/x-mpeg-3 mp3=video/mpeg mp3=video/x-mpeg mpa=audio/mpeg mpa=video/mpeg mpc=application/x-project mpe=video/mpeg mpeg=video/mpeg mpg=audio/mpeg mpg=video/mpeg mpga=audio/mpeg mpp=application/vnd.ms-project mpt=application/x-project mpv=application/x-project mpx=application/x-project mrc=application/marc ms=application/x-troff-ms mv=video/x-sgi-movie my=audio/make mzz=application/x-vnd.audioexplosion.mzz nap=image/naplps naplps=image/naplps nc=application/x-netcdf ncm=application/vnd.nokia.configuration-message nif=image/x-niff niff=image/x-niff nix=application/x-mix-transfer nsc=application/x-conference nvd=application/x-navidoc o=application/octet-stream oda=application/oda omc=application/x-omc omcd=application/x-omcdatamaker omcr=application/x-omcregerator p=text/x-pascal p10=application/pkcs10 p10=application/x-pkcs10 p12=application/pkcs-12 p12=application/x-pkcs12 p7a=application/x-pkcs7-signature p7c=application/pkcs7-mime p7c=application/x-pkcs7-mime p7m=application/pkcs7-mime p7m=application/x-pkcs7-mime p7r=application/x-pkcs7-certreqresp p7s=application/pkcs7-signature part=application/pro_eng pas=text/pascal pbm=image/x-portable-bitmap pcl=application/vnd.hp-pcl pcl=application/x-pcl pct=image/x-pict pcx=image/x-pcx pdb=chemical/x-pdb pdf=application/pdf pfunk=audio/make pfunk=audio/make.my.funk pgm=image/x-portable-graymap pgm=image/x-portable-greymap pic=image/pict pict=image/pict pkg=application/x-newton-compatible-pkg pko=application/vnd.ms-pki.pko pl=text/plain pl=text/x-script.perl plx=application/x-pixclscript pm=image/x-xpixmap pm=text/x-script.perl-module pm4=application/x-pagemaker pm5=application/x-pagemaker png=image/png pnm=application/x-portable-anymap pnm=image/x-portable-anymap pot=application/mspowerpoint pot=application/vnd.ms-powerpoint pov=model/x-pov ppa=application/vnd.ms-powerpoint ppm=image/x-portable-pixmap pps=application/mspowerpoint pps=application/vnd.ms-powerpoint ppt=application/mspowerpoint ppt=application/powerpoint ppt=application/vnd.ms-powerpoint ppt=application/x-mspowerpoint ppz=application/mspowerpoint pre=application/x-freelance prt=application/pro_eng ps=application/postscript psd=application/octet-stream pvu=paleovu/x-pv pwz=application/vnd.ms-powerpoint py=text/x-script.phyton pyc=applicaiton/x-bytecode.python qcp=audio/vnd.qcelp qd3=x-world/x-3dmf qd3d=x-world/x-3dmf qif=image/x-quicktime qt=video/quicktime qtc=video/x-qtc qti=image/x-quicktime qtif=image/x-quicktime ra=audio/x-pn-realaudio ra=audio/x-pn-realaudio-plugin ra=audio/x-realaudio ram=audio/x-pn-realaudio ras=application/x-cmu-raster ras=image/cmu-raster ras=image/x-cmu-raster rast=image/cmu-raster rexx=text/x-script.rexx rf=image/vnd.rn-realflash rgb=image/x-rgb rm=application/vnd.rn-realmedia rm=audio/x-pn-realaudio rmi=audio/mid rmm=audio/x-pn-realaudio rmp=audio/x-pn-realaudio rmp=audio/x-pn-realaudio-plugin rng=application/ringing-tones rng=application/vnd.nokia.ringing-tone rnx=application/vnd.rn-realplayer roff=application/x-troff rp=image/vnd.rn-realpix rpm=audio/x-pn-realaudio-plugin rt=text/richtext rt=text/vnd.rn-realtext rtf=application/rtf rtf=application/x-rtf rtf=text/richtext rtx=application/rtf rtx=text/richtext rv=video/vnd.rn-realvideo s=text/x-asm s3m=audio/s3m saveme=application/octet-stream sbk=application/x-tbook scm=application/x-lotusscreencam scm=text/x-script.guile scm=text/x-script.scheme scm=video/x-scm sdml=text/plain sdp=application/sdp sdp=application/x-sdp sdr=application/sounder sea=application/sea sea=application/x-sea set=application/set sgm=text/sgml sgm=text/x-sgml sgml=text/sgml sgml=text/x-sgml sh=application/x-bsh sh=application/x-sh sh=application/x-shar sh=text/x-script.sh shar=application/x-bsh shar=application/x-shar shtml=text/html shtml=text/x-server-parsed-html sid=audio/x-psid sit=application/x-sit sit=application/x-stuffit skd=application/x-koan skm=application/x-koan skp=application/x-koan skt=application/x-koan sl=application/x-seelogo smi=application/smil smil=application/smil snd=audio/basic snd=audio/x-adpcm sol=application/solids spc=application/x-pkcs7-certificates spc=text/x-speech spl=application/futuresplash spr=application/x-sprite sprite=application/x-sprite src=application/x-wais-source ssi=text/x-server-parsed-html ssm=application/streamingmedia sst=application/vnd.ms-pki.certstore step=application/step stl=application/sla stl=application/vnd.ms-pki.stl stl=application/x-navistyle stp=application/step sv4cpio=application/x-sv4cpio sv4crc=application/x-sv4crc svf=image/vnd.dwg svf=image/x-dwg svr=application/x-world svr=x-world/x-svr swf=application/x-shockwave-flash t=application/x-troff talk=text/x-speech tar=application/x-tar tbk=application/toolbook tbk=application/x-tbook tcl=application/x-tcl tcl=text/x-script.tcl tcsh=text/x-script.tcsh tex=application/x-tex texi=application/x-texinfo texinfo=application/x-texinfo text=application/plain text=text/plain tgz=application/gnutar tgz=application/x-compressed tif=image/tiff tif=image/x-tiff tiff=image/tiff tiff=image/x-tiff tr=application/x-troff tsi=audio/tsp-audio tsp=application/dsptype tsp=audio/tsplayer tsv=text/tab-separated-values turbot=image/florian txt=text/plain uil=text/x-uil uni=text/uri-list unis=text/uri-list unv=application/i-deas uri=text/uri-list uris=text/uri-list ustar=application/x-ustar ustar=multipart/x-ustar uu=application/octet-stream uu=text/x-uuencode uue=text/x-uuencode vcd=application/x-cdlink vcs=text/x-vcalendar vda=application/vda vdo=video/vdo vew=application/groupwise viv=video/vivo viv=video/vnd.vivo vivo=video/vivo vivo=video/vnd.vivo vmd=application/vocaltec-media-desc vmf=application/vocaltec-media-file voc=audio/voc voc=audio/x-voc vos=video/vosaic vox=audio/voxware vqe=audio/x-twinvq-plugin vqf=audio/x-twinvq vql=audio/x-twinvq-plugin vrml=application/x-vrml vrml=model/vrml vrml=x-world/x-vrml vrt=x-world/x-vrt vsd=application/x-visio vst=application/x-visio vsw=application/x-visio w60=application/wordperfect6.0 w61=application/wordperfect6.1 w6w=application/msword wav=audio/wav wav=audio/x-wav wb1=application/x-qpro wbmp=image/vnd.wap.wbmp web=application/vnd.xara wiz=application/msword wk1=application/x-123 wmf=windows/metafile wml=text/vnd.wap.wml wmlc=application/vnd.wap.wmlc wmls=text/vnd.wap.wmlscript wmlsc=application/vnd.wap.wmlscriptc word=application/msword wp=application/wordperfect wp5=application/wordperfect wp5=application/wordperfect6.0 wp6=application/wordperfect wpd=application/wordperfect wpd=application/x-wpwin wq1=application/x-lotus wri=application/mswrite wri=application/x-wri wrl=application/x-world wrl=model/vrml wrl=x-world/x-vrml wrz=model/vrml wrz=x-world/x-vrml wsc=text/scriplet wsrc=application/x-wais-source wtk=application/x-wintalk xbm=image/x-xbitmap xbm=image/x-xbm xbm=image/xbm xdr=video/x-amt-demorun xgz=xgl/drawing xif=image/vnd.xiff xl=application/excel xla=application/excel xla=application/x-excel xla=application/x-msexcel xlb=application/excel xlb=application/vnd.ms-excel xlb=application/x-excel xlc=application/excel xlc=application/vnd.ms-excel xlc=application/x-excel xld=application/excel xld=application/x-excel xlk=application/excel xlk=application/x-excel xll=application/excel xll=application/vnd.ms-excel xll=application/x-excel xlm=application/excel xlm=application/vnd.ms-excel xlm=application/x-excel xls=application/excel xls=application/vnd.ms-excel xls=application/x-excel xls=application/x-msexcel xlt=application/excel xlt=application/x-excel xlv=application/excel xlv=application/x-excel xlw=application/excel xlw=application/vnd.ms-excel xlw=application/x-excel xlw=application/x-msexcel xm=audio/xm xml=application/xml xml=text/xml xmz=xgl/movie xpix=application/x-vnd.ls-xpix xpm=image/x-xpixmap xpm=image/xpm x-png=image/png xsr=video/x-amt-showrun xwd=image/x-xwd xwd=image/x-xwindowdump xyz=chemical/x-pdb z=application/x-compress z=application/x-compressed zip=application/x-compressed zip=application/x-zip-compressed zip=application/zip zip=multipart/x-zip zoo=application/octet-stream zsh=text/x-script.zsh simple-http-4.1.21/src/org/simpleframework/http/resource/FileIndex.java0000644000175000017500000001662611417313373026642 0ustar jamespagejamespage/* * FileIndex.java December 2005 * * Copyright (C) 2005, Niall Gallagher * * 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.resource; import java.io.File; import org.simpleframework.http.Address; import org.simpleframework.http.Path; import org.simpleframework.http.parse.AddressParser; /** * The FileIndex provides an implementation of an index * that makes use of the OS file system to acquire meta data. This * will acquire information directly from the URI target, as well as * a MIME database maintained in the FileIndexer.properties * file. This caches all meta data acquired so that there is no expense * in re-acquiring the data. This allows for faster meta data retrieval * and facilitates the implementation of the meta data cache used. * * @author Niall Gallagher * * @see org.simpleframework.http.resource.FileIndexer */ class FileIndex implements Index { /** * This is the source indexer used to acquire the meta data. */ private Indexer indexer; /** * This is the path portion of the specified URI target. */ private Path path; /** * This is the OS specific file referencing the resource. */ private File file; /** * This is the MIME type resolved for the resource. */ private String type; /** * This contains all the information regarding the URI. */ private Address target; /** * Constructor for the FileIndex object. This uses a * URI target to acquire the meta data for the resource. The URI * provides the resource name and path and also provides a hint * for the MIME type of the resource from the file extension. * * @param indexer this is the source indexer for this instance * @param target this is the URI target that is to be indexed */ public FileIndex(Indexer indexer, String target) { this(indexer, new AddressParser(target)); } /** * Constructor for the FileIndex object. This uses a * URI target to acquire the meta data for the resource. The URI * provides the resource name and path and also provides a hint * for the MIME type of the resource from the file extension. * * @param indexer this is the source indexer for this instance * @param target this is the URI target that is to be indexed */ public FileIndex(Indexer indexer, Address target) { this.indexer = indexer; this.target = target; } /** * This is used to get the path that this object refers to. * This should be the fully qualified normalized path. This * refers to the OS system specific path that this represents. * * @return this returns the OS specific path for the target */ public String getContentType() { if(type == null) { type = getContentType(target); } return type; } /** * This is used to get the path that this object refers to. * This should be the fully qualified normalized path. This * refers to the OS system specific path that this represents. * * @param target the index target to get the real path for * * @return this returns the OS specific path for the target */ public String getContentType(Address target) { return indexer.getContentType(target); } /** * This is used to acquire the File reference * for the index target. This is typically rooted at a * base path, for instance the Context root * is typically used. This allows the file to be opened, * deleted, or read should the need arise in a service. * * @return this returns the OS file for the resource */ public File getFile() { if(file == null) { file = getFile(target); } return file; } /** * This is used to acquire the File reference * for the index target. This is typically rooted at a * base path, for instance the Context root * is typically used. This allows the file to be opened, * deleted, or read should the need arise in a service. * * @param target the index target to get the OS file for * * @return this returns the OS file for the resource */ public File getFile(Address target) { return indexer.getFile(target); } /** * This is used to acquire the Path object that * exposes various parts of the URI path. This can be used * to extract the individual path segments as strings as * well as the file extension and various other details. * * @return this returns a path object with various details */ public Path getPath() { if(path == null) { path = getPath(target); } return path; } /** * This is used to acquire the Path object that * exposes various parts of the URI path. This can be used * to extract the individual path segments as strings as * well as the file extension and various other details. * * @param target the index target to get the URI path for * * @return this returns a path object with various details */ public Path getPath(Address target) { return indexer.getPath(target); } /** * This is used to get the path that this object refers to. * This should be the fully qualified normalized path. This * refers to the OS system specific path that this represents. * * @return this returns the OS specific path for the target */ public String getRealPath() { return getFile().getAbsolutePath(); } /** * This is used to acquire the File directory * for the index target. This is typically rooted at a * base path, for instance the Context root * is typically used. This allows resources within the * same directory to be acquired easily. * * @return this returns the OS file for the directory */ public File getDirectory() { return getFile().getParentFile(); } /** * This is used to acquire the normalized URI style path for * the index target. This allows the path to be used within * the Context and other such objects that need * a normalized URI style path to resolve resources. * * @return this returns the normalized path for the target */ public String getRequestPath() { return getPath().getPath(); } /** * This allows the name for this object to be acquired. The * name usually refers to the last entry in the path. So if * the index target path was "/usr/bin/" the name is "bin". * * @return this returns the name of this index target */ public String getName() { return getPath().getName(); } } simple-http-4.1.21/src/org/simpleframework/http/resource/Indexer.java0000644000175000017500000000553211417313373026363 0ustar jamespagejamespage/* * Indexer.java December 2005 * * Copyright (C) 2005, Niall Gallagher * * 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.resource; import java.io.File; import org.simpleframework.http.Address; import org.simpleframework.http.Path; /** * The Indexer object is used to acquire meta data for * a address target. This provides a centralized source for meta data * within the server. The need to acquire information such as MIME * type, locale, and various other details for a Address frequently * arises. In order to provide a consistent set of details for a * specific target an Indexer implementation is used. * This helps various services and resources acquire meta data * quickly by facilitating a meta data cache for a context. * * @author Niall Gallagher * * @see org.simpleframework.http.resource.Index */ interface Indexer { /** * This is used to acquire the File reference * for the index target. This is typically rooted at a * base path, for instance the Context root * is typically used. This allows the file to be opened, * deleted, or read should the need arise in a service. * * @param target the index target to get the OS file for * * @return this returns the OS file for the resource */ public File getFile(Address target); /** * This is used to acquire the Path object that * exposes various parts of the address path. This can be used * to extract the individual path segments as strings as * well as the file extension and various other details. * * @param target the index target to get the Address path for * * @return this returns a path object with various details */ public Path getPath(Address target); /** * This allows the MIME type of this Index to * be acquired. The MIME type of a file is retrieved by the * Context.getContentType method for a specific * request address. This should have a value and perhaps some * parameters like the charset, "text/html; charset=UTF-8". * * @param target the index target to get the MIME type for * * @return the MIME type this object has been set to */ public String getContentType(Address target); }simple-http-4.1.21/src/org/simpleframework/http/Path.java0000644000175000017500000001553311417313373024034 0ustar jamespagejamespage/* * Path.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 Path 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. *

* If the path that this represents is /usr/bin/../etc/./README * then the actual path, normalized, is /usr/etc/README. 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 file.en_US.extension * will produce an extension of extension. This * will return null if the path contains no file extension. * * @return this will return the extension this path contains */ public 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 /usr/README the name is README. * 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(); /** * 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 /usr/../etc/./ the * path is /etc/. 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 * ../ or ./ */ public 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 substring 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 * ../ or ./ */ public 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 substring 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 * ../ or ./ */ public 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 /usr/bin/../etc then the segments * returned would be usr and etc as * the path is normalized before the segments are extracted. * * @return return all the path segments within the directory */ public 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 /pub/./bin/README would be to return * the highest directory path /pub/bin/. The "/" * character will allways be the last character in the path. * * @return this method will return the highest directory */ public 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 getDirectory. 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 * /usr/share/rfc/rfc2396.txt and the issued * path was /usr/share/text.txt then this will * return the path string /rfc/rfc2396.txt. * * @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); /** * 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 /usr/../etc/./ the * path is /etc/. 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 * ../ or ./ */ public String toString(); } simple-http-4.1.21/src/org/simpleframework/http/RequestHeader.java0000644000175000017500000001604511417313373025700 0ustar jamespagejamespage/* * RequestHeader.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 Header 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. *

* 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 List 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 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); /** * 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); /** * 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 Cookie 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); /** * 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 Cookie 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 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 */ public String getValue(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 substring and trim calls. *

* 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. *

* 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 getValues(String name); /** * This is used to acquire the locales from the request header. The * locales are provided in the Accept-Language 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 getLocales(); /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(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 Content-Type 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(); /** * This is a convenience method that can be used to determine * the length of the message body. This will determine if there * is a Content-Length 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 int getContentLength(); } simple-http-4.1.21/src/org/simpleframework/http/Part.java0000644000175000017500000000742511417313373024047 0ustar jamespagejamespage/* * Part.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Part 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 InputStream 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 * * @see org.simpleframework.http.Form */ 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 */ public 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 */ public 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 */ public 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 */ public 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 */ public String getContent() throws IOException; /** * This is used to acquire an InputStream 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; /** * 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(); } simple-http-4.1.21/src/org/simpleframework/http/Query.java0000644000175000017500000000733211417313373024243 0ustar jamespagejamespage/* * Query.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 Query 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 { /** * This method is used to acquire a List 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 get(String) 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 */ public List 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 */ public 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 */ public 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 true or * false 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); /** * This will return all parameters represented using the HTTP * URL query format. The x-www-form-urlencoded * format is used to encode the attributes, see RFC 2616. *

* 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 */ public String toString(); } simple-http-4.1.21/src/org/simpleframework/http/RequestWrapper.java0000644000175000017500000004435311417313373026133 0ustar jamespagejamespage/* * RequestWrapper.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.http.session.Session; import org.simpleframework.util.lease.LeaseException; /** * The RequestWrapper object is used so that the original * Request object can be wrapped in a filtering proxy * object. This allows a Container 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 RequestWrapper object. This allows * the original Request 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 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 Path 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. 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 request.getQuery(); } /** * This method is used to get a List 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 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 Cookie 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 Cookie 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 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 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 substring and trim calls. *

* 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. *

* 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 getValues(String name) { return request.getValues(name); } /** * This is used to acquire the locales from the request header. The * locales are provided in the Accept-Language 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 getLocales() { return request.getLocales(); } /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(String name) { return request.contains(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 Content-Type 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 Content-Length 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 int 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 Connection: close * header. If the close token is present then this stream is not * a keep-alive connection. 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 * is not keep-alive by default. * * @return returns true if this has a keep-alive stream */ public boolean isKeepAlive() { return request.isKeepAlive(); } /** * 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 Map * 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 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 InputStream can be determined * by the getContentLength 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 Part 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 ReadableByteChannel can be * determined by the getContentLength 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 method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @return returns an active session for the associated client */ public Session getSession() throws LeaseException { return request.getSession(); } /** * This method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @param create creates the session if it does not exist * * @return returns an active session for the associated client */ public Session getSession(boolean create) throws LeaseException { return request.getSession(create); } /** * This is used to acquire all the form parameters from the * HTTP request. This includes the query and POST data values * as well as the parts of a multipart request. The form is * a convenience object enabling easy access to state. * * @return this returns the form containing the state * * @throws IOException thrown if it could not be acquired */ public Form getForm() throws IOException { return request.getForm(); } /** * This is used to provide quick access to the parameters. This * avoids having to acquire the request Form object. * This basically acquires the parameters object and invokes * the getParameters method with the given name. * * @param name this is the name of the parameter value * * @exception IOException thrown if there is an I/O problem */ public String getParameter(String name) throws IOException { return request.getParameter(name); } /** * This method is used to acquire a Part from the * form 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 are added to the query values * as strings so that they can be used in a convenient way. * * @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) throws IOException { return request.getPart(name); } } simple-http-4.1.21/src/org/simpleframework/http/ContentType.java0000644000175000017500000001207411417313372025410 0ustar jamespagejamespage/* * ContentType.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 charset parameter is one of many parameters that * can be associated with a MIME type. This however this exposes this * parameter with a typed method. *

* The getCharset 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 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 */ public 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 text/plain; charset=UTF-8. This will * return the text value. If there is no primary type then this * will return null otherwise the string value. * * @return the primary type part of this MIME type */ public 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 */ public 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 text/html; charset=UTF-8. This * will return the HTML value. If there is no secondary type then * this will return null otherwise the string value. * * @return the primary type part of this MIME type */ public String getSecondary(); /** * This will set the charset to whatever value the * string contains. If the string is null then this will not set * the parameter to any value and the toString method * will not contain any details of the parameter. * * @param charset parameter value to add to the MIME type */ public void setCharset(String charset); /** * This is used to retrieve the charset 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(); /** * This is used to retrieve an arbitrary parameter from the MIME * type header. This ensures that values for boundary * 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); /** * 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 boundary 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); /** * This will return the value of the MIME type as a string. This * will concatenate the primary and secondary type values and * add the charset parameter to the type which will * recreate the content type. * * @return this returns the string representation of the type */ public String toString(); } simple-http-4.1.21/src/org/simpleframework/http/core/0000755000175000017500000000000011767603362023226 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/http/core/ResponseMessage.java0000644000175000017500000001773311417313372027176 0ustar jamespagejamespage/* * ResponseMessage.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.ContentType; import org.simpleframework.http.Cookie; import org.simpleframework.http.ResponseHeader; import org.simpleframework.http.parse.ContentParser; /** * The ResponseMessage 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 Message 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 ResponseMessage 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 getText() { 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 setText(String text) { this.text = text; } /** * 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 Content-Type 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 ContentParser(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 Content-Length 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 int getContentLength() { return getInteger("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 Transfer-Encoding 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 convert the representation of the response to * an ISO-8850-1 encoded array of bytes. The array is composed * from the toString method an then converted to a * byte array. * * @return this returns a byte array representing the header */ protected byte[] getMessage() throws IOException { return toString().getBytes("ISO-8859-1"); } /** * 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(cookie); head.append("\r\n"); } return head.append("\r\n").toString(); } } simple-http-4.1.21/src/org/simpleframework/http/core/TransportSender.java0000644000175000017500000001230511417313373027217 0ustar jamespagejamespage/* * TransportSender.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.Transport; /** * The TransportSender object is used to send bytes to * and underlying transport. This is essentially an adapter between * an OutputStream and the underlying transport. Each * byte array segment written to the underlying transport is wrapped * in a bytes buffer so that it can be sent by the transport layer. * * @author Niall Gallagher * * @see org.simpleframework.transport.Transport */ class TransportSender implements Sender { /** * This is the underlying transport to write the bytes to. */ private Transport transport; /** * This is used to determine if the transport has been closed. */ private volatile boolean closed; /** * Constructor for the TransportSender object. This * is used to create an adapter for the transport such that a * byte array can be used to write bytes to the array. * * @param transport the underlying transport to send bytes to */ public TransportSender(Transport transport) { this.transport = transport; } /** * This method is used to deliver the provided array of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @param array this is the array of bytes to send to the client */ public void send(byte[] array) throws IOException { send(array, 0, array.length); } /** * This method is used to deliver the provided array of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @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 send(byte[] array, int off, int len) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(array, off, len); if(len > 0) { send(buffer); } } /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @param buffer this is the buffer of bytes to send to the client */ public void send(ByteBuffer buffer) throws IOException { int mark = buffer.position(); int size = buffer.limit(); if(mark > size) { throw new IOException("Buffer position greater than limit"); } send(buffer, 0, size - mark); } /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @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 send(ByteBuffer buffer, int off, int len) throws IOException { int mark = buffer.position(); int limit = buffer.limit(); if(limit - mark > len) { buffer.limit(mark + len); // reduce usable size } transport.write(buffer); buffer.limit(limit); } /** * 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 { transport.flush(); } /** * This is used to close the sender and the underlying transport. * If a close is performed on the sender then no more bytes can * be read from or written to the transport and the client will * received a connection close on their side. */ public void close() throws IOException { if(!closed) { transport.close(); closed = true; } } } simple-http-4.1.21/src/org/simpleframework/http/core/Message.java0000644000175000017500000004064711417313373025460 0ustar jamespagejamespage/* * Message.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.ArrayList; import java.util.List; import org.simpleframework.http.Cookie; import org.simpleframework.http.parse.DateParser; import org.simpleframework.http.parse.ValueParser; import org.simpleframework.util.KeyMap; /** * The Message 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 */ class Message { /** * This is used to store the cookies added to the HTTP header. */ private final KeyMap cookies; /** * This is used to store multiple header values for a name. */ private final KeyMap values; /** * This is used to store the individual names for the header. */ private final KeyMap names; /** * This is used to parse all date headers added to the message. */ private final DateParser parser; /** * Constructor for the Message 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 Message() { this.cookies = new KeyMap(); this.values = new KeyMap(); this.names = new KeyMap(); 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 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 getValue in combination with the get methods. * This will perform a remove 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 set(String name, String value) { List list = getAll(name); if(value != null) { 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 getValue in combination with the get methods. * This will perform a remove 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 set(String name, int value) { set(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 remove 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) { set(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 getValue 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 add(String name, String value) { List 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 getInteger 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 add(String name, int value) { add(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) { add(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) { List list = getAll(name); if(list.size() > 0) { return list.get(0); } 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 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 Cookie object stored under the * specified name. This is used to retrieve cookies that have been * set with the setCookie 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 by the given name */ public Cookie getCookie(String name) { return cookies.get(name); } /** * This returns all Cookie objects stored under the * specified name. This is used to retrieve cookies that have been * set with the setCookie methods. If there are no * cookies then this will return an empty list. * * @return returns all the Cookie in the response */ public List getCookies() { return cookies.getValues(); } /** * The setCookie 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 setCookie 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 substring and trim calls. *

* 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. *

* 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 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 substring and trim calls. *

* 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. *

* 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 getValues(List 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 getAll(String name) { String token = name.toLowerCase(); Entry entry = values.get(token); if(entry == null) { return getAll(name, token); } return entry.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 getAll(String name, String token) { Entry entry = new Entry(name); String value = names.get(token); if(value == null) { names.put(token, name); } values.put(token, entry); return entry.getValues(); } /** * This is used to remove the named header from the response. This * removes all header values assigned to the specified name. If it * does not exist then this will return without modifying the HTTP * response. Headers names removed are case insensitive. * * @param name the HTTP message header to remove from the response */ public void remove(String name) { String token = name.toLowerCase(); String value = names.get(token); if(value != null) { names.remove(token); } values.remove(token); } /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(String name) { return !getAll(name).isEmpty(); } /** * The Entry 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. * * @author Niall Gallagher */ private class Entry { /** * Contains the header values that belong to the entry name. */ private List value; /** * Contains the unformatted header name for this entry. */ private String name; /** * Constructor for the Entry 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. * * @param name this is the name of the HTTP header to use */ public Entry(String name) { this.value = new ArrayList(2); this.name = name; } /** * This is used to acquire the unformatted name of the header. * The name provided by this method is used to compose the * HTTP header by appending a semicolon and the header value. * * @return this returns the unformatted header name */ public String getName() { return name; } /** * 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 getValues() { return value; } } } simple-http-4.1.21/src/org/simpleframework/http/core/Policy.java0000644000175000017500000000307511417313373025325 0ustar jamespagejamespage/* * Policy.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.Cookie; /** * The Policy object represents a policy for creating * session cookies. This governs the cookie name used and the value * that is created for the cookie. Typically the cookie value needs * to be a unique identifier such as the time of creation or an * incrementing number. * * @author Niall Gallagher * * @see org.simpleframework.http.core.Header */ interface Policy { /** * This is used to acquire the session cookie for the request. The * session cookie is either sent with the HTTP request header or * it can be created if required. This ensures that if no cookie * has been sent one can be created on demand. * * @param create if true the session cookie will be created * * @return the cookie associated with the session or null */ public Cookie getSession(boolean create); } simple-http-4.1.21/src/org/simpleframework/http/core/RequestMessage.java0000644000175000017500000003054211417313373027022 0ustar jamespagejamespage/* * RequestMessage.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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; /** * The RequestMessage 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 RequestMessage 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 RequestMessage 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 Path 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 List 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 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 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 substring and trim calls. *

* 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. *

* 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 getValues(String name) { return header.getValues(name); } /** * This is used to acquire the locales from the request header. The * locales are provided in the Accept-Language 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 getLocales() { return header.getLocales(); } /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(String name) { return header.contains(name); } /** * This is used to see if there is a HTTP message header with the * given name in this container, if it exists this will check to * see if the provided value exists. This is used for a comma * separated list of values found within the HTTP header value. * If the header and token exits this returns true otherwise false. * * @param name the HTTP message header to get the value from * @param value this value to find within the HTTP value * * @return this returns true if the HTTP message value exists */ public boolean contains(String name, String value) { List list = getValues(name); for(String next : list) { if(next.equalsIgnoreCase(value)) { return true; } } return false; } /** * 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 Cookie 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 Cookie 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 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 Content-Type 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 Content-Length 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 int getContentLength() { return header.getContentLength(); } /** * 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(); } } simple-http-4.1.21/src/org/simpleframework/http/core/Tracker.java0000644000175000017500000000520311417313373025454 0ustar jamespagejamespage/* * Tracker.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session.Session; import org.simpleframework.util.lease.LeaseException; /** * The Tracker interface represents an object that is * used to track a given client using some identifier. Typically the * client is tracked using a cookie or encoded URI. However it is * left up to the implementation to determine how the client will be * tracked. In order to acquire the information to determine what * client is connected the request entity is used. * * @author Niall Gallagher * * @see org.simpleframework.http.session.SessionProvider */ interface Tracker { /** * This is used to acquire the Session object using * the information within the HTTP entity. The entity contains all * of the information that has been sent by the client and is the * ideal means to differentiate clients. * * @param entity this is the entity consumed from the pipeline * * @return a session associated with the provided entity */ public Session getSession(Entity entity) throws LeaseException; /** * This is used to acquire the Session object using * the information within the HTTP entity. The entity contains all * of the information that has been sent by the client and is the * ideal means to differentiate clients. * * @param entity this is the entity consumed from the pipeline * @param create determines if a session should be created * * @return a session associated with the provided entity */ public Session getSession(Entity entity, boolean create) throws LeaseException; /** * This close method is used to close the tracker and * release all resources associated with it. This means canceling * all active sessions and emptying the contents of those sessions. * Threads and other such resources are released by this method. */ public void close() throws LeaseException; } simple-http-4.1.21/src/org/simpleframework/http/core/CloseProducer.java0000644000175000017500000001427511417313373026643 0ustar jamespagejamespage/* * CloseProducer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 CloseProducer 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 CloseProducer implements Producer { /** * This is the monitor used to notify the initiator of events. */ private final Monitor monitor; /** * This is the underlying sender used to deliver the raw data. */ private final Sender sender; /** * Constructor for the CloseProducer 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 sender this is used to send to the underlying transport * @param monitor this is used to deliver signals to the kernel */ public CloseProducer(Sender sender, Monitor monitor) { this.monitor = monitor; this.sender = sender; } /** * 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 produce(byte[] array) throws IOException { produce(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 produce(byte[] array, int off, int len) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(array, off, len); if(len > 0) { produce(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 produce(ByteBuffer buffer) throws IOException { int mark = buffer.position(); int size = buffer.limit(); if(mark > size) { throw new ProducerException("Buffer position greater than limit"); } produce(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 produce(ByteBuffer buffer, int off, int len) throws IOException { if(monitor.isClosed()) { throw new ProducerException("Stream has been closed"); } try { sender.send(buffer, off, len); } catch(Exception cause) { if(sender != null) { monitor.error(sender); } throw new ProducerException("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(!monitor.isClosed()) { sender.flush(); } } catch(Exception cause) { if(sender != null) { monitor.error(sender); } throw new ProducerException("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(!monitor.isClosed()) { monitor.close(sender); sender.close(); } } catch(Exception cause) { if(sender != null) { monitor.error(sender); } throw new ProducerException("Error sending response", cause); } } } simple-http-4.1.21/src/org/simpleframework/http/core/PartBodyConsumer.java0000644000175000017500000001167311417313373027331 0ustar jamespagejamespage/* * PartBodyConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.InputStream; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.Allocator; /** * The PartBodyConsumer 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 extends 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 Consumer token; /** * Constructor for the PartBodyConsumer 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 PartList(), boundary); } /** * Constructor for the PartBodyConsumer 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 list 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, PartList list, byte[] boundary) { this.content = new ContentConsumer(allocator, segment, list, boundary); this.token = new TokenConsumer(allocator, LINE); } /** * 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 Part 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(Cursor 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(); } /** * 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 */ @Override public String getContent() throws IOException { return content.getContent(); } /** * This is used to acquire an InputStream 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 */ @Override public InputStream getInputStream() throws IOException { return content.getInputStream(); } } simple-http-4.1.21/src/org/simpleframework/http/core/Conversation.java0000644000175000017500000002164311417313373026541 0ustar jamespagejamespage/* * Conversation.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 Conversation 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.Accumulator * @see org.simpleframework.http.core.Transfer */ class Conversation { /** * This is the response object that requires HTTP headers set. */ private final Response response; /** * This contains the request headers and protocol version. */ private final Request request; /** * Constructor for the Conversation 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(Request request, Response response) { this.response = response; this.request = request; } /** * This provides the Request 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 Request getRequest() { return request; } /** * This provides the Response 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 Response 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 int getContentLength() { return response.getContentLength(); } /** * This is used to determine if the Response 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 false; } return method.equalsIgnoreCase("HEAD"); } /** * 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(int length) { boolean keepAlive = isKeepAlive(); if(keepAlive) { response.set("Connection", "keep-alive"); } else { response.set("Connection", "close"); } response.set("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 minor >= 1; } return false; } /** * The isKeepAlive 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("keep-alive"); } return isPersistent(); } /** * The isChunkable 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.set("Transfer-Encoding", "chunked"); response.set("Connection", "keep-alive"); } else { response.set("Connection", "close"); } } /** * 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.remove("Transfer-Encoding"); } /** * The isChunkedEncoded 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; } } simple-http-4.1.21/src/org/simpleframework/http/core/Monitor.java0000644000175000017500000000654511417313373025522 0ustar jamespagejamespage/* * Monitor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Monitor object is core to how the requests are * processed from a pipeline. This monitors 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 monitor is notified. It can * then shutdown the connection, as RFC 2616 suggests on errors. *

* If however the response is delivered successfully the monitor is * notified of this event. On successful delivery the monitor will * hand the Channel 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.Initiator */ interface Monitor { /** * 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 sender this is the sender used to send the response */ public void close(Sender sender); /** * 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 sender this is the sender used to send the response */ public void error(Sender sender); /** * 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 sender this is the sender used to send the response */ public void ready(Sender sender); /** * This is used to determine if the response has completed or * if there has been an error. This basically allows the sender * 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(); /** * This is used to determine if the response was in error. If * the response was in error this allows the sender to throw an * exception indicating that there was a problem responding. * * @return this returns true if there was a response error */ public boolean isError(); }simple-http-4.1.21/src/org/simpleframework/http/core/Selector.java0000644000175000017500000000646311417313373025652 0ustar jamespagejamespage/* * Selector.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Selector 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. *

* 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. *

* 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 Selector extends Initiator { /** * 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; /** * 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; /** * 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; /** * This method is used to stop the Selector 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. */ public void stop() throws IOException; } simple-http-4.1.21/src/org/simpleframework/http/core/Accumulator.java0000644000175000017500000002555111417313373026350 0ustar jamespagejamespage/* * Accumulator.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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; /** * The Accumulator 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 accumulator buffer. Once the accumulator buffer has * overflown then the response is committed. * * @author Niall Gallagher * * @see org.simpleframework.http.core.Transfer */ class Accumulator extends OutputStream implements WritableByteChannel { /** * This is the transfer object used to transfer the response. */ private Transfer transfer; /** * 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 Accumulator 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 support this is used to determine the response semantics * @param entity this is used to acquire the underlying transport * @param monitor this is used to report I/O events to the kernel */ public Accumulator(Conversation support, Entity entity, Monitor monitor) { this(support, entity.getChannel(), monitor); } /** * Constructor for the Accumulator 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 support this is used to determine the response semantics * @param channel this is the channel used to write the data to * @param monitor this is used to report I/O events to the kernel */ public Accumulator(Conversation support, Channel channel, Monitor monitor) { this(support, channel.getSender(), monitor); } /** * Constructor for the Accumulator object. This will * create a buffering output stream which will flush data to the * underlying transport using the sender provided. 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 support this is used to determine the response semantics * @param sender this is used to write to the underlying transport * @param monitor this is used to report I/O events to the kernel */ public Accumulator(Conversation support, Sender sender, Monitor monitor) { this.transfer = new Transfer(support, sender, monitor); 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 TransferException("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){ transfer.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) { transfer.start(); } if(count > 0) { transfer.write(buffer, 0, count); } if(flush) { transfer.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) { transfer.start(count); } if(count > 0) { transfer.write(buffer, 0, count); } transfer.close(); } } simple-http-4.1.21/src/org/simpleframework/http/core/Body.java0000644000175000017500000000677711417313373024777 0ustar jamespagejamespage/* * Body.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.InputStream; import org.simpleframework.http.Part; /** * The Body interface is used to represent the body of * a HTTP entity. It contains the information that is delivered with * the request. Typically this will be a form POST or an XML message * from a SOAP request. However, it can be any 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 * Part instance for it. * * @author Niall Gallagher */ 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 */ public 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 */ public 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 */ public InputStream getInputStream() throws IOException; /** * This method is used to acquire a Part from the * body 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 are added to the query values * as strings so that they can be used in a convenient way. * * @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); /** * This method provides all parts for this body. The parts for a * body can contain text parameters or files. Each file part can * contain headers, which are the typical HTTP headers. Typically * headers describe the content and any encoding if required. * * @return this returns a list of parts for this body */ public PartList getParts(); } simple-http-4.1.21/src/org/simpleframework/http/core/Reader.java0000644000175000017500000000665511417313373025277 0ustar jamespagejamespage/* * Reader.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.nio.channels.SocketChannel; import org.simpleframework.transport.reactor.Operation; /** * The Reader object is used to read the bytes to form * a 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 selector. If at any point the reading * results in an error the operation is canceled and the collector * is closed, which shuts down the connection. * * @author Niall Gallagher * * @see org.simpleframework.transport.reactor.Reactor */ class Reader implements Operation { /** * This is the selector used to process the collection events. */ private final Selector source; /** * This is the collector used to consume the entity bytes. */ private final Collector task; /** * This is the channel object associated with the collector. */ private final Channel channel; /** * Constructor for the Reader object. This requires * a selector and a collector object in order to consume the data * from the connected socket which forms a HTTP request entity. * * @param source the selector object used to process events * @param task this is the task used to collect the entity */ public Reader(Selector source, Collector task){ this.channel = task.getChannel(); this.source = source; this.task = task; } /** * This is the SocketChannel 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 task.getSocket(); } /** * This run 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 Selector object to dispatch the request. This * is sequence of events that occur for each transaction. */ public void run() { try { task.collect(source); }catch(Throwable e){ cancel(); } } /** * 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 canceled and the underlying transport is * closed. This helps to clean up occupied resources. */ public void cancel() { try { channel.close(); } catch(Throwable e) { return; } } } simple-http-4.1.21/src/org/simpleframework/http/core/BufferConsumer.java0000644000175000017500000000642611417313373027016 0ustar jamespagejamespage/* * BufferConsumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer.ArrayBuffer; import org.simpleframework.util.buffer.Buffer; /** * The BufferConsumer is used to create a consumer that * is used to consume data and append that data to a buffer. This is * done so that the buffer can act as a data source for the content * that has been consumed. For example, take the message body, this * can be consumed in such a way that the internal buffer can be * used acquire an InputStream to read the contents. * Buffering in such a way provides an efficient means to store the * contents of the message as only one read only copy is created. * * @author Niall Gallagher */ abstract class BufferConsumer implements Consumer { /** * Constructor for the BufferConsumer object. This * will create a consumer that provides methods that can be * used to buffer content consumed from the provided cursor. * Such consumers can accumulate the transport data. */ protected BufferConsumer() { super(); } /** * 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 InputStream, 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 */ protected void append(byte[] array) throws IOException { append(array, 0, array.length); } /** * 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 InputStream, 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 */ protected void append(byte[] array, int off, int len) throws IOException { Buffer buffer = allocate(); if(buffer != null) { buffer.append(array, off, len); } } /** * This method is used to allocate the internal buffer. If there * has already been a call to this method the previous instance * is returned. If there is any issue allocating the buffer then * this will throw an exception. * * @return this returns the buffer to append the bytes to */ protected Buffer allocate() throws IOException { return new ArrayBuffer(); } } simple-http-4.1.21/src/org/simpleframework/http/core/PartFactory.java0000644000175000017500000000522011417313373026316 0ustar jamespagejamespage/* * PartFactory.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.buffer.Allocator; /** * The PartFactory 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.core.PartListConsumer * @see org.simpleframework.http.core.PartBodyConsumer */ class PartFactory extends ConsumerFactory { /** * Constructor for the PartFactory 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 */ public PartFactory(Allocator allocator, Segment header) { super(allocator, header); } /** * 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 list 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(PartList list, byte[] boundary) throws IOException { byte[] terminal = getBoundary(segment); if(isPart(segment)) { return new PartListConsumer(allocator, list, terminal); } return new PartBodyConsumer(allocator, segment, list, boundary); } } simple-http-4.1.21/src/org/simpleframework/http/core/EmptyConsumer.java0000644000175000017500000000334511417313373026700 0ustar jamespagejamespage/* * EmptyConsumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.Cursor; /** * The EmptyConsumer 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 */ class EmptyConsumer extends BodyConsumer { /** * 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(Cursor 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; } } simple-http-4.1.21/src/org/simpleframework/http/core/TransferException.java0000644000175000017500000000401711417313373027526 0ustar jamespagejamespage/* * TransferException.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 TransferException 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 IOException * objects that are thrown from the underlying transport. * * @author Niall Gallagher */ class TransferException extends IOException { /** * Constructor for the TransferException 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 TransferException(String message) { super(message); } /** * Constructor for the TransferException 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 TransferException(String message, Throwable cause) { super(message); initCause(cause); } } simple-http-4.1.21/src/org/simpleframework/http/core/Collector.java0000644000175000017500000000527511417313373026020 0ustar jamespagejamespage/* * Collector.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.channels.SocketChannel; /** * The Collector object is used to collect bytes from * a channel in order to create the HTTP entity. In order to create * the entity this makes use of a collection of consumer objects * which will consume the data required to compose the header and * body of the entity. Once the entity has been fully collected * this object exposes the consumed data as typed objects, which * can be used to examine the request sent by the client. *

* Because this collects bytes in a non-blocking manner there may * be a situation where there are no bytes left. In such an event * the collector needs to be queued until such time as there are * more bytes ready to read from the socket. To achieve this it is * given a Selector object, which it can used to * hibernate until such time as there is data ready to read. * * @author Niall Gallagher * * @see org.simpleframework.http.core.Channel */ interface Collector extends Entity { /** * This is used to collect the data from a Channel * which is used to compose the entity. If at any stage there * are no ready bytes on the socket the selector 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 selector as ready, * which processes the entity as a new client HTTP request. * * @param selector this is the selector used to queue this */ public void collect(Selector selector) throws IOException; /** * 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(); } simple-http-4.1.21/src/org/simpleframework/http/core/ConsumerFactory.java0000644000175000017500000001457511417313373027220 0ustar jamespagejamespage/* * ConsumerFactory.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.ContentType; import org.simpleframework.util.buffer.Allocator; /** * The ConsumerFactory 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 ConsumerFactory 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 delimeter was provided. * * @return this returns the consumer used to consume the body */ public BodyConsumer getInstance() { int 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 delimeter 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(int length) { byte[] boundary = getBoundary(segment); if(isPart(segment)) { return new PartListConsumer(allocator, boundary, length); } if(isChunked(segment)) { return new ChunkedConsumer(allocator); } if(isFixed(segment)) { return new FixedConsumer(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 isPart(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) { int 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 Content-Length 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 int getContentLength() { return segment.getContentLength(); } } simple-http-4.1.21/src/org/simpleframework/http/core/Disposition.java0000644000175000017500000000412511417313373026367 0ustar jamespagejamespage/* * Disposition.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Disposition object is used to represent 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 getName can be used to determine the name, if * it represents a file then getFileName is preferred. * * @author Niall Gallagher */ interface Disposition { /** * 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(); /** * 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(); /** * 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(); } simple-http-4.1.21/src/org/simpleframework/http/core/Expectation.java0000644000175000017500000000471111417313373026347 0ustar jamespagejamespage/* * Expectation.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Expectation object is used to send the expect * 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 Expectation { /** * 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 sender that is used to deliver the continue. */ private final Sender sender; /** * Constructor for the Expectation 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 Expectation(Channel channel) { this.sender = channel.getSender(); } /** * 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()) { sender.send(STATUS); sender.send(MESSAGE); sender.flush(); } } } simple-http-4.1.21/src/org/simpleframework/http/core/Header.java0000644000175000017500000001762711417313373025266 0ustar jamespagejamespage/* * Header.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.Cookie; import org.simpleframework.http.Path; import org.simpleframework.http.Query; /** * This is a Header 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. *

* 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 */ 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 */ public 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 target. * * @return this returns the address of the request line */ public Address getAddress(); /** * This is used to acquire the path as extracted from the * the HTTP request URI. The Path 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(); /** * 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(); /** * 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(); /** * 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 */ public 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 */ public int getMinor(); /** * This method is used to get a List 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 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); /** * 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); /** * 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 Cookie 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); /** * 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 Cookie 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 getCookies(); /** * This is used to acquire the locales from the request header. The * locales are provided in the Accept-Language 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 getLocales(); /** * This is used to acquire the session cookie for the request. The * session cookie is either sent with the HTTP request header or * it can be created if required. This ensures that if no cookie * has been sent one can be created on demand. * * @param create if true the session cookie will be created * * @return the cookie associated with the session or null */ public Cookie getSession(boolean create); /** * 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(); /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(String name); } simple-http-4.1.21/src/org/simpleframework/http/core/ContainerSelector.java0000644000175000017500000001411711417313373027510 0ustar jamespagejamespage/* * ContainerSelector.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.transport.reactor.ExecutorReactor; import org.simpleframework.transport.reactor.Reactor; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.thread.PoolExecutor; /** * The ContainerSelector 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 Container 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 ContainerSelector implements Selector { /** * This is the thread pool used for servicing the requests. */ private final PoolExecutor executor; /** * This is the thread pool used for collecting the requests. */ private final PoolExecutor 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 handler; /** * This is the reactor used to schedule the collectors. */ private final Reactor reactor; /** * This is the tracker used to create the session objects. */ private final Tracker tracker; /** * Constructor for the ContainerSelector object. This * is used to create a selector 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 handler 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 selector threads to use */ public ContainerSelector(Container handler, Allocator allocator, int count, int select) throws IOException { this.collect = new PoolExecutor(Reader.class, count); this.reactor = new ExecutorReactor(collect, select); this.executor = new PoolExecutor(Dispatcher.class, count); this.tracker = new CookieTracker(); this.allocator = allocator; this.handler = handler; } /** * 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 Container 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 EntityCollector(allocator, tracker, 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 Reader(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 Reader(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 Dispatcher(handler, this, collector)); } /** * This method is used to stop the Selector 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. *

* Here we stop the Reactor first, this ensures * that there are no further selects performed if a given socket * does not have enough data to fulfill a request. From there we * stop the main dispatch Executor 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(); tracker.close(); } catch(Exception e) { throw new IOException("Error stopping"); } } } simple-http-4.1.21/src/org/simpleframework/http/core/Producer.java0000644000175000017500000001047011417313373025646 0ustar jamespagejamespage/* * Producer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 Producer object is used to produce 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.Monitor */ interface Producer { /** * 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 */ public void produce(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 */ public void produce(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 */ public void produce(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 */ public void produce(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. */ public 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. */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/http/core/PartForm.java0000644000175000017500000002543011417313373025617 0ustar jamespagejamespage/* * PartForm.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.Collection; import java.util.List; import java.util.Map; import java.util.Set; import org.simpleframework.http.Form; import org.simpleframework.http.Part; import org.simpleframework.http.Query; /** * The PartForm is a form that delegates to a body to * get any parts requested. This combines both the query and the body * in to a single object which can be used to acquire all data that * has been sent by the client in a single convenient location. All * changes made to this form will be made to the underlying query. * * @author Niall Gallagher */ class PartForm implements Form { /** * This is the query used to acquire the request parameters. */ private final Query query; /** * This is the body used to acquire the uploaded parts. */ private final Body body; /** * Constructor for the PartForm object. This is used * to create a form using the parameters from the query and the * parts from the specified body. Combining both objects under a * single instance provides a convenient means to find data. * * @param body this is the body that contains all the parts * @param query this is the query used to get parameters */ public PartForm(Body body, Query query) { this.query = query; this.body = body; } /** * 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) { return query.getInteger(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 */ public float getFloat(Object name) { return query.getFloat(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 true or * false 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) { return query.getBoolean(name); } /** * This method is used to acquire a Part from the * form 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 are added to the query values * as strings so that they can be used in a convenient way. * * @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 body.getPart(name); } /** * This method provides all parts for this Form. The * parts for a form can contain text parameters or files. Each file * part can contain headers, which take the form of HTTP headers to * describe the payload. Typically headers describe the content. * * @return this returns a list of parts for this form */ public List getParts() { return body.getParts(); } /** * This is used to determine whether a value representing the * name of a pair has been inserted into the internal map. The * object passed into this method should be a string, as all * values stored within the map will be stored as strings. * * @param name this is the name of a pair within the map * * @return this returns true if the pair of that name exists */ public boolean containsKey(Object name) { return query.containsKey(name); } /** * This method is used to determine whether any pair that has * been inserted into the internal map had the presented value. * If one or more pairs within the collected values contains * the value provided then this method will return true. * * @param value this is the value that is to be searched for * * @return this returns true if any value is equal to this */ public boolean containsValue(Object value) { return query.containsValue(value); } /** * This method is used to acquire the name and value pairs that * have currently been collected by this parser. This is used * to determine which values have been extracted from the * source. It is useful when the values have to be gathered. * * @return this set of value pairs that have been extracted */ public Set> entrySet() { return query.entrySet(); } /** * The get method is used to acquire the value for * a named pair. So if a pair of name=value has been parsed and * inserted into the collection of values this will return the * value given the name. The value returned will be a string. * * @param name this is a string used to search for the value * * @return this is the value, as a string, that has been found */ public String get(Object name) { return query.get(name); } /** * This method is used to acquire a List for all of * the values that have been put in to the map. The list allows * all values associated with the specified key. This enables a * parser to collect a number of associated values. * * @param name this is the key used to search for the value * * @return this is the list of values associated with the key */ public List getAll(Object name) { return query.getAll(name); } /** * This method is used to determine whether the parser has any * values available. If the size is zero then the * parser is empty and this returns true. The is acts as a * proxy the the isEmpty of the internal map. * * @return this is true if there are no available values */ public boolean isEmpty() { return query.isEmpty(); } /** * This is used to acquire the names for all the values that * have currently been collected by this parser. This is used * to determine which values have been extracted from the * source. It is useful when the values have to be gathered. * * @return the set of name values that have been extracted */ public Set keySet() { return query.keySet(); } /** * The put method is used to insert the name and * value provided into the collection of values. Although it is * up to the parser to decide what values will be inserted it * is generally the case that the inserted values will be text. * * @param name this is the name string from a name=value pair * @param value this is the value string from a name=value pair * * @return this returns the previous value if there was any */ public String put(String name, String value) { return query.put(name, value); } /** * This method is used to insert a collection of values into * the parsers map. This is used when another source of values * is required to populate the connection currently maintained * within this parsers internal map. Any values that currently * exist with similar names will be overwritten by this. * * @param map this is the collection of values to be added */ public void putAll(Map map) { query.putAll(map); } /** * The remove method is used to remove the named * value pair from the collection of values. This acts like a * take, in that it will get the value string and remove if * from the collection of values the parser has stored. * * @param name this is a string used to search for the value * * @return this is the value, as a string, that is removed */ public String remove(Object name) { return query.remove(name); } /** * This obviously enough provides the number of values that * have been inserted into the internal map. This acts as * a proxy method for the internal map size. * * @return this returns the number of values are available */ public int size() { return query.size(); } /** * This method is used to acquire the value for all values that * have currently been collected by this parser. This is used * to determine which values have been extracted from the * source. It is useful when the values have to be gathered. * * @return the list of value strings that have been extracted */ public Collection values() { return query.values(); } /** * The clear method is used to wipe out all the * currently existing values from the collection. This is used * to recycle the parser so that it can be used to parse some * other source of values without any lingering state. */ public void clear() { query.clear(); } /** * This will return all parameters represented using the HTTP * URL query format. The x-www-form-urlencoded * format is used to encode the attributes, see RFC 2616. *

* 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 */ public String toString() { return query.toString(); } } simple-http-4.1.21/src/org/simpleframework/http/core/Container.java0000644000175000017500000000506511417313373026011 0ustar jamespagejamespage/* * Container.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 Container object is used to process HTTP requests * and compose HTTP responses. The Request 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. *

* 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 Response object used to * deliver the HTTP response will only commit and send once it has * its OutputStream closed. *

* The Container 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 Request and Response * 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. *

* 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 Response 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 */ public void handle(Request req, Response resp); } simple-http-4.1.21/src/org/simpleframework/http/core/CookieTracker.java0000644000175000017500000001150611417313373026611 0ustar jamespagejamespage/* * CookieTracker.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.SECONDS; import java.util.concurrent.TimeUnit; import org.simpleframework.http.Cookie; import org.simpleframework.http.session.Session; import org.simpleframework.http.session.SessionManager; import org.simpleframework.http.session.SessionProvider; import org.simpleframework.util.lease.LeaseException; /** * The CookieTracker object provides a tracker that is * used to create and retrieve sessions based on a cookie. This will * extract the session cookie object using a provided entity. If * the entity header does not contain a cookie then it can be * created. Once created all subsequent requests can acquire the * same session using the unique session cookie value in the header. * * @author Niall Gallagher * * @see org.simpleframework.http.session.SessionManager */ class CookieTracker implements Tracker { /** * This is the provider used to manage all created sessions. */ private final SessionProvider provider; /** * Constructor for the CookieTracker object. This is * an adapter for a session provider implementation, which uses * a unique value provided in a cookie sent with the request. By * default the idle life of a session is twenty minutes. */ public CookieTracker() { this(1200, SECONDS); } /** * Constructor for the CookieTracker object. This is * an adapter for a session provider implementation, which uses * a unique value provided in a cookie sent with the request. The * idle life of a session can be specified in this constructor. * * @param duration this is the idle life of the sessions * @param unit the time unit used to measure the duration */ public CookieTracker(long duration, TimeUnit unit) { this.provider = new SessionManager(duration, unit); } /** * This is used to acquire the Session object using * the information within the HTTP entity. The entity contains all * of the information that has been sent by the client and is the * ideal means to differentiate clients. * * @param entity this is the entity consumed from the pipeline * * @return a session associated with the provided entity */ public Session getSession(Entity entity) throws LeaseException { return getSession(entity, true); } /** * This is used to acquire the Session object using * the information within the HTTP entity. The entity contains all * of the information that has been sent by the client and is the * ideal means to differentiate clients. * * @param entity this is the entity consumed from the pipeline * @param create determines if a session should be created * * @return a session associated with the provided entity */ public Session getSession(Entity entity, boolean create) throws LeaseException { Header header = entity.getHeader(); Cookie cookie = header.getSession(create); if(cookie == null) { return null; } return getSession(cookie, create); } /** * This is used to acquire the Session object using * the provided cookie. The value of the cookies is used as a key * to acquire the session from the session provider. If one does * not exist then it can be created if required. * * @param cookie this is the cookie used to acquire the provider * @param create determines if a session should be created * * @return a session associated with the provided cookie */ private Session getSession(Cookie cookie, boolean create) throws LeaseException { String value = cookie.getValue(); if(value == null) { return null; } return provider.open(value, create); } /** * This close method is used to close the tracker and * release all resources associated with it. This means canceling * all active sessions and emptying the contents of those sessions. * Threads and other such resources are released by this method. */ public void close() throws LeaseException { provider.close(); } } simple-http-4.1.21/src/org/simpleframework/http/core/TokenConsumer.java0000644000175000017500000000643111417313373026661 0ustar jamespagejamespage/* * TokenConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.buffer.Allocator; import org.simpleframework.util.buffer.Buffer; /** * The TokenConsumer 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 TokenConsumer 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; } } simple-http-4.1.21/src/org/simpleframework/http/core/DispositionParser.java0000644000175000017500000002174011417313373027546 0ustar jamespagejamespage/* * DispositionParser.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.parse.ParseBuffer; import org.simpleframework.util.parse.Parser; /** * The DispositionParser 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 */ class DispositionParser extends Parser implements Disposition { /** * 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 DispositionParser 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 DispositionParser() { this.file = new ParseBuffer(); this.name = new ParseBuffer(); this.skip = new ParseBuffer(); } /** * Constructor for the DispositionParser 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 DispositionParser(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 Parser when it is ready * to parse a new String. 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 * String excluding the whitespace within literals. * The definition of a literal can be found in RFC 2616. *

* 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 filename parameter within the set of parameters * which are given to the type. The filename param and the * the name 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 filename parameter within the set of parameters * which are given to the type. The filename param and the * the name 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 IndexOutOfBoundsException 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 * char's upto but excluding the first terminal char * 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(ParseBuffer 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 == '"'; } } simple-http-4.1.21/src/org/simpleframework/http/core/Builder.java0000644000175000017500000000351511417313373025453 0ustar jamespagejamespage/* * Builder.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 Builder object is used to build and entity from * its constituent parts. Each component of the entity is provided * to the builder once they have been extracted from the connected * pipeline. Once all parts have been acquired by the builder the * entity is ready to used to process the request. * * @author Niall Gallagher */ interface Builder extends Entity { /** * Provides the Body object for the builder. This * is used by the entity to read the content of the HTTP request. * Also, if the entity body is a multipart upload then each of * the individual parts of the body is available to read from. * * @param body this is the entity body provided by the request */ public void setBody(Body body); /** * Provides the Header object for the builder. This * is used by the entity to determine the request URI and method * type. The header also provides start in the form of cookies * which can be used to track the client. * * @param header this is the header provided by the request */ public void setHeader(Header header); } simple-http-4.1.21/src/org/simpleframework/http/core/ProducerException.java0000644000175000017500000000401711417313373027525 0ustar jamespagejamespage/* * ProducerException.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 ProducerException 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 IOException * objects that are thrown from the underlying transport. * * @author Niall Gallagher */ class ProducerException extends IOException { /** * Constructor for the ProducerException 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 ProducerException(String message) { super(message); } /** * Constructor for the ProducerException 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 ProducerException(String message, Throwable cause) { super(message); initCause(cause); } } simple-http-4.1.21/src/org/simpleframework/http/core/ContainerServer.java0000644000175000017500000001255111417313373027176 0ustar jamespagejamespage/* * ContainerServer.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.Processor; import org.simpleframework.transport.ProcessorServer; import org.simpleframework.transport.Server; import org.simpleframework.transport.Socket; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.FileAllocator; /** * The ContainerServer object provides a processor * 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. *

* 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 ContainerServer implements Server { /** * This is the transporter used to process the connections. */ private final Processor processor; /** * This is used to deliver pipelines to the container. */ private final Server server; /** * Constructor for the ContainerServer object. The * processor 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 ContainerServer(Container container) throws IOException { this(container, new FileAllocator(), 8); } /** * Constructor for the ContainerServer object. The * processor 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 ContainerServer(Container container, int count) throws IOException { this(container, new FileAllocator(), count); } /** * Constructor for the ContainerServer object. The * processor 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 ContainerServer(Container container, Allocator allocator) throws IOException { this(container, allocator, 8); } /** * Constructor for the ContainerServer object. The * processor 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 ContainerServer(Container container, Allocator allocator, int count) throws IOException { this.processor = new ContainerProcessor(container, allocator, count); this.server = new ProcessorServer(processor, count); } /** * Used to process the Socket which is a full duplex * communication link that may contain several HTTP requests. This * will be used to read the requests from the Socket * and to pass these requests to a Container for * processing. *

* Typical usage of this method is to accept multiple pipeline * objects, each representing a unique HTTP channel to the client, * and process requests from those pipelines concurrently. * * @param socket this is the connected HTTP pipeline to process */ public void process(Socket socket) throws IOException { server.process(socket); } /** * This method is used to stop the Processor such that * it will accept no more pipelines. Stopping the processor ensures * that all resources occupied will be released. This is required * so that all threads are stopped, and all memory is released. *

* Typically this method is called once all connections to the * server have been stopped. As a final act of shutting down the * entire server all threads must be stopped, this allows collection * of unused memory and the closing of file and socket resources. */ public void stop() throws IOException { server.stop(); } } simple-http-4.1.21/src/org/simpleframework/http/core/FlushMonitor.java0000644000175000017500000001201011417313373026504 0ustar jamespagejamespage/* * FlushMonitor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 FlushMonitor object is used to monitor 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 FlushMonitor implements Monitor { /** * This is the initiator used to initiate a new request. */ private Initiator reactor; /** * This is the channel associated with the client connection. */ private Channel channel; /** * This flag determines whether the connection was closed. */ private boolean closed; /** * This flag determines whether the was a response error. */ private boolean error; /** * Constructor for the FlushMonitor object. This is * used to create a monitor 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 reactor this is the reactor used to process channels * @param entity this is the entity associated with the channel */ public FlushMonitor(Initiator reactor, Entity entity) { this.channel = entity.getChannel(); this.reactor = reactor; } /** * 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 sender this is the sender used to send the response */ public void close(Sender sender) { try { if(!isClosed()) { closed = true; sender.close(); } } catch(Exception e) { fail(sender); } } /** * 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 sender this is the sender used to send the response */ public void error(Sender sender) { try { if(!isClosed()) { error = true; sender.close(); } } catch(Exception e) { fail(sender); } } /** * 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 sender this is the sender used to send the response */ public void ready(Sender sender) { try { if(!isClosed()) { closed = true; sender.flush(); reactor.start(channel); } } catch(Exception e) { fail(sender); } } /** * This is used to purge the sender so that it closes the socket * ensuring there is no connection leak on shutdown. This is used * when there is an exception signaling the state of the sender. * * @param sender this is the sender that is to be purged */ private void fail(Sender sender) { try { sender.close(); } catch(Exception e) { return; } } /** * This is used to determine if the response has completed or * if there has been an error. This basically allows the sender * 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 || error; } /** * This is used to determine if the response was in error. If * the response was in error this allows the sender 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; } } simple-http-4.1.21/src/org/simpleframework/http/core/PartEntryConsumer.java0000644000175000017500000000732211417313373027531 0ustar jamespagejamespage/* * PartEntryConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.Cursor; import org.simpleframework.util.buffer.Allocator; /** * The PartEntryConsumer 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 Consumer { /** * This is used to consume the boundary at the end of a part. */ private BoundaryConsumer boundary; /** * This is used to consume the actual part from the list. */ private Consumer consumer; /** * Constructor for the PartEntryConsumer 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 list this is the list used to accumulate the parts * @param terminal this is the terminal token for the part list */ public PartEntryConsumer(Allocator allocator, PartList list, byte[] terminal) { this.consumer = new PartConsumer(allocator, list, terminal); 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 Part 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(Cursor 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(); } }simple-http-4.1.21/src/org/simpleframework/http/core/PartList.java0000644000175000017500000000513611417313373025630 0ustar jamespagejamespage/* * PartList.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.ArrayList; import org.simpleframework.http.Part; import org.simpleframework.util.KeyMap; /** * The PartList 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 Part object by name it must * have had a name within the Content-Disposition header. * * @author Niall Gallagher */ class PartList extends ArrayList { /** * This is the key map that is used to store the part objects. */ private KeyMap map; /** * Constructor for the PartList 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 PartList() { this.map = new KeyMap(); } /** * 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 non-null name 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 */ @Override public boolean add(Part part) { String name = part.getName(); if(name != null) { map.put(name, part); } return super.add(part); } /** * This method is used to acquire a Part 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); } } simple-http-4.1.21/src/org/simpleframework/http/core/FixedConsumer.java0000644000175000017500000001245111417313373026637 0ustar jamespagejamespage/* * FixedConsumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.InputStream; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.Buffer; /** * The FixedConsumer object reads a fixed number of bytes * from the 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 */ class FixedConsumer 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 int limit; /** * Constructor for the FixedConsumer object. This is * used to create a consumer that reads a fixed number of bytes * from the 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 FixedConsumer(Allocator allocator, int limit) { this.allocator = allocator; this.limit = limit; } /** * 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 */ @Override 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. * * @param charset this is the charset encoding to be used * * @return returns an encoded string representation of the body */ @Override 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 */ @Override public InputStream getInputStream() throws IOException { if(buffer == null) { return new EmptyInputStream(); } return buffer.getInputStream(); } /** * 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 = 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; } /** * This method is used to allocate the internal buffer. If there * has already been a call to this method the previous instance * is returned. If there is any issue allocating the buffer then * this will throw an exception. * * @return this returns the buffer to append the bytes to */ @Override protected Buffer allocate() throws IOException { if(buffer == null) { buffer = allocator.allocate(limit); } return buffer; } } simple-http-4.1.21/src/org/simpleframework/http/core/RequestEntity.java0000644000175000017500000003236111417313373026713 0ustar jamespagejamespage/* * RequestEntity.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.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.Map; import org.simpleframework.http.ContentType; import org.simpleframework.http.Form; import org.simpleframework.http.Part; import org.simpleframework.http.Request; import org.simpleframework.http.session.Session; import org.simpleframework.util.lease.LeaseException; /** * 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. *

* The Request is used to provide an interface to the * HTTP InputStream and message header. The stream can * have certain characteristics, these characteristics are available * by this object. The Request provides methods that * allow the InputStream's semantics to be known, for * example if the stream is keep-alive or if the stream has a length. *

* The Request origin is also retrievable from the * Request as is the attributes Map object * which defines specific connection attributes. And acts as a * simple model for the request transaction. *

* It is important to note that the Request 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 InputStream. 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 will create the form object using the query and body. */ private FormCreator builder; /** * This channel represents the connected pipeline used. */ private Channel channel; /** * The entity contains all the constituent request parts. */ private Entity entity; /** * 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; /** * The form contains the parts and parameters of the request. */ private Form form; /** * Constructor for the RequestEntity 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 entity this is the entity that was sent by the client * @param monitor this is the monitor used to monitor events */ public RequestEntity(Entity entity, Monitor monitor) { this.builder = new FormCreator(this, entity); this.channel = entity.getChannel(); this.header = entity.getHeader(); this.body = entity.getBody(); this.entity = entity; } /** * 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(){ if(contains("Connection")) { return !contains("Connection", "close"); } else if(getMajor() > 1) { return true; } else if(getMajor() == 1) { return getMinor() > 0; } return false; } /** * 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("UTF-8"); } 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 = "UTF-8"; } return body.getContent(charset); } /** * This is used to read the content body. The specifics of the data * that is read from this InputStream can be determined * by the getContentLength 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 Part 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 ReadableByteChannel can be * determined by the getContentLength 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 method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @return returns an active session object for the client */ public Session getSession() throws LeaseException { return getSession(true); } /** * This method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @param create creates the session if it does not exist * * @return returns an active session object for the client */ public Session getSession(boolean create) throws LeaseException { return entity.getSession(create); } /** * 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 Map * 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 is used to provide quick access to the parameters. This * avoids having to acquire the request Form object. * This basically acquires the parameters object and invokes * the getParameters method with the given name. * * @param name this is the name of the parameter value */ public String getParameter(String name) throws IOException { if(form == null) { form = builder.getInstance(); } return form.get(name); } /** * This method is used to acquire a Part from the * form 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 are added to the query values * as strings so that they can be used in a convenient way. * * @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) throws IOException { if(form == null) { form = builder.getInstance(); } return form.getPart(name); } /** * This is used to acquire all the form parameters from the * HTTP request. This includes the query and POST data values * as well as the parts of a multipart request. The form is * a convenience object enabling easy access to state. * * @return this returns the form containing the state */ public Form getForm() throws IOException { if(form == null) { form = builder.getInstance(); } return form; } } simple-http-4.1.21/src/org/simpleframework/http/core/TransportChannel.java0000644000175000017500000001240011417313373027343 0ustar jamespagejamespage/* * TransportChannel.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.nio.channels.SocketChannel; import java.util.Map; import javax.net.ssl.SSLEngine; import org.simpleframework.transport.Cursor; import org.simpleframework.transport.Transport; import org.simpleframework.transport.TransportCursor; /** * The TransportChannel provides a means to deliver and * receive content over a transport. This essentially provides two * adapters which enable simpler communication with the underlying * transport. They hide the complexities involved with buffering and * resetting data written to and read from the socket. * * @author Niall Gallagher * * @see org.simpleframework.transport.Processor */ class TransportChannel implements Channel { /** * This represents the underlying transport that is to be used. */ private final Transport transport; /** * This is the engine that is used to secure the transport. */ private final SSLEngine engine; /** * This is used to provide a cursor view on the input. */ private final Cursor cursor; /** * This is used to provide a blocking means for sending data. */ private final Sender sender; /** * Constructor for the TransportChannel object. The * transport channel basically wraps a channel and provides a * means to send and receive data using specialized adapters. * These adapters provide a simpler means for communicating over * the network to the connected client. * * @param transport this is the underlying transport to be used */ public TransportChannel(Transport transport) { this.cursor = new TransportCursor(transport); this.sender = new TransportSender(transport); this.engine = transport.getEngine(); this.transport = transport; } /** * This is used to determine if the channel is secure and that * data read from and data written to the request is encrypted. * Channels transferred over SSL are considered secure and will * have this return true, otherwise it will return false. * * @return true if this is secure for reading and writing */ public boolean isSecure() { return engine != null; } /** * This is the connected socket channel associated with this. In * order to determine if content can be read or written to or * from the channel this socket can be used with a selector. This * provides a means to react to I/O events as they occur rather * than polling the channel which is generally less performant. * * @return this returns the connected socket channel */ public SocketChannel getSocket() { return transport.getChannel(); } /** * This returns the Map of attributes used to hold * connection information for the channel. The attributes here * are taken from the pipeline attributes and may contain details * such as SSL certificates or other such useful information. * * @return returns the attributes associated with the channel */ public Map getAttributes() { return transport.getAttributes(); } /** * This provides the Cursor for this channel. The * cursor provides a resettable view of the input buffer and will * allow the server kernel to peek into the input buffer without * having to take the data from the input. This allows overflow * to be pushed back on to the cursor for subsequent reads. * * @return this returns the input cursor for the channel */ public Cursor getCursor() { return cursor; } /** * This provides the Sender for the channel. This is * used to provide a blocking output mechanism for the channel. * Enabling blocking reads ensures that output buffering can be * limited to an extent, which ensures that memory remains low at * high load periods. Writes to the sender may result in the data * being copied and queued until the socket is write ready. * * @return this returns the output sender for this channel */ public Sender getSender() { return sender; } /** * Because the channel represents a duplex means of communication * there needs to be a means to close it down. This provides such * a means. By closing the channel the cursor and sender will no * longer send or recieve data to or from the network. The client * will also be signaled that the connection has been severed. */ public void close() { try { transport.close(); }catch(Exception e) { return; } } } simple-http-4.1.21/src/org/simpleframework/http/core/PartConsumer.java0000644000175000017500000001033211417313373026502 0ustar jamespagejamespage/* * PartConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.Cursor; import org.simpleframework.util.buffer.Allocator; /** * The PartConsumer 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 Consumer { /** * 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 the current consumer used to read from the cursor. */ private Consumer current; /** * This is used to add the consumed parts to when finished. */ private PartList list; /** * This is the terminal token that ends the part payload. */ private byte[] terminal; /** * Constructor for the PartConsumer 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 list this is the part list used to store the parts * @param terminal this is the terminal token for the part */ public PartConsumer(Allocator allocator, PartList list, byte[] terminal) { this.header = new PartHeaderConsumer(allocator); this.factory = new PartFactory(allocator, header); this.terminal = terminal; this.current = header; this.list = list; } /** * 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() throws IOException { return factory.getInstance(list, 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 Part 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(Cursor 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; } } simple-http-4.1.21/src/org/simpleframework/http/core/Initiator.java0000644000175000017500000000352511417313373026030 0ustar jamespagejamespage/* * Initiator.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Initiator represents an interface in to the main * processor for the server kernel. It is used to read and parse a * new HTTP request from the connected socket and dispatch that * request to the Container when ready. All channels * begin at the initiator, and when a request has finished it is * passed back in to the initiator to further process requests from * the HTTP pipeline. * * @author Niall Gallagher * * @see org.simpleframework.http.core.Monitor */ interface Initiator { /** * 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 Container 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; } simple-http-4.1.21/src/org/simpleframework/http/core/BodyConsumer.java0000644000175000017500000000764411417313373026505 0ustar jamespagejamespage/* * BodyConsumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.InputStream; import org.simpleframework.http.Part; /** * The BodyConsumer is used to consume the body of an * HTTP message. Implementations of this consumer must provide the * size of the content read and an InputStream to read * that content. Providing these features in a consumer allows the * server to treat the body as a simple stream source. * * @author Niall Gallagher */ abstract class BodyConsumer extends BufferConsumer implements Body { /** * Constructor for the BodyConsumer object. This * will create a consumer that provides default implementations * for the Body methods. This is a convenience * implementation that can be extended body consumers. */ protected BodyConsumer() { super(); } /** * 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 { return new String(); } /** * 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. * * @param charset this is the charset encoding to be used * * @return returns an encoded string representation of the body */ public String getContent(String charset) throws IOException { return new String(); } /** * 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 { return new EmptyInputStream(); } /** * This method is used to acquire a Part from the * body 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 are added to the query values * as strings so that they can be used in a convenient way. * * @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 null; } /** * This method provides all parts for this body. The parts for a * body can contain text parameters or files. Each file part can * contain headers, which are the typical HTTP headers. Typically * headers describe the content and any encoding if required. * * @return this returns a list of parts for this body */ public PartList getParts() { return new PartList(); } } simple-http-4.1.21/src/org/simpleframework/http/core/Dispatcher.java0000644000175000017500000000732111417313373026152 0ustar jamespagejamespage/* * Dispatcher.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Dispatcher 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 throwables from the contains 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 Dispatcher implements Runnable { /** * 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 monitor object used to signal completion events. */ private final Monitor monitor; /** * This is the entity containing the raw request details. */ private final Entity entity; /** * Constructor for the Dispatcher object. This creates * a request and response object using the provided entity, these * can then be passed to the container to handle the transaction. * * @param container this is the container to handle the request * @param reactor the reactor used to handle the next request * @param entity this contains the current request entity */ public Dispatcher(Container container, Initiator reactor, Entity entity) { this.monitor = new FlushMonitor(reactor, entity); this.request = new RequestEntity(entity, monitor); this.response = new ResponseEntity(request, entity, monitor); this.container = container; this.entity = entity; } /** * This run 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 e) { return; } } /** * This dispatch 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 { Channel channel = entity.getChannel(); try { container.handle(request, response); } catch(Throwable e) { channel.close(); } } } simple-http-4.1.21/src/org/simpleframework/http/core/SecurePolicy.java0000644000175000017500000000614711417313373026477 0ustar jamespagejamespage/* * SecurePolicy.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.UUID; import org.simpleframework.http.Cookie; /** * The SecurePolicy object is used to create a policy * that can create cookies that are secure. This ensures that the * cookies can not be predicted and thus hijacked. Because there is * no standard for session cookies, this uses the Java Servlet API * naming convention, which names cookie JSESSIONID. * * @author Niall Gallagher */ class SecurePolicy implements Policy { /** * This provides the name of the cookies that are created. */ private static final String NAME = "JSESSIONID"; /** * This is the header to acquire the cookie value from. */ private Header header; /** * This is the cookie created for the session identifier. */ private Cookie cookie; /** * Constructor for the SecurePolicy object. This is * used to create a policy that will generate cookies which have * secure value, which ensures session hijacking is not possible. * * @param header this is the header to search for the cookie */ public SecurePolicy(Header header) { this.header = header; } /** * This is used to acquire the session cookie for the request. The * session cookie is either sent with the HTTP request header or * it can be created if required. This ensures that if no cookie * has been sent one can be created on demand. * * @param create if true the session cookie will be created * * @return the cookie associated with the session or null */ public Cookie getSession(boolean create) { if(cookie != null) { return cookie; } cookie = header.getCookie(NAME); if(cookie == null) { if(create) { cookie = getCookie(NAME); } } return cookie; } /** * This is used to create the cookie value to be used. The value * used for the cookie is created using a random number generator. * This ensures that the cookie names created are secure to a * point that they can not be hijacked by another used. * * @param name this is the name of the cookie to be created * * @return a session cookie with a secure cookie value */ private Cookie getCookie(String name) { UUID identity = UUID.randomUUID(); String value = identity.toString(); return new Cookie(name, value, true); } } simple-http-4.1.21/src/org/simpleframework/http/core/UpdateConsumer.java0000644000175000017500000001161211417313373027020 0ustar jamespagejamespage/* * UpdateConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.Cursor; /** * The UpdateConsumer 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 */ abstract class UpdateConsumer extends 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 UpdateConsumer 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(1024); } /** * Constructor for the UpdateConsumer 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 Cursor object should be read. * If there are no ready bytes then this method should return. * * @param cursor used to consume the bytes from the HTTP pipeline */ public void consume(Cursor 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(Cursor 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; } simple-http-4.1.21/src/org/simpleframework/http/core/PartListConsumer.java0000644000175000017500000001767711417313373027361 0ustar jamespagejamespage/* * PartListConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.InputStream; import org.simpleframework.http.Part; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.BufferAllocator; /** * The PartListConsumer 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 PartListConsumer extends 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 PartList list; /** * Constructor for the PartListConsumer 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 PartListConsumer(Allocator allocator, byte[] boundary) { this(allocator, boundary, 8192); } /** * Constructor for the PartListConsumer 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 PartListConsumer(Allocator allocator, byte[] boundary, int length) { this(allocator, new PartList(), boundary, length); } /** * Constructor for the PartListConsumer 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 list this is the part list used to accumulate the parts */ public PartListConsumer(Allocator allocator, PartList list, byte[] boundary) { this(allocator, list, boundary, 8192); } /** * Constructor for the PartListConsumer 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 * @param list this is the part list used to accumulate the parts */ public PartListConsumer(Allocator allocator, PartList list, byte[] boundary, int length) { this.buffer = new BufferAllocator(allocator, length); this.consumer = new PartEntryConsumer(buffer, list, boundary); this.factory = new PartEntryFactory(buffer, list, boundary); this.list = list; } /** * 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 PartList 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(Cursor 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(); } /** * 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 */ @Override public String getContent() throws IOException { return buffer.encode(); } /** * 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. * * @param charset this is the character encoding to be used * * @return this returns a string representing the content */ @Override public String getContent(String charset) throws IOException { return buffer.encode(charset); } /** * This is used to acquire an InputStream 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 */ @Override public InputStream getInputStream() throws IOException { return buffer.getInputStream(); } /** * This method is used to acquire a Part from the * body 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 are added to the query values * as strings so that they can be used in a convenient way. * * @param name this is the name of the part to acquire * * @return the named part or null if the part does not exist */ @Override public Part getPart(String name) { return list.getPart(name); } /** * This method provides all parts for this body. The parts for a * body can contain text parameters or files. Each file part can * contain headers, which are the typical HTTP headers. Typically * headers describe the content and any encoding if required. * * @return this returns a list of parts for this body */ @Override public PartList getParts() { return list; } } simple-http-4.1.21/src/org/simpleframework/http/core/EmptyProducer.java0000644000175000017500000001101211417313373026656 0ustar jamespagejamespage/* * EmptyProducer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 EmptyProducer 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 EmptyProducer implements Producer { /** * This is the monitor that is used to process the pipeline. */ private final Monitor monitor; /** * This is the sender that is passed to the monitor when ready. */ private final Sender sender; /** * Constructor for the EmptyProducer 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 sender this is used to send to the underlying transport * @param monitor this is used to deliver signals to the kernel */ public EmptyProducer(Sender sender, Monitor monitor) { this.monitor = monitor; this.sender = sender; } /** * 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 produce(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 produce(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 produce(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 produce(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 { monitor.ready(sender); } } simple-http-4.1.21/src/org/simpleframework/http/core/Segment.java0000644000175000017500000001317611417313373025473 0ustar jamespagejamespage/* * Segment.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.ContentType; import java.util.List; /** * The Segment 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 */ 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 */ public 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 */ public 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 */ public 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 */ public String getValue(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 substring and trim calls. *

* 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. *

* 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 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 Content-Type 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(); /** * 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 Content-Disposition 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 Disposition 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 Transfer-Encoding 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(); /** * This is a convenience method that can be used to determine * the length of the message body. This will determine if there * is a Content-Length 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 int getContentLength(); } simple-http-4.1.21/src/org/simpleframework/http/core/Channel.java0000644000175000017500000001013511417313373025431 0ustar jamespagejamespage/* * Channel.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.nio.channels.SocketChannel; import java.util.Map; import org.simpleframework.transport.Cursor; /** * The Channel interface represents a connected channel * through which data can be sent and received. Typically a channel * will have a connected TCP socket, which can be used to determine * when the channel is read ready, and write ready. A channel can * also contain a bag of attributes used to describe the connection. *

* Reading and writing to a channel is performed using two special * interfaces. The first is the Cursor object which is * used to read data from the channel in a non-blocking manner. This * can also be used to reset data if it has read too much. To write * the Sender can be used, this provides a blocking * interface much like a conventional output stream. * * @author Niall Gallagher */ interface Channel { /** * This is used to determine if the channel is secure and that * data read from and data written to the request is encrypted. * Channels transferred over SSL are considered secure and will * have this return true, otherwise it will return false. * * @return true if this is secure for reading and writing */ public boolean isSecure(); /** * This is the connected socket channel associated with this. In * order to determine if content can be read or written to or * from the channel this socket can be used with a selector. This * provides a means to react to I/O events as they occur rather * than polling the channel which is generally less performant. * * @return this returns the connected socket channel */ public SocketChannel getSocket(); /** * This provides the Cursor for this channel. The * cursor provides a resettable view of the input buffer and will * allow the server kernel to peek into the input buffer without * having to take the data from the input. This allows overflow * to be pushed back on to the cursor for subsequent reads. * * @return this returns the input cursor for the channel */ public Cursor getCursor(); /** * This provides the Sender for the channel. This is * used to provide a blocking output mechanism for the channel. * Enabling blocking reads ensures that output buffering can be * limited to an extent, which ensures that memory remains low at * high load periods. Writes to the sender may result in the data * being copied and queued until the socket is write ready. * * @return this returns the output sender for this channel */ public Sender getSender(); /** * This returns the Map of attributes used to hold * connection information for the channel. The attributes here * are taken from the pipeline attributes and may contain details * such as SSL certificates or other such useful information. * * @return returns the attributes associated with the channel */ public Map getAttributes(); /** * Because the channel represents a duplex means of communication * there needs to be a means to close it down. This provides such * a means. By closing the channel the cursor and sender will no * longer send or recieve data to or from the network. The client * will also be signaled that the connection has been severed. */ public void close(); }simple-http-4.1.21/src/org/simpleframework/http/core/BoundaryConsumer.java0000644000175000017500000001463411417313373027370 0ustar jamespagejamespage/* * BoundaryConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.buffer.Allocator; import org.simpleframework.util.buffer.Buffer; /** * The BoundaryConsumer 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 BoundaryConsumer 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 + 6; this.allocator = allocator; this.boundary = boundary; } /** * This method is used to allocate the internal buffer. If there * has already been a call to this method the previous instance * is returned. If there is any issue allocating the buffer then * this will throw an exception. * * @return this returns the buffer to append the bytes to */ @Override protected Buffer allocate() throws IOException { if(buffer == null) { buffer = allocator.allocate(chunk); } return buffer; } /** * 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 { append(TOKEN); append(boundary); if(seek == chunk) { append(TOKEN); } append(LINE); } /** * 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 >= size + 4) { if(array[size + 2] == LAST[0]) { return boundary(LAST); } return boundary(LINE); } return 0; } /** * This is used to scan the boundary with an optional terminal. If * the terminal token is scanned correctly then this will return * the number of bytes of overflow that has been consumed. * * @param terminal this is the terminal token for the boundary * * @return this returns the number of bytes of overflow consumed */ private int boundary(byte[] terminal) throws IOException { int size = boundary.length + 2; if(count >= size + terminal.length) { scan(TOKEN); scan(boundary); scan(terminal); done = true; return count - seek; } return 0; } /** * 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 - 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; } }simple-http-4.1.21/src/org/simpleframework/http/core/BuilderConsumer.java0000644000175000017500000001016011417313373027161 0ustar jamespagejamespage/* * BuilderConsumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.Cursor; import org.simpleframework.util.buffer.Allocator; /** * The BuilderConsumer 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 passed to a builder * which will use the parts to create an entry. Once all parts have * been consumed and given to the builder then this is finished. * * @author Niall Gallagher */ class BuilderConsumer implements Consumer { /** * 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 determine if there a continue is expected. */ protected Expectation expect; /** * This is used to build the entity from the part and header. */ protected Builder builder; /** * Constructor for the BuilderConsumer object. This * is used to build an entity from the constituent parts. Once * all of the parts have been consumed they are passed to the * builder and the consumer is considered finished. * * @param allocator this is used to allocate the memory used * @param builder this is used to build the request entity * @param channel this is the channel used to send a response */ public BuilderConsumer(Allocator allocator, Builder builder, Channel channel) { this.header = new RequestConsumer(); this.expect = new Expectation(channel); this.factory = new ConsumerFactory(allocator, header); this.builder = builder; } /** * 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(Cursor cursor) throws IOException { while(cursor.isReady()) { if(header.isFinished()) { if(body == null) { body = factory.getInstance(); } body.consume(cursor); if(body.isFinished()) { break; } } else { header.consume(cursor); } } if(header.isFinished()) { if(body == null) { expect.execute(header); body = factory.getInstance(); } builder.setBody(body); builder.setHeader(header); } } /** * 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) { body = factory.getInstance(); } return body.isFinished(); } return false; } } simple-http-4.1.21/src/org/simpleframework/http/core/Transfer.java0000644000175000017500000002476411417313373025662 0ustar jamespagejamespage/* * Transfer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.http.Response; /** * The Transfer 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 start 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.Producer */ class Transfer { /** * This is used to create a producer based on the HTTP headers. */ private ProducerFactory 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 Producer producer; /** * Constructor for the Transfer object, this is used * to create an object used to transfer a response body. This must * be given a Conversation that can be used to set * and get information regarding the type of transfer required. * * @param support this is used to determine the semantics * @param sender this is used to send data over the transport * @param monitor this is used to signal I/O events to the kernel */ public Transfer(Conversation support, Sender sender, Monitor monitor) { this.factory = new ProducerFactory(support, sender, monitor); this.response = support.getResponse(); this.support = support; } /** * This is used to determine if the transfer has started. It has * started when a producer 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 producer != 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(producer != null) { throw new TransferException("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(producer != null) { throw new TransferException("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 Producer object to * encode the response body as required. If the producer 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 Producer object to * encode the response body as required. If the producer 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(producer == null) { throw new TransferException("Conversation details not ready"); } producer.produce(array, off, len); } /** * This method is used to write content to the underlying socket. * This will make use of the Producer object to * encode the response body as required. If the producer 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 TransferException("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 Producer object to * encode the response body as required. If the producer 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(producer == null) { throw new TransferException("Conversation details not ready"); } producer.produce(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(producer == null) { throw new TransferException("Conversation details not ready"); } producer.flush(); } /** * 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(producer == null) { throw new TransferException("Conversation details not ready"); } producer.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 { int length = support.getContentLength(); boolean empty = support.isEmpty(); if(empty) { support.setContentLength(0); } else if(length >= 0) { support.setContentLength(length); } else { support.setChunkedEncoded(); } producer = 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(int count) throws IOException { int 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(int count, int length) throws IOException { boolean empty = support.isEmpty(); if(empty) { support.setContentLength(0); } else if(length >= 0) { support.setContentLength(length); } else { support.setChunkedEncoded(); } producer = 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 producer is created. */ private void commit() throws IOException { try { response.commit(); } catch(Exception cause) { throw new TransferException("Unable to commit", cause); } } } simple-http-4.1.21/src/org/simpleframework/http/core/TrackerBuilder.java0000644000175000017500000001172311417313373026767 0ustar jamespagejamespage/* * TrackerBuilder.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session.Session; import org.simpleframework.util.lease.LeaseException; /** * The TrackerBuilder object is used to build an entity * with the aid of a session tracker implementation. This basically * accepts the header and body for the entity and delegates the * session creation to the tracker. * * @author Niall Gallagher * * @see org.simpleframework.http.core.CookieTracker */ class TrackerBuilder implements Builder { /** * This is the tracker object used to create the sessions. */ private Tracker tracker; /** * This is the channel that represents the underlying transport. */ private Channel channel; /** * This is the header object that holds the request headers. */ private Header header; /** * This is the body object used to hold the body delivered. */ private Body body; /** * Constructor for the TrackerBuilder object. This * is used to create a builder that delegates creation of the * session object to a tracker implementation. Also the channel * for the entity must also be provided. * * @param tracker this is the tracker used to create sessions * @param channel this is the channel representing the transport */ public TrackerBuilder(Tracker tracker, Channel channel) { this.tracker = tracker; this.channel = channel; } /** * Provides the Body object for the builder. This * is used by the entity to read the content of the HTTP request. * Also, if the entity body is a multipart upload then each of * the individual parts of the body is available to read from. * * @param body this is the entity body provided by the request */ public void setBody(Body body) { this.body = body; } /** * Provides the Header object for the builder. This * is used by the entity to determine the request URI and method * type. The header also provides start in the form of cookies * which can be used to track the client. * * @param header this is the header provided by the request */ public void setHeader(Header header) { this.header = header; } /** * 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 Part 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; } /** * 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 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 method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @param create creates the session if it does not exist * * @return returns an active session object for the entity */ public Session getSession(boolean create) throws LeaseException { return tracker.getSession(this, create); } } simple-http-4.1.21/src/org/simpleframework/http/core/PartEntryFactory.java0000644000175000017500000000471111417313373027344 0ustar jamespagejamespage/* * PartEntryFactory.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.buffer.Allocator; /** * This PartEntryFactory 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.core.PartListConsumer */ class PartEntryFactory { /** * This is used to allocate the buffers used by the entry. */ private Allocator allocator; /** * This is used to accumulate all the parts of the upload. */ private PartList list; /** * This is the terminal token used to delimiter the upload. */ private byte[] terminal; /** * Constructor for the PartEntryFactory 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 list this is the list of parts that are extracted * @param terminal this is the terminal buffer to be used */ public PartEntryFactory(Allocator allocator, PartList list, byte[] terminal) { this.allocator = allocator; this.terminal = terminal; this.list = list; } /** * 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, list, terminal); } }simple-http-4.1.21/src/org/simpleframework/http/core/EntityCollector.java0000644000175000017500000001441611417313373027212 0ustar jamespagejamespage/* * EntityCollector.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.channels.SocketChannel; import org.simpleframework.http.session.Session; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.lease.LeaseException; /** * The EntityCollector 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 Selector object for processing. * When the request has completed the next request can be collected * from the underlying transport using a new collector object. * * @author Niall Gallagher */ class EntityCollector implements Collector { /** * This is used to consume the request entity from the channel. */ private final Consumer entity; /** * This is used to build the entity for its constituent parts. */ private final Builder builder; /** * This is the channel used to acquire the underlying data. */ private final Channel channel; /** * This is the cursor used to read and reset the data. */ private final Cursor cursor; /** * The EntityCollector object used to collect the * data from the underlying transport. In order to collect a body * this must be given an Allocator 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 EntityCollector(Allocator allocator, Tracker tracker, Channel channel) { this.builder = new TrackerBuilder(tracker, channel); this.entity = new BuilderConsumer(allocator, builder, channel); this.cursor = channel.getCursor(); this.channel = channel; } /** * This is used to collect the data from a Channel * which is used to compose the entity. If at any stage there * are no ready bytes on the socket the selector 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 selector as ready, * which processes the entity as a new client HTTP request. * * @param selector this is the selector used to queue this */ public void collect(Selector selector) throws IOException { while(cursor.isReady()) { if(entity.isFinished()) { break; } else { entity.consume(cursor); } } // XXX What happens to the socket connection here if(cursor.isOpen()) { // reject if closed from client side if(entity.isFinished()) { selector.ready(this); } else { selector.select(this); } } } /** * This method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @param create creates the session if it does not exist * * @return returns an active session object for the entity */ public Session getSession(boolean create) throws LeaseException { return builder.getSession(create); } /** * 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 builder.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 Part objects. Each * part can then be read as an individual message. * * @return the body provided by the HTTP request message */ public Body getBody() { return builder.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 builder.getChannel(); } /** * 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(); } } simple-http-4.1.21/src/org/simpleframework/http/core/RequestConsumer.java0000644000175000017500000003165111417313373027233 0ustar jamespagejamespage/* * RequestConsumer.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.List; import org.simpleframework.http.Address; import org.simpleframework.http.Path; import org.simpleframework.http.Query; import org.simpleframework.http.parse.AddressParser; /** * The RequestConsumer 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 */ 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 RequestConsumer 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 corrosponds 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 Path 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 is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(String name) { return header.contains(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 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 List 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 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(); adjust(); 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 Address object which provides * all the details of the target including the query part. */ private void target() { Token token = new Token(pos, 0); while(pos < count){ if(space(array[pos])){ pos++; break; } token.size++; pos++; } target = token.text(); } /** * 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(pos, 0); while(pos < count){ if(space(array[pos])){ pos++; break; } token.size++; pos++; } method = token.text(); } /** * 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 is used to determine if a given ISO-8859-1 byte is a white * space character, such as a tab or space. 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 space(byte octet) { switch(octet) { case ' ': case '\r': case '\n': case '\t': return true; default: return false; } } /** * 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. * * @author Niall Gallagher */ 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 Token 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; } } } } simple-http-4.1.21/src/org/simpleframework/http/core/Sender.java0000644000175000017500000000737111417313373025311 0ustar jamespagejamespage/* * Sender.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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 Sender object is used to send data over the TCP * transport. This provides direct contact with the connected socket. * Delivery over a sender implementation can be either SSL based or * direct as with plain HTTP connections. It is the responsibility * of the implementation to provide such behavior as required. * * @author Niall Gallagher */ interface Sender { /** * This method is used to deliver the provided array of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @param array this is the array of bytes to send to the client */ public void send(byte[] array) throws IOException; /** * This method is used to deliver the provided array of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @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 send(byte[] array, int off, int len) throws IOException; /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @param buffer this is the buffer of bytes to send to the client */ public void send(ByteBuffer buffer) throws IOException; /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @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 send(ByteBuffer buffer, int off, int len) 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. */ public void flush() throws IOException; /** * This is used to close the sender and the underlying transport. * If a close is performed on the sender then no more bytes can * be read from or written to the transport and the client will * received a connection close on their side. */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/http/core/HeaderConsumer.java0000644000175000017500000001215611417313373026772 0ustar jamespagejamespage/* * HeaderConsumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 org.simpleframework.http.Cookie; /** * The HeaderConsumer 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 */ abstract class HeaderConsumer extends SegmentConsumer implements Header { /** * This is the policy object used to create the session cookie. */ private Policy policy; /** * Constructor for the HeaderConsumer object. This * is used to create a consumer capable of reading a header from * a provided cursor. All methods of the Header * interface are implemented in this object. */ protected HeaderConsumer() { this.policy = new SecurePolicy(this); } /** * This is used to see if there is a HTTP message header with the * given name in this container. If there is a HTTP message header * with the specified name then this returns true otherwise false. * * @param name the HTTP message header to get the value from * * @return this returns true if the HTTP message header exists */ public boolean contains(String name) { return header.contains(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 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 List 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 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 Cookie 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 the session cookie for the request. The * session cookie is either sent with the HTTP request header or * it can be created if required. This ensures that if no cookie * has been sent one can be created on demand. * * @param create if true the session cookie will be created * * @return the cookie associated with the session or null */ public Cookie getSession(boolean create) { return policy.getSession(create); } /** * 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 Cookie 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 getCookies() { return header.getCookies(); } } simple-http-4.1.21/src/org/simpleframework/http/core/FormCreator.java0000644000175000017500000001405011417313373026304 0ustar jamespagejamespage/* * FormCreator.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.ContentType; import org.simpleframework.http.Form; import org.simpleframework.http.Part; import org.simpleframework.http.Query; import org.simpleframework.http.Request; /** * The FormCreator object is used to create the form. * It is created using the request URI query and a form post body if * sent, also if the request is a multipart upload the text parts * are added to the form. Building a single object form multiple * sources within the request ensures there is a single convenient * means to access the data from the request. * * @author Niall Gallagher * * @see org.simpleframework.http.Form */ class FormCreator { /** * This is the request that is used to acquire the data. */ private final Request request; /** * This is used to acquire the content from the request body. */ private final Body body; /** * Constructor for the FormCreator object. This will * create an object that can be used to construct a single form * from the multiple sources of data within the request entity. * * @param request this is the request used to acquire the data * @param entity this is the entity that contains the data */ public FormCreator(Request request, Entity entity) { this.body = entity.getBody(); this.request = request; } /** * This is used to acquire a Form using all of the * parameters from the request URI as well as a form post if * it exists, finally all the text parts from an upload are * added to the form. This ensures that all data is accessible. * * @return this returns the form created from the request */ public Form getInstance() throws IOException { PartList list = getParts(); Query form = getQuery(); for(Part part : list) { String name = part.getName(); String value = part.getContent(); form.put(name, value); } return new PartForm(body, form); } /** * 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 */ private Query getQuery() throws IOException { Query query = request.getQuery(); if(!isFormPost()) { return query; } return getQuery(query); // only get if form } /** * 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) throws IOException { String body = request.getContent(); if(body == null) { return query; } return new QueryForm(query, body); } /** * This method provides all parts for this body. The parts for a * body can contain text parameters or files. Each file part can * contain headers, which are the typical HTTP headers. Typically * headers describe the content and any encoding if required. * * @return this returns a list of parts for this body */ private PartList getParts() { PartList list = body.getParts(); if(list.isEmpty()) { return list; } return getParts(list); } /** * This method provides all parts for this body. The parts for a * body can contain text parameters or files. Each file part can * contain headers, which are the typical HTTP headers. Typically * headers describe the content and any encoding if required. * * @param body this is the part list taken from the body * * @return this returns a list of parts for this body */ private PartList getParts(PartList body) { PartList list = new PartList(); for(Part part : body) { if(!part.isFile()) { list.add(part); } } return list; } /** * 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("x-www-form-urlencoded"); } } simple-http-4.1.21/src/org/simpleframework/http/core/Consumer.java0000644000175000017500000000460711417313373025663 0ustar jamespagejamespage/* * Consumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.Cursor; /** * The Consumer 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. *

* 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 CRLF 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.Cursor */ interface Consumer { /** * 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 Cursor object should be read. * If there are no ready bytes then this method should return. * * @param cursor used to consume the bytes from the HTTP pipeline */ public void consume(Cursor 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 */ public boolean isFinished(); } simple-http-4.1.21/src/org/simpleframework/http/core/PartHeaderConsumer.java0000644000175000017500000000526111417313373027620 0ustar jamespagejamespage/* * PartHeaderConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.buffer.Allocator; import org.simpleframework.util.buffer.Buffer; /** * The PartHeaderConsumer 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 PartHeaderConsumer 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); } } simple-http-4.1.21/src/org/simpleframework/http/core/EmptyInputStream.java0000644000175000017500000000257511417313373027364 0ustar jamespagejamespage/* * EmptyInputStream.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.InputStream; /** * The EmptyInputStream 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; } } simple-http-4.1.21/src/org/simpleframework/http/core/ChunkedProducer.java0000644000175000017500000001720511417313373027153 0ustar jamespagejamespage/* * ChunkedProducer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 ChunkedProducer 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 CloseProducer for HTTP/1.1 as * it maintains the pipeline and thus the cost of creating it. * * @author Niall Gallagher * * @see org.simpleframework.http.core.ChunkedConsumer */ class ChunkedProducer implements Producer { /** * 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 monitor used to notify the initiator of events. */ private Monitor monitor; /** * This is the underlying sender used to deliver the encoded data. */ private Sender sender; /** * Constructor for the ChunkedProducer 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 Sender object which will * then deliver it to the client using the underlying transport. * * @param sender this is the sender used to deliver the content * @param monitor this is the monitor used to signal I/O events */ public ChunkedProducer(Sender sender, Monitor monitor) { this.monitor = monitor; this.sender = sender; } /** * 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 produce(byte[] array) throws IOException { produce(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 produce(byte[] array, int off, int len) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(array, off, len); if(len > 0) { produce(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 produce(ByteBuffer buffer) throws IOException { int mark = buffer.position(); int size = buffer.limit(); if(mark > size) { throw new ProducerException("Buffer position greater than limit"); } produce(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 produce(ByteBuffer buffer, int off, int len) throws IOException { int pos = 7; if(monitor.isClosed()) { throw new ProducerException("Stream has been closed"); } if(len > 0) { for(int num = len; num > 0; num >>>= 4){ size[pos--] = index[num & 0xf]; } try { sender.send(size, pos + 1, 9 - pos); sender.send(buffer, off, len); sender.send(size, 8, 2); } catch(Exception cause) { if(sender != null) { monitor.error(sender); } throw new ProducerException("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(!monitor.isClosed()) { sender.flush(); } } catch(Exception cause) { if(sender != null) { monitor.close(sender); } throw new ProducerException("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 { sender.send(zero); monitor.ready(sender); } catch(Exception cause) { if(sender != null) { monitor.close(sender); } throw new ProducerException("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(!monitor.isClosed()) { finish(); } } } simple-http-4.1.21/src/org/simpleframework/http/core/ArrayConsumer.java0000644000175000017500000001422011417313373026652 0ustar jamespagejamespage/* * ArrayConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.Cursor; /** * The ArrayConsumer 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 scan 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 */ abstract class ArrayConsumer extends BufferConsumer { /** * 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 ArrayConsumer 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 ArrayConsumer 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 ArrayConsumer 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 Cursor 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(Cursor 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 */ private void resize(int size) { 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 scan 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; } simple-http-4.1.21/src/org/simpleframework/http/core/FixedProducer.java0000644000175000017500000001532711417313373026634 0ustar jamespagejamespage/* * FixedProducer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 FixedProducer 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.core.FixedConsumer */ class FixedProducer implements Producer{ /** * This is the monitor used to notify the initiator of events. */ private Monitor monitor; /** * This is the underlying sender used to deliver the raw data. */ private Sender sender; /** * This is the number of bytes that have been sent so far. */ private int count; /** * This is the number of bytes this producer is limited to. */ private int limit; /** * Constructor for the FixedProducer object. This is * used to create a producer 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 sender this is used to send to the underlying transport * @param monitor this is used to deliver signals to the kernel * @param limit this is used to limit the number of bytes sent */ public FixedProducer(Sender sender, Monitor monitor, int limit) { this.monitor = monitor; this.sender = sender; 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 produce(byte[] array) throws IOException { produce(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 produce(byte[] array, int off, int len) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(array, off, len); if(len > 0) { produce(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 produce(ByteBuffer buffer) throws IOException { int mark = buffer.position(); int size = buffer.limit(); if(mark > size) { throw new ProducerException("Buffer position greater than limit"); } produce(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 produce(ByteBuffer buffer, int off, int len) throws IOException { int size = Math.min(len, limit - count); try { if(monitor.isClosed()) { throw new ProducerException("Response content complete"); } sender.send(buffer, off, size); if(count + size == limit) { monitor.ready(sender); } } catch(Exception cause) { if(sender != null) { monitor.error(sender); } throw new ProducerException("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(!monitor.isClosed()) { sender.flush(); } } catch(Exception cause) { if(sender != null) { monitor.error(sender); } throw new ProducerException("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(!monitor.isClosed()) { if(count < limit) { monitor.error(sender); } else { monitor.ready(sender); } } } } simple-http-4.1.21/src/org/simpleframework/http/core/Entity.java0000644000175000017500000000611411417313373025337 0ustar jamespagejamespage/* * Entity.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.session.Session; import org.simpleframework.util.lease.LeaseException; /** * The Entity object is used to represent the HTTP entity * received from the client. The entity contains a header and body as * well as the underlying Channel 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 */ interface Entity { /** * 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 Part objects. Each * part can then be read as an individual message. * * @return the body provided by the HTTP request message */ public 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(); /** * This method is used to acquire a Session for the * request. The object retrieved provides a container for data * associated to the connected client. This allows the request * to perform more complex operations based on knowledge that is * built up through a series of requests. The session is known * to the system using a Cookie, which contains * the session reference. This cookie value should not be * modified as it used to reference the active session object. * * @param create creates the session if it does not exist * * @return returns an active session object for the entity */ public Session getSession(boolean create) throws LeaseException; /** * 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(); } simple-http-4.1.21/src/org/simpleframework/http/core/ChunkedConsumer.java0000644000175000017500000002234211417313373027161 0ustar jamespagejamespage/* * ChunkedConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.InputStream; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.Buffer; /** * The ChunkedConsumer 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. *

 *
 *    length := 0
 *    read chunk-size, chunk-extension (if any) and CRLF
 *    while (chunk-size > 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
 *    }
 *
 * 
* 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 */ 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 ChunkedConsumer 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 ChunkedConsumer 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 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 */ @Override 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. * * @param charset this is the charset encoding to be used * * @return returns an encoded string representation of the body */ @Override 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 */ @Override public InputStream getInputStream() throws IOException { if(buffer == null) { return new EmptyInputStream(); } return buffer.getInputStream(); } /** * 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 allocate the internal buffer. If there * has already been a call to this method the previous instance * is returned. If there is any issue allocating the buffer then * this will throw an exception. * * @return this returns the buffer to append the bytes to */ @Override protected Buffer allocate() throws IOException { if(buffer == null) { buffer = allocator.allocate(); } return buffer; } /** * This method is used to convert the size in hexidecimal to a * decimal int. 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 byte, is a hexidecimal * char this will convert it into its integer equivelant. So a * char of A is converted into 10. * * @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 - (int)'A') + 10; } if(octet >= '0' && octet <= '9') { return octet - (int)'0'; } if(octet >= 'a' && octet <= 'f') { return (octet - (int)'a') + 10; } return -1; } } simple-http-4.1.21/src/org/simpleframework/http/core/QueryForm.java0000644000175000017500000001121411417313373026011 0ustar jamespagejamespage/* * QueryForm.java May 2003 * * Copyright (C) 2003, Niall Gallagher * * 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 QueryForm is used to parse several text strings * as a complete URL encoded parameter string. This will do the * following concatenations. * *
 * null + "a=b&c=d&e=f" = "a=b&c=d&e=f"
 * "a=b" + "e=f&g=h" = "a=b&e=f&g=h";
 * "a=b&c=d&e=f" + "" = "a=b&c=d&e=f"
 * 
* * This ensures that the QueryForm 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 QueryForm 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 QueryForm(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 QueryForm(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 QueryForm(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 parse(String) 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 keySet = query.keySet(); for(String key : keySet) { List list = query.getAll(key); String first = query.get(key); if(first != null) { all.put(key, list); map.put(key, first); } } } } simple-http-4.1.21/src/org/simpleframework/http/core/SegmentConsumer.java0000644000175000017500000005231711417313373027207 0ustar jamespagejamespage/* * SegmentConsumer.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.ArrayList; import java.util.List; import java.util.Locale; import org.simpleframework.http.ContentType; import org.simpleframework.http.Cookie; import org.simpleframework.http.parse.ContentParser; import org.simpleframework.http.parse.CookieParser; import org.simpleframework.http.parse.LanguageParser; /** * The SegmentConsumer 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 */ 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 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 parse the content type header consumed. */ protected ContentType type; /** * This is used to represent the content disposition header. */ protected Disposition part; /** * This represents the transfer encoding value of the body. */ protected String encoding; /** * This is used to store all consumed headers by the header name. */ protected Message header; /** * 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 int length; /** * 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 SegmentConsumer 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.language = new LanguageParser(); this.cookies = new CookieParser(); this.header = new Message(); 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(part == null) { return false; } return part.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(part == null) { return null; } return part.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(part == null) { return null; } return part.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 Content-Type 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 Content-Length 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 int 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 Transfer-Encoding 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 Content-Disposition 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 Disposition getDisposition() { return part; } /** * This is used to acquire the locales from the request header. The * locales are provided in the Accept-Language 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 getLocales() { if(language != null) { return language.list(); } return new ArrayList(); } /** * 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 substring and trim calls. *

* 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. *

* 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 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 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 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(); } /** * 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.add(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 Cookie 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 Accept-Language 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 ContentType 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 ContentParser(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 Disposition 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) { part = new DispositionParser(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 = Integer.parseInt(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(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 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 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. * * @author Niall Gallagher */ 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 Token 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; } } } } simple-http-4.1.21/src/org/simpleframework/http/core/ContentConsumer.java0000644000175000017500000002502111417313373027207 0ustar jamespagejamespage/* * ContentConsumer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.InputStream; import org.simpleframework.http.ContentType; import org.simpleframework.http.Part; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.Buffer; /** * The ContentConsumer 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.core.PartConsumer */ class ContentConsumer extends UpdateConsumer implements Part { /** * This represents the start of the boundary token for the body. */ private static final byte[] START = { '\r', '\n', '-', '-' }; /** * This is used to allocate the internal buffer when required. */ private Allocator allocator; /** * This is the internal buffer used to house the part body. */ private Buffer buffer; /** * Represents the HTTP headers that were provided for the part. */ private Segment segment; /** * This is the part list that this part is to be added to. */ private PartList list; /** * 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 ContentConsumer 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 list 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, PartList list, byte[] boundary) { this.allocator = allocator; this.boundary = boundary; this.segment = segment; this.list = list; } /** * 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 segment.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 segment.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 segment.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 */ public String getHeader(String name) { return segment.getValue(name); } /** * 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 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 */ @Override public String getContent() throws IOException { return getContent("ISO-8859-1"); } /** * 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. * * @param charset this is the charset encoding to be used * * @return this returns a string representing the content */ @Override public String getContent(String charset) throws IOException { if(buffer == null) { return new String(); } return buffer.encode(charset); } /** * This is used to acquire an InputStream 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 */ @Override public InputStream getInputStream() throws IOException { if(buffer == null) { return new EmptyInputStream(); } return buffer.getInputStream(); } /** * 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(Cursor 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; list.add(this); if(valid > 0) { append(array, mark, valid); } 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; } /** * This method is used to allocate the internal buffer. If there * has already been a call to this method the previous instance * is returned. If there is any issue allocating the buffer then * this will throw an exception. * * @return this returns the buffer to append the bytes to */ @Override protected Buffer allocate() throws IOException { if(buffer == null) { buffer = allocator.allocate(); } return buffer; } /** * 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 segment.toString(); } } simple-http-4.1.21/src/org/simpleframework/http/core/ResponseEntity.java0000644000175000017500000004026611417313373027064 0ustar jamespagejamespage/* * ResponseEntity.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.io.PrintStream; import java.nio.channels.WritableByteChannel; import java.util.Map; import org.simpleframework.http.ContentType; import org.simpleframework.http.Cookie; import org.simpleframework.http.Request; import org.simpleframework.http.Response; /** * This is used to represent the HTTP response. This provides methods * that can be used to set various characteristics of the response. * The OutputStream of the Response can be * retrieved from this interface as can the I.P address of the client * that will be receiving the Response. 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 Response 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. *

* 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. *

* It is important to note that the Response 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 commit method is invoked if there * is NO content body. Committing with a content body is done only if * correct content is given. The OutputStream acts as * a client and commits the response once the specified content has * been written to the issued OutputStream. * * @author Niall Gallagher */ class ResponseEntity extends ResponseMessage implements Response { /** * This is the conversation used to determine connection type. */ private Conversation support; /** * This is used to buffer the bytes that are sent to the client. */ private Accumulator buffer; /** * This is the underlying channel for the connected pipeline. */ private Channel channel; /** * This is the sender object used to deliver to response data. */ private Sender sender; /** * This is the header which contains the session cookie used. */ private Header header; /** * This is used to determine if the response has been committed. */ private boolean committed; /** * Constructor for the ResponseEntity 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 Request 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 request this is the request that was sent by the client * @param entity this is the entity that contains the channel * @param monitor this is the monitor used to signal events */ public ResponseEntity(Request request, Entity entity, Monitor monitor) { this.support = new Conversation(request, this); this.buffer = new Accumulator(support, entity, monitor); this.channel = entity.getChannel(); this.sender = channel.getSender(); this.header = entity.getHeader(); } /** * This is used as a shortcut for acquiring attributes for the * response. This avoids acquiring the Attributes * 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 Response. The attributes contains certain * properties about the Response. 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). *

* 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 OutputStream.close is used. * * @param length this is the length of the HTTP message body */ public void setContentLength(int length) { set("Content-Length", length); } /** * This determines the charset for PrintStream objects * returned from the getPrintStream 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 ISO-8859-1, * 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 Response. The * semantics of this OutputStream will be determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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 Response. The * semantics of this OutputStream will be determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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. *

* This will ensure that there is buffering done so that the output * can be reset using the reset 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 print methods provided by * the PrintStream. This will basically wrap the * getOutputStream with a buffer size of zero. *

* The retrieved PrintStream 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 text/plain; charset=UTF-8 the * resulting PrintStream 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 print methods provided by * the PrintStream. This will basically wrap the * getOutputStream with a specified buffer size. *

* The retrieved PrintStream 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 text/plain; charset=UTF-8 the * resulting PrintStream 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 getOutputStream object in * a PrintStream, which will write content using a * specified charset. The PrintStream created will not * buffer the content, it will write directly to the underlying * OutputStream where it is buffered (if there is a * buffer size greater than zero specified). In future the buffer * of the PrintStream 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 Response. The * semantics of this WritableByteChannel are determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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 Response. The * semantics of this WritableByteChannel are determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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. *

* This will ensure that there is buffering done so that the output * can be reset using the reset 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 can be used to determine whether the Response * has been committed. This is true if the Response * was committed, either due to an explicit invocation of the * commit method or due to the writing of content. If * the Response has committed the reset * method will not work in resetting content already written. * * @return true if the response has been fully committed */ public boolean isCommitted() { return committed; } /** * This is used to write the headers that where given to the * Response. Any further attempts to give headers * to the Response will be futile as only the headers * that were given at the time of the first commit will be used * in the message header. *

* 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(!committed) { Cookie cookie = header.getSession(false); if(cookie != null) { setSession(cookie); } byte[] message = getMessage(); sender.send(message); committed = true; } } /** * This is used to set the session cookie for the response. This * checks to see if the session cookie has not been overwritten. * If the cookie was overwritten then the provided session cookie * will not be used and the existing one will be used by this. * * @param session this is the session cookie that is to be set */ private void setSession(Cookie session) { String name = session.getName(); Cookie cookie = getCookie(name); if(cookie == null) { cookie = session; } if(cookie.isNew()) { setCookie(cookie); } } /** * This can be used to determine whether the Response * has been committed. This is true if the Response * was committed, either due to an explicit invocation of the * commit method or due to the writing of content. If * the Response has committed the reset * 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(); } } simple-http-4.1.21/src/org/simpleframework/http/core/ContainerProcessor.java0000644000175000017500000001031511417313373027703 0ustar jamespagejamespage/* * ContainerProcessor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.Processor; import org.simpleframework.transport.Transport; import org.simpleframework.util.buffer.Allocator; /** * The ContainerProcessor object is used to create * channels which can be used to consume and process requests. This * is basically an adapter to the Selector 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 ContainerProcessor implements Processor { /** * This is the allocator used internally be this processor. */ private final Allocator allocator; /** * This is the selector used to process the created channels. */ private final Selector selector; /** * Constructor for the ContainerProcessor object. * This is used to create a processor which will convert the * provided transport objects to channels, which can then be * processed by the selector 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 ContainerProcessor(Container container, Allocator allocator, int count) throws IOException { this(container, allocator, count, 1); } /** * Constructor for the ContainerProcessor object. * This is used to create a processor which will convert the * provided transport objects to channels, which can then be * processed by the selector 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 selector threads to use */ public ContainerProcessor(Container container, Allocator allocator, int count, int select) throws IOException { this.selector = new ContainerSelector(container, allocator, count, select); this.allocator = allocator; } /** * This is used to process the requests from a provided transport * and deliver a response to those requests. A transport can be * a direct transport or a secure transport providing SSL. *

* Typical usage of this method is to accept multiple transport * objects, each representing a unique HTTP channel to the client, * and process requests from those transports concurrently. * * @param transport the transport to process requests from */ public void process(Transport transport) throws IOException { selector.start(new TransportChannel(transport)); } /** * This method is used to stop the Processor such * that it will accept no more pipelines. Stopping the processor * ensures that all resources occupied will be released. This is * required so that all threads are stopped and released. *

* Typically this method is called once all connections to the * server have been stopped. As a final act of shutting down the * entire server all threads must be stopped, this allows collection * of unused memory and the closing of file and socket resources. */ public void stop() throws IOException { selector.stop(); allocator.close(); } }simple-http-4.1.21/src/org/simpleframework/http/core/ProducerFactory.java0000644000175000017500000000775611417313373027213 0ustar jamespagejamespage/* * ProducerFactory.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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 ProducerFactory 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.Transfer */ class ProducerFactory { /** * 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 Monitor monitor; /** * This is the underlying sender used to deliver the raw data. */ private final Sender sender; /** * Constructor for the ProducerFactory 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 support this contains details regarding the semantics * @param sender this is used to send to the underlying transport * @param monitor this is used to deliver signals to the kernel */ public ProducerFactory(Conversation support, Sender sender, Monitor monitor) { this.support = support; this.sender = sender; this.monitor = monitor; } /** * This is used to create an a Producer object that * can be used to produce 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 Producer getInstance() throws IOException { boolean keepAlive = support.isKeepAlive(); boolean chunkable = support.isChunkedEncoded(); if(!keepAlive) { return new CloseProducer(sender, monitor); } return getInstance(chunkable); } /** * This is used to create an a Producer object that * can be used to produce 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 Producer getInstance(boolean chunkable) throws IOException { int length = support.getContentLength(); if(!support.isHead()) { if(length > 0) { return new FixedProducer(sender, monitor, length); } if(chunkable) { return new ChunkedProducer(sender, monitor); } } return new EmptyProducer(sender, monitor); } } simple-http-4.1.21/src/org/simpleframework/http/Cookie.java0000644000175000017500000003436711417313373024357 0ustar jamespagejamespage/* * Cookie.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 class is used to represent a generic cookie. This exposes * the fields that a cookie can have. By default the version of the * Cookie is set to 1. The version can be configured * using the setVersion method. The domain, path, * security, and expiry of the cookie can also be set using their * respective set methods. *

* The toString method allows the Cookie * 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 Cookie instance can be converted * to a string which can be used as a a HTTP header. * * @author Niall Gallagher */ public class Cookie { /** * 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; /** * 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 Cookie that does not need * the name or value to be set. This allows the object to be * extended without the need to supply the name and value of * the cookie. If this constructor is used then the name and * values retrieved should not be null values. */ protected Cookie() { super(); } /** * Constructor of the Cookie 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. *

* 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 Cookie 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. *

* 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 Cookie 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. *

* 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 Cookie 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. *

* 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.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 Cookie to be * set. By default the version of the Cookie 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. *

* 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 Cookie * is for, these values will always be present. These are * mandatory for both the Cookie and Set-Cookie headers. *

* 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 Cookie * 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 some value * 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 "some value". * * @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 getSecure() { 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 protected */ public void setSecure(boolean secure) { this.secure = secure; } /** * 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 valie 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 toString * or toClientString 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 /pub/bin, then requests for the * resource http://hostname:port/pub/bin/README * 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 * toString or toClientString 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 Cookie to be * set. The form of the domain is specified by RFC 2109. The * value can begin with a dot, like .host.com. * This means that the cookie is visible within a specific * DNS zone like www.host.com. 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 toString 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. *

* Netscape and IE-5.0 cant or wont handle Path * it must be path also Netscape can not handle * the path in quotations such as "/path" it must * be /path. This value is never in quotations. *

* 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. * * @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 ? "" : "; max-age="+expiry)+ (secure ? "; secure;" : ";"); } } simple-http-4.1.21/src/org/simpleframework/http/session/0000755000175000017500000000000011767603362023761 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/http/session/Session.java0000644000175000017500000000323311417313373026240 0ustar jamespagejamespage/* * Session.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import java.util.Map; import org.simpleframework.util.lease.Lease; /** * The Session object is a simple leased container for * state within a web application. This is essentially a map of key * value pairs leased on a fixed duration to ensure it remains active * between we requests. If the session remains idle for sufficiently * long then it is disposed of by the SessionProvider * so that resources occupied can be released. * * @author Niall Gallagher * * @see org.simpleframework.util.lease.Lease */ public interface Session extends Map { /** * This is used to acquire the Lease object to control * the session. The lease is responsible for maintaining this map * within the application. Once the lease expires the session will * be removed and its mapped values will be available for recovery. * * @return this returns the lease used to manage this session */ public Lease getLease(); } simple-http-4.1.21/src/org/simpleframework/http/session/Controller.java0000644000175000017500000000445511417313373026747 0ustar jamespagejamespage/* * Controller.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import org.simpleframework.util.lease.Lease; import org.simpleframework.util.lease.LeaseException; /** * The Controller object is used to manage all life cycle * events for sessions. This is used to start and renew all leases * issued. The start life cycle event is used when the session is first * created, this will lease the new session for some fixed duration. * The renew event is performed when an existing session is accessed * again so that the session can be maintained for the default period. * * @author Niall Gallagher */ interface Controller { /** * The start method is used when a session is to * be created for the first time. This will ensure that the key * specified is used to dispose of the session when its idle * timeout has expired. * * @param key this is the unique key identifying the session */ public Lease start(T key) throws LeaseException; /** * The renew method is used when a session has been * accessed for a again. This ensures that the key specified is * used to dispose of the session when its idle timeout expires. * * @param key this is the unique key identifying the session */ public void renew(T key) throws LeaseException; /** * The cancel method is used when a session is no * longer required and is to be disposed of. This is typically * invoked by a web application to release occupied resources. * * @param key this is the unique key identifying the session */ public void cancel(T key) throws LeaseException; }simple-http-4.1.21/src/org/simpleframework/http/session/SessionProvider.java0000644000175000017500000000634611417313373027763 0ustar jamespagejamespage/* * SessionProvider.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import org.simpleframework.util.lease.LeaseException; /** * The SessionProvider object is used to create sessions * that are active for a given time period. Each session is referenced * using a unique key, and only one session exists per key at a time. * Once a session has been created it remains active for a fixed time * period, this time period is renewed each time that session is open. *

* When the session provider is no longer required it can be closed. * Closing the session provider will cancel and destroy all active * sessions, and release all resources occupied by the provider such * as threads and memory. Typically it is advisable to close a provider * when it is not longer required by the application. * * @author Niall Gallagher */ public interface SessionProvider { /** * This open method is used to either open an existing * session or create a new session if one does not exist. This is * always guaranteed to open a session. Upon each open for a given * session that sessions expiry is renewed for a fixed period of * time, typically this is several minutes long. * * @param key the unique key identifying the session instance * * @return returns either a new session object or an existing * * @throws LeaseException if the keyed session can not be retrieved */ public Session open(T key) throws LeaseException; /** * This open method is used to either open an existing * session or create a new session if requested. This is used to * optionally create a new session if one does not already exist. * This is used in situations where a session might not be required * but the existence of one may be queried. Once created a session * is active for a fixed time period. * * @param key the unique key identifying the session instance * * @return returns either a new session object or an existing one * * @throws LeaseException if the keyed session can not be retrieved */ public Session open(T key, boolean create) throws LeaseException; /** * This close method is used to close the provider and * release all resources associated with it. This includes canceling * all active sessions and emptying the contents of those sessions. * All threads and other such resources are released by this method. * * @throws LeaseException if the session provider can not be shutdown */ public void close() throws LeaseException; } simple-http-4.1.21/src/org/simpleframework/http/session/Composer.java0000644000175000017500000001534411417313373026412 0ustar jamespagejamespage/* * Composer.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import org.simpleframework.util.lease.Cleaner; import org.simpleframework.util.lease.Lease; import org.simpleframework.util.lease.LeaseException; import org.simpleframework.util.lease.LeaseManager; /** * The Composer object is used to create an object that * is used to create, store, and find session objects. This is also * used to dispose of sessions once they have expired. Creating a * new session is done with the compose method. This * will create a lease for the session for a fixed duration of time. * * @author Niall Gallagher */ class Composer extends ConcurrentHashMap implements Cleaner { /** * This is the lease manager used to maintain the session objects. */ private LeaseManager manager; /** * This is the controller used to manage all the session leases. */ private Controller handler; /** * This is the observer used to observe the session activity. */ private Observer observer; /** * This is used to determine whether the composer has been closed. */ private boolean closed; /** * Constructor for the Composer object. This is used * create an object to compose session objects, and to manage the * leasing of those sessions. This makes use of the existing lease * framework to manage the lifecycle of the session instances. * * @param observer this is used to observe the session manager * @param duration this is the idle duration for each session * @param unit this is the duration time unit measurement used */ public Composer(Observer observer, long duration, TimeUnit unit) { this.manager = new LeaseManager(this); this.handler = new Maintainer(manager, duration, unit); this.observer = observer; } /** * This is used to acquire an existing session using the unique * key for the session. If a session has previously been created * and the idle timeout period for that session has not expired * then this can be used to acquire and renew the session. * * @param key this is the unique key for the session object * * @return this returns the session found for the key */ public Session lookup(T key) throws LeaseException { if(closed) { throw new SessionException("Session creation is closed"); } return locate(key); } /** * This is used to acquire an existing session using the unique * key for the session. If a session has previously been created * and the idle timeout period for that session has not expired * then this can be used to acquire and renew the session. * * @param key this is the unique key for the session object * * @return this returns the session found for the key */ private Session locate(T key) throws LeaseException { Session session = get(key); if(session != null) { handler.renew(key); } return session; } /** * This is used to create a new session using the specified key * as the unique identifier for that session. The key can be * any identifier, typically it is a string however it can be * any comparable object, such as an integer. * * @param key this is the unique key for the session object * * @return this returns the session created for the key */ public Session compose(T key) throws LeaseException { if(closed) { throw new SessionException("Session creation is closed"); } return create(key); } /** * This is used to create a new session using the specified key * as the unique identifier for that session. The key can be * any identifier, typically it is a string however it can be * any comparable object, such as an integer. * * @param key this is the unique key for the session object * * @return this returns the session created for the key */ private Session create(T key) throws LeaseException { Lease lease = handler.start(key); if(lease != null) { return create(key, lease); } return null; } /** * This is used to create a new session using the specified key * as the unique identifier for that session. The key can be * any identifier, typically it is a string however it can be * any comparable object, such as an integer. * * @param key this is the unique key for the session object * @param lease this is the lease that is used by the session * * @return this returns the session created for the key */ private Session create(T key, Lease lease) throws LeaseException { Session session = new LeaseSession(lease); if(key != null) { put(key, session); } if(observer != null) { observer.create(session); } return session; } /** * This close method is used to close the provider * and release all resources associated with it. This includes * canceling all active sessions. All resources held by this are * released by this method and all can be garbage collected. * * @exception Exception if the composer can not be closed */ public void close() throws LeaseException { if(!closed) { manager.close(); } closed = true; } /** * This is used to remove the keyed session from the composer. If * the session does not exist then this will throw an exception. * Once this method has been executed the session is no longer * available and a new one is required for the specified key. * * @exception if the session does not exist or has expired */ public void clean(T key) throws Exception { Session session = remove(key); if(key != null) { handler.cancel(key); } if(observer != null) { observer.cancel(session); } } }simple-http-4.1.21/src/org/simpleframework/http/session/SessionException.java0000644000175000017500000000302011417313373030111 0ustar jamespagejamespage/* * SessionException.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import org.simpleframework.util.lease.LeaseException; /** * The SessionException is used to indicate that some * operation failed when trying to access a session or when trying * to perform some operation on an existing session. Typically this * is thrown when a session no longer exists. * * @author Niall Gallagher */ public class SessionException extends LeaseException { /** * This constructor is used if there is a description of the * event that caused the exception required. This can be given * a message used to describe the situation for the exception. * * @param template this is a description of the exception * @param list this is the list of arguments that can be used */ public SessionException(String template, Object... list) { super(template, list); } } simple-http-4.1.21/src/org/simpleframework/http/session/LeaseSession.java0000644000175000017500000000405011417313373027210 0ustar jamespagejamespage/* * Delegate.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import java.util.concurrent.ConcurrentHashMap; import org.simpleframework.util.lease.Lease; /** * The LeaseSession object is used to provide a session * instance that is created from a concurrent hash map. This makes * use of a Lease to handle all life cycle events such * as canceling and renewing the lease maintaining the session. * * @author Niall Gallagher */ class LeaseSession extends ConcurrentHashMap implements Session { /** * This is the key used to uniquely manage the session state. */ private final Lease lease; /** * Constructor for the LeaseSession object. This will * create an instance using the provided lease for the session. * The lease is used to handle all session life cycle events. * * @param lease this is used to manage all the life cycle events */ public LeaseSession(Lease lease) { this.lease = lease; } /** * This is used to acquire the Lease object to control * the session. The lease is responsible for maintaining this map * within the application. Once the lease expires the session will * be removed and its mapped values will be available for recovery. * * @return this returns the lease used to manage this session */ public Lease getLease() { return lease; } } simple-http-4.1.21/src/org/simpleframework/http/session/SessionManager.java0000644000175000017500000001356611417313373027545 0ustar jamespagejamespage/* * SessionManager.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import static java.util.concurrent.TimeUnit.SECONDS; import java.util.concurrent.TimeUnit; import org.simpleframework.util.lease.LeaseException; /** * The SessionManager object is used to create sessions * that are active for a given time period. Each session is referenced * using a unique key, and only one session exists per key at a time. * Once a session has been created it remains active for a fixed time * period, this time period is renewed each time that session is open. *

* When the session manager is no longer required it can be closed. * Closing the session manager will cancel and destroy all active * sessions, and release all resources occupied by the provider such * as threads and memory. Typically it is advisable to close a manager * when it is not longer required by the application. * * @author Niall Gallagher */ public class SessionManager implements SessionProvider { /** * The composer is used to create and renew session instances. */ private final Composer composer; /** * Constructor for the SessionManager object. This is * used to create a session manager than can manage sessions. This * sets the default renewal duration of the sessions created to * twenty minutes. If the session is not used within this duration * then it is destroyed and its state is lost. */ public SessionManager() { this(null); } /** * Constructor for the SessionManager object. This is * used to create a session manager than can manage sessions. This * sets the default renewal duration of the sessions created to * twenty minutes. If the session is not used within this duration * then it is destroyed and its state is lost. * * @param observer this is used to observe the session manager */ public SessionManager(Observer observer) { this(observer, 1200, SECONDS); } /** * Constructor for the SessionManager object. This is * used to create a session manager than can manage sessions. This * takes a duration which specifies the default lifetime of a * session, also when a session is opened for a second time this * represents the renewal duration for that instance. * * @param duration this is the default renewal duration to use * @param unit this is the time unit for the specified duration */ public SessionManager(long duration, TimeUnit unit) { this(null, duration, unit); } /** * Constructor for the SessionManager object. This is * used to create a session manager than can manage sessions. This * takes a duration which specifies the default lifetime of a * session, also when a session is opened for a second time this * represents the renewal duration for that instance. * * @param observer this is used to observe the session manager * @param duration this is the default renewal duration to use * @param unit this is the time unit for the specified duration */ public SessionManager(Observer observer, long duration, TimeUnit unit) { this.composer = new Composer(observer, duration, unit); } /** * This open method is used to either open an existing * session or create a new session if one does not exist. This is * always guaranteed to open a session. Upon each open for a given * sesion that sessions expiry is renewed for a fixed period of * time, typically this is several minutes long. * * @param key the unique key identifying the session instance * * @return returns either a new session object or an existing one * * @throws LeaseException if the keyed session can not be retrieved */ public Session open(T key) throws LeaseException { return open(key, true); } /** * This open method is used to either open an existing * session or create a new session if requested. This is used to * optionally create a new session if one does not already exist. * This is used in situations where a session might not be required * but the existence of one may be queried. Once created a session * is active for a fixed time period. * * @param key the unique key identifying the session instance * * @return returns either a new session object or an existing one * * @throws LeaseException if the keyed session can not be retrieved */ public Session open(T key, boolean create) throws LeaseException { Session session = composer.lookup(key); if(session != null) { return session; } if(create == false) { return null; } return composer.compose(key); } /** * This close method is used to close the manager and * release all resources associated with it. This includes canceling * all active sessions and emptying the contents of those sessions. * All threads and other such resources are released by this method. * * @throws LeaseException if the session provider can not be shutdown */ public void close() throws LeaseException { composer.close(); } } simple-http-4.1.21/src/org/simpleframework/http/session/Maintainer.java0000644000175000017500000001013011417313373026676 0ustar jamespagejamespage/* * Maintainer.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; import java.util.concurrent.TimeUnit; import org.simpleframework.util.lease.Lease; import org.simpleframework.util.lease.LeaseException; import org.simpleframework.util.lease.LeaseManager; import org.simpleframework.util.lease.LeaseMap; /** * The Maintainer object is used to manage all life cycle * events for sessions. This is used to start and renew all leases * issued. The start life cycle event is used when the session is first * created, this will lease the new session for some fixed duration. * The renew event is performed when an existing session is accessed * again so that the session can be maintained for the default period. * * @author Niall Gallagher */ class Maintainer implements Controller { /** * This is the lease manager used to create the lease objects. */ private final LeaseManager manager; /** * This is the lease map used to maintain the leases by key. */ private final LeaseMap map; /** * This is the time unit used for the fixed lease duration. */ private final TimeUnit unit; /** * This is the duration the lease objects are renewed for. */ private final long duration; /** * Constructor for the Maintainer object. This is used * to maintain a lease for a resource using only the key for that * resource. Each renewal of the lease is performed using a fixed * duration so that the the time does not need to be specified. * * @param manager this is the manager used to create the leases * @param duration this is the fixed duration to renew for * @param unit this is the unit of measurement for the duration */ public Maintainer(LeaseManager manager, long duration, TimeUnit unit) { this.map = new LeaseMap(); this.manager = manager; this.duration = duration; this.unit = unit; } /** * The start method is used when a session is to * be created for the first time. This will ensure that the key * specified is used to dispose of the session when its idle * timeout has expired. * * @param key this is the unique key identifying the session */ public Lease start(T key) throws LeaseException { Lease lease = manager.lease(key, duration, unit); if(lease != null) { map.put(key, lease); } return lease; } /** * The renew method is used when a session has been * accessed for a again. This ensures that the key specified is * used to dispose of the session when its idle timeout expires. * * @param key this is the unique key identifying the session */ public void renew(T key) throws LeaseException { Lease lease = map.get(key); if(lease == null) { throw new SessionException("Session does not exist"); } lease.renew(duration, unit); } /** * The cancel method is used when a session is no * longer required and is to be disposed of. This is typically * invoked by a web application to release occupied resources. * * @param key this is the unique key identifying the session */ public void cancel(T key) throws LeaseException { Lease lease = map.remove(key); if(lease == null) { throw new SessionException("Session does not exist"); } } } simple-http-4.1.21/src/org/simpleframework/http/session/Observer.java0000644000175000017500000000375011417313373026410 0ustar jamespagejamespage/* * Observer.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.session; /** * The Observer interface is used to observe the session * activity within the session manager. This enables tracking of all * sessions created and destroyed by the session manager. Once the * session is created the observer start method is * invoked and when the session is canceled the cancel * method is invoked. Observations allow specialized monitoring. * * @author Niall Gallagher * * @see org.simpleframework.http.session.SessionManager */ public interface Observer { /** * This method is called after the session has been created but * before it is used. Listening to invocations of this method will * allow the user to establish any data structures required or * add any attributes to the session that are needed. * * @param session this is the session instance that was created */ public void create(Session session); /** * This method is called after the session has been canceled or * expired. It allows the listener to clean up session resources * and attributes in a specialized manner. When finished the * session will no longer exist within the application. * * @param session this is the session object that is canceled */ public void cancel(Session session); } simple-http-4.1.21/src/org/simpleframework/http/Response.java0000644000175000017500000002462411417313373024737 0ustar jamespagejamespage/* * Response.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 OutputStream 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. *

* 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. *

* It is important to note that the Response 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 close 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). *

* 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 */ public void setContentLength(int length); /** * Used to write a message body with the Response. The * semantics of this OutputStream will be determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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 */ public OutputStream getOutputStream() throws IOException; /** * Used to write a message body with the Response. The * semantics of this OutputStream will be determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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. *

* This will ensure that there is buffering done so that the output * can be reset using the reset 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 */ public OutputStream getOutputStream(int size) throws IOException; /** * This method is provided for convenience so that the HTTP content * can be written using the print methods provided by * the PrintStream. This will basically wrap the * getOutputStream with a buffer size of zero. *

* The retrieved PrintStream 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 text/plain; charset=UTF-8 the * resulting PrintStream 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. *

* Implementations of the Response must guarantee * that this can be invoked repeatedly without effecting any issued * OutputStream or PrintStream object. * * @return a print stream that provides convenience writing */ public PrintStream getPrintStream() throws IOException; /** * This method is provided for convenience so that the HTTP content * can be written using the print methods provided by * the PrintStream. This will basically wrap the * getOutputStream with a specified buffer size. *

* The retrieved PrintStream 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 text/plain; charset=UTF-8 the * resulting PrintStream 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. *

* Implementations of the Response must guarantee * that this can be invoked repeatedly without effecting any issued * OutputStream or PrintStream object. * * @param size the minimum size that the response buffer must be * * @return a print stream that provides convenience writing */ public PrintStream getPrintStream(int size) throws IOException; /** * Used to write a message body with the Response. The * semantics of this WritableByteChannel are determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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; /** * Used to write a message body with the Response. The * semantics of this WritableByteChannel are determined * by the HTTP version of the client, and whether or not the content * length has been set, through the setContentLength * 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. *

* This will ensure that there is buffering done so that the output * can be reset using the reset 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; /** * This can be used to determine whether the Response * has been committed. This is true if the Response * was committed, either due to an explicit invocation of the * commit method or due to the writing of content. If * the Response has committed the reset * method will not work in resetting content already written. * * @return true if the response headers have been committed */ public boolean isCommitted(); /** * This is used to write the headers that where given to the * Response. Any further attempts to give headers * to the Response will be futile as only the headers * that were given at the time of the first commit will be used * in the message header. *

* 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; /** * This can be used to determine whether the Response * has been committed. This is true if the Response * was committed, either due to an explicit invocation of the * commit method or due to the writing of content. If * the Response has committed the reset * method will not work in resetting content already written. * * @throws IOException thrown if there is a problem resetting */ public 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 */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/http/parse/0000755000175000017500000000000011767603362023410 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/http/parse/CookieParser.java0000644000175000017500000004606111417313373026640 0ustar jamespagejamespage/* * CookieParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.http.Cookie; import org.simpleframework.util.parse.Parser; 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. *

 *
 *  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
 *
 * 
* 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. *

* Each cookie has a $Version attribute followed by multiple * cookies. Each contains a name and a value, followed by an optional * $Path and $Domain attribute. This will parse * a given cookie header and return each cookie extracted as a * Cookie object. * * @author Niall Gallagher */ public class CookieParser extends Parser implements Iterable { /** * Determines when the Parser has finished. */ private boolean finished; /** * Used so the Parser does not parse twice. */ private boolean parsed; /** * Version of the Cookie being parsed. */ private int version; /** * Used to store the name of the Cookie. */ private Token name; /** * Used to store the value of the Cookie. */ private Token value; /** * Used to store the $Path values. */ private Token path; /** * Used to store the $Domain values. */ private Token domain; /** * Create a CookieParser that contains no cookies. * the instance will return false for the * hasNext method. cookies may be parsed using * this instance by using the parse 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 * String given to extract the cookies. This could be * achived by calling the default no-arg constructor and then using * the instance to invoke the parse method on that * String. * * @param header a String containing a cookie value */ public CookieParser(String header){ this(); parse(header); } /** * Resets the cookie and the buffer variables for this * CookieParser. 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 Cookie 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 Cookie. */ protected void parse() { if(!finished){ cookie(); parsed=true; } } /** * This is used to skip an arbitrary String within the * char buf. It checks the length of the String * first to ensure that it will not go out of bounds. A comparison * is then made with the buffers contents and the String * if the reigon in the buffer matched the String then the * offset within the buffer is increased by the String's * length so that it has effectively skipped it. *

* This skip 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 String value to be skipped * * @return true if the String 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 iterator() { return new Sequence(); } /** * This is used so that the collection of Cookies * can be reiterated. This allows the collection to be reused. * The reset method will invoke the super classes * init method. This will reinitialize this * Parser so the cookie will be reparsed. */ public void reset() { init(); parse(); } /** * Creates the Cookie from the token objects. It is * assumed that the Cookie String has * been parsed when this is called. This should only be used after * the parse method has been called. *

* If there is no $Domain or $Path * within the Cookie String then the * getDomain and getPath are null. * * @return the Cookie that was just parsed */ private Cookie getCookie() { return getCookie(name.toString(), value.toString()); } /** * Creates the Cookie from the token objects. It is * assumed that the Cookie String has * been parsed when this is called. This should only be used after * the parse method has been called. *

* If there is no $Domain or $Path * within the Cookie String then the * getDomain and getPath are null. * * @param name the name that the Cookie contains * @param value the value that the Cookie contains * * @return the Cookie 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 Cookie from the buffer * that contains the Cookie values. This will first * try to remove any trailing value after the version/prev * Cookie once this is removed it will extract the * name/value pair from the Cookie. The name and * value of the Cookie 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 * Cookie. The offset and length of the name will be * saved in the name token. This will read all char's * upto but excluding the first '=' char encountered * from the off 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 NAME '=' * within a Cookie. This extracts the Cookie * value the $Path and $Domain attributes * if they exist (i.e. $Path and $Domain * are optional in a cookie see RFC 2109). *

* The path method reads the terminal found before it as does the * domain 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 Cookie. */ private void value() { data(); path(); domain(); } /** * This initializes the value token and extracts the value of this * Cookie. The offset and length of the value will be * saved in the value token. This will read all char'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 $Path * of this Cookie. The offset and length of the path will * be saved in the path token. This will read all char's * up to 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 '\'). *

* This reads the terminal before the $Path so that if * there is no $Path for the Cookie 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 $Domain * of this Cookie. 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 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 '\'). *

* This reads the terminal before the $Domain so that * if there is no $Domain for the Cookie * 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 $Version of this Cookie. * The version is parsed and converted into a decimal int from the digit * characters that make up a version. *

* This will read all digit char's up to but excluding the * first non digit char 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 Iterator 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. * * @author Niall Gallagher */ private class Sequence implements Iterator { /** * Extracts the next Cookie object from the string * given. This will return null when there are no * more cookies left in the String being parsed. *

* To find out when there are no more cookies left use the * hasNext method. This will only set the name, * value, path, domain name version of the cookie * because as of RFC 2109 these are the only attributes a * Cookie may have, the path and domain are * optional. * * @return an initialized Cookie object */ public Cookie next(){ if(!hasNext()) { return null; } parsed = false; return getCookie(); } /** * Determine whether or not there are any Cookies * left in the String. This will attempt to extract * another Cookie from the String and * cache the result so the next method will produce * this Cookie. If another Cookie cannot * be parsed from the remainder of the String then * this will return false otherwise it will return * true. * * @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 reset 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 CookieParser.buf * array. The toString method of this token will * produce the String 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 String. * This converts the region only if there is a sufficient length. * * @return the String value of the region */ public String toString(){ return new String(buf,off,len); } } } simple-http-4.1.21/src/org/simpleframework/http/parse/AddressParser.java0000644000175000017500000012624011417313373027012 0ustar jamespagejamespage/* * AddressParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.io.Serializable; import org.simpleframework.http.Address; import org.simpleframework.http.Path; import org.simpleframework.http.Query; import org.simpleframework.util.KeyMap; import org.simpleframework.util.parse.Parser; /** * 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 *

 *
 *    URI         = (absoluteURI | relativeURI)
 *    absoluteURI = scheme ":" ("//" netpath | relativeURI)
 *    relativeURI = path ["?" querypart]
 *    netpath     = domain [":" port] relativeURI
 *    path        = *("/" segment)
 *    segment     = *pchar *( ";" param )
 *
 * 
* This implements the Address 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 http://domain/path1;x=y/path2;x=z the value for * the parameter named x would be z. *

* 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. *

* The setPath 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 AddressParser * that contains no specifics. The instance will return * null for all the get methods. The parsers * get methods are populated by using the parse * 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 String given to extract the specifics. This * could be achieved by calling the default no-arg constructor * and then using the instance to invoke the parse * method on that String to extract the parts. * * @param text a String 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 gopher://domain/path is * a URI that is intended for the gopher protocol. The * scheme is the string gopher. * * @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 retrive the domain of this URI. The * domain part in the URI is an optional part, an example * http://domain/path?querypart. 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 retrive the path of this URI. The path part * is the most fundemental part of the URI. This will return * the value of the path. If there is no path part then this * will return / to indicate the root. *

* The Path object returned by this will contain * no path parameters. The path parameters are available using * the Address 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 ab 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(){ 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 Query object. The query is * an optional member of a URI and comes after the path part, it * is preceded by a question mark, ? character. * For example the following URI contains query for * its query part, http://host:port/path?query. *

* This returns a org.simpleframework.http.Query * object that can be used to interact directly with the query * values. The Query object is a read-only interface * to the query parameters, and so will not affect the URI. * * @return a Query object for the query part */ public Query getQuery(){ String text = query.toString(); if(text == null) { data = new QueryParser(); } if(data == null){ data = new QueryParser(text); } return data; } /** * This is used to retrive the port of the uniform resource * identifier. The port part in this is an optional part, an * example http://host:port/path?querypart. This * will return the value of the port. If there is no port then * this will return -1 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 Map instance. *

* 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 http://domain/path1;x=y/path2;x=z * the value for the parameter named x would be * z. * * @return this will return the parameter names found in the URI */ public KeyMap 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 :// identifier * to ensure that the Address.toString will * produce the correct syntax. *

* Caution must be taken to ensure that the port and * the scheme are consistent. So if the original URI * was http://domain:80/path and the scheme * was changed to ftp 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 toString method will not contain * the domain. The result of the toString * method will be /path/path?query. 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){ domain.value = value; } /** * This will set the port to whatever value it is given. If * the value is 0 or less then the toString will * will not contain the optional port. If port number is above * 0 then the toString method will produce a URI * like http://host:123/path 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 Address.toString method will * not contain the path, that is if path is null then it will be * interpreted as /. *

* 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 getPath 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; } 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 Address.toString method * will not contain the path, that is if path is null then it will * be interpreted as /. *

* 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 getPath 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 setPath * 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 Address 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 Address.toString method * will not contain the query. If the query was abc * then the toString method would produca a string * like http://host:port/path?abc. If the query is * null this URI would have no query part. The query must not * contain the ? 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 Address.toString method * will not contain the query. If the Query.toString * returns null then the query will be empty. This is basically * the setQuery(String) method with the string value * from the issued Query.toString method. * * @param query a Query 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 * absoluteURI or a relativeURI. 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 toString * method has been called. Thus when the toString * method is called then the token depends on the value of the * cache alone in further calls to toString. * 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 *

    *
    *    absoluteURI = scheme ":" ("//" netpath | relativeURI)
    *    relativeURI = path ["?" querypart]
    *    netpath     = domain [":" port] relativeURI
    *    path        = *("/" segment)
    *    segment     = *pchar *( ";" param )
    *
    * 
* This syntax is sufficent 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 absoluteURI. */ 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. *

* The syntax for the scheme is given in RFC 2396 as follows *

    *
    *    scheme = alpha *( alpha | digit | "+" | "-" | "." )
    *
    * 
* This will however also skips the "://" from the tag * so of the URI was gopher://domain/path then * the URI would be domain/path 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 * "+", "-", ".". * * @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 *
    *
    *    netpath     = domain [":" port] relativeURI
    *
    * 
* This syntax is modified from the URI specification on * RFC 2396. */ private void netPath(){ domain(); if(skip(":")){ port(); } relativeURI(); } /** * 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 relativeURI. */ 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 String 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 toString method to * return the extracted value of the token rather than getting * confused with previous values set by a previous parse method. *

* This uses the following delimiters to determine the end of the * domain ?,: and /. This * ensures that the read offset does not go out of bounds and * consequently throw an IndexOutOfBoundsException. */ 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 toString method to * return the extracted value of the token rather than getting * confused with previous values set by a previous parse method. *

* 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 IndexOutOfBoundsException. */ 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 toString 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. *

* 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 * Token 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 getQuery 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 getQuery 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. *

* 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 "%" HEX HEX. * 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 char can only hold UCS-2, the UCS-4 * characters will have only the low order octets stored. *

* 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. *

* 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 "%" HEX HEX * 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. *

* 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 "%" HEX HEX, 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 * char or not. A hexidecimal character is consdered * to be a character within the range of 0 - 9 and * between a - f and A - F. This will * return true 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 cetain chars * within a Uniform Resource Identifier. RFC 2396 defines * an unreserverd char as alphanum | mark. * * @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 0 - 9 and between * a - z it uses iso-8859-1 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 * iso-8859-1 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 "-", * "_", ".", "!", "~", "*", "'", "(", ")". * * @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 ":", "@", "=", * "&", "+", "$", "," this will not check to see if the * char is an escaped char, that is % HEX HEX. 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 ";", "/", "?", * ":", "@", "&", "=", "+", "$", ",". * * @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 String * 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 *

    * scheme://host:port/path/path?querypart
    * 
*

* It can return /path/path?querypart style relative * URI's. If any of the parts are set to null then that part will be * missing, for example if setDomain 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 * setPath then the path will be /. An * example URI with the path part of null would be *

    * scheme://host:port/?querypart
    * 
* * @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 ParameterMap 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 { /** * 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 ParseBuffer * 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 String object. */ private class Token implements Serializable { /** * 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 Token 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 Token into it's * String 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 String 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; } } } simple-http-4.1.21/src/org/simpleframework/http/parse/ContentParser.java0000644000175000017500000004140011417313373027031 0ustar jamespagejamespage/* * ContentParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.http.ContentType; import org.simpleframework.util.KeyMap; import org.simpleframework.util.parse.ParseBuffer; import org.simpleframework.util.parse.Parser; /** * This provides access to the MIME type parts, that is the primary * type, the secondary type and an optional character set parameter. * The charset parameter is one of many parameters that * can be associated with a MIME type. This however this exposes this * parameter with a typed method. *

* The getCharset 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 ContentParser extends Parser implements ContentType { /** * Used to store the characters consumed for the subtype. */ private ParseBuffer subtype; /** * Used to store the characters for the charset. */ 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 map; /** * The default constructor will create a ContentParser * that contains no charset, type or subtype. This can be used to * extract the type, subtype and the optional charset * parameter by using the parser's parse(String) * method. */ public ContentParser(){ this.subtype = new ParseBuffer(); this.charset = new ParseBuffer(); this.value = new ParseBuffer(); this.name = new ParseBuffer(); this.type = new ParseBuffer(); this.map = new KeyMap(); } /** * This is primarily a convenience constructor. This will parse * the String given to extract the MIME type. This * could be achived by calling the default no-arg constructor * and then using the instance to invoke the parse * method on that String. * * @param header String containing a MIME type value */ public ContentParser(String header){ this(); parse(header); } /** * 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 primary the type to set for the primary type of this */ public void setPrimary(String primary) { type.reset(primary); } /** * 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 text/plain; charset=UTF-8. This will * return the text value. If there is no primary type then this * will return null otherwise the string value. * * @return the primary type part of this MIME type */ public String getPrimary() { return type.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 type the type to set for the primary type of this */ public void setSecondary(String type) { subtype.reset(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 text/html; charset=UTF-8. This * will return the HTML value. If there is no secondary type then * this will return null otherwise the string value. * * @return the primary type part of this MIME type */ public String getSecondary(){ return subtype.toString(); } /** * This will set the charset to whatever value the * string contains. If the string is null then this will not set * the parameter to any value and the toString 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 charset 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 boundary * 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 boundary 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 String. This will reset the parser to a * ready state. The init method is invoked by the parser when * the Parser.parse 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() { type.clear(); subtype.clear(); charset.clear(); name.clear(); value.clear(); map.clear(); off = 0; } /** * Reads and parses the MIME type from the given String * object. This uses the syntax defined by RFC 2616 for the media-type * syntax. This parser is only concerned with one parameter, the * charset parameter. The syntax for the media type is *

    * media-type = token "/" token *( ";" parameter )
    * parameter = token | literal 
    * 
*/ protected void parse(){ type(); off++; subtype(); parameters(); } /** * This is used to remove all whitespace characters from the * String excluding the whitespace within literals. * The definition of a literal can be found in RFC 2616. *

* 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 ParseBuffer. 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 * type/subtype;param=val;param2=val. */ private void type(){ while(off < count){ if(buf[off] =='/'){ break; } type.append(buf[off]); off++; } } /** * This reads the subtype from the MIME type. This will fill the * subtype ParseBuffer. 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 * type/subtype;param=val;param2=val. */ private void subtype(){ while(off < count){ if(buf[off] ==';'){ break; } subtype.append(buf[off]); off++; } } /** * This will read the parameters from the MIME type. This will search * for the charset parameter within the set of parameters * which are given to the type. The charset param is the * only parameter that this parser will tokenize. *

* This will remove any parameters that preceed the charset parameter. * Once the charset 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); } /** * 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. type/subtype;param=val 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 charset 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 IndexOutOfBoundsException 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 * char's upto but excluding the first terminal char * 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 charset param. * This will fill the charset ParseBuffer and with * the charset value. This will read a literal or a token as * the charset value. If the charset 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 charset 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(type != null) { text.append(type); text.append("/"); text.append(subtype); } 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 charset 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 charset parameter to the type which will * recreate the content type. * * @return this returns the string representation of the type */ public String toString() { return encode(); } } simple-http-4.1.21/src/org/simpleframework/http/parse/LanguageParser.java0000644000175000017500000001302711417313373027146 0ustar jamespagejamespage/* * LanguageParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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 Accept-Language * header. This takes in an Accept-Language header and parses * it according the RFC 2616 BNF for the Accept-Language header. * This also has the ability to sequence the language tokens in terms of * the most preferred and the least preferred. *

* 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 { /** * This is used to create a LanguageParser for the * Accept-Language 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 parse(String). */ public LanguageParser() { super(); } /** * This is used to create a LanguageParser for the * Accept-Language 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 parse(String). * * @param text value of a Accept-Language header */ public LanguageParser(String text) { super(text); } /** * This is used to create a LanguageParser for the * Accept-Language 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 parse(String). * * @param list value of a Accept-Language header */ public LanguageParser(List 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 Locale 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 Locale 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 == ';'; } } simple-http-4.1.21/src/org/simpleframework/http/parse/QueryParser.java0000644000175000017500000005224211417313373026532 0ustar jamespagejamespage/* * QueryParser.java December 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.http.Query; import org.simpleframework.util.parse.MapParser; import java.net.URLEncoder; import java.util.Set; /** * The ParameterParser is used to parse data encoded in * the application/x-www-form-urlencoded 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 org.simpleframework.http.net.Query interface. The * syntax of the parsed parameters is described below in BNF. *

 *
 *    params  = *(pair [ "&" params])
 *    pair    = name "=" value
 *    name    = *(text | escaped)
 *    value   = *(text | escaped)
 *    escaped = % HEX HEX
 *
 * 
* 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 "=", "&", and "%" as having special values. * The "=" character delimits the name from the value and the "&" * 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 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 ParameterParser. 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 * org.simpleframework.util.net.Query. */ public QueryParser(){ this.name = new Token(); this.value = new Token(); } /** * Constructor for the ParameterParser. 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 * org.simpleframework.util.net.Query. * * @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 true or * false 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 parse(String) is * invoked the status of the Query 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 "&". * 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 * Token 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. *

* 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. *

* 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 "%" HEX HEX. * 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 char can only hold UCS-2, the UCS-4 * characters will have only the low order octets stored. *

* 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. *

* 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 "%" HEX HEX * 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. *

* 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 "%" HEX HEX, 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 * char or not. A hexadecimal character is considered * to be a character within the range of 0 - 9 and * between a - f and A - F. This will * return true 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 encode 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 encode 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 toString method is used to compose an string * in the application/x-www-form-urlencoded MIME type. * This will encode the tokens specified in the Set. * 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 toString method is used to compose an string * in the application/x-www-form-urlencoded 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 ParseBuffer * 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 Token as a value * that can be used this converts it to a String. * 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); } } } simple-http-4.1.21/src/org/simpleframework/http/parse/ListParser.java0000644000175000017500000003615511417313373026345 0ustar jamespagejamespage/* * ListParser.java September 2003 * * Copyright (C) 2003, Niall Gallagher * * 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.util.parse.Parser; /** * The ListParser 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. *

 * 
 *    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
 *
 * 
* 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. *

* 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 list * 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 extends Parser { /** * Provides a quick means of sorting the values extracted. */ private PriorityQueue order; /** * Contains all the values extracted from the header(s). */ private List 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 ListParser. This creates a * parser with no initial parse data, if there are headers to * be parsed then the parse(String) method or * parse(List) method can be used. This will * parse a delimited list according so RFC 2616 section 4.2. */ public ListParser(){ this.order = new PriorityQueue(); this.list = new ArrayList(); this.text = new char[0]; } /** * Constructor for the ListParser. 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 list * 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 ListParser. 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 list * method, which will also sort and trim the tokens. * * @param list a list of comma separated lists to be parsed */ public ListParser(List 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 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 list() { return list; } /** * This is used to remove the String tokens from * the priority queue and place those tokens in an array. The * The String 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. *

* 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. *

* 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. *

    *
    *    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,,
    *    
    * 
* 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 PriorityQueue. 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 String.trim * 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 PriorityQueue. 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 qvalue * 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 Entry 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. * * @author Niall Gallagher */ private class Entry implements Comparable { /** * 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 Entry 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; } } }simple-http-4.1.21/src/org/simpleframework/http/parse/ValueParser.java0000644000175000017500000001013711417313373026476 0ustar jamespagejamespage/* * ValueParser.java September 2003 * * Copyright (C) 2003, Niall Gallagher * * 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 ValueParser 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. *
 * 
 *    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
 *
 * 
* 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. *

* 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 list * 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 { /** * Constructor for the ValueParser. This creates * a parser with no initial parse data, if there are headers to * be parsed then the parse(String) method or * parse(List) method can be used. This will * parse a delimited list according so RFC 2616 section 4.2. */ public ValueParser(){ super(); } /** * Constructor for the ValueParser. 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 list * 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 ValueParser. 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 list * method, which will also sort and trim the tokens. * * @param list a list of comma separated lists to be parsed */ public ValueParser(List 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); } }simple-http-4.1.21/src/org/simpleframework/http/parse/DateParser.java0000644000175000017500000004635411417313373026311 0ustar jamespagejamespage/* * DateParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.Calendar; import java.util.TimeZone; import org.simpleframework.util.parse.Parser; /** * This is used to create a Parser 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 *

 * 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
 * 
*

* This can also parse the date in ms as retrived from the System's * System.currentTimeMillis method. This has a parse method for a * long which will do the same as the parse(String). * Once the date has been parsed there are two methods that allow the date * to be represented, the toLong method converts the date to a * long and the toString method will convert the date * into a String. *

* This produces the same string as the SimpleDateFormat.format * using the pattern "EEE, dd MMM yyyy hh:mm:ss 'GMT'". 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 parser contains this method so that the a date does not * have to be parsed from System.currentTimeMillis. * This returns a date using a DataParser.parse(long) * method invocation. * * @return this returns a RFC 1123 date for the current time */ public static String getDate() { long time = System.currentTimeMillis(); return new DateParser(time).toString(); } /** * The default constructor will create a parser that can parse * Strings 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 long argument * in the constructor. This can also be done by first calling the no-arg * constructor and then using the parse method. *

* 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 String * argument in the constructor. This can also be done by first calling * the no-arg constructor and then using the parse method. *

* 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 long. If this * method is given the value of the date as a long it will * construct the RFC 1123 date as required by RFC 2616 sec 3.3. *

* This saves time on parsing a String that is encoded in * the HTTP-date format. The date given must be positive, if the date * given is not a positive 'long' 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(Calendar.DAY_OF_WEEK); year = calendar.get(Calendar.YEAR); month = calendar.get(Calendar.MONTH); day = calendar.get(Calendar.DAY_OF_MONTH); hour = calendar.get(Calendar.HOUR_OF_DAY); mins = calendar.get(Calendar.MINUTE); secs = calendar.get(Calendar.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 Date 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 DateParser. 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 buf. 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 *

    * rfc1123 = 'wkday "," SP date1 SP time SP GMT'. 
    * date1 = '2DIGIT SP month SP 4DIGIT' and finally
    * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'. 
    * 
*/ 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 *
    * rfc850 = 'weekday "," SP date2 SP time SP GMT'. 
    * date2 = '2DIGIT "-" month "-" 2DIGIT' and finally
    * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'. 
    * 
*/ 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 *
    * asctime = 'weekday SP date3 SP time SP 4DIGIT'. 
    * date3 = 'month SP (2DIGIT | (SP 1DIGIT))' and 
    * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT'. 
    * 
*/ 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 *
    * date1 = '2DIGIT SP month SP 4DIGIT'.
    * example '02 Jun 1982'.
    * 
*/ 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 *
    * date2 = '2DIGIT "-" month "-" 2DIGIT'
    * example '02-Jun-82'.
    * 
*/ 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 *
    * date3 = 'month SP (2DIGIT | (SP 1DIGIT))' 
    * example 'Jun  2'.
    * 
    */
   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.
    * 

* 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). *

* 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 00:00:00 that is *

    * time = '2DIGIT ":" 2DIGIT ":" 2DIGIT' so this will
    * read only a time of that form, although this will
    * parse time = '2DIGIT CHAR 2DIGIT CHAR 2DIGIT'.
    * 
*/ 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. *

* 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. *

* 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. *

* 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. *

    * Tue, 02 Jun 1982 
    * 
. * 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. *
    * 23:59:59
    * 
. * 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 long, given the exact * time this will use the java.util.Date to parse this date * into a long. The GregorianCalendar uses * the method getTime which produces the Date * object from this the getTime returns the long * * @return the date parsed as a long */ public long toLong() { Calendar calendar = Calendar.getInstance(ZONE); /* GMT*/ calendar.set(year,month, day, hour, mins, secs); return calendar.getTime().getTime(); } /** * This prints the date in the format of a RFC 1123 date. Example *
    * Tue, 02 Jun 1982 23:59:59 GMT
    * 
. * This uses a StringBuffer to accumulate the various * Strings/ints to form the resulting date * value. The resulting date value is the one required by RFC 2616. *

* 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(); } } simple-http-4.1.21/src/org/simpleframework/http/parse/PrincipalParser.java0000644000175000017500000002521211417313373027343 0ustar jamespagejamespage/* * PrincipalParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.http.Principal; import org.simpleframework.util.parse.ParseBuffer; import org.simpleframework.util.parse.Parser; /** * PrincipalParser is a parser class for the HTTP basic authorization * header. It decodes the base64 encoding of the user and * password pair. *

* This follows the parsing tree of RFC 2617. The goal of this parser * is to decode the base64 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 Basic authorization scheme. The format of the basic * scheme can be found in RFC 2617 and is of the form *

 *  Basic SP base64-encoding.
 * 
* * @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 bytes 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 Parser 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 Parser 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 String 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 null * * @return the password for the user or null */ public String getPassword(){ if(password.length() == 0){ return null; } return password.toString(); } /** * Gets the users name from the Authorization header value. * This will return null 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 Parser when it is ready * to parse a new String. This will reset the * Parser to a ready state. The init method * is invoked by the Parser when the parse * 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 * String excluding the whitespace within literals. * The definition of a literal can be found in RFC 2616. *

* 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 name : password 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 base64 encoding of * the user name and password. This uses a standart base64 * decoding scheme. *

* For information on the decoding scheme used for base64 * 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 byte character to the * byte number. *

* 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 byte from the set of four * bytes given. This method is used to isolate the correct * bits that corrospond to an actual character withing the * base64 data. * * @param four this is the four bytes 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 byte from the set of four * bytes given. This method is used to isolate the correct * bits that corrospond to an actual character withing the * base64 data. * * @param four this is the four bytes 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 byte from the set of four * bytes given. This method is used to isolate the correct * bits that corrospond to an actual character withing the * base64 data. * * @param four this is the four bytes 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 * TEXT character according to the HTTP specification, * that is RFC 2616 specifies a TEXT 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 TEXT */ private boolean text(char c){ return c > 31 && c != 127 && c <= 0xffff; } } simple-http-4.1.21/src/org/simpleframework/http/parse/PathParser.java0000644000175000017500000006166611417313373026333 0ustar jamespagejamespage/* * PathParser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.io.Serializable; import org.simpleframework.http.Path; import org.simpleframework.util.parse.Parser; /** * 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. *

* If the path that this represents is /usr/bin/../etc/./README * then the actual path, normalized, is /usr/etc/README. 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. *

* 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 AddressParser 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 PathParser that * contains no specifics. The instance will return null * for all the get methods. The PathParser'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 * String given to extract the specifics. This could be * achived by calling the default no-arg constructor and then using * the instance to invoke the parse method on that * String to extract the parts. * * @param path a String 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 * PathParser will create the same String * path given a set of paths that have different back references. For * example the paths /path/../path and /path * are the same path but different String's. *

* This will NOT parse an immediate back reference as this signifies * a path that cannot exist. So a path such as /../ will * result in a null for all methods. Paths such as ../bin * 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 * Parser.parse 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 file.en_US.extension * will produce an extension of extension. 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 /usr/README the name is README. * 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 /usr/../etc/./ the * path is /etc/. 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 * ../ or ./ */ 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 substring 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 * ../ or ./ */ 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 substring 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 * ../ or ./ */ 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 /pub/./bin/README would be to return * the highest directory path /pub/bin/. 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 /usr/bin/../etc then the segments * returned would be usr and etc 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 getDirectory. 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 * /usr/share/rfc/rfc2396.txt and the issued * path was /usr/share/text.txt then this will * return the path string /rfc/rfc2396.txt. * * @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 getRelative(String) 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 ../, 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 getDirectory. 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 * /usr/share/rfc/rfc2396.txt and the issued * path was /usr/share/text.txt then this will * return the path string /rfc/rfc2396.txt. * * @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){ 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 String * 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 null. */ 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 /usr/bin/../etc/./README then the actual * path, the normalized path, is /usr/etc/README. *

* 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 ../ or /.. within the path. * This will also remove empty segments like //. */ private void normalize(){ int size = count + off; int pos = off; for(off = count = 0; pos < size; pos++) { buf[count++] = buf[pos]; if(buf[pos] == '/') { if(count -1 > 0){ if(buf[count -2] == '/') /* [/]/./path/ */ count--; /* /[/]./path/ */ } } else 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 /usr/README the name is README. * 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 extract 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 /usr/../etc/./ the * path is /etc/. 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 * ../ or ./ */ public String toString(){ return getPath(); } /** * This is used so that the PathParser can speed * up the parsing of the data. Rather than using a buffer like * a ParseBuffer or worse a StringBuffer * 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 String does not need to be made * again after the first time it is created. */ private class Token implements Serializable { /** * 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 Token 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 Token into it's * String 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 String 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 TokenList class is used to store a list of * tokens. This provides an add 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 * list method. This has a scalable capacity. */ private class TokenList implements Serializable { /** * 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 TokenList 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 list 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 add 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 add 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 init 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; } } } simple-http-4.1.21/src/org/simpleframework/util/0000755000175000017500000000000011767603362022274 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/util/buffer/0000755000175000017500000000000011767603362023545 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/util/buffer/ArrayBuffer.java0000644000175000017500000003303511417313373026614 0ustar jamespagejamespage/* * ArrayBuffer.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * The ArrayBuffer is intended to be a general purpose * byte buffer that stores bytes in an single internal byte array. The * intended use of this buffer is to provide a simple buffer object to * read and write bytes with. In particular this provides a high * performance buffer that can be used to read and write bytes fast. *

* This provides several convenience methods which make the use of the * buffer easy and useful. This buffer allows an initial capacity to be * specified however if there is a need for extra space to be added to * buffer then the append methods will expand the capacity * of the buffer as needed. * * @author Niall Gallagher * * @see org.simpleframework.util.buffer.ArrayAllocator */ public class ArrayBuffer implements Buffer { /** * This is the internal array used to store the buffered bytes. */ private byte[] buffer; /** * This is used to determine whether this buffer has been closed. */ private boolean closed; /** * This is the count of the number of bytes buffered. */ private int count; /** * This is the maximum allowable buffer capacity for this. */ private int limit; /** * Constructor for the ArrayBuffer object. The initial * capacity of the default buffer object is set to 16, the capacity * will be expanded when the append methods are used and there is * not enough space to accommodate the extra bytes. */ public ArrayBuffer() { this(16); } /** * Constructor for the ArrayBuffer object. The initial * capacity of the buffer object is set to given size, the capacity * will be expanded when the append methods are used and there is * not enough space to accommodate the extra bytes. * * @param size the initial capacity of this buffer instance */ public ArrayBuffer(int size) { this(size, size); } /** * Constructor for the ArrayBuffer object. The initial * capacity of the buffer object is set to given size, the capacity * will be expanded when the append methods are used and there is * not enough space to accommodate the extra bytes. * * @param size the initial capacity of this buffer instance * @param limit this is the maximum allowable buffer capacity */ public ArrayBuffer(int size, int limit) { this.buffer = new byte[size]; this.limit = limit; } /** * This method is used so that the buffer can be represented as a * stream of bytes. This provides a quick means to access the data * that has been written to the buffer. It wraps the buffer within * an input stream so that it can be read directly. * * @return a stream that can be used to read the buffered bytes */ public InputStream getInputStream() { return new ByteArrayInputStream(buffer, 0, count); } /** * This method is used to allocate a segment of this buffer as a * separate buffer object. This allows the buffer to be sliced in * to several smaller independent buffers, while still allowing the * parent buffer to manage a single buffer. This is useful if the * parent is split in to logically smaller segments. * * @return this returns a buffer which is a segment of this buffer */ public Buffer allocate() throws IOException { return new Segment(this,count); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. If the UTF-8 * content encoding is not supported the platform default is * used, however this is unlikely as UTF-8 should be supported. * * @return this returns a UTF-8 encoding of the buffer contents */ public String encode() throws IOException { return encode("UTF-8"); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. This will convert * the bytes using the specified character encoding format. * * @return this returns the encoding of the buffer contents */ public String encode(String charset) throws IOException { return new String(buffer,0,count, charset); } /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer * * @return this returns this buffer for another operation */ public Buffer append(byte[] array) throws IOException { return append(array, 0, array.length); } /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer * @param off this is the offset to begin reading the bytes from * @param size the number of bytes to be read from the array * * @return this returns this buffer for another operation */ public Buffer append(byte[] array, int off, int size) throws IOException { if(closed) { throw new BufferException("Buffer is closed"); } if(size + count > buffer.length) { expand(count + size); } if(size > 0) { System.arraycopy(array, off, buffer, count, size); count += size; } return this; } /** * This is used to ensure that there is enough space in the buffer * to allow for more bytes to be added. If the buffer is already * larger than the required capacity the this will do nothing. * * @param capacity the minimum size needed for this buffer object */ private void expand(int capacity) throws IOException { if(capacity > limit) { throw new BufferException("Capacity limit %s exceeded", limit); } int resize = buffer.length * 2; int size = Math.max(capacity, resize); byte[] temp = new byte[size]; System.arraycopy(buffer, 0, temp, 0, count); buffer = temp; } /** * This will clear all data from the buffer. This simply sets the * count to be zero, it will not clear the memory occupied by the * instance as the internal buffer will remain. This allows the * memory occupied to be reused as many times as is required. */ public void clear() throws IOException { if(closed) { throw new BufferException("Buffer is closed"); } count = 0; } /** * This method is used to ensure the buffer can be closed. Once * the buffer is closed it is an immutable collection of bytes and * can not longer be modified. This ensures that it can be passed * by value without the risk of modification of the bytes. */ public void close() throws IOException { closed = true; } /** * A Segment represents a segment within a buffer. It * is used to allow a buffer to be split in to several logical parts * without the need to create several separate buffers. This means * that the buffer can be represented in a single memory space, as * both a single large buffer and as several individual buffers. * * @author Niall Gallagher */ private class Segment implements Buffer { /** * This is the parent buffer which is used for collecting data. */ private Buffer parent; /** * This is used to determine if the buffer has closed or not. */ private boolean closed; /** * This represents the start of the segment within the buffer. */ private int start; /** * This represents the number of bytes this segment contains. */ private int length; /** * Constructor for the Segment object. This is used * to create a buffer within a buffer. A segment is a region of * bytes within the original buffer. It allows the buffer to be * split in to several logical parts of a single buffer. * * @param parent this is the parent buffer used to append to * @param start this is the start within the buffer to read */ public Segment(Buffer parent, int start) { this.parent = parent; this.start = start; } /** * This method is used so that the buffer can be represented as a * stream of bytes. This provides a quick means to access the data * that has been written to the buffer. It wraps the buffer within * an input stream so that it can be read directly. * * @return a stream that can be used to read the buffered bytes */ public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(buffer,start,length); } /** * This method is used to allocate a segment of this buffer as a * separate buffer object. This allows the buffer to be sliced in * to several smaller independent buffers, while still allowing the * parent buffer to manage a single buffer. This is useful if the * parent is split in to logically smaller segments. * * @return this returns a buffer which is a segment of this buffer */ public Buffer allocate() throws IOException { return new Segment(this,count); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. If the UTF-8 * content encoding is not supported the platform default is * used, however this is unlikely as UTF-8 should be supported. * * @return this returns a UTF-8 encoding of the buffer contents */ public String encode() throws IOException { return encode("UTF-8"); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. This will convert * the bytes using the specified character encoding format. * * @return this returns the encoding of the buffer contents */ public String encode(String charset) throws IOException { return new String(buffer,start,length, charset); } /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer */ public Buffer append(byte[] array) throws IOException { return append(array, 0, array.length); } /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer * @param off this is the offset to begin reading the bytes from * @param size the number of bytes to be read from the array */ public Buffer append(byte[] array, int off, int size) throws IOException { if(closed) { throw new BufferException("Buffer is closed"); } if(size > 0) { parent.append(array, off, size); length += size; } return this; } /** * This will clear all data from the buffer. This simply sets the * count to be zero, it will not clear the memory occupied by the * instance as the internal buffer will remain. This allows the * memory occupied to be reused as many times as is required. */ public void clear() throws IOException { length = 0; } /** * This method is used to ensure the buffer can be closed. Once * the buffer is closed it is an immutable collection of bytes and * can not longer be modified. This ensures that it can be passed * by value without the risk of modification of the bytes. */ public void close() throws IOException { closed = true; } } } simple-http-4.1.21/src/org/simpleframework/util/buffer/Stream.java0000644000175000017500000000315211417313373025634 0ustar jamespagejamespage/* * Stream.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.IOException; import java.io.InputStream; /** * The Stream interface is used to represent anything that * can be streamed. Typically this is used to represent a region of * memory that can be read through an InputStream object. * Representing an object as a stream ensures it can each time the * input stream is acquired it reads from the start of the buffer. * * @author Niall Gallagher * * @see org.simpleframework.util.buffer.Buffer */ public interface Stream { /** * This method is used so that a buffer can be represented as a * stream of bytes. This provides a quick means to access the data * that has been written to the buffer. It wraps the buffer within * an input stream so that it can be read directly. * * @return a stream that can be used to read the buffered bytes */ public InputStream getInputStream() throws IOException; } simple-http-4.1.21/src/org/simpleframework/util/buffer/FilterAllocator.java0000644000175000017500000001137411417313373027474 0ustar jamespagejamespage/* * FilterAllocator.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.IOException; /** * The FilterAllocator object is used to provide a means * to provide a general set of constraints around buffer allocation. * It can ensure that a minimum capacity is used for default allocation * and that an upper limit is used for allocation. In general this can * be used in conjunction with another Allocator which may * not have such constraints. It ensures that a set of requirements can * be observed when allocating buffers. * * @author Niall Gallagher */ public class FilterAllocator implements Allocator { /** * This is the allocator the underlying buffer is allocated with. */ protected Allocator source; /** * This is the default initial minimum capacity of the buffer. */ protected int capacity; /** * This is the maximum number of bytes that can be allocated. */ protected int limit; /** * Constructor for the FilterAllocator object. This is * used to instantiate the allocator with a default buffer size of * half a kilobyte. This ensures that it can be used for general * purpose byte storage and for minor I/O tasks. * * @param source this is where the underlying buffer is allocated */ public FilterAllocator(Allocator source) { this(source, 512, 1048576); } /** * Constructor for the FilterAllocator object. This is * used to instantiate the allocator with a specified buffer size. * This is typically used when a very specific buffer capacity is * required, for example a request body with a known length. * * @param source this is where the underlying buffer is allocated * @param capacity the initial capacity of the allocated buffers */ public FilterAllocator(Allocator source, int capacity) { this(source, capacity, 1048576); } /** * Constructor for the FilterAllocator object. This is * used to instantiate the allocator with a specified buffer size. * This is typically used when a very specific buffer capacity is * required, for example a request body with a known length. * * @param source this is where the underlying buffer is allocated * @param capacity the initial capacity of the allocated buffers * @param limit this is the maximum buffer size created by this */ public FilterAllocator(Allocator source, int capacity, int limit) { this.limit = Math.max(capacity, limit); this.capacity = capacity; this.source = source; } /** * This method is used to allocate a default buffer. This will * allocate a buffer of predetermined size, allowing it to grow * to an upper limit to accommodate extra data. If the buffer * requested is larger than the limit an exception is thrown. * * @return this returns an allocated buffer with a default size */ public Buffer allocate() throws IOException { return allocate(capacity); } /** * This method is used to allocate a default buffer. This will * allocate a buffer of predetermined size, allowing it to grow * to an upper limit to accommodate extra data. If the buffer * requested is larger than the limit an exception is thrown. * * @param size the initial capacity of the allocated buffer * * @return this returns an allocated buffer with a default size */ public Buffer allocate(int size) throws IOException { if(size > limit) { throw new BufferException("Specified size %s beyond limit", size); } if(capacity > size) { size = capacity; } return source.allocate(size); } /** * This method is used to close the allocator so that resources * that are occupied by the allocator can be freed. This will * allow the allocator to be created and closed repeatedly in * a single process without holding on to resources such as * mapped file buffers or threads. */ public void close() throws IOException { source.close(); } }simple-http-4.1.21/src/org/simpleframework/util/buffer/Allocator.java0000644000175000017500000000464111417313373026325 0ustar jamespagejamespage/* * Allocator.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.Closeable; import java.io.IOException; /** * The Allocator interface is used to describe a resource * that can allocate a buffer. This is used so that memory allocation * can be implemented as a strategy allowing many different sources of * memory. Typically memory will be allocated as an array of bytes but * can be a mapped region of shared memory or a file. * * @author Niall Gallagher */ public interface Allocator extends Closeable { /** * This method is used to allocate a default buffer. Typically this * will allocate a buffer of predetermined size, allowing it to * grow to an upper limit to accommodate extra data. If the buffer * can not be allocated for some reason this throws an exception. * * @return this returns an allocated buffer with a default size */ public Buffer allocate() throws IOException; /** * This method is used to allocate a default buffer. This is used * to allocate a buffer of the specified size, allowing it to * grow to an upper limit to accommodate extra data. If the buffer * can not be allocated for some reason this throws an exception. * * @param size this is the initial capacity the buffer should have * * @return this returns an allocated buffer with a specified size */ public Buffer allocate(int size) throws IOException; /** * This method is used to close the allocator so that resources * that are occupied by the allocator can be freed. This will * allow the allocator to be created and closed repeatedly in * a single process without holding on to resources such as * mapped file buffers or threads. */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/util/buffer/ArrayAllocator.java0000644000175000017500000001022111417313373027313 0ustar jamespagejamespage/* * ArrayAllocator.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.IOException; /** * The ArrayAllocator object is used to provide a means * to allocate buffers using a single byte array. This essentially uses * the heap to allocate all buffers. As a result the performance of the * resulting buffers is good, however for very large buffers this will * use quote allot of the usable heap space. For very large buffers a * mapped region of shared memory of a file should be considered. * * @author Niall Gallagher */ public class ArrayAllocator implements Allocator { /** * This represents the largest portion of memory that is allowed. */ private int limit; /** * This represents the default capacity of all allocated buffers. */ private int size; /** * Constructor for the ArrayAllocator object. This is * used to instantiate the allocator with a default buffer size of * half a kilobyte. This ensures that it can be used for general * purpose byte storage and for minor I/O tasks. */ public ArrayAllocator() { this(512); } /** * Constructor for the ArrayAllocator object. This is * used to instantiate the allocator with a specified buffer size. * This is typically used when a very specific buffer capacity is * required, for example a request body with a known length. * * @param size the initial capacity of the allocated buffers */ public ArrayAllocator(int size) { this(size, 1048576); } /** * Constructor for the ArrayAllocator object. This is * used to instantiate the allocator with a specified buffer size. * This is typically used when a very specific buffer capacity is * required, for example a request body with a known length. * * @param size the initial capacity of the allocated buffers * @param limit this is the maximum buffer size created by this */ public ArrayAllocator(int size, int limit) { this.limit = Math.max(size, limit); this.size = size; } /** * This method is used to allocate a default buffer. This will * allocate a buffer of predetermined size, allowing it to grow * to an upper limit to accomodate extra data. If the buffer * requested is larger than the limit an exception is thrown. * * @return this returns an allocated buffer with a default size */ public Buffer allocate() throws IOException { return allocate(size); } /** * This method is used to allocate a default buffer. This will * allocate a buffer of predetermined size, allowing it to grow * to an upper limit to accomodate extra data. If the buffer * requested is larger than the limit an exception is thrown. * * @param size the initial capacity of the allocated buffer * * @return this returns an allocated buffer with a default size */ public Buffer allocate(int size) throws IOException { if(size > limit) { throw new BufferException("Specified size %s beyond limit", size); } return new ArrayBuffer(size, limit); } /** * This method is used to close the allocator so that resources * that are occupied by the allocator can be freed. This will * allow the allocator to be created and closed repeatedly in * a single process without holding on to resources such as * mapped file buffers or threads. */ public void close() throws IOException { return; } } simple-http-4.1.21/src/org/simpleframework/util/buffer/FileAllocator.java0000644000175000017500000001211011417313373027113 0ustar jamespagejamespage/* * FileAllocator.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.util.buffer; import java.io.File; import java.io.IOException; /** * The FileAllocator object is used to create buffers * that can be written to the file system. This creates buffers as * files if they are larger than the specified limit. This ensures * that buffers of arbitrary large size can be created. All buffer * sizes under the limit are created using byte arrays allocated * on the executing VM heap. This ensures that optimal performance * is maintained for buffers of reasonable size. * * @author Niall Gallagher */ public class FileAllocator implements Allocator { /** * This is the default prefix used when none has been specified. */ private static final String PREFIX = "temp"; /** * This is the file manager used to create the buffer files. */ private FileManager manager; /** * This is the limit up to which buffers are allocated in memory. */ private int limit; /** * Constructor for the FileAllocator object. This is * used to create buffers in memory up to a threshold size. If a * buffer is required over the threshold size then the data is * written to a file, where it can be retrieved at a later point. */ public FileAllocator() throws IOException { this(1048576); } /** * Constructor for the FileAllocator object. This is * used to create buffers in memory up to a threshold size. If a * buffer is required over the threshold size then the data is * written to a file, where it can be retrieved at a later point. * * @param limit this is the maximum size for a heap buffer */ public FileAllocator(int limit) throws IOException { this(PREFIX, limit); } /** * Constructor for the FileAllocator object. This is * used to create buffers in memory up to a threshold size. If a * buffer is required over the threshold size then the data is * written to a file, where it can be retrieved at a later point. * * @param prefix this is the file prefix for the file buffers */ public FileAllocator(String prefix) throws IOException { this(prefix, 1048576); } /** * Constructor for the FileAllocator object. This is * used to create buffers in memory up to a threshold size. If a * buffer is required over the threshold size then the data is * written to a file, where it can be retrieved at a later point. * * @param prefix this is the file prefix for the file buffers * @param limit this is the maximum size for a heap buffer */ public FileAllocator(String prefix, int limit) throws IOException { this.manager = new FileManager(prefix); this.limit = limit; } /** * This will allocate a file buffer which will write data for the * buffer to a file. Buffers allocated by this method can be of * arbitrary size as data is appended directly to a temporary * file. This ensures there is no upper limit for appended data. * * @return a buffer which will write to a temporary file */ public Buffer allocate() throws IOException { File file = manager.create(); if(!file.exists()) { throw new BufferException("Could not create file %s", file); } return new FileBuffer(file); } /** * This will allocate a file buffer which will write data for the * buffer to a file. Buffers allocated by this method can be of * arbitrary size as data is appended directly to a temporary * file. This ensures there is no upper limit for appended data. * If the size required is less than the limit then the buffer * is an in memory array which provides optimal performance. * * @param size this is the size of the buffer to be created * * @return a buffer which will write to a created temporary file */ public Buffer allocate(int size) throws IOException { if(size <= limit) { return new ArrayBuffer(size); } return allocate(); } /** * This method is used to close the allocator so that resources * that are occupied by the allocator can be freed. This will * allow the allocator to be created and closed repeatedly in * a single process without holding on to resources such as * mapped file buffers or threads. */ public void close() throws IOException { manager.close(); } } simple-http-4.1.21/src/org/simpleframework/util/buffer/BufferAllocator.java0000644000175000017500000001735211417313373027462 0ustar jamespagejamespage/* * BufferAllocator.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.IOException; import java.io.InputStream; /** * The BufferAllocator object is used to provide a means * to allocate buffers using a single underlying buffer. This uses a * buffer from a existing allocator to create the region of memory to * use to allocate all other buffers. As a result this allows a single * buffer to acquire the bytes in a number of associated buffers. This * has the advantage of allowing bytes to be read in sequence without * joining data from other buffers or allocating multiple regions. * * @author Niall Gallagher */ public class BufferAllocator extends FilterAllocator implements Buffer { /** * This is the underlying buffer all other buffers are within. */ private Buffer buffer; /** * Constructor for the BufferAllocator object. This is * used to instantiate the allocator with a default buffer size of * half a kilobyte. This ensures that it can be used for general * purpose byte storage and for minor I/O tasks. * * @param source this is where the underlying buffer is allocated */ public BufferAllocator(Allocator source) { super(source); } /** * Constructor for the BufferAllocator object. This is * used to instantiate the allocator with a specified buffer size. * This is typically used when a very specific buffer capacity is * required, for example a request body with a known length. * * @param source this is where the underlying buffer is allocated * @param capacity the initial capacity of the allocated buffers */ public BufferAllocator(Allocator source, int capacity) { super(source, capacity); } /** * Constructor for the BufferAllocator object. This is * used to instantiate the allocator with a specified buffer size. * This is typically used when a very specific buffer capacity is * required, for example a request body with a known length. * * @param source this is where the underlying buffer is allocated * @param capacity the initial capacity of the allocated buffers * @param limit this is the maximum buffer size created by this */ public BufferAllocator(Allocator source, int capacity, int limit) { super(source, capacity, limit); } /** * This method is used so that a buffer can be represented as a * stream of bytes. This provides a quick means to access the data * that has been written to the buffer. It wraps the buffer within * an input stream so that it can be read directly. * * @return a stream that can be used to read the buffered bytes */ public InputStream getInputStream() throws IOException { if(buffer == null) { allocate(); } return buffer.getInputStream(); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. If the UTF-8 * content encoding is not supported the platform default is * used, however this is unlikely as UTF-8 should be supported. * * @return this returns a UTF-8 encoding of the buffer contents */ public String encode() throws IOException { if(buffer == null) { allocate(); } return buffer.encode(); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. This will convert * the bytes using the specified character encoding format. * * @return this returns the encoding of the buffer contents */ public String encode(String charset) throws IOException { if(buffer == null) { allocate(); } return buffer.encode(charset); } /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer * * @return this returns this buffer for another operation */ public Buffer append(byte[] array) throws IOException { return append(array, 0, array.length); } /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer * @param size the number of bytes to be read from the array * @param off this is the offset to begin reading the bytes from * * @return this returns this buffer for another operation */ public Buffer append(byte[] array, int off, int size) throws IOException { if(buffer == null) { allocate(size); } return buffer.append(array, off, size); } /** * This will clear all data from the buffer. This simply sets the * count to be zero, it will not clear the memory occupied by the * instance as the internal buffer will remain. This allows the * memory occupied to be reused as many times as is required. */ public void clear() throws IOException { if(buffer != null) { buffer.clear(); } } /** * This method is used to ensure the buffer can be closed. Once * the buffer is closed it is an immutable collection of bytes and * can not longer be modified. This ensures that it can be passed * by value without the risk of modification of the bytes. */ public void close() throws IOException { if(buffer == null) { allocate(); } buffer.close(); } /** * This method is used to allocate a default buffer. This will * allocate a buffer of predetermined size, allowing it to grow * to an upper limit to accommodate extra data. If the buffer * requested is larger than the limit an exception is thrown. * * @return this returns an allocated buffer with a default size */ @Override public Buffer allocate() throws IOException { return allocate(capacity); } /** * This method is used to allocate a default buffer. This will * allocate a buffer of predetermined size, allowing it to grow * to an upper limit to accommodate extra data. If the buffer * requested is larger than the limit an exception is thrown. * * @param size the initial capacity of the allocated buffer * * @return this returns an allocated buffer with a default size */ @Override public Buffer allocate(int size) throws IOException { if(size > limit) { throw new BufferException("Specified size %s beyond limit", size); } if(capacity > size) { // lazily create backing buffer size = capacity; } if(buffer == null) { buffer = source.allocate(size); } return buffer.allocate(); } } simple-http-4.1.21/src/org/simpleframework/util/buffer/FileManager.java0000644000175000017500000001626211417313373026561 0ustar jamespagejamespage/* * FileManager.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.util.buffer; import java.io.File; import java.io.FileFilter; import java.io.IOException; import org.simpleframework.util.thread.Daemon; /** * The FileManager object is used to create files that * are to be used for file buffers. All files created by this are * created in the java.io.tmpdir path. Temporary files * created in this directory last for five minutes before being * deleted. This ensures that if the server is running for a long * period of time the file system is not exhausted. * * @author Niall Gallagher */ class FileManager extends Daemon implements FileFilter { /** * This is the prefix for the temporary files created. */ private String prefix; /** * This is the duration the files created will exist for. */ private long duration; /** * This determines whether the file system should clean up. */ private volatile boolean dead; /** * Constructor for the FileManager object. This will * create a thread that runs every five minutes and cleans up * files that have been created for buffers. Due to the period * of time polled, files could exist for up to ten minutes. * * @param prefix this is the file name prefix for the files */ public FileManager(String prefix) throws IOException { this(prefix, 300000); } /** * Constructor for the FileManager object. This will * create a thread that runs every five minutes and cleans up * files that have been created for buffers. Due to the period * of time polled, files could exist for up to ten minutes. * * @param prefix this is the file name prefix for the files * @param duration this is the duration the files exist for */ public FileManager(String prefix, long duration) throws IOException { this.duration = duration; this.prefix = prefix; this.start(); } /** * This will create a temporary file which can be used as a buffer * for FileBuffer objects. The file returned by this * method will be created before it is returned, which ensures it * can be used as a means to buffer bytes. All files are created * in the java.io.tmpdir location, which represents * the underlying file system temporary file destination. * * @return this returns a created temporary file for buffers */ public File create() throws IOException { return create(prefix); } /** * This will create a temporary file which can be used as a buffer * for FileBuffer objects. The file returned by this * method will be created before it is returned, which ensures it * can be used as a means to buffer bytes. All files are created * in the java.io.tmpdir location, which represents * the underlying file system temporary file destination. * * @param prefix this is the prefix of the file to be created * * @return this returns a created temporary file for buffers */ private File create(String prefix) throws IOException { File file = File.createTempFile(prefix, null); if(!file.exists()) { file.createNewFile(); } return file; } /** * This is the run method that will periodically poll the file * file system for temporary buffer files. If files matching the * pattern are found and have not been modified in the duration * period of time then they will be deleted to ensure that the * file system is not exhausted during a long server execution. */ public void run() { while(!dead) { try { Thread.sleep(duration); clean(); } catch(Exception e) { continue; } } } /** * When this method is invoked the files that match the pattern * of the temporary files are evaluated for deletion. Only those * files that have not been modified in the duration period can * be deleted. This ensures the file system is not exhausted. */ private void clean() throws IOException { File path = create(); if(!path.isDirectory()) { path = path.getParentFile(); } clean(path); } /** * When this method is invoked the files that match the pattern * of the temporary files are evaluated for deletion. Only those * files that have not been modified in the duration period can * be deleted. This ensures the file system is not exhausted. * * @param path this is the path of the file to be evaluated */ private void clean(File path) throws IOException { File[] list = path.listFiles(this); for(File next : list) { next.delete(); } } /** * This determines if the file provided is an acceptable file for * deletion. Acceptable files are those that match the pattern * of files created by this file system object. If the file is * a matching file then it is a candidate for deletion. * * @param file this is the file to evaluate for deletion * * @return this returns true if the file matches the pattern */ public boolean accept(File file) { String name = file.getName(); if(file.isDirectory()) { return false; } return accept(file, name); } /** * This determines if the file provided is an acceptable file for * deletion. Acceptable files are those that match the pattern * of files created by this file system object. If the file is * a matching file then it is a candidate for deletion. * * @param file this is the file to evaluate for deletion * @param name this is the name of the file to be evaluated * * @return this returns true if the file matches the pattern */ private boolean accept(File file, String name) { long time = System.currentTimeMillis(); long modified = file.lastModified(); if(modified + duration > time) { // not yet expired return false; } return name.startsWith(prefix); } /** * This method is used to close the allocator so that resources * that are occupied by the allocator can be freed. This will * allow the allocator to be created and closed repeatedly in * a single process without holding on to resources such as * mapped file buffers or threads. */ public void close() throws IOException { if(!dead) { dead = true; interrupt(); clean(); } } } simple-http-4.1.21/src/org/simpleframework/util/buffer/BufferException.java0000644000175000017500000000273011417313373027472 0ustar jamespagejamespage/* * BufferException.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.IOException; /** * The BufferException is used to report problems that * can occur during the use or allocation of a buffer. Typically * this is thrown if the upper capacity limit is exceeded. * * @author Niall Gallagher */ public class BufferException extends IOException { /** * Constructor for the BufferException object. The * exception can be provided with a message describing the issue * that has arisen in the use or allocation of the buffer. * * @param format this is the template for the exception * @param values these are the values to be added to the template */ public BufferException(String format, Object... values) { super(String.format(format, values)); } } simple-http-4.1.21/src/org/simpleframework/util/buffer/Buffer.java0000644000175000017500000001024611417313373025614 0ustar jamespagejamespage/* * Buffer.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.buffer; import java.io.IOException; /** * The Buffer interface represents a collection of bytes * that can be written to and later read. This is used to provide a * region of memory is such a way that the underlying representation * of that memory is independent of its use. Typically buffers are * implemented as either allocated byte arrays or files. * * @author Niall Gallagher * * @see org.simpleframework.util.buffer.Allocator */ public interface Buffer extends Stream { /** * This method is used to allocate a segment of this buffer as a * separate buffer object. This allows the buffer to be sliced in * to several smaller independent buffers, while still allowing the * parent buffer to manage a single buffer. This is useful if the * parent is split in to logically smaller segments. * * @return this returns a buffer which is a segment of this buffer */ public Buffer allocate() throws IOException; /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. If the UTF-8 * content encoding is not supported the platform default is * used, however this is unlikely as UTF-8 should be supported. * * @return this returns a UTF-8 encoding of the buffer contents */ public String encode() throws IOException; /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. This will convert * the bytes using the specified character encoding format. * * @param charset this is the charset to encode the data with * * @return this returns the encoding of the buffer contents */ public String encode(String charset) throws IOException; /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer * * @return this returns this buffer for another operation */ public Buffer append(byte[] array) throws IOException; /** * This method is used to append bytes to the end of the buffer. * This will expand the capacity of the buffer if there is not * enough space to accommodate the extra bytes. * * @param array this is the byte array to append to this buffer * @param len the number of bytes to be read from the array * @param off this is the offset to begin reading the bytes from * * @return this returns this buffer for another operation */ public Buffer append(byte[] array, int off, int len) throws IOException; /** * This will clear all data from the buffer. This simply sets the * count to be zero, it will not clear the memory occupied by the * instance as the internal buffer will remain. This allows the * memory occupied to be reused as many times as is required. */ public void clear() throws IOException; /** * This method is used to ensure the buffer can be closed. Once * the buffer is closed it is an immutable collection of bytes and * can not longer be modified. This ensures that it can be passed * by value without the risk of modification of the bytes. */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/util/buffer/FileBuffer.java0000644000175000017500000004752211417313373026423 0ustar jamespagejamespage/* * FileBuffer.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.util.buffer; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * The FileBuffer object is used to create a buffer * which will write the appended data to an underlying file. This * is typically used for buffers that are too large for to allocate * in memory. Data appended to the buffer can be retrieved at a * later stage by acquiring the InputStream for the * underlying file. To ensure that excessive file system space is * not occupied the buffer files are cleaned every five minutes. * * @author Niall Gallagher * * @see org.simpleframework.util.buffer.FileAllocator */ class FileBuffer implements Buffer { /** * This is the file output stream used for this buffer object. */ private OutputStream buffer; /** * This represents the last file segment that has been created. */ private Segment segment; /** * This is the path for the file that this buffer appends to. */ private File file; /** * This is the number of bytes currently appended to the buffer. */ private int count; /** * This is used to determine if this buffer has been closed. */ private boolean closed; /** * Constructor for the FileBuffer object. This will * create a buffer using the provided file. All data appended to * this buffer will effectively written to the underlying file. * If the appended data needs to be retrieved at a later stage * then it can be acquired using the buffers input stream. * * @param file this is the file used for the file buffer */ public FileBuffer(File file) throws IOException { this.buffer = new FileOutputStream(file); this.file = file; } /** * This is used to allocate a segment within this buffer. If the * buffer is closed this will throw an exception, if however the * buffer is still open then a segment is created which will * write all appended data to this buffer. However it can be * treated as an independent source of data. * * @return this returns a buffer which is a segment of this */ public Buffer allocate() throws IOException { if(closed) { throw new BufferException("Buffer has been closed"); } if(segment != null) { segment.close(); } if(!closed) { segment = new Segment(this, count); } return segment; } /** * This is used to append the specified data to the underlying * file. All bytes appended to the file can be consumed at a * later stage by acquiring the InputStream from * this buffer. Also if require the data can be encoded as a * string object in a required character set. * * @param array this is the array to write the the file * * @return this returns this buffer for further operations */ public Buffer append(byte[] array) throws IOException { return append(array, 0, array.length); } /** * This is used to append the specified data to the underlying * file. All bytes appended to the file can be consumed at a * later stage by acquiring the InputStream from * this buffer. Also if require the data can be encoded as a * string object in a required character set. * * @param array this is the array to write the the file * @param off this is the offset within the array to write * @param size this is the number of bytes to be appended * * @return this returns this buffer for further operations */ public Buffer append(byte[] array, int off, int size) throws IOException { if(closed) { throw new BufferException("Buffer has been closed"); } if(size > 0) { buffer.write(array, off, size); count += size; } return this; } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. If the UTF-8 * content encoding is not supported the platform default is * used, however this is unlikely as UTF-8 should be supported. * * @return this returns a UTF-8 encoding of the buffer contents */ public String encode() throws IOException { return encode("UTF-8"); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. This will convert * the bytes using the specified character encoding format. * * @param charset this is the charset to encode the data with * * @return this returns the encoding of the buffer contents */ public String encode(String charset) throws IOException { InputStream source = getInputStream(); if(count <= 0) { return new String(); } return convert(source, charset, count); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. This will convert * the bytes using the specified character encoding format. * * @param source this is the source stream that is to be encoded * @param charset this is the charset to encode the data with * @param count this is the number of bytes to be encoded * * @return this returns the encoding of the buffer contents */ private String convert(InputStream source, String charset, int count) throws IOException { byte[] buffer = new byte[count]; int left = count; int mark = 0; while(left > 0) { int size = source.read(buffer, 0, left); if(size == -1) { throw new BufferException("Could not read buffer"); } left -= count; mark += count; } return new String(buffer, charset); } /** * This method is used so that a buffer can be represented as a * stream of bytes. This provides a quick means to access the data * that has been written to the buffer. It wraps the buffer within * an input stream so that it can be read directly. * * @return a stream that can be used to read the buffered bytes */ public InputStream getInputStream() throws IOException { if(!closed) { close(); } return getInputStream(file); } /** * This method is used so that a buffer can be represented as a * stream of bytes. This provides a quick means to access the data * that has been written to the buffer. It wraps the buffer within * an input stream so that it can be read directly. * * @param file this is the file used to create the input stream * * @return a stream that can be used to read the buffered bytes */ private InputStream getInputStream(File file) throws IOException { InputStream source = new FileInputStream(file); if(count <= 0) { source.close(); // release file descriptor } return new Range(source, count); } /** * This will clear all data from the buffer. This simply sets the * count to be zero, it will not clear the memory occupied by the * instance as the internal buffer will remain. This allows the * memory occupied to be reused as many times as is required. */ public void clear() throws IOException { if(closed) { throw new BufferException("Buffer has been closed"); } } /** * This method is used to ensure the buffer can be closed. Once * the buffer is closed it is an immutable collection of bytes and * can not longer be modified. This ensures that it can be passed * by value without the risk of modification of the bytes. */ public void close() throws IOException { if(!closed) { buffer.close(); closed = true; } if(segment != null) { segment.close(); } } /** * The Segment object is used to create a segment of * the parent buffer. The segment will write to the parent however * if can be read as a unique range of bytes starting with the * first sequence of bytes appended to the segment. A segment can * be used to create a collection of buffers backed by the same * underlying file, as is require with multipart uploads. * * @author Niall Gallagher */ private class Segment implements Buffer { /** * This is an internal segment created from this buffer object. */ private Segment segment; /** * This is the parent buffer that bytes are to be appended to. */ private Buffer parent; /** * This is the offset of the first byte within the sequence. */ private int first; /** * This is the last byte within the segment for this segment. */ private int last; /** * This determines if the segment is currently open or closed. */ private boolean closed; /** * Constructor for the Segment object. This is used * to create a segment from a parent buffer. A segment is a part * of the parent buffer and appends its bytes to the parent. It * can however be treated as an independent source of bytes. * * @param parent this is the parent buffer to be appended to * @param first this is the offset for the first byte in this */ public Segment(Buffer parent, int first) { this.parent = parent; this.first = first; this.last = first; } /** * This is used to allocate a segment within this buffer. If the * buffer is closed this will throw an exception, if however the * buffer is still open then a segment is created which will * write all appended data to this buffer. However it can be * treated as an independent source of data. * * @return this returns a buffer which is a segment of this */ public Buffer allocate() throws IOException { if(closed) { throw new BufferException("Buffer has been closed"); } if(segment != null) { segment.close(); } if(!closed) { segment = new Segment(this, last); } return segment; } /** * This is used to append the specified data to the underlying * file. All bytes appended to the file can be consumed at a * later stage by acquiring the InputStream from * this buffer. Also if require the data can be encoded as a * string object in a required character set. * * @param array this is the array to write the the file * * @return this returns this buffer for further operations */ public Buffer append(byte[] array) throws IOException { return append(array, 0, array.length); } /** * This is used to append the specified data to the underlying * file. All bytes appended to the file can be consumed at a * later stage by acquiring the InputStream from * this buffer. Also if require the data can be encoded as a * string object in a required character set. * * @param array this is the array to write the the file * @param off this is the offset within the array to write * @param size this is the number of bytes to be appended * * @return this returns this buffer for further operations */ public Buffer append(byte[] array, int off, int size) throws IOException { if(closed) { throw new BufferException("Buffer has been closed"); } if(size > 0) { parent.append(array, off, size); last += size; } return this; } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. If the UTF-8 * content encoding is not supported the platform default is * used, however this is unlikely as UTF-8 should be supported. * * @return this returns a UTF-8 encoding of the buffer contents */ public String encode() throws IOException { return encode("UTF-8"); } /** * This method is used to acquire the buffered bytes as a string. * This is useful if the contents need to be manipulated as a * string or transferred into another encoding. This will convert * the bytes using the specified character encoding format. * * @param charset this is the charset to encode the data with * * @return this returns the encoding of the buffer contents */ public String encode(String charset) throws IOException { InputStream source = getInputStream(); int count = last - first; if(count <= 0) { return new String(); } return convert(source, charset, count); } /** * This method is used so that a buffer can be represented as a * stream of bytes. This provides a quick means to access the data * that has been written to the buffer. It wraps the buffer within * an input stream so that it can be read directly. * * @return a stream that can be used to read the buffered bytes */ public InputStream getInputStream() throws IOException { InputStream source = new FileInputStream(file); int length = last - first; if(first > 0) { source.skip(first); } return new Range(source, length); } /** * This will clear all data from the buffer. This simply sets the * count to be zero, it will not clear the memory occupied by the * instance as the internal buffer will remain. This allows the * memory occupied to be reused as many times as is required. */ public void clear() throws IOException { if(closed) { throw new BufferException("Buffer is closed"); } } /** * This method is used to ensure the buffer can be closed. Once * the buffer is closed it is an immutable collection of bytes and * can not longer be modified. This ensures that it can be passed * by value without the risk of modification of the bytes. */ public void close() throws IOException { if(!closed) { closed = true; } if(segment != null) { segment.close(); } } } /** * The Range object is used to provide a stream that * can read a range of bytes from a provided input stream. This * allows buffer segments to be allocated from the main buffer. * Providing a range in this manner ensures that only one backing * file is needed for the primary buffer allocated. * * @author Niall Gallagher */ private class Range extends FilterInputStream { /** * This is the length of the bytes that exist in the range. */ private int length; /** * This is used to close the stream once it has been read. */ private boolean closed; /** * Constructor for the Range object. This ensures * that only a limited number of bytes can be consumed from a * backing input stream giving the impression of an independent * stream of bytes for a segmented region of the parent buffer. * * @param source this is the input stream used to read data * @param length this is the number of bytes that can be read */ public Range(InputStream source, int length) { super(source); this.length = length; } /** * This will read data from the underlying stream up to the * number of bytes this range is allowed to read. When all of * the bytes are exhausted within the stream this returns -1. * * @return this returns the octet from the underlying stream */ @Override public int read() throws IOException { if(length-- > 0) { return in.read(); } if(length <= 0) { close(); } return -1; } /** * This will read data from the underlying stream up to the * number of bytes this range is allowed to read. When all of * the bytes are exhausted within the stream this returns -1. * * @param array this is the array to read the bytes in to * @param off this is the start offset to append the bytes to * @param size this is the number of bytes that are required * * @return this returns the number of bytes that were read */ @Override public int read(byte[] array, int off, int size) throws IOException { int left = Math.min(length, size); if(left > 0) { int count = in.read(array, off, left); if(count > 0){ length -= count; } if(length <= 0) { close(); } return count; } return -1; } /** * This returns the number of bytes that can be read from the * range. This will be the actual number of bytes the range * contains as the underlying file will not block reading. * * @return this returns the number of bytes within the range */ @Override public int available() throws IOException { return length; } /** * This is the number of bytes to skip from the buffer. This * will allow up to the number of remaining bytes within the * range to be read. When all the bytes have been read this * will return zero indicating no bytes were skipped. * * @param size this returns the number of bytes to skip * * @return this returns the number of bytes that were skipped */ @Override public long skip(long size) throws IOException { long left = Math.min(length, size); long skip = in.skip(left); if(skip > 0) { length -= skip; } if(length <= 0) { close(); } return skip; } /** * This is used to close the range once all of the content has * been fully read. The Range object forces the * close of the stream once all the content has been consumed * to ensure that excessive file descriptors are used. Also * this will ensure that the files can be deleted. */ @Override public void close() throws IOException { if(!closed) { in.close(); closed =true; } } } } simple-http-4.1.21/src/org/simpleframework/util/KeyMap.java0000644000175000017500000000607211417313373024322 0ustar jamespagejamespage/* * KeyMap.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Set; /** * The KeyMap object is used to represent a map of values * keyed using a known string. This also ensures that the keys and * the values added to this hash map can be acquired in an independent * list of values, ensuring that modifications to the map do not have * an impact on the lists provided, and vice versa. The key map can * also be used in a fore each look using the string keys. * * @author Niall Gallagher */ public class KeyMap extends HashMap implements Iterable { /** * Constructor for the KeyMap object. This creates * a hash map that can expose the keys and values of the map as * an independent List containing the values. This * can also be used within a for loop for convenience. */ public KeyMap() { super(); } /** * This is used to produce an Iterator of values * that can be used to acquire the contents of the key map within * a for each loop. The key map can be modified while it is been * iterated as the iterator is an independent list of values. * * @return this returns an iterator of the keys in the map */ public Iterator iterator() { return getKeys().iterator(); } /** * This is used to produce a List of the keys in * the map. The list produced is a copy of the internal keys and * so can be modified and used without affecting this map object. * * @return this returns an independent list of the key values */ public List getKeys() { Set keys = keySet(); if(keys == null) { return new ArrayList(); } return new ArrayList(keys); } /** * This is used to produce a List of the values in * the map. The list produced is a copy of the internal values and * so can be modified and used without affecting this map object. * * @return this returns an independent list of the values */ public List getValues() { Collection values = values(); if(values == null) { return new ArrayList(); } return new ArrayList(values); } }simple-http-4.1.21/src/org/simpleframework/util/FormatException.java0000644000175000017500000000402211417313373026234 0ustar jamespagejamespage/* * FormatException.java May 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util; /** * The FormatException is used to create exceptions that * can use a template string for the message. Each format exception * will accept a string and an ordered list of variables which can * be used to complete the exception message. * * @author Niall Gallagher */ public class FormatException extends Exception { /** * Constructor for the FormatException this requires * a template message and an ordered list of values that are to * be inserted in to the provided template to form the error. * * @param template this is the template string to be modified * @param list this is the list of values that are to be inserted */ public FormatException(String template, Object... list) { super(String.format(template, list)); } /** * Constructor for the FormatException this requires * a template message and an ordered list of values that are to * be inserted in to the provided template to form the error. * * @param cause this is the original cause of the exception * @param template this is the template string to be modified * @param list this is the list of values that are to be inserted */ public FormatException(Throwable cause, String template, Object... list) { super(String.format(template, list), cause); } } simple-http-4.1.21/src/org/simpleframework/util/thread/0000755000175000017500000000000011767603362023543 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/util/thread/Daemon.java0000644000175000017500000000743111417313373025606 0ustar jamespagejamespage/* * Daemon.java February 2009 * * Copyright (C) 2009, Niall Gallagher * * 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.util.thread; import static java.lang.Thread.State.NEW; /** * The Daemon object provides a named daemon thread * which will execute the run method when started. * This offers some convenience in that it hides the normal thread * methods and also allows the object extending this to provide * the name of the internal thread, which is given an incrementing * sequence number appended to the name provided. * * @author Niall Gallagher */ public abstract class Daemon implements Runnable { /** * This is the internal thread used by this daemon instance. */ private Thread thread; /** * Constructor for the Daemon object. This will * create the internal thread and ensure it is a daemon. When it * is started the name of the internal thread is set using the * name of the instance as taken from getName. If * the name provided is null then no name is set for the thread. */ protected Daemon() { this.thread = new Thread(this); } /** * This is used to start the internal thread. Once started the * internal thread will execute the run method of * this instance. Aside from starting the thread this will also * ensure the internal thread has a unique name. */ public void start() { String prefix = getName(); String name = ThreadNamer.getName(prefix); if(!isStarted()) { thread.setName(name); thread.start(); } } /** * This is used to determine if the daemon has already started. * Once started it can not be started again. This ensures that * when dead it remains dead. The contract of this method is * that if the start method is invoked at any * point this method will always return true. * * @return true if the daemon has already been started */ public boolean isStarted() { return thread.getState() != NEW; } /** * This is used to interrupt the internal thread. This is used * when there is a need to wake the thread from a sleeping or * waiting state so that some other operation can be performed. * Typically this is required when killing the thread. */ public void interrupt() { thread.interrupt(); } /** * This is used to join with the internal thread of this daemon. * Rather than exposing the internal thread a join * method is provided. This allows asynchronous threads to wait * for the daemon to complete simulating synchronous action. * * @throws InterruptedException if the thread is interrupted */ public void join() throws InterruptedException { thread.join(); } /** * This is used to acquire the name of the thread. This will be * overridden by instances that wish to provide a descriptive * name for the thread. If this is not overridden then the name * of the thread is the simple name of the implementation. * * @return the name of the internal thread executed */ public String getName() { return getClass().getSimpleName(); } } simple-http-4.1.21/src/org/simpleframework/util/thread/DirectExecutor.java0000644000175000017500000000261111417313373027327 0ustar jamespagejamespage/* * DirectExecutor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.thread; import java.util.concurrent.Executor; /** * The DirectExecutor object is used for synchronous * execution of tasks. This simple acts as an adapter for running * a Runnable implementation and can be used wherever * the executor interface is required. * * @author Niall Gallagher */ public class DirectExecutor implements Executor { /** * This will execute the provided Runnable within * the current thread. This implementation will simple invoke * the run method of the task and wait for it to complete. * * @param task this is the task that is to be executed */ public void execute(Runnable task) { task.run(); } }simple-http-4.1.21/src/org/simpleframework/util/thread/PoolQueue.java0000644000175000017500000000767611417313373026334 0ustar jamespagejamespage/* * PoolQueue.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.thread; import static java.util.concurrent.TimeUnit.SECONDS; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * The PoolQueue object is used to execute tasks in * a thread pool. This creates a thread pool with an unbounded list * of outstanding tasks, which ensures that any system requesting * a task to be executed will not block when handing it over. * * @author Niall Gallagher */ class PoolQueue extends ThreadPoolExecutor { /** * Constructor for the PoolQueue object. This is * used to create a pool of threads that can be used to execute * arbitrary Runnable tasks. If the threads are * busy this will simply enqueue the tasks and return. * * @param type this is the type of runnable that this accepts * @param rest this is the number of threads to use in the pool * @param active this is the maximum size the pool can grow to */ public PoolQueue(Class type, int rest, int active) { this(type, rest, active, 120, TimeUnit.SECONDS); } /** * Constructor for the PoolQueue object. This is * used to create a pool of threads that can be used to execute * arbitrary Runnable tasks. If the threads are * busy this will simply enqueue the tasks and return. * * @param type this is the type of runnable that this accepts * @param rest this is the number of threads to use in the pool * @param active this is the maximum size the pool can grow to * @param duration the duration active threads remain idle for * @param unit this is the time unit used for the duration */ public PoolQueue(Class type, int rest, int active, long duration, TimeUnit unit) { super(rest, active, duration, unit, new Queue(), new PoolFactory(type)); } /** * This is used to wait until such time as the pool has terminated. * Using a join such as this allows the user to be sure that there * are no further tasks enqueued for execution and there are no * tasks currently executing. This helps provide graceful shutdown. */ public void join() { boolean dead = isTerminated(); while(!dead) { try { dead = awaitTermination(10, SECONDS); } catch(InterruptedException e) { break; } } } /** * This is used to stop the executor by interrupting all running * tasks and shutting down the threads within the pool. This will * return once it has been stopped, and no further tasks will be * accepted by this pool for execution. */ public void stop() { shutdown(); join(); } /** * This is the internal queue used by this implementation. This * provides an unlimited number of positions for new tasks to * be queued. Having an unlimited queue prevents deadlocks. * * @author Niall Gallagher */ private static class Queue extends LinkedBlockingQueue { /** * Constructor for the Queue object. This will * create a linked blocking queue with an unlimited capacity. */ public Queue() { super(); } } } simple-http-4.1.21/src/org/simpleframework/util/thread/PoolExecutor.java0000644000175000017500000000663711417313373027042 0ustar jamespagejamespage/* * PoolExecutor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.thread; import java.util.concurrent.Executor; /** * The PoolExecutor object is used to execute tasks in * a thread pool. This creates a thread pool with an unbounded list * of outstanding tasks, which ensures that any system requesting * a task to be executed will not block when handing it over. * * @author Niall Gallagher */ public class PoolExecutor implements Executor { /** * This is the queue used to enqueue the tasks for execution. */ private final PoolQueue queue; /** * Constructor for the PoolExecutor object. This is * used to create a pool of threads that can be used to execute * arbitrary Runnable tasks. If the threads are * busy this will simply enqueue the tasks and return. * * @param type this is the type of runnable that this accepts */ public PoolExecutor(Class type) { this(type, 10); } /** * Constructor for the PoolExecutor object. This is * used to create a pool of threads that can be used to execute * arbitrary Runnable tasks. If the threads are * busy this will simply enqueue the tasks and return. * * @param type this is the type of runnable that this accepts * @param size this is the number of threads to use in the pool */ public PoolExecutor(Class type, int size) { this(type, size, size); } /** * Constructor for the PoolExecutor object. This is * used to create a pool of threads that can be used to execute * arbitrary Runnable tasks. If the threads are * busy this will simply enqueue the tasks and return. * * @param type this is the type of runnable that this accepts * @param rest this is the number of threads to use in the pool * @param active this is the maximum size the pool can grow to */ public PoolExecutor(Class type, int rest, int active) { this.queue = new PoolQueue(type, rest, active); } /** * The execute method is used to queue the task for * execution. If all threads are busy the provided task is queued * and waits until all current and outstanding tasks are finished. * * @param task this is the task to be queued for execution */ public void execute(Runnable task) { queue.execute(task); } /** * This is used to stop the executor by interrupting all running * tasks and shutting down the threads within the pool. This will * return once it has been stopped, and no further tasks will be * accepted by this pool for execution. */ public void stop() { queue.stop(); } } simple-http-4.1.21/src/org/simpleframework/util/thread/PoolFactory.java0000644000175000017500000000411211417313373026635 0ustar jamespagejamespage/* * PoolFactory.java February 2009 * * Copyright (C) 2009, Niall Gallagher * * 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.util.thread; import java.util.concurrent.ThreadFactory; /** * The PoolFactory object is used to create a thread * factory that will instantiate named threads. This provides a * means to name the thread started using the type of task the * pool is required to execute. * * @author Niall Gallagher */ class PoolFactory implements ThreadFactory { /** * This is the type of the task this pool will execute. */ private final Class type; /** * Constructor for the PoolFactory object. This * will provide a thread factory that names the threads based * on the type of Runnable the pool executes. Each * of the threads is given a unique sequence number. * * @param type this is the type of runnable this will execute */ public PoolFactory(Class type) { this.type = type; } /** * This is used to create a new thread. The new thread will be * given the simple name of the Runnable class that * it accepts. Each thread is also given a unique sequence. * * @param task this is the worker that the thread pool uses * * @return this returns the thread that is to be used by this */ public Thread newThread(Runnable task) { String prefix = type.getSimpleName(); String name = ThreadNamer.getName(prefix); return new Thread(task, name); } } simple-http-4.1.21/src/org/simpleframework/util/thread/Scheduler.java0000644000175000017500000000665711417313373026332 0ustar jamespagejamespage/* * Scheduler.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.thread; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; /** * The Scheduler object is used to schedule tasks for * execution. This queues the task for the requested period of time * before it is executed. It ensures that the delay is adhered to * such that tasks can be timed for execution in an accurate way. * * @author Niall Gallagher */ public class Scheduler implements Executor { /** * This is the scheduler queue used to enque tasks to execute. */ private SchedulerQueue engine; /** * Constructor for the Scheduler object. This will * create a scheduler with a fixed number of threads to use * before execution. Depending on the types of task that are * to be executed this should be increased for accuracy. * * @param size this is the number of threads for the scheduler */ public Scheduler(int size) { this.engine = new SchedulerQueue(size); } /** * This will execute the task within the executor immediately * as it uses a delay duration of zero milliseconds. This can * be used if the scheduler is to be used as a thread pool. * * @param task this is the task to schedule for execution */ public void execute(Runnable task) { execute(task, 0); } /** * This will execute the task within the executor after the time * specified has expired. If the time specified is zero then it * will be executed immediately. Once the scheduler has been * stopped then this method will no longer accept runnable tasks. * * @param task this is the task to schedule for execution * @param delay the time in milliseconds to wait for execution */ public void execute(Runnable task, long delay) { execute(task, delay, TimeUnit.MILLISECONDS); } /** * This will execute the task within the executor after the time * specified has expired. If the time specified is zero then it * will be executed immediately. Once the scheduler has been * stopped then this method will no longer accept runnable tasks. * * @param task this is the task to schedule for execution * @param delay this is the delay to wait before execution * @param unit this is the duration time unit to wait for */ public void execute(Runnable task, long delay, TimeUnit unit) { engine.schedule(task, delay, unit); } /** * This is used to stop the executor by interrupting all running * tasks and shutting down the threads within the pool. This will * return immediately once it has been stopped, and not further * tasks will be accepted by this pool for execution. */ public void stop() { engine.stop(); } } simple-http-4.1.21/src/org/simpleframework/util/thread/SchedulerQueue.java0000644000175000017500000000354111417313373027324 0ustar jamespagejamespage/* * SchedulerQueue.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.util.thread; import java.util.concurrent.ScheduledThreadPoolExecutor; /** * The SchedulerQueue object is used to schedule tasks * for execution. This queues the task for the requested period of * time before it is executed. It ensures that the delay is adhered * to such that tasks can be timed for execution in an accurate way. * * @author Niall Gallagher */ class SchedulerQueue extends ScheduledThreadPoolExecutor { /** * Constructor for the SchedulerQueue object. This * will create a scheduler with a fixed number of threads to use * before execution. Depending on the types of task that are * to be executed this should be increased for accuracy. * * @param size this is the number of threads for the scheduler */ public SchedulerQueue(int size) { super(size); } /** * This is used to stop the executor by interrupting all running * tasks and shutting down the threads within the pool. This will * return immediately once it has been stopped, and not further * tasks will be accepted by this pool for execution. */ public void stop() { shutdown(); } } simple-http-4.1.21/src/org/simpleframework/util/thread/ThreadNamer.java0000644000175000017500000000653311417313373026577 0ustar jamespagejamespage/* * ThreadNamer.java February 2009 * * Copyright (C) 2009, Niall Gallagher * * 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.util.thread; import java.util.concurrent.atomic.AtomicInteger; import org.simpleframework.util.KeyMap; /** * The ThreadNamer object is used to name threads using * sequence numbers. Each thread with the same name will be given a * unique sequence number which is appended to the end of the name. * This is similar to the Java thread naming convention used by the * standard thread and thread pool implementations. * * @author Niall Gallagher */ class ThreadNamer { /** * This is the singleton sequencer that is used to track names. */ private static final Sequencer SEQUENCER; static { SEQUENCER = new Sequencer(); } /** * This will create a thread name that is unique. The thread name * is a combination of the provided name and a sequence number * which is appended to the end of the name. This ensures that * each thread within the system has a unique name. * * @param name this is the prefix for the thread name produced * * @return this will return the name of the thread produced */ public static String getName(String name) { int count = SEQUENCER.next(name); if(name == null) { return null; } return String.format("%s-%s", name, count); } /** * The Sequencer is used to create sequence numbers * for the threads that are to be named. This basically uses a * hash map of strings to atomic integers. When a name is used * the integer is incremented and returned. * * @author Niall Gallagher */ private static class Sequencer { /** * This is the map of atomic integers that are referenced. */ private final KeyMap map; /** * Constructor for the Sequencer object. This is * used to keep track of the sequence numbers used for the * threads in the system, so that they are all unique. */ public Sequencer() { this.map = new KeyMap(); } /** * This is used to get the next sequence number for the name * provided. This allows the thread namer to construct a * unique sequence number for threads with the same name. * * @param name this is the name of the thread to sequence * * @return this is the sequence number that has been retrieved */ public synchronized int next(String name) { AtomicInteger count = map.get(name); if(count == null) { count = new AtomicInteger(); map.put(name, count); } return count.getAndIncrement(); } } } simple-http-4.1.21/src/org/simpleframework/util/lease/0000755000175000017500000000000011767603362023365 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/util/lease/ContractQueue.java0000644000175000017500000000273011417313373027004 0ustar jamespagejamespage/* * ContractQueue.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import java.util.concurrent.DelayQueue; /** * The ContraceQueue object is used to queue contracts * between two asynchronous threads of execution. This allows the * controller to schedule the lease contract for expiry. Taking the * contracts from the queue is delayed for the contract duration. * * @author Niall Gallagher * * @see org.simpleframework.util.lease.Contract */ class ContractQueue extends DelayQueue> { /** * Constructor for the ContractQueue object. This * is used to create a queue for passing contracts between two * asynchronous threads of execution. This is used by the * lease controller to schedule the lease contract for expiry. */ public ContractQueue() { super(); } } simple-http-4.1.21/src/org/simpleframework/util/lease/Entry.java0000644000175000017500000001276511417313373025334 0ustar jamespagejamespage/* * Entry.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import static java.util.concurrent.TimeUnit.NANOSECONDS; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; /** * A Entry is used to represent the contract a lease * has been issued. This contains all relevant information for the * the lease, such as the keyed resource that has been leased and * the duration of the lease. Durations for the contract can be * measured in any TimeUnit for convenience. * * @author Niall Gallagher */ class Entry implements Contract { /** * This is the time that this entry expires in nanoseconds. */ private volatile long time; /** * This is the key representing the resource being lease. */ private T key; /** * Constructor for the Entry object. This is used * to create a contract with an initial expiry period. Once this * is created the time is taken and the contract can be issued. * * @param key this is the key that this contract represents * @param lease this is the initial lease duration to be used * @param scale this is the time unit scale that is to be used */ public Entry(T key, long lease, TimeUnit scale) { this.time = getTime() + scale.toNanos(lease); this.key = key; } /** * This returns the key for the resource this represents. * This is used when the contract has expired to clean resources * associated with the lease. It is passed in to the cleaner as * an parameter to the callback. The cleaner is then responsible * for cleaning any resources associated with the lease. * * @return returns the resource key that this represents */ public T getKey() { return key; } /** * This method will return the number of TimeUnit * seconds that remain in the contract. If the value returned is * less than or equal to zero then it should be assumed that the * lease has expired, if greater than zero the lease is active. * * @return returns the duration in the time unit remaining */ public long getDelay(TimeUnit unit) { return unit.convert(time - getTime(), NANOSECONDS); } /** * This method is used to set the number of TimeUnit * seconds that should remain within the contract. This is used * when the contract is to be reissued. Once a new duration has * been set the contract for the lease has been changed and the * previous expiry time is ignores, so only one clean is called. * * @param delay this is the delay to be used for this contract * @param unit this is the time unit measurment for the delay */ public void setDelay(long delay, TimeUnit unit) { this.time = getTime() + unit.toNanos(delay); } /** * This method returns the current time in nanoseconds. This is * used to allow the duration of the lease to be calculated with * any given time unit which allows flexibility in setting and * getting the current delay for the contract. * * @return returns the current time in nanoseconds remaining */ private long getTime() { return System.nanoTime(); } /** * This is used to compare the specified delay to this delay. The * result of this operation is used to prioritize contracts in * order of first to expire. Contracts that expire first reach * the top of the contract queue and are taken off for cleaning. * * @param other this is the delay to be compared with this * * @return this returns zero if equal otherwise the difference */ public int compareTo(Delayed other) { Entry entry = (Entry) other; if(other == this) { return 0; } return compareTo(entry); } /** * This is used to compare the specified delay to this delay. The * result of this operation is used to prioritize contracts in * order of first to expire. Contracts that expire first reach * the top of the contract queue and are taken off for cleaning. * * @param entry this is the entry to be compared with this * * @return this returns zero if equal otherwise the difference */ private int compareTo(Entry entry) { long diff = time - entry.time; if(diff < 0) { return -1; } else if(diff > 0) { return 1; } return 0; } /** * This is used to provide a description of the contract that the * instance represents. A description well contain the key owned * by the contract as well as the expiry time expected for it. * This is used to provide descriptive messages in the exceptions. * * @return a descriptive message describing the contract object */ public String toString() { return String.format("contract %s", key); } } simple-http-4.1.21/src/org/simpleframework/util/lease/LeaseException.java0000644000175000017500000000267011417313373027135 0ustar jamespagejamespage/* * LeaseException.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import org.simpleframework.util.FormatException; /** * The LeaseException is used to indicate that some * operation failed when using the lease after the lease duration * has expired. Typically this will be thrown when the lease is * renewed after the expiry period has passed. * * @author Niall Gallagher */ public class LeaseException extends FormatException { /** * This constructor is used if there is a description of the * event that caused the exception required. This can be given * a message used to describe the situation for the exception. * * @param message this is a description of the exception */ public LeaseException(String message, Object... list) { super(message, list); } } simple-http-4.1.21/src/org/simpleframework/util/lease/Controller.java0000644000175000017500000000632311417313373026347 0ustar jamespagejamespage/* * Controller.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import org.simpleframework.util.lease.LeaseException; /** * The Controller forms the interface to the lease * management system. There are two actions permitted for leased * resources, these are lease issue and lease renewal. When the * lease is first issued it is scheduled for the initial contract * duration. Once issued the lease can be renewed with another * duration, which can be less than the previous duration used. * * @author Niall Gallagher * * @see org.simpleframework.util.lease.Maintainer */ interface Controller { /** * This method will establish a contract for the given duration. * If the contract duration expires before it is renewed then a * notification is sent, typically to a Cleaner to * to signify that the resource should be released. The contract * can also be cancelled by providing a zero length duration. * * @param contract a contract representing a leased resource * * @exception Exception if the lease could not be done */ public void issue(Contract contract) throws LeaseException; /** * This ensures that the contract is renewed for the duration on * the contract, which may have changed since it was issued or * last renewed. If the duration on the contract has changed this * will insure the previous contract duration is revoked and the * new duration is used to maintain the leased resource. * * @param contract a contract representing a leased resource * * @exception Exception if the lease could not be done */ public void renew(Contract contract) throws LeaseException; /** * This will cancel the lease and release the resource. This * has the same effect as the renew method with * a zero length duration. Once this has been called the * Cleaner used should be notified immediately. * If the lease has already expired this throws an exception. * * @param contract a contract representing a leased resource * * @exception Exception if the expiry has been passed */ public void cancel(Contract contract) throws LeaseException; /** * This method is used to cancel all outstanding leases and to * close the controller. Closing the controller ensures that it * can no longer be used to issue or renew leases. All resources * occupied by the controller are released, including threads, * memory, and all leased resources occupied by the instance. */ public void close(); } simple-http-4.1.21/src/org/simpleframework/util/lease/Contract.java0000644000175000017500000000560711417313373026005 0ustar jamespagejamespage/* * Contract.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; /** * A Contract is used to represent the contract a * lease has been issued. This contains all relevant information * regarding the lease, such as the keyed resource that has been * leased and the duration of the lease. Delays for the contract * can be measured in any TimeUnit for convinienct. * * @author Niall Gallagher */ interface Contract extends Delayed { /** * This returns the key for the resource this represents. * This is used when the contract has expired to clean resources * associated with the lease. It is passed in to the cleaner as * an parameter to the callback. The cleaner is then responsible * for cleaning any resources associated with the lease. * * @return returns the resource key that this represents */ public T getKey(); /** * This method will return the number of TimeUnit * seconds that remain in the contract. If the value returned is * less than or equal to zero then it should be assumed that the * lease has expired, if greater than zero the lease is active. * * @return returns the duration in time unit remaining */ public long getDelay(TimeUnit unit); /** * This method is used to set the number of TimeUnit * seconds that should remain within the contract. This is used * when the contract is to be reissued. Once a new duration has * been set the contract for the lease has been changed and the * previous expiry time is ignores, so only one clean is called. * * @param delay this is the delay to be used for this contract * @param unit this is the time unit measurment for the delay */ public void setDelay(long delay, TimeUnit unit); /** * This is used to provide a description of the contract that the * instance represents. A description well contain the key owned * by the contract as well as the expiry time expected for it. * This is used to provide descriptive messages in the exceptions. * * @return a descriptive message describing the contract object */ public String toString(); } simple-http-4.1.21/src/org/simpleframework/util/lease/ContractLease.java0000644000175000017500000001030111417313373026742 0ustar jamespagejamespage/* * ContractLease.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import java.util.concurrent.TimeUnit; /** * The ContractLease is used to maintain contracts by * using a controller object. This will invoke the controller with * the contract when a lease operation is performed. A lease is * renewed by changing the contract duration and passing that to * the controller which will reestablish the expiry time for it. * * @author Niall Gallagher */ class ContractLease implements Lease { /** * This is the controller object used to handle contracts. */ private final Controller handler; /** * This is the contract object representing the lease. */ private final Contract contract; /** * Constructor for the ContractLease object. This is * used to create a lease which will maintain a contract using a * controller object. Lease renewals are performed by changing the * expiry duration on the contract and notifying the controller. * * @param handler this is used to manage the contract expiration * @param contract this is the contract representing the lease */ public ContractLease(Controller handler, Contract contract) { this.handler = handler; this.contract = contract; } /** * Determines the duration remaining before the lease expires. * The expiry is given as the number of TimeUnit * seconds remaining before the lease expires. If this value is * negative it should be assumed that the lease has expired. * * @param unit this is the time unit used for the duration * * @return the duration remaining within this lease instance * * @exception LeaseException if the lease expiry has passed */ public long getExpiry(TimeUnit unit) throws LeaseException { return contract.getDelay(unit); } /** * This ensures that the leased resource is maintained for the * specified number of TimeUnit seconds. Allowing * the duration unit to be specified enables the lease system * to maintain a resource with a high degree of accuracy. The * accuracy of the leasing system is dependant on how long it * takes to clean the resource associated with the lease. * * @param duration this is the length of time to renew for * @param unit this is the time unit used for the duration * * @exception LeaseException if the expiry has been passed */ public void renew(long duration, TimeUnit unit) throws LeaseException { if(duration >= 0) { contract.setDelay(duration, unit); } handler.renew(contract); } /** * This will cancel the lease and release the resource. This * has the same effect as the renew method with * a zero length duration. Once this has been called the * Cleaner used should be notified immediately. * If the lease has already expired this throws an exception. * * @exception LeaseException if the expiry has been passed */ public void cancel() throws LeaseException { handler.cancel(contract); } /** * Provides the key for the resource that this lease represents. * This can be used to identify the resource should the need * arise. Also, this provides a convenient means of identifying * leases when using or storing it as an Object. * * @return this returns the key for the resource represented */ public T getKey() { return contract.getKey(); } } simple-http-4.1.21/src/org/simpleframework/util/lease/LeaseMap.java0000644000175000017500000000553111417313373025713 0ustar jamespagejamespage/* * LeaseMap.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import java.util.concurrent.ConcurrentHashMap; /** * The LeaseMap object is used to map lease keys to the * lease objects managing those objects. This allows components that * are using the leasing framework to associate an object with its * lease and vice versa. Such a capability enables lease renewals to * be performed without the need for a direct handle on the lease. * * @author Niall Gallagher */ public class LeaseMap extends ConcurrentHashMap> { /** * Constructor for the LeaseMap object. This will * create a map for mapping leased resource keys to the leases * that manage them. Having such a map allows leases to be * maintained without having a direct handle on the lease. */ public LeaseMap() { super(); } /** * Constructor for the LeaseMap object. This will * create a map for mapping leased resource keys to the leases * that manage them. Having such a map allows leases to be * maintained without having a direct handle on the lease. * * @param capacity this is the initial capacity of the map */ public LeaseMap(int capacity) { super(capacity); } /** * This is used to acquire the Lease object that is * mapped to the specified key. Overriding this method ensures * that even without generic parameters a type safe method for * acquiring the registered lease objects can be used. * * @param key this is the key used to acquire the lease object * * @return this is the lease that is associated with the key */ public Lease get(Object key) { return super.get(key); } /** * This is used to remove the Lease object that is * mapped to the specified key. Overriding this method ensures * that even without generic parameters a type safe method for * removing the registered lease objects can be used. * * @param key this is the key used to remove the lease object * * @return this is the lease that is associated with the key */ public Lease remove(Object key) { return super.remove(key); } } simple-http-4.1.21/src/org/simpleframework/util/lease/Maintainer.java0000644000175000017500000001057411417313373026316 0ustar jamespagejamespage/* * Maintainer.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import static java.util.concurrent.TimeUnit.MILLISECONDS; /** * The Maintainer is used provide an implementation of * a controller. This simple delegates to the cleaner queue when a * renewal is required. Renewals are performed by revoking the * contract and then reissuing it. This will ensure that the delay * for expiry of the contract is reestablished within the queue. * * @author Niall Gallagher * * @see org.simpleframework.util.lease.LeaseCleaner */ class Maintainer implements Controller { /** * The queue that is used to issue and revoke contracts. */ private LeaseCleaner queue; /** * Constructor for the Maintainer object. This is * used to create a controller for contracts which will ensure * that the lease expiry durations are met. All notifications of * expiry will be delivered to the provided cleaner instance. * * @param cleaner this is used to receive expiry notifications */ public Maintainer(Cleaner cleaner) { this.queue = new LeaseCleaner(cleaner); } /** * This method will establish a contract for the given duration. * If the contract duration expires before it is renewed then a * notification is sent, typically to a Cleaner to * to signify that the resource should be released. The contract * can also be cancelled by providing a zero length duration. * * @param contract a contract representing a leased resource * * @exception LeaseException if the lease could not be done */ public synchronized void issue(Contract contract) throws LeaseException { queue.issue(contract); } /** * This ensures that the contract is renewed for the duration on * the contract, which may have changed since it was issued or * last renewed. If the duration on the contract has changed this * will insure the previous contract duration is revoked and the * new duration is used to maintain the leased resource. * * @param contract a contract representing a leased resource * * @exception LeaseException if the lease could not be done */ public synchronized void renew(Contract contract) throws LeaseException { boolean active = queue.revoke(contract); if(!active) { throw new LeaseException("Lease has expired for %s", contract); } queue.issue(contract); } /** * This will cancel the lease and release the resource. This * has the same effect as the renew method with * a zero length duration. Once this has been called the * Cleaner used should be notified immediately. * If the lease has already expired this throws an exception. * * @param contract a contract representing a leased resource * * @exception Exception if the expiry has been passed */ public synchronized void cancel(Contract contract) throws LeaseException { boolean active = queue.revoke(contract); if(!active) { throw new LeaseException("Lease has expired for %s", contract); } contract.setDelay(0, MILLISECONDS); queue.issue(contract); } /** * This method is used to cancel all outstanding leases and to * close the controller. Closing the controller ensures that it * can no longer be used to issue or renew leases. All resources * occupied by the controller are released, including threads, * memory, and all leased resources occupied by the instance. * * @throws LeaseException if the controller can not be closed */ public synchronized void close() { queue.close(); } } simple-http-4.1.21/src/org/simpleframework/util/lease/LeaseCleaner.java0000644000175000017500000001240011417313373026540 0ustar jamespagejamespage/* * LeaseCleaner.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import static java.util.concurrent.TimeUnit.NANOSECONDS; import org.simpleframework.util.thread.Daemon; /** * The LeaseCleaner provides a means of providing * callbacks to clean a leased resource once the contract duration * has expired. This will acquire contracts from the queue and * invoke the Cleaner notification method. This will * wait until the current clean operation has completed before it * attempts to clean the next contract. * * @author Niall Gallagher */ class LeaseCleaner extends Daemon { /** * This is used to queue contracts that are to be cleaned. */ private ContractQueue queue; /** * This is the cleaner that is invoked to clean contracts. */ private Cleaner cleaner; /** * This is used to determine if the invoker should stop. */ private volatile boolean dead; /** * Constructor for the LeaseCleaner object. This * can be used to issue, update, and expire leases. When a lease * expires notification is sent to the Cleaner * object provided. This allows an implementation independent * means to clean up once a specific lease has expired. * * @param cleaner this will receive expiration notifications */ public LeaseCleaner(Cleaner cleaner) { this.queue = new ContractQueue(); this.cleaner = cleaner; this.start(); } /** * This revokes a contract that has previously been issued. This * is used when the contract duration has changed so that it can * be reissued again with a new duration. This returns true if * the contract was still active and false if it did not exist. * * @param contract this is the contract that contains details */ public boolean revoke(Contract contract) throws LeaseException { if(dead) { throw new LeaseException("Lease can not be revoked"); } return queue.remove(contract); } /** * This method will establish a contract for a given resource. * If the contract duration expires before it is renewed then * a notification is sent, to the issued Cleaner * implementation, to signify that the resource has expired. * * @param contract this is the contract that contains details */ public boolean issue(Contract contract) throws LeaseException { if(dead) { throw new LeaseException("Lease can not be issued"); } return queue.offer(contract); } /** * This acquires expired lease contracts from the queue once the * expiry duration has passed. This will deliver notification to * the Cleaner object once the contract has been * taken from the queue. This allows the cleaner to clean up any * resources associated with the lease before the next expiration. */ public void run() { while(!dead) { try { clean(); } catch(Throwable e) { continue; } } purge(); } /** * This method is used to take the lease from the queue and give * it to the cleaner for expiry. This effectively waits until the * next contract expiry has passed, once it has passed the key * for that contract is given to the cleaner to clean up resources. */ private void clean() throws Exception { Contract next = queue.take(); T key = next.getKey(); if(key != null) { cleaner.clean(key); } } /** * Here all of the existing contracts are purged when the invoker * is closed. This ensures that each leased resource has a chance * to clean up after the lease manager has been closed. All of the * contracts are given a zero delay and cleaned immediately such * that once this method has finished the queue will be empty. */ private void purge() { for(Contract next : queue) { T key = next.getKey(); try { next.setDelay(0L, NANOSECONDS); cleaner.clean(key); } catch(Throwable e) { continue; } } } /** * Here we shutdown the lease maintainer so that the thread will * die. Shutting down the maintainer is done by interrupting the * thread and setting the dead flag to true. Once this is invoked * then the thread will no longer be running for this object. */ public void close() { dead = true; interrupt(); } } simple-http-4.1.21/src/org/simpleframework/util/lease/Cleaner.java0000644000175000017500000000306311417313373025573 0ustar jamespagejamespage/* * Cleaner.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; /** * The Cleaner represents an object that is used to * clean up after the keyed resource. Typically this is used when * a Lease referring a resource has expired meaning * that any memory, file descriptors, or other such limited data * should be released for the keyed resource. The resource keys * used should be distinct over time to avoid conflicts. * * @author Niall Gallagher * * @see org.simpleframework.util.lease.Lease */ public interface Cleaner { /** * This method is used to clean up after a the keyed resource. * To ensure that the leasing infrastructure operates properly * this should not block releasing resources. If required this * should spawn a thread to perform time consuming tasks. * * @param key this is the key for the resource to clean */ public void clean(T key) throws Exception; } simple-http-4.1.21/src/org/simpleframework/util/lease/LeaseManager.java0000644000175000017500000000644311417313373026553 0ustar jamespagejamespage/* * LeaseManager.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import java.util.concurrent.TimeUnit; /** * The LeaseManager is used to issue a lease for a * named resource. This is effectively used to issue a request * for a keyed resource to be released when a lease has expired. * The use of a Lease simplifies the interface to * the notification and also enables other objects to manage the * lease without any knowledge of the resource it represents. * * @author Niall Gallagher */ public class LeaseManager implements LeaseProvider { /** * This is the controller used to handle lease operations. */ private Controller handler; /** * Constructor for the LeaseManager object. This * instance is created using a specified notification handler. * The specified Cleaner will be notified when * the lease for a named resource expires, which will allow * the cleaner object to perform a clean up for that resource. * * @param cleaner the cleaner object receiving notifications */ public LeaseManager(Cleaner cleaner) { this.handler = new Maintainer(cleaner); } /** * This method will issue a Lease object that * can be used to manage the release of a keyed resource. If * the lease duration expires before it is renewed then the * notification is sent, typically to a Cleaner * implementation, to signify that the resource should be * recovered. The issued lease can also be canceled. * * @param key this is the key for the leased resource * @param duration this is the duration of the issued lease * @param unit this is the time unit to issue the lease with * * @return a lease that can be used to manage notification */ public Lease lease(T key, long duration, TimeUnit unit) { Contract contract = new Entry(key, duration, unit); try { handler.issue(contract); } catch(Exception e) { return null; } return new ContractLease(handler, contract); } /** * This is used to close the lease provider such that all of * the outstanding leases are canceled. This also ensures the * provider can no longer be used to issue new leases, such * that further invocations of the lease method * will result in null leases. Once the provider has been * closes all threads and other such resources are released. */ public void close() { try { handler.close(); } catch(Exception e) { return; } } } simple-http-4.1.21/src/org/simpleframework/util/lease/Lease.java0000644000175000017500000000627511417313373025263 0ustar jamespagejamespage/* * Lease.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import java.util.concurrent.TimeUnit; /** * The Lease object is used to keep a keyed resource * active. This provides a very simple lease that can be used to * track the activity of a resource or system. Keeping track of * activity allows resources to be maintained until such time * that they are no longer required, allowing the server to clean * up any allocated memory, file descriptors, or other such data. * * @author Niall Gallagher */ public interface Lease { /** * Determines the duration remaining before the lease expires. * The expiry is given as the number of TimeUnit * seconds remaining before the lease expires. If this value is * negative it should be assumed that the lease has expired. * * @param unit this is the time unit used for the duration * * @return the duration remaining within this lease instance * * @exception Exception if the expiry could not be acquired */ public long getExpiry(TimeUnit unit) throws LeaseException; /** * This ensures that the leased resource is maintained for the * specified number of TimeUnit seconds. Allowing * the duration unit to be specified enables the lease system * to maintain a resource with a high degree of accuracy. The * accuracy of the leasing system is dependent on how long it * takes to clean the resource associated with the lease. * * @param duration this is the length of time to renew for * @param unit this is the time unit used for the duration * * @exception Exception if the lease could not be renewed */ public void renew(long duration, TimeUnit unit) throws LeaseException; /** * This will cancel the lease and release the resource. This * has the same effect as the renew method with * a zero length duration. Once this has been called the * Cleaner used should be notified immediately. * If the lease has already expired this throws an exception. * * @exception Exception if the expiry has been passed */ public void cancel() throws LeaseException; /** * Provides the key for the resource that this lease represents. * This can be used to identify the resource should the need * arise. Also, this provides a convenient means of identifying * leases when using or storing it as an Object. * * @return this returns the key for the resource represented */ public T getKey(); } simple-http-4.1.21/src/org/simpleframework/util/lease/LeaseProvider.java0000644000175000017500000000444111417313373026767 0ustar jamespagejamespage/* * LeaseProvider.java May 2004 * * Copyright (C) 2004, Niall Gallagher * * 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.util.lease; import java.util.concurrent.TimeUnit; /** * The LeaseProvider is used to issue a lease for a * named resource. This is effectively used to issue a request * for a keyed resource to be released when a lease has expired. * The use of a Lease simplifies the interface to * the notification and also enables other objects to manage the * lease without any knowledge of the resource it represents. * * @author Niall Gallagher */ public interface LeaseProvider { /** * This method will issue a Lease object that * can be used to manage the release of a keyed resource. If * the lease duration expires before it is renewed then the * notification is sent, typically to a Cleaner * implementation, to signify that the resource should be * recovered. The issued lease can also be canceled. * * @param key this is the key for the leased resource * @param duration this is the duration of the issued lease * @param unit this is the time unit to issue the lease with * * @return a lease that can be used to manage notification */ public Lease lease(T key, long duration, TimeUnit unit); /** * This is used to close the lease provider such that all of * the outstanding leases are canceled. This also ensures the * provider can no longer be used to issue new leases, such * that further invocations of the lease method * will result in null leases. Once the provider has been * closes all threads and other such resources are released. */ public void close(); } simple-http-4.1.21/src/org/simpleframework/util/parse/0000755000175000017500000000000011767603362023406 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/util/parse/MapParser.java0000644000175000017500000002045111417313373026135 0ustar jamespagejamespage/* * MapParser.java February 2005 * * Copyright (C) 2005, Niall Gallagher * * 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.util.parse; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * The MapParser object represents a parser for name * value pairs. Any parser extending this will typically be parsing * name=value tokens or the like, and inserting these pairs into * the internal map. This type of parser is useful as it exposes all * pairs extracted using the java.util.Map interface * and as such can be used with the Java collections framework. The * internal map used by this is a Hashtable, however * subclasses are free to assign a different type to the map used. * * @author Niall Gallagher */ public abstract class MapParser extends Parser implements Map { /** * Represents all values inserted to the map as a list of values. */ protected Map> all; /** * Represents the last value inserted into this map instance. */ protected Map map; /** * Constructor for the MapParser object. This is * used to create a new parser that makes use of a thread safe * map implementation. The HashMap is used so * that the resulting parser can be accessed in a concurrent * environment with the fear of data corruption. */ protected MapParser(){ this.all = new HashMap>(); this.map = new HashMap(); } /** * This is used to determine whether a token representing the * name of a pair has been inserted into the internal map. The * object passed into this method should be a string, as all * tokens stored within the map will be stored as strings. * * @param name this is the name of a pair within the map * * @return this returns true if the pair of that name exists */ public boolean containsKey(Object name) { return map.containsKey(name); } /** * This method is used to determine whether any pair that has * been inserted into the internal map had the presented value. * If one or more pairs within the collected tokens contains * the value provided then this method will return true. * * @param value this is the value that is to be searched for * * @return this returns true if any value is equal to this */ public boolean containsValue(Object value) { return map.containsValue(value); } /** * This method is used to acquire the name and value pairs that * have currently been collected by this parser. This is used * to determine which tokens have been extracted from the * source. It is useful when the tokens have to be gathered. * * @return this set of token pairs that have been extracted */ public Set> entrySet() { return map.entrySet(); } /** * The get method is used to acquire the value for * a named pair. So if a pair of name=value has been parsed and * inserted into the collection of tokens this will return the * value given the name. The value returned will be a string. * * @param name this is a string used to search for the value * * @return this is the value, as a string, that has been found */ public T get(Object name) { return map.get(name); } /** * This method is used to acquire a List for all of * the values that have been put in to the map. The list allows * all values associated with the specified key. This enables a * parser to collect a number of associated tokens. * * @param key this is the key used to search for the value * * @return this is the list of values associated with the key */ public List getAll(Object key) { return all.get(key); } /** * This method is used to determine whether the parser has any * tokens available. If the size is zero then the * parser is empty and this returns true. The is acts as a * proxy the the isEmpty of the internal map. * * @return this is true if there are no available tokens */ public boolean isEmpty() { return map.isEmpty(); } /** * This is used to acquire the names for all the tokens that * have currently been collected by this parser. This is used * to determine which tokens have been extracted from the * source. It is useful when the tokens have to be gathered. * * @return the set of name tokens that have been extracted */ public Set keySet() { return map.keySet(); } /** * The put method is used to insert the name and * value provided into the collection of tokens. Although it is * up to the parser to decide what values will be inserted it * is generally the case that the inserted tokens will be text. * * @param name this is the name token from a name=value pair * @param value this is the value token from a name=value pair * * @return this returns the previous value if there was any */ public T put(T name, T value) { List list = all.get(name); T first = map.get(name); if(list == null) { list = new ArrayList(); all.put(name, list); } list.add(value); if(first == null) { return map.put(name, value); } return null; } /** * 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 data this is the collection of tokens to be added */ public void putAll(Map data) { Set keySet = data.keySet(); for(T key : keySet) { T value = data.get(key); if(value != null) { put(key, value); } } } /** * The remove method is used to remove the named * token pair from the collection of tokens. This acts like a * take, in that it will get the token value and remove if * from the collection of tokens the parser has stored. * * @param name this is a string used to search for the value * * @return this is the value, as a string, that is removed */ public T remove(Object name) { return map.remove(name); } /** * This obviously enough provides the number of tokens that * have been inserted into the internal map. This acts as * a proxy method for the internal map size. * * @return this returns the number of tokens are available */ public int size() { return map.size(); } /** * This method is used to acquire the value for all tokens that * have currently been collected by this parser. This is used * to determine which tokens have been extracted from the * source. It is useful when the tokens have to be gathered. * * @return the list of value tokens that have been extracted */ public Collection values() { return map.values(); } /** * The clear method is used to wipe out all the * currently existing tokens from the collection. This is used * to recycle the parser so that it can be used to parse some * other source of tokens without any lingering state. */ public void clear() { all.clear(); map.clear(); } } simple-http-4.1.21/src/org/simpleframework/util/parse/Parser.java0000644000175000017500000001462611417313373025506 0ustar jamespagejamespage/* * Parser.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.parse; /** * This Parser object is to be used as a simple template * for parsing uncomplicated expressions. This object is used to parse * a String. This provides a few methods that can be used * to store and track the reading of data from a buffer. There are two * abstract methods provided to allow this to be subclassed to create * a Parser for a given String. * * @author Niall Gallagher */ public abstract class Parser { /** * This is the buffer that is being parsed. */ protected char[] buf; /** * This represents the current read offset. */ protected int off; /** * This represents the length of the buffer. */ protected int count; /** * This is a no argument constructor for the Parser. * This will be invoked by each subclass of this object. It will * set the buffer to a zero length buffer so that when the * ensureCapacity method is used the buf's * length can be checked. */ protected Parser(){ this.buf = new char[0]; } /** * This is used to parse the String given to it. This * will ensure that the char buffer has enough space * to contain the characters from the String. This * will firstly ensure that the buffer is resized if nessecary. The * second step in this parse method is to initialize * the Parser object so that multiple parse invokations * can be made. The init method will reset this to an * prepared state. Then finally the parse method is * called to parse the char buffer. * * @param text the String to be parsed with this * Parser */ public void parse(String text){ if(text != null){ ensureCapacity(text.length()); count = text.length(); text.getChars(0, count, buf,0); init(); parse(); } } /** * This ensure that there is enough space in the buffer to allow * for more char's to be added. If the buffer is * already larger than min then the buffer will not be expanded * at all. * * @param min the minimum size needed to accomodate the characters */ protected void ensureCapacity(int min) { if(buf.length < min) { int size = buf.length * 2; int max = Math.max(min, size); char[] temp = new char[max]; buf = temp; } } /** * This is used to determine if a given ISO-8859-1 character is * a space character. That is a whitespace character this sees * the, space, carrage return and line feed characters as * whitespace characters. * * @param c the character that is being determined by this * * @return true if the character given it is a space character */ protected boolean space(char c) { switch(c){ case ' ': case '\t': case '\n': case '\r': return true; default: return false; } } /** * This is used to determine wheather or not a given character is * a digit character. It assumes iso-8859-1 encoding to compare. * * @param c the character being determined by this method * * @return true if the character given is a digit character */ protected boolean digit(char c){ return c <= '9' && '0' <= c; } /** * This takes a unicode character and assumes an encoding of * ISO-8859-1. This then checks to see if the given character * is uppercase if it is it converts it into is ISO-8859-1 * lowercase char. * * @param c the char to be converted to lowercase * * @return the lowercase ISO-8859-1 of the given character */ protected char toLower(char c) { if(c >= 'A' && c <= 'Z') { return (char)((c - 'A') + 'a'); } return c; } /** This is used to skip an arbitrary String within the * char buf. It checks the length of the String * first to ensure that it will not go out of bounds. A comparison * is then made with the buffers contents and the String * if the reigon in the buffer matched the String then the * offset within the buffer is increased by the String's * length so that it has effectively skipped it. * * @param text this is the String value to be skipped * * @return true if the String was skipped */ protected boolean skip(String text){ int size = text.length(); int read = 0; if(off + size > count){ return false; } while(read < size){ char a = text.charAt(read); char b = buf[off + read++]; if(toLower(a) != toLower(b)){ return false; } } off += size; return true; } /** * This will initialize the Parser when it is ready * to parse a new String. This will reset the * Parser to a ready state. The init * method is invoked by the Parser when the * parse method is invoked. */ protected abstract void init(); /** * This is the method that should be implemented to read * the buf. This method should attempt to extract tokens * from the buffer so that thes tokens may some how be * used to determine the semantics. This method is invoked * after the init method is invoked. */ protected abstract void parse(); } simple-http-4.1.21/src/org/simpleframework/util/parse/ParseBuffer.java0000644000175000017500000002042411417313373026447 0ustar jamespagejamespage/* * ParseBuffer.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.util.parse; /** * This is primarily used to replace the StringBuffer * class, as a way for the Parser to store the char's * for a specific region within the parse data that constitutes a * desired value. The methods are not synchronized so it enables * the char's to be taken quicker than the * StringBuffer class. * * @author Niall Gallagher */ public class ParseBuffer { /** * This is used to quicken toString. */ protected String cache; /** * The char's this buffer accumulated. */ protected char[] buf; /** * This is the number of char's stored. */ protected int count; /** * Constructor for ParseBuffer. The default * ParseBuffer stores 16 char's * before a resize is needed to accommodate * extra characters. */ public ParseBuffer(){ this(16); } /** * This creates a ParseBuffer with a specific * default size. The buffer will be created the with the * length specified. The ParseBuffer can grow * to accommodate a collection of char's larger * the the size specified. * * @param size initial size of this ParseBuffer */ public ParseBuffer(int size){ this.buf = new char[size]; } /** * This will add a char to the end of the buffer. * The buffer will not overflow with repeated uses of the * append, it uses an ensureCapacity * method which will allow the buffer to dynamically grow in * size to accommodate more char's. * * @param c the char to be appended */ public void append(char c){ ensureCapacity(count+ 1); buf[count++] = c; } /** * This will add a String to the end of the buffer. * The buffer will not overflow with repeated uses of the * append, it uses an ensureCapacity * method which will allow the buffer to dynamically grow in * size to accommodate large String objects. * * @param text the String to be appended to this */ public void append(String text){ ensureCapacity(count+ text.length()); text.getChars(0,text.length(),buf,count); count += text.length(); } /** * This will reset the buffer in such a way that the buffer is * cleared of all contents and then has the given string appended. * This is used when a value is to be set into the buffer value. * See the append(String) method for reference. * * @param text this is the text that is to be appended to this */ public void reset(String text) { clear(); append(text); } /** * This will add a ParseBuffer to the end of this. * The buffer will not overflow with repeated uses of the * append, it uses an ensureCapacity * method which will allow the buffer to dynamically grow in * size to accommodate large ParseBuffer objects. * * @param text the ParseBuffer to be appended */ public void append(ParseBuffer text){ append(text.buf, 0, text.count); } /** * This will reset the buffer in such a way that the buffer is * cleared of all contents and then has the given string appended. * This is used when a value is to be set into the buffer value. * See the append(ParseBuffer) method for reference. * * @param text this is the text that is to be appended to this */ public void reset(ParseBuffer text) { clear(); append(text); } /** * This will add a char to the end of the buffer. * The buffer will not overflow with repeated uses of the * append, it uses an ensureCapacity * method which will allow the buffer to dynamically grow in * size to accommodate large char arrays. * * @param c the char array to be appended to this * @param off the read offset for the array * @param len the number of char's to add */ public void append(char[] c, int off, int len){ ensureCapacity(count+ len); System.arraycopy(c,off,buf,count,len); count+=len; } /** * This will add a String to the end of the buffer. * The buffer will not overflow with repeated uses of the * append, it uses an ensureCapacity * method which will allow the buffer to dynamically grow in * size to accommodate large String objects. * * @param str the String to be appended to this * @param off the read offset for the String * @param len the number of char's to add */ public void append(String str, int off, int len){ ensureCapacity(count+ len); str.getChars(off,len,buf,count); count += len; } /** * This will add a ParseBuffer to the end of this. * The buffer will not overflow with repeated uses of the * append, it uses an ensureCapacity * method which will allow the buffer to dynamically grow in * size to accommodate large ParseBuffer objects. * * @param text the ParseBuffer to be appended * @param off the read offset for the ParseBuffer * @param len the number of char's to add */ public void append(ParseBuffer text, int off, int len){ append(text.buf, off, len); } /** * This ensure that there is enough space in the buffer to * allow for more char's to be added. If * the buffer is already larger than min then the buffer * will not be expanded at all. * * @param min the minimum size needed */ protected void ensureCapacity(int min) { if(buf.length < min) { int size = buf.length * 2; int max = Math.max(min, size); char[] temp = new char[max]; System.arraycopy(buf, 0, temp, 0, count); buf = temp; } } /** * This will empty the ParseBuffer so that the * toString parameter will return null. * This is used so that the same ParseBuffer can be * recycled for different tokens. */ public void clear(){ cache = null; count = 0; } /** * This will return the number of bytes that have been appended * to the ParseBuffer. This will return zero after * the clear method has been invoked. * * @return the number of char's within the buffer */ public int length(){ return count; } /** * This will return the characters that have been appended to the * ParseBuffer as a String object. * If the String object has been created before then * a cached String object will be returned. This * method will return null after clear is invoked. * * @return the char's appended as a String */ public String toString(){ if(count <= 0) { return null; } if(cache != null) { return cache; } cache = new String(buf,0,count); return cache; } } simple-http-4.1.21/src/org/simpleframework/transport/0000755000175000017500000000000011767603362023353 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/transport/Server.java0000644000175000017500000000570311417313373025461 0ustar jamespagejamespage/* * Server.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The Server interface represents a handler that is * used to process Socket objects. Implementations of * this object will read HTTP requests for the provided sockets and * dispatch the requests for processing by the core protocol handler. *

* The intended use of a Server is that it be used in * conjunction with a Container object, which acts as the * primary protocol handler for a server. Typically the server will * deliver callbacks to a container with both Request and * Response objects encapsulating the transaction. *

* Core responsibilities of the server are to manage connections, * to ensure that all HTTP requests are collected, and to dispatch the * collected requests to an appropriate handler. It is also required * to manage multiplexing such that many connections can be processed * concurrently without a high latency period. * * @author Niall Gallagher */ public interface Server { /** * Used to process the Socket which is a full duplex * communication link that may contain several HTTP requests. This * will be used to read the requests from the Socket * and to pass these requests to a Container for * processing. *

* Typical usage of this method is to accept multiple sockets * objects, each representing a unique HTTP channel to the client, * and process requests from those sockets concurrently. * * @param socket this is the connected HTTP socket to process */ public void process(Socket socket) throws IOException; /** * This method is used to stop the Server such that * it will accept no more sockets. Stopping the server ensures * that all resources occupied will be released. This is required * so that all threads are stopped, and all memory is released. *

* Typically this method is called once all connections to the * server have been stopped. As a final act of shutting down the * entire server all threads must be stopped, this allows collection * of unused memory and the closing of file and socket resources. */ public void stop() throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/SegmentBuilder.java0000644000175000017500000001762311417313373027130 0ustar jamespagejamespage/* * SegmentBuilder.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import java.util.PriorityQueue; /** * The SegmentBuilder object is used to build segments * such that they represent segments of data from the the packets that * are provided to the builder. This enables packets to be compacted * into each other so that packets can be freed when they are no * longer needed. This enables the transport as a whole to perform * better as it ensures the packet pool is not exhausted when there * is sufficient space in other queued packets. Also this will copy * shared packets in to allocated space if requested, ensuring that * the writing thread does not need to block. * * @author Niall Gallagher * * @see org.simpleframework.transport.Writer */ class SegmentBuilder { /** * This is a compact queue which contains the compact packets. */ private final Queue compact; /** * This is the packet queue that is used to queue packets. */ private final Queue ready; /** * This is the maximum size a packet can be duplicated as. */ private final int limit; /** * Constructor for the SegmentBuilder object. This * is used to create a queue of packets such that each packet is * of a minimum size. To ensure packets are of a minimum size * this aggregates them by moving bytes between packets. */ public SegmentBuilder() { this(20480); } /** * Constructor for the SegmentBuilder object. This * is used to create a queue of packets such that each packet is * of a minimum size. To ensure packets are of a minimum size * this aggregates them by moving bytes between packets. * * @param limit this is the threshold for asynchronous buffers */ public SegmentBuilder(int limit) { this.compact = new Queue(); this.ready = new Queue(); this.limit = limit; } /** * This is used to determine if the builder contains any references. * If the segment builder contains any reference packets it forces * the Writer to block. Blocking is required so * that a race condition is avoided where the writing thread and * the flushing thread to not confuse each other. * * @return true if there are any referenced buffers in the builder */ public boolean isReference() { for(Packet packet : ready) { if(packet.isReference()) { return true; } } return false; } /** * This will aggregate the queued packets in to a packet that is * at least the minimum required size. If there are none in the * queue then this will return null. Also if the queued packets * are of zero length this will return null. * * @return this returns a packet from the queue of packets */ public Segment build() throws IOException { Packet packet = ready.peek(); if(packet == null) { return null; } return create(packet); } /** * This will aggregate the queued packets in to a packet that is * at least the minimum required size. If there are none in the * queue then this will return null. Also if the queued packets * are of zero length this will return null. * * @param packet this is the packet to wrap within a closer packet * * @return this returns a packet from the queue of packets */ private Segment create(Packet packet) throws IOException { int length = packet.length(); if(length <= 0) { packet.close(); ready.poll(); return build(); } return new Segment(packet, ready); } /** * This will aggregate the queued packets in to a packet that is * at least the minimum required size. If there are none in the * queue then this will return null. Also if the queued packets * are of zero length this will return null. * * @param packet this is a new packet to be added to the packet queue * * @return this returns a packet from the queue of packets */ public Segment build(Packet packet) throws IOException { boolean update = ready.offer(packet); long sequence = packet.sequence(); if(!update) { throw new PacketException("Could not add packet " + sequence); } return build(); } /** * This method is used to compact the packets within the builder * such that it duplicates any shared packets and closes them. * Duplicating and closing shared packets is done so that the * writing thread does not need to block. Duplication of shared * packets only occurs if the remaining length is less that * than the maximum duplication size specified. */ public void compact() throws IOException { Packet packet = ready.peek(); while(packet != null) { packet = ready.poll(); if(packet != null) { compact.offer(packet); } } extract(); } /** * This is used to take all packets queued in to the compact queue * and determine whether they need to be extracted in to separate * private packets. Extracting shared packets ensures that they * do not suffer from race conditions when the writing thread is * released. This increases the concurrency capability. */ private void extract() throws IOException { int count = limit; for(Packet packet : compact) { int length = packet.length(); if(length <= count) { packet = packet.extract(); count -= length; } if(packet != null) { ready.offer(packet); } } compact.clear(); } /** * This returns the total length of all packets within the queue. * This can be used to determine if any packets can be created * using the aggregate method. If the length is zero * there are no packets waiting to be aggregated. * * @return this returns the total length of all queued packets */ public int length() throws IOException{ int count = 0; for(Packet packet : ready) { count += packet.length(); } return count; } /** * This is used to close all packets within the builder. This * is done when there is an error or the client has closed the * connection from their size. This is important as it releases * any resources occupied by the queued packets. */ public void close() throws IOException { for(Packet packet : ready) { packet.close(); } ready.clear(); } /** * The Queue object is used to create a queue of * packets that represent the order the packets have been added * to the builder. This order ensures that the packets can * be reassembled on the client size as a complete resource. * * @author Niall Gallagher */ private class Queue extends PriorityQueue { /** * Constructor for the SegmentBuilder object. This * is used to create a queue to order the packets that are * to be aggregated and delivered to a client. */ public Queue() { super(); } } } simple-http-4.1.21/src/org/simpleframework/transport/ProcessorServer.java0000644000175000017500000001171711417313373027363 0ustar jamespagejamespage/* * ProcessorServer.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import org.simpleframework.transport.reactor.Operation; import org.simpleframework.util.thread.Daemon; /** * The ProcessorServer is used to convert pipelines * to transports. This simply acts as an adapter to a processor * which converts a connected pipeline to a Transport * which can be used to read and write data. Conversion of the * pipeline is performed only once per connection. * * @author Niall Gallagher */ public class ProcessorServer implements Server { /** * This is the factory used to create the required operations. */ private final OperationFactory factory; /** * This is the processor used to process transport objects. */ private final Negotiator negotiator; /** * This is used to terminate the internals of the processor. */ private final Daemon trigger; /** * Constructor for the ProcessorServer object. * The transport processor is used to process plain connections * and wrap those connections in a Transport that * can be used to send and receive data to and from. * * @param processor this is used to process transports */ public ProcessorServer(Processor processor) throws IOException { this(processor, 8); } /** * Constructor for the ProcessorServer object. * The transport processor is used to process plain connections * and wrap those connections in a Transport that * can be used to send and receive data to and from. * * @param processor this is used to process transports * @param count this is the number of threads this will use */ public ProcessorServer(Processor processor, int count) throws IOException { this(processor, count, 20480); } /** * Constructor for the ProcessorServer object. * The transport processor is used to process plain connections * and wrap those connections in a Transport that * can be used to send and receive data to and from. * * @param processor this is used to process transports * @param count this is the number of threads this will use * @param limit this is the threshold for asynchronous buffers */ public ProcessorServer(Processor processor, int count, int limit) throws IOException { this.negotiator = new SecureNegotiator(processor, count); this.factory = new OperationFactory(negotiator, limit); this.trigger = new StopTrigger(processor, negotiator); } /** * Used to process the Pipeline which is a full duplex * communication link that may contain several HTTP requests. This * will be used to read the requests from the Pipeline * and to pass these requests to a Container for * processing. *

* Typical usage of this method is to accept multiple pipeline * objects, each representing a unique HTTP channel to the client, * and process requests from those pipelines concurrently. * * @param socket this is the connected HTTP pipeline to process */ public void process(Socket socket) throws IOException { Operation task = factory.getInstance(socket); if(task != null) { negotiator.process(task); } } /** * This method is used to stop the Processor such that * it will accept no more pipelines. Stopping the processor ensures * that all resources occupied will be released. This is required * so that all threads are stopped, and all memory is released. *

* Typically this method is called once all connections to the * server have been stopped. As a final act of shutting down the * entire server all threads must be stopped, this allows collection * of unused memory and the closing of file and socket resources. *

* This is implemented to shut down the server asynchronously. It * will start a process to perform the shutdown. Asynchronous * shutdown allows a server resource executed via a HTTP request * can stop the server without any danger of killing itself or * even worse causing a deadlock. */ public void stop() throws IOException { trigger.start(); } } simple-http-4.1.21/src/org/simpleframework/transport/Negotiation.java0000644000175000017500000000450311417313373026470 0ustar jamespagejamespage/* * Negotiation.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The Negotiation interface is used to represent an * SSL negotiation. When an operation can not be completed this * will allow a task to perform asynchronous operations and resume * the negotiation when those operations can be fulfilled. * * @author Niall Gallagher */ interface Negotiation extends Notifier { /** * This is used to resume the negotiation when an operation * has completed. This will continue the decrypt and encrypt * sequence of messages required to fulfill the negotiation. */ public void resume() throws IOException; /** * This method is invoked when the negotiation is done and * the next phase of the connection is to take place. This * will typically be invoked when an SSL handshake or * termination exchange has completed successfully. */ public void commit() throws IOException; /** * This is used to send any messages the negotiation may have. * If the negotiation can not send the information during its * execution then this method will be executed when a select * operation is signaled. * * @return this returns true when the message has been sent */ public boolean send() throws IOException; /** * This is used to receive data from the client. If at any * point during the negotiation a message is required that * can not be read immediately this is used to asynchronously * read the data when a select operation is signaled. * * @return this returns true when the message has been read */ public boolean receive() throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/SocketController.java0000644000175000017500000000647111417313373027512 0ustar jamespagejamespage/* * SocketController.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import org.simpleframework.transport.reactor.Reactor; /** * The SocketController is used to represent the means * to write packets to an underlying transport. This manages all of * the selection required to determine if the socket is write ready. * If the packet to be written is to block then this will wait * until all queue packets are fully written. * * @author Niall Gallagher */ class SocketController implements Controller { /** * This is the flusher that is used to asynchronously flush. */ private final Flusher flusher; /** * This is the writer that is used to queue the packets. */ private final Writer writer; /** * Constructor for the SocketWriter object. This is * used to create a writer that can write packets to the socket * in such a way that it write either asynchronously or block * the calling thread until such time as the packets are written. * * @param socket this is the pipeline that this writes to * @param reactor this is the writer used to scheduler writes * @param limit this is the threshold for asynchronous buffers */ public SocketController(Socket socket, Reactor reactor, int limit) throws IOException { this.writer = new SocketWriter(socket, limit); this.flusher = new SocketFlusher(reactor, writer); } /** * This method is used to deliver the provided packet of bytes to * the underlying transport. This will not modify the data that * is to be written, this will simply queue the packets in the * order that they are provided. * * @param packet this is the array of bytes to send to the client */ public void write(Packet packet) throws IOException { boolean done = writer.write(packet); if(!done) { flusher.flush(); } } /** * This method is used to flush all of the queued packets to * the client. This method will block not block but will simply * flush any data to the underlying transport. Internally the * data will be queued for delivery to the connected entity. */ public void flush() throws IOException { boolean done = writer.flush(); if(!done) { flusher.flush(); } } /** * This is used to close the writer and the underlying socket. * If a close is performed on the writer then no more bytes * can be read from or written to the writer and the client * will receive a connection close on their side. */ public void close() throws IOException { flusher.close(); writer.close(); } } simple-http-4.1.21/src/org/simpleframework/transport/SocketWriter.java0000644000175000017500000001521611417313373026640 0ustar jamespagejamespage/* * SocketWriter.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.channels.SocketChannel; /** * The SocketWriter object is used to coalesce the * packets to be written in to a minimum size. Also this will queue * the packets to be written in the order they are provided to that * if the contents of the packets can not be fully written they * can be flushed in the correct order. * * @author Niall Gallagher * * @see org.simpleframework.transport.SegmentBuilder */ class SocketWriter implements Writer { /** * This is the manager used to build the segments to be written. */ private SegmentBuilder builder; /** * The socket channel that the byte contents are written to. */ private SocketChannel channel; /** * This is used to determine whether the socket is closed. */ private boolean closed; /** * Constructor for the SocketWriter object. This * is used to wrap the socket in an object that will queue and * coalesce the packets written. It ensures that the packets * that are sent are of a minimum size for performance. * * @param socket this is the pipeline instance this wraps */ public SocketWriter(Socket socket) { this(socket, 20480); } /** * Constructor for the SocketWriter object. This * is used to wrap the socket in an object that will queue and * coalesce the packets written. It ensures that the packets * that are sent are of a minimum size for performance. * * @param socket this is the pipeline instance this wraps * @param limit this is the threshold for asynchronous buffers */ public SocketWriter(Socket socket, int limit) { this.builder = new SegmentBuilder(limit); this.channel = socket.getChannel(); } /** * This provides the socket for the writer. Providing this * enables a Reactor to be used to determine when * the writer is write ready and thus when the writer can * be flushed if it contains packets that have not been written. * * @return this returns the socket associated with this */ public synchronized SocketChannel getChannel() { return channel; } /** * This is used to determine if the writer should block or not. * A writer will block only if there are shared packets still * within the write queue. When all shared packets have either * been written or duplicated then the writer does not need to * block any waiting threads and they can be released. * * @return true if any writing thread should be blocked */ public synchronized boolean isBlocking() throws IOException { return builder.isReference(); } /** * This is used to write the packets to the writer which will * be either written to the underlying socket or queued until * such time as the socket is write ready. This will return true * if the packet has been written to the underlying transport. * * @param packet this is the packet that is the be written * * @return true if the packet has been written to the transport */ public synchronized boolean write(Packet packet) throws IOException { Segment segment = builder.build(packet); if(segment == null) { return true; } return flush(); } /** * This is used to send the packets to the socket. This attempts * to write the provided packet to the underlying socket, if * all of the bytes are written the the packet is closed. This * will return the number of bytes that have been written. * * @param segment this is the packet that is the be sent * * @return the number of bytes written to the underlying socket */ private synchronized int write(Segment segment) throws IOException { int size = segment.write(channel); int left = segment.length(); if(left == 0) { segment.close(); } if(size < 0) { throw new TransportException("Socket is closed"); } return size; } /** * This is used to flush all queued packets to the underlying * socket. If all of the queued packets have been fully written * then this returns true, otherwise this will return false. * * @return true if all queued packets are flushed to the socket */ public synchronized boolean flush() throws IOException { Segment segment = builder.build(); while(segment != null) { int size = write(segment); if(size < 0) { throw new TransportException("Connection reset"); } if(size == 0) { break; } segment = builder.build(); } return complete(); } /** * This is used to determine the the writer is done writing all * of the enqueued packets. This is determined by checking if * the sum of the number of packets remaining is greater than * zero. If there are remaining packets they are compacted. * * @return this returns true if the writer is now empty */ private synchronized boolean complete() throws IOException { int count = builder.length(); if(count > 0) { builder.compact(); } return count <= 0; } /** * This is used to close the writer and the underlying socket. * If a close is performed on the writer then no more bytes * can be read from or written to the writer and the client * will receive a connection close on their side. This also * ensures that the TCP FIN ACK is sent before the actual * channel is closed. This is required for a clean shutdown. */ public synchronized void close() throws IOException { if(!closed) { closed = true; builder.close(); try{ channel.socket().shutdownOutput(); }catch(Throwable e){ } channel.close(); } } } simple-http-4.1.21/src/org/simpleframework/transport/TransportSource.java0000644000175000017500000001645511417313373027376 0ustar jamespagejamespage/* * TransportSource.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; /** * The TransportSource object represents a cursor that * can read and buffer data from an underlying transport. If the * number of bytes read from the cursor is more than required for * the HTTP request then those bytes can be pushed back in to the * cursor using the reset method. This will only allow * the last read to be reset within the cursor safely. * * @author Niall Gallagher * * @see org.simpleframework.transport.Transport */ class TransportSource implements Source { /** * This is the underlying transport to read the bytes from. */ private Transport transport; /** * This is used to store the bytes read from the transport. */ private ByteBuffer buffer; /** * This is used to determine if the transport has been closed. */ private boolean closed; /** * This represents the number of bytes that are ready to read. */ private int count; /** * Constructor for the TransportSource object. This * requires a transport to read the bytes from. By default this * will create a buffer of two kilobytes to read the input in to * which ensures several requests can be read at once. * * @param transport this is the underlying transport to use */ public TransportSource(Transport transport) { this(transport, 2048); } /** * Constructor for the TransportSource object. This * requires a transport to read the bytes from. By default this * will create a buffer of of the specified size to read the * input in to which enabled bytes to be buffered internally. * * @param transport this is the underlying transport to use * @param size this is the size of the internal buffer to use */ public TransportSource(Transport transport, int size) { this.buffer = ByteBuffer.allocate(size); this.transport = transport; } /** * Determines whether the source is still open. The source is * considered open if there are still bytes to read. If there is * still bytes buffered and the underlying transport is closed * then the source is still considered open. * * @return true if there is nothing more to be read from this */ public boolean isOpen() throws IOException { return count != -1; } /** * Determines whether the source is ready for reading. When the * source 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 { return ready() > 0; } /** * 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 { if(count <= 0) { // has the channel ended return count; } int size = Math.min(len, count); // get the minimum if(size > 0) { buffer.get(data, off, size); // get the bytes count -= size; } return Math.max(0, size); } /** * 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 source may perform a blocking read. * * @return the number of bytes that can be read without blocking */ public int ready() throws IOException { if(count < 0) { return count; } if(count > 0) { // if the are ready bytes don't read return count; } return peek(); } /** * 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 source may perform a blocking read. * * @return the number of bytes that can be read without blocking */ private int peek() throws IOException { if(count <= 0) { // reset the buffer for filling buffer.clear(); } if(count > 0) { buffer.compact(); // compact the buffer } count += transport.read(buffer); // how many were read if(count > 0) { buffer.flip(); // if there is something then flip } if(count < 0) { // close when stream is fully read close(); } return count; } /** * Moves the source 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 size this is the number of bytes to reset back * * @return this is the number of bytes that have been reset */ public int reset(int size) throws IOException { int mark = buffer.position(); if(size > mark) { size = mark; } if(mark > 0) { buffer.position(mark - size); count += size; } return size; } /** * This is used to close the underlying transport. This is used * when the transport returns a negative value, indicating that * the client has closed the connection on the other side. If * this is invoked the read method returns -1 and the cursor * is not longer open, further bytes can no longer be read. */ public void close() throws IOException { if(!closed) { transport.close(); closed = true; count = -1; } } } simple-http-4.1.21/src/org/simpleframework/transport/Wrapper.java0000644000175000017500000003264611417313373025641 0ustar jamespagejamespage/* * Wrapper.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.ByteChannel; import java.nio.charset.Charset; /** * The Wrapper object represents a packet that wraps an * unmodifiable buffer. This ensures that the contents of the buffer * are not modified during the use of the packet. To ensure that the * buffer can not be modified the append methods will * always append zero bytes, also the write methods do * no compact the buffers when some content has been written. * * @author Niall Gallagher */ class Wrapper implements Packet { /** * This is the ready only byte buffer that this packet wraps. */ private ByteBuffer buffer; /** * This is the unique sequence number for this packet. */ private long sequence; /** * This determines if the packet has already been closed. */ private boolean closed; /** * This determines if this packet represents a shared one. */ private boolean shared; /** * Constructor for the Wrapper object. This will * create a wrapper for the provided buffer which will enable * the buffer to be written to a byte channel without being * modified by the write process. * * @param buffer this is the buffer that is to be wrapped * @param sequence this is the sequence number for this packet */ public Wrapper(ByteBuffer buffer, long sequence) { this(buffer, sequence, true); } /** * Constructor for the Wrapper object. This will * create a wrapper for the provided buffer which will enable * the buffer to be written to a byte channel without being * modified by the write process. * * @param buffer this is the buffer that is to be wrapped * @param sequence this is the sequence number for this packet */ public Wrapper(ByteBuffer buffer, long sequence, boolean shared) { this.sequence = sequence; this.buffer = buffer; this.shared = shared; } /** * The sequence number represents the order with which this is * to be delivered to the underlying network. This allows safer * transfer of packets in an asynchronous environment where it * may be possible for a packet to be written out of sequence. * The sequence number also determines the order of closure. * * @return this returns an increasing packet sequence number */ public long sequence() { return sequence; } /** * This is used to determine how much space is left to append * data to this packet. This is typically equivilant to capacity * minus the length. However in the event that the packet uses * a private memory store that can not be written to then this * can return zero regardless of the capacity and length. * * @return the space left within the buffer to append data to */ public int space() { return 0; } /** * This represents the capacity of the backing store. The buffer * is full when length is equal to capacity and it can typically * be appended to when the length is less than the capacity. The * only exception is when space returns zero, which * means that the packet can not have bytes appended to it. * * @return this is the capacity of other backing byte storage */ public int capacity() { return length(); } /** * This is used to determine how many bytes remain within this * packet. It represents the number of write ready bytes, so if * the length is greater than zero the packet can be written to * a byte channel. When length is zero the packet can be closed. * * @return this is the number of bytes remaining in this packet */ public int length() { int offset = buffer.position(); int limit = buffer.limit(); if(closed) { return 0; } return limit - offset; } /** * This is used to that packets can be entered in to a priority * queue such that they are ordered based on their sequence * numbers. Ordering based on sequence numbers ensures that * packets can be remove and inserted back in to the equeue * without concern for othe order of their insertion. * * @param packet this is the packet that is to be compared * * @return this is negative is less than otherwise its positive */ public int compareTo(Packet packet) { long other = packet.sequence(); if(other > sequence) { return -1; } if(sequence > other) { return 1; } return 0; } /** * This method is used to extract the contents of the packet in * to a duplicate packet. The purpose of this is to ensure that * when a packet wraps a shared buffer the contents of that * buffer can be drained in to an allocated buffer, resulting * in a packet that can be used without read write conflicts. * * @return this returns the packets contents in a new buffer */ public Packet extract() throws IOException { int length = length(); if(length <= 0) { throw new PacketException("Buffer is empty"); } if(!shared) { return this; } return extract(length); } /** * This method is used to extract the contents of the packet in * to a duplicate packet. The purpose of this is to ensure that * when a packet wraps a shared buffer the contents of that * buffer can be drained in to an allocated buffer, resulting * in a packet that can be used without read write conflicts. * * @param size this is the size of the buffer to be extracted * * @return this returns the packets contents in a new buffer */ private Packet extract(int size) throws IOException { ByteBuffer data = ByteBuffer.allocate(size); if(size > 0) { data.put(buffer); data.position(0); } return new Wrapper(data, sequence, false); } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @return this returns the bytes sequence as a string object */ public String encode() throws IOException { return encode("ISO-8859-1"); } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @param encoding this is the character set to use for encoding * * @return this returns the bytes sequence as a string object */ public String encode(String encoding) throws IOException { ByteBuffer segment = buffer.duplicate(); if(segment == null) { return new String(); } return encode(encoding, segment); } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @param encoding this is the character set to use for encoding * @param buffer this is the buffer that will be encoded * * @return this returns the bytes sequence as a string object */ private String encode(String encoding, ByteBuffer buffer) throws IOException { Charset charset = Charset.forName(encoding); CharBuffer text = charset.decode(buffer); return text.toString(); } /** * This will not append any bytes to the packet. Because this is * an immutable implementation of the Packet it can * not modify the underlying buffer. So this will simply return * having made no changes to either the buffer of the packet. * * @param buffer this is the buffer containing the bytes * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer buffer) throws IOException { return append(buffer, 0); } /** * This will not append any bytes to the packet. Because this is * an immutable implementation of the Packet it can * not modify the underlying buffer. So this will simply return * having made no changes to either the buffer of the packet. * * @param buffer this is the buffer containing the bytes * @param count this is the number of bytes that should be used * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer buffer, int count) throws IOException { if(closed) { throw new PacketException("Packet is closed"); } return 0; } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel) throws IOException { int size = length(); if(closed) { throw new PacketException("Packet is closed"); } if(size <= 0) { return 0; } return write(channel, size); } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * @param count the number of bytes to write to the channel * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel, int count) throws IOException { if(closed) { throw new PacketException("Packet is closed"); } return write(channel, buffer); } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * @param segment this is the segment that is to be written * * @return this returns the number of bytes that were written */ private int write(ByteChannel channel, ByteBuffer segment) throws IOException { int require = segment.remaining(); int count = 0; while(count < require) { int size = channel.write(segment); if(size <= 0) { break; } count += size; } return count; } /** * This method is used to determine if the buffer is shared with * another thread or service. It is important to know whether a * packet is shared as it tells the writer whether it needs to * block the writing thread whilst the packet is pending a write * to the socket channel. * * @return true if the buffer is shared with another service */ public boolean isReference() { return shared; } /** * The close method for the packet is used to ensure * that any resources occupied by the packet are released. This * can be subclassed to introduce such functionality, however the * current implementation does not hold any releasable resources. */ public void close() throws IOException { closed = true; } /** * Provides a string representation of the state of the packet. * This can be useful for debugging the state transitions that a * packet will go through when being written and appended to. * * @return this returns a string representation for the packet */ @Override public String toString() { return String.format("%s %s", sequence, buffer); } } simple-http-4.1.21/src/org/simpleframework/transport/connect/0000755000175000017500000000000011767603362025004 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/transport/connect/ConnectionException.java0000644000175000017500000000407111417313373031617 0ustar jamespagejamespage/* * ConnectionException.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.connect; import java.io.IOException; /** * The ConnectionException is thrown if there is a problem * establishing a connection to the server. Such a problem can occur * if the server has been stopped when a new connection arrives. This * can also be thrown if some other connection related issue occurs. * * @author Niall Gallagher */ class ConnectionException extends IOException { /** * Constructor for the ConnectionException object. This * is used to represent an exception that is thrown when an error * occurs during the connect process. Typically this is thrown if * there is a problem connecting or accepting from a socket. * * @param message this is the message describing the exception */ public ConnectionException(String message) { super(message); } /** * Constructor for the ConnectionException object. This * is used to represent an exception that is thrown when an error * occurs during the connect process. Typically this is thrown if * there is a problem connecting or accepting from a socket. * * @param message this is the message describing the exception * @param cause this is the cause of the producer exception */ public ConnectionException(String message, Throwable cause) { super(message); initCause(cause); } } simple-http-4.1.21/src/org/simpleframework/transport/connect/SocketConnection.java0000644000175000017500000001235111417313373031111 0ustar jamespagejamespage/* * SocketConnection.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.transport.connect; import java.io.IOException; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.simpleframework.http.core.Container; import org.simpleframework.http.core.ContainerServer; import org.simpleframework.transport.Server; /** * The SocketConnectionis used to manage connections * from a server socket. In order to achieve this it spawns a task * to listen for incoming connect requests. When a TCP connection * request arrives it hands off the SocketChannel to * the Server which processes the request. *

* This handles connections from a ServerSocketChannel * object so that features such as SSL can be used by a server that * uses this package. The background acceptor process will terminate * if the connection is closed. * * @author Niall Gallagher * * @see org.simpleframework.transport.Server */ public class SocketConnection implements Connection { /** * This is used to maintain the active connection end points. */ private ListenerManager manager; /** * The processor is used to process connected HTTP pipelines. */ private Server server; /** * This is used to determine if the connection has been closed. */ private boolean closed; /** * Constructor for the SocketConnection object. This * will create a new connection that accepts incoming connections * and hands these connections as Pipeline objects * to the specified processor. This in turn will deliver request * and response objects to the internal container. * * @param server this is the processor that receives requests */ public SocketConnection(Server server) throws IOException { this.manager = new ListenerManager(server); this.server = server; } /** * Constructor for the SocketConnection object. This * will create a new connection that accepts incoming connections * and hands these connections as Pipeline objects * to the specified processor. This in turn will deliver request * and response objects to the internal container. * * @param container this is the container that receives requests */ public SocketConnection(Container container) throws IOException { this(new ContainerServer(container)); } /** * This creates a new background task that will listen to the * specified ServerAddress for incoming TCP connect * requests. When an connection is accepted it is handed to the * internal Server implementation as a pipeline. The * background task is a non daemon task to ensure the server is * kept active, to terminate the connection this can be closed. * * @param address this is the address used to accept connections * * @return this returns the actual local address that is used */ public SocketAddress connect(SocketAddress address) throws IOException { if(closed) { throw new ConnectionException("Connection is closed"); } return manager.listen(address); } /** * This creates a new background task that will listen to the * specified ServerAddress for incoming TCP connect * requests. When an connection is accepted it is handed to the * internal Server implementation as a pipeline. The * background task is a non daemon task to ensure the server is * kept active, to terminate the connection this can be closed. * * @param address this is the address used to accept connections * @param context this is used for secure SSL connections * * @return this returns the actual local address that is used */ public SocketAddress connect(SocketAddress address, SSLContext context) throws IOException { if(closed) { throw new ConnectionException("Connection is closed"); } return manager.listen(address, context); } /** * This is used to close the connection and the server socket * used to accept connections. This will perform a close of all * connected server sockets that have been created from using * the connect method. The connection can be * reused after the existing server sockets have been closed. * * @throws IOException thrown if there is a problem closing */ public void close() throws IOException { if(!closed) { manager.close(); server.stop(); } closed = true; } }simple-http-4.1.21/src/org/simpleframework/transport/connect/Acceptor.java0000644000175000017500000002133711417313373027405 0ustar jamespagejamespage/* * Acceptor.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.transport.connect; import java.io.IOException; import java.net.ServerSocket; import java.net.SocketAddress; import java.nio.channels.SelectableChannel; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import org.simpleframework.transport.Server; import org.simpleframework.transport.Socket; import org.simpleframework.transport.reactor.Operation; /** * The Acceptor object is used to accept incoming TCP * connections from a specified socket address. This is used by the * Connection object as a background process to accept * the connections, transform the connected sockets to pipelines, * and hand the pipelines off to a Server. *

* This is capable of processing SSL connections created by the * internal server socket. All SSL connections are forced to finish * the SSL handshake before being dispatched to the server. This * ensures that there are no problems with reading the request. * * @author Niall Gallagher * * @see org.simpleframework.transport.connect.Connection */ class Acceptor implements Operation { /** * This is the server socket channel used to accept connections. */ private final ServerSocketChannel server; /** * This is the server socket to bind the socket address to. */ private final ServerSocket socket; /** * If provided the SSL context is used to create SSL engines. */ private final SSLContext context; /** * The handler that manages the incoming HTTP connections. */ private final Server handler; /** * Constructor for the Acceptor object. This accepts * new TCP connections from the specified server socket. Each of * the connections that is accepted is configured for performance * for HTTP applications, also SSL connection can also be handled. * * @param address this is the address to accept connections from * @param context this is the SSL context used for secure HTTPS * @param handler this is used to initiate the HTTP processing */ public Acceptor(SocketAddress address, SSLContext context, Server handler) throws IOException { this.server = ServerSocketChannel.open(); this.socket = server.socket(); this.handler = handler; this.context = context; this.bind(address); } /** * This is used to acquire the local socket address that this is * listening to. This required in case the socket address that * is specified is an emphemeral address, that is an address that * is assigned dynamically when a port of 0 is specified. * * @return this returns the address for the listening address */ public SocketAddress getAddress() { return socket.getLocalSocketAddress(); } /** * This is the SelectableChannel which is used to * determine if the operation should be executed. If the channel * is ready for a given I/O event it can be run. For instance if * the operation is used to perform some form of read operation * it can be executed when ready to read data from the channel. * * @return this returns the channel used to govern execution */ public SelectableChannel getChannel() { return server; } /** * This is used to accept a new TCP connections. When the socket * is ready to accept a connection this method is invoked. It will * then create a HTTP pipeline object using the accepted socket * and if provided with an SSLContext it will also * provide an SSLEngine which is handed to the * processor to handle the HTTP requests. */ public void run() { try { accept(); } catch(Exception e) { pause(); } } /** * This is used to throttle the acceptor when there is an error * such as exhaustion of file descriptors. This will prevent the * CPU from being hogged by the acceptor on such occasions. If * the thread can not be put to sleep then this will freeze. */ private void pause() { try { Thread.sleep(10); } catch(Exception e) { return; } } /** * This is used to cancel the operation if the reactor decides to * reject it for some reason. Typically this method will never be * invoked as this operation never times out. However, should the * reactor cancel the operation this will close the socket. */ public void cancel() { try { close(); } catch(Throwable e) { return; } } /** * This is used to configure the server socket for non-blocking * mode. It will also bind the server socket to the socket port * specified in the SocketAddress object. Once done * the acceptor is ready to accept newly arriving connections. * * @param address this is the server socket address to bind to */ private void bind(SocketAddress address) throws IOException { server.configureBlocking(false); socket.setReuseAddress(true); socket.bind(address, 100); } /** * The main processing done by this object is done using a thread * calling the run method. Here the TCP connections * are accepted from the ServerSocketChannel which * creates the socket objects. Each socket is then encapsulated in * to a pipeline and dispatched to the processor for processing. * * @throws IOException if there is a problem accepting the socket */ private void accept() throws IOException { SocketChannel channel = server.accept(); while(channel != null) { configure(channel); if(context == null) { process(channel, null); } else { process(channel); } channel = server.accept(); } } /** * This method is used to configure the accepted channel. This * will disable Nagles algorithm to improve the performance of the * channel, also this will ensure the accepted channel disables * blocking to ensure that it works within the processor object. * * @param channel this is the channel that is to be configured */ private void configure(SocketChannel channel) throws IOException { channel.socket().setTcpNoDelay(true); channel.configureBlocking(false); } /** * This method is used to dispatch the socket for processing. The * socket will be configured and connected to the client, this * will hand processing to the Server which will * create the pipeline instance used to wrap the socket object. * * @param channel this is the connected socket to be processed */ private void process(SocketChannel channel) throws IOException { SSLEngine engine = context.createSSLEngine(); try { process(channel, engine); } catch(Exception e) { channel.close(); } } /** * This method is used to dispatch the socket for processing. The * socket will be configured and connected to the client, this * will hand processing to the Server which will * create the pipeline instance used to wrap the socket object. * * @param channel this is the connected socket to be processed * @param engine this is the SSL engine used for secure HTTPS */ private void process(SocketChannel channel, SSLEngine engine) throws IOException { Socket socket = new Subscription(channel, engine); try { handler.process(socket); } catch(Exception e) { channel.close(); } } /** * This is used to close the server socket channel so that the * port that it is bound to is released. This allows the acceptor * to close off the interface to the server. Ensuring the socket * is closed allows it to be recreated at a later point. * * @throws IOException thrown if the socket can not be closed */ public void close() throws IOException { server.close(); } } simple-http-4.1.21/src/org/simpleframework/transport/connect/Subscription.java0000644000175000017500000001035511417313373030327 0ustar jamespagejamespage/* * Subscription.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.transport.connect; import java.nio.channels.SocketChannel; import java.util.HashMap; import java.util.Map; import javax.net.ssl.SSLEngine; import org.simpleframework.transport.Socket; /** * This is a Subscription objects that represents a TCP * socket connections. This contains a map that allows attributes to be * associated with the client connection. Attributes such as security * certificates or other transport related details can be exposed to * the Request using the socket attribute map. *

* This provides the connected SocketChannel that can be * used to receive and response to HTTP requests. The socket channel * must be selectable and in non-blocking mode. If the socket is not * in a non-blocking state the connection will not be processed. * * @author Niall Gallagher */ class Subscription implements Socket { /** * This is the socket that provides the input and output. */ private final SocketChannel channel; /** * This is used to encrypt content for secure connections. */ private final SSLEngine engine; /** * This is used to store the attributes for the socket. */ private final Map map; /** * This creates a Subscription from a socket channel. * Any implementations of the Subscription object may * use this constructor to ensure that all the data is initialized * for the Subscription. * * @param channel the socket channel that is used as the transport */ public Subscription(SocketChannel channel) { this(channel, null); } /** * This creates a Subscription from a socket channel. * Any implementations of the Subscription object may * use this constructor to ensure that all the data is initialized * for the Subscription. * * @param channel the socket channel that is used as the transport * @param engine this is the SSL engine used for secure transport */ public Subscription(SocketChannel channel, SSLEngine engine) { this.map = new HashMap(); this.engine = engine; this.channel = channel; } /** * This is used to acquire the SSL engine used for HTTPS. If the * socket is connected to an SSL transport this returns an SSL * engine which can be used to establish the secure connection * and send and receive content over that connection. If this is * null then the socket represents a normal transport. * * @return the SSL engine used to establish a secure transport */ public SSLEngine getEngine() { return engine; } /** * This method is used to acquire the SocketChannel * for the connection. This allows the server to acquire the input * and output streams with which to communicate. It can also be * used to configure the connection and perform various network * operations that could otherwise not be performed. * * @return this returns the socket used by this HTTP socket */ public SocketChannel getChannel() { return channel; } /** * This method is used to get the Map of attributes * by this socket. The attributes map is used to maintain details * about the connection. Information such as security credentials * to client details can be placed within the attribute map. * * @return this returns the map of attributes for this socket */ public Map getAttributes() { return map; } } simple-http-4.1.21/src/org/simpleframework/transport/connect/Connection.java0000644000175000017500000000554511417313373027747 0ustar jamespagejamespage/* * Connection.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.transport.connect; import java.io.Closeable; import java.io.IOException; import java.net.SocketAddress; import javax.net.ssl.SSLContext; /** * The Connection object is used to manage connections * from a server socket. In order to achieve this it spawns a task * to listen for incoming connect requests. When a TCP connection * request arrives it hands off the SocketChannel to * the Server which processes the request. *

* This handles connections from a ServerSocketChannel * object so that features such as SSL can be used by a server that * uses this package. The background acceptor process will terminate * if the connection is closed. * * @author Niall Gallagher * * @see org.simpleframework.transport.Server */ public interface Connection extends Closeable { /** * This creates a new background task that will listen to the * specified ServerAddress for incoming TCP connect * requests. When an connection is accepted it is handed to the * internal Server implementation as a pipeline. The * background task is a non daemon task to ensure the server is * kept active, to terminate the connection this can be closed. * * @param address this is the address used to accept connections * * @return this returns the actual local address that is used */ public SocketAddress connect(SocketAddress address) throws IOException; /** * This creates a new background task that will listen to the * specified ServerAddress for incoming TCP connect * requests. When an connection is accepted it is handed to the * internal Server implementation as a pipeline. The * background task is a non daemon task to ensure the server is * kept active, to terminate the connection this can be closed. * * @param address this is the address used to accept connections * @param context this is used for secure SSL connections * * @return this returns the actual local address that is used */ public SocketAddress connect(SocketAddress address, SSLContext context) throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/connect/ListenerManager.java0000644000175000017500000000771111417313373030725 0ustar jamespagejamespage/* * ListenerManager.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.connect; import java.io.Closeable; import java.io.IOException; import java.net.SocketAddress; import java.util.HashSet; import javax.net.ssl.SSLContext; import org.simpleframework.transport.Server; /** * The ListenerManager contains the set of all listeners * that have been created for a connection. This set is used to hold * and manage the listeners that have been created for a connection. * All listeners will be closed if the listener manager is closed. * This ensures all resources held by the manager can be released. * * @author Niall Gallagher * * @see org.simpleframework.transport.connect.SocketConnection */ class ListenerManager extends HashSet implements Closeable { /** * This is the server that listeners will dispatch sockets to. */ private final Server server; /** * Constructor for the ListenerManager object. This * is used to create a manager that will enable listeners to be * created to listen to specified sockets for incoming TCP * connections, which will be converted to socket objects. * * @param server this is the server that sockets are handed to */ public ListenerManager(Server server) { this.server = server; } /** * This creates a new background task that will listen to the * specified ServerAddress for incoming TCP connect * requests. When an connection is accepted it is handed to the * internal Server implementation as a pipeline. The * background task is a non daemon task to ensure the server is * kept active, to terminate the connection this can be closed. * * @param address this is the address used to accept connections * * @return this returns the actual local address that is used */ public SocketAddress listen(SocketAddress address) throws IOException { return listen(address, null); } /** * This creates a new background task that will listen to the * specified ServerAddress for incoming TCP connect * requests. When an connection is accepted it is handed to the * internal Server implementation as a pipeline. The * background task is a non daemon task to ensure the server is * kept active, to terminate the connection this can be closed. * * @param address this is the address used to accept connections * @param context this is used for secure SSL connections * * @return this returns the actual local address that is used */ public SocketAddress listen(SocketAddress address, SSLContext context) throws IOException { Listener listener = new Listener(address, context, server); if(server != null) { add(listener); } return listener.getAddress(); } /** * This is used to close all the listeners that have been * added to the connection. Closing all the listeners in the * set ensures that there are no lingering threads or sockets * consumed by the connection after the connection is closed. * * @throws IOException thrown if there is an error closing */ public void close() throws IOException { for(Closeable listener : this) { listener.close(); } clear(); } }simple-http-4.1.21/src/org/simpleframework/transport/connect/Listener.java0000644000175000017500000001060511417313373027426 0ustar jamespagejamespage/* * Listener.java October 2002 * * Copyright (C) 2002, Niall Gallagher * * 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.transport.connect; import static java.nio.channels.SelectionKey.OP_ACCEPT; import java.io.Closeable; import java.io.IOException; import java.net.SocketAddress; import javax.net.ssl.SSLContext; import org.simpleframework.transport.Server; import org.simpleframework.transport.reactor.DirectReactor; import org.simpleframework.transport.reactor.Reactor; /** * The Listener object is represents the interface to * the server that the clients can connect to. This is responsible * for making call backs to the Acceptor when there * is a new connection waiting to be accepted. When the connection * is to be closed the interface object can be closed. * * @author Niall Gallagher */ class Listener implements Closeable { /** * This is the acceptor that is used to accept the connections. */ private final Acceptor acceptor; /** * This is the reactor used to notify the acceptor of sockets. */ private final Reactor reactor; /** * Constructor for the Listener object. This needs * a socket address and a processor to hand created sockets * to. This creates a Reactor which will notify the * acceptor when there is a new connection waiting to be accepted. * * @param address this is the address to listen for new sockets * @param processor this is where pipelines are handed to */ public Listener(SocketAddress address, Server processor) throws IOException { this(address, null, processor); } /** * Constructor for the Listener object. This needs * a socket address and a processor to hand created sockets * to. This creates a Reactor which will notify the * acceptor when there is a new connection waiting to be accepted. * * @param address this is the address to listen for new sockets * @param context this is the SSL context used for secure HTTPS * @param processor this is where pipelines are handed to */ public Listener(SocketAddress address, SSLContext context, Server processor) throws IOException { this.acceptor = new Acceptor(address, context, processor); this.reactor = new DirectReactor(); this.process(); } /** * This is used to acquire the local socket address that this is * listening to. This required in case the socket address that * is specified is an emphemeral address, that is an address that * is assigned dynamically when a port of 0 is specified. * * @return this returns the address for the listening address */ public SocketAddress getAddress() { return acceptor.getAddress(); } /** * This is used to register the Acceptor to listen * for new connections that are ready to be accepted. Once this * is registered it will remain registered until the interface * is closed, at which point the socket is closed. * * @throws IOException thrown if there is a problem registering */ private void process() throws IOException { try { reactor.process(acceptor, OP_ACCEPT); } catch(Exception e) { throw new ConnectionException("Listen error", e); } } /** * This is used to close the connection and the server socket * used to accept connections. This will perform a close of the * connected server socket that have been created from using * the Acceptor object. * * @throws IOException thrown if there is a problem closing */ public void close() throws IOException { try { acceptor.close(); reactor.stop(); } catch(Exception e) { throw new ConnectionException("Close error", e); } } } simple-http-4.1.21/src/org/simpleframework/transport/SocketTransport.java0000644000175000017500000002140511417313373027355 0ustar jamespagejamespage/* * SocketTransport.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Map; import javax.net.ssl.SSLEngine; import org.simpleframework.transport.reactor.Reactor; /** * The SocketTransport object offers a transport that can * send and receive bytes in a non-blocking manner. The contract of * the Transport is that it must either write the data * it is asked to write or it must queue that data for delivery. For * the vast majority of cases data is written directly to the socket * without any need for queuing or selection for write ready events. *

* In the event that the client TCP window is full and writing would * block this makes use of a queue of buffers which can be used to * append data to. The buffers are lazily instantiated so the memory * required is created only in the rare case that they are needed. * Once a buffer is full it is queued to an asynchronous thread where * the buffer queue is drained and sent to the client when the TCP * window of the client is capable of accepting it. *

* In order to improve the network performance of this transport the * default packet size sent to the TCP stack is four kilobytes. This * ensures that the fragments of response delivered to the TCP layer * are sufficiently large for optimum network performance. * * @author Niall Gallagher */ class SocketTransport implements Transport { /** * This creates packets with increasing sequence numbers. */ private PacketBuilder builder; /** * This is the underlying byte channel used to send the data. */ private SocketChannel channel; /** * This is the writer that is used to flush the buffer queue. */ private Controller writer; /** * This is the socket that this transport is representing. */ private Socket socket; /** * This is used to determine if the transport has been closed. */ private boolean closed; /** * Constructor for the SocketTransport object. This * requires a reactor to perform asynchronous writes and also the * pipeline which is used to read and write data. This transport * will use a queue of buffers which are lazily initialized so as * to only allocate the memory on demand. * * @param socket this is used to read and write the data * @param reactor this is used to perform asynchronous writes */ public SocketTransport(Socket socket, Reactor reactor) throws IOException { this(socket, reactor, 20480); } /** * Constructor for the SocketTransport object. This * requires a reactor to perform asynchronous writes and also the * pipeline which is used to read and write data. This transport * will use a queue of buffers which are lazily initialized so as * to only allocate the memory on demand. * * @param socket this is used to read and write the data * @param reactor this is used to perform asynchronous writes * @param limit this is the threshold for asynchronous buffers */ public SocketTransport(Socket socket, Reactor reactor, int limit) throws IOException { this(socket, reactor, limit, 3); } /** * Constructor for the SocketTransport object. This * requires a reactor to perform asynchronous writes and also the * pipeline which is used to read and write data. This transport * will use a queue of buffers which are lazily initialized so as * to only allocate the memory on demand. * * @param socket this is used to read and write the data * @param reactor this is used to perform asynchronous writes * @param limit this is the threshold for asynchronous buffers * @param queue this is the queue size for asynchronous writes */ public SocketTransport(Socket socket, Reactor reactor, int limit, int queue) throws IOException { this.writer = new SocketController(socket, reactor, limit); this.builder = new PacketBuilder(queue); this.channel = socket.getChannel(); this.socket = socket; } /** * This method is used to get the Map of attributes * by this pipeline. The attributes map is used to maintain details * about the connection. Information such as security credentials * to client details can be placed within the attribute map. * * @return this returns the map of attributes for this pipeline */ public Map getAttributes() { return socket.getAttributes(); } /** * This is used to acquire the SSL engine used for https. If the * pipeline is connected to an SSL transport this returns an SSL * engine which can be used to establish the secure connection * and send and receive content over that connection. If this is * null then the pipeline represents a normal transport. * * @return the SSL engine used to establish a secure transport */ public SSLEngine getEngine() { return socket.getEngine(); } /** * This method is used to acquire the SocketChannel * for the connection. This allows the server to acquire the input * and output streams with which to communicate. It can also be * used to configure the connection and perform various network * operations that could otherwise not be performed. * * @return this returns the socket used by this HTTP pipeline */ public SocketChannel getChannel() { return socket.getChannel(); } /** * This is used to perform a non-blocking read on the transport. * If there are no bytes available on the input buffers then * this method will return zero and the buffer will remain the * same. If there is data and the buffer can be filled then this * will return the number of bytes read. Finally if the socket * is closed this will return a -1 value. * * @param data this is the buffer to append the bytes to * * @return this returns the number of bytes that were read */ public int read(ByteBuffer data) throws IOException { if(closed) { throw new TransportException("Transport is closed"); } return channel.read(data); } /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. This * will buffer the bytes within the internal buffer to ensure * that the response fragments are sufficiently large for the * network. Smaller packets result poorer performance. * * @param data this is the array of bytes to send to the client */ public void write(ByteBuffer data) throws IOException{ if(closed) { throw new TransportException("Transport is closed"); } Packet packet = builder.build(data); while(packet != null) { if(!closed) { writer.write(packet); } packet = builder.build(data); } } /** * This is used to flush the internal buffer to the underlying * socket. Flushing with this method is always non-blocking, so * if the socket is not write ready and the buffer can be queued * it will be queued and the calling thread will return. */ public void flush() throws IOException { if(closed) { throw new TransportException("Transport is closed"); } Packet packet = builder.build(); if(packet != null) { writer.write(packet); } } /** * This method is used to flush the internal buffer and close * the underlying socket. This method will not complete until * all buffered data is written and the underlying socket is * closed at which point this can be disposed of. */ public void close() throws IOException { if(!closed) { Packet packet = builder.build(); if(packet != null) { writer.write(packet); } writer.close(); closed = true; } } } simple-http-4.1.21/src/org/simpleframework/transport/SocketFlusher.java0000644000175000017500000001547011417313373026776 0ustar jamespagejamespage/* * SocketFlusher.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.channels.SocketChannel; import org.simpleframework.transport.reactor.Operation; import org.simpleframework.transport.reactor.Reactor; /** * The SocketFlusher flushes bytes to the underlying * socket channel. This allows asynchronous writes to the socket * to be managed in such a way that there is order to the way data * is delivered over the socket. This uses a selector to dispatch * flush invocations to the underlying socket when the socket is * write ready. This allows the writing thread to continue without * having to wait for all the data to be written to the socket. * * @author Niall Gallagher * * @see org.simpleframework.transport.Controller */ class SocketFlusher implements Flusher { /** * This is the signaller used to determine when to flush. */ private Signaller signaller; /** * This is the scheduler used to block and signal the writer. */ private Scheduler scheduler; /** * This is the writer used to queue the packets written. */ private Writer writer; /** * This is used to determine if the socket flusher is closed. */ private boolean closed; /** * Constructor for the SocketFlusher object. This is * used to flush buffers to the underlying socket asynchronously. * When finished flushing all of the buffered data this signals * any threads that are blocking waiting for the write to finish. * * @param reactor this is used to perform asynchronous writes * @param writer this is used to write the buffered packets */ public SocketFlusher(Reactor reactor, Writer writer) throws IOException { this.signaller = new Signaller(writer); this.scheduler = new Scheduler(reactor, signaller, this); this.writer = writer; } /** * Here in this method we schedule a flush when the underlying * writer is write ready. This allows the writer thread to return * without having to fully flush the content to the underlying * transport. If there are references queued this will block. */ public synchronized void flush() throws IOException { if(closed) { throw new TransportException("Flusher is closed"); } boolean block = writer.isBlocking(); if(!closed) { scheduler.schedule(block); } } /** * This is executed when the flusher is to write all of the data to * the underlying socket. In this situation the writes are attempted * in a non blocking way, if the task does not complete then this * will simply enqueue the writing task for OP_WRITE and leave the * method. This returns true if all the buffers are written. */ private synchronized void execute() throws IOException { boolean ready = writer.flush(); if(!ready) { boolean block = writer.isBlocking(); if(!block && !closed) { scheduler.release(); } scheduler.repeat(); } else{ scheduler.ready(); } } /** * This is used to abort the flushing process when the reactor has * been stopped. An abort to the flusher typically happens when the * server has been shutdown. It prevents threads lingering waiting * for a I/O operation which prevents the server from shutting down. */ private synchronized void abort() throws IOException { scheduler.close(); writer.close(); } /** * This is used to close the flusher ensuring that all of the * data within the writer will be flushed regardless of the * amount of data within the writer that needs to be written. If * the writer does not block then this waits to be finished. */ public synchronized void close() throws IOException { boolean ready = writer.flush(); if(!closed) { closed = true; } if(!ready) { scheduler.schedule(true); } } /** * The Signaller is an operation that performs the * write operation asynchronously. This will basically determine * if the socket is write ready and drain each queued buffer to * the socket until there are no more pending buffers. * * @author Niall Gallagher */ private class Signaller implements Operation { /** * This is the writer that is used to write the data. */ private final Writer writer; /** * Constructor for the Signaller object. This will * create an operation that is used to flush the packet queue * to the underlying socket. This ensures that the data is * written to the socket in the queued order. * * @param writer this is the writer to flush the data to */ public Signaller(Writer writer) { this.writer = writer; } /** * This returns the socket channel for the connected pipeline. It * is this channel that is used to determine if there are bytes * that can be written. When closed this is no longer selectable. * * @return this returns the connected channel for the pipeline */ public SocketChannel getChannel() { return writer.getChannel(); } /** * This is used to perform the drain of the pending buffer * queue. This will drain each pending queue if the socket is * write ready. If the socket is not write ready the operation * is enqueued for selection and this returns. This ensures * that all the data will eventually be delivered. */ public void run() { try { execute(); } catch(Exception e) { cancel(); } } /** * This is used to cancel the operation if it has timed out. * If the delegate is waiting too long to flush the contents * of the buffers to the underlying transport then the socket * is closed and the flusher times out to avoid deadlock. */ public void cancel() { try { abort(); }catch(Exception e){ return; } } } } simple-http-4.1.21/src/org/simpleframework/transport/TransportException.java0000644000175000017500000000351111417313373030061 0ustar jamespagejamespage/* * TransportException.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The TransportException object is thrown when there * is a problem with the transport. Typically this is done thrown if * there is a problem reading or writing to the transport. * * @author Niall Gallagher */ public class TransportException extends IOException { /** * Constructor for the TransportException object. If * there is a problem sending or reading from a transport then it * will throw a transport exception to report the error. * * @param message this is the message associated with the error */ public TransportException(String message) { super(message); } /** * Constructor for the TransportException object. If * there is a problem sending or reading from a transport then it * will throw a transport exception to report the error. * * @param message this is the message associated with the error * @param cause this is the cause of the producer exception */ public TransportException(String message, Throwable cause) { super(message); initCause(cause); } } simple-http-4.1.21/src/org/simpleframework/transport/Controller.java0000644000175000017500000000413311417313373026332 0ustar jamespagejamespage/* * Controller.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The Controller interface is used to represent the means * to write packets to an underlying transport. This controls all of * the selection required to determine if the socket is write ready. * If the packet to be written is to block then this will wait until * all queued packets are fully written. * * @author Niall Gallagher */ interface Controller { /** * This method is used to deliver the provided packet of bytes to * the underlying transport. This will not modify the data that * is to be written, this will simply queue the packets in the * order that they are provided. * * @param packet this is the packet to send to the client */ public void write(Packet packet) throws IOException; /** * This method is used to flush all of the queued packets to * the client. This method will block not block but will simply * flush any data to the underlying transport. Internally the * data will be queued for delivery to the connected entity. */ public void flush() throws IOException; /** * This is used to close the writer and the underlying socket. * If a close is performed on the writer then no more bytes * can be read from or written to the writer and the client * will receive a connection close on their side. */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/PacketAllocator.java0000644000175000017500000000743511417313373027267 0ustar jamespagejamespage/* * PacketAllocator.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicLong; /** * The PacketAllocator object is used to create packets * that contain increasing sequence numbers. This acts as a pool for * buffers which can be recycled by closing the Packet * objects created by this allocator. As well as creating buffers * from a pool of byte buffers this can wrap an existing buffer * within a packet so it can be used uniformly. * * @author Niall Gallagher */ class PacketAllocator { /** * This is the memory manager used to recycle the buffers. */ private final PacketManager manager; /** * This is the counter used to generate the sequence numbers. */ private final AtomicLong count; /** * Constructor for the PacketAllocator object. This * is provided the size of the buffers that will be allocated and * the number of buffers that can be lazily created before it * will block waiting for the next buffer to be returned. */ public PacketAllocator() { this(3); } /** * Constructor for the PacketAllocator object. This * is provided the size of the buffers that will be allocated and * the number of buffers that can be lazily created before it * will block waiting for the next buffer to be returned. * * @param allow this is the queue size for asynchronous writes */ public PacketAllocator(int allow) { this(allow, 4096); } /** * Constructor for the PacketAllocator object. This * is provided the size of the buffers that will be allocated and * the number of buffers that can be lazily created before it * will block waiting for the next buffer to be returned. * * @param allow this is the queue size for asynchronous writes * @param size this is the size of the buffers to be allocated */ public PacketAllocator(int allow, int size) { this.manager = new PacketManager(allow, size); this.count = new AtomicLong(); } /** * This creates a Packet from a buffer within the * pool of buffers. The buffer provided can be modified up until * such point as it is recycled. To recycle the buffer the packet * must be closed, when closed the buffer can be reused. * * @return this returns a packet backed by a pooled buffer */ public Packet allocate() throws PacketException { long sequence = count.getAndIncrement(); ByteBuffer buffer = manager.allocate(); return new Appender(buffer, manager, sequence); } /** * This creates a Packet by wrapping the provided * buffer within the packet interface. The buffer provided will * be read only such that the buffer it wraps is not modified. * * @param buffer this is the buffer that has been wrapped * * @return this returns a packet backed by a pooled buffer */ public Packet allocate(ByteBuffer buffer) throws PacketException { long sequence = count.getAndIncrement(); return new Wrapper(buffer, sequence); } } simple-http-4.1.21/src/org/simpleframework/transport/Status.java0000644000175000017500000000240511417313373025472 0ustar jamespagejamespage/* * Status.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; /** * The Status enumeration is used to determine what * action is required within a negotiation. This allows the * negotiation to control the selection for read and write ready * operations. Also, status signals completion of the handshake. * * @author Niall Gallagher */ enum Status { /** * Tells the negotiation that a read operations is needed. */ CLIENT, /** * Tells the negotiation that a write operation is required. */ SERVER, /** * Tells the negotiation that the the handshake is complete. */ DONE }simple-http-4.1.21/src/org/simpleframework/transport/Writer.java0000644000175000017500000000622611417313373025470 0ustar jamespagejamespage/* * Writer.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.channels.SocketChannel; /** * The Writer object represents a means to coalesce * packets at a single point before being written to the socket. It * is used to ensure all packets are queued in order of sequence * number. Any packets that are partially written to the underlying * socket can be coalesced in to a single packet so that a larger * packet can be delivered over the network. * * @author Niall Gallagher * * @see org.simpleframework.transport.Flusher */ interface Writer { /** * This is used to determine if the writer should block or not. * A writer will block only if there are shared packets still * within the write queue. When all shared packets have either * been written or duplicated then the writer does not need to * block any waiting threads and they can be released. * * @return true if any writing thread should be blocked */ public boolean isBlocking() throws IOException; /** * This is used to write the packets to the writer which will * be either written to the underlying socket or queued until * such time as the socket is write ready. This will return true * if the packet has been written to the underlying transport. * * @param packet this is the packet that is the be written * * @return true if the packet has been written to the transport */ public boolean write(Packet packet) throws IOException; /** * This is used to flush all queued packets to the underlying * socket. If all of the queued packets have been fully written * then this returns true, otherwise this will return false. * * @return true if all queued packets are flushed to the socket */ public boolean flush() throws IOException; /** * This is used to close the writer and the underlying socket. * If a close is performed on the writer then no more bytes * can be read from or written to the writer and the client * will receive a connection close on their side. */ public void close() throws IOException; /** * This provides the socket for the writer. Providing this * enables a Reactor to be used to determine when * the writer is write ready and thus when the writer can * be flushed if it contains packets that have not been written. * * @return this returns the socket associated with this */ public SocketChannel getChannel(); } simple-http-4.1.21/src/org/simpleframework/transport/Notifier.java0000644000175000017500000000256111417313373025771 0ustar jamespagejamespage/* * Notifier.java February 2009 * * Copyright (C) 2009, Niall Gallagher * * 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.transport; import org.simpleframework.transport.reactor.Operation; /** * The Notifier object is a special type of operation * that is used to notify the kernel that there is an initialized * connection ready for processing. Typically an initialized socket * is one that has undergone the SSL handshake or is a raw byte * stream that does not require the SSL handshake. * * @author Niall Gallagher */ interface Notifier extends Operation { /** * This method is executed when the operation is in a state * that is ready for execution. Typically for a notifier this * is executed when there is data ready to read from the socket. */ public void run(); } simple-http-4.1.21/src/org/simpleframework/transport/Packet.java0000644000175000017500000002133211417313373025416 0ustar jamespagejamespage/* * Packet.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; /** * The Packet object is used to represent a collection * of bytes that can be written to a byte channel. The packet is used * to provide a uniform interface to byte sequences that need to be * transferred to the connected client. It ensures that regardless of * the backing memory store the transport can deal with the packets * transparently. In particular packets provide a means to ensure the * order requested is the order delivered. It uses sequence numbers * to ensure that the delivery is performed in an orderly manner. *

* When using a packet it is important to note that they must always * be closed with the close method when finished with. * This ensures any occupied resource is released. Resources such as * buffers can be placed back in to a pool and locks released when a * packet is closed. Failure to close can lead to a leak in resources. * * @author Niall Gallagher * * @param org.simpleframework.transport.Writer */ interface Packet extends Comparable { /** * This is used to determine how many bytes remain within this * packet. It represents the number of write ready bytes, so if * the length is greater than zero the packet can be written to * a byte channel. When length is zero the packet can be closed. * * @return this is the number of bytes remaining in this packet */ public int length(); /** * This represents the capacity of the backing store. The buffer * is full when length is equal to capacity and it can typically * be appended to when the length is less than the capacity. The * only exception is when space returns zero, which * means that the packet can not have bytes appended to it. * * @return this is the capacity of other backing byte storage */ public int capacity(); /** * This is used to determine how much space is left to append * data to this packet. This is typically equivelant to capacity * minus the length. However in the event that the packet uses * a private memory store that can not be written to then this * can return zero regardless of the capacity and length. * * @return the space left within the buffer to append data to */ public int space(); /** * The sequence number represents the order with which this is * to be delivered to the underlying network. This allows safer * transfer of packets in an asynchronous environment where it may * be possible for a packet to be written out of sequence. The * sequence number also determines the order of closure. * * @return this returns an increasing packet sequence number */ public long sequence(); /** * This method is used to determine if the buffer is a reference * to a byte buffer rather than a copy. It is important to know if * a packet is shared as it tells the writer whether it needs to * block the writing thread while the packet is pending a write * to the socket channel. * * @return true if the packet is a reference to the byte buffer */ public boolean isReference(); /** * This is used to that packets can be entered in to a priority * queue such that they are ordered based on their sequence * numbers. Ordering based on sequence numbers ensures that * packets can be removed and inserted back in to the queue * without concern for the order of their insertion. * * @param packet this is the packet that is to be compared * * @return this is negative is less than otherwise its positive */ public int compareTo(Packet packet); /** * This method is used to extract the contents of the packet in * to a duplicate packet. The purpose of this is to ensure that * when a packet wraps a shared buffer the contents of that * buffer can be drained in to an allocated buffer, resulting * in a packet that can be used without read write conflicts. * * @return this returns the packets contents in a new buffer */ public Packet extract() throws IOException; /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @return this returns the bytes sequence as a string object */ public String encode() throws IOException; /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @param charset this is the character set to use for encoding * * @return this returns the bytes sequence as a string object */ public String encode(String charset) throws IOException; /** * This will append bytes within the given buffer to the packet. * Once invoked the packet will contain the buffer bytes, which * will have been drained from the buffer. This effectively moves * the bytes in the buffer to the end of the packet instance. * * @param buffer this is the buffer containing the bytes * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer buffer) throws IOException; /** * This will append bytes within the given buffer to the packet. * Once invoked the packet will contain the buffer bytes, which * will have been drained from the buffer. This effectively moves * the bytes in the buffer to the end of the packet instance. * * @param buffer this is the buffer containing the bytes * @param count this is the number of bytes that should be used * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer buffer, int count) throws IOException; /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel) throws IOException; /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * @param count the number of bytes to write to the channel * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel, int count) throws IOException; /** * The close method for the packet is used to ensure * that any resources occupied by the packet are released. These * could be anything from internally pooled buffers to locks. If * the packet is not closed on completion then this can result in * a leak of resources within the associated transport. */ public void close() throws IOException; /** * Provides a string representation of the state of the packet. * This can be useful for debugging the state transitions that a * packet will go through when being written and appended to. * * @return this returns a string representation for the packet */ public String toString(); } simple-http-4.1.21/src/org/simpleframework/transport/PacketManager.java0000644000175000017500000001271611417313373026717 0ustar jamespagejamespage/* * PacketManager.java October 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.nio.ByteBuffer; import java.util.concurrent.LinkedBlockingQueue; /** * The PacketManager object is used to create buffers * used to buffer output. Buffers are created lazily so that they * are allocated only on demand. Typically buffers are only created * when small chunks of data are written to the transport and the * socket is blocking. This ensures that writing can continue * without waiting for the data to be fully drained. * * @author Niall Gallagher */ class PacketManager implements Recycler { /** * This is the queue that is used to recycle the buffers. */ private Queue queue; /** * Determines how many buffers can be lazily created. */ private int allow; /** * Determines the size of the buffers that are created. */ private int size; /** * Constructor for the PacketManager object. This * requires the size of the buffers that will be allocated and * the number of buffers that can be lazily created before it * will block waiting for the next buffer to be returned. */ public PacketManager() { this(3); } /** * Constructor for the PacketManager object. This * requires the size of the buffers that will be allocated and * the number of buffers that can be lazily created before it * will block waiting for the next buffer to be returned. * * @param allow this is the size of the buffers to be allocated */ public PacketManager(int allow) { this(allow, 4096); } /** * Constructor for the PacketManager object. This * requires the size of the buffers that will be allocated and * the number of buffers that can be lazily created before it * will block waiting for the next buffer to be returned. * * @param allow this is the number of buffers to be created * @param size this is the size of the buffers to be allocated */ public PacketManager(int allow, int size) { this.queue = new Queue(); this.allow = allow; this.size = size; } /** * This checks to see if there is a buffer ready within the queue. * If there is one ready then this returns it, if not then this * checks how many buffers have been created. If we can create one * then return a newly instantiated buffer, otherwise block and * wait for one to be recycled. * * @return this returns the next ready buffer within the manager */ public ByteBuffer allocate() throws PacketException { ByteBuffer next = queue.poll(); if(next != null) { return next; } return create(); } /** * This checks to see if there is a buffer ready within the queue. * If there is one ready then this returns it, if not then this * checks how many buffers have been created. If we can create one * then return a newly instantiated buffer, otherwise block and * wait for one to be enqueued. * * @return this returns the next ready buffer within the queue */ private ByteBuffer create() throws PacketException { if(allow-- >= 0) { return build(); } try { return queue.take(); } catch(Exception e) { throw new PacketException("Thread interrupt", e); } } /** * This method is used to recycle the buffer. Invoking this with * a buffer instance will pass the buffer back in to the pool. * Once passed back in to the pool the buffer should no longer * be used as it may affect future uses of the buffer. * * @param buffer this is the buffer that is to be recycled */ public void recycle(ByteBuffer buffer) { buffer.clear(); queue.offer(buffer); } /** * This is used to allocate a buffer if there is no buffer ready * within the queue. The size of the buffer is determined from * the size specified when the buffer queue is created. * * @return this returns a newly allocated byte buffer */ private ByteBuffer build() { try { return ByteBuffer.allocateDirect(size); }catch(Throwable e) { return ByteBuffer.allocate(size); } } /** * The Queue is used to transfer recycled buffers * back in to the main memory store. When recycled they can be * allocated again and used to transfer data to the socket. * * @author Niall Gallagher */ private class Queue extends LinkedBlockingQueue { /** * Constructor for the Queue object. This is * used to create a queue that will transfer buffers back * in to the main memory store so they can be reused. */ public Queue() { super(); } } } simple-http-4.1.21/src/org/simpleframework/transport/Transport.java0000644000175000017500000000567511417313373026217 0ustar jamespagejamespage/* * Transport.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; /** * The Transport interface represents a low level means * to deliver content to the connected client. Typically this will * be a connected, non-blocking, TCP connection. However, for tests * and other purposes this may be adapted. The general contract of * the transport is that it provides non-blocking reads and blocking * writes. Blocking writes are required to ensure that memory does * not build up in output buffers during high load periods. * * @author Niall Gallagher */ public interface Transport extends Socket { /** * This is used to perform a non-blocking read on the transport. * If there are no bytes available on the input buffers then * this method will return zero and the buffer will remain the * same. If there is data and the buffer can be filled then this * will return the number of bytes read. Finally if the socket * is closed this will return a -1 value. * * @param buffer this is the buffer to append the bytes to * * @return this returns the number of bytes that have been read */ public int read(ByteBuffer buffer) throws IOException; /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @param buffer this is the buffer of bytes to send to the client */ public void write(ByteBuffer buffer) throws IOException; /** * This method is used to flush the contents of the buffer to * the client. This method will block not block but will simply * flush any data to the underlying transport. Internally the * data will be queued for delivery to the connected entity. */ public void flush() throws IOException; /** * This is used to close the transport and the underlying socket. * If a close is performed on the transport then no more bytes * can be read from or written to the transport and the client * will receive a connection close on their side. */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/Dispatcher.java0000644000175000017500000000651411417313373026302 0ustar jamespagejamespage/* * Dispatcher.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.nio.channels.SocketChannel; import org.simpleframework.transport.reactor.Operation; /** * The Dispatcher operation is used transfer a transport * to the negotiator so it can be processed. This is uses so that * when a pipeline is given to the processor it can be dispatched * in another thread to the transporter. This is needed so that the * connection thread is occupied only briefly. * * @author Niall Gallagher */ class Dispatcher implements Operation { /** * This is the negotiator used to transfer the transport to. */ private final Negotiator negotiator; /** * This is the transport to be passed to the negotiator. */ private final Transport transport; /** * Constructor for the Dispatcher object. This is used * to transfer a transport to a negotiator. Transferring the * transport using an operation ensures that the thread that is * used to process the pipeline is not occupied for long. * * @param transport this is the transport this exchange uses * @param negotiator this is the negotiation to dispatch to */ public Dispatcher(Transport transport, Negotiator negotiator) { this.negotiator = negotiator; this.transport = transport; } /** * This is the SelectableChannel which is used to * determine if the operation should be executed. If the channel * is ready for a given I/O event it can be run. For instance if * the operation is used to perform some form of read operation * it can be executed when ready to read data from the channel. * * @return this returns the channel used to govern execution */ public SocketChannel getChannel() { return transport.getChannel(); } /** * This is used to transfer the transport to the negotiator. This * will typically be executed asynchronously so that it does not * delay the thread that passes the Pipeline to the * transport processor, ensuring quicker processing. */ public void run() { try { negotiator.process(transport); }catch(Exception e) { cancel(); } } /** * This is used to cancel the operation if it has timed out. This * is typically invoked when it has been waiting in a selector for * an extended duration of time without any active operations on * it. In such a case the reactor must purge the operation to free * the memory and open channels associated with the operation. */ public void cancel() { try { transport.close(); }catch(Exception e) { return; } } } simple-http-4.1.21/src/org/simpleframework/transport/PacketException.java0000644000175000017500000000345711417313373027305 0ustar jamespagejamespage/* * PacketException.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; /** * The PacketException represents an exception that * is thrown when there is a problem with a packet operation. This * is typically thrown when the packet is closed and an attempt * is made to either append or write the packets contents. * * @author Niall Gallagher */ class PacketException extends TransportException { /** * Constructor for the PacketException object. This * creates an exception that takes a template string an a list * of arguments to pass in to the message template. * * @param message this is the message template string to use */ public PacketException(String message) { super(message); } /** * Constructor for the PacketException object. This * creates an exception that takes a template string an a list * of arguments to pass in to the message template. * * @param message this is the message template string to use * @param cause this is the root cause of the exception */ public PacketException(String message, Throwable cause) { super(message, cause); } } simple-http-4.1.21/src/org/simpleframework/transport/Processor.java0000644000175000017500000000445311417313373026173 0ustar jamespagejamespage/* * Processor.java February 2007 * * Copyright (C) 2001, Niall Gallagher * * 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.transport; import java.io.IOException; /** * This is the Processor used to process HTTP requests * from a connected transport. This will process each request from * a provided transport and pass those requests to a container. The * transport provided can be either a direct transport or provide * some form of secure encoding such as SSL. * * @author Niall Gallagher * * @see org.simpleframework.transport.Transport */ public interface Processor { /** * This is used to process the requests from a provided transport * and deliver a response to those requests. A transport can be * a direct transport or a secure transport providing SSL. *

* Typical usage of this method is to accept multiple transport * objects, each representing a unique HTTP channel to the client, * and process requests from those transports concurrently. * * @param transport the transport to process requests from */ public void process(Transport transport) throws IOException; /** * This method is used to stop the Processor such * that it will accept no more pipelines. Stopping the processor * ensures that all resources occupied will be released. This is * required so that all threads are stopped and released. *

* Typically this method is called once all connections to the * server have been stopped. As a final act of shutting down the * entire server all threads must be stopped, this allows collection * of unused memory and the closing of file and socket resources. */ public void stop() throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/Task.java0000644000175000017500000001113211417313373025106 0ustar jamespagejamespage/* * Task.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.channels.SelectableChannel; import org.simpleframework.transport.reactor.Operation; import org.simpleframework.transport.reactor.Reactor; /** * The Task object is used to represent an asynchronous * task that interacts with the negotiation. This is typically used * to either schedule an asynchronous read or write when it can not * be performed directly. It ensures that the negotiation does not * block the thread so that execution can be optimized. * * @author Niall Gallagher * * @see org.simpleframework.transport.Handshake */ abstract class Task implements Operation { /** * This is the negotiation that this task will operate on. */ protected final Negotiation state; /** * This is the reactor that is used to schedule execution. */ protected final Reactor reactor; /** * This is the required operation for the task to complete. */ protected final int require; /** * Constructor for the Task object. This is used to * create an operation that performs some action with respect * the the negotiation. It allows the negotiation to schedule * the read and write operations so the thread does not block. * * @param state this is the negotiation this task works on * @param reactor this is the reactor used to schedule the task * @param require this is the required operation for the task */ public Task(Negotiation state, Reactor reactor, int require) { this.reactor = reactor; this.require = require; this.state = state; } /** * This is the SelectableChannel which is used to * determine if the operation should be executed. If the channel * is ready for a given I/O event it can be run. For instance if * the operation is used to perform some form of read operation * it can be executed when ready to read data from the channel. * * @return this returns the channel used to govern execution */ public SelectableChannel getChannel() { return state.getChannel(); } /** * This is used to execute the task. It is up to the specific * task implementation to decide what to do when executed. If * the task needs to read or write data then it can attempt * to perform the read or write, if it incomplete the it can * be scheduled for execution with the reactor. */ public void run() { try { execute(); }catch(Exception e) { cancel(); } } /** * This is used to cancel the operation if it has timed out. This * is typically invoked when it has been waiting in a selector for * an extended duration of time without any active operations on * it. In such a case the reactor must purge the operation to free * the memory and open channels associated with the operation. */ public void cancel() { try { state.cancel(); }catch(Exception e) { return; } } /** * This is used to execute the task. It is up to the specific * task implementation to decide what to do when executed. If * the task needs to read or write data then it can attempt * to perform the read or write, if it incomplete the it can * be scheduled for execution with the reactor. */ protected void execute() throws IOException { boolean done = ready(); if(!done) { reactor.process(this, require); } else { state.resume(); } } /** * This method is used to determine if the task is ready. This is * executed when the select operation is signaled. When this is * true the the task completes. If not then this will schedule * the task again for the specified select operation. * * @return this returns true when the task has completed */ protected boolean ready() throws IOException { return true; } } simple-http-4.1.21/src/org/simpleframework/transport/PacketBuilder.java0000644000175000017500000001262211417313372026726 0ustar jamespagejamespage/* * PacketBuilder.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; /** * The PacketBuilder object is used to accumulate octets * from provided byte buffers. This will create Packet * objects which encapsulate all of the information associated with * the byte buffer, and can tell the transport how the buffer should * be written. Packets can be either references or copies. * * @author Niall Gallagher */ class PacketBuilder { /** * This is packet allocator that is used to create packets. */ private PacketAllocator allocator; /** * This is the packet within the packet builder being built. */ private Packet packet; /** * This is the size of the packets that will be created. */ private int size; /** * Constructor for the PacketBuilder object. This is * used to create a builder that can be used to aggregate multiple * byte buffers in to a single packet object. This will limit the * number of packets that can be created by this builder. */ public PacketBuilder() { this(3); } /** * Constructor for the PacketBuilder object. This is * used to create a builder that can be used to aggregate multiple * byte buffers in to a single packet object. This will limit the * number of packets that can be created by this builder. * * @param limit this is a limit to the number of packets built */ public PacketBuilder(int limit) { this(3, 4098); } /** * Constructor for the PacketBuilder object. This is * used to create a builder that can be used to aggregate multiple * byte buffers in to a single packet object. This will limit the * number of packets that can be created by this builder. * * @param limit this is a limit to the number of packets built * @param size this is the size of the packets to be built */ public PacketBuilder(int limit, int size) { this.allocator = new PacketAllocator(limit, size); this.size = size; } /** * This is used to acquire the current packet that has been built * within the builder. When the packet is returned from this * method another packet is allocated for the next build. * * @return this returns the current packet within the builder */ public Packet build() throws IOException { Packet local = null; if(packet != null) { int length = packet.length(); if(length <= 0) { packet.close(); } else { local = packet; } packet = null; } return local; } /** * This is used to build the a Packet within the * builder using the provided buffer. The returned packet will * contain the accumulated bytes from calls to the build method. * If the previous packets are not closed this method will * block until such time as the packet is closed. * * @param buffer this is the buffer to be added to the packet * * @return this returns the packet containing the bytes */ public Packet build(ByteBuffer buffer) throws IOException { int ready = buffer.remaining(); if(packet != null) { return build(buffer, packet); } if(ready > size) { return allocator.allocate(buffer); } if(ready > 0) { if(packet == null) { packet = allocator.allocate(); } return build(buffer, packet); } return null; } /** * This is used to build the a Packet within the * builder using the provided buffer. The returned packet will * contain the accumulated bytes from calls to the build method. * If the previous packets are not closed this method will * block until such time as the packet is closed. * * @param buffer this is the buffer to be added to the packet * @param packet this is the packet to add the buffer to * * @return this returns the packet containing the bytes */ private Packet build(ByteBuffer buffer, Packet packet) throws IOException { int ready = buffer.remaining(); int length = packet.length(); int space = packet.space(); if(ready <= space) { packet.append(buffer); } else { int capacity = buffer.capacity(); if(length == 0) { return allocator.allocate(buffer); } if(space < capacity) { if(space > 0) { packet.append(buffer); } return build(); } return allocator.allocate(buffer); } if(space == ready) { return build(); } return null; } } simple-http-4.1.21/src/org/simpleframework/transport/Segment.java0000644000175000017500000002276211417313373025621 0ustar jamespagejamespage/* * Segment.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.util.Queue; /** * The Segment object is used to provide a means to * remove a packet from an segment builder when closed. This can * be used to wrap an existing packet and add functionality to it. * Once closed this segment should no longer be used. * * @author Niall Gallagher */ class Segment implements Packet { /** * This is the packet that is being wrapped by this instance. */ private final Packet packet; /** * This is the queue that the packet is to be removed from. */ private final Queue queue; /** * Constructor for the Segment object. This is used * to create a packet that wraps another packet, and removes * that packet from the head of a given queue when it is closed. * * @param packet this is the packet that is to be wrapped * @param queue this is the queue to remove the packet from */ public Segment(Packet packet, Queue queue) { this.packet = packet; this.queue = queue; } /** * This is used to determine how much space is left to append * data to this packet. This is typically equivalent to capacity * minus the length. However in the event that the packet uses * a private memory store that can not be written to then this * can return zero regardless of the capacity and length. * * @return the space left within the buffer to append data to */ public int space() { return packet.space(); } /** * The sequence number represents the order with which this is * to be delivered to the underlying network. This allows safer * transfer of packets in an asynchronous environment where it may * be possible for a packet to be written out of sequence. The * sequence number also determines the order of closure. * * @return this returns an increasing packet sequence number */ public long sequence() { return packet.sequence(); } /** * This represents the capacity of the backing store. The buffer * is full when length is equal to capacity and it can typically * be appended to when the length is less than the capacity. The * only exception is when space returns zero, which * means that the packet can not have bytes appended to it. * * @return this is the capacity of other backing byte storage */ public int capacity() { return packet.capacity(); } /** * This is used to determine how many bytes remain within this * packet. It represents the number of write ready bytes, so if * the length is greater than zero the packet can be written to * a byte channel. When length is zero the packet can be closed. * * @return this is the number of bytes remaining in this packet */ public int length() { return packet.length(); } /** * This is used to that packets can be entered in to a priority * queue such that they are ordered based on their sequence * numbers. Ordering based on sequence numbers ensures that * packets can be remove and inserted back in to the equeue * without concern for othe order of their insertion. * * @param other this is the packet that is to be compared * * @return this is negative is less than otherwise its positive */ public int compareTo(Packet other) { return packet.compareTo(other); } /** * This method is used to extract the contents of the packet in * to a duplicate packet. The purpose of this is to ensure that * when a packet wraps a shared buffer the contents of that * buffer can be drained in to an allocated buffer, resulting * in a packet that can be used without read write conflicts. * * @return this returns the packets contents in a new buffer */ public Packet extract() throws IOException { return packet.extract(); } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @return this returns the bytes sequence as a string object */ public String encode() throws IOException { return packet.encode(); } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @param encoding this is the character set to use for encoding * * @return this returns the bytes sequence as a string object */ public String encode(String encoding) throws IOException { return packet.encode(encoding); } /** * This will append bytes within the given buffer to the packet. * Once invoked the packet will contain the buffer bytes, which * will have been drained from the buffer. This effectively moves * the bytes in the buffer to the end of the packet instance. * * @param buffer this is the buffer containing the bytes * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer buffer) throws IOException { return packet.append(buffer); } /** * This will append bytes within the given buffer to the packet. * Once invoked the packet will contain the buffer bytes, which * will have been drained from the buffer. This effectively moves * the bytes in the buffer to the end of the packet instance. * * @param buffer this is the buffer containing the bytes * @param count this is the number of bytes that should be used * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer buffer, int count) throws IOException { return packet.append(buffer, count); } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel) throws IOException { return packet.write(channel); } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * @param count the number of bytes to write to the channel * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel, int count) throws IOException { return packet.write(channel, count); } /** * This method is used to determine if the buffer is shared with * another thread or service. It is important to know whether a * packet is shared as it tells the writer whether it needs to * block the writing thread while the packet is pending a write * to the socket channel. * * @return true if the buffer is shared with another service */ public boolean isReference() { return packet.isReference(); } /** * The close method for the packet is used to ensure * that any resources occupied by the packet are released. The * resources held by this instance include pooled buffers. If the * packet is not closed on completion then this can result in a * leak of resources within the associated transport. */ public void close() throws IOException { Object top = queue.peek(); if(top != packet) { throw new PacketException("Close out of sequence"); } packet.close(); queue.poll(); } /** * Provides a string representation of the state of the packet. * This can be useful for debugging the state transitions that a * packet will go through when being written and appended to. * * @return this returns a string representation for the packet */ @Override public String toString() { return packet.toString(); } } simple-http-4.1.21/src/org/simpleframework/transport/OperationFactory.java0000644000175000017500000000703011417313373027476 0ustar jamespagejamespage/* * OperationFactory.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import javax.net.ssl.SSLEngine; import org.simpleframework.transport.reactor.Operation; /** * The OperationFactory is used to create operations * for the transport processor. Depending on the configuration of the * pipeline object this will create different operations. Typically * this will create an SSL handshake operation if the pipeline has * an SSLEngine instance. This allows the transport * processor to complete the handshake before handing the transport * to the transporter for processing. * * @author Niall Gallagher */ class OperationFactory { /** * This is the negotiator used to process the created transport. */ private final Negotiator negotiator; /** * This is the threshold for the asynchronous buffers to use. */ private final int limit; /** * Constructor for the OperationFactory object. This * uses the negotiator provided to hand off the created transport * when it has been created. All operations created typically * execute in an asynchronous thread. * * @param negotiator the negotiator used to process transports * @param limit this is the threshold for asynchronous buffers */ public OperationFactory(Negotiator negotiator, int limit) { this.negotiator = negotiator; this.limit = limit; } /** * This method is used to create Operation object to * process the next phase of the negotiation. The operations that * are created using this factory ensure the processing can be * done asynchronously, which reduces the overhead the connection * thread has when handing the pipelines over for processing. * * @param socket this is the pipeline that is to be processed * * @return this returns the operation used for processing */ public Operation getInstance(Socket socket) throws IOException { return getInstance(socket, socket.getEngine()); } /** * This method is used to create Operation object to * process the next phase of the negotiation. The operations that * are created using this factory ensure the processing can be * done asynchronously, which reduces the overhead the connection * thread has when handing the pipelines over for processing. * * @param socket this is the pipeline that is to be processed * @param engine this is the engine used for SSL negotiations * * @return this returns the operation used for processing */ private Operation getInstance(Socket socket, SSLEngine engine) throws IOException { Transport transport = new SocketTransport(socket, negotiator, limit); if(engine != null) { return new Handshake(transport, negotiator); } return new Dispatcher(transport, negotiator); } } simple-http-4.1.21/src/org/simpleframework/transport/SecureTransport.java0000644000175000017500000003110611417313373027352 0ustar jamespagejamespage/* * SecureTransport.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.Map; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.Status; /** * The SecureTransport object provides an implementation * of a transport used to send and receive data over SSL. Data read * from this transport is decrypted using an SSLEngine. * Also, all data is written is encrypted with the same engine. This * ensures that data can be send and received in a transparent way. * * @author Niall Gallagher */ class SecureTransport implements Transport { /** * This is the transport used to send data over the socket. */ private Transport transport; /** * This buffer is used to output the data for the SSL sent. */ private ByteBuffer output; /** * This is the internal buffer used to exchange the SSL data. */ private ByteBuffer input; /** * This is the internal buffer used to exchange the SSL data. */ private ByteBuffer swap; /** * This is the SSL engine used to encrypt and decrypt data. */ private SSLEngine engine; /** * This is used to determine if the transport was closed. */ private boolean closed; /** * This is used to determine if the end of stream was reached. */ private boolean finished; /** * Constructor for the SecureTransport object. This * is used to create a transport for sending and receiving data * over SSL. This must be created with a pipeline that has already * performed the SSL handshake and is read to used. * * @param transport this is the transport to delegate operations to * @param input this is the input buffer used to read the data * @param swap this is the swap buffer to be used for reading */ public SecureTransport(Transport transport, ByteBuffer input, ByteBuffer swap) { this(transport, input, swap, 20480); } /** * Constructor for the SecureTransport object. This * is used to create a transport for sending and receiving data * over SSL. This must be created with a pipeline that has already * performed the SSL handshake and is read to used. * * @param transport this is the transport to delegate operations to * @param input this is the input buffer used to read the data * @param swap this is the swap buffer to be used for reading * @param size this is the size of the buffers to be allocated */ public SecureTransport(Transport transport, ByteBuffer input, ByteBuffer swap, int size) { this.output = ByteBuffer.allocate(size); this.engine = transport.getEngine(); this.transport = transport; this.input = input; this.swap = swap; } /** * This is used to acquire the SSL engine used for HTTPS. If the * pipeline is connected to an SSL transport this returns an SSL * engine which can be used to establish the secure connection * and send and receive content over that connection. If this is * null then the pipeline represents a normal transport. * * @return the SSL engine used to establish a secure transport */ public SSLEngine getEngine() { return engine; } /** * This method is used to get the Map of attributes * by this pipeline. The attributes map is used to maintain details * about the connection. Information such as security credentials * to client details can be placed within the attribute map. * * @return this returns the map of attributes for this pipeline */ public Map getAttributes() { return transport.getAttributes(); } /** * This method is used to acquire the SocketChannel * for the connection. This allows the server to acquire the input * and output streams with which to communicate. It can also be * used to configure the connection and perform various network * operations that could otherwise not be performed. * * @return this returns the socket used by this HTTP pipeline */ public SocketChannel getChannel() { return transport.getChannel(); } /** * This is used to perform a non-blocking read on the transport. * If there are no bytes available on the input buffers then * this method will return zero and the buffer will remain the * same. If there is data and the buffer can be filled then this * will return the number of bytes read. Finally if the socket * is closed this will return a -1 value. * * @param buffer this is the buffer to append the bytes to * * @return this returns the number of bytes that have been read */ public int read(ByteBuffer buffer) throws IOException { if(closed) { throw new TransportException("Transport is closed"); } if(finished) { return -1; } int count = fill(buffer); if(count <= 0) { return process(buffer); } return count; } /** * This is used to perform a non-blocking read on the transport. * If there are no bytes available on the input buffers then * this method will return zero and the buffer will remain the * same. If there is data and the buffer can be filled then this * will return the number of bytes read. * * @param buffer this is the buffer to append the bytes to * * @return this returns the number of bytes that have been read */ private int process(ByteBuffer buffer) throws IOException { int size = swap.position(); if(size >= 0) { swap.compact(); } int space = swap.remaining(); if(space > 0) { size = transport.read(swap); if(size < 0) { finished = true; } } if(size > 0 || space > 0) { swap.flip(); receive(); } return fill(buffer); } /** * This is used to fill the provided buffer with data that has * been read from the secure socket channel. This enables reading * of the decrypted data in chunks that are smaller than the * size of the input buffer used to contain the plain text data. * * @param buffer this is the buffer to append the bytes to * * @return this returns the number of bytes that have been read */ private int fill(ByteBuffer buffer) throws IOException { int space = buffer.remaining(); int count = input.position(); if(count > 0) { if(count > space) { count = space; } } return fill(buffer, count); } /** * This is used to fill the provided buffer with data that has * been read from the secure socket channel. This enables reading * of the decrypted data in chunks that are smaller than the * size of the input buffer used to contain the plain text data. * * @param buffer this is the buffer to append the bytes to * @param count this is the number of bytes that are to be read * * @return this returns the number of bytes that have been read */ private int fill(ByteBuffer buffer, int count) throws IOException { input.flip(); if(count > 0) { count = append(buffer, count); } input.compact(); return count; } /** * This will append bytes within the transport to the given buffer. * Once invoked the buffer will contain the transport bytes, which * will have been drained from the buffer. This effectively moves * the bytes in the buffer to the end of the packet instance. * * @param buffer this is the buffer containing the bytes * @param count this is the number of bytes that should be used * * @return returns the number of bytes that have been moved */ private int append(ByteBuffer buffer, int count) throws IOException { ByteBuffer segment = input.slice(); if(closed) { throw new TransportException("Transport is closed"); } int mark = input.position(); int size = mark + count; if(count > 0) { input.position(size); segment.limit(count); buffer.put(segment); } return count; } /** * This is used to perform a non-blocking read on the transport. * If there are no bytes available on the input buffers then * this method will return zero and the buffer will remain the * same. If there is data and the buffer can be filled then this * will return the number of bytes read. Finally if the socket * is closed this will return a -1 value. */ private void receive() throws IOException { int count = swap.remaining(); while(count > 0) { SSLEngineResult result = engine.unwrap(swap, input); Status status = result.getStatus(); switch(status) { case BUFFER_OVERFLOW: case BUFFER_UNDERFLOW: return; case CLOSED: throw new TransportException("Transport error " + result); } count = swap.remaining(); if(count <= 0) { break; } } } /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @param buffer this is the array of bytes to send to the client */ public void write(ByteBuffer buffer) throws IOException { if(closed) { throw new TransportException("Transport is closed"); } int capacity = output.capacity(); int ready = buffer.remaining(); int length = ready; while(ready > 0) { int size = Math.min(ready, capacity / 2); int mark = buffer.position(); if(length * 2 > capacity) { buffer.limit(mark + size); } send(buffer); output.clear(); ready -= size; } } /** * This method is used to deliver the provided buffer of bytes to * the underlying transport. Depending on the connection type the * array may be encoded for SSL transport or send directly. Any * implementation may choose to buffer the bytes for performance. * * @param buffer this is the array of bytes to send to the client */ private void send(ByteBuffer buffer) throws IOException { SSLEngineResult result = engine.wrap(buffer, output); Status status = result.getStatus(); switch(status){ case BUFFER_OVERFLOW: case BUFFER_UNDERFLOW: case CLOSED: throw new TransportException("Transport error " + status); default: output.flip(); } transport.write(output); } /** * 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(closed) { throw new TransportException("Transport is closed"); } transport.flush(); } /** * This is used to close the sender and the underlying transport. * If a close is performed on the sender then no more bytes can * be read from or written to the transport and the client will * received a connection close on their side. */ public void close() throws IOException { if(!closed) { transport.close(); closed = true; } } } simple-http-4.1.21/src/org/simpleframework/transport/Flusher.java0000644000175000017500000000362311417313372025621 0ustar jamespagejamespage/* * Flusher.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The Flusher object is used to flush data to the * underlying socket. This allows asynchronous writes to the socket * to be managed in such a way that there is order to the way data * is delivered over the socket. This uses a selector to dispatch * flush invocations to the underlying socket when the socket is * read ready. This allows the writing thread to continue without * having to wait for all the data to be written to the socket. * * @author Niall Gallagher * * @see org.simpleframework.transport.Controller */ interface Flusher { /** * Here in this method we schedule a flush when the underlying * writer is write ready. This allows the writer thread to return * without having to fully flush the content to the underlying * transport. This will block if references are queued. */ public void flush() throws IOException; /** * This is used to close the flusher ensuring that all of the * data within the writer will be flushed regardless of the * amount of data within the writer that needs to be written. If * the writer does not block then this waits to be finished. */ public void close() throws IOException; }simple-http-4.1.21/src/org/simpleframework/transport/NegotiationException.java0000644000175000017500000000253011417313373030345 0ustar jamespagejamespage/* * NegotiationException.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; /** * The NegotiationException object is thrown when there * is a problem with a negotiation. Typically this is done thrown if * there is a problem reading or writing to in the negotiation. * * @author Niall Gallagher */ class NegotiationException extends TransportException { /** * Constructor for the NegotiationException object. If * there is a problem sending or reading in a negotiation then it * will throw a negotiation exception to report the error. * * @param message this is the message associated with the error */ public NegotiationException(String message) { super(message); } } simple-http-4.1.21/src/org/simpleframework/transport/Cursor.java0000644000175000017500000001207111417313372025463 0ustar jamespagejamespage/* * Cursor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The Cursor object is used to acquire bytes from a * given source. This provides a cursor style reading of bytes from * a stream in that it will allow the reader to move the cursor back * if the amount of bytes read is too much. Allowing the cursor to * move ensures that excess bytes back be placed back in the stream. *

* This is used when parsing input from a stream as it ensures that * on arrival at a terminal token any excess bytes can be placed * back in to the stream. This allows data to be read efficiently * in large chunks from blocking streams such as sockets. * * @author Niall Gallagher * * @see org.simpleframework.transport.TransportCursor */ public interface 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; /** * 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; /** * 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; /** * 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; /** * 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; /** * 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 reset 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; /** * 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 reset 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; /** * 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; } simple-http-4.1.21/src/org/simpleframework/transport/Handshake.java0000644000175000017500000004232311417313373026100 0ustar jamespagejamespage/* * Handshake.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import static java.nio.channels.SelectionKey.OP_READ; import static java.nio.channels.SelectionKey.OP_WRITE; import static org.simpleframework.transport.Status.CLIENT; import static org.simpleframework.transport.Status.DONE; import static org.simpleframework.transport.Status.SERVER; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SelectableChannel; import java.nio.channels.SocketChannel; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; /** * The Handshake object is used to perform secure SSL * negotiations on a pipeline or Transport. This can * be used to perform an SSL handshake. To perform the negotiation * this uses an SSL engine provided with the pipeline to direct * the conversation. The SSL engine tells the negotiation what is * expected next, whether this is a response to the client or a * message from it. During the negotiation this may need to wait * for either a write ready event or a read ready event. Event * notification is done using the negotiator provided. * * @author Niall Gallagher * * @see org.simpleframework.transport.Negotiator */ class Handshake implements Negotiation { /** * This is the negotiator used to process the secure transport. */ private final Negotiator negotiator; /** * This is the socket channel used to read and write data to. */ private final SocketChannel channel; /** * This is the transport dispatched when the negotiation ends. */ private final Transport transport; /** * This is the output buffer used to generate data to. */ private final ByteBuffer output; /** * This is the input buffer used to read data from the socket. */ private final ByteBuffer input; /** * This is an empty byte buffer used to generate a response. */ private final ByteBuffer empty; /** * This is the SSL engine used to direct the conversation. */ private final SSLEngine engine; /** * Constructor for the Negotiation object. This is * used to create an operation capable of performing negotiations * for SSL connections. Typically this is used to perform request * response negotiations, such as a handshake or termination. * * @param negotiator the negotiator used to check socket events * @param transport the transport to perform the negotiation for */ public Handshake(Transport transport, Negotiator negotiator) { this(transport, negotiator, 20480); } /** * Constructor for the Negotiation object. This is * used to create an operation capable of performing negotiations * for SSL connections. Typically this is used to perform request * response negotiations, such as a handshake or termination. * * @param negotiator the negotiator used to check socket events * @param transport the transport to perform the negotiation for * @param size the size of the buffers used for the negotiation */ public Handshake(Transport transport, Negotiator negotiator, int size) { this.output = ByteBuffer.allocate(size); this.input = ByteBuffer.allocate(size); this.channel = transport.getChannel(); this.engine = transport.getEngine(); this.empty = ByteBuffer.allocate(0); this.negotiator = negotiator; this.transport = transport; } /** * This returns the socket channel for the connected pipeline. It * is this channel that is used to determine if there are bytes * that can be read. When closed this is no longer selectable. * * @return this returns the connected channel for the pipeline */ public SelectableChannel getChannel() { return channel; } /** * This is used to start the negotiation. Once started this will * send a message to the client, once sent the negotiation reads * the response. However if the response is not yet ready this * will schedule the negotiation for a selectable operation * ensuring that it can resume execution when ready. */ public void run() { if(engine != null) { engine.setUseClientMode(false); input.flip(); } begin(); } /** * This is used to terminate the negotiation. This is excecuted * when the negotiation times out. When the negotiation expires it * is rejected by the negotiator and must be canceled. Canceling * is basically termination of the connection to free resources. */ public void cancel() { try { transport.close(); } catch(Exception e) { return; } } /** * This is used to start the negotation. Once started this will * send a message to the client, once sent the negotiation reads * the response. However if the response is not yet ready this * will schedule the negotiation for a selectable operation * ensuring that it can resume execution when ready. */ private void begin() { try { resume(); } catch(Exception e) { return; } } /** * This is the main point of execution within the negotiation. It * is where the negotiation is performed. Negotiations are done * by performing a request response flow, governed by the SSL * engine associated with the pipeline. Typically the client is * the one to initiate the handshake and the server initiates the * termination sequence. This may be executed several times * depending on whether reading or writing blocks. */ public void resume() throws IOException { Runnable task = process(); if(task != null) { task.run(); } } /** * This is the main point of execution within the negotiation. It * is where the negotiation is performed. Negotiations are done * by performing a request response flow, governed by the SSL * engine associated with the pipeline. Typically the client is * the one to initiate the handshake and the server initiates the * termination sequence. This may be executed several times * depending on whether reading or writing blocks. * * @return this returns a task used to execute the next phase */ private Runnable process() throws IOException { Status require = exchange(); if(require == CLIENT) { return new Client(this); } if(require == SERVER) { return new Server(this); } return new Done(this); } /** * This is the main point of execution within the negotiation. It * is where the negotiation is performed. Negotiations are done * by performing a request response flow, governed by the SSL * engine associated with the pipeline. Typically the client is * the one to initiate the handshake and the server initiates the * termination sequence. This may be executed several times * depending on whether reading or writing blocks. * * @return this returns what is expected next in the negotiation */ private Status exchange() throws IOException { HandshakeStatus status = engine.getHandshakeStatus(); switch(status){ case NEED_WRAP: return write(); case NOT_HANDSHAKING: case NEED_UNWRAP: return read(); } return DONE; } /** * This is used to perform the read part of the negotiation. The * read part is where the client sends information to the server * and the server consumes the data and determines what action * to take. Typically it is the SSL engine that determines what * action is to be taken depending on the client data. * * @return the next action that should be taken by the handshake */ private Status read() throws IOException { return read(5); } /** * This is used to perform the read part of the negotiation. The * read part is where the client sends information to the server * and the server consumes the data and determines what action * to take. Typically it is the SSL engine that determines what * action is to be taken depending on the client data. * * @param count this is the number of times a read can repeat * * @return the next action that should be taken by the handshake */ private Status read(int count) throws IOException { while(count > 0) { SSLEngineResult result = engine.unwrap(input, output); HandshakeStatus status = result.getHandshakeStatus(); switch(status) { case NOT_HANDSHAKING: return DONE; case NEED_WRAP: return SERVER; case FINISHED: case NEED_UNWRAP: return read(count-1); case NEED_TASK: execute(); } } return CLIENT; } /** * This is used to perform the write part of the negotiation. The * read part is where the server sends information to the client * and the client interprets the data and determines what action * to take. After a write the negotiation typically completes or * waits for the next response from the client. * * @return the next action that should be taken by the handshake */ private Status write() throws IOException { return write(5); } /** * This is used to perform the write part of the negotiation. The * read part is where the server sends information to the client * and the client interprets the data and determines what action * to take. After a write the negotiation typically completes or * waits for the next response from the client. * * @param count this is the number of times a read can repeat * * @return the next action that should be taken by the handshake */ private Status write(int count) throws IOException { while(count > 0) { SSLEngineResult result = engine.wrap(empty, output); HandshakeStatus status = result.getHandshakeStatus(); switch(status) { case NOT_HANDSHAKING: case FINISHED: case NEED_UNWRAP: return SERVER; case NEED_WRAP: return write(count-1); case NEED_TASK: execute(); } } return SERVER; } /** * This is used to execute the delegated tasks. These tasks are * used to digest the information received from the client in * order to generate a response. This may need to execute several * tasks from the associated SSL engine. */ private void execute() throws IOException { while(true) { Runnable task = engine.getDelegatedTask(); if(task == null) { break; } task.run(); } } /** * This is used to receive data from the client. If at any * point during the negotiation a message is required that * can not be read immediately this is used to asynchronously * read the data when a select operation is signaled. * * @return this returns true when the message has been read */ public boolean receive() throws IOException { int count = input.capacity(); if(count > 0) { input.compact(); } int size = channel.read(input); if(size < 0) { throw new TransportException("Client closed connection"); } if(count > 0) { input.flip(); } return size > 0; } /** * Here we attempt to send all data within the output buffer. If * all of the data is delivered to the client then this will * return true. If however there is content yet to be sent to * the client then this returns false, telling the negotiation * that in order to resume it must attempt to send the content * again after a write ready operation on the underlying socket. * * @return this returns true if all of the content is delivered */ public boolean send() throws IOException { int require = output.position(); int count = 0; if(require > 0) { output.flip(); } while(count < require) { int size = channel.write(output); if(size <= 0) { break; } count += size; } if(require > 0) { output.compact(); } return count == require; } /** * This method is invoked when the negotiation is done and the * next phase of the connection is to take place. This will * be invoked when the SSL handshake has completed and the new * secure transport is to be handed to the processor. */ public void commit() throws IOException { Transport secure = new SecureTransport(transport, output, input); if(negotiator != null) { negotiator.process(secure); } } /** * The Done task is used to transfer the transport * created to the negotiator. This is executed when the SSL * handshake is completed. It allows the transporter to use the * newly created transport to read and write in plain text and * to have the SSL transport encrypt and decrypt transparently. * * @author Niall Gallagher */ private class Done extends Task { /** * Constructor for the Done task. This is used to * pass the transport object object to the negotiator when the * SSL handshake has completed. * * @param state this is the underlying negotiation to use */ public Done(Negotiation state) { super(state, negotiator, OP_READ); } /** * This is used to execute the task. It is up to the specific * task implementation to decide what to do when executed. If * the task needs to read or write data then it can attempt * to perform the read or write, if it incomplete the it can * be scheduled for execution with the reactor. */ @Override public void execute() throws IOException{ state.commit(); } } /** * The Client task is used to schedule the negotiation * for a read operation. This allows the negotiation to receive any * messages generated by the client asynchronously. Once this has * completed then it will resume the negotiation. * * @author Niall Gallagher */ private class Client extends Task { /** * Constructor for the Client task. This is used * to create a task which will schedule a read operation for * the negotiation. When the operation completes this will * resume the negotiation. * * @param state this is the negotiation object that is used */ public Client(Negotiation state) { super(state, negotiator, OP_READ); } /** * This method is used to determine if the task is ready. This * is executed when the select operation is signaled. When this * is true the the task completes. If not then this will * schedule the task again for the specified select operation. * * @return this returns true when the task has completed */ @Override protected boolean ready() throws IOException { return state.receive(); } } /** * The Server is used to schedule the negotiation * for a write operation. This allows the negotiation to send any * messages generated during the negotiation asynchronously. Once * this has completed then it will resume the negotiation. * * @author Niall Gallagher */ private class Server extends Task { /** * Constructor for the Server task. This is used * to create a task which will schedule a write operation for * the negotiation. When the operation completes this will * resume the negotiation. * * @param state this is the negotiation object that is used */ public Server(Negotiation state) { super(state, negotiator, OP_WRITE); } /** * This method is used to determine if the task is ready. This * is executed when the select operation is signaled. When this * is true the the task completes. If not then this will * schedule the task again for the specified select operation. * * @return this returns true when the task has completed */ @Override protected boolean ready() throws IOException { return state.send(); } } } simple-http-4.1.21/src/org/simpleframework/transport/TransportCursor.java0000644000175000017500000002050511417313373027402 0ustar jamespagejamespage/* * TransportCursor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The TransportCursor object represents a cursor that * can read and buffer data from an underlying transport. If the * number of bytes read from the cursor is more than required for * the HTTP request then those bytes can be pushed back in to the * cursor using the reset method. This will only allow * the last read to be reset within the cursor safely. * * @author Niall Gallagher * * @see org.simpleframework.transport.Transport */ public class TransportCursor implements Cursor { /** * This is the source for the bytes read by this cursor object. */ private Source source; /** * This is the buffer used to collect the bytes pushed back. */ private byte[] buffer; /** * This is the number of bytes that have been pushed back. */ private int count; /** * This is the mark from the last read from this cursor object. */ private int mark; /** * This is the position to read data from the internal buffer. */ private int pos; /** * This is the maximum number of bytes that can be pushed back. */ private int limit; /** * Constructor for the TransportCursor object. This * requires a transport to read the bytes from. By default this * will create a buffer of of the specified size to read the * input in to which enabled bytes to be buffered internally. * * @param transport this is the underlying transport to use */ public TransportCursor(Transport transport) { this(transport, 2048); } /** * Constructor for the TransportCursor object. This * requires a transport to read the bytes from. By default this * will create a buffer of of the specified size to read the * input in to which enabled bytes to be buffered internally. * * @param transport this is the underlying transport to use * @param size this is the size of the internal buffer to use */ public TransportCursor(Transport transport, int size) { this.source = new TransportSource(transport, size); this.buffer = new byte[0]; this.limit = size; } /** * 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 there is nothing more to be read from this */ public boolean isOpen() throws IOException { return source.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 { return ready() > 0; } /** * 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 { if(count > 0) { return count; } return source.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 { if(count <= 0) { mark = pos; return source.read(data, off, len); } int size = Math.min(count, len); if(size > 0) { System.arraycopy(buffer, pos, data, off, size); mark = pos; pos += size; count -= size; } return size; } /** * 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 reset 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 reset 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 { int size = buffer.length; if(size < len + count) { expand(len + count); } int start = pos - len; if(len > 0) { System.arraycopy(data, off, buffer, start, len); mark = start; pos = start; count += len; } } /** * This is used to ensure that there is enough space in the buffer * to allow for more bytes to be added. If the buffer is already * larger than the required capacity the this will do nothing. * * @param capacity the minimum size needed for the buffer */ private void expand(int capacity) throws IOException { if(capacity > limit) { throw new TransportException("Capacity limit exceeded"); } byte[] temp = new byte[capacity]; int start = capacity - count; int shift = pos - mark ; if(count > 0) { System.arraycopy(buffer, pos, temp, start, count); } pos = capacity - count; mark = pos - shift; buffer = temp; } /** * 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 size this is the number of bytes to reset back * * @return this is the number of bytes that have been reset */ public int reset(int size) throws IOException { if(mark == pos) { return source.reset(size); } if(pos - size < mark) { size = pos - mark; } if(size > 0) { count += size; pos -= size; } return size; } } simple-http-4.1.21/src/org/simpleframework/transport/Appender.java0000644000175000017500000003225511417313373025753 0ustar jamespagejamespage/* * Appender.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.ByteChannel; import java.nio.charset.Charset; /** * The Appender represents a packet that wraps a pooled * byte buffer. This implementation provides write access to the * underlying buffer, so it can modify its contents if required. This * means that the append methods can add data to the * buffer, and that the buffer can be compacted if required. * * @author Niall Gallagher */ class Appender implements Packet { /** * This is the buffer used to store the contents of the packet. */ private ByteBuffer buffer; /** * This is the manager object that is used to recycle buffers. */ private Recycler manager; /** * This represents the sequence number used by this packet. */ private long sequence; /** * This determines whether the packet has been closed already. */ private boolean closed; /** * Constructor for the Appender object. This creates * a packet with a recyclable buffer. This appender can not be * recycled as it is provided with no recycler. * * @param buffer this is the buffer used by this packet * @param sequence this is the unique sequence number for this */ public Appender(ByteBuffer buffer, long sequence) { this(buffer, null, sequence); } /** * Constructor for the Appender object. This creates * a packet with a recyclable buffer. The buffer provided can be * modified up until such point as it is recycled. To recycle the * buffer the packet must be closed. * * @param buffer this is the buffer used by this packet * @param manager this is the buffer pool to pass the buffer to * @param sequence this is the unique sequence number for this */ public Appender(ByteBuffer buffer, Recycler manager, long sequence) { this.sequence = sequence; this.manager = manager; this.buffer = buffer; } /** * The sequence number represents the order with which this is * to be delivered to the underlying network. This allows safer * transfer of packets in an asynchronous environment where it may * be possible for a packet to be written out of sequence. The * sequence number also determines the order of closure. * * @return this returns an increasing packet sequence number */ public long sequence() { return sequence; } /** * This is used to determine how much space is left to append * data to this packet. This is typically equivalent to capacity * minus the length. However in the event that the packet uses * a private memory store that can not be written to then this * can return zero regardless of the capacity and length. * * @return the space left within the buffer to append data to */ public int space() { if(closed) { return 0; } return buffer.remaining(); } /** * This represents the capacity of the backing store. The buffer * is full when length is equal to capacity and it can typically * be appended to when the length is less than the capacity. The * only exception is when space returns zero, which * means that the packet can not have bytes appended to it. * * @return this is the capacity of other backing byte storage */ public int capacity() { if(closed) { return 0; } return buffer.capacity(); } /** * This is used to determine how mnay bytes remain within this * packet. It represents the number of write ready bytes, so if * the length is greater than zero the packet can be written to * a byte channel. When length is zero the packet can be closed. * * @return this is the number of bytes remaining in this packet */ public int length() { if(closed) { return 0; } return capacity() - space(); } /** * This is used to that packets can be entered in to a priority * queue such that they are ordered based on their sequence * numbers. Ordering based on sequence numbers ensures that * packets can be remove and inserted back in to the equeue * without concern for othe order of their insertion. * * @param packet this is the packet that is to be compared * * @return this is negative is less than otherwise its positive */ public int compareTo(Packet packet) { long other = packet.sequence(); if(other > sequence) { return -1; } if(sequence > other) { return 1; } return 0; } /** * This method is used to extract the contents of the packet in * to a duplicate packet. The purpose of this is to ensure that * when a packet wraps a shared buffer the contents of that * buffer can be drained in to an allocated buffer, resulting * in a packet that can be used without read write conflicts. * * @return this returns the packets contents in a new buffer */ public Packet extract() throws IOException { return this; } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @return this returns the bytes sequence as a string object */ public String encode() throws IOException { return encode("ISO-8859-1"); } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @param encoding this is the character set to use for encoding * * @return this returns the bytes sequence as a string object */ public String encode(String encoding) throws IOException { ByteBuffer segment = buffer.duplicate(); if(segment != null) { segment.flip(); } return encode(encoding, segment); } /** * This is used to encode the underlying byte sequence to text. * Converting the byte sequence to text can be useful when either * debugging what exactly is being sent. Also, for transports * that require string delivery of packets this can be used. * * @param encoding this is the character set to use for encoding * @param segment this is the buffer that is to be encoded * * @return this returns the bytes sequence as a string object */ private String encode(String encoding, ByteBuffer segment) throws IOException { Charset charset = Charset.forName(encoding); CharBuffer text = charset.decode(segment); return text.toString(); } /** * This will append bytes within the given buffer to the packet. * Once invoked the packet will contain the buffer bytes, which * will have been drained from the buffer. This effectively moves * the bytes in the buffer to the end of the packet instance. * * @param data this is the buffer containing the bytes * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer data) throws IOException { int require = data.remaining(); int space = space(); if(require > space) { require = space; } return append(data, require); } /** * This will append bytes within the given buffer to the packet. * Once invoked the packet will contain the buffer bytes, which * will have been drained from the buffer. This effectively moves * the bytes in the buffer to the end of the packet instance. * * @param data this is the buffer containing the bytes * @param count this is the number of bytes that should be used * * @return returns the number of bytes that have been moved */ public int append(ByteBuffer data, int count) throws IOException { ByteBuffer segment = data.slice(); if(closed) { throw new PacketException("Packet has been closed"); } int mark = data.position(); int size = mark + count; if(count > 0) { data.position(size); segment.limit(count); buffer.put(segment); } return count; } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel) throws IOException { int size = length(); if(size <= 0) { return 0; } return write(channel, size); } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * @param count the number of bytes to write to the channel * * @return this returns the number of bytes that were written */ public int write(ByteChannel channel, int count) throws IOException { if(closed) { throw new PacketException("Packet has been closed"); } if(count > 0) { buffer.flip(); } else { return 0; } return write(channel, buffer); } /** * This write method will write the contents of the packet to the * provided byte channel. If the whole packet can be be written * then this will simply return the number of bytes that have. * The number of bytes remaining within the packet after a write * can be acquired from the length method. Once all * of the bytes are written the packet must be closed. * * @param channel this is the channel to write the packet to * @param segment this is the buffer that is to be written * * @return this returns the number of bytes that were written */ private int write(ByteChannel channel, ByteBuffer segment) throws IOException { int require = segment.remaining(); int count = 0; while(count < require) { int size = channel.write(segment); if(size <= 0) { break; } count += size; } if(count >= 0) { segment.compact(); } return count; } /** * The close method for the packet is used to ensure * that any resources occupied by the packet are released. The * resources held by this instance include pooled buffers. If the * packet is not closed on completion then this can result in a * leak of resources within the associated transport. */ public void close() { if(manager != null) { manager.recycle(buffer); } manager = null; closed = true; } /** * This method is used to determine if the buffer is shared with * another thread or service. It is important to know whether a * packet is shared as it tells the writer whether it needs to * block the writing thread whilst the packet is pending a write * to the socket channel. * * @return true if the buffer is shared with another service */ public boolean isReference() { return false; } /** * Provides a string representation of the state of the packet. * This can be useful for debugging the state transitions that a * packet will go through when being written and appended to. * * @return this returns a string representation for the packet */ @Override public String toString() { return String.format("%s %s", sequence, buffer); } } simple-http-4.1.21/src/org/simpleframework/transport/Scheduler.java0000644000175000017500000001240611417313373026127 0ustar jamespagejamespage/* * Scheduler.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import static java.nio.channels.SelectionKey.OP_WRITE; import java.io.IOException; import org.simpleframework.transport.reactor.Operation; import org.simpleframework.transport.reactor.Reactor; /** * The Scheduler object is used to schedule a task for * execution when it is write ready. This is used by the socket * flusher to ensure that the writing thread can be blocked until * such time as all the bytes required to be written are written. *

* All methods are invoked by a SocketFlusher object * which is synchronized. This ensures that the methods of the * scheduler are thread safe in that only one thread will access * them at any given time. The lock used by the socket flusher can * thus be safely as it will be synchronized on by the flusher. * * @author Niall Gallagher * * @see org.simpleframework.transport.SocketFlusher */ class Scheduler { /** * This is the operation that is scheduled for execution. */ private Operation task; /** * This is the reactor to used to execute the operation. */ private Reactor reactor; /** * This is the lock that is used to signal a blocked thread. */ private Object lock; /** * This is used to determine if the scheduler is running. */ private volatile boolean running; /** * This is used to determine if the scheduler is interrupted. */ private volatile boolean closed; /** * Constructor for the Scheduler object. This is * used to create a scheduler that will execute the provided * task when the associated socket is write ready. * * @param reactor this is the rector used to schedule execution * @param task this is the task that is executed when writable * @param lock this is the lock used to signal blocking threads */ public Scheduler(Reactor reactor, Operation task, Object lock) { this.reactor = reactor; this.task = task; this.lock = lock; } /** * This is used to repeat schedule the operation for execution. * This is executed if the operation has not fully completed * its task. If the scheduler is not in a running state then * this will not schedule the task for a repeat execution. */ public void repeat() throws IOException { if(closed) { throw new TransportException("Socket closed"); } if(running) { reactor.process(task, OP_WRITE); } } /** * This is used to schedule the task for execution. If this is * given a boolean true to indicate that it wishes to block * then this will block the calling thread until such time as * the ready method is invoked. * * @param block indicates whether the thread should block */ public void schedule(boolean block) throws IOException { if(closed) { throw new TransportException("Socket closed"); } if(!running) { reactor.process(task, OP_WRITE); running = true; } if(block) { listen(); } } /** * This is used to listen for a notification from the reactor to * tell the thread that the write operation has completed. If * the thread is interrupted upon this call then this will throw * an IOException with the root cause. */ private void listen() throws IOException { try { if(!closed) { lock.wait(120000); } } catch(Exception e) { throw new TransportException("Schedule error", e); } if(closed) { throw new TransportException("Socket closed"); } } /** * This is used to notify any waiting threads that they no longer * need to wait. This is used when the flusher no longer needs * the waiting thread to block. Such an occurrence happens when * all shared data has been written or has been duplicated. */ public void release() { lock.notifyAll(); } /** * This is used to signal any blocking threads to wake up. When * this is invoked blocking threads are signaled and they can * return. This is typically done when the task has finished. */ public void ready() { lock.notifyAll(); running = false; } /** * This is used to close the scheduler when the reactor is * closed by the server. An close will happen when the server * has been shutdown, it ensures there are no threads lingering * waiting for a notification when the reactor has closed. */ public void close() { lock.notifyAll(); closed = true; } } simple-http-4.1.21/src/org/simpleframework/transport/Recycler.java0000644000175000017500000000265211417313373025763 0ustar jamespagejamespage/* * Recycler.java February 2008 * * Copyright (C) 2008, Niall Gallagher * * 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.transport; import java.nio.ByteBuffer; /** * The Recycler interface is used to represent a pool * of buffers that accepts used instances for recycling. This allows * buffers to be passed back in to a pool implementation when the * buffer has been used. Such occasions are when packets close. * * @author Niall Gallagher */ interface Recycler { /** * This method is used to recycle the buffer. Invoking this with * a buffer instance will pass the buffer back in to the pool. * Once passed back in to the pool the buffer should no longer * be used as it may affect future uses of the buffer. * * @param buffer this is the buffer that is to be recycled */ public void recycle(ByteBuffer buffer); } simple-http-4.1.21/src/org/simpleframework/transport/Source.java0000644000175000017500000001000511417313373025442 0ustar jamespagejamespage/* * Source.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; /** * The Source object is used to acquire bytes from a * given source. This provides a cursor style reading of bytes from * a stream in that it will allow the reader to move the cursor back * if the amount of bytes read is too much. Allowing the cursor to * move ensures that excess bytes can be placed back in the stream. *

* This is used when parsing input from a stream as it ensures that * on arrival at a terminal token any excess bytes can be placed * back in to the stream. This allows data to be read efficiently * in large chunks from blocking streams such as sockets. * * @author Niall Gallagher * * @see org.simpleframework.transport.Cursor */ interface Source { /** * Determines whether the source is still open. The source is * considered open if there are still bytes to read. If there is * still bytes buffered and the underlying transport is closed * then the source is still considered open. * * @return true if the read method does not return a -1 value */ public boolean isOpen() throws IOException; /** * Determines whether the source is ready for reading. When the * source 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; /** * 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 source may perform a blocking read. * * @return the number of bytes that can be read without blocking */ public int ready() throws IOException; /** * 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 source */ public int read(byte[] data) throws IOException; /** * 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 source */ public int read(byte[] data, int off, int len) throws IOException; /** * Moves the source 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; } simple-http-4.1.21/src/org/simpleframework/transport/reactor/0000755000175000017500000000000011767603362025012 5ustar jamespagejamespagesimple-http-4.1.21/src/org/simpleframework/transport/reactor/PartitionDistributor.java0000644000175000017500000001235711417313373032061 0ustar jamespagejamespage/* * PartitionDistributor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.io.IOException; import java.nio.channels.SelectableChannel; import java.util.concurrent.Executor; /** * The PartitionDistributor object is a distributor that * partitions the selection process in to several threads. Each of * the threads has a single selector, and operations are distributed * amongst the threads using the hash code of the socket. Partitions * ensure that several selector threads can share a higher load and * respond to a more I/O events. * * @author Niall Gallagher */ class PartitionDistributor implements Distributor { /** * This contains the distributors that represent a partition. */ private final Distributor[] list; /** * Constructor for the PartitionDistributor object. * This will create a distributor that partitions the operations * amongst a pool of selectors using the channels hash code. * * @param executor this is the executor used to run operations * @param count this is the number of partitions to be used */ public PartitionDistributor(Executor executor, int count) throws IOException { this(executor, count, 120000); } /** * Constructor for the PartitionDistributor object. * This will create a distributor that partitions the operations * amongst a pool of selectors using the channels hash code. * * @param executor this is the executor used to run operations * @param count this is the number of partitions to be used * @param expiry this is the expiry duration that is to be used */ public PartitionDistributor(Executor executor, int count, long expiry) throws IOException { this.list = new Distributor[count]; this.start(executor, expiry); } /** * This is used to create the partitions that represent a thread * used for selection. Operations will index to a particular one * using the hash code of the operations channel. If there is only * one partition all operations will index to the partition. * * @param executor the executor used to run the operations * @param expiry this is the expiry duration that is to be used */ private void start(Executor executor, long expiry) throws IOException { for(int i = 0; i < list.length; i++) { list[i] = new ActionDistributor(executor, true, expiry); } } /** * This is used to process the Operation object. This * will wake up the selector if it is currently blocked selecting * and register the operations associated channel. Once the * selector is awake it will acquire the operation from the queue * and register the associated SelectableChannel for * selection. The operation will then be executed when the channel * is ready for the interested I/O events. * * @param task this is the task that is scheduled for distribution * @param require this is the bit-mask value for interested events */ public void process(Operation task, int require) throws IOException { int length = list.length; if(length == 1) { list[0].process(task, require); } else { process(task, require, length); } } /** * This is used to process the Operation object. This * will wake up the selector if it is currently blocked selecting * and register the operations associated channel. Once the * selector is awake it will acquire the operation from the queue * and register the associated SelectableChannel for * selection. The operation will then be executed when the channel * is ready for the interested I/O events. * * @param task this is the task that is scheduled for distribution * @param require this is the bit-mask value for interested events * @param length this is the number of distributors to hash with */ private void process(Operation task, int require, int length) throws IOException { SelectableChannel channel = task.getChannel(); int hash = channel.hashCode(); list[hash % length].process(task, require); } /** * This is used to close the distributor such that it cancels all * of the registered channels and closes down the selector. This * is used when the distributor is no longer required, after the * close further attempts to process operations will fail. */ public void close() throws IOException { for(Distributor entry : list) { entry.close(); } } } simple-http-4.1.21/src/org/simpleframework/transport/reactor/Reactor.java0000644000175000017500000000601411417313373027245 0ustar jamespagejamespage/* * Reactor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.io.IOException; /** * The Reactor interface is used to describe an object * that is used to schedule asynchronous I/O operations. An operation * is performed by handing it to the reactor, which will determine * if an interested event has occurred. This allows the operation to * perform the task in a manner that does not block. *

* Implementing an Operation object requires that the * operation itself is aware of the I/O task it is performing. For * example, if the operation is concerned with reading data from the * underlying channel then the operation should perform the read, if * there is more data required then that operation to register with * the reactor again to receive further notifications. * * @author Niall Gallagher * * @see org.simpleframework.transport.reactor.Operation */ public interface Reactor { /** * This method is used to execute the provided operation without * the need to specifically check for I/O events. This is used if * the operation knows that the SelectableChannel is * ready, or if the I/O operation can be performed without knowing * if the channel is ready. Typically this is an efficient means * to perform a poll rather than a select on the channel. * * @param task this is the task to execute immediately */ public void process(Operation task) throws IOException; /** * This method is used to execute the provided operation when there * is an I/O event that task is interested in. This will used the * operations SelectableChannel object to determine * the events that are ready on the channel. If this reactor is * interested in any of the ready events then the task is executed. * * @param task this is the task to execute on interested events * @param require this is the bitmask value for interested events */ public void process(Operation task, int require) throws IOException; /** * This is used to stop the reactor so that further requests to * execute operations does nothing. This will clean up all of * the reactors resources and unregister any operations that are * currently awaiting execution. This should be used to ensure * any threads used by the reactor gracefully stop. */ public void stop() throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/reactor/ActionDistributor.java0000644000175000017500000004073411417313373031325 0ustar jamespagejamespage/* * ActionDistributor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.io.IOException; import java.nio.channels.Channel; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.concurrent.Executor; import org.simpleframework.util.thread.Daemon; /** * The ActionDistributor is used to execute operations * that have an interested I/O event ready. This acts much like a * scheduler would in that it delays the execution of the operations * until such time as the associated SelectableChannel * has an interested I/O event ready. *

* This distributor has two modes, one mode is used to cancel the * channel once an I/O event has occurred. This means that the channel * is removed from the Selector so that the selector * does not break when asked to select again. Canceling the channel * is useful when the operation execution may not fully read the * payload or when the operation takes a significant amount of time. * * @see org.simpleframework.transport.reactor.ExecutorReactor */ class ActionDistributor extends Daemon implements Distributor { /** * This is the queue that is used to provide the operations. */ private ActionQueue ready; /** * This is used to determine the operations that need canceling. */ private ChannelMap table; /** * This is used to execute the operations that are ready. */ private Executor executor; /** * This is the selector used to select for interested events. */ private Selector selector; /** * This is used to signal when the distributor has closed. */ private Latch latch; /** * This is the duration in milliseconds the operation expires in. */ private long expiry; /** * This is time in milliseconds when the next expiry will occur. */ private long update; /** * This is used to determine the mode the distributor uses. */ private boolean cancel; /** * This is used to determine when the distributor is closed. */ private volatile boolean dead; /** * Constructor for the ActionDistributor object. This * will create a distributor that distributes operations when those * operations show that they are ready for a given I/O event. The * interested I/O events are provided as a bitmask taken from the * actions of the SelectionKey. Distribution of the * operations is passed to the provided executor object. * * @param executor this is the executor used to execute operations */ public ActionDistributor(Executor executor) throws IOException { this(executor, true); } /** * Constructor for the ActionDistributor object. This * will create a distributor that distributes operations when those * operations show that they are ready for a given I/O event. The * interested I/O events are provided as a bitmask taken from the * actions of the SelectionKey. Distribution of the * operations is passed to the provided executor object. * * @param executor this is the executor used to execute operations * @param cancel should the channel be removed from selection */ public ActionDistributor(Executor executor, boolean cancel) throws IOException { this(executor, cancel, 120000); } /** * Constructor for the ActionDistributor object. This * will create a distributor that distributes operations when those * operations show that they are ready for a given I/O event. The * interested I/O events are provided as a bitmask taken from the * actions of the SelectionKey. Distribution of the * operations is passed to the provided executor object. * * @param executor this is the executor used to execute operations * @param cancel should the channel be removed from selection * @param expiry this the maximum idle time for an operation */ public ActionDistributor(Executor executor, boolean cancel, long expiry) throws IOException { this.selector = Selector.open(); this.table = new ChannelMap(); this.ready = new ActionQueue(); this.latch = new Latch(); this.executor = executor; this.cancel = cancel; this.expiry = expiry; this.start(); } /** * Performs the execution of the distributor. Each distributor runs * on an asynchronous thread to the Reactor which is * used to perform the selection on a set of channels. Each time * there is a new operation to be processed this will take the * operation from the ready queue, cancel all outstanding channels, * and register the operations associated channel for selection. */ public void run() { execute(); purge(); } /** * Performs the execution of the distributor. Each distributor runs * on an asynchronous thread to the Reactor which is * used to perform the selection on a set of channels. Each time * there is a new operation to be processed this will take the * operation from the ready queue, cancel all outstanding channels, * and register the operations associated channel for selection. */ private void execute() { while(!dead) { try { register(); cancel(); expire(); distribute(); } catch(Exception e) { continue; } } } /** * This will purge all the actions from the distributor when the * distributor ends. If there are any threads waiting on the close * to finish they are signaled when all operations are purged. * This will allow them to return ensuring no operations linger. */ private void purge() { try { register(); cancel(); drain(); } catch(Exception e) { return; } } /** * This is used to process the Operation object. This * will wake up the selector if it is currently blocked selecting * and register the operations associated channel. Once the * selector is awake it will acquire the operation from the queue * and register the associated SelectableChannel for * selection. The operation will then be executed when the channel * is ready for the interested I/O events. * * @param task this is the task that is scheduled for distribution * @param require this is the bit-mask value for interested events */ public void process(Operation task, int require) throws IOException { Action action = new ExecuteAction(task, require, expiry); if(dead) { throw new IOException("Distributor is closed"); } ready.offer(action); selector.wakeup(); } /** * This is used to close the distributor such that it cancels all * of the registered channels and closes down the selector. This * is used when the distributor is no longer required, after the * close further attempts to process operations will fail. */ public void close() throws IOException { dead = true; selector.wakeup(); latch.close(); } /** * Here we perform an expire which will take all of the registered * sockets and expire it. This ensures that the operations can be * executed within the executor and the cancellation of the sockets * can be performed. Once this method has finished then all of * the operations will have been scheduled for execution. */ private void drain() throws IOException { Set set = selector.keys(); for(SelectionKey key : set) { expire(key, Long.MAX_VALUE); } selector.close(); latch.signal(); } /** * This method is used to expire registered operations that remain * idle within the selector. Operations specify a time at which * point they wish to be canceled if the I/O event they wait on * has not arisen. This will enables the canceled operation to be * canceled so that the resources it occupies can be released. */ private void expire() throws IOException { Set set = selector.keys(); if(cancel) { long time = System.currentTimeMillis(); if(update <= time) { for(SelectionKey key : set) { expire(key, time); } update = time +10000; } } } /** * This method is used to expire registered operations that remain * idle within the selector. Operations specify a time at which * point they wish to be canceled if the I/O event they wait on * has not arisen. This will enables the canceled operation to be * canceled so that the resources it occupies can be released. * * @param key this is the selection key for the operation */ private void expire(SelectionKey key, long time) throws IOException { Action task = (Action)key.attachment(); if(task != null) { long expiry = task.getExpiry(); if(expiry < time) { expire(key, task); } } } /** * This method is used to expire registered operations that remain * idle within the selector. Operations specify a time at which * point they wish to be canceled if the I/O event they wait on * has not arisen. This will enables the canceled operation to be * canceled so that the resources it occupies can be released. * * @param key this is the selection key for the operation * @param action this is the actual action to be canceled */ private void expire(SelectionKey key, Action action) throws IOException { Action cancel = new CancelAction(action); if(key != null) { key.attach(cancel); key.cancel(); } process(key); } /** * This is used to cancel any selection keys that have previously * been selected with an interested I/O event. Performing a cancel * here ensures that on a the next select the associated channel * is not considered, this ensures the select does not break. */ private void cancel() throws IOException { Collection list = table.values(); for(SelectionKey key : list) { key.cancel(); } table.clear(); } /** * Here all the enqueued Operation objects will be * registered for selection. Each operations channel is used for * selection on the interested I/O events. Once the I/O event * occurs for the channel the operation is scheduled for execution. */ private void register() throws IOException { while(!ready.isEmpty()) { Action action = ready.poll(); if(action != null) { register(action); } } } /** * Here the specified Operation object is registered * with the selector. If the associated channel had previously * been canceled it is removed from the cancel map to ensure it * is not removed from the selector when cancellation is done. * * @param action this is the operation that is to be registered */ private void register(Action action) throws IOException { int require = action.getInterest(); register(action, require); } /** * Here the specified Operation object is registered * with the selector. If the associated channel had previously * been canceled it is removed from the cancel map to ensure it * is not removed from the selector when cancellation is done. * * @param action this is the operation that is to be registered * @param require this is the bit-mask value for interested events */ private void register(Action action, int require) throws IOException { SelectableChannel channel = action.getChannel(); SelectionKey key = table.remove(channel); if(key != null) { key.interestOps(require); key.attach(action); } else { if(channel.isOpen()) { select(channel, require).attach(action); } } } /** * This method is used to perform an actual select on a channel. It * will register the channel with the internal selector using the * required I/O event bit mask. In order to ensure that selection * is performed correctly the provided channel must be connected. * * @param channel this is the channel to register for selection * @param require this is the I/O bit mask that is required * * @return this returns the selection key used for selection */ private SelectionKey select(SelectableChannel channel, int require) throws IOException { return channel.register(selector, require); } /** * This method is used to perform the select and if required queue * the operations that are ready for execution. If the selector * is woken up without any ready channels then this will return * quietly. If however there are a number of channels ready to be * processed then they are handed to the executor object and * marked as ready for cancellation. */ private void distribute() throws IOException { if(selector.select(5000) > 0) { if(!dead) { process(); } } } /** * This will iterate over the set of selection keys and process each * of them. The Operation associated with the selection * key is handed to the executor to perform the channel operation. * Also, if configured to cancel, this method will add the channel * and the associated selection key to the cancellation map. */ private void process() throws IOException{ Set keys = selector.selectedKeys(); Iterator ready = keys.iterator(); while(ready.hasNext()) { SelectionKey key = ready.next(); if(key != null) { ready.remove(); } if(key != null) { process(key); } } } /** * This will use the specified selection key to acquire the channel * and Operation associated with it to hand to the * executor to perform the channel operation. Also, if configured to * cancel, this method will add the channel and the associated * selection key to the cancellation map. * * @param key this is the selection key that is to be processed */ private void process(SelectionKey key) throws IOException { Runnable task = (Runnable)key.attachment(); Channel channel = (Channel) key.channel(); if(cancel) { table.put(channel, key); } executor.execute(task); } /** * The ChannelMap object is used to store selection * keys using a given channel. This is used to determine which of * the registered operations has been executed, and thus should be * removed from the selector so that it does not break on further * selections of the interested operations. * * @author Niall Gallagher */ private class ChannelMap extends HashMap { /** * Constructor for the ChannelMap object. This is * used to create a map for channels to selection keys. This will * allows the selection keys that need to be canceled quickly * to be retrieved using the associated channel object. */ public ChannelMap() { super(); } } } simple-http-4.1.21/src/org/simpleframework/transport/reactor/ExecuteAction.java0000644000175000017500000001000711417313373030403 0ustar jamespagejamespage/* * ExecuteAction.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.nio.channels.SelectableChannel; /** * The ExecuteAction object is represents an action * that the distributor is to process. This contains the operation * and the required I/O events as an integer bit mask as well as the * selectable channel used to register for selection. In order to * ensure that the action does not remain within the distributor for * too long the action has an expiry time. * * @author Niall Gallagher */ class ExecuteAction implements Action { /** * The task to execute when the required operations is ready. */ private final Operation task; /** * This is the bit mask of required operations to be executed. */ private final int require; /** * This is the time in the future that the event will expire in. */ private final long expiry; /** * Constructor for the Event object. The actions are * used to encapsulate the task to execute and the operations * to listen to when some action is to be performed. * * @param task this is the task to be executed when it is ready * @param require this is the required operations to listen to */ public ExecuteAction(Operation task, int require, long expiry) { this.expiry = System.currentTimeMillis() + expiry; this.require = require; this.task = task; } /** * This is used to execute the operation for the action. This will * be executed when the interested I/O event is ready for the * associated SelectableChannel object. If the action * expires before the interested I/O operation is ready this will * not be executed, instead the operation is canceled. */ public void run() { task.run(); } /** * This is used to get the expiry for the operation. The expiry * represents some static time in the future when the action will * expire if it does not become ready. This is used to cancel the * operation so that it does not remain in the distributor. * * @return the remaining time this operation will wait for */ public long getExpiry() { return expiry; } /** * This is the SelectableChannel which is used to * determine if the operation should be executed. If the channel * is ready for a given I/O event it can be run. For instance if * the operation is used to perform some form of read operation * it can be executed when ready to read data from the channel. * * @return this returns the channel used to govern execution */ public SelectableChannel getChannel() { return task.getChannel(); } /** * This is used to acquire the Operation that is to * be executed when the required operations are ready. It is the * responsibility of the distributor to invoke the operation. * * @return the operation to be executed when it is ready */ public Operation getOperation() { return task; } /** * This returns the I/O operations that the action is interested * in as an integer bit mask. When any of these operations are * ready the distributor will execute the provided operation. * * @return the integer bit mask of interested I/O operations */ public int getInterest() { return require; } }simple-http-4.1.21/src/org/simpleframework/transport/reactor/Operation.java0000644000175000017500000000405111417313373027605 0ustar jamespagejamespage/* * Operation.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.nio.channels.SelectableChannel; /** * The Operation interface is used to describe a task * that can be executed when the associated channel is ready for some * operation. Typically the SelectableChannel is used to * register with a selector with a set of given interested operations * when those operations can be performed this is executed. * * @author Niall Gallagher * * @see org.simpleframework.transport.reactor.Reactor */ public interface Operation extends Runnable { /** * This is the SelectableChannel which is used to * determine if the operation should be executed. If the channel * is ready for a given I/O event it can be run. For instance if * the operation is used to perform some form of read operation * it can be executed when ready to read data from the channel. * * @return this returns the channel used to govern execution */ public SelectableChannel getChannel(); /** * This is used to cancel the operation if it has timed out. This * is typically invoked when it has been waiting in a selector for * an extended duration of time without any active operations on * it. In such a case the reactor must purge the operation to free * the memory and open channels associated with the operation. */ public void cancel(); } simple-http-4.1.21/src/org/simpleframework/transport/reactor/ExecutorReactor.java0000644000175000017500000001201311417313373030760 0ustar jamespagejamespage/* * ExecutorReactor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.io.IOException; import java.util.concurrent.Executor; /** * The ExecutorReactor is used to schedule operation for * execution using an Executor implementation. This can be * useful when the operations performed are time intensive. For example * if the operations performed a read of the underlying channel and * then had to parse the contents of the payload. Such operations would * reduce the performance of the reactor if it could not delegate to * some other form of executor, as it would delay their execution. * * @author Niall Gallagher */ public class ExecutorReactor implements Reactor { /** * This is used to distribute the ready operations for execution. */ private final Distributor exchange; /** * This is used to execute the operations that ready to run. */ private final Executor executor; /** * Constructor for the ExecutorReactor object. This is * used to create a reactor that can delegate to the executor. This * also accepts the operations it is interested in, the value is * taken from the SelectionKey object. A bit mask can * be used to show interest in several operations at once. * * @param executor this is the executor used to run the operations */ public ExecutorReactor(Executor executor) throws IOException { this(executor, 1); } /** * Constructor for the ExecutorReactor object. This is * used to create a reactor that can delegate to the executor. This * also accepts the operations it is interested in, the value is * taken from the SelectionKey object. A bit mask can * be used to show interest in several operations at once. * * @param executor this is the executor used to run the operations * @param count this is the number of distributors to be used */ public ExecutorReactor(Executor executor, int count) throws IOException { this(executor, count, 120000); } /** * Constructor for the ExecutorReactor object. This is * used to create a reactor that can delegate to the executor. This * also accepts the operations it is interested in, the value is * taken from the SelectionKey object. A bit mask can * be used to show interest in several operations at once. * * @param executor this is the executor used to run the operations * @param count this is the number of distributors to be used * @param expiry the length of time to maintain and idle operation */ public ExecutorReactor(Executor executor, int count, long expiry) throws IOException { this.exchange = new PartitionDistributor(executor, count, expiry); this.executor = executor; } /** * This method is used to execute the provided operation without * the need to specifically check for I/O events. This is used if * the operation knows that the SelectableChannel is * ready, or if the I/O operation can be performed without knowing * if the channel is ready. Typically this is an efficient means * to perform a poll rather than a select on the channel. * * @param task this is the task to execute immediately */ public void process(Operation task) throws IOException { executor.execute(task); } /** * This method is used to execute the provided operation when there * is an I/O event that task is interested in. This will used the * operations SelectableChannel object to determine * the events that are ready on the channel. If this reactor is * interested in any of the ready events then the task is executed. * * @param task this is the task to execute on interested events * @param require this is the bit-mask value for interested events */ public void process(Operation task, int require) throws IOException { exchange.process(task, require); } /** * This is used to stop the reactor so that further requests to * execute operations does nothing. This will clean up all of * the reactors resources and unregister any operations that are * currently awaiting execution. This should be used to ensure * any threads used by the reactor gracefully stop. */ public void stop() throws IOException { exchange.close(); } } simple-http-4.1.21/src/org/simpleframework/transport/reactor/Distributor.java0000644000175000017500000000504711417313373030165 0ustar jamespagejamespage/* * Distributor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.io.IOException; /** * The Distributor object is used to execute operations * that have an interested I/O event ready. This acts much like a * scheduler would in that it delays the execution of the operations * until such time as the associated SelectableChannel * has an interested I/O event ready. *

* This distributor has two modes, one mode is used to cancel the * channel once an I/O event has occurred. This means that the channel * is removed from the Selector so that the selector * does not break when asked to select again. Canceling the channel * is useful when the operation execution may not fully read the * payload or when the operation takes a significant amount of time. * * @see org.simpleframework.transport.reactor.ActionDistributor */ interface Distributor { /** * This is used to process the Operation object. This * will wake up the selector if it is currently blocked selecting * and register the operations associated channel. Once the * selector is awake it will acquire the operation from the queue * and register the associated SelectableChannel for * selection. The operation will then be executed when the channel * is ready for the interested I/O events. * * @param task this is the task that is scheduled for distribution * @param require this is the bit-mask value for interested events */ public void process(Operation task, int require) throws IOException; /** * This is used to close the distributor such that it cancels all * of the registered channels and closes down the selector. This * is used when the distributor is no longer required, after the * close further attempts to process operations will fail. */ public void close() throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/reactor/Latch.java0000644000175000017500000000436711417313373026712 0ustar jamespagejamespage/* * Latch.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.io.IOException; import java.util.concurrent.CountDownLatch; /** * The Latch is used to provide a simple latch that will * allow a thread to block until it is signaled that it is ready. * The latch will block on the close method and when the * latch is signaled the close method will release all threads. * * @author Niall Gallagher */ class Latch extends CountDownLatch { /** * Constructor for the Latch object. This will * create a count down latch that will block when it is * closed. Any blocked threads will be released when the * latch is signaled that it is ready. */ public Latch() { super(1); } /** * This is used to signal that the latch is ready. Invoking * this method will release all threads that are blocking on * the close method. This method is used when the distributor * is closed and all operations have been purged. */ public void signal() throws IOException { try { countDown(); } catch(Exception e) { throw new IOException("Thread interrupted"); } } /** * This will block all threads attempting to close the latch. * All threads will be release when the latch is signaled. This * is used to ensure the distributor blocks until it has fully * purged all registered operations that are registered. */ public void close() throws IOException { try { await(); } catch(Exception e){ throw new IOException("Thread interrupted"); } } }simple-http-4.1.21/src/org/simpleframework/transport/reactor/Action.java0000644000175000017500000000505311417313373027065 0ustar jamespagejamespage/* * Action.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.nio.channels.SelectableChannel; /** * The Action object is used to represent an action that * the distributor is to process. This contains the operation and * the required I/O events as an integer bit mask. When an operation * is considered ready it will be handed to an executor to execute. * * @author Niall Gallagher */ interface Action extends Runnable { /** * This is used to get the expiry for the operation. The expiry * represents some static time in the future when the action will * expire if it does not become ready. This is used to cancel the * operation so that it does not remain in the distributor. * * @return the remaining time this operation will wait for */ public long getExpiry(); /** * This returns the I/O operations that the action is interested * in as an integer bit mask. When any of these operations are * ready the distributor will execute the provided operation. * * @return the integer bit mask of interested I/O operations */ public int getInterest(); /** * This is the SelectableChannel which is used to * determine if the operation should be executed. If the channel * is ready for a given I/O event it can be run. For instance if * the operation is used to perform some form of read operation * it can be executed when ready to read data from the channel. * * @return this returns the channel used to govern execution */ public SelectableChannel getChannel(); /** * This is used to acquire the Operation that is to * be executed when the required operations are ready. It is the * responsibility of the distributor to invoke the operation. * * @return the operation to be executed when it is ready */ public Operation getOperation(); } simple-http-4.1.21/src/org/simpleframework/transport/reactor/CancelAction.java0000644000175000017500000000741511417313373030177 0ustar jamespagejamespage/* * CancelAction.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.nio.channels.SelectableChannel; /** * The CancelAction object is used to represent a task * that can be executed to cancel an operation. This is used in the * place of a normal Operation to pass for execution * when the operation has expired before the I/O event is was * interested in occurred. Before this is executed the operation is * removed from selection. * * @author Niall Gallagher */ class CancelAction implements Action { /** * This is the operation that is to be canceled by this action. */ private final Operation task; /** * This is the operation object that is to be canceled. */ private final Action action; /** * Constructor for the Cancellation object. This is * used to create a runnable task that delegates to the cancel * method of the operation. This will be executed asynchronously * by the executor after being removed from selection. * * @param action this is the task that is to be canceled by this */ public CancelAction(Action action) { this.task = action.getOperation(); this.action = action; } /** * This method is executed by the Executor object * if the operation expires before the required I/O event(s) * have occurred. It is typically used to shutdown the socket * and release any resources associated with the operation. */ public void run() { task.cancel(); } /** * This is used to get the expiry for the operation. The expiry * represents some static time in the future when the action will * expire if it does not become ready. This is used to cancel the * operation so that it does not remain in the distributor. * * @return the remaining time this operation will wait for */ public long getExpiry() { return 0; } /** * This returns the I/O operations that the action is interested * in as an integer bit mask. When any of these operations are * ready the distributor will execute the provided operation. * * @return the integer bit mask of interested I/O operations */ public int getInterest() { return action.getInterest(); } /** * This is the SelectableChannel which is used to * determine if the operation should be executed. If the channel * is ready for a given I/O event it can be run. For instance if * the operation is used to perform some form of read operation * it can be executed when ready to read data from the channel. * * @return this returns the channel used to govern execution */ public SelectableChannel getChannel() { return action.getChannel(); } /** * This is used to acquire the Operation that is to * be executed when the required operations are ready. It is the * responsibility of the distributor to invoke the operation. * * @return the operation to be executed when it is ready */ public Operation getOperation() { return task; } }simple-http-4.1.21/src/org/simpleframework/transport/reactor/ActionQueue.java0000644000175000017500000000267111417313373030075 0ustar jamespagejamespage/* * ActionQueue.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.util.concurrent.LinkedBlockingQueue; /** * The ActionQueue object is used to queue actions for * selection. This is used by the reactor to queue actions that are * to be executed on a given I/O event. It allows actions to be * queued in such a way that the caller does not block. * * @author Niall Gallagher */ class ActionQueue extends LinkedBlockingQueue { /** * Constructor for the ActionQueue object. This is * used to create a non-blocking queue to schedule actions for * execution. This allows any number of actions to be inserted * for selection so the associated channels can be registered. */ public ActionQueue() { super(); } } simple-http-4.1.21/src/org/simpleframework/transport/reactor/DirectReactor.java0000644000175000017500000000760611417313373030410 0ustar jamespagejamespage/* * DirectReactor.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport.reactor; import java.io.IOException; import java.util.concurrent.Executor; import org.simpleframework.util.thread.DirectExecutor; /** * The DirectReactor object is used to execute the ready * operations of within a single synchronous thread. This is used * when the I/O operations to be performed do not require much time * to execute and so will not block the execution thread. * * @author Niall Gallagher */ public class DirectReactor implements Reactor { /** * This is used to distribute the ready operations for execution. */ private Distributor exchange; /** * This is used to execute the operations that ready to run. */ private Executor executor; /** * Constructor for the DirectReactor object. This is * used to create a reactor that does not require thread pooling * to execute the ready operations. All I/O operations are run * in the selection thread and should complete quickly. */ public DirectReactor() throws IOException { this(false); } /** * Constructor for the DirectReactor object. This is * used to create a reactor that does not require thread pooling * to execute the ready operations. All I/O operations are run * in the selection thread and should complete quickly. * * @param cancel determines the selection key should be canceled */ public DirectReactor(boolean cancel) throws IOException { this.executor = new DirectExecutor(); this.exchange = new ActionDistributor(executor, cancel); } /** * This method is used to execute the provided operation without * the need to specifically check for I/O events. This is used if * the operation knows that the SelectableChannel is * ready, or if the I/O operation can be performed without knowing * if the channel is ready. Typically this is an efficient means * to perform a poll rather than a select on the channel. * * @param task this is the task to execute immediately */ public void process(Operation task) throws IOException { executor.execute(task); } /** * This method is used to execute the provided operation when there * is an I/O event that task is interested in. This will used the * operations SelectableChannel object to determine * the events that are ready on the channel. If this reactor is * interested in any of the ready events then the task is executed. * * @param task this is the task to execute on interested events * @param require this is the bit-mask value for interested events */ public void process(Operation task, int require) throws IOException { exchange.process(task, require); } /** * This is used to stop the reactor so that further requests to * execute operations does nothing. This will clean up all of * the reactors resources and unregister any operations that are * currently awaiting execution. This should be used to ensure * any threads used by the reactor graceful stop. */ public void stop() throws IOException { exchange.close(); } } simple-http-4.1.21/src/org/simpleframework/transport/StopTrigger.java0000644000175000017500000000477711417313373026476 0ustar jamespagejamespage/* * StopTrigger.java February 2009 * * Copyright (C) 2009, Niall Gallagher * * 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.transport; import org.simpleframework.transport.reactor.Reactor; import org.simpleframework.util.thread.Daemon; /** * The StopTrigger object allows termination of the * server to be done in an asynchronous manner. This ensures that * should a HTTP request be used to terminate the server that * it does not block waiting for the servicing thread pool to * terminate causing a deadlock. * * @author Niall Gallagher */ class StopTrigger extends Daemon { /** * This is the internal processor that is to be terminated. */ private final Processor processor; /** * This is the internal write reactor that is terminated. */ private final Reactor reactor; /** * Constructor for the StopTrigger object. For an * orderly termination of the server, the processor and reactor * provided to the constructor will be stopped asynchronously. * * @param processor this is the processor that is to be stopped * @param reactor this is the reactor that is to be closed */ public StopTrigger(Processor processor, Reactor reactor) { this.processor = processor; this.reactor = reactor; } /** * When this method runs it will firstly stop the processor in * a synchronous fashion. Once the Processor has * stopped it will stop the Reactor ensuring that * all threads will be released. *

* It is important to note that stopping the processor before * stopping the reactor is required. This ensures that if there * are any threads executing within the processor that require * the reactor threads, they can complete without a problem. */ public void run() { try { processor.stop(); reactor.stop(); } catch(Exception e) { return; } } }simple-http-4.1.21/src/org/simpleframework/transport/SecureNegotiator.java0000644000175000017500000001077411417313373027501 0ustar jamespagejamespage/* * SecureNegotiator.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import org.simpleframework.transport.reactor.ExecutorReactor; import org.simpleframework.transport.reactor.Operation; import org.simpleframework.transport.reactor.Reactor; import org.simpleframework.util.thread.PoolExecutor; /** * The SecureNegotiator object is used to negotiations * to complete SSL handshakes for secure connections. Negotiations * are performed on Pipeline object before a transport * is created from the pipeline. Performing the SSL handshake is * required to ensure that only HTTP specific data is read and * written to the underlying transport. * * @author Niall Gallagher */ class SecureNegotiator implements Negotiator { /** * This is the transport used to process complete transports. */ private final Processor transporter; /** * This is the executor used to execute the negotiations. */ private final PoolExecutor executor; /** * This is the reactor which is used to schedule I/O events. */ private final Reactor reactor; /** * Constructor for the SecureNegotiator object. This * is used to create a negotiator that will perform SSL handshakes * on provided pipelines so that the data read from an written to * the underlying transport is complete and ready to use. * * @param transporter this is used to process the transports * @param count this is the number of threads used by this */ public SecureNegotiator(Processor transporter, int count) throws IOException { this.executor = new PoolExecutor(Notifier.class, count); this.reactor = new ExecutorReactor(executor); this.transporter = transporter; } /** * This method is used to execute the provided operation without * the need to specifically check for I/O events. This is used if * the operation knows that the SelectableChannel is * ready, or if the I/O operation can be performed without knowing * if the channel is ready. Typically this is an efficient means * to perform a poll rather than a select on the channel. * * @param task this is the task to execute immediately */ public void process(Operation task) throws IOException { reactor.process(task); } /** * This method is used to execute the provided operation when there * is an I/O event that task is interested in. This will used the * operations SelectableChannel object to determine * the events that are ready on the channel. If this reactor is * interested in any of the ready events then the task is executed. * * @param task this is the task to execute on interested events * @param require this is the bitmask value for interested events */ public void process(Operation task, int require) throws IOException { reactor.process(task, require); } /** * Once the negotiation has completed this is used to perform * processing of the provided transport. Processing of the * transport is done only after the negotiation has completed. * The given transport is used to read and write to the socket. * * @param transport this is the transport for the pipeline */ public void process(Transport transport) throws IOException { transporter.process(transport); } /** * This is used to stop the reactor so that further requests to * execute operations does nothing. This will clean up all of * the reactors resources and unregister any operations that are * currently awaiting execution. This should be used to ensure * any threads used by the reactor gracefully stop. */ public void stop() throws IOException { executor.stop(); reactor.stop(); } }simple-http-4.1.21/src/org/simpleframework/transport/Negotiator.java0000644000175000017500000000315011417313373026320 0ustar jamespagejamespage/* * Negotiator.java February 2007 * * Copyright (C) 2007, Niall Gallagher * * 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.transport; import java.io.IOException; import org.simpleframework.transport.reactor.Reactor; /** * The Negotiator interface represents a special kind * of reactor which is used to perform negotiations. Negotiations * are performed on Pipeline objects as a means to * exchange data between the client and server that does not form * part of a HTTP request entity. * * @author Niall Gallagher * * @see org.simpleframework.transport.Negotiation */ interface Negotiator extends Reactor { /** * Once the negotiation has completed this is used to perform * processing of the provided transport. Processing of the * transport is done only after the negotiation has completed. * The given transport is used to read and write to the socket. * * @param transport this is the transport for the pipeline */ public void process(Transport transport) throws IOException; } simple-http-4.1.21/src/org/simpleframework/transport/Socket.java0000644000175000017500000000540211417313373025437 0ustar jamespagejamespage/* * Socket.java February 2001 * * Copyright (C) 2001, Niall Gallagher * * 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.transport; import java.nio.channels.SocketChannel; import java.util.Map; import javax.net.ssl.SSLEngine; /** * This is a Socket interface that is used to represent * a socket. This has a map that allows attributes to be associated * with the client connection. Attributes such as security details * or other transport related details can be exposed by placing them * in the socket map. The Processor can then use these * attributes as required. *

* This provides the connected SocketChannel that can * be used to read and write data asynchronously. The socket channel * must be selectable and in non-blocking mode. If the socket is not * in a non-blocking state the connection will not be processed. * * @author Niall Gallagher */ public interface Socket { /** * This is used to acquire the SSL engine used for security. If * the socket is connected to an SSL transport this returns an * SSL engine which can be used to establish the secure connection * and send and receive content over that connection. If this is * null then the socket represents a normal transport. * * @return the SSL engine used to establish a secure transport */ public SSLEngine getEngine(); /** * This method is used to acquire the SocketChannel * for the connection. This allows the server to acquire the input * and output streams with which to communicate. It can also be * used to configure the connection and perform various network * operations that could otherwise not be performed. * * @return this returns the socket used by this socket */ public SocketChannel getChannel(); /** * This method is used to get the Map of attributes * for this socket. The attributes map is used to maintain details * about the connection. Information such as security credentials * to client details can be placed within the attribute map. * * @return this returns the map of attributes for this socket */ public Map getAttributes(); } simple-http-4.1.21/jar/0000755000175000017500000000000011767603362015306 5ustar jamespagejamespagesimple-http-4.1.21/build.xml0000644000175000017500000000305311417313373016344 0ustar jamespagejamespage simple-http-4.1.21/test/0000755000175000017500000000000011767603362015511 5ustar jamespagejamespagesimple-http-4.1.21/test/src/0000755000175000017500000000000011417313373016270 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/0000755000175000017500000000000011417313373017057 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/0000755000175000017500000000000011417313373022266 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/http/0000755000175000017500000000000011767603362023255 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/http/StatusTest.java0000644000175000017500000000067611417313373026244 0ustar jamespagejamespagepackage org.simpleframework.http; import junit.framework.TestCase; public class StatusTest extends TestCase { private static final int ITERATIONS = 100000; public void testStatus() { testStatus(200, "OK"); testStatus(404, "Not Found"); } public void testStatus(int code, String expect) { for(int i = 0; i < ITERATIONS; i++) { assertEquals(expect, Status.getDescription(code)); } } } simple-http-4.1.21/test/src/org/simpleframework/http/resource/0000755000175000017500000000000011767603362025104 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/http/resource/FileIndexerTest.java0000644000175000017500000000132611417313373030777 0ustar jamespagejamespagepackage org.simpleframework.http.resource; import java.io.File; import junit.framework.TestCase; import org.simpleframework.http.Address; import org.simpleframework.http.parse.AddressParser; public class FileIndexerTest extends TestCase { public void testFileIndexer() { FileIndexer indexer = new FileIndexer(new File(".")); Address address = new AddressParser("/path/index.html"); assertEquals(indexer.getContentType(address), "text/html"); assertEquals(indexer.getPath(address).getName(), "index.html"); assertEquals(indexer.getPath(address).getExtension(), "html"); assertEquals(indexer.getPath(address).getDirectory(), "/path/"); } } simple-http-4.1.21/test/src/org/simpleframework/http/Debug.java0000644000175000017500000000037411417313373025142 0ustar jamespagejamespagepackage org.simpleframework.http; public class Debug { public void log(String text, Object... list) { System.out.printf(text, list); } public void logln(String text, Object... list) { System.out.printf(text + "%n", list); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/0000755000175000017500000000000011767603362024205 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/http/core/TicketProcessor.java0000644000175000017500000000130211417313373030157 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import java.nio.channels.SocketChannel; import org.simpleframework.transport.Server; import org.simpleframework.transport.Socket; class TicketProcessor implements Server { private Server delegate; public TicketProcessor(Server delegate) { this.delegate = delegate; } public void process(Socket pipe) throws IOException { SocketChannel channel = pipe.getChannel(); int port = channel.socket().getPort(); pipe.getAttributes().put(Ticket.KEY,new Ticket(port)); delegate.process(pipe); } public void stop() throws IOException { delegate.stop(); } }simple-http-4.1.21/test/src/org/simpleframework/http/core/DribbleCursor.java0000644000175000017500000000263511417313373027607 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import org.simpleframework.transport.Cursor; public class DribbleCursor implements Cursor { private Cursor cursor; private byte[] swap; private int dribble; public DribbleCursor(Cursor cursor, int dribble) { this.cursor = cursor; this.dribble = dribble; this.swap = new byte[1]; } public boolean isOpen() throws IOException { return true; } public boolean isReady() throws IOException { return cursor.isReady(); } public int ready() throws IOException { int ready = cursor.ready(); return Math.min(ready, dribble); } public int read() throws IOException { if(read(swap) > 0) { return swap[0] & 0xff; } return 0; } public int read(byte[] data) throws IOException { return read(data, 0, data.length); } public int read(byte[] data, int off, int len) throws IOException { int size = Math.min(len, dribble); return cursor.read(data, off, size); } public int reset(int len) throws IOException { return cursor.reset(len); } public void push(byte[] data) throws IOException { cursor.push(data); } public void push(byte[] data, int off, int len) throws IOException { cursor.push(data, off, len); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/FixedConsumerTest.java0000644000175000017500000000441711417313373030461 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import junit.framework.TestCase; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.ArrayAllocator; import org.simpleframework.util.buffer.Buffer; public class FixedConsumerTest extends TestCase implements Allocator { private Buffer buffer; public Buffer allocate() { return buffer; } public Buffer allocate(int size) { return buffer; } public void testConsumer() throws Exception { testConsumer(10, 10, 10); testConsumer(1024, 10, 1024); testConsumer(1024, 1024, 1024); testConsumer(1024, 1024, 1023); testConsumer(1024, 1, 1024); testConsumer(1, 1, 1); testConsumer(2, 2, 2); testConsumer(3, 1, 2); } public void testConsumer(int entitySize, int dribble, int limitSize) throws Exception { StringBuffer buf = new StringBuffer(); // Ensure that we dont try read forever limitSize = Math.min(entitySize, limitSize); for(int i = 0, line = 0; i < entitySize; i++) { String text = "["+String.valueOf(i)+"]"; line += text.length(); buf.append(text); if(line >= 48) { buf.append("\n"); line = 0; } } buffer = new ArrayAllocator().allocate(); String requestBody = buf.toString(); FixedConsumer consumer = new FixedConsumer(this, limitSize); Cursor cursor = new DribbleCursor(new StreamCursor(requestBody), dribble); byte[] requestBytes = requestBody.getBytes("UTF-8"); while(!consumer.isFinished()) { consumer.consume(cursor); } byte[] consumedBytes = buffer.encode("UTF-8").getBytes("UTF-8"); assertEquals(buffer.encode("UTF-8").length(), limitSize); for(int i = 0; i < limitSize; i++) { if(consumedBytes[i] != requestBytes[i]) { throw new IOException("Fixed consumer modified the request!"); } } } public void close() throws IOException { // TODO Auto-generated method stub } } simple-http-4.1.21/test/src/org/simpleframework/http/core/TransferTest.java0000644000175000017500000001714111417313373027470 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import org.simpleframework.http.core.Conversation; import org.simpleframework.http.core.Transfer; import junit.framework.TestCase; public class TransferTest extends TestCase { public void testTransferEncoding() throws IOException { MockSender sender = new MockSender(); MockMonitor monitor = new MockMonitor(); MockRequest request = new MockRequest(); MockResponse response = new MockResponse(); Conversation support = new Conversation(request, response); Transfer transfer = new Transfer(support, sender, monitor); // Start a HTTP/1.1 conversation request.setMajor(1); request.setMinor(1); transfer.start(); assertEquals(response.getValue("Connection"), "keep-alive"); assertEquals(response.getValue("Transfer-Encoding"), "chunked"); assertEquals(response.getValue("Content-Length"), null); assertEquals(response.getContentLength(), -1); assertTrue(response.isCommitted()); sender = new MockSender(); monitor = new MockMonitor(); request = new MockRequest(); response = new MockResponse(); support = new Conversation(request, response); transfer = new Transfer(support, sender, monitor); // Start a HTTP/1.0 conversation request.setMajor(1); request.setMinor(0); transfer.start(); assertEquals(response.getValue("Connection"), "close"); assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getValue("Content-Length"), null); assertEquals(response.getContentLength(), -1); assertTrue(response.isCommitted()); } public void testContentLength() throws IOException { MockSender sender = new MockSender(); MockMonitor monitor = new MockMonitor(); MockRequest request = new MockRequest(); MockResponse response = new MockResponse(); Conversation support = new Conversation(request, response); Transfer transfer = new Transfer(support, sender, monitor); // Start a HTTP/1.1 conversation request.setMajor(1); request.setMinor(1); transfer.start(1024); assertEquals(response.getValue("Connection"), "keep-alive"); assertEquals(response.getValue("Content-Length"), "1024"); assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getContentLength(), 1024); assertTrue(response.isCommitted()); sender = new MockSender(); monitor = new MockMonitor(); request = new MockRequest(); response = new MockResponse(); support = new Conversation(request, response); transfer = new Transfer(support, sender, monitor); // Start a HTTP/1.0 conversation request.setMajor(1); request.setMinor(0); transfer.start(1024); assertEquals(response.getValue("Connection"), "close"); assertEquals(response.getValue("Content-Length"), "1024"); assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getContentLength(), 1024); assertTrue(response.isCommitted()); sender = new MockSender(); monitor = new MockMonitor(); request = new MockRequest(); response = new MockResponse(); support = new Conversation(request, response); transfer = new Transfer(support, sender, monitor); // Start a HTTP/1.0 conversation request.setMajor(1); request.setMinor(1); response.set("Content-Length", "2048"); response.set("Connection", "close"); response.set("Transfer-Encoding", "chunked"); transfer.start(1024); assertEquals(response.getValue("Connection"), "close"); assertEquals(response.getValue("Content-Length"), "1024"); // should be 1024 assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getContentLength(), 1024); assertTrue(response.isCommitted()); } public void testHeadMethodWithConnectionClose() throws IOException { MockSender sender = new MockSender(); MockMonitor monitor = new MockMonitor(); MockRequest request = new MockRequest(); MockResponse response = new MockResponse(); Conversation support = new Conversation(request, response); Transfer transfer = new Transfer(support, sender, monitor); request.setMajor(1); request.setMinor(0); request.setMethod("HEAD"); request.set("Connection", "keep-alive"); response.setContentLength(1024); response.set("Connection", "close"); transfer.start(); assertEquals(response.getValue("Connection"), "close"); assertEquals(response.getValue("Content-Length"), "1024"); // should be 1024 assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getContentLength(), 1024); } public void testHeadMethodWithSomethingWritten() throws IOException { MockSender sender = new MockSender(); MockMonitor monitor = new MockMonitor(); MockRequest request = new MockRequest(); MockResponse response = new MockResponse(); Conversation support = new Conversation(request, response); Transfer transfer = new Transfer(support, sender, monitor); request.setMajor(1); request.setMinor(1); request.setMethod("HEAD"); request.set("Connection", "keep-alive"); response.setContentLength(1024); transfer.start(512); assertEquals(response.getValue("Connection"), "keep-alive"); assertEquals(response.getValue("Content-Length"), "512"); // should be 512 assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getContentLength(), 512); } public void testHeadMethodWithNoContentLength() throws IOException { MockSender sender = new MockSender(); MockMonitor monitor = new MockMonitor(); MockRequest request = new MockRequest(); MockResponse response = new MockResponse(); Conversation support = new Conversation(request, response); Transfer transfer = new Transfer(support, sender, monitor); request.setMajor(1); request.setMinor(1); request.setMethod("HEAD"); request.set("Connection", "keep-alive"); transfer.start(); assertEquals(response.getValue("Connection"), "keep-alive"); assertEquals(response.getValue("Content-Length"), null); assertEquals(response.getValue("Transfer-Encoding"), "chunked"); assertEquals(response.getContentLength(), -1); } public void testHeadMethodWithNoContentLengthAndSomethingWritten() throws IOException { MockSender sender = new MockSender(); MockMonitor monitor = new MockMonitor(); MockRequest request = new MockRequest(); MockResponse response = new MockResponse(); Conversation support = new Conversation(request, response); Transfer transfer = new Transfer(support, sender, monitor); request.setMajor(1); request.setMinor(1); request.setMethod("HEAD"); request.set("Connection", "keep-alive"); transfer.start(32); assertEquals(response.getValue("Connection"), "keep-alive"); assertEquals(response.getValue("Content-Length"), "32"); assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getContentLength(), 32); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/AccumulatorTest.java0000644000175000017500000000710511417313373030162 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import org.simpleframework.http.core.Accumulator; import org.simpleframework.http.core.Conversation; import junit.framework.TestCase; public class AccumulatorTest extends TestCase { public void testAccumulator() throws IOException { MockSender sender = new MockSender(); MockMonitor monitor = new MockMonitor(); MockRequest request = new MockRequest(); MockResponse response = new MockResponse(); Conversation support = new Conversation(request, response); Accumulator buffer = new Accumulator(support, sender, monitor); byte[] content = { 'T', 'E', 'S', 'T' }; // Start a HTTP/1.1 conversation request.setMajor(1); request.setMinor(1); // Write to a zero capacity buffer buffer.expand(0); buffer.write(content, 0, content.length); assertEquals(response.getValue("Connection"), "keep-alive"); assertEquals(response.getValue("Transfer-Encoding"), "chunked"); assertEquals(response.getValue("Content-Length"), null); assertEquals(response.getContentLength(), -1); assertTrue(response.isCommitted()); sender = new MockSender(); monitor = new MockMonitor(); request = new MockRequest(); response = new MockResponse(); support = new Conversation(request, response); buffer = new Accumulator(support, sender, monitor); // Start a HTTP/1.0 conversation request.setMajor(1); request.setMinor(0); // Write to a zero capacity buffer buffer.expand(0); buffer.write(content, 0, content.length); assertEquals(response.getValue("Connection"), "close"); assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getValue("Content-Length"), null); assertEquals(response.getContentLength(), -1); assertTrue(response.isCommitted()); sender = new MockSender(); monitor = new MockMonitor(); request = new MockRequest(); response = new MockResponse(); support = new Conversation(request, response); buffer = new Accumulator(support, sender, monitor); // Start a HTTP/1.1 conversation request.setMajor(1); request.setMinor(1); // Write to a large capacity buffer buffer.expand(1024); buffer.write(content, 0, content.length); assertEquals(response.getValue("Connection"), null); assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getValue("Content-Length"), null); assertEquals(response.getContentLength(), -1); assertFalse(response.isCommitted()); assertFalse(monitor.isReady()); assertFalse(monitor.isClose()); assertFalse(monitor.isError()); // Flush the buffer buffer.close(); assertEquals(response.getValue("Connection"), "keep-alive"); assertEquals(response.getValue("Transfer-Encoding"), null); assertEquals(response.getValue("Content-Length"), "4"); assertEquals(response.getContentLength(), 4); assertTrue(response.isCommitted()); assertTrue(monitor.isReady()); assertFalse(monitor.isClose()); assertFalse(monitor.isError()); boolean catchOverflow = false; try { buffer.write(content, 0, content.length); } catch(Exception e) { catchOverflow = true; } assertTrue(catchOverflow); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/Result.java0000644000175000017500000000132711417313373026321 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.util.List; import java.util.Map; import org.simpleframework.http.Cookie; class Result { private List cookies; private String response; private byte[] body; private Map map; public Result(String response, byte[] body, Map map, List cookies) { this.cookies = cookies; this.response = response; this.body = body; this.map = map; } public List getCookies() { return cookies; } public byte[] getBody() { return body; } public String getResponse() throws Exception { return response; } public Map getMap() { return map; } }simple-http-4.1.21/test/src/org/simpleframework/http/core/MockProxyRequest.java0000644000175000017500000000267411417313373030355 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.util.List; 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; public class MockProxyRequest extends MockRequest { private RequestHeader header; public MockProxyRequest(RequestHeader header) { this.header = header; } public int getContentLength() { return header.getContentLength(); } public ContentType getContentType() { return header.getContentType(); } public String getValue(String name) { return header.getValue(name); } public List getValues(String name) { return header.getValues(name); } public int getMajor() { return header.getMajor(); } public String getMethod() { return header.getMethod(); } public int getMinor() { return header.getMajor(); } public Path getPath() { return header.getPath(); } public Query getQuery() { return header.getQuery(); } public String getTarget() { return header.getTarget(); } public String getParameter(String name) { return header.getQuery().get(name); } public Cookie getCookie(String name) { return header.getCookie(name); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockPart.java0000644000175000017500000000162711417313373026566 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import org.simpleframework.http.ContentType; import org.simpleframework.http.Part; public class MockPart extends MockBody implements Part { private String name; private boolean file; public MockPart(String name, String body, boolean file) { super(body); this.file = file; this.name = name; } public String getContent() throws IOException { return body; } public ContentType getContentType() { return null; } public Disposition getDisposition() { return null; } public String getHeader(String name) { return null; } public String getName() { return name; } public boolean isFile() { return file; } public String getFileName() { return null; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/TokenConsumerTest.java0000644000175000017500000000342211417313373030475 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import junit.framework.TestCase; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.ArrayAllocator; public class TokenConsumerTest extends TestCase { public void testTokenConsumer() throws IOException { Allocator allocator = new ArrayAllocator(); TokenConsumer consumer = new TokenConsumer(allocator, "\r\n".getBytes()); Cursor cursor = new StreamCursor("\r\n"); consumer.consume(cursor); assertEquals(cursor.ready(), -1); assertTrue(consumer.isFinished()); } public void testTokenConsumerException() throws IOException { Allocator allocator = new ArrayAllocator(); TokenConsumer consumer = new TokenConsumer(allocator, "\r\n".getBytes()); Cursor cursor = new StreamCursor("--\r\n"); boolean exception = false; try { consumer.consume(cursor); } catch(Exception e) { exception = true; } assertTrue("Exception not thrown for invalid token", exception); } public void testTokenConsumerDribble() throws IOException { Allocator allocator = new ArrayAllocator(); TokenConsumer consumer = new TokenConsumer(allocator, "This is a large token to be consumed\r\n".getBytes()); DribbleCursor cursor = new DribbleCursor(new StreamCursor("This is a large token to be consumed\r\n0123456789"), 1); consumer.consume(cursor); assertEquals(cursor.ready(), 1); assertTrue(consumer.isFinished()); assertEquals(cursor.read(), '0'); assertEquals(cursor.read(), '1'); assertEquals(cursor.read(), '2'); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/RequestConsumerTest.java0000644000175000017500000001406211417313373031047 0ustar jamespagejamespagepackage org.simpleframework.http.core; import org.simpleframework.transport.Cursor; import junit.framework.TestCase; public class RequestConsumerTest extends TestCase { private static final byte[] SOURCE_1 = ("POST /index.html HTTP/1.0\r\n"+ "Content-Type: application/x-www-form-urlencoded\r\n"+ "Content-Length: 42\r\n"+ "Transfer-Encoding: chunked\r\n"+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n").getBytes(); private static final byte[] SOURCE_2 = ("GET /tmp/amazon_files/21lP7I1XB5L.jpg HTTP/1.1\r\n"+ "Accept-Encoding: gzip, deflate\r\n"+ "Connection: keep-alive\r\n"+ "Referer: http://localhost:9090/tmp/amazon.htm\r\n"+ "Cache-Control: max-age=0\r\n"+ "Host: localhost:9090\r\n"+ "Accept-Language: en-US\r\n"+ "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13\r\n"+ "Accept: */*\r\n" + "\r\n").getBytes(); private static final byte[] SOURCE_3 = ("GET /tmp/amazon_files/in-your-city-blue-large._V256095983_.gif HTTP/1.1Accept-Encoding: gzip, deflate\r\n"+ "Connection: keep-alive\r\n"+ "Referer: http://localhost:9090/tmp/amazon.htm\r\n"+ "Cache-Control: max-age=0\r\n"+ "Host: localhost:9090\r\n"+ "Accept-Language: en-US\r\n"+ "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13\r\n"+ "Accept: */*\r\n"+ "\r\n").getBytes(); private static final byte[] SOURCE_4 = ("GET /tmp/amazon_files/narrowtimer_transparent._V47062518_.gif HTTP/1.1\r\n"+ "Accept-Encoding: gzip, deflate\r\n"+ "Connection: keep-alive\r\n"+ "Referer: http://localhost:9090/tmp/amazon.htm\r\n"+ "Cache-Control: max-age=0\r\n"+ "Host: localhost:9090\r\n"+ "Accept-Language: en-US\r\n"+ "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Version/3.1 Safari/525.13\r\n"+ "Accept: */*\r\n"+ "\r\n").getBytes(); public void testPerformance() throws Exception { testPerformance(SOURCE_1, "/index.html"); testPerformance(SOURCE_2, "/tmp/amazon_files/21lP7I1XB5L.jpg"); testPerformance(SOURCE_3, "/tmp/amazon_files/in-your-city-blue-large._V256095983_.gif"); testPerformance(SOURCE_4, "/tmp/amazon_files/narrowtimer_transparent._V47062518_.gif"); } public void testPerformance(byte[] request, String path) throws Exception { long start = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) { RequestConsumer header = new RequestConsumer(); Cursor cursor = new StreamCursor(request); while(!header.isFinished()) { header.consume(cursor); } assertEquals(cursor.ready(), -1); assertEquals(header.getPath().getPath(), path); } System.err.printf("%s time=%s%n", path, (System.currentTimeMillis() - start)); } public void testHeader() throws Exception { long start = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) { RequestConsumer header = new RequestConsumer(); Cursor cursor = new StreamCursor(SOURCE_1); while(!header.isFinished()) { header.consume(cursor); } assertEquals(cursor.ready(), -1); assertEquals(header.getTarget(), "/index.html"); assertEquals(header.getMethod(), "POST"); assertEquals(header.getMajor(), 1); assertEquals(header.getMinor(), 0); assertEquals(header.getValue("Content-Length"), "42"); assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded"); assertEquals(header.getValue("Host"), "some.host.com"); assertEquals(header.getValues("Accept").size(), 4); assertEquals(header.getValues("Accept").get(0), "image/gif"); assertEquals(header.getValues("Accept").get(1), "image/png"); assertEquals(header.getValues("Accept").get(2), "image/jpeg"); assertEquals(header.getValues("Accept").get(3), "*"); assertEquals(header.getContentType().getPrimary(), "application"); assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded"); assertEquals(header.getTransferEncoding(), "chunked"); } System.err.printf("time=%s%n", (System.currentTimeMillis() - start)); } public void testDribble() throws Exception { RequestConsumer header = new RequestConsumer(); Cursor cursor = new DribbleCursor(new StreamCursor(SOURCE_1), 1); while(!header.isFinished()) { header.consume(cursor); } assertEquals(cursor.ready(), -1); assertEquals(header.getTarget(), "/index.html"); assertEquals(header.getMethod(), "POST"); assertEquals(header.getMajor(), 1); assertEquals(header.getMinor(), 0); assertEquals(header.getValue("Content-Length"), "42"); assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded"); assertEquals(header.getValue("Host"), "some.host.com"); assertEquals(header.getValues("Accept").size(), 4); assertEquals(header.getValues("Accept").get(0), "image/gif"); assertEquals(header.getValues("Accept").get(1), "image/png"); assertEquals(header.getValues("Accept").get(2), "image/jpeg"); assertEquals(header.getValues("Accept").get(3), "*"); assertEquals(header.getContentType().getPrimary(), "application"); assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded"); assertEquals(header.getTransferEncoding(), "chunked"); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockRequest.java0000644000175000017500000001024411417313373027303 0ustar jamespagejamespagepackage org.simpleframework.http.core; 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.http.ContentType; import org.simpleframework.http.Cookie; import org.simpleframework.http.Form; import org.simpleframework.http.Part; import org.simpleframework.http.Path; import org.simpleframework.http.Query; import org.simpleframework.http.Request; import org.simpleframework.http.parse.AddressParser; import org.simpleframework.http.parse.ContentParser; import org.simpleframework.http.session.Session; import org.simpleframework.http.session.SessionException; public class MockRequest extends RequestMessage implements Request { private Message message; private String target; private String method = "GET"; private String content; private String type; private int major = 1; private int minor = 1; public MockRequest() { this.header = new RequestConsumer(); this.message = new Message(); } public void set(String name, String value) { message.set(name, value); } public void add(String name, String value) { message.add(name, value); } public boolean isSecure(){ return false; } public String getTarget() { return target; } public void setContentType(String value) { type = value; } public void setTarget(String target) { this.target = target; } public Path getPath() { return new AddressParser(target).getPath(); } public Query getQuery() { return new AddressParser(target).getQuery(); } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public int getMajor() { return major; } public void setMajor(int major) { this.major = major; } public int getMinor() { return minor; } public void setMinor(int minor) { this.minor = minor; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public InputStream getInputStream() { return null; } public Session getSession() throws SessionException { return null; } public Session getSession(boolean create) throws SessionException { return null; } public Form getForm() { return null; } public Part getPart(String name) { return null; } public int size() { return 0; } public Cookie getCookie(String name) { return null; } public String getParameter(String name) { return null; } public Map getAttributes() { return null; } public ContentType getContentType() { return new ContentParser(type); } public int getContentLength() { String value = getValue("Content-Length"); if(value != null) { return new Integer(value); } return -1; } public String getTransferEncoding() { List list = getValues("Transfer-Encoding"); if(list.size() > 0) { return list.get(0); } return null; } public Disposition getDisposition() { String value = getValue("Content-Disposition"); if(value == null) { return null; } return new DispositionParser(value); } public List getValues(String name) { return message.getValues(name); } public String getValue(String name) { return message.getValue(name); } public Object getAttribute(Object key) { return null; } public boolean isKeepAlive() { return true; } public InetSocketAddress getClientAddress() { return null; } public ReadableByteChannel getByteChannel() throws IOException { return null; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/PartConsumerTest.java0000644000175000017500000000213211417313373030320 0ustar jamespagejamespagepackage org.simpleframework.http.core; import junit.framework.TestCase; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayAllocator; public class PartConsumerTest extends TestCase { private static final String SOURCE = "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "... contents of file1.txt ...\r\n"+ "--AaB03x\r\n"; public void testHeader() throws Exception { PartList list = new PartList(); PartConsumer consumer = new PartConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8")); Cursor cursor = new StreamCursor(SOURCE); while(!consumer.isFinished()) { consumer.consume(cursor); } assertEquals(list.size(), 1); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'"); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MessageTest.java0000644000175000017500000000543411417313373027272 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; import junit.framework.TestCase; public class MessageTest extends TestCase { public void testMessage() { Message message = new Message(); message.add("Content-Length", "10"); message.add("Connection", "keep-alive"); message.add("Accept", "image/gif, image/jpeg, */*"); message.add("Set-Cookie", "a=b"); message.add("Set-Cookie", "b=c"); assertEquals(message.getValue("CONTENT-LENGTH"), "10"); assertEquals(message.getValue("Content-Length"), "10"); assertEquals(message.getValue("CONTENT-length"), "10"); assertEquals(message.getValue("connection"), "keep-alive"); assertEquals(message.getValue("CONNECTION"), "keep-alive"); assertTrue(message.getValues("CONNECTION") != null); assertEquals(message.getValues("connection").size(), 1); assertTrue(message.getValues("set-cookie") != null); assertEquals(message.getValues("set-cookie").size(), 2); assertTrue(message.getValues("SET-COOKIE").contains("a=b")); assertTrue(message.getValues("SET-COOKIE").contains("b=c")); assertTrue(message.getNames().contains("Content-Length")); assertFalse(message.getNames().contains("CONTENT-LENGTH")); assertTrue(message.getNames().contains("Connection")); assertFalse(message.getNames().contains("CONNECTION")); assertTrue(message.getNames().contains("Set-Cookie")); assertFalse(message.getNames().contains("SET-COOKIE")); message.set("Set-Cookie", "d=e"); assertTrue(message.getValues("set-cookie") != null); assertEquals(message.getValues("set-cookie").size(), 1); assertFalse(message.getValues("SET-COOKIE").contains("a=b")); assertFalse(message.getValues("SET-COOKIE").contains("b=c")); assertTrue(message.getValues("SET-COOKIE").contains("d=e")); } public void testDates() { Message message = new Message(); DateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz"); TimeZone zone = TimeZone.getTimeZone("GMT"); long time = System.currentTimeMillis(); Date date = new Date(time); format.setTimeZone(zone); message.set("Date", format.format(date)); assertEquals(format.format(date), message.getValue("date")); assertEquals(new Date(message.getDate("DATE")).toString(), date.toString()); message.setDate("Date", time); assertEquals(format.format(date), message.getValue("date")); assertEquals(new Date(message.getDate("DATE")).toString(), date.toString()); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/Ticket.java0000644000175000017500000000064411417313373026267 0ustar jamespagejamespagepackage org.simpleframework.http.core; public class Ticket { public static final Class KEY = Ticket.class; private final String ticket; private final int port; public Ticket(int port) { this.ticket = String.valueOf(port); this.port = port; } public int getPort() { return port; } public String getTicket() { return ticket; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/DispositionParserTest.java0000644000175000017500000000157111417313373031365 0ustar jamespagejamespagepackage org.simpleframework.http.core; import junit.framework.TestCase; public class DispositionParserTest extends TestCase { private DispositionParser parser; public void setUp() { parser = new DispositionParser(); } public void testDisposition() { parser.parse("form-data; name=\"input_check\""); assertFalse(parser.isFile()); assertEquals(parser.getName(), "input_check"); parser.parse("form-data; name=\"input_password\""); assertFalse(parser.isFile()); assertEquals(parser.getName(), "input_password"); parser.parse("form-data; name=\"FileItem\"; filename=\"C:\\Inetpub\\wwwroot\\Upload\\file1.txt\""); assertTrue(parser.isFile()); assertEquals(parser.getName(), "FileItem"); assertEquals(parser.getFileName(), "C:\\Inetpub\\wwwroot\\Upload\\file1.txt"); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ReactorProcessorTest.java0000644000175000017500000002261011417313373031200 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.nio.channels.SocketChannel; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import junit.framework.TestCase; import org.simpleframework.http.Form; import org.simpleframework.http.Part; import org.simpleframework.http.Request; import org.simpleframework.http.Response; import org.simpleframework.http.core.ReactorTest.TestChannel; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayAllocator; public class ReactorProcessorTest extends TestCase implements Container { private static final int ITERATIONS = 2000; private static final String MINIMAL = "HEAD /MINIMAL/%s HTTP/1.0\r\n" + "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "\r\n"; private static final String SIMPLE = "GET /SIMPLE/%s HTTP/1.0\r\n" + "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n"; private static final String UPLOAD = "POST /UPLOAD/%s HTTP/1.0\r\n" + "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n" + "--AaB03x\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt\r\n"+ "--AaB03x\r\n"+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file2.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file3.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file3.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file4.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y--\r\n"+ "--AaB03x--\r\n"; private static class StopWatch { private long duration; private long start; public StopWatch() { this.start = System.currentTimeMillis(); } public long time() { return duration; } public void stop() { duration = System.currentTimeMillis() - start; } } public static class MockChannel implements Channel { private Cursor cursor; public MockChannel(StreamCursor cursor, int dribble) { this.cursor = new DribbleCursor(cursor, dribble); } public boolean isSecure() { return false; } public Cursor getCursor() { return cursor; } public Sender getSender() { return null; } public Map getAttributes() { return null; } public void close() {} public SocketChannel getSocket() { return null; } } private ConcurrentHashMap timers = new ConcurrentHashMap(); private LinkedBlockingQueue finished = new LinkedBlockingQueue(); public void testMinimal() throws Exception { Initiator handler = new ContainerSelector(this, new ArrayAllocator(), 10, 2); testRequest(handler, "/MINIMAL/%s", MINIMAL, "MINIMAL"); testRequest(handler, "/SIMPLE/%s", SIMPLE, "SIMPLE"); testRequest(handler, "/UPLOAD/%s", UPLOAD, "UPLOAD"); } public void testRequest(Initiator handler, String target, String payload, String name) throws Exception { long start = System.currentTimeMillis(); for(int i = 0; i < ITERATIONS; i++) { String request = String.format(payload, i); StopWatch stopWatch = new StopWatch(); timers.put(String.format(target, i), stopWatch); testHandler(handler, request, 2048); } double sum = 0; for(int i = 0; i < ITERATIONS; i++) { StopWatch stopWatch = finished.take(); sum += stopWatch.time(); } double total = (System.currentTimeMillis() - start); double count = ITERATIONS; System.err.println(String.format("%s total=[%s] for=[%s] average=[%s] time-per-request=[%s] request-per-millisecond=[%s] request-per-second=[%s]", name, total, count, sum / count, total / count, count / total + 1, count / (total / 1000))); } public void testHandler(Initiator handler, String payload, int dribble) throws Exception { StreamCursor cursor = new StreamCursor(payload); Channel channel = new TestChannel(cursor, dribble); handler.start(channel); } public void handle(Request request, Response response) { try { process(request, response); }catch(Exception e) { e.printStackTrace(); assertTrue(false); } } public void process(Request request, Response response) throws Exception { Form form = request.getForm(); List list = form.getParts(); String method = request.getMethod(); if(method.equals("HEAD")) { assertEquals(request.getMajor(), 1); assertEquals(request.getMinor(), 0); assertEquals(request.getValue("Host"), "some.host.com"); } else if(method.equals("GET")) { assertEquals(request.getMajor(), 1); assertEquals(request.getMinor(), 0); assertEquals(request.getValue("Host"), "some.host.com"); assertEquals(request.getValues("Accept").size(), 4); assertEquals(request.getValues("Accept").get(0), "image/gif"); assertEquals(request.getValues("Accept").get(1), "image/png"); assertEquals(request.getValues("Accept").get(2), "image/jpeg"); assertEquals(request.getValues("Accept").get(3), "*"); } else { assertEquals(request.getMajor(), 1); assertEquals(request.getMinor(), 0); assertEquals(request.getContentType().getPrimary(), "multipart"); assertEquals(request.getContentType().getSecondary(), "form-data"); assertEquals(request.getValue("Host"), "some.host.com"); assertEquals(request.getValues("Accept").size(), 4); assertEquals(request.getValues("Accept").get(0), "image/gif"); assertEquals(request.getValues("Accept").get(1), "image/png"); assertEquals(request.getValues("Accept").get(2), "image/jpeg"); assertEquals(request.getValues("Accept").get(3), "*"); assertEquals(list.size(), 4); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\""); assertEquals(list.get(0).getName(), "pics"); assertEquals(list.get(0).getFileName(), "file1.txt"); assertEquals(list.get(0).isFile(), true); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getContentType().getSecondary(), "plain"); assertEquals(list.get(1).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file2.txt\""); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getName(), "pics"); assertEquals(list.get(1).getFileName(), "file2.txt"); assertEquals(list.get(1).isFile(), true); assertEquals(list.get(2).getContentType().getSecondary(), "plain"); assertEquals(list.get(2).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file3.txt\""); assertEquals(list.get(2).getName(), "pics"); assertEquals(list.get(2).getFileName(), "file3.txt"); assertEquals(list.get(2).isFile(), true); assertEquals(list.get(3).getContentType().getPrimary(), "text"); assertEquals(list.get(3).getContentType().getSecondary(), "plain"); assertEquals(list.get(3).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file4.txt\""); assertEquals(list.get(3).getName(), "pics"); assertEquals(list.get(3).getFileName(), "file4.txt"); assertEquals(list.get(3).isFile(), true); } StopWatch stopWatch = timers.get(request.getTarget()); stopWatch.stop(); finished.offer(stopWatch); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ThreadDumper.java0000644000175000017500000000706511417313373027434 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; import java.util.concurrent.CountDownLatch; public class ThreadDumper extends Thread { private static String INDENT = " "; private CountDownLatch latch; private volatile boolean dead; private int wait; public ThreadDumper() { this(10000); } public ThreadDumper(int wait) { this.latch = new CountDownLatch(1); this.wait = wait; } public void waitUntilStarted() throws InterruptedException{ latch.await(); } public void kill(){ try { Thread.sleep(1000); dead = true; dumpThreadInfo(); }catch(Exception e){ e.printStackTrace(); } } public void run() { while(!dead) { try{ latch.countDown(); dumpThreadInfo(); findDeadlock(); Thread.sleep(wait); }catch(Exception e){ e.printStackTrace(); } } } /** * Prints the thread dump information to System.out. */ public static void dumpThreadInfo(){ System.out.println(getThreadInfo()); } public static String getThreadInfo() { ThreadMXBean tmbean = ManagementFactory.getThreadMXBean(); long[] tids = tmbean.getAllThreadIds(); ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE); StringWriter str = new StringWriter(); PrintWriter log = new PrintWriter(str); log.println("Full Java thread dump"); for (ThreadInfo ti : tinfos) { printThreadInfo(ti, log); } log.flush(); return str.toString(); } private static void printThreadInfo(ThreadInfo ti, PrintWriter log) { StringBuilder sb = new StringBuilder("\"" + ti.getThreadName() + "\"" + " Id=" + ti.getThreadId() + " in " + ti.getThreadState()); if (ti.getLockName() != null) { sb.append(" on lock=" + ti.getLockName()); } if (ti.isSuspended()) { sb.append(" (suspended)"); } if (ti.isInNative()) { sb.append(" (running in native)"); } log.println(sb.toString()); if (ti.getLockOwnerName() != null) { log.println(INDENT + " owned by " + ti.getLockOwnerName() + " Id=" + ti.getLockOwnerId()); } for (StackTraceElement ste : ti.getStackTrace()) { log.println(INDENT + "at " + ste.toString()); } log.println(); } /** * Checks if any threads are deadlocked. If any, print * the thread dump information. */ public static boolean findDeadlock() { ThreadMXBean tmbean = ManagementFactory.getThreadMXBean(); long[] tids = tmbean.findMonitorDeadlockedThreads(); if (tids == null) { return false; } else { StringWriter str = new StringWriter(); PrintWriter log = new PrintWriter(str); tids = tmbean.getAllThreadIds(); System.out.println("Deadlock found :-"); ThreadInfo[] tinfos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE); for (ThreadInfo ti : tinfos) { printThreadInfo(ti, log); } log.flush(); System.out.println(str.toString()); return true; } } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ChunkedProducerTest.java0000644000175000017500000000246411417313373030773 0ustar jamespagejamespagepackage org.simpleframework.http.core; import junit.framework.TestCase; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayAllocator; public class ChunkedProducerTest extends TestCase { public void testChunk() throws Exception { testChunk(1024, 1); testChunk(1024, 2); testChunk(512, 20); testChunk(64, 64); } public void testChunk(int chunkSize, int count) throws Exception { MockSender sender = new MockSender((chunkSize * count) + 1024); MockMonitor monitor = new MockMonitor(); ChunkedConsumer validator = new ChunkedConsumer(new ArrayAllocator()); ChunkedProducer producer = new ChunkedProducer(sender, monitor); byte[] chunk = new byte[chunkSize]; for(int i = 0; i < chunk.length; i++) { chunk[i] = (byte)String.valueOf(i).charAt(0); } for(int i = 0; i < count; i++) { producer.produce(chunk, 0, chunkSize); } producer.close(); System.err.println(sender.getBuffer().encode("UTF-8")); Cursor cursor = sender.getCursor(); while(!validator.isFinished()) { validator.consume(cursor); } assertEquals(cursor.ready(), -1); assertTrue(monitor.isReady()); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockEntity.java0000644000175000017500000000141711417313373027131 0ustar jamespagejamespage package org.simpleframework.http.core; import org.simpleframework.http.session.Session; import org.simpleframework.http.session.SessionException; public class MockEntity implements Entity { private Body body; public MockEntity() { super(); } public MockEntity(Body body) { this.body = body; } public Session getSession() throws SessionException { return null; } public Session getSession(boolean create) throws SessionException { return null; } public Body getBody() { return body; } public Header getHeader() { return null; } public Channel getChannel() { return null; } public void close() {} public long getStart() { return 0; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ConversationTest.java0000644000175000017500000000506211417313373030355 0ustar jamespagejamespagepackage org.simpleframework.http.core; import org.simpleframework.http.core.Conversation; import junit.framework.TestCase; public class ConversationTest extends TestCase { private MockRequest request; private MockResponse response; private Conversation support; public void setUp() { request = new MockRequest(); response = new MockResponse(); support = new Conversation(request, response); } public void testResponse() { request.setMajor(1); request.setMinor(1); response.set("Content-Length", "10"); response.set("Connection", "close"); assertFalse(support.isKeepAlive()); assertTrue(support.isPersistent()); assertEquals(support.getContentLength(), 10); assertEquals(support.isChunkedEncoded(), false); request.setMinor(0); assertFalse(support.isKeepAlive()); assertFalse(support.isPersistent()); response.set("Connection", "keep-alive"); assertTrue(support.isKeepAlive()); assertFalse(support.isPersistent()); response.set("Transfer-Encoding", "chunked"); assertTrue(support.isChunkedEncoded()); assertTrue(support.isKeepAlive()); } public void testConversation() { request.setMajor(1); request.setMinor(1); support.setChunkedEncoded(); assertEquals(response.getValue("Transfer-Encoding"), "chunked"); assertEquals(response.getValue("Connection"), "keep-alive"); assertTrue(support.isKeepAlive()); assertTrue(support.isPersistent()); request.setMinor(0); support.setChunkedEncoded(); assertEquals(response.getValue("Connection"), "close"); assertFalse(support.isKeepAlive()); request.setMajor(1); request.setMinor(1); response.set("Content-Length", "10"); response.set("Connection", "close"); assertFalse(support.isKeepAlive()); assertTrue(support.isPersistent()); assertEquals(support.getContentLength(), 10); request.setMinor(0); assertFalse(support.isKeepAlive()); assertFalse(support.isPersistent()); response.set("Connection", "keep-alive"); assertTrue(support.isKeepAlive()); assertFalse(support.isPersistent()); response.set("Transfer-Encoding", "chunked"); assertTrue(support.isChunkedEncoded()); assertTrue(support.isKeepAlive()); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/BoundaryConsumerTest.java0000644000175000017500000000463411417313373031206 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.ByteArrayInputStream; import junit.framework.TestCase; import org.simpleframework.util.buffer.ArrayAllocator; public class BoundaryConsumerTest extends TestCase { private static final byte[] TERMINAL = { '-', '-', 'A', 'a', 'B', '0', '3', 'x', '-', '-', '\r', '\n', 'X', 'Y' }; private static final byte[] NORMAL = { '-', '-', 'A', 'a', 'B', '0', '3', 'x', '\r', '\n', 'X', 'Y' }; private static final byte[] BOUNDARY = { 'A', 'a', 'B', '0', '3', 'x' }; private BoundaryConsumer boundary; public void setUp() { boundary = new BoundaryConsumer(new ArrayAllocator(), BOUNDARY); } public void testBoundary() throws Exception { StreamCursor cursor = new StreamCursor(new ByteArrayInputStream(NORMAL)); while(!boundary.isFinished()) { boundary.consume(cursor); } assertEquals(cursor.read(), 'X'); assertEquals(cursor.read(), 'Y'); assertTrue(boundary.isFinished()); assertFalse(boundary.isEnd()); assertFalse(cursor.isReady()); } public void testTerminal() throws Exception { StreamCursor cursor = new StreamCursor(new ByteArrayInputStream(TERMINAL)); while(!boundary.isFinished()) { boundary.consume(cursor); } assertEquals(cursor.read(), 'X'); assertEquals(cursor.read(), 'Y'); assertTrue(boundary.isFinished()); assertTrue(boundary.isEnd()); assertFalse(cursor.isReady()); } public void testDribble() throws Exception { DribbleCursor cursor = new DribbleCursor(new StreamCursor(new ByteArrayInputStream(TERMINAL)), 3); while(!boundary.isFinished()) { boundary.consume(cursor); } assertEquals(cursor.read(), 'X'); assertEquals(cursor.read(), 'Y'); assertTrue(boundary.isFinished()); assertTrue(boundary.isEnd()); assertFalse(cursor.isReady()); boundary.clear(); cursor = new DribbleCursor(new StreamCursor(new ByteArrayInputStream(TERMINAL)), 1); while(!boundary.isFinished()) { boundary.consume(cursor); } assertEquals(cursor.read(), 'X'); assertEquals(cursor.read(), 'Y'); assertTrue(boundary.isFinished()); assertTrue(boundary.isEnd()); assertFalse(cursor.isReady()); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/Connector.java0000644000175000017500000000023511417313373026772 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.net.Socket; public interface Connector { public Socket getSocket() throws Exception; } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockResponse.java0000644000175000017500000000303011417313373027444 0ustar jamespagejamespagepackage org.simpleframework.http.core; 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.Response; import org.simpleframework.http.core.ResponseMessage; public class MockResponse extends ResponseMessage implements Response { private boolean committed; public MockResponse() { super(); } public OutputStream getOutputStream() { return System.out; } public boolean isCommitted() { return committed; } public void commit() { committed = true; } public void reset() { return; } public void close() { return; } public Object getAttribute(String name) { return null; } public Map getAttributes() { return null; } public OutputStream getOutputStream(int size) throws IOException { return null; } public PrintStream getPrintStream() throws IOException { return null; } public PrintStream getPrintStream(int size) throws IOException { return null; } public void setContentLength(int length) { set("Content-Length", length); } public WritableByteChannel getByteChannel() throws IOException { return null; } public WritableByteChannel getByteChannel(int size) throws IOException { return null; } public boolean isEmpty() { return false; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/SegmentConsumerTest.java0000644000175000017500000001004611417313373031017 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import org.simpleframework.transport.Cursor; import junit.framework.TestCase; public class SegmentConsumerTest extends TestCase { private static final String SOURCE = "Content-Type: application/x-www-form-urlencoded\r\n"+ "User-Agent:\r\n" + "Content-Length: 42\r\n"+ "Transfer-Encoding: chunked\r\n"+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n"; private static final String EMPTY = "Accept-Language:\r\n"+ "Content-Length:\r\n"+ "Content-Type:\r\n"+ "Content-Disposition:\r\n"+ "Transfer-Encoding:\r\n"+ "Expect:\r\n"+ "Cookie:\r\n"+ "\r\n"; protected SegmentConsumer header; public void setUp() throws IOException { header = new SegmentConsumer(); } public void testHeader() throws Exception { Cursor cursor = new StreamCursor(SOURCE); while(!header.isFinished()) { header.consume(cursor); } assertEquals(cursor.ready(), -1); assertEquals(header.getValue("Pragma"), null); assertEquals(header.getValue("User-Agent"), ""); assertEquals(header.getValue("Content-Length"), "42"); assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded"); assertEquals(header.getValue("Host"), "some.host.com"); assertEquals(header.getValues("Accept").size(), 4); assertEquals(header.getValues("Accept").get(0), "image/gif"); assertEquals(header.getValues("Accept").get(1), "image/png"); assertEquals(header.getValues("Accept").get(2), "image/jpeg"); assertEquals(header.getValues("Accept").get(3), "*"); assertEquals(header.getContentType().getPrimary(), "application"); assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded"); assertEquals(header.getTransferEncoding(), "chunked"); } public void testEmptyHeader() throws Exception { Cursor cursor = new StreamCursor(EMPTY); while(!header.isFinished()) { header.consume(cursor); } assertEquals(cursor.ready(), -1); assertEquals(header.getValue("Accept-Language"), ""); assertEquals(header.getValue("Content-Length"), ""); assertEquals(header.getValue("Content-Type"), ""); assertEquals(header.getValue("Content-Disposition"), ""); assertEquals(header.getValue("Transfer-Encoding"), ""); assertEquals(header.getValue("Expect"), ""); assertEquals(header.getValue("Cookie"), ""); assertEquals(header.getContentType().getPrimary(), null); assertEquals(header.getContentType().getSecondary(), null); } public void testDribble() throws Exception { Cursor cursor = new DribbleCursor(new StreamCursor(SOURCE), 1); while(!header.isFinished()) { header.consume(cursor); } assertEquals(cursor.ready(), -1); assertEquals(header.getValue("Content-Length"), "42"); assertEquals(header.getValue("Content-Type"), "application/x-www-form-urlencoded"); assertEquals(header.getValue("Host"), "some.host.com"); assertEquals(header.getValues("Accept").size(), 4); assertEquals(header.getValues("Accept").get(0), "image/gif"); assertEquals(header.getValues("Accept").get(1), "image/png"); assertEquals(header.getValues("Accept").get(2), "image/jpeg"); assertEquals(header.getValues("Accept").get(3), "*"); assertEquals(header.getContentType().getPrimary(), "application"); assertEquals(header.getContentType().getSecondary(), "x-www-form-urlencoded"); assertEquals(header.getTransferEncoding(), "chunked"); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/FixedProducerTest.java0000644000175000017500000000302111417313373030437 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import org.simpleframework.http.core.FixedProducer; import junit.framework.TestCase; public class FixedProducerTest extends TestCase { public void testContent() throws IOException { testContent(1024, 1); testContent(1024, 2); testContent(512, 20); testContent(64, 64); } public void testContent(int chunkSize, int count) throws IOException { MockSender sender = new MockSender((chunkSize * count) + chunkSize); MockMonitor monitor = new MockMonitor(); FixedProducer producer = new FixedProducer(sender, monitor, chunkSize * count); byte[] chunk = new byte[chunkSize]; for(int i = 0; i < chunk.length; i++) { chunk[i] = (byte)String.valueOf(i).charAt(0); } for(int i = 0; i < count; i++) { producer.produce(chunk, 0, chunkSize); } producer.close(); System.err.println(sender.getBuffer().encode()); assertTrue(monitor.isReady()); assertFalse(monitor.isError()); assertFalse(monitor.isClose()); sender = new MockSender((chunkSize * count) + chunkSize); monitor = new MockMonitor(); producer = new FixedProducer(sender, monitor, chunkSize * count); for(int i = 0; i < count; i++) { producer.produce(chunk, 0, chunkSize); } producer.close(); assertFalse(monitor.isError()); assertTrue(monitor.isReady()); } }simple-http-4.1.21/test/src/org/simpleframework/http/core/MockSegment.java0000644000175000017500000000313211417313373027253 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.util.List; import org.simpleframework.http.ContentType; import org.simpleframework.http.parse.ContentParser; public class MockSegment implements Segment { private Message header; public MockSegment() { this.header = new Message(); } public boolean isFile() { return false; } public ContentType getContentType() { String value = getValue("Content-Type"); if(value == null) { return null; } return new ContentParser(value); } public int getContentLength() { String value = getValue("Content-Length"); if(value != null) { return new Integer(value); } return -1; } public String getTransferEncoding() { List list = getValues("Transfer-Encoding"); if(list.size() > 0) { return list.get(0); } return null; } public Disposition getDisposition() { String value = getValue("Content-Disposition"); if(value == null) { return null; } return new DispositionParser(value); } public List getValues(String name) { return header.getValues(name); } public String getValue(String name) { return header.getValue(name); } protected void add(String name, String value) { header.add(name, value); } public String getName() { return null; } public String getFileName() { return null; } }simple-http-4.1.21/test/src/org/simpleframework/http/core/StopTest.java0000644000175000017500000001222111417313373026623 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.Closeable; import java.io.InputStream; import java.io.PrintStream; import java.lang.management.ManagementFactory; import java.lang.management.ThreadMXBean; import java.net.InetSocketAddress; import java.net.URL; import java.net.URLConnection; import java.util.Date; import junit.framework.TestCase; import org.simpleframework.http.Request; import org.simpleframework.http.Response; import org.simpleframework.transport.connect.Connection; import org.simpleframework.transport.connect.SocketConnection; import org.simpleframework.util.thread.PoolExecutor; public class StopTest extends TestCase { private static final int ITERATIONS = 20; public void testStop() throws Exception { ThreadDumper dumper = new ThreadDumper(); dumper.start(); dumper.waitUntilStarted(); ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); int initialThreads = threadBean.getThreadCount(); for(int i = 0; i < ITERATIONS; i++) { try { ServerCriteria criteria = createServer(); InetSocketAddress address = criteria.getAddress(); Connection connection = criteria.getConnection(); Client client = createClient(address, String.format("[%s of %s]", i, ITERATIONS)); Thread.sleep(2000); // allow some requests to execute connection.close(); Thread.sleep(100); // ensure client keeps executing client.close(); Thread.sleep(1000); // wait for threads to terminate }catch(Exception e) { e.printStackTrace(); } assertEquals(initialThreads, threadBean.getThreadCount()); } dumper.kill(); } public static Client createClient(InetSocketAddress address, String tag) throws Exception { PoolExecutor executor = new PoolExecutor(Runnable.class, 20); int port = address.getPort(); Client client = new Client(executor, port, tag); client.start(); return client; } public static ServerCriteria createServer() throws Exception { Container container = new Container() { public void handle(Request request, Response response) { try { PrintStream out = response.getPrintStream(); response.set("Content-Type", "text/plain"); response.set("Connection", "close"); out.print("TEST " + new Date()); response.close(); }catch(Exception e) { e.printStackTrace(); try { response.close(); }catch(Exception ex) { ex.printStackTrace(); } } } }; ContainerServer server = new ContainerServer(container); Connection connection = new SocketConnection(server); InetSocketAddress address = (InetSocketAddress)connection.connect(null); // ephemeral port return new ServerCriteria(connection, address); } private static class Client extends Thread implements Closeable { private PoolExecutor executor; private RequestTask task; private volatile boolean dead; public Client(PoolExecutor executor, int port, String tag) { this.task = new RequestTask(port, tag); this.executor = executor; } public void run() { try { while(!dead) { executor.execute(task); Thread.sleep(10); } }catch(Exception e) { e.printStackTrace(); } } public void close() { dead = true; executor.stop(); } } private static class RequestTask implements Runnable { private String tag; private int port; public RequestTask(int port, String tag) { this.port = port; this.tag = tag; } public void run() { try { URL target = new URL("http://localhost:"+port+"/"); URLConnection connection = target.openConnection(); // set a timeout connection.setConnectTimeout(10000); connection.setReadTimeout(10000); InputStream stream = connection.getInputStream(); StringBuilder builder = new StringBuilder(); int octet = 0; while((octet = stream.read()) != -1) { builder.append((char)octet); } stream.close(); System.out.println(tag + " " + Thread.currentThread() + ": " + builder); }catch(Exception e) { e.printStackTrace(); } } } private static class ServerCriteria { private Connection connection; private InetSocketAddress address; public ServerCriteria(Connection connection, InetSocketAddress address){ this.connection = connection; this.address = address; } public Connection getConnection() { return connection; } public InetSocketAddress getAddress() { return address; } } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockBody.java0000644000175000017500000000157511417313373026557 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import org.simpleframework.http.Part; public class MockBody implements Body { protected PartList list; protected String body; public MockBody() { this(""); } public MockBody(String body) { this.list = new PartList(); this.body = body; } public PartList getParts() { return list; } public Part getPart(String name) { return list.getPart(name); } public String getContent(String charset) { return body; } public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(body.getBytes("UTF-8")); } public String getContent() throws IOException { return body; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ReplyConsumer.java0000644000175000017500000000163611417313373027655 0ustar jamespagejamespagepackage org.simpleframework.http.core; import org.simpleframework.http.StatusLine; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.ArrayAllocator; class ReplyConsumer extends BuilderConsumer { public Builder builder; public ReplyConsumer() { this(new ArrayAllocator(), new ResponseBuilder()); } public ReplyConsumer(Allocator allocator, Builder builder) { super(allocator, builder, null); this.header = new ResponseConsumer(); this.factory = new ConsumerFactory(allocator, header); this.builder = builder; } public StatusLine getStatusLine() { return (ResponseConsumer) header; } public Body getBody() { return builder.getBody(); } public Message getMessage() { return this.header.header; } public String toString() { return header.toString(); } }simple-http-4.1.21/test/src/org/simpleframework/http/core/ChunkedConsumerTest.java0000644000175000017500000000722111417313373030777 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import junit.framework.TestCase; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.ArrayAllocator; import org.simpleframework.util.buffer.ArrayBuffer; import org.simpleframework.util.buffer.Buffer; public class ChunkedConsumerTest extends TestCase implements Allocator { public Buffer buffer; public void setUp() { buffer = new ArrayBuffer(); } public Buffer allocate() { return buffer; } public Buffer allocate(int size) { return buffer; } public void testChunks() throws Exception { testChunks(64, 1024, 64); testChunks(64, 11, 64); testChunks(1024, 1024, 100000); testChunks(1024, 10, 100000); testChunks(1024, 11, 100000); testChunks(1024, 113, 100000); testChunks(1024, 1, 100000); testChunks(1024, 2, 50000); testChunks(1024, 3, 50000); testChunks(10, 1024, 50000); testChunks(1, 10, 71234); testChunks(2, 11, 123456); testChunks(15, 113, 25271); testChunks(16, 1, 43265); testChunks(64, 2, 63266); testChunks(32, 3, 9203); } public void testChunks(int chunkSize, int dribble, int entitySize) throws Exception { ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream plain = new ByteArrayOutputStream(); Chunker encode = new Chunker(out); StringBuffer buf = new StringBuffer(); int fill = 0; for(int i = 0, line = 0; i < entitySize; i++) { String text = "["+String.valueOf(i)+"]"; if(fill >= chunkSize) { encode.write(buf.toString().getBytes("UTF-8")); plain.write(buf.toString().getBytes("UTF-8")); buf.setLength(0); fill = 0; line = 0; } line += text.length(); fill += text.length(); buf.append(text); if(line >= 48) { buf.append("\n"); fill++; line = 0; } } if(buf.length() > 0) { encode.write(buf.toString().getBytes("UTF-8")); plain.write(buf.toString().getBytes("UTF-8")); } buffer = new ArrayAllocator().allocate(); // N.B clear previous buffer encode.close(); byte[] data = out.toByteArray(); byte[] plainText = plain.toByteArray(); //System.out.println(">>"+new String(data, 0, data.length, "UTF-8")+"<<"); //System.out.println("}}"+new String(plainText, 0, plainText.length,"UTF-8")+"{{"); DribbleCursor cursor = new DribbleCursor(new StreamCursor(new ByteArrayInputStream(data)), dribble); ChunkedConsumer test = new ChunkedConsumer(this); while(!test.isFinished()) { test.consume(cursor); } byte[] result = buffer.encode("UTF-8").getBytes("UTF-8"); //System.out.println("))"+new String(result, 0, result.length, "UTF-8")+"(("); if(result.length != plainText.length) { throw new IOException(String.format("Bad encoding result=[%s] plainText=[%s]", result.length, plainText.length)); } for(int i = 0; i < result.length; i++) { if(result[i] != plainText[i]) { throw new IOException(String.format("Values do not match for %s, %s, and %s", chunkSize, dribble, entitySize)); } } } public void close() throws IOException { // TODO Auto-generated method stub } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockMonitor.java0000644000175000017500000000136011417313373027301 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; public class MockMonitor implements Monitor { private boolean close; private boolean error; private boolean ready; public MockMonitor() { super(); } public void close(Sender sender) { close = true; } public boolean isClose() { return close; } public boolean isError() { return error; } public void ready(Sender sender) { ready = true; } public boolean isReady() { return ready; } public void error(Sender sender) { error = true; } public boolean isClosed() { return close || error; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/Client.java0000644000175000017500000004421011417313373026257 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.ByteArrayInputStream; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.Socket; import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.SocketFactory; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; public class Client { private static final byte[] CERTIFICATE = { (byte)254,(byte)237,(byte)254,(byte)237,(byte)0, (byte)0, (byte)0, (byte)2, (byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)3, (byte)107,(byte)101, (byte)121,(byte)0, (byte)0, (byte)1, (byte)26, (byte)105,(byte)38, (byte)187,(byte)170,(byte)0, (byte)0, (byte)2, (byte)187,(byte)48, (byte)130,(byte)2, (byte)183,(byte)48, (byte)14, (byte)6, (byte)10, (byte)43, (byte)6, (byte)1, (byte)4, (byte)1, (byte)42, (byte)2, (byte)17, (byte)1, (byte)1, (byte)5, (byte)0, (byte)4, (byte)130,(byte)2, (byte)163,(byte)138,(byte)122,(byte)194, (byte)218,(byte)31, (byte)101,(byte)210,(byte)131,(byte)160,(byte)37, (byte)111,(byte)187,(byte)43, (byte)192,(byte)67, (byte)244,(byte)136,(byte)120,(byte)166,(byte)171,(byte)204,(byte)87, (byte)156, (byte)50, (byte)58, (byte)153,(byte)37, (byte)180,(byte)248,(byte)60, (byte)73, (byte)16, (byte)110, (byte)176,(byte)84, (byte)239,(byte)247,(byte)113,(byte)133,(byte)193,(byte)239,(byte)94, (byte)107, (byte)126,(byte)141,(byte)199,(byte)243,(byte)243,(byte)25, (byte)179,(byte)181,(byte)201,(byte)100, (byte)194,(byte)146,(byte)114,(byte)162,(byte)124,(byte)96, (byte)198,(byte)248,(byte)232,(byte)162, (byte)143,(byte)170,(byte)120,(byte)106,(byte)171,(byte)128,(byte)32, (byte)18, (byte)134,(byte)69, (byte)2, (byte)230,(byte)204,(byte)18, (byte)191,(byte)212,(byte)236,(byte)130,(byte)76, (byte)24, (byte)24, (byte)131,(byte)210,(byte)150,(byte)209,(byte)205,(byte)174,(byte)25, (byte)175,(byte)45, (byte)39, (byte)223,(byte)17, (byte)57, (byte)129,(byte)6, (byte)195,(byte)116,(byte)197,(byte)143, (byte)14, (byte)160,(byte)120,(byte)249,(byte)220,(byte)48, (byte)71, (byte)109,(byte)122,(byte)64, (byte)195,(byte)139,(byte)61, (byte)206,(byte)83, (byte)159,(byte)78, (byte)137,(byte)160,(byte)88, (byte)252,(byte)120,(byte)217,(byte)251,(byte)254,(byte)151,(byte)94, (byte)242,(byte)170,(byte)0, (byte)247,(byte)170,(byte)53, (byte)197,(byte)34, (byte)253,(byte)96, (byte)47, (byte)248,(byte)194, (byte)230,(byte)62, (byte)121,(byte)117,(byte)163,(byte)35, (byte)80, (byte)15, (byte)113,(byte)203, (byte)71, (byte)202,(byte)36, (byte)187,(byte)163,(byte)78, (byte)228,(byte)31, (byte)3, (byte)53, (byte)214,(byte)149,(byte)170,(byte)214,(byte)161,(byte)180,(byte)53, (byte)207,(byte)158,(byte)150, (byte)161,(byte)37, (byte)59, (byte)150,(byte)107,(byte)161,(byte)9, (byte)195,(byte)79, (byte)254, (byte)62, (byte)231,(byte)13, (byte)195,(byte)173,(byte)139,(byte)15, (byte)153,(byte)62, (byte)20, (byte)204,(byte)111,(byte)64, (byte)89, (byte)180,(byte)201,(byte)58, (byte)64, (byte)15, (byte)195, (byte)18, (byte)29, (byte)29, (byte)44, (byte)5, (byte)101,(byte)132,(byte)113,(byte)204,(byte)251, (byte)225,(byte)3, (byte)82, (byte)52, (byte)62, (byte)86, (byte)142,(byte)43, (byte)240,(byte)201, (byte)26, (byte)226,(byte)143,(byte)162,(byte)9, (byte)97, (byte)96, (byte)185,(byte)59, (byte)85, (byte)54, (byte)115,(byte)135,(byte)199,(byte)26, (byte)58, (byte)185,(byte)100,(byte)118,(byte)48, (byte)119,(byte)110,(byte)203,(byte)115,(byte)74, (byte)152,(byte)144,(byte)137,(byte)13, (byte)18, (byte)192,(byte)82, (byte)101,(byte)163,(byte)8, (byte)128,(byte)57, (byte)68, (byte)183,(byte)225, (byte)79, (byte)6, (byte)143,(byte)94, (byte)203,(byte)203,(byte)121,(byte)52, (byte)128,(byte)94, (byte)184,(byte)223,(byte)107,(byte)217,(byte)68, (byte)118,(byte)145,(byte)164,(byte)13, (byte)220, (byte)135,(byte)11, (byte)74, (byte)193,(byte)48, (byte)7, (byte)95, (byte)190,(byte)17, (byte)0, (byte)69, (byte)109,(byte)6, (byte)64, (byte)86, (byte)80, (byte)93, (byte)82, (byte)20, (byte)106, (byte)191,(byte)201,(byte)13, (byte)91, (byte)132,(byte)102,(byte)47, (byte)188,(byte)123,(byte)79, (byte)209,(byte)43, (byte)180,(byte)152,(byte)128,(byte)20, (byte)182,(byte)148,(byte)19, (byte)24, (byte)230,(byte)249,(byte)42, (byte)51, (byte)197,(byte)176,(byte)113,(byte)44, (byte)100,(byte)95, (byte)59, (byte)91, (byte)78, (byte)226,(byte)184,(byte)224,(byte)72, (byte)233,(byte)133,(byte)154, (byte)42, (byte)221,(byte)32, (byte)165,(byte)41, (byte)156,(byte)165,(byte)247,(byte)86, (byte)115, (byte)183,(byte)22, (byte)89, (byte)17, (byte)165,(byte)215,(byte)148,(byte)32, (byte)199,(byte)64, (byte)139,(byte)171,(byte)236,(byte)43, (byte)5, (byte)36, (byte)35, (byte)223,(byte)35, (byte)247, (byte)255,(byte)112,(byte)27, (byte)215,(byte)57, (byte)251,(byte)236,(byte)128,(byte)168,(byte)219, (byte)146,(byte)235,(byte)241,(byte)68, (byte)213,(byte)127,(byte)63, (byte)231,(byte)236,(byte)176, (byte)166,(byte)121,(byte)203,(byte)114,(byte)33, (byte)19, (byte)200,(byte)167,(byte)155,(byte)27, (byte)38, (byte)109,(byte)133,(byte)1, (byte)184,(byte)173,(byte)253,(byte)198,(byte)122,(byte)98, (byte)196,(byte)43, (byte)145,(byte)86, (byte)182,(byte)208,(byte)78, (byte)246,(byte)234,(byte)249, (byte)229,(byte)202,(byte)75, (byte)66, (byte)108,(byte)134,(byte)81, (byte)134,(byte)90, (byte)251, (byte)137,(byte)155,(byte)209,(byte)11, (byte)249,(byte)87, (byte)164,(byte)98, (byte)242,(byte)51, (byte)184,(byte)162,(byte)35, (byte)20, (byte)248,(byte)14, (byte)224,(byte)76, (byte)31, (byte)132, (byte)125,(byte)44, (byte)83, (byte)15, (byte)221,(byte)43, (byte)62, (byte)187,(byte)211,(byte)176, (byte)41, (byte)70, (byte)187,(byte)3, (byte)48, (byte)150,(byte)206,(byte)54, (byte)38, (byte)33, (byte)94, (byte)133,(byte)145,(byte)148,(byte)58, (byte)219,(byte)252,(byte)124,(byte)251,(byte)46, (byte)72, (byte)35, (byte)244,(byte)33, (byte)97, (byte)50, (byte)21, (byte)207,(byte)163,(byte)3, (byte)226,(byte)225,(byte)252,(byte)149,(byte)214,(byte)200,(byte)132,(byte)65, (byte)224,(byte)121, (byte)205,(byte)241,(byte)107,(byte)155,(byte)252,(byte)158,(byte)64, (byte)40, (byte)252,(byte)143, (byte)76, (byte)71, (byte)227,(byte)13, (byte)176,(byte)50, (byte)250,(byte)115,(byte)198,(byte)64, (byte)174,(byte)146,(byte)108,(byte)106,(byte)66, (byte)98, (byte)78, (byte)196,(byte)126,(byte)118, (byte)51, (byte)65, (byte)251,(byte)8, (byte)28, (byte)75, (byte)123,(byte)92, (byte)5, (byte)125, (byte)16, (byte)127,(byte)250,(byte)65, (byte)178,(byte)54, (byte)169,(byte)109,(byte)94, (byte)171, (byte)97, (byte)154,(byte)232,(byte)24, (byte)196,(byte)91, (byte)103,(byte)90, (byte)217,(byte)75, (byte)126,(byte)76, (byte)129,(byte)240,(byte)67, (byte)131,(byte)147,(byte)178,(byte)29, (byte)234, (byte)150,(byte)91, (byte)78, (byte)165,(byte)76, (byte)200,(byte)99, (byte)175,(byte)240,(byte)3, (byte)76, (byte)151,(byte)111,(byte)167,(byte)220,(byte)162,(byte)7, (byte)249,(byte)12, (byte)201, (byte)171,(byte)58, (byte)170,(byte)26, (byte)149,(byte)224,(byte)135,(byte)201,(byte)186,(byte)201, (byte)253,(byte)153,(byte)248,(byte)148,(byte)171,(byte)197,(byte)70, (byte)179,(byte)127,(byte)210, (byte)30, (byte)172,(byte)207,(byte)179,(byte)140,(byte)240,(byte)244,(byte)2, (byte)24, (byte)156, (byte)116,(byte)6, (byte)237,(byte)42, (byte)221,(byte)201,(byte)244,(byte)207,(byte)123,(byte)19, (byte)189,(byte)58, (byte)189,(byte)107,(byte)223,(byte)44, (byte)230,(byte)114,(byte)115,(byte)194, (byte)189,(byte)163,(byte)189,(byte)224,(byte)161,(byte)221,(byte)40, (byte)29, (byte)73, (byte)244, (byte)231,(byte)213,(byte)139,(byte)178,(byte)248,(byte)84, (byte)137,(byte)65, (byte)124,(byte)98, (byte)248,(byte)62, (byte)229,(byte)86, (byte)128,(byte)57, (byte)106,(byte)38, (byte)193,(byte)185, (byte)10, (byte)162,(byte)0, (byte)0, (byte)0, (byte)1, (byte)0, (byte)5, (byte)88, (byte)46, (byte)53, (byte)48, (byte)57, (byte)0, (byte)0, (byte)2, (byte)72, (byte)48, (byte)130,(byte)2, (byte)68, (byte)48, (byte)130,(byte)1, (byte)173,(byte)2, (byte)4, (byte)72, (byte)76, (byte)18, (byte)25, (byte)48, (byte)13, (byte)6, (byte)9, (byte)42, (byte)134,(byte)72, (byte)134,(byte)247, (byte)13, (byte)1, (byte)1, (byte)4, (byte)5, (byte)0, (byte)48, (byte)105,(byte)49, (byte)16, (byte)48, (byte)14, (byte)6, (byte)3, (byte)85, (byte)4, (byte)6, (byte)19, (byte)7, (byte)67, (byte)111,(byte)117,(byte)110,(byte)116,(byte)114,(byte)121,(byte)49, (byte)17, (byte)48, (byte)15, (byte)6, (byte)3, (byte)85, (byte)4, (byte)7, (byte)19, (byte)8, (byte)76, (byte)111,(byte)99, (byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)28, (byte)48, (byte)26, (byte)6, (byte)3, (byte)85, (byte)4, (byte)11, (byte)19, (byte)19, (byte)79, (byte)114,(byte)103,(byte)97, (byte)110,(byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)97, (byte)108, (byte)32, (byte)85, (byte)110,(byte)105,(byte)116,(byte)49, (byte)21, (byte)48, (byte)19, (byte)6, (byte)3, (byte)85, (byte)4, (byte)10, (byte)19, (byte)12, (byte)79, (byte)114,(byte)103,(byte)97, (byte)110,(byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)13, (byte)48, (byte)11, (byte)6, (byte)3, (byte)85, (byte)4, (byte)3, (byte)19, (byte)4, (byte)78, (byte)97, (byte)109,(byte)101,(byte)48, (byte)30, (byte)23, (byte)13, (byte)48, (byte)56, (byte)48, (byte)54, (byte)48, (byte)56, (byte)49, (byte)55, (byte)48, (byte)56, (byte)52, (byte)49, (byte)90, (byte)23, (byte)13, (byte)48, (byte)57, (byte)48, (byte)54, (byte)48, (byte)56, (byte)49, (byte)55, (byte)48, (byte)56, (byte)52, (byte)49, (byte)90, (byte)48, (byte)105,(byte)49, (byte)16, (byte)48, (byte)14, (byte)6, (byte)3, (byte)85, (byte)4, (byte)6, (byte)19, (byte)7, (byte)67, (byte)111, (byte)117,(byte)110,(byte)116,(byte)114,(byte)121,(byte)49, (byte)17, (byte)48, (byte)15, (byte)6, (byte)3, (byte)85, (byte)4, (byte)7, (byte)19, (byte)8, (byte)76, (byte)111,(byte)99, (byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)28, (byte)48, (byte)26, (byte)6, (byte)3, (byte)85, (byte)4, (byte)11, (byte)19, (byte)19, (byte)79, (byte)114,(byte)103,(byte)97, (byte)110, (byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)97, (byte)108,(byte)32, (byte)85, (byte)110,(byte)105,(byte)116,(byte)49, (byte)21, (byte)48, (byte)19, (byte)6, (byte)3, (byte)85, (byte)4, (byte)10, (byte)19, (byte)12, (byte)79, (byte)114,(byte)103,(byte)97, (byte)110, (byte)105,(byte)122,(byte)97, (byte)116,(byte)105,(byte)111,(byte)110,(byte)49, (byte)13, (byte)48, (byte)11, (byte)6, (byte)3, (byte)85, (byte)4, (byte)3, (byte)19, (byte)4, (byte)78, (byte)97, (byte)109,(byte)101,(byte)48, (byte)129,(byte)159,(byte)48, (byte)13, (byte)6, (byte)9, (byte)42, (byte)134,(byte)72, (byte)134,(byte)247,(byte)13, (byte)1, (byte)1, (byte)1, (byte)5, (byte)0, (byte)3, (byte)129,(byte)141,(byte)0, (byte)48, (byte)129,(byte)137,(byte)2, (byte)129,(byte)129, (byte)0, (byte)137,(byte)239,(byte)22, (byte)193,(byte)171,(byte)79, (byte)177,(byte)85, (byte)159, (byte)210,(byte)81, (byte)174,(byte)63, (byte)210,(byte)57, (byte)43, (byte)172,(byte)130,(byte)205, (byte)144,(byte)207,(byte)100,(byte)16, (byte)69, (byte)78, (byte)72, (byte)22, (byte)155,(byte)44, (byte)146,(byte)252,(byte)202,(byte)119,(byte)199,(byte)69, (byte)38, (byte)48, (byte)38, (byte)39, (byte)46, (byte)119,(byte)219,(byte)200,(byte)105,(byte)216,(byte)188,(byte)162,(byte)175,(byte)74, (byte)43, (byte)175,(byte)6, (byte)148,(byte)131,(byte)125,(byte)226,(byte)198,(byte)239,(byte)115, (byte)204,(byte)196,(byte)28, (byte)189,(byte)108,(byte)236,(byte)29, (byte)132,(byte)72, (byte)207, (byte)238,(byte)3, (byte)97, (byte)223,(byte)227,(byte)82, (byte)115,(byte)202,(byte)134,(byte)43, (byte)242,(byte)83, (byte)70, (byte)226,(byte)172,(byte)162,(byte)177,(byte)183,(byte)128,(byte)126, (byte)164,(byte)233,(byte)250,(byte)230,(byte)18, (byte)177,(byte)126,(byte)40, (byte)36, (byte)30, (byte)169,(byte)124,(byte)126,(byte)203,(byte)23, (byte)252,(byte)38, (byte)55, (byte)250,(byte)181, (byte)232,(byte)168,(byte)84, (byte)232,(byte)140,(byte)85, (byte)119,(byte)163,(byte)255,(byte)117, (byte)133,(byte)174,(byte)51, (byte)195,(byte)8, (byte)174,(byte)200,(byte)142,(byte)43, (byte)2, (byte)3, (byte)1, (byte)0, (byte)1, (byte)48, (byte)13, (byte)6, (byte)9, (byte)42, (byte)134, (byte)72, (byte)134,(byte)247,(byte)13, (byte)1, (byte)1, (byte)4, (byte)5, (byte)0, (byte)3, (byte)129,(byte)129,(byte)0, (byte)9, (byte)240,(byte)8, (byte)65, (byte)178,(byte)238,(byte)119, (byte)127,(byte)249,(byte)164,(byte)9, (byte)159,(byte)110,(byte)132,(byte)177,(byte)76, (byte)239, (byte)164,(byte)27, (byte)130,(byte)174,(byte)97, (byte)100,(byte)2, (byte)154,(byte)231,(byte)44, (byte)217,(byte)30, (byte)210,(byte)42, (byte)221,(byte)225,(byte)114,(byte)205,(byte)165,(byte)152, (byte)188,(byte)232,(byte)1, (byte)128,(byte)143,(byte)116,(byte)113,(byte)128,(byte)50, (byte)199, (byte)80, (byte)16, (byte)172,(byte)112,(byte)129,(byte)236,(byte)34, (byte)189,(byte)106,(byte)79, (byte)152,(byte)67, (byte)233,(byte)61, (byte)114,(byte)137,(byte)40, (byte)157,(byte)233,(byte)83, (byte)123,(byte)28, (byte)138,(byte)168,(byte)46, (byte)151,(byte)36, (byte)177,(byte)7, (byte)22, (byte)148,(byte)253,(byte)80, (byte)144,(byte)122,(byte)52, (byte)104,(byte)196,(byte)15, (byte)225, (byte)148,(byte)136,(byte)193,(byte)68, (byte)133,(byte)113,(byte)48, (byte)244,(byte)8, (byte)64, (byte)117,(byte)110,(byte)115,(byte)80, (byte)110,(byte)105,(byte)56, (byte)20, (byte)170,(byte)125, (byte)182,(byte)159,(byte)190,(byte)4, (byte)173,(byte)193,(byte)200,(byte)153,(byte)246,(byte)155, (byte)249,(byte)33, (byte)180,(byte)233,(byte)48, (byte)109,(byte)55, (byte)208,(byte)209,(byte)196, (byte)16, (byte)23, (byte)172,(byte)125,(byte)207,(byte)94, (byte)238,(byte)23, (byte)38, (byte)60, (byte)58, (byte)92, (byte)244,(byte)100,(byte)145,(byte)44, (byte)204,(byte)92, (byte)21, (byte)136, (byte)39, }; public static class AnonymousTrustManager implements X509TrustManager { public boolean isClientTrusted(X509Certificate[] cert) { return true; } public boolean isServerTrusted(X509Certificate[] cert) { return true; } public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } } private static SSLContext sslContext; private static SocketFactory factory; static { try { KeyStore store = KeyStore.getInstance("JKS"); KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("SunX509"); sslContext = SSLContext.getInstance("TLS");//SSLv3"); InputStream stream = new ByteArrayInputStream(CERTIFICATE); X509TrustManager trustManager = new AnonymousTrustManager(); X509TrustManager[] trustManagers = new X509TrustManager[]{trustManager}; store.load(stream, "password".toCharArray()); keyFactory.init(store, "password".toCharArray()); sslContext.init(keyFactory.getKeyManagers(), trustManagers, null); factory = sslContext.getSocketFactory(); }catch(Exception e) { e.printStackTrace(); } } public SSLContext getServerSSLContext() { return sslContext; } public SocketFactory getClientSocketFactory() { return factory; } public static void main(String[] list) throws Exception { FileOutputStream out = new FileOutputStream("c:\\client"); final PrintStream console = System.out; OutputStream dup = new FilterOutputStream(out) { public void write(int off) throws IOException { console.write(off); out.write(off); } public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); console.write(b, off, len); } public void flush() throws IOException { out.flush(); console.flush(); } public void close() throws IOException { out.close(); } }; PrintStream p = new PrintStream(dup, true); System.setOut(p); System.setErr(p); Socket socket = factory.createSocket("localhost", 9091); OutputStream sockOut = socket.getOutputStream(); sockOut.write("GET /tmp/amazon.htm HTTP/1.1\r\nConnection: keep-alive\r\n\r\n".getBytes("ISO-8859-1")); sockOut.flush(); InputStream in = socket.getInputStream(); byte[] buf = new byte[1024]; int all = 0; int count = 0; while((count = in.read(buf)) != -1) { all += count; if(all >= 564325) { break; } System.out.write(buf, 0, count); System.out.flush(); } console.println(">>>>>>>>>>>>>> ALL=["+all+"]"); System.err.println("FINISHED READING"); Thread.sleep(10000); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/PartListConsumerTest.java0000644000175000017500000001733211417313373031164 0ustar jamespagejamespagepackage org.simpleframework.http.core; import junit.framework.TestCase; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayAllocator; public class PartListConsumerTest extends TestCase { private static final String SIMPLE = "--AaB03x\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt ...\r\n"+ "--AaB03x--\r\n"; private static final String NORMAL = "--AaB03x\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt\r\n"+ "--AaB03x\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file2.txt\r\n"+ "--AaB03x\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file3.txt ...\r\n"+ "--AaB03x--\r\n"; private static final String MIXED = "--AaB03x\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt\r\n"+ "--AaB03x\r\n"+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file2.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file3.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file4.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y--\r\n"+ "--AaB03x--\r\n"; public void n_testSimple() throws Exception { PartList list = new PartList(); PartListConsumer consumer = new PartListConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8")); Cursor cursor = new StreamCursor(SIMPLE); while(!consumer.isFinished()) { consumer.consume(cursor); } assertEquals(list.size(), 1); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'"); assertEquals(list.get(0).getContent(), "example contents of file1.txt ..."); assertEquals(cursor.ready(), -1); assertEquals(consumer.getContent(), SIMPLE); } public void testNormal() throws Exception { PartList list = new PartList(); PartListConsumer consumer = new PartListConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8")); Cursor cursor = new StreamCursor(NORMAL); while(!consumer.isFinished()) { consumer.consume(cursor); } assertEquals(list.size(), 3); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'"); assertEquals(list.get(0).getContent(), "example contents of file1.txt"); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getContentType().getSecondary(), "plain"); assertEquals(list.get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'"); assertEquals(list.get(1).getContent(), "example contents of file2.txt"); assertEquals(list.get(2).getContentType().getPrimary(), "text"); assertEquals(list.get(2).getContentType().getSecondary(), "plain"); assertEquals(list.get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'"); assertEquals(list.get(2).getContent(), "example contents of file3.txt ..."); assertEquals(cursor.ready(), -1); assertEquals(consumer.getContent(), NORMAL); } public void testMixed() throws Exception { PartList list = new PartList(); PartListConsumer consumer = new PartListConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8")); Cursor cursor = new StreamCursor(MIXED); while(!consumer.isFinished()) { consumer.consume(cursor); } assertEquals(list.size(), 4); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'"); assertEquals(list.get(0).getContent(), "example contents of file1.txt"); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getContentType().getSecondary(), "plain"); assertEquals(list.get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'"); assertEquals(list.get(1).getContent(), "example contents of file2.txt ..."); assertEquals(list.get(2).getContentType().getPrimary(), "text"); assertEquals(list.get(2).getContentType().getSecondary(), "plain"); assertEquals(list.get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'"); assertEquals(list.get(2).getContent(), "example contents of file3.txt ..."); assertEquals(list.get(3).getContentType().getPrimary(), "text"); assertEquals(list.get(3).getContentType().getSecondary(), "plain"); assertEquals(list.get(3).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file4.txt'"); assertEquals(list.get(3).getContent(), "example contents of file4.txt ..."); assertEquals(cursor.ready(), -1); assertEquals(consumer.getContent(), MIXED); } public void testDribble() throws Exception { PartList list = new PartList(); PartListConsumer consumer = new PartListConsumer(new ArrayAllocator(), list, "AaB03x".getBytes("UTF-8")); Cursor cursor = new DribbleCursor(new StreamCursor(NORMAL), 1); while(!consumer.isFinished()) { consumer.consume(cursor); } assertEquals(list.size(), 3); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'"); assertEquals(list.get(0).getContent(), "example contents of file1.txt"); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getContentType().getSecondary(), "plain"); assertEquals(list.get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'"); assertEquals(list.get(1).getContent(), "example contents of file2.txt"); assertEquals(list.get(2).getContentType().getPrimary(), "text"); assertEquals(list.get(2).getContentType().getSecondary(), "plain"); assertEquals(list.get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'"); assertEquals(list.get(2).getContent(), "example contents of file3.txt ..."); assertEquals(cursor.ready(), -1); assertEquals(consumer.getContent(), NORMAL); } public static void main(String[] list) throws Exception { new PartListConsumerTest().testMixed(); } }simple-http-4.1.21/test/src/org/simpleframework/http/core/MockSelector.java0000644000175000017500000000203511417313373027432 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; public class MockSelector implements Selector { private boolean ready; private boolean sleep; private boolean start; private boolean initiated; private boolean stop; public void start(Channel channel) throws IOException { initiated = true; } public void ready(Collector collector) throws IOException { ready = true; } public void select(Collector collector) throws IOException { sleep = true; } public void start(Collector collector) throws IOException { start = true; } public void stop() throws IOException { stop = true; } public boolean isStopped() { return stop; } public boolean isInitiated() { return initiated; } public boolean isReady() { return ready; } public boolean isSleep() { return sleep; } public boolean isStart() { return start; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/RequestTest.java0000644000175000017500000001776011417313373027343 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.nio.channels.SocketChannel; import java.util.List; import java.util.Map; import junit.framework.TestCase; import org.simpleframework.http.Form; import org.simpleframework.http.Part; import org.simpleframework.http.Request; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayAllocator; import org.simpleframework.util.lease.Lease; public class RequestTest extends TestCase { private static final String HEADER = "POST /index.html?a=b&c=d&e=f&g=h&a=1 HTTP/1.0\r\n"+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n"; private static final String BODY = "--AaB03x\r\n"+ "Content-Disposition: file; name=\"file1\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt\r\n"+ "--AaB03x\r\n"+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"file2\"; filename=\"file2.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file2.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"file3\"; filename=\"file3.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file3.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"file4\"; filename=\"file4.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y--\r\n"+ "--AaB03x--\r\n"; private static final byte[] PAYLOAD = (HEADER + BODY).getBytes(); public void testPayload() throws Exception { long start = System.currentTimeMillis(); for(int i = 1; i < 8192; i++) { testPayload(i); } System.err.printf("time=%s%n",(System.currentTimeMillis() - start)); } public void testPerformance() throws Exception { long start = System.currentTimeMillis(); for(int i = 1; i < 10000; i++) { testPayload(8192); } System.err.printf("time=%s%n",(System.currentTimeMillis() - start)); } public void testPayload(int dribble) throws Exception { System.out.println("Testing dribbling cursor of "+dribble+" ..."); Cursor cursor = new StreamCursor(PAYLOAD); if(dribble < PAYLOAD.length) { cursor = new DribbleCursor(cursor, dribble); } Channel channel = new MockChannel(cursor); MockSelector selector = new MockSelector(); Collector body = new EntityCollector(new ArrayAllocator(), null, channel); while(!selector.isReady()) { body.collect(selector); } Request request = new RequestEntity(body, null); Form form = request.getForm(); List list = form.getParts(); assertEquals(form.get("a"), "b"); assertEquals(form.get("c"), "d"); assertEquals(form.get("e"), "f"); assertEquals(form.get("g"), "h"); assertEquals(form.getAll("a").size(), 2); assertEquals(form.getAll("a").get(0), "b"); assertEquals(form.getAll("a").get(1), "1"); assertEquals(request.getTarget(), "/index.html?a=b&c=d&e=f&g=h&a=1"); assertEquals(request.getMethod(), "POST"); assertEquals(request.getMajor(), 1); assertEquals(request.getMinor(), 0); assertEquals(request.getContentType().getPrimary(), "multipart"); assertEquals(request.getContentType().getSecondary(), "form-data"); assertEquals(request.getValue("Host"), "some.host.com"); assertEquals(request.getValues("Accept").size(), 4); assertEquals(request.getValues("Accept").get(0), "image/gif"); assertEquals(request.getValues("Accept").get(1), "image/png"); assertEquals(request.getValues("Accept").get(2), "image/jpeg"); assertEquals(request.getValues("Accept").get(3), "*"); assertEquals(request.getCookie("UID").getValue(), "1234-5678"); assertEquals(request.getCookie("UID").getPath(), "/"); assertEquals(request.getCookie("UID").getDomain(), ".host.com"); assertEquals(request.getCookie("NAME").getValue(), "Niall Gallagher"); assertEquals(request.getCookie("NAME").getPath(), "/"); assertEquals(request.getCookie("NAME").getDomain(), null); assertEquals(list.size(), 4); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "file; name=\"file1\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\""); assertEquals(list.get(0).getName(), "file1"); assertEquals(list.get(0).getFileName(), "file1.txt"); assertEquals(list.get(0).isFile(), true); assertEquals(list.get(0).getContent(), "example contents of file1.txt"); assertEquals(request.getForm().getPart("file1").getContent(), "example contents of file1.txt"); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getContentType().getSecondary(), "plain"); assertEquals(list.get(1).getHeader("Content-Disposition"), "file; name=\"file2\"; filename=\"file2.txt\""); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getName(), "file2"); assertEquals(list.get(1).getFileName(), "file2.txt"); assertEquals(list.get(1).isFile(), true); assertEquals(list.get(1).getContent(), "example contents of file2.txt ..."); assertEquals(request.getForm().getPart("file2").getContent(), "example contents of file2.txt ..."); assertEquals(list.get(2).getContentType().getSecondary(), "plain"); assertEquals(list.get(2).getHeader("Content-Disposition"), "file; name=\"file3\"; filename=\"file3.txt\""); assertEquals(list.get(2).getName(), "file3"); assertEquals(list.get(2).getFileName(), "file3.txt"); assertEquals(list.get(2).isFile(), true); assertEquals(list.get(2).getContent(), "example contents of file3.txt ..."); assertEquals(request.getForm().getPart("file3").getContent(), "example contents of file3.txt ..."); assertEquals(list.get(3).getContentType().getPrimary(), "text"); assertEquals(list.get(3).getContentType().getSecondary(), "plain"); assertEquals(list.get(3).getHeader("Content-Disposition"), "file; name=\"file4\"; filename=\"file4.txt\""); assertEquals(list.get(3).getName(), "file4"); assertEquals(list.get(3).getFileName(), "file4.txt"); assertEquals(list.get(3).isFile(), true); assertEquals(list.get(3).getContent(), "example contents of file4.txt ..."); assertEquals(request.getForm().getPart("file4").getContent(), "example contents of file4.txt ..."); assertEquals(cursor.ready(), -1); assertEquals(request.getContent(), BODY); } public static class MockChannel implements Channel { private Cursor cursor; public MockChannel(Cursor cursor) { this.cursor = cursor; } public boolean isSecure() { return false; } public Lease getLease() { return null; } public Cursor getCursor() { return cursor; } public Sender getSender() { return null; } public Map getAttributes() { return null; } public void close() {} public SocketChannel getSocket() { return null; } } } simple-http-4.1.21/test/src/org/simpleframework/http/core/Chunker.java0000644000175000017500000000236011417313373026440 0ustar jamespagejamespage package org.simpleframework.http.core; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; class Chunker extends FilterOutputStream { private byte[] size = {'0', '0', '0', '0', '0', '0', '0', '0', 13, 10}; private byte[] index = {'0', '1', '2', '3', '4', '5','6', '7', '8', '9', 'a', 'b', 'c', 'd','e', 'f'}; private byte[] zero = {'0', 13, 10, 13, 10}; public Chunker(OutputStream out){ super(out); } public void write(int octet) throws IOException { byte[] swap = new byte[1]; swap[0] = (byte)octet; write(swap); } public void write(byte[] buf, int off, int len) throws IOException { int pos = 7; if(len > 0) { for(int num = len; num > 0; num >>>= 4){ size[pos--] = index[num & 0xf]; } String text = String.format("%s; %s\r\n", Integer.toHexString(len), len); out.write(text.getBytes("ISO-8859-1")); out.write(buf, off, len); out.write(size, 8, 2); } } public void close() throws IOException { out.write(zero); out.close(); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ResponseBuilder.java0000644000175000017500000000151011417313373030142 0ustar jamespagejamespagepackage org.simpleframework.http.core; import org.simpleframework.http.StatusLine; import org.simpleframework.http.session.Session; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.ArrayAllocator; class ResponseBuilder implements Builder { private Header header; private Body body; public ResponseBuilder() { super(); } public void setHeader(Header header) { this.header = header; } public Header getHeader() { return header; } public void setBody(Body body) { this.body = body; } public Body getBody() { return body; } public Channel getChannel() { return null; } public Session getSession(boolean create) { return null; } public void close() { } } simple-http-4.1.21/test/src/org/simpleframework/http/core/FormBuilderTest.java0000644000175000017500000000350011417313373030110 0ustar jamespagejamespagepackage org.simpleframework.http.core; import junit.framework.TestCase; import org.simpleframework.http.Form; public class FormBuilderTest extends TestCase{ public void testBuilder() throws Exception { MockRequest request = new MockRequest(); request.setTarget("/path?a=query_A&b=query_B&c=query_C&d=query_D"); request.setContentType("application/x-www-form-urlencoded"); request.setContent("a=post_A&c=post_C&e=post_E"); MockBody body = new MockBody(); MockEntity entity = new MockEntity(body); FormCreator builder = new FormCreator(request, entity); PartList list = body.getParts(); list.add(new MockPart("a", "part_A", false)); list.add(new MockPart("b", "part_B", false)); list.add(new MockPart("c", "part_C", false)); list.add(new MockPart("f", "part_F", true)); list.add(new MockPart("g", "part_G", false)); Form form = builder.getInstance(); assertEquals(form.getAll("a").size(), 3); assertEquals(form.getAll("b").size(), 2); assertEquals(form.getAll("c").size(), 3); assertEquals(form.getAll("e").size(), 1); assertEquals(form.getPart("a").getContent(), "part_A"); assertEquals(form.getPart("b").getContent(), "part_B"); assertEquals(form.getPart("c").getContent(), "part_C"); assertEquals(form.getPart("f").getContent(), "part_F"); assertEquals(form.getPart("g").getContent(), "part_G"); assertEquals(form.get("a"), "query_A"); assertEquals(form.get("b"), "query_B"); assertEquals(form.get("c"), "query_C"); assertEquals(form.get("d"), "query_D"); assertEquals(form.get("e"), "post_E"); assertEquals(form.get("f"), null); assertEquals(form.get("g"), "part_G"); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ProducerExceptionTest.java0000644000175000017500000000100111417313373031332 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import junit.framework.TestCase; public class ProducerExceptionTest extends TestCase { public void testException() { try { throw new IOException("Error"); }catch(Exception main) { try { throw new ProducerException("Wrapper", main); }catch(Exception cause) { cause.printStackTrace(); assertEquals(cause.getCause(), main); } } } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ResponseConsumer.java0000644000175000017500000000365411417313373030362 0ustar jamespagejamespagepackage org.simpleframework.http.core; import org.simpleframework.http.StatusLine; class ResponseConsumer extends RequestConsumer implements StatusLine { private int status; private String text; public ResponseConsumer() { super(); } private void status() { while(pos < count) { if(!digit(array[pos])) { break; } status *= 10; status += array[pos]; status -= '0'; pos++; } } private void text() { StringBuilder builder = new StringBuilder(); while(pos < count) { if(terminal(array[pos])) { pos += 2; break; } builder.append((char) array[pos]); pos++; } text = builder.toString(); } public String getText() { return text; } public void setText(String text) { this.text = text; } public int getStatus() { return status; } public void setCode(int status) { this.status = status; } @Override protected void add(String name, String value) { if(equal("Set-Cookie", name)) { // A=b; version=1; path=/; String[] list = value.split(";"); // "A=b", "version=1", "path=/" if(list.length > 0) { String[] pair = list[0].split("="); if(pair.length > 1) { header.setCookie(pair[0], pair[1]); // "A", "b" } } } super.add(name, value); } @Override protected void process() { version(); // HTTP/1.1 adjust(); status(); // 200 adjust(); text(); // OK adjust(); headers(); } public int getCode() { return status; } public void setMajor(int major) { this.major = major; } public void setMinor(int minor) { this.minor = minor; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/SecurePolicyTest.java0000644000175000017500000000141611417313373030310 0ustar jamespagejamespagepackage org.simpleframework.http.core; import junit.framework.TestCase; import org.simpleframework.http.Cookie; public class SecurePolicyTest extends TestCase { public void testPolicy() { Header header = new RequestConsumer(); Policy policy = new SecurePolicy(header); Cookie cookie = policy.getSession(false); assertNull(cookie); cookie = policy.getSession(true); assertNotNull(cookie); assertEquals(cookie.getName(), "JSESSIONID"); assertNotNull(cookie.getValue()); assertEquals(cookie, policy.getSession(false)); assertEquals(cookie, policy.getSession(true)); System.out.println(cookie); System.out.println(cookie.toClientString()); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockSocket.java0000644000175000017500000000166611417313373027113 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.SocketException; public class MockSocket extends Socket { private Socket socket; private OutputStream out; public MockSocket(Socket socket) { this(socket, System.err); } public MockSocket(Socket socket, OutputStream out){ this.socket = socket; this.out = out; } @Override public void setSoTimeout(int delay) throws SocketException { socket.setSoTimeout(delay); } @Override public int getSoTimeout() throws SocketException { return socket.getSoTimeout(); } public InputStream getInputStream() throws IOException { return socket.getInputStream(); } public OutputStream getOutputStream() { return out; } } simple-http-4.1.21/test/src/org/simpleframework/http/core/MockSender.java0000644000175000017500000000336311417313373027077 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import java.nio.ByteBuffer; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayBuffer; import org.simpleframework.util.buffer.Buffer; public class MockSender implements Sender { private Buffer buffer; public MockSender() { this(1024); } public MockSender(int size) { this.buffer = new ArrayBuffer(size); } public Buffer getBuffer() { return buffer; } public Cursor getCursor() throws IOException { return new StreamCursor(buffer.encode("UTF-8")); } public void send(byte[] array) throws IOException { buffer.append(array); } public void send(byte[] array, int off, int len) throws IOException { buffer.append(array, off, len); } public void flush() throws IOException { return; } public void close() throws IOException { return; } public String toString() { return buffer.toString(); } public boolean isOpen() throws Exception { return true; } public void send(ByteBuffer source) throws IOException { int mark = source.position(); int limit = source.limit(); byte[] array = new byte[limit - mark]; source.get(array, 0, array.length); buffer.append(array); } public void send(ByteBuffer source, int off, int len) throws IOException { int mark = source.position(); int limit = source.limit(); if(limit - mark < len) { len = limit - mark; } byte[] array = new byte[len]; source.get(array, 0, len); buffer.append(array); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ContentConsumerTest.java0000644000175000017500000000564511417313373031040 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import junit.framework.TestCase; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.ArrayAllocator; import org.simpleframework.util.buffer.Buffer; public class ContentConsumerTest extends TestCase implements Allocator { private static final byte[] BOUNDARY = { 'A', 'a', 'B', '0', '3', 'x' }; private Buffer buffer; public Buffer allocate() { return buffer; } public Buffer allocate(int size) { return buffer; } public void testContent() throws Exception { testContent(1, 1); for(int i = 1; i < 1000; i++) { testContent(i, i); } for(int i = 20; i < 1000; i++) { for(int j = 1; j < 19; j++) { testContent(i, j); } } testContent(10, 10); testContent(100, 2); } public void testContent(int entitySize, int dribble) throws Exception { MockSegment segment = new MockSegment(); PartList list = new PartList(); ContentConsumer consumer = new ContentConsumer(this, segment, list, BOUNDARY); StringBuffer buf = new StringBuffer(); segment.add("Content-Type", "text/plain"); segment.add("Content-ID", ""); for(int i = 0, line = 0; buf.length() < entitySize; i++) { String text = String.valueOf(i); line += text.length(); buf.append(text); if(line >= 48) { buf.append("\n"); line = 0; } } // Get request body without boundary String requestBody = buf.toString(); // Add the boundary to the request body buf.append("\r\n--"); buf.append(new String(BOUNDARY, 0, BOUNDARY.length, "UTF-8")); buffer = new ArrayAllocator().allocate(); DribbleCursor cursor = new DribbleCursor(new StreamCursor(buf.toString()), dribble); while(!consumer.isFinished()) { consumer.consume(cursor); } byte[] consumedBytes = buffer.encode("UTF-8").getBytes("UTF-8"); String consumedBody = new String(consumedBytes, 0, consumedBytes.length, "UTF-8"); assertEquals(String.format("Failed for entitySize=%s and dribble=%s", entitySize, dribble), consumedBody, requestBody); assertEquals(cursor.read(), '\r'); assertEquals(cursor.read(), '\n'); assertEquals(cursor.read(), '-'); assertEquals(cursor.read(), '-'); assertEquals(cursor.read(), BOUNDARY[0]); assertEquals(cursor.read(), BOUNDARY[1]); assertEquals(consumer.getContentType().getPrimary(), "text"); assertEquals(consumer.getContentType().getSecondary(), "plain"); } public void close() throws IOException { // TODO Auto-generated method stub } } simple-http-4.1.21/test/src/org/simpleframework/http/core/ReactorTest.java0000644000175000017500000001433211417313373027302 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.IOException; import java.nio.channels.SocketChannel; import java.util.List; import java.util.Map; import junit.framework.TestCase; import org.simpleframework.http.Part; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayAllocator; import org.simpleframework.util.lease.Lease; public class ReactorTest extends TestCase implements Selector { private static final String SOURCE = "POST /index.html HTTP/1.0\r\n"+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n" + "--AaB03x\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt\r\n"+ "--AaB03x\r\n"+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file2.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file3.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file3.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: file; name=\"pics\"; filename=\"file4.txt\"\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y--\r\n"+ "--AaB03x--\r\n"; public static class TestChannel implements Channel { private Cursor cursor; public TestChannel(StreamCursor cursor, int dribble) { this.cursor = new DribbleCursor(cursor, dribble); } public boolean isSecure() { return false; } public Lease getLease() { return null; } public Cursor getCursor() { return cursor; } public Sender getSender() { return null; } public Map getAttributes() { return null; } public void close() {} public SocketChannel getSocket() { return null; } } public void testHandler() throws Exception { testHandler(1024); for(int i = 10; i < 2048; i++) { testHandler(i); } } public void testHandler(int dribble) throws Exception { StreamCursor cursor = new StreamCursor(SOURCE); Channel channel = new TestChannel(cursor, dribble); start(channel); assertEquals(cursor.ready(), -1); } public void start(Channel channel) throws IOException { start(new EntityCollector(new ArrayAllocator(), null, channel)); } public void start(Collector collector) throws IOException { collector.collect(this); } public void select(Collector collector) throws IOException { collector.collect(this); } public void ready(Collector collector) throws IOException { Entity entity = collector; Channel channel = entity.getChannel(); Cursor cursor = channel.getCursor(); Header header = entity.getHeader(); Body body = entity.getBody(); List list = body.getParts(); assertEquals(header.getTarget(), "/index.html"); assertEquals(header.getMethod(), "POST"); assertEquals(header.getMajor(), 1); assertEquals(header.getMinor(), 0); assertEquals(header.getContentType().getPrimary(), "multipart"); assertEquals(header.getContentType().getSecondary(), "form-data"); assertEquals(header.getValue("Host"), "some.host.com"); assertEquals(header.getValues("Accept").size(), 4); assertEquals(header.getValues("Accept").get(0), "image/gif"); assertEquals(header.getValues("Accept").get(1), "image/png"); assertEquals(header.getValues("Accept").get(2), "image/jpeg"); assertEquals(header.getValues("Accept").get(3), "*"); assertEquals(list.size(), 4); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file1.txt\"; modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\""); assertEquals(list.get(0).getName(), "pics"); assertEquals(list.get(0).getFileName(), "file1.txt"); assertEquals(list.get(0).isFile(), true); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getContentType().getSecondary(), "plain"); assertEquals(list.get(1).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file2.txt\""); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getName(), "pics"); assertEquals(list.get(1).getFileName(), "file2.txt"); assertEquals(list.get(1).isFile(), true); assertEquals(list.get(2).getContentType().getSecondary(), "plain"); assertEquals(list.get(2).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file3.txt\""); assertEquals(list.get(2).getName(), "pics"); assertEquals(list.get(2).getFileName(), "file3.txt"); assertEquals(list.get(2).isFile(), true); assertEquals(list.get(3).getContentType().getPrimary(), "text"); assertEquals(list.get(3).getContentType().getSecondary(), "plain"); assertEquals(list.get(3).getHeader("Content-Disposition"), "file; name=\"pics\"; filename=\"file4.txt\""); assertEquals(list.get(3).getName(), "pics"); assertEquals(list.get(3).getFileName(), "file4.txt"); assertEquals(list.get(3).isFile(), true); assertEquals(cursor.ready(), -1); } public void stop() throws IOException {} } simple-http-4.1.21/test/src/org/simpleframework/http/core/PayloadTest.java0000644000175000017500000001052311417313373027272 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.util.List; import junit.framework.TestCase; import org.simpleframework.http.Part; import org.simpleframework.http.core.RequestTest.MockChannel; import org.simpleframework.transport.Cursor; import org.simpleframework.util.buffer.ArrayAllocator; public class PayloadTest extends TestCase { private static final String PAYLOAD = "POST /index.html HTTP/1.0\r\n"+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n" + "--AaB03x\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt\r\n"+ "--AaB03x\r\n"+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file3.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file4.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y--\r\n"+ "--AaB03x--\r\n"; public void testPayload() throws Exception { for(int i = 1; i < 4096; i++) { testPayload(i); } } public void testPayload(int dribble) throws Exception { Cursor cursor = new DribbleCursor(new StreamCursor(PAYLOAD), 10); Channel channel = new MockChannel(cursor); MockSelector selector = new MockSelector(); Collector body = new EntityCollector(new ArrayAllocator(), null, channel); long time = System.currentTimeMillis(); while(!selector.isReady()) { body.collect(selector); } System.err.println("Time taken to parse payload "+(System.currentTimeMillis() - time)+" ms"); Header header = body.getHeader(); List list = body.getBody().getParts(); assertEquals(header.getTarget(), "/index.html"); assertEquals(header.getMethod(), "POST"); assertEquals(header.getMajor(), 1); assertEquals(header.getMinor(), 0); assertEquals(header.getContentType().getPrimary(), "multipart"); assertEquals(header.getContentType().getSecondary(), "form-data"); assertEquals(header.getValue("Host"), "some.host.com"); assertEquals(header.getValues("Accept").size(), 4); assertEquals(header.getValues("Accept").get(0), "image/gif"); assertEquals(header.getValues("Accept").get(1), "image/png"); assertEquals(header.getValues("Accept").get(2), "image/jpeg"); assertEquals(header.getValues("Accept").get(3), "*"); assertEquals(list.size(), 4); assertEquals(list.get(0).getContentType().getPrimary(), "text"); assertEquals(list.get(0).getContentType().getSecondary(), "plain"); assertEquals(list.get(0).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file1.txt'"); assertEquals(list.get(1).getContentType().getPrimary(), "text"); assertEquals(list.get(1).getContentType().getSecondary(), "plain"); assertEquals(list.get(1).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file2.txt'"); assertEquals(list.get(2).getContentType().getPrimary(), "text"); assertEquals(list.get(2).getContentType().getSecondary(), "plain"); assertEquals(list.get(2).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file3.txt'"); assertEquals(list.get(3).getContentType().getPrimary(), "text"); assertEquals(list.get(3).getContentType().getSecondary(), "plain"); assertEquals(list.get(3).getHeader("Content-Disposition"), "form-data; name='pics'; filename='file4.txt'"); assertEquals(cursor.ready(), -1); } } simple-http-4.1.21/test/src/org/simpleframework/http/core/StreamCursor.java0000644000175000017500000000371411417313373027476 0ustar jamespagejamespagepackage org.simpleframework.http.core; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.simpleframework.transport.Cursor; import org.simpleframework.transport.StreamTransport; import org.simpleframework.transport.Transport; import org.simpleframework.transport.TransportCursor; public class StreamCursor implements Cursor { private TransportCursor cursor; private Transport transport; private byte[] swap; public StreamCursor(String source) throws IOException { this(source.getBytes("UTF-8")); } public StreamCursor(byte[] data) throws IOException { this(new ByteArrayInputStream(data)); } public StreamCursor(InputStream source) throws IOException { this.transport = new StreamTransport(source, new OutputStream() { public void write(int octet){} }); this.cursor = new TransportCursor(transport); this.swap = new byte[1]; } // TODO investigate this public boolean isOpen() throws IOException { return true; } public boolean isReady() throws IOException { return cursor.isReady(); } public int ready() throws IOException { return cursor.ready(); } public int read() throws IOException { if(read(swap) > 0) { return swap[0] & 0xff; } return 0; } public int read(byte[] data) throws IOException { return read(data, 0, data.length); } public int read(byte[] data, int off, int len) throws IOException { return cursor.read(data, off, len); } public int reset(int len) throws IOException { return cursor.reset(len); } public void push(byte[] data) throws IOException { push(data, 0, data.length); } public void push(byte[] data, int off, int len) throws IOException { cursor.push(data, off, len); } }simple-http-4.1.21/test/src/org/simpleframework/http/session/0000755000175000017500000000000011767603362024740 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/http/session/SessionTest.java0000644000175000017500000000316611417313373030064 0ustar jamespagejamespagepackage org.simpleframework.http.session; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; import org.simpleframework.util.lease.Lease; public class SessionTest extends TestCase { private static final int ITERATIONS = 5000; public void testSession() throws Exception { SessionProvider manager = new SessionManager(5, TimeUnit.SECONDS); for(int i = 0; i < ITERATIONS; i++) { Session session = manager.open(i); session.put("key", i); } for(int i = 0; i < ITERATIONS; i++) { Session session = manager.open(i); assertEquals(i, session.get("key")); } Thread.sleep(6000); // wait for expiry for(int i = 0; i < ITERATIONS; i++) { Session session = manager.open(i); //assertNull(session.get("key")); session.put("key", i); } for(int i = 0; i < ITERATIONS; i++) { Session session = manager.open(i); Lease lease = session.getLease(); assertEquals(i, session.get("key")); assertEquals(new Integer(i), lease.getKey()); System.err.printf("lease=[%s] expiry=[%s]%n", lease.getKey(), lease.getExpiry(TimeUnit.MILLISECONDS)); lease.cancel(); } Thread.sleep(1000); for(int i = 0; i < ITERATIONS; i++) { Session session = manager.open(i); assertNull(session.get("key")); } } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/0000755000175000017500000000000011767603362024367 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/http/parse/CookieParserTest.java0000644000175000017500000000117411417313373030453 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import java.util.ArrayList; import java.util.List; import junit.framework.TestCase; import org.simpleframework.http.Cookie; public class CookieParserTest extends TestCase { public void testParse() throws Exception { CookieParser parser = new CookieParser("blackbird={\"pos\": 1, \"size\": 0, \"load\": null}; JSESSIONID=31865d30-e252-4729-ac6f-9abdd1fb9071"); List cookies = new ArrayList(); for(Cookie cookie : parser) { System.out.println(cookie.toClientString()); cookies.add(cookie); } } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/DateParserTest.java0000644000175000017500000000236711417313373030124 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import junit.framework.TestCase; public class DateParserTest extends TestCase { /** * Sun, 06 Nov 2009 08:49:37 GMT ; RFC 822, updated by RFC 1123 * Sunday, 06-Nov-09 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 * Sun Nov 6 08:49:37 2009 ; ANSI C's asctime() format */ public void testDate() { DateParser rfc822 = new DateParser("Sun, 06 Nov 2009 08:49:37 GMT"); DateParser rfc850 = new DateParser("Sunday, 06-Nov-09 08:49:37 GMT"); DateParser asctime = new DateParser("Sun Nov 6 08:49:37 2009"); assertEquals(rfc822.toLong() >> 10, rfc850.toLong() >> 10); // shift out seconds assertEquals(rfc822.toLong() >> 10, asctime.toLong() >> 10); // shift out seconds assertEquals(rfc822.toString(), rfc850.toString()); assertEquals(rfc822.toString(), asctime.toString()); assertEquals(rfc850.toString(), "Sun, 06 Nov 2009 08:49:37 GMT"); assertEquals(rfc850.toString().length(), 29); assertEquals(rfc822.toString(), "Sun, 06 Nov 2009 08:49:37 GMT"); assertEquals(rfc822.toString().length(), 29); assertEquals(asctime.toString(), "Sun, 06 Nov 2009 08:49:37 GMT"); assertEquals(asctime.toString().length(), 29); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/ContentParserTest.java0000644000175000017500000000342111417313373030651 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import junit.framework.TestCase; import org.simpleframework.http.parse.ContentParser; public class ContentParserTest extends TestCase { private ContentParser type; protected void setUp() { type = new ContentParser(); } public void testEmpty() { assertEquals(null, type.getPrimary()); assertEquals(null, type.getSecondary()); assertEquals(null, type.getCharset()); } public void testPlain() { type.parse("text/html"); assertEquals("text", type.getPrimary()); assertEquals("html", type.getSecondary()); type.setSecondary("plain"); assertEquals("text", type.getPrimary()); assertEquals("plain", type.getSecondary()); } public void testCharset() { type.parse("text/html; charset=UTF-8"); assertEquals("text", type.getPrimary()); assertEquals("UTF-8", type.getCharset()); type.setCharset("ISO-8859-1"); assertEquals("ISO-8859-1", type.getCharset()); } public void testIgnore() { type.parse("text/html; name=value; charset=UTF-8; property=value"); assertEquals("UTF-8", type.getCharset()); assertEquals("html", type.getSecondary()); } public void testFlexibility() { type.parse(" text/html ;charset= UTF-8 ; name = value" ); assertEquals("text", type.getPrimary()); assertEquals("html", type.getSecondary()); assertEquals("UTF-8", type.getCharset()); } public void testString() { type.parse(" image/gif; name=value"); assertEquals("image/gif; name=value", type.toString()); type.parse(" text/html; charset =ISO-8859-1"); assertEquals("text/html; charset=ISO-8859-1", type.toString()); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/PathParserTest.java0000644000175000017500000000441311417313373030135 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import junit.framework.TestCase; import org.simpleframework.http.parse.PathParser; public class PathParserTest extends TestCase { private PathParser path; protected void setUp() { path = new PathParser(); } public void testEmpty() { assertEquals(null, path.getPath()); assertEquals(null, path.getExtension()); assertEquals(null, path.getName()); } public void testSegments() { path.parse("/a/b/c/d"); String[] list = path.getSegments(); assertEquals("a", list[0]); assertEquals("b", list[1]); assertEquals("c", list[2]); assertEquals("d", list[3]); } public void testSubPath() { path.parse("/0/1/2/3/4/5/6/index.html"); testSubPath(1); testSubPath(2); testSubPath(3); testSubPath(4); testSubPath(5); testSubPath(6); testSubPath(7); testSubPath(0,4); testSubPath(1,2); testSubPath(2,3); testSubPath(3,4); testSubPath(1,3); testSubPath(1,4); testSubPath(1,5); path.parse("/a/b/c/d/e/index.html"); testSubPath(1,2); testSubPath(2,3); testSubPath(3,1); testSubPath(1,3); } private void testSubPath(int from) { System.err.printf("[%s] %s: %s%n", path, from, path.getPath(from)); } private void testSubPath(int from, int to) { System.err.printf("[%s] %s, %s: %s%n", path, from, to, path.getPath(from, to)); } public void testDirectory() { path.parse("/some/directory/path/index.html"); assertEquals("/some/directory/path/", path.getDirectory()); path.parse("/some/path/README"); assertEquals("/some/path/", path.getDirectory()); } public void testNormalization() { path.parse("/path/./../index.html"); assertEquals("/", path.getDirectory()); path.parse("/path/hidden/./index.html"); assertEquals("/path/hidden/", path.getDirectory()); path.parse("/path/README"); assertEquals("/path/", path.getDirectory()); } public void testString() { path.parse("/some/path/../path/./to//a/file.txt"); assertEquals("/some/path/to/a/file.txt", path.toString()); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/QueryParserTest.java0000644000175000017500000000342611417313373030351 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import org.simpleframework.http.parse.QueryParser; import junit.framework.TestCase; public class QueryParserTest extends TestCase { private QueryParser data; protected void setUp() { data = new QueryParser(); } public void testEmptyPath() { assertEquals(0, data.size()); } public void testValue() { data.parse("a="); assertEquals(1, data.size()); assertEquals("", data.get("a")); data.parse("a=&b=c"); assertEquals(2, data.size()); assertEquals("", data.get("a")); assertEquals("c", data.get("b")); data.parse("a=b&c=d&e=f&"); assertEquals(3, data.size()); assertEquals("b", data.get("a")); assertEquals("d", data.get("c")); assertEquals("f", data.get("e")); data.clear(); data.put("a", "A"); data.put("c", "C"); data.put("x", "y"); assertEquals(3, data.size()); assertEquals("A", data.get("a")); assertEquals("C", data.get("c")); assertEquals("y", data.get("x")); } public void testValueList() { data.parse("a=1&a=2&a=3"); assertEquals(data.size(), 1); assertEquals(data.getAll("a").size(), 3); assertEquals(data.getAll("a").get(0), "1"); assertEquals(data.getAll("a").get(1), "2"); assertEquals(data.getAll("a").get(2), "3"); data.parse("a=b&c=d&c=d&a=1"); assertEquals(data.size(), 2); assertEquals(data.getAll("a").size(), 2); assertEquals(data.getAll("a").get(0), "b"); assertEquals(data.getAll("a").get(1), "1"); assertEquals(data.getAll("c").size(), 2); assertEquals(data.getAll("c").get(0), "d"); assertEquals(data.getAll("c").get(1), "d"); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/AddressParserTest.java0000644000175000017500000000515211417313373030627 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import junit.framework.TestCase; import org.simpleframework.http.Query; public class AddressParserTest extends TestCase { private AddressParser link; protected void setUp() { link = new AddressParser(); } public void testEmptyPath() { assertEquals("/", link.getPath().toString()); } public void testEmptyQuery() { Query query = link.getQuery(); assertEquals(0, query.size()); } public void testPath() { link.parse("/this/./is//some/relative/./hidden/../URI.txt"); assertEquals("/this/is/some/relative/URI.txt", link.getPath().toString()); link.parse("/this//is/a/simple/path.html?query"); assertEquals("/this/is/a/simple/path.html", link.getPath().toString()); } public void testQuery() { link.parse("/?name=value&attribute=string"); Query query = link.getQuery(); assertEquals(2, query.size()); assertEquals("value", query.get("name")); assertTrue(query.containsKey("attribute")); query.clear(); query.put("name", "change"); assertEquals("change", query.get("name")); } public void testPathParameters() { link.parse("/index.html;jsessionid=1234567890?jsessionid=query"); assertEquals("1234567890", link.getParameters().get("jsessionid")); link.parse("/path/index.jsp"); link.getParameters().put("jsessionid", "value"); assertEquals("/path/index.jsp;jsessionid=value", link.toString()); link.parse("/path"); link.getParameters().put("a", "1"); link.getParameters().put("b", "2"); link.getParameters().put("c", "3"); link.parse(link.toString()); assertEquals("1", link.getParameters().get("a")); assertEquals("2", link.getParameters().get("b")); assertEquals("3", link.getParameters().get("c")); } public void testAbsolute() { link.parse("http://domain:9090/index.html?query=value"); assertEquals("domain", link.getDomain()); link.setDomain("some.domain"); assertEquals("some.domain", link.getDomain()); assertEquals("http://some.domain:9090/index.html?query=value", link.toString()); assertEquals(9090, link.getPort()); link.parse("domain.com:80/index.html?a=b&c=d"); assertEquals("domain.com", link.getDomain()); assertEquals(80, link.getPort()); link.parse("https://secure.com/index.html"); assertEquals("https", link.getScheme()); assertEquals("secure.com", link.getDomain()); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/ListParserTest.java0000644000175000017500000000652511417313373030162 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import junit.framework.TestCase; public class ListParserTest extends TestCase { private ValueParser list; protected void setUp() { list = new ValueParser(); } public void testEmpty() { assertEquals(0, list.list().size()); } public void testQvalue() { list.parse("ISO-8859-1,utf-8;q=0.7,*;q=0.7"); assertEquals(list.list().get(0), "ISO-8859-1"); assertEquals(list.list().get(1), "utf-8"); assertEquals(list.list().get(2), "*"); } public void testPlain() { list.parse("en-gb"); assertEquals("en-gb", list.list().get(0)); list.parse("en"); assertEquals("en", list.list().get(0)); } public void testList() { list.parse("en-gb, en-us"); assertEquals(2, list.list().size()); assertEquals("en-gb", list.list().get(0)); assertEquals("en-us", list.list().get(1)); } public void testOrder() { list.parse("en-gb, en-us"); assertEquals(2, list.list().size()); assertEquals("en-gb", list.list().get(0)); assertEquals("en-us", list.list().get(1)); list.parse("da, en-gb;q=0.8, en;q=0.7"); assertEquals("da", list.list().get(0)); assertEquals("en-gb", list.list().get(1)); assertEquals("en", list.list().get(2)); list.parse("fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7"); assertEquals("en-gb", list.list().get(0)); assertEquals("en", list.list().get(1)); assertEquals("en-us", list.list().get(2)); assertEquals("fr", list.list().get(3)); list.parse("en;q=0.2, en-us;q=1.0, en-gb"); assertEquals("en-gb", list.list().get(0)); assertEquals("en-us", list.list().get(1)); assertEquals("en", list.list().get(2)); } public void testRange() { list.parse("image/gif, image/jpeg, text/html"); assertEquals(3, list.list().size()); assertEquals("image/gif", list.list().get(0)); assertEquals("text/html", list.list().get(2)); list.parse("image/gif;q=1.0, image/jpeg;q=0.8, image/png; q=1.0,*;q=0.1"); assertEquals("image/gif", list.list().get(0)); assertEquals("image/png", list.list().get(1)); assertEquals("image/jpeg", list.list().get(2)); list.parse("gzip;q=1.0, identity; q=0.5, *;q=0"); assertEquals("gzip", list.list().get(0)); assertEquals("identity", list.list().get(1)); } public void testFlexibility() { list.parse("last; quantity=1;q=0.001, first; text=\"a, b, c, d\";q=0.4"); assertEquals(2, list.list().size()); assertEquals("first; text=\"a, b, c, d\"", list.list().get(0)); assertEquals("last; quantity=1", list.list().get(1)); list.parse("image/gif, , image/jpeg, image/png;q=0.8, *"); assertEquals(4, list.list().size()); assertEquals("image/gif", list.list().get(0)); assertEquals("image/jpeg", list.list().get(1)); assertEquals("*", list.list().get(2)); assertEquals("image/png", list.list().get(3)); list.parse("first=\"\\\"a, b, c, d\\\", a, b, c, d\", third=\"a\";q=0.9,,second=2"); assertEquals(3, list.list().size()); assertEquals("first=\"\\\"a, b, c, d\\\", a, b, c, d\"", list.list().get(0)); assertEquals("second=2", list.list().get(1)); assertEquals("third=\"a\"", list.list().get(2)); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/ParameterTest.java0000644000175000017500000000342411417313373030005 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import org.simpleframework.http.parse.QueryParser; import junit.framework.TestCase; public class ParameterTest extends TestCase { private QueryParser data; protected void setUp() { data = new QueryParser(); } public void testEmptyPath() { assertEquals(0, data.size()); } public void testValue() { data.parse("a="); assertEquals(1, data.size()); assertEquals("", data.get("a")); data.parse("a=&b=c"); assertEquals(2, data.size()); assertEquals("", data.get("a")); assertEquals("c", data.get("b")); data.parse("a=b&c=d&e=f&"); assertEquals(3, data.size()); assertEquals("b", data.get("a")); assertEquals("d", data.get("c")); assertEquals("f", data.get("e")); data.clear(); data.put("a", "A"); data.put("c", "C"); data.put("x", "y"); assertEquals(3, data.size()); assertEquals("A", data.get("a")); assertEquals("C", data.get("c")); assertEquals("y", data.get("x")); } public void testValueList() { data.parse("a=1&a=2&a=3"); assertEquals(data.size(), 1); assertEquals(data.getAll("a").size(), 3); assertEquals(data.getAll("a").get(0), "1"); assertEquals(data.getAll("a").get(1), "2"); assertEquals(data.getAll("a").get(2), "3"); data.parse("a=b&c=d&c=d&a=1"); assertEquals(data.size(), 2); assertEquals(data.getAll("a").size(), 2); assertEquals(data.getAll("a").get(0), "b"); assertEquals(data.getAll("a").get(1), "1"); assertEquals(data.getAll("c").size(), 2); assertEquals(data.getAll("c").get(0), "d"); assertEquals(data.getAll("c").get(1), "d"); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/LanguageParserTest.java0000644000175000017500000000160511417313373030764 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import junit.framework.TestCase; public class LanguageParserTest extends TestCase { public void testLanguages() throws Exception { LanguageParser parser = new LanguageParser(); parser.parse("en-gb,en;q=0.5"); assertEquals(parser.list().get(0).getLanguage(), "en"); assertEquals(parser.list().get(0).getCountry(), "GB"); assertEquals(parser.list().get(1).getLanguage(), "en"); assertEquals(parser.list().get(1).getCountry(), ""); parser.parse("en-gb,en;q=0.5,*;q=0.9"); assertEquals(parser.list().get(0).getLanguage(), "en"); assertEquals(parser.list().get(0).getCountry(), "GB"); assertEquals(parser.list().get(1).getLanguage(), "*"); assertEquals(parser.list().get(2).getLanguage(), "en"); assertEquals(parser.list().get(2).getCountry(), ""); } } simple-http-4.1.21/test/src/org/simpleframework/http/parse/PriorityQueueTest.java0000644000175000017500000000241211417313373030707 0ustar jamespagejamespagepackage org.simpleframework.http.parse; import java.util.PriorityQueue; import junit.framework.TestCase; public class PriorityQueueTest extends TestCase { private static class Entry implements Comparable { private final String text; private final int priority; private final int start; public Entry(String text, int priority, int start) { this.priority = priority; this.start = start; this.text = text; } public int compareTo(Entry entry) { int value = entry.priority - priority; if(value == 0) { return entry.start - start; } return value; } } public void testPriorityQueue() { PriorityQueue queue = new PriorityQueue(); int start = 10000; queue.offer(new Entry("a", 10, start--)); queue.offer(new Entry("b", 10, start--)); queue.offer(new Entry("c", 10, start--)); queue.offer(new Entry("d", 10, start--)); queue.offer(new Entry("e", 20, start--)); queue.offer(new Entry("f", 30, start--)); queue.offer(new Entry("g", 20, start--)); while(!queue.isEmpty()) { System.err.println(queue.remove().text); } } } simple-http-4.1.21/test/src/org/simpleframework/util/0000755000175000017500000000000011767603362023253 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/util/buffer/0000755000175000017500000000000011767603362024524 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/util/buffer/FileByteQueue.java0000644000175000017500000000541511417313373030074 0ustar jamespagejamespagepackage org.simpleframework.util.buffer; import java.io.IOException; import java.io.InputStream; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class FileByteQueue { private BlockingQueue blocks; private BlockAllocator allocator; private Block source; public FileByteQueue(Allocator allocator) throws IOException { this.blocks = new LinkedBlockingQueue(); this.allocator = new BlockAllocator(allocator); } public int read(byte[] array, int off, int size) throws Exception { int left = blocks.size(); int mark = size; for(int i = 0; source != null || i < left; i++) { if(source == null) { source = blocks.take(); } int remain = source.remaining(); int read = Math.min(remain, size); if(read > 0) { source.read(array, off, size); size -= read; off += read; } if(remain == 0) { source.close(); // clear up file handles source = null; } if(size <= 0) { return mark; } } return mark - size; } public void write(byte[] array, int off, int size) throws Exception { Block buffer = allocator.allocate(array, off, size); if(size > 0) { blocks.offer(buffer); } } private class BlockAllocator { private Allocator allocator; public BlockAllocator(Allocator allocator) { this.allocator = new BufferAllocator(allocator); } public Block allocate(byte[] array, int off, int size) throws IOException { Buffer buffer = allocator.allocate(); if(size > 0) { buffer.append(array, off, size); } return new Block(buffer, size); } } private class Block { private InputStream source; private int remaining; private int size; public Block(Buffer buffer, int size) throws IOException { this.source = buffer.getInputStream(); this.remaining = size; this.size = size; } public int read(byte[] array, int off, int size) throws IOException { int count = source.read(array, off, size); if(count > 0) { remaining -= size; } return count; } public void close() throws IOException { source.close(); } public int remaining() { return remaining; } public int size() { return size; } } } simple-http-4.1.21/test/src/org/simpleframework/util/buffer/FileByteQueueTest.java0000644000175000017500000000130611417313373030727 0ustar jamespagejamespagepackage org.simpleframework.util.buffer; import junit.framework.TestCase; public class FileByteQueueTest extends TestCase { public void testQueue() throws Exception { /* Allocator allocator = new FileAllocator(); FileByteQueue queue = new FileByteQueue(allocator); for(int i = 0; i < 26; i++) { queue.write(new byte[]{(byte)(i+'a')}, 0, 1); System.err.println("WRITE>>"+(char)(i+'a')); } for(int i = 0; i < 26; i++) { byte[] buffer = new byte[1]; assertEquals(queue.read(buffer, 0, 1), 1); System.err.println("READ>>"+((char)buffer[0])); assertEquals(buffer[0], (byte)(i+'a')); }*/ } } simple-http-4.1.21/test/src/org/simpleframework/util/buffer/BufferAllocatorTest.java0000644000175000017500000000475711417313373031306 0ustar jamespagejamespagepackage org.simpleframework.util.buffer; import junit.framework.TestCase; public class BufferAllocatorTest extends TestCase { public void testBuffer() throws Exception { Allocator allocator = new ArrayAllocator(1, 2); Buffer buffer = new BufferAllocator(allocator, 1, 2); buffer.append(new byte[]{'a'}).append(new byte[]{'b'}); assertEquals(buffer.encode(), "ab"); assertEquals(buffer.encode("ISO-8859-1"), "ab"); boolean overflow = false; try { buffer.append(new byte[]{'c'}); } catch(Exception e) { overflow = true; } assertTrue(overflow); buffer.clear(); assertEquals(buffer.encode(), ""); assertEquals(buffer.encode("UTF-8"), ""); allocator = new ArrayAllocator(1024, 2048); buffer = new BufferAllocator(allocator, 1024, 2048); buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); Buffer alphabet = buffer.allocate(); alphabet.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); Buffer digits = buffer.allocate(); digits.append("0123456789".getBytes()); assertEquals(alphabet.encode(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); assertEquals(digits.encode(), "0123456789"); assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); Buffer extra = digits.allocate(); extra.append("#@?".getBytes()); assertEquals(extra.encode(), "#@?"); assertEquals(digits.encode(), "0123456789#@?"); assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#@?"); } public void testCascadingBufferAllocator() throws Exception { Allocator allocator = new ArrayAllocator(1024, 2048); allocator = new BufferAllocator(allocator, 1024, 2048); allocator = new BufferAllocator(allocator, 1024, 2048); allocator = new BufferAllocator(allocator, 1024, 2048); allocator = new BufferAllocator(allocator, 1024, 2048); Buffer buffer = allocator.allocate(1024); buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyz"); buffer.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); } } simple-http-4.1.21/test/src/org/simpleframework/util/buffer/ArrayBufferTest.java0000644000175000017500000000314011417313373030425 0ustar jamespagejamespagepackage org.simpleframework.util.buffer; import junit.framework.TestCase; public class ArrayBufferTest extends TestCase { public void testBuffer() throws Exception { Buffer buffer = new ArrayBuffer(1, 2); buffer.append(new byte[]{'a'}).append(new byte[]{'b'}); assertEquals(buffer.encode(), "ab"); assertEquals(buffer.encode("ISO-8859-1"), "ab"); boolean overflow = false; try { buffer.append(new byte[]{'c'}); } catch(Exception e) { overflow = true; } assertTrue(overflow); buffer.clear(); assertEquals(buffer.encode(), ""); assertEquals(buffer.encode("UTF-8"), ""); buffer = new ArrayBuffer(1024, 2048); buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); Buffer alphabet = buffer.allocate(); alphabet.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); Buffer digits = buffer.allocate(); digits.append("0123456789".getBytes()); assertEquals(alphabet.encode(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); assertEquals(digits.encode(), "0123456789"); assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); Buffer extra = digits.allocate(); extra.append("#@?".getBytes()); assertEquals(extra.encode(), "#@?"); assertEquals(digits.encode(), "0123456789#@?"); assertEquals(buffer.encode(), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#@?"); } } simple-http-4.1.21/test/src/org/simpleframework/util/buffer/FileBufferTest.java0000644000175000017500000000251111417313373030227 0ustar jamespagejamespagepackage org.simpleframework.util.buffer; import java.io.File; import java.io.IOException; import java.io.InputStream; import junit.framework.TestCase; public class FileBufferTest extends TestCase { public void testFileBuffer() throws Exception { File tempFile = File.createTempFile(FileBufferTest.class.getSimpleName(), null); Buffer buffer = new FileBuffer(tempFile); buffer.append("abcdefghijklmnopqrstuvwxyz".getBytes()); Buffer alphabet = buffer.allocate(); alphabet.append("ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); Buffer digits = buffer.allocate(); digits.append("0123456789".getBytes()); expect(buffer, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".getBytes()); expect(alphabet, "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes()); expect(digits, "0123456789".getBytes()); } private void expect(Buffer buffer, byte[] expect) throws IOException { InputStream result = buffer.getInputStream(); for(int i =0; i < expect.length; i++) { byte octet = expect[i]; int value = result.read(); if(value < 0) { throw new IOException("Buffer exhausted too early"); } assertEquals(octet, (byte)value); } assertEquals(-1, result.read()); } } simple-http-4.1.21/test/src/org/simpleframework/util/KeyTest.java0000644000175000017500000001303611417313373025501 0ustar jamespagejamespagepackage org.simpleframework.util; import java.util.HashMap; import java.util.Map; import junit.framework.TestCase; /** * Test for fast case insensitive mapping for headers that have been taken * from the request HTTP header or added to the response HTTP header. * * @author Niall Gallagher */ public class KeyTest extends TestCase { public class Index implements Name { private final String value; public Index(String value) { this.value = value.toLowerCase(); } public int hashCode() { return value.hashCode(); } public boolean equals(Object key) { if(key instanceof Name) { return key.equals(value); } if(key instanceof String) { return key.equals(value); } return false; } } public interface Name { public int hashCode(); public boolean equals(Object value); } public class ArrayName implements Name { private String cache; private byte[] array; private int off; private int size; private int hash; public ArrayName(byte[] array) { this(array, 0, array.length); } public ArrayName(byte[] array, int off, int size) { this.array = array; this.size = size; this.off = off; } public boolean equals(Object value) { if(value instanceof String) { String text = value.toString(); return equals(text); } return false; } public boolean equals(String value) { int length = value.length(); if(length != size) { return false; } for(int i = 0; i < size; i++) { int left = value.charAt(i); int right = array[off + i]; if(right >= 'A' && right <= 'Z') { right = (right - 'A') + 'a'; } if(left != right) { return false; } } return true; } public int hashCode() { int code = hash; if(code == 0) { int pos = off; for(int i = 0; i < size; i++) { int next = array[pos++]; if(next >= 'A' && next <= 'Z') { next = (next - 'A') + 'a'; } code = 31*code + next; } hash = code; } return code; } } public class StringName implements Name { private final String value; private final String key; public StringName(String value) { this.key = value.toLowerCase(); this.value = value; } public int hashCode() { return key.hashCode(); } public boolean equals(Object value) { return value.equals(key); } } public class NameTable { private final Map map; public NameTable() { this.map = new HashMap(); } public void put(Name key, T value) { map.put(key, value); } public void put(String text, T value) { Name key = new StringName(text); map.put(key, value); } public T get(String key) { Index index = new Index(key); return map.get(index); } public T remove(String key) { Index index = new Index(key); return map.remove(index); } } public void testName() { Name contentLength = new ArrayName("Content-Length".getBytes()); Name contentType = new ArrayName("Content-Type".getBytes()); Name transferEncoding = new ArrayName("Transfer-Encoding".getBytes()); Name userAgent = new ArrayName("User-Agent".getBytes()); NameTable map = new NameTable(); assertEquals(contentLength.hashCode(), "Content-Length".toLowerCase().hashCode()); assertEquals(contentType.hashCode(), "Content-Type".toLowerCase().hashCode()); assertEquals(transferEncoding.hashCode(), "Transfer-Encoding".toLowerCase().hashCode()); assertEquals(userAgent.hashCode(), "User-Agent".toLowerCase().hashCode()); map.put(contentLength, "1024"); map.put(contentType, "text/html"); map.put(transferEncoding, "chunked"); map.put(userAgent, "Mozilla/4.0"); map.put("Date", "18/11/1977"); map.put("Accept", "text/plain, text/html, image/gif"); assertEquals(map.get("Content-Length"), "1024"); assertEquals(map.get("CONTENT-LENGTH"), "1024"); assertEquals(map.get("content-length"), "1024"); assertEquals(map.get("Content-length"), "1024"); assertEquals(map.get("Content-Type"), "text/html"); assertEquals(map.get("Transfer-Encoding"), "chunked"); assertEquals(map.get("USER-AGENT"), "Mozilla/4.0"); assertEquals(map.get("Accept"), "text/plain, text/html, image/gif"); assertEquals(map.get("ACCEPT"), "text/plain, text/html, image/gif"); assertEquals(map.get("accept"), "text/plain, text/html, image/gif"); assertEquals(map.get("DATE"), "18/11/1977"); assertEquals(map.get("Date"), "18/11/1977"); assertEquals(map.get("date"), "18/11/1977"); } } simple-http-4.1.21/test/src/org/simpleframework/util/thread/0000755000175000017500000000000011767603362024522 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/util/thread/TransientApplication.java0000644000175000017500000000254211417313373031513 0ustar jamespagejamespagepackage org.simpleframework.util.thread; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class TransientApplication { public static void main(String[] list) throws Exception { BlockingQueue queue = new LinkedBlockingQueue(); PoolExecutor pool = new PoolExecutor(TerminateTask.class, 10); for(int i = 0; i < 50; i++) { pool.execute(new LongTask(queue, String.valueOf(i))); } pool.execute(new TerminateTask(pool)); } private static class TerminateTask implements Runnable { private PoolExecutor pool; public TerminateTask(PoolExecutor pool) { this.pool = pool; } public void run() { pool.stop(); } } private static class LongTask implements Runnable { private BlockingQueue queue; private String name; public LongTask(BlockingQueue queue, String name) { this.queue = queue; this.name = name; } public void run() { try { Thread.sleep(1000); } catch(Exception e) { e.printStackTrace(); } System.err.println(name); queue.offer(name); } } } simple-http-4.1.21/test/src/org/simpleframework/util/thread/SchedulerTest.java0000644000175000017500000000336111417313373030136 0ustar jamespagejamespagepackage org.simpleframework.util.thread; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import org.simpleframework.util.thread.Scheduler; import junit.framework.TestCase; public class SchedulerTest extends TestCase { private static final int ITERATIONS = 10000; public void testScheduler() throws Exception { Scheduler queue = new Scheduler(10); LinkedBlockingQueue list = new LinkedBlockingQueue(); for(int i = 0; i < ITERATIONS; i++) { queue.execute(new Task(list, new Timer(i)), i, TimeUnit.MILLISECONDS); } for(Timer timer = list.take(); timer.getValue() < ITERATIONS - 10; timer = list.take()) { System.err.println("value=["+timer.getValue()+"] delay=["+timer.getDelay()+"] expect=["+timer.getExpectation()+"]"); } } public class Timer { private Integer value; private long time; public Timer(Integer value) { this.time = System.currentTimeMillis(); this.value = value; } public Integer getValue() { return value; } public long getDelay() { return System.currentTimeMillis() - time; } public int getExpectation() { return value.intValue(); } } public class Task implements Runnable { private LinkedBlockingQueue queue; private Timer timer; public Task(LinkedBlockingQueue queue, Timer timer) { this.queue = queue; this.timer = timer; } public void run() { queue.offer(timer); } } } simple-http-4.1.21/test/src/org/simpleframework/util/lease/0000755000175000017500000000000011767603362024344 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/util/lease/TimeTestCase.java0000644000175000017500000000147111417313373027534 0ustar jamespagejamespagepackage org.simpleframework.util.lease; import junit.framework.TestCase; public class TimeTestCase extends TestCase { public void testTime() { } public static void assertLessThan(long a, long b) { assertTrue(String.format("Value %s is not less than %s", a, b), a < b); } public static void assertLessThanOrEqual(long a, long b) { assertTrue(String.format("Value %s is not less than or equal to %s", a, b), a <= b); } public static void assertGreaterThan(long a, long b) { assertTrue(String.format("Value %s is not greater than %s", a, b), a > b); } public static void assertGreaterThanOrEqual(long a, long b) { assertTrue(String.format("Value %s is not greater than or equal to %s", a, b), a >= b); } } simple-http-4.1.21/test/src/org/simpleframework/util/lease/LeaseTest.java0000644000175000017500000000474611417313373027103 0ustar jamespagejamespagepackage org.simpleframework.util.lease; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; public class LeaseTest extends TimeTestCase { private static int ITERATIONS = 10000; static { String value = System.getProperty("iterations"); if (value != null) { ITERATIONS = Integer.parseInt(value); } } public void testLease() throws Exception { final BlockingQueue clean = new LinkedBlockingQueue(); Cleaner cleaner = new Cleaner() { public void clean(Integer key) { clean.offer(key); } }; Map table = new ConcurrentHashMap(); List list = new ArrayList(); Controller controller = new Maintainer(cleaner); for (int i = 0; i < ITERATIONS; i++) { long random = (long) (Math.random() * 1000) + 1000L; Contract contract = new Entry(i, random, TimeUnit.MILLISECONDS); Lease lease = new ContractLease(controller, contract); table.put(i, contract); list.add(lease); controller.issue(contract); } for (int i = 0; i < ITERATIONS; i++) { long random = (long) (Math.random() * 1000); try { list.get(i).renew(random, TimeUnit.MILLISECONDS); } catch (Exception e) { continue; // e.printStackTrace(); } } for (int i = 0; i < ITERATIONS; i++) { try { System.err.println("delay: " + list.get(i).getExpiry(TimeUnit.MILLISECONDS)); } catch (Exception e) { continue; // e.printStackTrace(); } } System.err.println("clean: " + clean.size()); for (int i = 0; i < ITERATIONS; i++) { Integer index = clean.take(); Contract contract = table.get(index); // assertLessThanOrEqual(-4000, // contract.getDelay(TimeUnit.MILLISECONDS)); System.err.println(String.format("index=[%s] delay=[%s]", index, contract.getDelay(TimeUnit.MILLISECONDS))); } } public static void main(String[] list) throws Exception { new LeaseTest().testLease(); } } simple-http-4.1.21/test/src/org/simpleframework/util/lease/ContractQueueTest.java0000644000175000017500000000400111417313373030614 0ustar jamespagejamespagepackage org.simpleframework.util.lease; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; public class ContractQueueTest extends TimeTestCase { public void testTimeUnits() throws Exception { ContractQueue queue = new ContractQueue(); List complete = new ArrayList(); for(long i = 0; i < 10000; i++) { long random = (long)(Math.random() * 1000); Contract contract = new Entry(random, random, TimeUnit.NANOSECONDS); queue.offer(contract); } for(int i = 0; i < 10000; i++) { Contract contract = queue.take(); assertGreaterThanOrEqual(contract.getDelay(TimeUnit.NANOSECONDS), contract.getDelay(TimeUnit.NANOSECONDS)); assertGreaterThanOrEqual(contract.getDelay(TimeUnit.MILLISECONDS), contract.getDelay(TimeUnit.MILLISECONDS)); assertGreaterThanOrEqual(contract.getDelay(TimeUnit.SECONDS), contract.getDelay(TimeUnit.SECONDS)); long nanoseconds = contract.getDelay(TimeUnit.NANOSECONDS); long milliseconds = contract.getDelay(TimeUnit.MILLISECONDS); complete.add(String.format("index=[%s] nano=[%s] milli=[%s]", i, nanoseconds, milliseconds)); } for(int i = 0; i < 10000; i++) { System.err.println("expiry=[" + complete.get(i)+ "]"); } } public void testAccuracy() throws Exception { ContractQueue queue = new ContractQueue(); for(long i = 0; i < 10000; i++) { long random = (long)(Math.random() * 1000); Contract contract = new Entry(random, random, TimeUnit.NANOSECONDS); queue.offer(contract); } for(int i = 0; i < 10000; i++) { Contract contract = queue.take(); assertLessThanOrEqual(-2000, contract.getDelay(TimeUnit.MILLISECONDS)); } } } simple-http-4.1.21/test/src/org/simpleframework/util/lease/LeaseManagerTest.java0000644000175000017500000002005411417313373030364 0ustar jamespagejamespagepackage org.simpleframework.util.lease; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; public class LeaseManagerTest extends TimeTestCase { private static int ITERATIONS = 1000; private static int MAXIMUM = 20000; static { String value = System.getProperty("iterations"); if (value != null) { ITERATIONS = Integer.parseInt(value); } } public void testClock() { List timeList = new ArrayList(); for(int i = 0; i < ITERATIONS; i++) { long time = System.nanoTime(); long milliseconds = TimeUnit.MILLISECONDS.convert(time, TimeUnit.MILLISECONDS); timeList.add(milliseconds); } for(int i = 1; i < ITERATIONS; i++) { assertLessThanOrEqual(timeList.get(i - 1), timeList.get(i)); } } public void testRandom() { for(int i = 0; i < ITERATIONS; i++) { long randomTime = getRandomTime(MAXIMUM); assertGreaterThanOrEqual(MAXIMUM, randomTime); assertGreaterThanOrEqual(randomTime, 0); } } public void testOrder() throws Exception { final BlockingQueue clean = new LinkedBlockingQueue(); final ConcurrentHashMap record = new ConcurrentHashMap(); Cleaner cleaner = new Cleaner() { long start = System.currentTimeMillis(); public void clean(Integer key) { record.put(key, start - System.currentTimeMillis()); clean.offer(key); } }; LeaseManager manager = new LeaseManager(cleaner); List> list = new ArrayList>(); long start = System.currentTimeMillis(); for(int i = 0; i < ITERATIONS; i++) { long randomTime = getRandomTime(MAXIMUM) + MAXIMUM + i * 50; System.err.printf("leasing [%s] for [%s] @ %s%n", i, randomTime, System.currentTimeMillis() - start); Lease lease = manager.lease(i, randomTime, TimeUnit.MILLISECONDS); list.add(lease); } start = System.currentTimeMillis(); for(int i = 0; i < ITERATIONS; i++) { try { System.err.printf("renewing [%s] for [%s] expires [%s] @ %s expired [%s] %n", i, i, list.get(i).getExpiry(TimeUnit.MILLISECONDS), System.currentTimeMillis() - start, record.get(i)); list.get(i).renew(i, TimeUnit.MILLISECONDS); }catch(Exception e) { System.err.printf("Lease %s in error: ", i); e.printStackTrace(System.err); } } int variation = 20; int cleaned = 0; for(int i = 0; i < ITERATIONS; i++) { int value = clean.take(); cleaned++; System.err.printf("index=[%s] clean=[%s] expiry[%s]=%s expiry[%s]=%s%n ", i, value, i, record.get(i), value, record.get(value)); assertLessThanOrEqual(i - variation, value); } assertEquals(cleaned, ITERATIONS); } public void testLease() throws Exception { final BlockingQueue clean = new LinkedBlockingQueue(); Cleaner cleaner = new Cleaner() { public void clean(Expectation key) { clean.offer(key); } }; final BlockingQueue> renewalQueue = new LinkedBlockingQueue>(); final BlockingQueue> expiryQueue = new LinkedBlockingQueue>(); final CountDownLatch ready = new CountDownLatch(21); final AtomicInteger renewCount = new AtomicInteger(ITERATIONS); for(int i = 0; i < 20; i++) { new Thread(new Runnable() { public void run() { while(renewCount.getAndDecrement() > 0) { long randomTime = getRandomTime(MAXIMUM); try { ready.countDown(); ready.await(); Lease lease = renewalQueue.take(); try { lease.renew(randomTime, TimeUnit.MILLISECONDS); lease.getKey().setExpectation(randomTime, TimeUnit.MILLISECONDS); assertGreaterThanOrEqual(randomTime, 0); assertGreaterThanOrEqual(randomTime, lease.getExpiry(TimeUnit.MILLISECONDS)); } catch(Exception e) { expiryQueue.offer(lease); } } catch(Exception e) { e.printStackTrace(); } } } }).start(); } final LeaseManager manager = new LeaseManager(cleaner); final CountDownLatch latch = new CountDownLatch(21); final AtomicInteger leaseCount = new AtomicInteger(ITERATIONS); for(int i = 0; i < 20; i++) { new Thread(new Runnable() { public void run() { while(leaseCount.getAndDecrement() > 0) { long randomTime = getRandomTime(MAXIMUM); Expectation expectation = new Expectation(randomTime, TimeUnit.MILLISECONDS); try { latch.countDown(); latch.await(); } catch(InterruptedException e) { e.printStackTrace(); } assertGreaterThanOrEqual(randomTime, 0); Lease lease = manager.lease(expectation, randomTime, TimeUnit.MILLISECONDS); renewalQueue.offer(lease); } } }).start(); } ready.countDown(); latch.countDown(); for (int i = 0; i < ITERATIONS; i++) { Expectation expectation = clean.poll(MAXIMUM, TimeUnit.MILLISECONDS); if(expectation != null) { long accuracy = System.nanoTime() - expectation.getExpectation(TimeUnit.NANOSECONDS); long milliseconds = TimeUnit.MILLISECONDS.convert(accuracy, TimeUnit.NANOSECONDS); System.err.printf("index=[%s] accuracy=[%s] queue=[%s]%n", i, milliseconds, clean.size()); } else { System.err.printf("index=[%s] queue=[%s]%n"); } } System.err.printf("waiting=[%s]%n", clean.size()); } public static class Expectation { private long time; public Expectation(long duration, TimeUnit unit) { setExpectation(duration, unit); } public void setExpectation(long duration, TimeUnit unit) { long nano = TimeUnit.NANOSECONDS.convert(duration, unit); long expect = nano + System.nanoTime(); this.time = expect; } public long getExpectation(TimeUnit unit) { return unit.convert(time, TimeUnit.NANOSECONDS); } } public static long getRandomTime(long maximum) { long random = new Random().nextLong() % maximum; if(random < 0) { random *= -1; } return random; } public static void main(String[] list) throws Exception { new LeaseManagerTest().testClock(); new LeaseManagerTest().testRandom(); new LeaseManagerTest().testOrder(); new LeaseManagerTest().testLease(); } } simple-http-4.1.21/test/src/org/simpleframework/util/lease/ContractTest.java0000644000175000017500000000345411417313373027622 0ustar jamespagejamespagepackage org.simpleframework.util.lease; import java.util.concurrent.TimeUnit; public class ContractTest extends TimeTestCase { public void testContract() throws Exception { Contract ten = new Entry(this, 10, TimeUnit.MILLISECONDS); Contract twenty = new Entry(this, 20, TimeUnit.MILLISECONDS); Contract thirty= new Entry(this, 30, TimeUnit.MILLISECONDS); assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.NANOSECONDS), ten.getDelay(TimeUnit.NANOSECONDS)); assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.NANOSECONDS), twenty.getDelay(TimeUnit.NANOSECONDS)); assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.MILLISECONDS), ten.getDelay(TimeUnit.MILLISECONDS)); assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.MILLISECONDS), twenty.getDelay(TimeUnit.MILLISECONDS)); ten.setDelay(0, TimeUnit.MILLISECONDS); twenty.setDelay(0, TimeUnit.MILLISECONDS); assertLessThanOrEqual(ten.getDelay(TimeUnit.MILLISECONDS), 0); assertLessThanOrEqual(twenty.getDelay(TimeUnit.MILLISECONDS), 0); ten.setDelay(10, TimeUnit.MILLISECONDS); twenty.setDelay(20, TimeUnit.MILLISECONDS); thirty.setDelay(30, TimeUnit.MILLISECONDS); assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.NANOSECONDS), ten.getDelay(TimeUnit.NANOSECONDS)); assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.NANOSECONDS), twenty.getDelay(TimeUnit.NANOSECONDS)); assertGreaterThanOrEqual(twenty.getDelay(TimeUnit.MILLISECONDS), ten.getDelay(TimeUnit.MILLISECONDS)); assertGreaterThanOrEqual(thirty.getDelay(TimeUnit.MILLISECONDS), twenty.getDelay(TimeUnit.MILLISECONDS)); ten.setDelay(0, TimeUnit.MILLISECONDS); twenty.setDelay(0, TimeUnit.MILLISECONDS); } } simple-http-4.1.21/test/src/org/simpleframework/transport/0000755000175000017500000000000011767603362024332 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/transport/TransportTest.java0000644000175000017500000003202011417313373030016 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import java.util.LinkedList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import junit.framework.TestCase; import org.simpleframework.transport.reactor.ExecutorReactor; import org.simpleframework.transport.reactor.Reactor; /** * Measure the performance of the transports to ensure that the perform * well and that they send the correct sequence of bytes and that the * blocks sent are in the correct order. This also performs a comparison * with direct socket output streams to ensure there is a reasonable * performance difference. * * @author Niall Gallagher */ public class TransportTest extends TestCase { private static final int REPEAT = 1000; public void testTransport() throws Exception { testTransport(REPEAT); } public void testTransport(int repeat) throws Exception { for(int i = 1; i < 7; i++) { // just do some random sizes testTransport(i, 100); } for(int i = 4092; i < 4102; i++) { testTransport(i, 100); } for(int i = 8190; i < 8200; i++) { testTransport(i, 100); } for(int i = 11282; i < 11284; i++) { testTransport(i, 1000); } for(int i = 204800; i < 204805; i++) { testTransport(i, 1000); } testTransport(16, repeat); testTransport(64, repeat); testTransport(256, repeat); testTransport(1024, repeat); testTransport(2048, repeat); testTransport(4096, repeat); testTransport(4098, repeat); testTransport(8192, repeat); testTransport(8197, repeat); } // Test blocking transport private void testTransport(int size, int repeat) throws Exception { // ThreadDumper dumper = new ThreadDumper(); SocketConsumer consumer = new SocketConsumer(size, repeat); SocketAddress address = new InetSocketAddress("localhost", consumer.getPort()); SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); // underlying socket must be non-blocking channel.connect(address); while(!channel.finishConnect()) { // wait to finish connection Thread.sleep(10); }; ExecutorService executor = Executors.newFixedThreadPool(20); Reactor reactor = new ExecutorReactor(executor); // Transport transport = new SocketTransport(channel, reactor, 2, 3);//XXX bug MockSocket pipeline = new MockSocket(channel); Transport transport = new SocketTransport(pipeline, reactor, 8192); OutputStream out = new TransportOutputStream(transport); // dumper.start(); testOutputStream(consumer, out, size, repeat); out.close(); executor.shutdown(); channel.close(); reactor.stop(); // dumper.kill(); Thread.sleep(100); } public void s_testSocket() throws Exception { s_testSocket(REPEAT); } public void s_testSocket(int repeat) throws Exception { testSocket(16, repeat); testSocket(64, repeat); testSocket(256, repeat); testSocket(1024, repeat); testSocket(2048, repeat); testSocket(4098, repeat); testSocket(8192, repeat); } // Test blocking socket private void testSocket(int size, int repeat) throws Exception { // ThreadDumper dumper = new ThreadDumper(); SocketConsumer consumer = new SocketConsumer(size, repeat); Socket socket = new Socket("localhost", consumer.getPort()); OutputStream out = socket.getOutputStream(); //dumper.start(); testOutputStream(consumer, out, size, repeat); out.close(); socket.close(); //dumper.kill(); Thread.sleep(100); } private class AlpahbetIterator { private byte[] alphabet = "abcdefghijklmnopqstuvwxyz".getBytes(); private int off; public byte next() { if(off == alphabet.length) { off = 0; } return alphabet[off++]; } public void reset() { off = 0; } } private void testOutputStream(SocketConsumer consumer, OutputStream out, int size, int repeat) throws Exception { byte[] block = new byte[size]; // write size AlpahbetIterator it = new AlpahbetIterator(); // write known data for(int i = 1; i < block.length; i++) { block[i] = it.next(); } AtomicLong count = new AtomicLong(); PerformanceMonitor monitor = new PerformanceMonitor(consumer, count, out.getClass().getSimpleName(), size); for(int i = 0; i < repeat; i++) { block[0] = (byte) i; // mark the first byte in the block to be sure we get blocks in sequence //System.err.println("["+i+"]"+new String(block,"ISO-8859-1")); out.write(block); // manipulation of the underlying buffer is taking place when the compact is invoked, this is causing major problems as the next packet will be out of sequence count.addAndGet(block.length); } Thread.sleep(2000); // wait for all bytes to flush through to consumer monitor.kill(); } private class PerformanceMonitor extends Thread { private AtomicLong count; private volatile boolean dead; private SocketConsumer consumer; private String name; private int size; public PerformanceMonitor(SocketConsumer consumer, AtomicLong count, String name, int size) { this.consumer = consumer; this.count = count; this.name = name; this.size = size; this.start(); } public void run() { int second = 0; while(!dead) { try { long octets = count.longValue(); System.out.printf("%s,%s,%s,%s,%s%n", name, size, second++, octets, consumer.getWindow()); Thread.sleep(1000); } catch(Exception e) { e.printStackTrace(); } } } public void kill() throws Exception { dead = true; } } private class SocketConsumer extends Thread { private ServerSocket server; private Window window; private long repeat; private long size; public SocketConsumer(int size, int repeat) throws Exception { this.window = new Window(20); this.server = getSocket(); this.repeat = repeat; this.size = size; this.start(); } public int getPort() { return server.getLocalPort(); } public String getWindow() { return window.toString(); } private ServerSocket getSocket() throws Exception { // Scan the ephemeral port range for(int i = 2000; i < 10000; i++) { // keep trying to grab the socket try { ServerSocket socket = new ServerSocket(i); System.out.println("port=["+socket.getLocalPort()+"]"); return socket; } catch(Exception e) { Thread.sleep(200); } } // Scan a second time for good measure, maybe something got freed up for(int i = 2000; i < 10000; i++) { // keep trying to grab the socket try { ServerSocket socket = new ServerSocket(i); System.out.println("port=["+socket.getLocalPort()+"]"); return socket; } catch(Exception e) { Thread.sleep(200); } } throw new IOException("Could not create a client socket"); } public void run() { long count = 0; int windowOctet = 0; int expectWindowOctet = 0; try { Socket socket = server.accept(); InputStream in = socket.getInputStream(); InputStream source = new BufferedInputStream(in); AlpahbetIterator it = new AlpahbetIterator(); scan: for(int i = 0; i < repeat; i++) { int octet = source.read(); // check first byte in the block to make sure its correct in sequence if(octet == -1) { break scan; } count++; // we have read another byte windowOctet = octet & 0x000000ff; expectWindowOctet = i & 0x000000ff; if((byte) octet != (byte) i) { throw new Exception("Wrong sequence of blocks sent, was " + (byte)octet + " should have been " + (byte)i + " count is "+count+" window is "+window+" compare "+explore(it, source, 5)); } window.recieved(octet); for(int j = 1, k = 0; j < size; j++, k++) { octet = source.read(); if(octet == -1) { break scan; } byte next = it.next(); if((byte) octet != next) { throw new Exception("Invalid data received expected "+((byte)octet)+"("+((char)octet)+ ") but was "+next+"("+((char)next)+") total count is "+count+" block count is "+k+" window is expected "+ expectWindowOctet+"("+((char)expectWindowOctet)+")("+((byte)expectWindowOctet)+") got "+windowOctet+"("+ ((char)windowOctet)+")("+((byte)windowOctet)+") "+window+" compare "+explore(it, source, 5)); } count++; } it.reset(); } } catch(Throwable e) { e.printStackTrace(); } if(count != size * repeat) { new Exception("Invalid number of bytes read, was " + count + " should have been " + (size * repeat)).printStackTrace(); } try { // server.close(); }catch(Exception e) { e.printStackTrace(); } } private String explore(AlpahbetIterator it, InputStream source, int count) throws IOException { StringBuffer buf = new StringBuffer(); buf.append("expected ("); for(int i = 0; i < count; i++) { buf.append( (char)it.next() ); } buf.append(") is ("); for(int i = 0; i < count; i++) { buf.append( (char)source.read() ); } buf.append(")"); return buf.toString(); } } private static class TransportOutputStream extends OutputStream { private Transport transport; public TransportOutputStream(Transport transport) { this.transport = transport; } public void write(int octet) throws IOException { byte[] data = new byte[] { (byte) octet }; write(data); } public void write(byte[] data, int off, int len) throws IOException { try { ByteBuffer buffer = ByteBuffer.wrap(data, off, len); ByteBuffer safe = buffer.asReadOnlyBuffer(); if(len > 0) { transport.write(safe); } } catch(Exception e) { e.printStackTrace(); throw new IOException("Write failed"); } } public void flush() throws IOException { try { transport.flush(); } catch(Exception e) { e.printStackTrace(); throw new IOException("Flush failed"); } } public void close() throws IOException { try { transport.close(); } catch(Exception e) { e.printStackTrace(); throw new IOException("Close failed"); } } } private static class Window { private final LinkedList window; private final int size; public Window(int size) { this.window = new LinkedList(); this.size = size; } public synchronized void recieved(int sequence) { window.addLast(String.valueOf(sequence)); if(window.size() > size) { window.removeFirst(); } } public synchronized String toString() { StringBuilder builder = new StringBuilder("["); String delim = ""; for(String b : window) { builder.append(delim).append(b); delim=", "; } builder.append("]"); return builder.toString(); } } } simple-http-4.1.21/test/src/org/simpleframework/transport/PacketTest.java0000644000175000017500000000342411417313373027237 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.io.IOException; import java.nio.ByteBuffer; import java.util.PriorityQueue; import junit.framework.TestCase; public class PacketTest extends TestCase { public void testPacket() throws IOException { byte[] content = "Hello World".getBytes(); ByteBuffer buffer = ByteBuffer.wrap(content); Packet packet = new Wrapper(buffer, 0); assertEquals(packet.length(), content.length); assertEquals(packet.encode(), "Hello World"); Packet extract = packet.extract(); assertEquals(packet.length(), 0); assertEquals(packet.encode(), ""); assertEquals(extract.length(), content.length); assertEquals(extract.encode(), "Hello World"); } public void testComparison() throws IOException { Packet less = new Wrapper(ByteBuffer.wrap("Least".getBytes()), 1); Packet greater = new Wrapper(ByteBuffer.wrap("Greater".getBytes()), 10); assertEquals(less.compareTo(less), 0); // ensure comparison contract adhered to assertEquals(less.compareTo(greater), -1); assertEquals(greater.compareTo(less), 1); Packet first = new Wrapper(ByteBuffer.wrap("First".getBytes()), 1); Packet second = new Wrapper(ByteBuffer.wrap("Second".getBytes()), 2); Packet third = new Wrapper(ByteBuffer.wrap("Third".getBytes()), 3); PriorityQueue queue = new PriorityQueue(); queue.offer(first); queue.offer(third); queue.offer(second); assertEquals(queue.poll().encode(), "First"); // first in sequence should be first out assertEquals(queue.poll().encode(), "Second"); assertEquals(queue.poll().encode(), "Third"); } } simple-http-4.1.21/test/src/org/simpleframework/transport/StreamTransport.java0000644000175000017500000000236511417313373030343 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SocketChannel; import java.nio.channels.WritableByteChannel; import java.util.Map; import javax.net.ssl.SSLEngine; public class StreamTransport implements Transport { private final WritableByteChannel write; private final ReadableByteChannel read; private final OutputStream out; public StreamTransport(InputStream in, OutputStream out) { this.write = Channels.newChannel(out); this.read = Channels.newChannel(in); this.out = out; } public void close() throws IOException { write.close(); read.close(); } public void flush() throws IOException { out.flush(); } public int read(ByteBuffer buffer) throws IOException { return read.read(buffer); } public void write(ByteBuffer buffer) throws IOException { write.write(buffer); } public Map getAttributes() { return null; } public SocketChannel getChannel() { return null; } public SSLEngine getEngine() { return null; } } simple-http-4.1.21/test/src/org/simpleframework/transport/connect/0000755000175000017500000000000011767603362025763 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/transport/connect/SecureSocketChannel.java0000644000175000017500000004100511417313373032506 0ustar jamespagejamespagepackage org.simpleframework.transport.connect; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.Channel; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLEngineResult.HandshakeStatus; import javax.net.ssl.SSLEngineResult.Status; /** * A secure socket channel implementation adding SSL engine based cryprography * to an adapted non-secure concrete SocketChannel * implementation. * *

* This implementation extends abstract SocketChannel and * forwards applicable calls to methods of the adapted concrete implementation. * It also implements AdaptableChannel as selectors typically * don't accept channel implementations from other vendors, so the selector * registration must be done with the adaptee channel. *

* *

* The additional SecureChannel methods help handshake and * shutdown even though they can also be handled by the read, write and close * methods. Note that the handshake method performs only one such channel read * or write operation during each call that is enabled by the ready set * parameter. Any other way of action seems to cause brosers to block * occasionally. *

* * @author Ilkka Priha */ public class SecureSocketChannel extends FilterSocketChannel{ /** * The unsecure socket channel. */ private SocketChannel channel; /** * The SSL engine to apply. */ private SSLEngine engine; /** * The active SSL session. */ private SSLSession sslSession; /** * The minimum cache size. */ private int minCacheSize; /** * The decrypted input cache. */ private ByteBuffer[] inputCache; /** * The minimum buffer size. */ private int minBufferSize; /** * The encrypted input buffer. */ private ByteBuffer[] inputBuffer; /** * The encrypted output buffer. */ private ByteBuffer[] outputBuffer; /** * An empty buffer for handshaking. */ private ByteBuffer emptyBuffer; /** * The engine handshake status. */ private HandshakeStatus handshake; /** * The initial handshake ops. */ private int initialized = -1; /** * The engine shutdown flag. */ private boolean shutdown; /** * Construct a new channel. * * @param channel the unsecure socket channel. * @param engine the SSL engine. */ public SecureSocketChannel(SocketChannel channel, SSLEngine engine) { super(channel); this.engine = engine; } public synchronized int read(ByteBuffer dst) throws IOException { if (channel.socket().isInputShutdown()) { throw new ClosedChannelException(); } else if (initialized != 0) { handshake(SelectionKey.OP_READ); return 0; } else if (shutdown) { shutdown(); return 0; } else if (engine.isInboundDone()) { return -1; } else if ((fill(inputBuffer[0]) < 0) && (inputBuffer[0].position() == 0)) { return -1; } SSLEngineResult result; Status status; do { if (!prepare(inputCache, minCacheSize)) { // Overflow! break; } inputBuffer[0].flip(); try { result = engine.unwrap(inputBuffer[0], inputCache[0]); } finally { inputBuffer[0].compact(); inputCache[0].flip(); } status = result.getStatus(); if ((status == Status.OK) || (status == Status.BUFFER_UNDERFLOW)) { if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { runTasks(); } } else { if (status == Status.CLOSED) { shutdown(); } throw new IOException("Read error '" + result.getStatus() + '\''); } } while ((inputBuffer[0].position() != 0) && (status != Status.BUFFER_UNDERFLOW)); int n = inputCache[0].remaining(); if (n > 0) { if (n > dst.remaining()) { n = dst.remaining(); } for (int i = 0; i < n; i++) { dst.put(inputCache[0].get()); } } return n; } public synchronized int write(ByteBuffer src) throws IOException { if (channel.socket().isOutputShutdown()) { throw new ClosedChannelException(); } else if (initialized != 0) { handshake(SelectionKey.OP_WRITE); return 0; } else if (shutdown) { shutdown(); return 0; } // Check how much to write. int t = src.remaining(); int n = 0; // Write as much as we can. SSLEngineResult result; Status status; do { if (!prepare(outputBuffer, minBufferSize)) { // Overflow! break; } inputBuffer[0].flip(); try { result = engine.wrap(src, outputBuffer[0]); } finally { outputBuffer[0].flip(); } n += result.bytesConsumed(); status = result.getStatus(); if (status == Status.OK) { if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { runTasks(); } } else { if (status == Status.CLOSED) { shutdown(); } throw new IOException("Write error '" + result.getStatus() + '\''); } } while (n < t); // Try to flush what we got. flush(); return n; } public Channel getAdapteeChannel() { return channel; } public boolean finished() { return initialized == 0; } public int encrypted() { return outputBuffer[0].remaining(); } public int decrypted() { return inputCache[0].remaining(); } public synchronized int handshake(int ops) throws IOException { if (initialized != 0) { if (handshake == null) { engine.beginHandshake(); handshake = engine.getHandshakeStatus(); } if (outputBuffer[0].hasRemaining()) { if ((ops & SelectionKey.OP_WRITE) != 0) { flush(outputBuffer[0]); if (outputBuffer[0].hasRemaining()) { initialized = SelectionKey.OP_WRITE; } else { initialized = SelectionKey.OP_READ; } ops = 0; } else { initialized = SelectionKey.OP_WRITE; } } else { initialized = SelectionKey.OP_READ; } while (initialized != 0) { if (handshake == HandshakeStatus.FINISHED) { initialized = 0; } else if (handshake == HandshakeStatus.NEED_TASK) { handshake = runTasks(); } else if (handshake == HandshakeStatus.NEED_UNWRAP) { ops = unwrap(ops); if (ops != 0) { initialized = ops; return initialized; } } else if (handshake == HandshakeStatus.NEED_WRAP) { ops = wrap(ops); if (ops != 0) { initialized = ops; return initialized; } } else { // NOT_HANDSHAKING throw new IllegalStateException( "Unexpected handshake status '" + handshake + '\''); } } } return initialized; } public synchronized boolean shutdown() throws IOException { shutdown = true; if (!engine.isOutboundDone()) { engine.closeOutbound(); } // Try to "fire-and-forget" the closed notification (RFC2616). SSLEngineResult result; if (prepare(outputBuffer, minBufferSize)) { result = engine.wrap(emptyBuffer, outputBuffer[0]); if (result.getStatus() != Status.CLOSED) { throw new SSLException("Unexpected shutdown status '" + result.getStatus() + '\''); } outputBuffer[0].flip(); } else { result = null; } flush(outputBuffer[0]); return !outputBuffer[0].hasRemaining() && (result != null) && (result.getHandshakeStatus() != HandshakeStatus.NEED_WRAP); } public synchronized void flush() throws IOException { flush(outputBuffer[0]); } public String toString() { return "SSLSocketChannel[" + socket().toString() + "]"; } /** * Gets the SSL session. * * @return the session. */ public SSLSession getSession() { return sslSession; } protected synchronized void implCloseSelectableChannel() throws IOException { try { shutdown(); } catch (Exception x) { } channel.close(); notifyAll(); } protected void implConfigureBlocking(boolean block) throws IOException { channel.configureBlocking(block); } /** * Handshake unwrap. * * @param ops the current ready operations set. * @return the interest set to continue or 0 if finished. * @throws IOException on I/O errors. */ private synchronized int unwrap(int ops) throws IOException { // Fill the buffer, if applicable. if ((ops & SelectionKey.OP_READ) != 0) { fill(inputBuffer[0]); } // Unwrap the buffer. SSLEngineResult result; Status status; do { // Prepare the input cache, although no app // data should be produced during handshake. prepare(inputCache, minCacheSize); inputBuffer[0].flip(); try { result = engine.unwrap(inputBuffer[0], inputCache[0]); } finally { inputBuffer[0].compact(); inputCache[0].flip(); } handshake = result.getHandshakeStatus(); status = result.getStatus(); if (status == Status.OK) { if (handshake == HandshakeStatus.NEED_TASK) { handshake = runTasks(); } } else if (status == Status.BUFFER_UNDERFLOW) { return SelectionKey.OP_READ; } else { // BUFFER_OVERFLOW/CLOSED throw new IOException("Handshake failed '" + status + '\''); } } while (handshake == HandshakeStatus.NEED_UNWRAP); return 0; } /** * Handshake wrap. * * @param ops the current ready operations set. * @return the interest set to continue or 0 if finished. * @throws IOException on I/O errors. */ private synchronized int wrap(int ops) throws IOException { // Prepare the buffer. if (prepare(outputBuffer, minBufferSize)) { // Wrap the buffer. SSLEngineResult result; Status status; try { result = engine.wrap(emptyBuffer, outputBuffer[0]); } finally { outputBuffer[0].flip(); } handshake = result.getHandshakeStatus(); status = result.getStatus(); if (status == Status.OK) { if (handshake == HandshakeStatus.NEED_TASK) { handshake = runTasks(); } } else { // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED throw new IOException("Handshake failed '" + status + '\''); } } // Flush the buffer, if applicable. if ((ops & SelectionKey.OP_WRITE) != 0) { flush(outputBuffer[0]); } return outputBuffer[0].hasRemaining() ? SelectionKey.OP_WRITE : 0; } /** * Fills the specified buffer. * * @param in the buffer. * @return the number of read bytes. * @throws IOException on I/O errors. */ private synchronized long fill(ByteBuffer in) throws IOException { try { long n = channel.read(in); if (n < 0) { // EOF reached. engine.closeInbound(); } return n; } catch (IOException x) { // Can't read more bytes... engine.closeInbound(); throw x; } } /** * Flushes the specified buffer. * * @param out the buffer. * @return the number of written bytes. * @throws IOException on I/O errors. */ private synchronized long flush(ByteBuffer out) throws IOException { try { // Flush only if bytes available. return out.hasRemaining() ? channel.write(out) : 0; } catch (IOException x) { // Can't write more bytes... engine.closeOutbound(); shutdown = true; throw x; } } /** * Runs delegated handshaking tasks. * * @return the handshake status. */ private SSLEngineResult.HandshakeStatus runTasks() { Runnable runnable; while ((runnable = engine.getDelegatedTask()) != null) { runnable.run(); } return engine.getHandshakeStatus(); } /** * Prepares the specified buffer for the remaining number of bytes. * * @param src the source buffer. * @param remaining the number of bytes. * @return true if prepared, false otherwise. */ private boolean prepare(ByteBuffer[] src, int remaining) { ByteBuffer bb = src[0]; if (bb.compact().remaining() < remaining) { int position = bb.position(); int capacity = position + remaining; if (capacity <= 2 * remaining) { bb = ByteBuffer.allocate(capacity); if (position > 0) { src[0].flip(); bb.put(src[0]); src[0] = bb; } } else { bb.flip(); bb = null; } } return bb != null; } } simple-http-4.1.21/test/src/org/simpleframework/transport/connect/FilterSocketChannel.java0000644000175000017500000000557211417313373032516 0ustar jamespagejamespagepackage org.simpleframework.transport.connect; import java.io.IOException; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; // Can this be selected with????? public class FilterSocketChannel extends SocketChannel { protected SocketChannel channel; public FilterSocketChannel(SocketChannel channel) { super(channel.provider()); this.channel = channel; } @Override public Socket socket() { return channel.socket(); } @Override public boolean isConnected() { return channel.isConnected(); } @Override public boolean isConnectionPending() { return channel.isConnectionPending(); } @Override public boolean connect(SocketAddress remote) throws IOException { return channel.connect(remote); } @Override public boolean finishConnect() throws IOException { return channel.finishConnect(); } @Override public int read(ByteBuffer dst) throws IOException { return channel.read(dst); } @Override public long read(ByteBuffer[] array, int offset, int length) throws IOException { long count = 0; for(int i = offset; i < length; i++) { if(array[i].hasRemaining()) { int done = read(array[i]); if (done > 0) { count += done; if(!array[i].hasRemaining()) { break; } } else { if(done < 0 && count == 0){ count = -1; } break; } } } return count; } @Override public int write(ByteBuffer src) throws IOException { return channel.write(src); } @Override public long write(ByteBuffer[] array, int offset, int length) throws IOException { long count = 0; for(int i = offset; i < length; i++) { if (array[i].hasRemaining()) { int done = write(array[i]); if (done > 0) { count += done; if(!array[i].hasRemaining()) { break; } } else { break; } } } return count; } @Override protected void implCloseSelectableChannel() throws IOException { channel.close(); } @Override protected void implConfigureBlocking(boolean block) throws IOException { channel.configureBlocking(block); } } simple-http-4.1.21/test/src/org/simpleframework/transport/connect/ConnectionTest.java0000644000175000017500000001773011417313373031565 0ustar jamespagejamespagepackage org.simpleframework.transport.connect; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URL; import java.util.List; import java.util.Vector; import java.util.concurrent.CountDownLatch; import junit.framework.TestCase; import org.simpleframework.http.Request; import org.simpleframework.http.Response; import org.simpleframework.http.core.Container; import org.simpleframework.http.core.ContainerProcessor; import org.simpleframework.http.core.ThreadDumper; import org.simpleframework.transport.Processor; import org.simpleframework.transport.ProcessorServer; import org.simpleframework.transport.Server; import org.simpleframework.transport.Socket; import org.simpleframework.util.buffer.Allocator; import org.simpleframework.util.buffer.FileAllocator; import org.simpleframework.util.thread.PoolExecutor; public class ConnectionTest extends TestCase { public void testSocketPing() throws Exception { // for(int i = 0; i < 10; i++) { // System.err.printf("Ping [%s]%n", i); // testPing(8080, "Hello World!", true, 2); // } } public void testURLPing() throws Exception { for(int i = 0; i < 20; i++) { System.err.printf("Ping [%s]%n", i); testPing(8080, "Hello World!", false, 10); } } public void testMixPing() throws Exception { //for(int i = 0; i < 50; i+=2) { // System.err.printf("Ping [%s]%n", i); // testPing(8080, "Hello World!", true, 2); // System.err.printf("Ping [%s]%n", i+1); // testPing(8080, "Hello World!", false, 10); //} } private void testPing(int port, String message, boolean socket, int count) throws Exception { PingServer server = new PingServer(8080, message); Pinger pinger = new Pinger(8080, socket, count); server.start(); List list = pinger.execute(); for(int i = 0; i < count; i++) { // at least 20 String result = list.get(i); assertNotNull(result); assertEquals(result, message); } server.stop(); pinger.validate(); pinger.stop(); // wait for it all to finish } private static class DebugServer implements Server { private Server server; public DebugServer(Server server) { this.server = server; } public void process(Socket socket) throws IOException { System.err.println("Connect..."); server.process(socket); } public void stop() throws IOException { System.err.println("Stop..."); server.stop(); } } private static class PingServer implements Container { private final Connection connection; private final SocketAddress address; private final String message; public PingServer(int port, String message) throws Exception { Allocator allocator = new FileAllocator(); Processor processor = new ContainerProcessor(this, allocator, 5); Server server = new ProcessorServer(processor); DebugServer debug = new DebugServer(server); this.connection = new SocketConnection(debug); this.address = new InetSocketAddress(port); this.message = message; } public void start() throws Exception { try { System.err.println("Starting..."); connection.connect(address); }finally { System.err.println("Started..."); } } public void stop() throws Exception { connection.close(); } public void handle(Request req, Response resp) { try { System.err.println(req); PrintStream out = resp.getPrintStream(1024); resp.set("Content-Type", "text/plain"); out.print(message); out.close(); }catch(Exception e) { e.printStackTrace(); } } } private static class Pinger implements Runnable { private final int count; private final int port; private final boolean socket; private final CountDownLatch latch; private final CountDownLatch stop; private final PoolExecutor executor; private final ThreadDumper dumper; private final List list; private final List sockets; public Pinger(int port, boolean socket, int count) throws Exception { this.executor = new PoolExecutor(Pinger.class, count); this.list = new Vector(count); this.sockets = new Vector(count); this.latch = new CountDownLatch(count); this.stop = new CountDownLatch(count + count); this.dumper = new ThreadDumper(); this.port = port; this.socket = socket; this.count = count; } public List execute() throws Exception { dumper.start(); for(int i = 0; i < count; i++) { executor.execute(this); } latch.await(); // Overrun with pings to ensure they close if(socket) { for(int i = 0; i < count; i++) { executor.execute(this); } } return list; } public void validate() throws Exception { if(socket) { for(java.net.Socket socket : sockets) { if(socket.getInputStream().read() != -1) { throw new IOException("Connection not closed"); } else { System.err.println("Socket is closed"); } } } } public void stop() throws Exception { executor.stop(); if(socket) { stop.await(); // wait for all excess pings to finish } dumper.kill(); } private String ping() throws Exception { if(socket) { return pingWithSocket(); } return pingWithURL(); } public void run() { try { String result = ping(); list.add(result); latch.countDown(); }catch(Throwable e){ System.err.println(e); } finally { stop.countDown(); // account for excess pings } } /** * This works as it opens a socket and sends the request. * This will split using the CRLF and CRLF ending. * * @return the response body * * @throws Exception if the socket can not connect */ private String pingWithSocket() throws Exception { java.net.Socket socket = new java.net.Socket("localhost", port); OutputStream out = socket.getOutputStream(); out.write( ("GET / HTTP/1.1\r\n" + "Host: localhost\r\n"+ "\r\n").getBytes()); out.flush(); InputStream in = socket.getInputStream(); byte[] block = new byte[1024]; int count = in.read(block); String result = new String(block, 0, count); String parts[] = result.split("\r\n\r\n"); if(!result.startsWith("HTTP")) { throw new IOException("Header is not valid"); } sockets.add(socket); return parts[1]; } /** * Use the standard URL tool to get the content. * * @return the response body * * @throws Exception if a connection can not be made. */ private String pingWithURL() throws Exception { URL target = new URL("http://localhost:"+ port+"/"); InputStream in = target.openStream(); byte[] block = new byte[1024]; int count = in.read(block); String result = new String(block, 0, count); return result; } } } simple-http-4.1.21/test/src/org/simpleframework/transport/PacketBuilderTest.java0000644000175000017500000000402011417313373030537 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.io.IOException; import java.nio.ByteBuffer; import junit.framework.TestCase; public class PacketBuilderTest extends TestCase { public void testBuilder() throws IOException { PacketBuilder builder = new PacketBuilder(3, 4096); byte[] chunk = new byte[1024]; for(int i = 0; i < chunk.length; i++) { chunk[i] = (byte) 255; } ByteBuffer buffer = ByteBuffer.wrap(chunk); assertNull(builder.build(buffer.duplicate())); assertNull(builder.build(buffer.duplicate())); assertNull(builder.build(buffer.duplicate())); Packet packet = builder.build(buffer.duplicate()); assertNotNull(packet); assertEquals(packet.sequence(), 0L); assertEquals(packet.length(), 4096); assertFalse(packet.isReference()); chunk = new byte[8192]; for(int i = 0; i < chunk.length; i++) { chunk[i] = (byte) 255; } buffer = ByteBuffer.wrap(chunk); packet = builder.build(buffer.duplicate()); assertNotNull(packet); assertEquals(packet.sequence(), 1L); assertEquals(packet.length(), 8192); assertTrue(packet.isReference()); } public void testPacketSize() throws IOException { PacketBuilder builder = new PacketBuilder(3, 4096); byte[] chunk = new byte[4096]; for(int i = 0; i < chunk.length; i++) { chunk[i] = (byte) 255; } ByteBuffer buffer = ByteBuffer.wrap(chunk); Packet packet = builder.build(buffer.duplicate()); assertNotNull(packet); assertEquals(packet.length(), 4096); assertFalse(packet.isReference()); chunk = new byte[8192]; for(int i = 0; i < chunk.length; i++) { chunk[i] = (byte) 255; } buffer = ByteBuffer.wrap(chunk); packet = builder.build(buffer.duplicate()); assertNotNull(packet); assertEquals(packet.length(), 8192); assertTrue(packet.isReference()); } } simple-http-4.1.21/test/src/org/simpleframework/transport/TransportCursorTest.java0000644000175000017500000000605411417313373031224 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import junit.framework.TestCase; public class TransportCursorTest extends TestCase { private static final String ALPHABET = "abcdefghijklmnopqrstuvwxyz"; private static final String SOURCE = ALPHABET + "\r\n"; public void testCursor() throws IOException { byte[] data = SOURCE.getBytes("ISO-8859-1"); InputStream source = new ByteArrayInputStream(data); Transport transport = new StreamTransport(source, System.out); Cursor cursor = new TransportCursor(transport); byte[] buffer = new byte[1024]; assertEquals(cursor.ready(), data.length); assertEquals(26, cursor.read(buffer, 0, 26)); assertEquals(26, cursor.reset(26)); assertEquals(new String(buffer, 0, 26), ALPHABET); assertEquals(cursor.ready(), data.length); assertEquals(26, cursor.read(buffer, 0, 26)); assertEquals(26, cursor.reset(26)); assertEquals(new String(buffer, 0, 26), ALPHABET); assertEquals(cursor.ready(), data.length); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(4, cursor.reset(26)); assertEquals(new String(buffer, 0, 4), "abcd"); assertEquals(cursor.ready(), data.length); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(4, cursor.reset(26)); assertEquals(new String(buffer, 0, 4), "abcd"); assertEquals(cursor.ready(), data.length); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(new String(buffer, 0, 4), "abcd"); assertEquals(cursor.ready(), data.length - 4); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(new String(buffer, 0, 4), "efgh"); assertEquals(cursor.ready(), data.length - 8); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(new String(buffer, 0, 4), "ijkl"); assertEquals(cursor.ready(), data.length - 12); assertEquals(12, cursor.reset(12)); assertEquals(10, cursor.read(buffer, 0, 10)); assertEquals(new String(buffer, 0, 10), "abcdefghij"); cursor.push("1234".getBytes("ISO-8859-1")); cursor.push("5678".getBytes("ISO-8859-1")); cursor.push("90".getBytes("ISO-8859-1")); assertEquals(cursor.ready(), 10); assertEquals(2, cursor.read(buffer, 0, 2)); assertEquals(new String(buffer, 0, 2), "90"); assertEquals(cursor.ready(), 8); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(new String(buffer, 0, 4), "5678"); assertEquals(cursor.ready(), 4); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(new String(buffer, 0, 4), "1234"); assertEquals(4, cursor.reset(4)); assertEquals(cursor.ready(), 4); assertEquals(4, cursor.read(buffer, 0, 4)); assertEquals(new String(buffer, 0, 4), "1234"); assertEquals(8, cursor.read(buffer, 0, 8)); assertEquals(new String(buffer, 0, 8), "klmnopqr"); } } simple-http-4.1.21/test/src/org/simpleframework/transport/Console.java0000644000175000017500000000350311417313373026570 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.io.IOException; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SocketChannel; import java.nio.channels.WritableByteChannel; class Console extends SocketChannel { private final WritableByteChannel out; private final ReadableByteChannel in; public Console() { super(null); this.out = Channels.newChannel(System.out); this.in = Channels.newChannel(System.in); } public void flush() throws IOException { System.out.flush(); } @Override public int read(ByteBuffer buffer) throws IOException { return in.read(buffer); } @Override public int write(ByteBuffer buffer) throws IOException { return out.write(buffer); } @Override public boolean connect(SocketAddress remote) throws IOException { return false; } @Override public boolean finishConnect() throws IOException { return false; } @Override public boolean isConnected() { return false; } @Override public boolean isConnectionPending() { return false; } @Override public long read(ByteBuffer[] dsts, int offset, int length) throws IOException { return 0; } @Override public Socket socket() { return null; } @Override public long write(ByteBuffer[] srcs, int offset, int length) throws IOException { return 0; } @Override protected void implCloseSelectableChannel() throws IOException { in.close(); out.close(); } @Override protected void implConfigureBlocking(boolean block) throws IOException { } } simple-http-4.1.21/test/src/org/simpleframework/transport/NetworkSimulator.java0000644000175000017500000000541211417313373030520 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NetworkSimulator extends SocketChannel { private SocketChannel channel; private Socket socket; public NetworkSimulator(final int port, final int buffer) throws Exception{ super(null); final ServerSocket server = new ServerSocket(port); new Thread(new Runnable() { public void run() { try { socket = server.accept(); socket.setReceiveBufferSize(buffer); }catch(Exception e) { e.printStackTrace(); } } }).start(); SocketAddress address = new InetSocketAddress("localhost", port); channel = SocketChannel.open(); channel.configureBlocking(false); // underlying socket must be non-blocking channel.connect(address); while(!channel.finishConnect()) { // wait to finish connection Thread.sleep(10); }; channel.socket().setSendBufferSize(buffer); } public int available() throws IOException { return socket.getInputStream().available(); } public void drainTo(OutputStream out, int count) throws IOException { InputStream in = socket.getInputStream(); for(int i = 0; i < count; i++) { int octet = in.read(); if(octet == -1) { throw new IOException("Socket closed"); } out.write(octet); } } @Override public boolean connect(SocketAddress arg0) throws IOException { return false; } @Override public boolean finishConnect() throws IOException { return false; } @Override public boolean isConnected() { return false; } @Override public boolean isConnectionPending() { return false; } @Override public int read(ByteBuffer arg0) throws IOException { return channel.read(arg0); } @Override public long read(ByteBuffer[] arg0, int arg1, int arg2) throws IOException { return channel.read(arg0, arg1, arg2); } @Override public Socket socket() { return channel.socket(); } @Override public int write(ByteBuffer arg0) throws IOException { return channel.write(arg0); } @Override public long write(ByteBuffer[] arg0, int arg1, int arg2) throws IOException { return channel.write(arg0, arg1, arg2); } @Override protected void implCloseSelectableChannel() throws IOException { return; } @Override protected void implConfigureBlocking(boolean arg0) throws IOException { return; } } simple-http-4.1.21/test/src/org/simpleframework/transport/SegmentBuilderTest.java0000644000175000017500000001212311417313373030735 0ustar jamespagejamespagepackage org.simpleframework.transport; import java.nio.ByteBuffer; import junit.framework.TestCase; public class SegmentBuilderTest extends TestCase { public void testOrder() throws Exception { Packet first = new Wrapper(ByteBuffer.wrap("First".getBytes()), 1); Packet second = new Wrapper(ByteBuffer.wrap("Second".getBytes()), 2); Packet third = new Wrapper(ByteBuffer.wrap("Third".getBytes()), 3); SegmentBuilder builder = new SegmentBuilder(); assertEquals(builder.build(first).encode(), "First"); assertEquals(builder.build(second).encode(), "First"); assertEquals(builder.build(third).encode(), "First"); Segment firstSegment = builder.build(); assertEquals(firstSegment.length(), first.length()); assertEquals(firstSegment.encode(), "First"); assertEquals(firstSegment.encode(), builder.build().encode()); firstSegment.close(); // removew it from the builder Segment secondSegment = builder.build(); assertEquals(secondSegment.length(), second.length()); assertEquals(secondSegment.encode(), "Second"); assertEquals(secondSegment.encode(), builder.build().encode()); Packet newFirst = new Wrapper(ByteBuffer.wrap("First".getBytes()), 1); assertEquals(newFirst.sequence(), 1); assertEquals(newFirst.length(), "First".length()); assertEquals(builder.build(newFirst).encode(), "First"); assertEquals(builder.build().encode(), "First"); Segment newFirstSegment = builder.build(); assertEquals(newFirstSegment.sequence(), 1); assertEquals(newFirstSegment.encode(), "First"); newFirstSegment.close(); secondSegment.close(); assertEquals(builder.build().sequence(), 3); assertEquals(builder.build().encode(), "Third"); assertEquals(builder.length(), builder.build().length()); // because only one packet left } public void testSegmentClose() throws Exception { Packet first = new Wrapper(ByteBuffer.wrap("First".getBytes()), 1); Packet second = new Wrapper(ByteBuffer.wrap("Second".getBytes()), 2); SegmentBuilder builder = new SegmentBuilder(); builder.build(first); builder.build(second); builder.build(first); builder.build(second); builder.build(first); assertEquals(builder.build().sequence(), 1); assertEquals(builder.build().encode(), "First"); builder.build().close(); assertEquals(builder.build().sequence(), 2); // don't extract closed packet assertEquals(builder.build().encode(), "Second"); builder.build().close(); assertEquals(builder.build(), null); // all segments drained assertEquals(builder.length(), 0); } public void testCompact() throws Exception { Packet first = new Wrapper(ByteBuffer.wrap("First".getBytes()), 1); Packet second = new Wrapper(ByteBuffer.wrap("Second".getBytes()), 2); SegmentBuilder builder = new SegmentBuilder(); builder.build(first); builder.build(second); assertTrue(first.isReference()); assertTrue(second.isReference()); assertEquals(first.length(), "First".length()); assertEquals(second.length(), "Second".length()); assertEquals(builder.length(), "First".length() + "Second".length()); assertEquals(builder.build().encode(), "First"); builder.compact(); assertTrue(first.isReference()); assertTrue(second.isReference()); assertEquals(first.length(), 0); assertEquals(second.length(), 0); assertEquals(builder.length(), "First".length() + "Second".length()); assertEquals(builder.build().encode(), "First"); assertFalse(builder.build().isReference()); first = new Wrapper(ByteBuffer.wrap("First".getBytes()), 1); second = new Wrapper(ByteBuffer.wrap("Second".getBytes()), 2); builder = new SegmentBuilder(0); builder.build(first); builder.build(second); assertTrue(first.isReference()); assertTrue(second.isReference()); assertEquals(first.length(), "First".length()); assertEquals(second.length(), "Second".length()); assertEquals(builder.length(), "First".length() + "Second".length()); assertEquals(builder.build().encode(), "First"); builder.compact(); assertTrue(first.isReference()); assertTrue(second.isReference()); assertEquals(first.length(), "First".length()); assertEquals(second.length(), "Second".length()); assertEquals(builder.length(), "First".length() + "Second".length()); assertEquals(builder.build().encode(), "First"); } public void testNulls() throws Exception { SegmentBuilder builder = new SegmentBuilder(); Packet packet = new Wrapper(ByteBuffer.allocate(0), 1); Segment segment = builder.build(packet); assertNull(segment); } } simple-http-4.1.21/test/src/org/simpleframework/transport/MockSocket.java0000644000175000017500000000141211417313373027225 0ustar jamespagejamespage package org.simpleframework.transport; import java.nio.channels.SocketChannel; import java.util.HashMap; import java.util.Map; import javax.net.ssl.SSLEngine; import org.simpleframework.transport.Socket; public class MockSocket implements Socket { private SocketChannel socket; private SSLEngine engine; private Map map; public MockSocket(SocketChannel socket) { this(socket, null); } public MockSocket(SocketChannel socket, SSLEngine engine) { this.map = new HashMap(); this.engine = engine; this.socket = socket; } public SSLEngine getEngine() { return engine; } public SocketChannel getChannel() { return socket; } public Map getAttributes() { return map; } } simple-http-4.1.21/test/src/org/simpleframework/transport/reactor/0000755000175000017500000000000011767603362025771 5ustar jamespagejamespagesimple-http-4.1.21/test/src/org/simpleframework/transport/reactor/DistributorTest.java0000644000175000017500000002173311417313373032004 0ustar jamespagejamespagepackage org.simpleframework.transport.reactor; import static java.util.concurrent.TimeUnit.MILLISECONDS; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Iterator; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import junit.framework.TestCase; public class DistributorTest extends TestCase { private static final String PAYLOAD = "POST /index.html HTTP/1.0\r\n"+ "Content-Type: multipart/form-data; boundary=AaB03x\r\n"+ "Accept: image/gif;q=1.0,\r\n image/jpeg;q=0.8,\r\n"+ " \t\t image/png;\t\r\n\t"+ " q=1.0,*;q=0.1\r\n"+ "Accept-Language: fr;q=0.1, en-us;q=0.4, en-gb; q=0.8, en;q=0.7\r\n"+ "Host: some.host.com \r\n"+ "Cookie: $Version=1; UID=1234-5678; $Path=/; $Domain=.host.com\r\n"+ "Cookie: $Version=1; NAME=\"Niall Gallagher\"; $path=\"/\"\r\n"+ "\r\n" + "--AaB03x\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file1.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file1.txt\r\n"+ "--AaB03x\r\n"+ "Content-Type: multipart/mixed; boundary=BbC04y\r\n\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file2.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file3.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file3.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y\r\n"+ "Content-Disposition: form-data; name='pics'; filename='file4.txt'\r\n"+ "Content-Type: text/plain\r\n\r\n"+ "example contents of file4.txt ...\r\n"+ "--BbC04y--\r\n"+ "--AaB03x--\r\n"; public class Client extends Thread { private CountDownLatch latch; private String message; private int requests; private int port; public Client(CountDownLatch latch, String message, int port, int requests) throws Exception { this.message = message; this.requests = requests; this.port = port; this.latch = latch; this.start(); } public void run() { try { latch.await(); Socket socket = new Socket("localhost", port); OutputStream out = socket.getOutputStream(); byte[] payload = message.getBytes(); for(int i = 0; i < requests; i++){ out.write(payload); } out.close(); } catch(Exception e) { e.printStackTrace(); } } } public class Worker implements Operation { private BlockingQueue done; private Reactor reactor; private SocketChannel channel; private ByteBuffer buffer; private String payload; private int accumulate; private long finish; private long start; private int id; public Worker(BlockingQueue done, Reactor reactor, SocketChannel channel, String payload, int id) throws Exception { this.buffer = ByteBuffer.allocate(8192); this.start = System.currentTimeMillis(); this.finish = start + 60000; this.payload = payload; this.channel = channel; this.reactor = reactor; this.done = done; this.id = id; } public long getExpiry(TimeUnit unit) { return unit.convert(finish - System.currentTimeMillis(), MILLISECONDS); } public int getAccumulate() { return accumulate; } // XXX should this be executed in a thread!!!!???? yes... public void cancel() { System.err.println("############################# Worker has been canceled"); } public void run() { try { // N.B Fundamental to performance buffer.clear(); if(channel.isOpen()) { int count = channel.read(buffer); accumulate += count; System.err.println("Worker-"+id+" read ["+count +"] of payload sized ["+payload.length()+"] took ["+(System.currentTimeMillis() -start)+"]"); if(count != -1) { reactor.process(this, SelectionKey.OP_READ); } else { channel.close(); done.offer(this); System.err.println("Worker-"+id+" Channel is closed after time ["+(System.currentTimeMillis() - start)+"] and read ["+accumulate+"]"); } } else { System.err.println("Worker-"+id+" Channel is closed after time ["+(System.currentTimeMillis() - start)+"] and read ["+accumulate+"]"); done.offer(this); } }catch(Exception e) { e.printStackTrace(); } } public SocketChannel getChannel() { return channel; } } public class Server extends Thread { private BlockingQueue ready; private CountDownLatch latch; private ServerSocketChannel server; private Selector selector; private int port; public Server(CountDownLatch latch, BlockingQueue ready, int port) throws Exception { this.server = ServerSocketChannel.open(); this.selector = Selector.open(); this.latch = latch; this.port = port; this.ready = ready; this.start(); } private void configure() throws Exception { server.socket().bind(new InetSocketAddress(port)); server.configureBlocking(false); } public void run() { try { configure(); execute(); } catch(Exception e) { e.printStackTrace(); } } private void execute() throws Exception { SelectionKey serverKey = server.register(selector, SelectionKey.OP_ACCEPT); latch.countDown(); while(true){ selector.select(); Set keys = selector.selectedKeys(); for(Iterator i = keys.iterator(); i.hasNext();){ SelectionKey key = (SelectionKey) i.next(); i.remove(); if(key != serverKey) { return; } if(key.isAcceptable()) { SocketChannel channel = server.accept(); channel.configureBlocking(false); ready.offer(channel); } } } } } public static void main(String[] list) throws Exception { new DistributorTest().testReactor(); } public void testReactor() throws Exception { testReactor(PAYLOAD, 200, 100, 10, 8123); } private void testReactor(String payload, int clients, int requests, int threads, int port) throws Exception { BlockingQueue done = new LinkedBlockingQueue(); BlockingQueue ready = new LinkedBlockingQueue(); CountDownLatch latch = new CountDownLatch(1); Server server = new Server(latch, ready, port); Executor executor = Executors.newFixedThreadPool(10); Reactor reactor = new ExecutorReactor(executor, 1); long start = System.currentTimeMillis(); for(int i = 0; i < clients; i++) { new Client(latch, payload, port, requests); } for(int i = 0; i < clients; i++) { SocketChannel channel = ready.take(); Worker worker = new Worker(done, reactor, channel, payload, i); reactor.process(worker); } int total = 0; for(int i = 0; i < clients; i++) { Worker worker = done.take(); int accumulate = worker.getAccumulate(); total += accumulate; System.err.println("Accumulated ["+accumulate+"] of ["+(requests*payload.length())+"] closed ["+worker.getChannel().socket().isClosed()+"]"); } System.err.println("Accumulated ["+total+"] of ["+(clients*requests*payload.length())+"]"); System.err.println("Total time to process ["+(clients*requests)+"] payloads from ["+clients+"] clients took ["+(System.currentTimeMillis() - start)+"]"); } } simple-http-4.1.21/test/lib/0000755000175000017500000000000011767603362016257 5ustar jamespagejamespagesimple-http-4.1.21/test/build.xml0000644000175000017500000000472111417313373017326 0ustar jamespagejamespage