HttpServletRequest Class

package javaxt.http.servlet;
import java.util.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.HttpInput;

//******************************************************************************
//**  HttpServletRequest
//******************************************************************************
/**
 *   Used to read raw bytes sent from the client to the server. Assumes the
 *   data is a valid HTTP/1.1 request. Supports both http and https (ssl/tls).
 *   This class implements the javax.servlet.http.HttpServletRequest interface
 *   defined in Version 2.5 of the Java Servlet API.
 *
 ******************************************************************************/

public class HttpServletRequest {

    private HttpServlet servlet;
    private javax.servlet.http.HttpServletRequest request;
    private HashMap<String, List<String>> parameters;
    private ServletContext servletContext;
    private String servletPath;

    private java.net.URL url;
    private Boolean isKeepAlive;
    private Boolean isWebSocket;

    private Authenticator authenticator;
    private boolean authenticate = true;
    private ServletException authenticationException = null;
    private java.security.Principal principal;
    private boolean getUserPrincipal = true;
    private boolean getCredentials = true;
    private String[] credentials = null;


    public HttpServletRequest(javax.servlet.http.HttpServletRequest request, HttpServlet servlet){
        this.request = request;
        this.servlet = servlet;
        this.servletPath = servlet.getServletPath();
        this.servletContext = servlet.getServletContext();

        try{
            StringBuffer str = request.getRequestURL();
            String query = request.getQueryString();
            if (query!=null){
                str.append("?");
                str.append(query);
            }
            this.url = new java.net.URL(str.toString());
        }
        catch(Exception e){
            e.printStackTrace();
            //Should never happen...
        }


      //Instantiate the authenticator
        try{
            authenticator = servlet.getAuthenticator(this);
        }
        catch(Exception e){
            //TODO: Figure out how to propogate this error to the caller!
            e.printStackTrace();
        }


      //Parse query string
        parameters = parseQueryString(url.getQuery());
    }



  //**************************************************************************
  //** getRemoteAddr
  //**************************************************************************
  /** Returns the IP address of the client that sent the request.
   */
    public String getRemoteAddr(){
        return request.getRemoteAddr();
    }


  //**************************************************************************
  //** getRemoteHost
  //**************************************************************************
  /** Returns the hostname of the client that sent the request.
   */
    public String getRemoteHost(){
        return request.getRemoteHost();
    }


  //**************************************************************************
  //** getRemotePort
  //**************************************************************************
  /** Returns the port of the client that sent the request.
   */
    public int getRemotePort(){
        return request.getRemotePort();
    }


  //**************************************************************************
  //** getHttpVersion
  //**************************************************************************
  /** Returns the HTTP version number passed in as part of the request (e.g.
   *  "1.0", "1.1", etc).
   */
    public String getHttpVersion(){
        String protocol = request.getProtocol();
        if (protocol!=null){
            int idx = protocol.indexOf("/");
            if (idx>0) return protocol.substring(idx+1);
        }
        return null;
    }


  //**************************************************************************
  //** getHeader
  //**************************************************************************
  /** Returns the value of the specified request header as a String. If the
   *  request did not include a header of the specified name, this method
   *  returns null. If there are multiple headers with the same name, this
   *  method returns the first head in the request. The header name is case
   *  insensitive.
   *
   *  @param name A String specifying the header name (e.g. "Content-Encoding")
   *  The header name is case insensitive.
   */
    public String getHeader(String name){
        return request.getHeader(name);
    }


  //**************************************************************************
  //** setHeader
  //**************************************************************************
    public void setHeader(String name, String value){

        Request req = Request.getBaseRequest(request);
        req.setHeader(name, value);


      //Reset authentication parameters
        authenticate = true;
        authenticationException = null;
        principal = null;
        getUserPrincipal = true;
        getCredentials = true;
        credentials = null;
        try{
            authenticator = servlet.getAuthenticator(this);
        }
        catch(Exception e){
            //TODO: Figure out how to propogate this error to the caller!
            e.printStackTrace();
        }
    }


  //**************************************************************************
  //** getHeaders
  //**************************************************************************
  /** Returns all the values of the specified request header as an Enumeration.
   *  If the request did not include any headers of the specified name, this
   *  method returns an empty Enumeration.
   *
   *  @param name A String specifying the header name (e.g. "Accept-Language").
   *  The header name is case insensitive.
   */
    public Enumeration<String> getHeaders(String name){
        return request.getHeaders(name);
    }


  //**************************************************************************
  //** getHeaderNames
  //**************************************************************************
  /** Returns an enumeration of all the header names this request contains. If
   *  the request has no headers, this method returns an empty enumeration.
   */
    public Enumeration<String> getHeaderNames(){
        return request.getHeaderNames();
    }


  //**************************************************************************
  //** getIntHeader
  //**************************************************************************
  /** Returns the value of the specified request header as an int. If the
   *  request does not have a header of the specified name, this method
   *  returns -1. If the header cannot be converted to an integer, this method
   *  throws a NumberFormatException.
   *
   *  @param name A String specifying the header name (e.g. "Content-Length").
   *  The header name is case insensitive.
   */
    public int getIntHeader(String name) throws NumberFormatException {
        return request.getIntHeader(name);
    }


  //**************************************************************************
  //** getDateHeader
  //**************************************************************************
  /** Returns the value of the specified request header as a long representing
   *  the number of milliseconds since January 1, 1970 GMT. If the request did
   *  not have a header of the specified name, this method returns -1. If the
   *  header can't be converted to a date, the method throws an
   *  IllegalArgumentException.
   *
   *  @param name A String specifying the header name (e.g. "If-Modified-Since").
   *  The header name is case insensitive.
   */
    public long getDateHeader(String name) throws IllegalArgumentException{
        return request.getDateHeader(name);
    }


  //**************************************************************************
  //** getCharacterEncoding
  //**************************************************************************
  /** Returns the name of the character encoding used in the body of this
   *  request as specified in the "Content-Type" in the request header (e.g.
   *  "UTF-8"). Returns a null if the request does not specify a character
   *  encoding.
   */
    public String getCharacterEncoding(){
        return request.getCharacterEncoding();
    }


  //**************************************************************************
  //** setCharacterEncoding
  //**************************************************************************
  /** Overrides the name of the character encoding used in the body of this
   *  request. This method must be called prior to reading request parameters
   *  or reading input using getReader().
   */
    public void setCharacterEncoding(String env) throws java.io.UnsupportedEncodingException{
        request.setCharacterEncoding(env);
    }


  //**************************************************************************
  //** getContentType
  //**************************************************************************
  /** Returns the "Content-Type" defined in the request header. Returns null
   *  if the "Content-Type" is not defined.
   */
    public String getContentType(){
        return request.getContentType();
    }


  //**************************************************************************
  //** getLocale
  //**************************************************************************
  /** Returns the preferred Locale that the client will accept content in,
   *  based on the "Accept-Language" header. If the client request doesn't
   *  provide an Accept-Language header, this method returns the default
   *  locale for the server.
   */
    public Locale getLocale(){
        return request.getLocale();
    }


  //**************************************************************************
  //** getLocales
  //**************************************************************************
  /** Returns an Enumeration of Locale objects indicating the locales that are
   *  acceptable to the client based on the Accept-Language header. The list
   *  of Locales is ordered, starting with the preferred locale. If the client
   *  request doesn't provide an Accept-Language header, this method returns
   *  an Enumeration containing one Locale, the default locale for the server.
   */
    public Enumeration<Locale> getLocales(){
        return request.getLocales();
    }




  //**************************************************************************
  //** getPath
  //**************************************************************************
  /** Returns the requested path and querystring. This usually corresponds to
   *  the first line of the request header. Example:
   *  <pre>GET /index.html?abc=123 HTTP/1.1</pre>
   *  If the server is acting as a proxy, the first line may include a full
   *  url. In this case, use the getURL() method to retrieve the original path.
   */
    public String getPath(){
        return url.getPath() + (url.getQuery()==null ? "" : "?"+url.getQuery());
    }


  //**************************************************************************
  //** getMethod
  //**************************************************************************
  /** Returns the method specified in the first line of the request (e.g. GET,
   *  POST, PUT, HEAD, etc). Note that the method is always returned in
   *  uppercase.
   */
    public String getMethod(){
        return request.getMethod();
    }


  //**************************************************************************
  //** getHost
  //**************************************************************************
    public String getHost(){
        return url.getHost();
    }


  //**************************************************************************
  //** getPort
  //**************************************************************************
    public int getPort(){
        int port = url.getPort();
        if (port < 0 || port > 65535) port = 80;
        return port;
    }


  //**************************************************************************
  //** getServerName
  //**************************************************************************
  /** Returns the host name of the server to which the request was sent.
   *  It is the value of the part before ":" in the "Host" header,
   *  header value, if any, or the resolved server name, or the server IP
   *  address.
   */
    public String getServerName(){
        return getHost();
    }


  //**************************************************************************
  //** getServerPort
  //**************************************************************************
  /** Returns the port number to which the request was sent. It is the value
   *  of the part after ":" in the Host header value, if any, or the server
   *  port where the client connection was accepted on.
   */
    public int getServerPort(){
        //return request.getServerPort();
        return getPort();
    }


  /** Returns the host name of the Internet Protocol (IP) interface on which
   *  the request was received.
   */
    public String getLocalName(){
        return request.getLocalName();
    }

  /** Returns the Internet Protocol (IP) address of the interface on which the
   *  request was received.
   */
    public String getLocalAddr(){
        return request.getLocalAddr();
    }


  /** Returns the Internet Protocol (IP) port number of the interface on which
   *  the request was received.
   */
    public int getLocalPort(){
        return request.getLocalPort();
    }

//
//  //**************************************************************************
//  //** setHost
//  //**************************************************************************
//  /** Used to update the Host attribute defined in the header and in the url.
//   */
//    public void setHost(String host, int port) {
//        try{
//            url = new java.net.URL(updateURL(host,port,url));
//            host = getHost();
//            port = getPort();
//            setHeader("Host", (port==80 ? host : host+":"+port));
//        }
//        catch(Exception e){}
//    }


//  //**************************************************************************
//  //** setRefererHost
//  //**************************************************************************
//  /** Used to set/update the RefererHost attribute defined in the header. */
//
//    public void setRefererHost(String host, int port) {
//
//        String referer = getHeader("Referer");
//        if (referer!=null)
//        try{
//            java.net.URL url = new java.net.URL(referer);
//            setHeader("Referer", updateURL(host, port, url));
//        }
//        catch(Exception e){
//        }
//    }


//  //**************************************************************************
//  //** updateURL
//  //**************************************************************************
//  /**  Used to update the host and port in a URL.
//   */
//    private String updateURL(String host, Integer port, java.net.URL url) {
//
//        port = ((port==null || port < 0 || port > 65535) ? 80 : port);
//        host = (port==80 ? host : host+":"+port);
//        String str = url.toString();
//        String protocol = str.substring(0, str.indexOf(url.getHost()));
//        String path = (url.getPath()==null ? "" : url.getPath());
//        if (path.length()>0){
//            str = str.substring(protocol.length());
//            path = str.substring(str.indexOf(url.getPath()));
//        }
//        else{
//            if (url.getQuery()!=null)
//                path = str.substring(str.indexOf("?"+url.getQuery()));
//        }
//        return protocol + host + path;
//    }


  //**************************************************************************
  //** isKeepAlive
  //**************************************************************************
  /** Used to determine whether the Connection attribute is set to Keep-Alive.
   */
    public boolean isKeepAlive(){
        if (isKeepAlive==null){
            String connType = getHeader("Connection");
            isKeepAlive = (connType==null ? false : connType.toUpperCase().contains("KEEP-ALIVE"));
        }
        return isKeepAlive;
    }


  //**************************************************************************
  //** isWebSocket
  //**************************************************************************
  /** Used to determine whether the client is requesting a WebSocket
   *  connection. Returns true if the Upgrade header contains a "websocket"
   *  keyword and if the Connection header contains a "upgrade" keyword.
   */
    public boolean isWebSocket(){
        if (isWebSocket==null){
            isWebSocket = false;
            Object obj = servletContext.getAttribute("javaxt.http.websocket.WebSocketServer");
            if (obj!=null){
                if (obj instanceof javaxt.http.websocket.WebSocketServer){
                    javaxt.http.websocket.WebSocketServer ws = (javaxt.http.websocket.WebSocketServer) obj;
                    isWebSocket = ws.accept(this);
                }
            }
        }
        return isWebSocket;
    }


//  //**************************************************************************
//  //** isEncrypted
//  //**************************************************************************
//  /** Used to determine whether the Connection is encrypted (e.g. SSL/TLS)
//   */
//    public boolean isEncrypted(){
//        return sslEngine!=null;
//    }


  //**************************************************************************
  //** isSecure
  //**************************************************************************
  /** Returns a boolean indicating whether this request was made using a
   *  secure channel, such as HTTPS.
   */
    public boolean isSecure(){
        return request.isSecure();
    }


  //**************************************************************************
  //** getProtocol
  //**************************************************************************
  /** Returns the name and version of the protocol the request uses in the
   *  form <i>protocol/majorVersion.minorVersion</i> (e.g. "HTTP/1.1").
   */
    public String getProtocol(){
        return request.getProtocol();
    }


  //**************************************************************************
  //** getScheme
  //**************************************************************************
  /** Returns the name of the scheme used to make this request (e.g. "http").
   */
    public String getScheme(){
        return request.getScheme();
        //if (isWebSocket()) protocol = protocol.equals("https") ? "wss" : "ws";

        //return getURL().getProtocol().toLowerCase();
    }


  //**************************************************************************
  //** getURL
  //**************************************************************************
  /** Used to retrieve the requested url defined in the header. */

    public java.net.URL getURL(){
        return url;
    }


  //**************************************************************************
  //** getRequestURI
  //**************************************************************************
  /** Returns the part of this request's URL from the protocol name up to the
   *  query string in the first line of the HTTP request. The web container
   *  does not decode this String
   *  For example:

     * <table summary="Examples of Returned Values">
     * <tr align=left><th>First line of HTTP request      </th>
     * <th>     Returned Value</th>
     * <tr><td>POST /some/path.html HTTP/1.1<td><td>/some/path.html
     * <tr><td>GET http://foo.bar/a.html HTTP/1.0
     * <td><td>/a.html
     * <tr><td>HEAD /xyz?a=b HTTP/1.1<td><td>/xyz
     * </table>
     *
     * <p>To reconstruct an URL with a scheme and host, use
     * {@link HttpUtils#getRequestURL}.
     *
     * @return		a <code>String</code> containing
     *			the part of the URL from the
     *			protocol name up to the query string
     */
    public String getRequestURI(){
        return request.getRequestURI();
    }


  //**************************************************************************
  //** getRequestURL
  //**************************************************************************
  /** Reconstructs the URL the client used to make the request. The returned
   *  URL contains a protocol, server name, port number, and server path, but
   *  it does not include query string parameters.
   */
    public StringBuffer getRequestURL(){
        return request.getRequestURL();
    }


  //**************************************************************************
  //** getURL
  //**************************************************************************
  /** Returns the url query string. Returns null if one does not exist. */
    public String getQueryString(){
        return url.getQuery();
    }


  //**************************************************************************
  //** getParameter
  //**************************************************************************
  /** Used to retrieve the value of a specific variable supplied in the query
   *  string. Does NOT retrieve or parse posted data from form data. Use the
   *  getForm() method instead.
   *
   *  @param key Parameter name. Performs a case insensitive search for the
   *  keyword.
   *
   *  @return Returns a comma delimited list of values associated with the
   *  given key or a null value if the key is not found or if the value is
   *  an empty string.
   */
    public String getParameter(String key){
        StringBuffer str = new StringBuffer();
        List<String> values = getParameter(key, parameters);
        if (values!=null){
            for (int i=0; i<values.size(); i++){
                str.append(values.get(i));
                if (i<values.size()-1) str.append(",");
            }
            return str.toString();
        }
        else{
            return null;
        }
    }


  //**************************************************************************
  //** getParameterNames
  //**************************************************************************
  /** Returns an Enumeration of String objects containing the names of the
   *  parameters contained in the query string. If the request has no
   *  parameters, the method returns an empty Enumeration. <p/>
   *  Note that this method does NOT retrieve or parse posted data from form
   *  data. Use the getForm() method instead.
   */
    public Enumeration<String> getParameterNames(){
        return Collections.enumeration(parameters.keySet());
    }


  //**************************************************************************
  //** getParameterValues
  //**************************************************************************
  /** Returns an array containing all of the values for a given query string
   *  parameter or null if the parameter does not exist.<p/>
   *  Note that this method does NOT retrieve or parse posted data from form
   *  data. Use the getForm() method instead.
   */
    public String[] getParameterValues(String name){
        List<String> values = getParameter(name, parameters);
        if (values!=null){
            return values.toArray(new String[values.size()]);
        }
        return null;
    }


  //**************************************************************************
  //** getParameterMap
  //**************************************************************************
  /** Returns an immutable Map containing parameters found in the
   *  query string. The keys in the parameter map are of type String. The
   *  values in the parameter map are of type String array.<p/>
   *  Note that this method does NOT retrieve or parse posted data from form
   *  data. Use the getForm() method instead.
   */
    public Map<String, String[]> getParameterMap(){
        HashMap<String, String[]> map = new HashMap<String, String[]>();
        Iterator<String> it = parameters.keySet().iterator();
        while (it.hasNext()){
            String key = it.next();
            map.put(key, getParameterValues(key));
        }
        return map;
    }


  //**************************************************************************
  //** getRequestURL
  //**************************************************************************
  /** Returns a StringBuffer similar to one returned by an implementation of a
   *  HttpServletRequest class. The returned URL contains a protocol, server
   *  name, port number, and server path, but it does not include query string
   *  parameters.
   *
    public StringBuffer getRequestURL(){
        String url = this.url.toString();
        if (url.contains("?")) url = url.substring(0, url.indexOf("?"));
        return new StringBuffer().append(url);
    }
    */


  //**************************************************************************
  //** getContentLength
  //**************************************************************************
  /** Returns the "Content-Length" specified in the http request header.
   */
    public int getContentLength(){
        return request.getContentLength();
    }


  //**************************************************************************
  //** getBody
  //**************************************************************************
  /** Returns the body of the http request as a byte array. Reads all remaining
   *  bytes from the socket. Therefore, you should only call this method once.
   *  Subsequent calls will return an empty array.
   */
    public byte[] getBody() throws IOException {

      //Only POST, PUT, AND DELETE should have a body. Otherwise, return an empty array.
        String method = this.getMethod();
        if (method.equals("POST") || method.equals("PUT") || method.equals("DELETE")){}
        else return new byte[0];


      //If the client specified a Content-Length of 0, simply return an empty
      //array. Otherwise, the server will pause for 15 seconds trying to read
      //data from the socket.
        int contentLength = getContentLength();
        if (contentLength<1) return new byte[0];


        final java.io.ByteArrayOutputStream bas = new java.io.ByteArrayOutputStream();
        final HttpInput input = (HttpInput) request.getInputStream();
        int numBytesRead = -1;
        byte buffer[] = new byte[8192];
        while (input.isReady() && (numBytesRead = input.read(buffer)) != -1) {
            ByteBuffer buf = ByteBuffer.wrap(buffer, 0, numBytesRead);
            byte[] b = new byte[numBytesRead];
            buf.get(b, 0, numBytesRead);
            bas.write(b);
            //if (bas.size()==contentLength) break;
        }

        //bas.close();
        return bas.toByteArray();
    }

    private class StreamReader implements Runnable {

        private java.io.ByteArrayOutputStream bas;

        public StreamReader(java.io.ByteArrayOutputStream bas){
            this.bas = bas;
        }

        public void run(){

            while (true){
            try{

                final HttpInput input = (HttpInput) request.getInputStream();
                final javax.servlet.AsyncContext async = request.startAsync();
                input.setReadListener(new javax.servlet.ReadListener() {



                    @Override
                    public void onDataAvailable() throws IOException {
                        int numBytesRead = -1;
                        byte buffer[] = new byte[8192];
                        while (ready() && (numBytesRead = input.read(buffer)) != -1) {
                            ByteBuffer buf = ByteBuffer.wrap(buffer, 0, numBytesRead);

                            if (numBytesRead>0){
                                byte[] b = new byte[numBytesRead];
                                buf.get(b, 0, numBytesRead);
                                bas.write(b);
                                //if (contentLength<0 && numBytesRead!=bufferSize) break; //<-- Is this a valid use case? Can Content-Length be undefined?
                                //if (bas.size()==contentLength) break;
                            }
                            else{
                                break;
                            }

                        }

                        end();
                    }

                    @Override
                    public void onAllDataRead() throws IOException {
                        end();
                    }

                    @Override
                    public void onError(Throwable t) {
                        request.getServletContext().log("Async Error",t);
                    }


                    void end() {
                        //System.out.println("Done!");

                        async.complete();
                        try{
                            bas.close();
                        }
                        catch(Exception e){
                            e.printStackTrace();
                        }

                    }

                    boolean ready() {
                        return input.isReady();
                    }

                });
                return;

            }
            catch(Exception e){
                return;
            }

            }
        }
    }


  //**************************************************************************
  //** getInputStream
  //**************************************************************************
  /** Returns the body of the http request as an input stream. Automatically
   *  decrypts the body if the data is SSL/TLS encrypted. Example:
   <pre>
        java.io.InputStream inputStream = request.getInputStream();
        byte[] b = new byte[1024];
        int x=0;
        while ( (x = inputStream.read(b)) != -1) {
            //Do something! Example: outputStream.write(b,0,x);
        }
        inputStream.close();
   </pre>
   */
    public ServletInputStream getInputStream() throws java.io.IOException {
        return new ServletInputStream(request.getInputStream());
    }


  //**************************************************************************
  //** getReader
  //**************************************************************************
  /** Returns a BufferedReader used to process the body of the http request.
   *  Automatically decrypts the body if the data is SSL/TLS encrypted.
   *  Either this method or getInputStream() may be called to read the body,
   *  but not both.
   */
    public java.io.BufferedReader getReader() throws IOException{
        return request.getReader();
    }


  //**************************************************************************
  //** getFormInputs
  //**************************************************************************
  /** Returns form elements in the body of the http request as an iterator.
   *  Reads data from the client on-demand, meaning form data will only be
   *  retrieved from the client when calling Iterator.next(). This is
   *  potentially more memory efficient than calling getBody() and parsing
   *  the entire byte array. This is especially true when processing
   *  "multipart/form-data" with large amounts of binary data (e.g. uploaded
   *  files). Please see the FormInput.getInputStream() or FormInput.toFile()
   *  methods for more information on handling large binary streams.
   *  <p/>
   *
   *  Here's a simple example of how to iterate through form data using the
   *  getFormInputs() method. Note how easy it is to identify an uploaded
   *  file and save it to disk.
   <pre>
        Iterator<FormInput> it = request.getFormInputs();
        while (it.hasNext()){
            FormInput input = it.next();
            String name = input.getName();
            FormValue value = input.getValue();

            if (input.isFile()){
                value.toFile(new java.io.File("/temp/" + input.getFileName()));
                System.out.println(name + ": <FILE>");
            }
            else{
                System.out.println(name + ": " + value);
            }
        }
   </pre>
   *  Note that the form iterator reads data directly from the socket
   *  connection. Therefore, you should only call this method once.
   *  <p/>
   *
   *  More information on HTML form data can be found here:<br/>
   *  http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
   */
    public FormIterator getFormInputs() throws IOException {

        if (!this.getMethod().equals("POST"))
            throw new IOException("Unsupported method: " + this.getMethod());

        String contentType = getHeader("Content-Type");
        if (contentType==null) throw new IOException("Content-Type is undefined.");


        String boundary = null;
        if (contentType.contains("application/x-www-form-urlencoded")){
            boundary = "&";
        }
        else if (contentType.contains("multipart/form-data")){
            for (String s : contentType.split(";")){
                s = s.toLowerCase().trim();
                if (s.toLowerCase().trim().startsWith("boundary=")){
                    boundary = s.trim().substring("boundary=".length());
                    break;
                }
            }
        }
        else{
            throw new IOException("Unsupported Content-Type: " + contentType);
        }

        return new FormIterator(getInputStream(), boundary);
    }


  //**************************************************************************
  //** FormIterator Class
  //**************************************************************************
  /** Simple implementation of a Iterator used to parse form data.
   */
    private class FormIterator implements Iterator {

        private FormInput currInput = null;
        private FormInput prevInput = null;
        private ServletInputStream is;
        private String boundary;


        private FormIterator(ServletInputStream is, String boundary){
            this.is = is;
            this.boundary = boundary;
        }

        public boolean hasNext(){
            if (currInput==null) getNextInput();
            return currInput!=null;
        }

        public FormInput next(){
            if (currInput==null) getNextInput();
            FormInput input = currInput;
            prevInput = currInput;
            currInput = null;
            return input;

        }

        private void getNextInput(){
            try{
                FormInput input = new FormInput(is, prevInput, boundary);
                if (currInput!=null) prevInput = currInput;
                currInput = input;
            }
            catch(Exception e){
                currInput = null;
            }
        }

        public void remove(){
        }
    }


  //**************************************************************************
  //** getSession
  //**************************************************************************
  /** Returns the current session associated with this request, or if the
   *  request does not have a session, creates one.
   */
    public HttpSession getSession(){
        return getSession(true);
    }


  //**************************************************************************
  //** getSession
  //**************************************************************************
  /** Returns the current HttpSession associated with this request or, if
   *  there is no current session and create is true, returns a new session.
   */
    public HttpSession getSession(boolean create){
        return new HttpSession(request.getSession(create), servletContext);
    }


  //**************************************************************************
  //** getRequestedSessionId
  //**************************************************************************
  /** Returns the session ID specified by the client ("JSESSIONID" cookie).
   *  If the client did not specify a session ID, this method returns null.
   *  Use the isRequestedSessionIdValid() method to verify whether the
   *  session ID is valid.
   */
    public String getRequestedSessionId(){
        return request.getRequestedSessionId();
//        Cookie[] cookies = getCookies();
//        if (cookies!=null){
//            for (Cookie cookie : cookies){
//                if (cookie.getName().equalsIgnoreCase("JSESSIONID")){
//                    return cookie.getValue();
//                }
//            }
//        }
//        return null;
    }


  //**************************************************************************
  //** isRequestedSessionIdValid
  //**************************************************************************
  /** Checks whether the requested session ID is still valid. Returns true if
   *  this request has an id for a valid session in the current session
   *  context.
   */
    public boolean isRequestedSessionIdValid(){
        HttpSession session = getSession(false);
        return session!=null;
    }


  //**************************************************************************
  //** isRequestedSessionIdFromCookie
  //**************************************************************************
  /** Checks whether the requested session ID came in as a cookie. */

    public boolean isRequestedSessionIdFromCookie(){
        return true; //This server manages sessions via cookies...
    }


  //**************************************************************************
  //** isRequestedSessionIdFromURL
  //**************************************************************************
  /** Checks whether the requested session ID came in as part of the request
   *  URL.
   */
    public boolean isRequestedSessionIdFromURL(){
        return false; //This server manages sessions via cookies...
    }


  //**************************************************************************
  //** isRequestedSessionIdFromUrl
  //**************************************************************************
  /** @deprecated As of Version 2.1 of the Java Servlet API.
   *  Use isRequestedSessionIdFromURL() instead.
   */
    @Deprecated
    public boolean isRequestedSessionIdFromUrl(){
        return isRequestedSessionIdFromURL();
    }


  //**************************************************************************
  //** getCookies
  //**************************************************************************
  /** Returns an array containing all of the Cookie objects the client sent
   *  with this request. This method returns null if no cookies were sent.
   */
    public Cookie[] getCookies(){
        javax.servlet.http.Cookie[] cookies = request.getCookies();
        Cookie[] arr = new Cookie[cookies.length];
        for (int i=0; i<arr.length; i++){
            arr[i] = new Cookie(cookies[i]);
        }
        return arr;
    }


  //**************************************************************************
  //** setAttribute
  //**************************************************************************
  /** Returns the value of a given attribute. Returns null if no attribute of
   *  the given name exists. <p/>
   *  Attributes contain custom information about a request. Attributes are
   *  set programatically using the setAttribute() method and are typically
   *  used in conjunction with a RequestDispatcher. Attribute names should
   *  follow the same conventions as package names. The servlet specification
   *  reserves names matching "java.*", "javax.*", and "sun.*".
   */
    public Object getAttribute(String name){
        return request.getAttribute(name);
    }


  //**************************************************************************
  //** setAttribute
  //**************************************************************************
  /** Used to add, update, or delete an attribute associated with this request.
   *  Attributes contain custom information about a request and are typically
   *  used in conjunction with a RequestDispatcher. If the object passed in is
   *  null, the effect is the same as calling removeAttribute().
   */
    public void setAttribute(String name, Object o){
        request.setAttribute(name, o);
    }


  //**************************************************************************
  //** removeAttribute
  //**************************************************************************
  /** Removes an attribute associated with this request. See getAttribute()
   *  and setAttribute() for more information.
   */
    public void removeAttribute(String name){
        request.removeAttribute(name);
    }


  //**************************************************************************
  //** getAttributeNames
  //**************************************************************************
  /** Returns an Enumeration containing the names of the attributes associated
   *  with this request. Returns an empty Enumeration if the request has no
   *  attributes associated with it. See getAttribute() and setAttribute() for
   *  more information.
   */
    public Enumeration<String> getAttributeNames(){
        return request.getAttributeNames();
    }


  //**************************************************************************
  //** getRequestDispatcher
  //**************************************************************************
  /** This method is supposed to return a RequestDispatcher object that can be
   *  used to forward a request to the resource or to include the resource in
   *  a response. This server does not currently support RequestDispatcher so
   *  this method returns a null.
   */
    public Object getRequestDispatcher(String path){
        return null; //RequestDispatcher
    }


  //**************************************************************************
  //** getRealPath
  //**************************************************************************
  /** @deprecated As of Version 2.1 of the Java Servlet API. Use
   *  ServletContext.getRealPath() instead.
   */
    @Deprecated
    public String getRealPath(String path){
        //return request.getRealPath(path);
        return servletContext.getRealPath(path);
    }


  //**************************************************************************
  //** getPathInfo
  //**************************************************************************
  /** Returns any extra path information associated with the URL the client
   *  sent when it made this request. The extra path information follows the
   *  servlet path but precedes the query string and will start with a "/"
   *  character. Consider this example:
   *  <pre>http://localhost:8080/MyServlet/Extra/Path/?abc=123</pre>
   *  In this example, "/MyServlet" is the servlet path and this method will
   *  return "/Extra/Path/" as the extra path. If no extra path is found, this
   *  method will return a null.
   */
    public String getPathInfo(){
        //return request.getPathInfo();
        String path = this.getURL().getPath();
        String servletPath = getContextPath()+getServletPath();
        if (servletPath.length()>0){
            int idx = path.toLowerCase().indexOf(servletPath.toLowerCase());
            if (idx>-1){
                path = path.substring(idx + servletPath.length());
            }
        }
        if (path.length()==0) return null;
        if (path.equals("/")) return null;
        if (!path.startsWith("/")) path = "/" + path;
        return path;
    }


  //**************************************************************************
  //** getPathTranslated
  //**************************************************************************
  /** Returns any extra path information after the servlet name but before the
   *  query string, and translates it to a real path. If the URL does not have
   *  any extra path information, or if  the servlet container cannot
   *  translate the virtual path to a real path for any reason, this method
   *  returns a null.
   */
    public String getPathTranslated(){
        //return request.getPathTranslated();
        String path = getPathInfo();
        if (path!=null) return getRealPath(path);
        return null;
    }


  //**************************************************************************
  //** getContextPath
  //**************************************************************************
  /** Returns a string in the requested URL that represents the servlet
   *  context. This is typically defined in the META-INF/context.xml file in
   *  Java EE web applications. For example, if a web application is called
   *  "WebApplication", the context path might be "/WebApplication". In this
   *  case, a requested URL will include the context path like this:
   *  <pre>http://localhost:8080/WebApplication/MyServlet/?abc=123</pre><p/>
   *
   *  The context path always comes first in a request URL. The path starts
   *  with a "/" character but does not end with a "/" character. For servlets
   *  in the default (root) context, this method returns "". <p/>
   *
   *  Note that this server does not currently support the container concept
   *  where multiple servlets are managed by a servlet container. Instead, we
   *  have a single servlet that processes all web requests and can dispatch
   *  the requests to other servlets. Therefore, to retrieve a "context path"
   *  developers must explicitely set the "context path" in the servlet and
   *  implement logic to generate/process the URLs accordingly.
   */
    public String getContextPath(){
        //return request.getContextPath();
        return servletContext.getContextPath();
    }


  //**************************************************************************
  //** getServletPath
  //**************************************************************************
  /** Returns a string in the requested URL that represents the servlet path.
   *  This path starts with a "/" character and includes either the servlet
   *  name or a path to the servlet, but does not include any extra path
   *  information or a query string. For example, consider the following URL:
   *  <pre>http://localhost:8080/WebApplication/MyServlet/?abc=123</pre><p/>
   *  In this example, the context path is "/WebApplication" and "/MyServlet"
   *  is the servlet path. <p/>
   *
   *  Note that this server does not require a URL "Pattern" to be defined for
   *  for individual servlets. Instead, we have a single servlet that processes
   *  all web requests and can dispatch the requests to other servlets.
   *  Therefore, to retrieve a "servlet path" developers must explicitely set
   *  the servlet path in the servlet and implement logic to process the
   *  URLs accordingly.
   */
    public String getServletPath(){
        //return request.getServletPath();
         return servletPath;
    }


  //**************************************************************************
  //** getServletContext
  //**************************************************************************
    public ServletContext getServletContext(){
        return this.servletContext;
    }


  //**************************************************************************
  //** getAuthType
  //**************************************************************************
  /** Returns the authentication scheme used to authenticate clients (e.g.
   *  "BASIC", "DIGEST", "CLIENT_CERT", etc). This value is retrieved from an
   *  Authenticator and does not necessarily correspond to the "Authorization"
   *  request header. If an Authenticator is not used to secure the servlet,
   *  a null is returned.
   */
    public String getAuthType(){
        //return request.getAuthType();
        if (authenticator!=null) return authenticator.getAuthType();
        else return null;
    }


  //**************************************************************************
  //** getCredentials
  //**************************************************************************
  /** Returns an array representing the client credentials associated with
   *  this request. The first element in the array represents the username and
   *  the second element represents the password. <p/>
   *  Credentials are retrieved from an Authenticator. If no Authenticator is
   *  defined or if the Authenticator fails to parse the credentials, this
   *  method returns a null.
   */
    public String[] getCredentials(){
        if (getCredentials){
            try {
                //System.out.println(authenticator);
                credentials = authenticator.getCredentials();
            }
            catch(Exception e){
                e.printStackTrace();
            }
            getCredentials = false;
        }
        return credentials;
    }


  //**************************************************************************
  //** authenticate
  //**************************************************************************
  /** Used to authenticate a client request. Authentication is performed by an
   *  Authenticator. If no Authenticator is defined or if the Authenticator
   *  fails to authenticate the client, this method throws a ServletException.
   */
    public void authenticate() throws ServletException {
        if (authenticate==true){
            try{
                authenticator.authenticate();
            }
            catch(ServletException e){
                authenticationException = e;
            }
            catch(Exception e){
                authenticationException = new ServletException(e.getLocalizedMessage());
                authenticationException.setStackTrace(e.getStackTrace());
            }
            authenticate = false;
        }

        if (authenticationException!=null) throw authenticationException;
    }


  //**************************************************************************
  //** getRemoteUser
  //**************************************************************************
  /** Returns the login of the user making this request, if the user has been
   *  authenticated, or null if the user has not been authenticated.
   */
    public String getRemoteUser(){
        try{
            String[] credentials = getCredentials();
            authenticate();
            return credentials[0];
        }
        catch(Exception e){
        }
        return null;
    }


  //**************************************************************************
  //** isUserInRole
  //**************************************************************************
  /** Returns a boolean indicating whether the authenticated user is included
   *  in the specified "role". Roles and role membership are often managed by
   *  an Authenticator. If no Authenticator is defined, or if the user is not
   *  authenticated, or if no role is defined for the user, the method returns
   *  false.
   */
    public boolean isUserInRole(String role){
        try{
            return authenticator.isUserInRole(role);
        }
        catch(Exception e){
        }
        return false;
    }


  //**************************************************************************
  //** getUserPrincipal
  //**************************************************************************
  /** Returns a java.security.Principal object containing the name of the
   *  current authenticated user. User Principals are resolved by an
   *  Authenticator. If no Authenticator is defined, or if the user has not
   *  been authenticated, the method returns a null.
   */
    public java.security.Principal getUserPrincipal(){
        if (getUserPrincipal){
            try{
                principal = authenticator.getPrinciple();
            }
            catch(Exception e){
            }
            getUserPrincipal = false;
        }
        return principal;
    }


    public AsyncContext startAsync() throws IllegalStateException{
        return request.startAsync();
    }

    public AsyncContext startAsync(ServletRequest sr, ServletResponse sr1) throws IllegalStateException{
        return request.startAsync(sr, sr1);
    }

    public boolean isAsyncStarted(){
        return request.isAsyncStarted();
    }

    public boolean isAsyncSupported(){
        return request.isAsyncSupported();
    }

    public AsyncContext getAsyncContext(){
        return request.getAsyncContext();
    }

    public DispatcherType getDispatcherType(){
        return request.getDispatcherType();
    }



  //**************************************************************************
  //** toString
  //**************************************************************************
  /** Returns the full HTTP Request Header.
   */
    public String toString(){

        StringBuffer out = new StringBuffer();

        //GET /pub/WWW/TheProject.html HTTP/1.1
        out.append(getMethod());
        out.append(" ");
        out.append(getPath());
        out.append(" ");
        out.append(getProtocol());
        out.append("\r\n");


        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String name = headerNames.nextElement();
            Enumeration<String> headerValues = request.getHeaders(name);
            while (headerValues.hasMoreElements()) {
                String value = headerValues.nextElement();
                out.append(name);
                out.append(": ");
                out.append(value);
                out.append("\r\n");
            }
        }

        out.append("\r\n");
        return out.toString();
    }



  //**************************************************************************
  //** parseQueryString
  //**************************************************************************
  /** Used to parse a url query string and create a list of name/value pairs.
   *  This method is called in the constructor to parse the querystring found
   *  in the request URL.
   */
    private HashMap<String, List<String>> parseQueryString(String query){


      //IMPLEMENTATION NOTE: Code copied from the javaxt.utils.URL class

      //Create an empty hashmap
        HashMap<String, List<String>> parameters = new HashMap<String, List<String>>();
        if (query==null) return parameters;

        query = query.trim();
        if (query.length()==0) return parameters;


      //Parse the querystring, one character at a time
        if (query.startsWith("&")) query = query.substring(1);
        query += "&";


        StringBuffer word = new StringBuffer();
        for (int i=0; i<query.length(); i++){

            String c = query.substring(i,i+1);
            if (c.equals("&")){

                if (i+5<query.length() && query.substring(i,i+5).equals("&")){
                    word.append(c);
                }
                else{
                    int x = word.indexOf("=");
                    if (x>=0){
                        String key = word.substring(0,x);
                        String value = decode(word.substring(x+1));

                        List<String> values = getParameter(key, parameters);
                        if (values==null) values = new LinkedList<String>();
                        values.add(value);
                        setParameter(key, values, parameters);
                    }
                    else{
                        setParameter(word.toString(), null, parameters);
                    }

                    word = new StringBuffer();
                }
            }
            else{
                word.append(c);
            }
        }
        return parameters;
    }

    private static List<String> getParameter(String key, HashMap<String, List<String>> parameters){
        List<String> values = parameters.get(key);
        if (values==null){
            Iterator<String> it = parameters.keySet().iterator();
            while (it.hasNext()){
                String s = it.next();
                if (s.equalsIgnoreCase(key)) return parameters.get(s);
            }
        }
        return values;
    }

    private static void setParameter(String key, List<String> values, HashMap<String, List<String>> parameters){
        Iterator<String> it = parameters.keySet().iterator();
        while (it.hasNext()){
            String s = it.next();
            if (s.equalsIgnoreCase(key)){
                parameters.put(key, values);
                return;
            }
        }
        parameters.put(key, values);
    }

    private static String decode(String str){
        try{
            return java.net.URLDecoder.decode(str, "UTF-8");
        }
        catch(Exception e){
          //This should never happen. Try to decode the string manually?
            String find[] = new String[]{"%2C","%2F","%3A"};
            String replace[] = new String[]{",","/",":"};
            for (int i=0; i<find.length; i++){
                 str = str.replace(find[i],replace[i]);
            }
            return str;
        }
    }
}