// =========================================================================== // Copyright (c) 1996 Mort Bay Consulting Pty. Ltd. All rights reserved. // $Id: HttpRequest.java,v 1.16.2.16 2003/07/11 00:55:12 jules_gosnell Exp $ // --------------------------------------------------------------------------- package org.mortbay.http; import java.io.IOException; import java.io.InputStream; import java.io.Writer; import java.net.InetAddress; import java.security.Principal; import java.util.Collections; import java.util.Enumeration; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; import javax.servlet.http.Cookie; import org.mortbay.util.ByteArrayOutputStream2; import org.mortbay.util.Code; import org.mortbay.util.IO; import org.mortbay.util.InetAddrPort; import org.mortbay.util.LazyList; import org.mortbay.util.LineInput; import org.mortbay.util.MultiMap; import org.mortbay.util.QuotedStringTokenizer; import org.mortbay.util.StringMap; import org.mortbay.util.StringUtil; import org.mortbay.util.TypeUtil; import org.mortbay.util.URI; import org.mortbay.util.UrlEncoded; /* ------------------------------------------------------------ */ /** HTTP Request. * This class manages the headers, trailers and content streams * of a HTTP request. It can be used for receiving or generating * requests. *
* This class is not synchronized. It should be explicitly * synchronized if it is used by multiple threads. * * @see HttpResponse * @version $Id: HttpRequest.java,v 1.16.2.16 2003/07/11 00:55:12 jules_gosnell Exp $ * @author Greg Wilkins (gregw) */ public class HttpRequest extends HttpMessage { /* ------------------------------------------------------------ */ /** Request METHODS. */ public static final String __GET="GET", __POST="POST", __HEAD="HEAD", __PUT="PUT", __OPTIONS="OPTIONS", __DELETE="DELETE", __TRACE="TRACE", __CONNECT="CONNECT", __MOVE="MOVE"; /* ------------------------------------------------------------ */ /** Max size of the form content. * Limits the size of the data a client can push at the server. * Set via the org.mortbay.http.HttpRequest.maxContentSize * system property. */ public static int __maxFormContentSize= Integer.getInteger("org.mortbay.http.HttpRequest.maxFormContentSize", 200000).intValue(); /* ------------------------------------------------------------ */ /** Maximum header line length. */ public static int __maxLineLength=4096; public static final StringMap __methodCache = new StringMap(true); public static final StringMap __versionCache = new StringMap(true); static { __methodCache.put(__GET,null); __methodCache.put(__POST,null); __methodCache.put(__HEAD,null); __methodCache.put(__PUT,null); __methodCache.put(__OPTIONS,null); __methodCache.put(__DELETE,null); __methodCache.put(__TRACE,null); __methodCache.put(__CONNECT,null); __methodCache.put(__MOVE,null); __versionCache.put(__HTTP_1_1,null); __versionCache.put(__HTTP_1_0,null); __versionCache.put(__HTTP_0_9,null); } private static Cookie[] __noCookies = new Cookie[0]; /* ------------------------------------------------------------ */ private String _method=null; private URI _uri=null; private String _host; private String _hostPort; private int _port; private List _te; private MultiMap _parameters; private boolean _paramsExtracted; private boolean _handled; private Cookie[] _cookies; private String[] _lastCookies; private boolean _cookiesExtracted; private long _timeStamp; private String _timeStampStr; private UserPrincipal _userPrincipal; private String _authUser; private String _authType; /* ------------------------------------------------------------ */ /** Constructor. */ public HttpRequest() {} /* ------------------------------------------------------------ */ /** Constructor. * @param connection */ public HttpRequest(HttpConnection connection) { super(connection); } /* ------------------------------------------------------------ */ /** Get Request TimeStamp * @return The time that the request was received. */ public String getTimeStampStr() { if (_timeStampStr==null && _timeStamp>0) _timeStampStr=HttpFields.__dateCache.format(_timeStamp); return _timeStampStr; } /* ------------------------------------------------------------ */ /** Get Request TimeStamp * @return The time that the request was received. */ public long getTimeStamp() { return _timeStamp; } /* ------------------------------------------------------------ */ public void setTimeStamp(long ts) { _timeStamp=ts; } /* ------------------------------------------------------------ */ /** * @deprecated use getHttpResponse() */ public HttpResponse getResponse() { return getHttpResponse(); } /* ------------------------------------------------------------ */ /** Get the HTTP Response. * Get the HTTP Response associated with this request. * @return associated response */ public HttpResponse getHttpResponse() { if (_connection==null) return null; return _connection.getResponse(); } /* ------------------------------------------------------------ */ /** Is the request handled. * @return True if the request has been set to handled or the * associated response is not editable. */ public boolean isHandled() { if (_handled) return true; HttpResponse response= getHttpResponse(); return (response!=null && response.getState()!=HttpMessage.__MSG_EDITABLE); } /* ------------------------------------------------------------ */ /** Set the handled status. * @param handled true or false */ public void setHandled(boolean handled) { _handled=handled; } /* ------------------------------------------------------------ */ /** Read the request line and header. * @param in * @exception IOException */ public void readHeader(LineInput in) throws IOException { _state=__MSG_BAD; // Get start line org.mortbay.util.LineInput.LineBuffer line_buffer; do { line_buffer=in.readLineBuffer(); if (line_buffer==null) throw new EOFException(); } while(line_buffer.size==0); if (line_buffer.size>=__maxLineLength) throw new HttpException(HttpResponse.__414_Request_URI_Too_Large); decodeRequestLine(line_buffer.buffer,line_buffer.size); _timeStamp=System.currentTimeMillis(); // Handle version - replace with fast compare if (__HTTP_1_1.equals(_version)) { _dotVersion=1; _version=__HTTP_1_1; _header.read(in); setMimeAndEncoding(_header.get(HttpFields.__ContentType)); } else if (__HTTP_0_9.equals(_version)) { _dotVersion=-1; _version=__HTTP_0_9; } else { _dotVersion=0; _version=__HTTP_1_0; _header.read(in); setMimeAndEncoding(_header.get(HttpFields.__ContentType)); } _handled=false; _state=__MSG_RECEIVED; } /* -------------------------------------------------------------- */ /** Write the HTTP request line as it was received. */ public void writeRequestLine(Writer writer) throws IOException { writer.write(_method); writer.write(' '); writer.write(_uri!=null?_uri.toString():"null"); writer.write(' '); writer.write(_version); } /* -------------------------------------------------------------- */ /** Write the request header. * Places the message in __MSG_SENDING state. * @param writer Http output stream * @exception IOException IO problem */ public void writeHeader(Writer writer) throws IOException { if (_state!=__MSG_EDITABLE) throw new IllegalStateException("Not MSG_EDITABLE"); _state=__MSG_BAD; writeRequestLine(writer); writer.write(HttpFields.__CRLF); _header.write(writer); _state=__MSG_SENDING; } /* -------------------------------------------------------------- */ /** Return the HTTP request line as it was received. */ public String getRequestLine() { return _method+" "+_uri+" "+_version; } /* -------------------------------------------------------------- */ /** Get the HTTP method for this request. * Returns the method with which the request was made. The returned * value can be "GET", "HEAD", "POST", or an extension method. Same * as the CGI variable REQUEST_METHOD. * @return The method */ public String getMethod() { return _method; } /* ------------------------------------------------------------ */ public void setMethod(String method) { if (getState()!=__MSG_EDITABLE) throw new IllegalStateException("Not EDITABLE"); _method=method; } /* ------------------------------------------------------------ */ public String getVersion() { return _version; } /* ------------------------------------------------------------ */ /** * Reconstructs the URL the client used to make the request. * The returned URL contains a protocol, server name, port * number, and, but it does not include a path. *
* Because this method returns a StringBuffer,
* not a string, you can modify the URL easily, for example,
* to append path and query parameters.
*
* This method is useful for creating redirect messages
* and for reporting errors.
*
* @return "scheme://host:port"
*/
public StringBuffer getRootURL()
{
StringBuffer url = new StringBuffer (48);
synchronized(url)
{
String scheme = getScheme();
int port = getPort();
url.append (scheme);
url.append ("://");
if (_hostPort!=null)
url.append(_hostPort);
else
{
url.append (getHost());
if (port>0 && ((scheme.equalsIgnoreCase ("http") && port != 80)||
(scheme.equalsIgnoreCase ("https") && port != 443)))
{
url.append (':');
url.append (port);
}
}
return url;
}
}
/* ------------------------------------------------------------ */
/**
* 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.
*
*
Because this method returns a StringBuffer,
* not a string, you can modify the URL easily, for example,
* to append query parameters.
*
*
This method is useful for creating redirect messages
* and for reporting errors.
*
* @return a StringBuffer object containing
* the reconstructed URL
*
*/
public StringBuffer getRequestURL()
{
StringBuffer buf = getRootURL();
buf.append(getPath());
return buf;
}
/* -------------------------------------------------------------- */
/** Get the full URI.
* @return the request URI (not a clone).
*/
public URI getURI()
{
return _uri;
}
/* ------------------------------------------------------------ */
/** Get the request Scheme.
* The scheme is obtained from an absolute URI. If the URI in
* the request is not absolute, then the connections default
* scheme is returned. If there is no connection "http" is returned.
* @return The request scheme (eg. "http", "https", etc.)
*/
public String getScheme()
{
String scheme=_uri.getScheme();
if (scheme==null && _connection!=null)
scheme=_connection.getDefaultScheme();
return scheme==null?"http":scheme;
}
/* ------------------------------------------------------------ */
/**
* @return True if this request came over an integral channel such as SSL
*/
public boolean isIntegral()
{
return _connection.getListener().isIntegral(_connection);
}
/* ------------------------------------------------------------ */
/**
* @return True if this request came over an confidential channel such as SSL.
*/
public boolean isConfidential()
{
return _connection.getListener().isConfidential(_connection);
}
/* ------------------------------------------------------------ */
/** Get the request host.
* @return The host name obtained from an absolute URI, the HTTP header field,
* the requests connection or the local host name.
*/
public String getHost()
{
// Return already determined host
if (_host!=null)
return _host;
// Return host from absolute URI
_host=_uri.getHost();
_port=_uri.getPort();
if (_host!=null)
return _host;
// Return host from header field
_hostPort=_header.get(HttpFields.__Host);
_host=_hostPort;
_port=0;
if (_host!=null && _host.length()>0)
{
int colon=_host.indexOf(':');
if (colon>=0)
{
if (colon<_host.length())
{
try{
_port=TypeUtil.parseInt(_host,colon+1,-1,10);
}
catch(Exception e)
{Code.ignore(e);}
}
_host=_host.substring(0,colon);
}
return _host;
}
// Return host from connection
if (_connection!=null)
{
_host=_connection.getServerName();
_port=_connection.getServerPort();
if (_host!=null && !InetAddrPort.__0_0_0_0.equals(_host))
return _host;
}
// Return the local host
try {_host=InetAddress.getLocalHost().getHostAddress();}
catch(java.net.UnknownHostException e){Code.ignore(e);}
return _host;
}
/* ------------------------------------------------------------ */
/** Get the request port.
* The port is obtained either from an absolute URI, the HTTP
* Host header field, the connection or the default.
* @return The port. 0 should be interpreted as the default port.
*/
public int getPort()
{
if (_port>0)
return _port;
if (_host!=null)
return 0;
if (_uri.isAbsolute())
_port=_uri.getPort();
else if (_connection!=null)
_port=_connection.getServerPort();
return _port;
}
/* ------------------------------------------------------------ */
/** Get the request path.
* @return The URI path of the request.
*/
public String getPath()
{
return _uri.getPath();
}
/* ------------------------------------------------------------ */
public void setPath(String path)
{
if (getState()!=__MSG_EDITABLE)
throw new IllegalStateException("Not EDITABLE");
if (_uri==null)
_uri=new URI(path);
else
_uri.setURI(path);
}
/* ------------------------------------------------------------ */
/** Get the encoded request path.
* @return The path with % encoding.
*/
public String getEncodedPath()
{
return _uri.getEncodedPath();
}
/* ------------------------------------------------------------ */
/** Get the request query.
* @return the request query excluding the '?'
*/
public String getQuery()
{
return _uri.getQuery();
}
/* ------------------------------------------------------------ */
public void setQuery(String q)
{
if (getState()!=__MSG_EDITABLE)
throw new IllegalStateException("Not EDITABLE");
_uri.setQuery(q);
}
/* ------------------------------------------------------------ */
public String getRemoteAddr()
{
String addr="127.0.0.1";
HttpConnection connection = getHttpConnection();
if (connection!=null)
{
addr=connection.getRemoteAddr();
if (addr==null)
addr=connection.getRemoteHost();
}
return addr;
}
/* ------------------------------------------------------------ */
public String getRemoteHost()
{
String host="127.0.0.1";
HttpConnection connection = getHttpConnection();
if (connection!=null)
{
host=connection.getRemoteHost();
if (host==null)
host=connection.getRemoteAddr();
}
return host;
}
/* ------------------------------------------------------------ */
/** Decode HTTP request line.
* @param buf Character buffer
* @param len Length of line in buffer.
* @exception IOException
*/
void decodeRequestLine(char[] buf,int len)
throws IOException
{
// Search for first space separated chunk
int s1=-1,s2=-1,s3=-1;
int state=0;
startloop:
for (int i=0;i