| // ======================================================================== |
| // $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.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; |
| } |
| |
| public void setURI(URI uri) { |
| _uri = 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; |
| } |
| }; |
| } |