// ========================================================================
// $Id: HttpRequest.java,v 1.91 2006/11/23 08:56:52 gregwilkins Exp $
// Copyright 1996-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package org.openqa.jetty.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.print.URIException;
import javax.servlet.http.Cookie;
import org.apache.commons.logging.Log;
import org.openqa.jetty.log.LogFactory;
import org.openqa.jetty.util.ByteArrayOutputStream2;
import org.openqa.jetty.util.IO;
import org.openqa.jetty.util.InetAddrPort;
import org.openqa.jetty.util.LazyList;
import org.openqa.jetty.util.LineInput;
import org.openqa.jetty.util.LogSupport;
import org.openqa.jetty.util.MultiMap;
import org.openqa.jetty.util.QuotedStringTokenizer;
import org.openqa.jetty.util.StringMap;
import org.openqa.jetty.util.StringUtil;
import org.openqa.jetty.util.TypeUtil;
import org.openqa.jetty.util.URI;
import org.openqa.jetty.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.
* <P>
* 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.91 2006/11/23 08:56:52 gregwilkins Exp $
* @author Greg Wilkins (gregw)
*/
public class HttpRequest extends HttpMessage
{
private static Log log = LogFactory.getLog(HttpRequest.class);
/* ------------------------------------------------------------ */
/**
* 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.openqa.jetty.http.HttpRequest.maxContentSize system property.
*/
public static int __maxFormContentSize = Integer.getInteger(
"org.openqa.jetty.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 Principal _userPrincipal;
private String _authUser;
private String _authType;
private char[] _uriExpanded;
/* ------------------------------------------------------------ */
/**
* 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.openqa.jetty.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);
updateMimeType();
}
else if (__HTTP_0_9.equals(_version))
{
_dotVersion = -1;
_version = __HTTP_0_9;
}
else
{
_dotVersion = 0;
_version = __HTTP_1_0;
_header.read(in);
updateMimeType();
}
_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.
* <p>
* Because this method returns a <code>StringBuffer</code>, 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.
*
* <p>
* Because this method returns a <code>StringBuffer</code>, not a string, you can modify the
* URL easily, for example, to append query parameters.
*
* <p>
* This method is useful for creating redirect messages and for reporting errors.
*
* @return a <code>StringBuffer</code> 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.lastIndexOf(':');
if (colon >= 0)
{
if (colon < _host.length())
{
try
{
_port = TypeUtil.parseInt(_host, colon + 1, -1, 10);
}
catch (Exception e)
{
LogSupport.ignore(log, 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)
{
LogSupport.ignore(log, 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 < len; i++)
{
char c = buf[i];
switch (state)
{
case 0: // leading white
if (c == ' ') continue;
state = 1;
s1 = i;
case 1: // reading method
if (c == ' ')
state = 2;
else
{
s2 = i;
if (c >= 'a' && c <= 'z') buf[i] = (char) (c - 'a' + 'A');
}
continue;
case 2: // skip whitespace after method
s3 = i;
if (c != ' ') break startloop;
}
}
// Search for last space separated chunk
int e1 = -1, e2 = -1, e3 = -1;
state = 0;
endloop: for (int i = len; i-- > 0;)
{
char c = buf[i];
switch (state)
{
case 0: // trailing white
if (c == ' ') continue;
state = 1;
e1 = i;
case 1: // reading Version
if (c == ' ')
state = 2;
else
e2 = i;
continue;
case 2: // skip whitespace before version
e3 = i;
if (c != ' ') break endloop;
}
}
// Check sufficient params
if (s3 < 0 || e1 < 0 || e3 < s2)
throw new IOException("Bad Request: " + new String(buf, 0, len));
// get method
Map.Entry method = __methodCache.getEntry(buf, s1, s2 - s1 + 1);
if (method != null)
_method = (String) method.getKey();
else
_method = new String(buf, s1, s2 - s1 + 1).toUpperCase();
// get version as uppercase
if (s2 != e3 || s3 != e2)
{
Map.Entry version = __versionCache.getEntry(buf, e2, e1 - e2 + 1);
if (version != null)
_version = (String) version.getKey();
else
{
for (int i = e2; i <= e1; i++)
if (buf[i] >= 'a' && buf[i] <= 'z') buf[i] = (char) (buf[i] - 'a' + 'A');
_version = new String(buf, e2, e1 - e2 + 1);
}
}
else
{
// missing version
_version = __HTTP_0_9;
e3 = e1;
}
// handle URI
try
{
String raw_uri =null;
if (URI.__CHARSET_IS_DEFAULT)
raw_uri = new String(buf, s3, e3 - s3 + 1);
else
{
int l=e3-s3+1;
for (int i=0;i<l;i++)
{
char c=buf[s3+i];
if (c>=0 && c<0x80)
continue;
if (_uriExpanded==null || _uriExpanded.length<3*l)
_uriExpanded=new char[3*l];
if (i>0)
System.arraycopy(buf, s3, _uriExpanded, 0, i);
int j=i;
for (;i<l;i++)
{
c=buf[s3+i];
if (c>=0 && c<0x80)
_uriExpanded[j++]=c;
else
{
_uriExpanded[j++]='%';
_uriExpanded[j++]=TypeUtil.toHexChar(0xf&(c>>4));
_uriExpanded[j++]=TypeUtil.toHexChar(0xf&c);
}
}
raw_uri=new String(_uriExpanded, 0, j);
}
if (raw_uri==null)
raw_uri = new String(buf, s3, e3 - s3 + 1);
}
if (_uri == null)
_uri = new URI(raw_uri);
else
_uri.setURI(raw_uri);
}
catch (IllegalArgumentException e)
{
LogSupport.ignore(log, e);
throw new HttpException(HttpResponse.__400_Bad_Request,
new String(buf, s3, e3 - s3 + 1));
}
}
/* ------------------------------------------------------------ */
/**
* Force a removeField. This call ignores the message state and forces a field to be removed
* from the request. It is required for the handling of the Connection field.
*
* @param name The field name
* @return The old value or null.
*/
Object forceRemoveField(String name)
{
int saved_state = _state;
try
{
_state = __MSG_EDITABLE;
return removeField(name);
}
finally
{
_state = saved_state;
}
}
/* ------------------------------------------------------------ */
/**
* Get the acceptable transfer encodings. The TE field is used to construct a list of acceptable
* extension transfer codings in quality order. An empty list implies that only "chunked" is
* acceptable. A null list implies that no transfer coding can be applied.
*
* If the "trailer" coding is found in the TE field, then message trailers are enabled in any
* linked response.
*
* @return List of codings.
*/
public List getAcceptableTransferCodings()
{
if (_dotVersion < 1) return null;
if (_te != null) return _te;
// Decode any TE field
Enumeration tenum = getFieldValues(HttpFields.__TE, HttpFields.__separators);
if (tenum != null)
{
// Sort the list
List te = HttpFields.qualityList(tenum);
int size = te.size();
// Process if something there
if (size > 0)
{
Object acceptable = null;
// remove trailer and chunked items.
ListIterator iter = te.listIterator();
while (iter.hasNext())
{
String coding = StringUtil.asciiToLowerCase(HttpFields.valueParameters(iter
.next().toString(), null));
if (!HttpFields.__Chunked.equalsIgnoreCase(coding))
{
acceptable = LazyList.ensureSize(acceptable, size);
acceptable = LazyList.add(acceptable, coding);
}
}
_te = LazyList.getList(acceptable);
}
else
_te = Collections.EMPTY_LIST;
}
else
_te = Collections.EMPTY_LIST;
return _te;
}
/* ------------------------------------------------------------ */
/*
* Extract Paramters from query string and/or form content.
*/
private void extractParameters()
{
if (_paramsExtracted) return;
_paramsExtracted = true;
if (_parameters == null) _parameters = new MultiMap(16);
// Handle query string
String encoding = getCharacterEncoding();
if (encoding == null)
{
_uri.putParametersTo(_parameters);
}
else
{
// An encoding has been set, so reencode query string.
String query = _uri.getQuery();
if (query != null) UrlEncoded.decodeTo(query, _parameters, encoding);
}
// handle any content.
if (_state == __MSG_RECEIVED)
{
String content_type = getField(HttpFields.__ContentType);
if (content_type != null && content_type.length() > 0)
{
content_type = StringUtil.asciiToLowerCase(content_type);
content_type = HttpFields.valueParameters(content_type, null);
if (HttpFields.__WwwFormUrlEncode.equalsIgnoreCase(content_type)
&& HttpRequest.__POST.equals(getMethod()))
{
int content_length = getIntField(HttpFields.__ContentLength);
if (content_length == 0)
log.debug("No form content");
else
{
try
{
int max = content_length;
if (__maxFormContentSize > 0)
{
if (max < 0)
max = __maxFormContentSize;
else if (max > __maxFormContentSize)
throw new IllegalStateException("Form too large");
}
// Read the content
ByteArrayOutputStream2 bout = new ByteArrayOutputStream2(max > 0 ? max
: 4096);
InputStream in = getInputStream();
// Copy to a byte array.
// TODO - this is very inefficient and we could
// save lots of memory by streaming this!!!!
IO.copy(in, bout, max);
if (bout.size()==__maxFormContentSize && in.available()>0)
throw new IllegalStateException("Form too large");
// Add form params to query params
UrlEncoded.decodeTo(bout.getBuf(), 0, bout.getCount(), _parameters,encoding);
}
catch (EOFException e)
{
LogSupport.ignore(log, e);
}
catch (IOException e)
{
if (log.isDebugEnabled())
log.warn(LogSupport.EXCEPTION, e);
else
log.warn(e.toString());
}
}
}
}
}
}
/* ------------------------------------------------------------ */
/**
* @return Map of parameters
*/
public MultiMap getParameters()
{
if (!_paramsExtracted) extractParameters();
return _parameters;
}
/* ------------------------------------------------------------ */
/**
* Get the set of parameter names.
*
* @return Set of parameter names.
*/
public Set getParameterNames()
{
if (!_paramsExtracted) extractParameters();
return _parameters.keySet();
}
/* ------------------------------------------------------------ */
/**
* Get a parameter value.
*
* @param name Parameter name
* @return Parameter value
*/
public String getParameter(String name)
{
if (!_paramsExtracted) extractParameters();
return (String) _parameters.getValue(name, 0);
}
/* ------------------------------------------------------------ */
/**
* Get multi valued paramater.
*
* @param name Parameter name
* @return Parameter values
*/
public List getParameterValues(String name)
{
if (!_paramsExtracted) extractParameters();
return _parameters.getValues(name);
}
/* ------------------------------------------------------------ */
/**
* @return Parameters as a map of String arrays
*/
public Map getParameterStringArrayMap()
{
if (!_paramsExtracted) extractParameters();
return _parameters.toStringArrayMap();
}
/* -------------------------------------------------------------- */
/**
* Extract received cookies from a header.
*
* @return Array of Cookies.
*/
public Cookie[] getCookies()
{
if (_cookies != null && _cookiesExtracted) return _cookies;
try
{
// Handle no cookies
if (!_header.containsKey(HttpFields.__Cookie))
{
_cookies = __noCookies;
_cookiesExtracted = true;
_lastCookies = null;
return _cookies;
}
// Check if cookie headers match last cookies
if (_lastCookies != null)
{
int last = 0;
Enumeration enm = _header.getValues(HttpFields.__Cookie);
while (enm.hasMoreElements())
{
String c = (String) enm.nextElement();
if (last >= _lastCookies.length || !c.equals(_lastCookies[last]))
{
_lastCookies = null;
break;
}
last++;
}
if (_lastCookies != null)
{
_cookiesExtracted = true;
return _cookies;
}
}
// Get ready to parse cookies (Expensive!!!)
Object cookies = null;
Object lastCookies = null;
int version = 0;
Cookie cookie = null;
// For each cookie header
Enumeration enm = _header.getValues(HttpFields.__Cookie);
while (enm.hasMoreElements())
{
// Save a copy of the unparsed header as cache.
String hdr = enm.nextElement().toString();
lastCookies = LazyList.add(lastCookies, hdr);
// Parse the header
QuotedStringTokenizer tok = new QuotedStringTokenizer(hdr, ",;", false, false);
tok.setSingle(false);
while (tok.hasMoreElements())
{
String c = (String) tok.nextElement();
if (c == null) continue;
c = c.trim();
try
{
String n;
String v;
int e = c.indexOf('=');
if (e > 0)
{
n = c.substring(0, e);
v = c.substring(e + 1);
}
else
{
n = c;
v = "";
}
// Handle quoted values
if (version > 0) v = StringUtil.unquote(v);
// Ignore $ names
if (n.startsWith("$"))
{
if ("$version".equalsIgnoreCase(n))
version = Integer.parseInt(QuotedStringTokenizer.unquoteDouble(v));
else if ("$path".equalsIgnoreCase(n) && cookie != null)
cookie.setPath(v);
else if ("$domain".equalsIgnoreCase(n) && cookie != null)
cookie.setDomain(v);
continue;
}
v = URI.decodePath(v);
cookie = new Cookie(n, v);
if (version > 0) cookie.setVersion(version);
cookies = LazyList.add(cookies, cookie);
}
catch (Exception ex)
{
LogSupport.ignore(log, ex);
}
}
}
int l = LazyList.size(cookies);
if (_cookies == null || _cookies.length != l) _cookies = new Cookie[l];
for (int i = 0; i < l; i++)
_cookies[i] = (Cookie) LazyList.get(cookies, i);
_cookiesExtracted = true;
l = LazyList.size(lastCookies);
_lastCookies = new String[l];
for (int i = 0; i < l; i++)
_lastCookies[i] = (String) LazyList.get(lastCookies, i);
}
catch (Exception e)
{
log.warn(LogSupport.EXCEPTION, e);
}
return _cookies;
}
/* ------------------------------------------------------------ */
public boolean isUserInRole(String role)
{
Principal principal = getUserPrincipal();
if (principal != null)
{
UserRealm realm = getHttpResponse().getHttpContext().getRealm();
if (realm != null) return realm.isUserInRole(principal, role);
}
return false;
}
/* ------------------------------------------------------------ */
public String getAuthType()
{
if (_authType == null) getUserPrincipal();
return _authType;
}
/* ------------------------------------------------------------ */
public void setAuthType(String a)
{
_authType = a;
}
/* ------------------------------------------------------------ */
public String getAuthUser()
{
if (_authUser == null) getUserPrincipal();
return _authUser;
}
/* ------------------------------------------------------------ */
public void setAuthUser(String user)
{
_authUser = user;
}
/* ------------------------------------------------------------ */
public boolean hasUserPrincipal()
{
if (_userPrincipal == __NOT_CHECKED) getUserPrincipal();
return _userPrincipal != null && _userPrincipal != __NO_USER;
}
/* ------------------------------------------------------------ */
public Principal getUserPrincipal()
{
if (_userPrincipal == __NO_USER) return null;
if (_userPrincipal == __NOT_CHECKED)
{
_userPrincipal = __NO_USER;
// Try a lazy authentication
HttpContext context = getHttpResponse().getHttpContext();
if (context != null)
{
Authenticator auth = context.getAuthenticator();
UserRealm realm = context.getRealm();
if (realm != null && auth != null)
{
try
{
// TODO - should not need to recalculate this!
String pathInContext = getPath().substring(
context.getContextPath().length());
auth.authenticate(realm, pathInContext, this, null);
}
catch (Exception e)
{
LogSupport.ignore(log, e);
}
}
}
if (_userPrincipal == __NO_USER) return null;
}
return _userPrincipal;
}
/* ------------------------------------------------------------ */
public void setUserPrincipal(Principal principal)
{
_userPrincipal = principal;
}
/* ------------------------------------------------------------ */
/**
* Recycle the request.
*/
void recycle(HttpConnection connection)
{
_method = null;
//_uri=null;
_host = null;
_hostPort = null;
_port = 0;
_te = null;
if (_parameters != null) _parameters.clear();
_paramsExtracted = false;
_handled = false;
_cookiesExtracted = false;
_timeStamp = 0;
_timeStampStr = null;
_authUser = null;
_authType = null;
_userPrincipal = null;
super.recycle(connection);
}
/* ------------------------------------------------------------ */
/**
* Destroy the request. Help the garbage collector by null everything that we can.
*/
public void destroy()
{
_parameters = null;
_method = null;
_uri = null;
_host = null;
_hostPort = null;
_te = null;
_cookies = null;
_lastCookies = null;
_timeStampStr = null;
_userPrincipal = null;
_authUser = null;
_authUser = null;
if (_attributes != null) _attributes.clear();
super.destroy();
}
static Principal __NO_USER = new Principal()
{
public String getName()
{
return null;
}
};
static Principal __NOT_CHECKED = new Principal()
{
public String getName()
{
return null;
}
};
}