blob: d1520e02d804df601844c22e34c6464df06e8a0c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1999, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.http.servlet;
import java.io.*;
import java.security.Principal;
import java.util.*;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.*;
import org.eclipse.equinox.http.*;
import org.eclipse.equinox.socket.SocketInterface;
import org.eclipse.osgi.util.NLS;
import org.osgi.service.http.HttpContext;
/**
* Implementation of the HttpServletRequest interface
*
* Per Servlet 2.2 Section 3.3.3.3, this object need not be thread-safe.
*/
public class HttpServletRequestImpl implements HttpServletRequest {
protected Http http;
protected HttpServletResponseImpl response;
protected SocketInterface socket;
protected ServletInputStreamImpl servletInputStream;
protected String scheme;
protected String authType = null;
protected String remoteUser = null;
protected int contentLength = -2;
protected String contentType = null;
protected String serverName = null;
protected Hashtable parameters = null;
protected Hashtable attributes = null;
protected BufferedReader reader = null;
protected ServletInputStream inputstream = null;
protected Cookie[] cookies;
//request-line variables
protected String method = null;
protected String reqURI = null; /* URI decoded */
protected String protocol = null;
protected String servletPath = null;
protected String pathInfo = null; /* URI decoded */
protected String queryString = null;
protected String charset = null;
protected Hashtable headers = null;
protected HttpSessionImpl session;
protected String requestedSessionId = null;
protected ServletContextImpl servletContext;
protected boolean parsedQueryData = false;
public HttpServletRequestImpl(SocketInterface socket, Http http, HttpServletResponseImpl response) throws IOException {
this.response = response;
this.socket = socket;
this.http = http;
scheme = socket.getScheme();
servletInputStream = new ServletInputStreamImpl(socket.getInputStream());
response.setRequest(this);
parseHeaders(); /* allocate headers Hashtable */
}
/**
* Initialize additional request data.
*
* @param servletPathParam URI alias for this request
* @param servletContextParam ServletContext for this request
*/
public void init(String servletPathParam, ServletContextImpl servletContextParam) {
// BUGBUG Need to deal with context path
// Servlet 2.2 Section 5.4
this.servletPath = servletPathParam;
this.servletContext = servletContextParam;
String tempPathInfo = reqURI.substring(servletPathParam.length());
if ((tempPathInfo.length() == 0) || tempPathInfo.equals("/")) //$NON-NLS-1$
{
/* leave as null */
} else {
this.pathInfo = tempPathInfo;
}
if (authType == null) {
Object obj = getAttribute(HttpContext.AUTHENTICATION_TYPE);
if (obj instanceof String) {
authType = (String) obj;
}
}
if (remoteUser == null) {
Object obj = getAttribute(HttpContext.REMOTE_USER);
if (obj instanceof String) {
remoteUser = (String) obj;
}
}
}
/**
* Returns the value of the named attribute of the request, or
* null if the attribute does not exist. This method allows
* access to request information not already provided by the other
* methods in this interface. Attribute names should follow the
* same convention as package names.
* The following predefined attributes are provided.
*
* <TABLE BORDER>
* <tr>
* <th>Attribute Name</th>
* <th>Attribute Type</th>
* <th>Description</th>
* </tr>
*
* <tr>
* <td VALIGN=TOP>javax.net.ssl.cipher_suite</td>
* <td VALIGN=TOP>string</td>
* <td>The string name of the SSL cipher suite in use, if the
* request was made using SSL</td>
* </tr>
*
* <tr>
* <td VALIGN=TOP>javax.net.ssl.peer_certificates</td>
* <td VALIGN=TOP>array of javax.security.cert.X509Certificate</td>
* <td>The chain of X.509 certificates which authenticates the client.
* This is only available when SSL is used with client
* authentication is used.</td>
* </tr>
*
* <tr>
* <td VALIGN=TOP>javax.net.ssl.session</td>
* <td VALIGN=TOP>javax.net.ssl.SSLSession</td>
* <td>An SSL session object, if the request was made using SSL.</td>
* </tr>
*
* </TABLE>
*
* <BR>
* <P>The package (and hence attribute) names beginning with java.*,
* and javax.* are reserved for use by Javasoft. Similarly, com.sun.*
* is reserved for use by Sun Microsystems.
*
* @param name the name of the attribute whose value is required
*/
public Object getAttribute(String name) {
if (attributes != null) {
return (attributes.get(name));
}
return (null);
}
/**
* Returns an enumeration of attribute names contained in this request.
*/
public Enumeration getAttributeNames() {
if (attributes != null) {
return (attributes.keys());
}
return (new Vector(0).elements());
}
/**
* Gets the authentication scheme of this request. Same as the CGI
* variable AUTH_TYPE.
*
* @return this request's authentication scheme, or null if none.
*/
public String getAuthType() {
return (authType);
}
/**
* Returns the character set encoding for the input of this request.
*/
public String getCharacterEncoding() {
if (contentType == null) {
getContentType(); /* parse the content type */
}
return (charset);
}
/**
* Returns the size of the request entity data, or -1 if not known.
* Same as the CGI variable CONTENT_LENGTH.
*/
public int getContentLength() {
if (contentLength == -2) {
contentLength = getIntHeaderUpper("CONTENT-LENGTH"); //$NON-NLS-1$
}
return (contentLength);
}
/**
* Returns the Internet Media Type of the request entity data, or
* null if not known. Same as the CGI variable CONTENT_TYPE.
*/
public String getContentType() {
if (contentType == null) {
contentType = getHeaderUpper("CONTENT-TYPE"); //$NON-NLS-1$
if (contentType != null) {
int index = contentType.indexOf(';', 0);
if (index >= 0) {
Tokenizer tokenizer = new Tokenizer(contentType);
// TODO: verify next statement. It was String mimetype = tokenizer.getToken(";");
tokenizer.getToken(";"); //$NON-NLS-1$
tokenizer.getChar(); /* eat semicolon */
parseloop: while (true) {
String attribute = tokenizer.getToken("="); //$NON-NLS-1$
char c = tokenizer.getChar();
if (c != '=') {
break parseloop; /* invalid content type */
}
String value = tokenizer.getString(";"); //$NON-NLS-1$
c = tokenizer.getChar();
if ("charset".equalsIgnoreCase(attribute)) //$NON-NLS-1$
{
charset = value;
}
if (c == '\0') {
break parseloop;
}
}
}
}
}
return (contentType);
}
/**
* Gets an array of cookies found in this request. If no cookies are present, an empty
* array was returned.
* @return the array of cookies found in this request
*/
public Cookie[] getCookies() {
parseCookies();
return ((Cookie[]) cookies.clone());
}
/**
* Gets the value of the requested date header field of this
* request. If the header can't be converted to a date, the method
* throws an IllegalArgumentException. The case of the header
* field name is ignored.
*
* From HTTP/1.1 RFC 2616
* 3.3.1 Full Date
*
* HTTP applications have historically allowed three different formats
* for the representation of date/time stamps:
*
* Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123
* Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
* Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format
*
* The first format is preferred as an Internet standard and represents
* a fixed-length subset of that defined by RFC 1123 [8] (an update to
* RFC 822 [9]). The second format is in common use, but is based on the
* obsolete RFC 850 [12] date format and lacks a four-digit year.
* HTTP/1.1 clients and servers that parse the date value MUST accept
* all three formats (for compatibility with HTTP/1.0), though they MUST
* only generate the RFC 1123 format for representing HTTP-date values
* in header fields. See section 19.3 for further information.
*
* Note: Recipients of date values are encouraged to be robust in
* accepting date values that may have been sent by non-HTTP
* applications, as is sometimes the case when retrieving or posting
* messages via proxies/gateways to SMTP or NNTP.
*
* All HTTP date/time stamps MUST be represented in Greenwich Mean Time
* (GMT), without exception. For the purposes of HTTP, GMT is exactly
* equal to UTC (Coordinated Universal Time). This is indicated in the
* first two formats by the inclusion of "GMT" as the three-letter
* abbreviation for time zone, and MUST be assumed when reading the
* asctime format. HTTP-date is case sensitive and MUST NOT include
* additional LWS beyond that specifically included as SP in the
* grammar.
*
* HTTP-date = rfc1123-date | rfc850-date | asctime-date
* rfc1123-date = wkday "," SP date1 SP time SP "GMT"
* rfc850-date = weekday "," SP date2 SP time SP "GMT"
* asctime-date = wkday SP date3 SP time SP 4DIGIT
* date1 = 2DIGIT SP month SP 4DIGIT
* ; day month year (e.g., 02 Jun 1982)
* date2 = 2DIGIT "-" month "-" 2DIGIT
* ; day-month-year (e.g., 02-Jun-82)
* date3 = month SP ( 2DIGIT | ( SP 1DIGIT ))
* ; month day (e.g., Jun 2)
* time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
* ; 00:00:00 - 23:59:59
* wkday = "Mon" | "Tue" | "Wed"
* | "Thu" | "Fri" | "Sat" | "Sun"
* weekday = "Monday" | "Tuesday" | "Wednesday"
* | "Thursday" | "Friday" | "Saturday" | "Sunday"
* month = "Jan" | "Feb" | "Mar" | "Apr"
* | "May" | "Jun" | "Jul" | "Aug"
* | "Sep" | "Oct" | "Nov" | "Dec"
*
* Note: HTTP requirements for the date/time stamp format apply only
* to their usage within the protocol stream. Clients and servers are
* not required to use these formats for user presentation, request
* logging, etc.
*
* @param name the String containing the name of the requested
* header field
* @return the value the requested date header field, or -1 if not
* found.
*/
public long getDateHeader(String name) {
//headers are stored as strings and must be converted
String date = getHeader(name);
if (date != null) {
HttpDate d = new HttpDate(date);
if (d.isValid()) {
return (d.getAsLong()); // Parsed OK, so get the value as a Long
}
throw new IllegalArgumentException();
}
return (-1);
}
/**
* Gets the value of the requested header field of this request.
* The case of the header field name is ignored.
*
* @param name the String containing the name of the requested
* header field
* @return the value of the requested header field, or null if not
* known.
*/
//This should be case insensitive
public String getHeader(String name) {
return ((String) headers.get(name.toUpperCase()));
}
/**
* Gets the header names for this request.
*
* @return an enumeration of strings representing the header names
* for this request. Some server implementations do not allow
* headers to be accessed in this way, in which case this method
* will return null.
*/
public Enumeration getHeaderNames() {
return (headers.keys());
}
protected String getHeaderUpper(String name) {
return ((String) headers.get(name));
}
/**
* Returns an input stream for reading binary data in the request body.
*
* @see #getReader()
* @exception IllegalStateException if getReader has been
* called on this same request.
*/
public ServletInputStream getInputStream() {
if (inputstream == null) {
synchronized (this) {
if (inputstream == null) {
if (reader != null) {
throw new IllegalStateException();
}
inputstream = servletInputStream.getServletInputStream(getContentLength());
}
}
}
return (inputstream);
}
/**
* Gets the value of the specified integer header field of this
* request. The case of the header field name is ignored. If the
* header can't be converted to an integer, the method throws a
* NumberFormatException.
*
* @param name the String containing the name of the requested
* header field
* @return the value of the requested header field, or -1 if not
* found.
*/
//This lookup is case insensitive
public int getIntHeader(String name) {
String value = getHeader(name);
if (value != null) {
return (Integer.parseInt(value));
}
return (-1);
}
protected int getIntHeaderUpper(String name) {
String value = getHeaderUpper(name);
if (value != null) {
return (Integer.parseInt(value));
}
return (-1);
}
/**
* Gets the HTTP method (for example, GET, POST, PUT) with which
* this request was made. Same as the CGI variable REQUEST_METHOD.
*
* @return the HTTP method with which this request was made
*/
public String getMethod() {
return (method);
}
/**
* Returns a string containing the lone value of the specified
* parameter, or null if the parameter does not exist. For example,
* in an HTTP servlet this method would return the value of the
* specified query string parameter. Servlet writers should use
* this method only when they are sure that there is only one value
* for the parameter. If the parameter has (or could have)
* multiple values, servlet writers should use
* getParameterValues. If a multiple valued parameter name is
* passed as an argument, the return value is implementation
* dependent.
*
* @see #getParameterValues
*
* @param name the name of the parameter whose value is required.
*/
public String getParameter(String name) {
String[] values = getParameterValues(name);
if ((values != null) && (values.length > 0)) {
return (values[0]);
}
return (null);
}
/**
* Returns the parameter names for this request as an enumeration
* of strings, or an empty enumeration if there are no parameters
* or the input stream is empty. The input stream would be empty
* if all the data had been read from the stream returned by the
* method getInputStream.
*/
public Enumeration getParameterNames() {
if (!parsedQueryData) {
parseQueryData();
}
if (parameters != null) {
return (parameters.keys());
}
return (new Vector(0).elements());
}
/**
* Returns the values of the specified parameter for the request as
* an array of strings, or null if the named parameter does not
* exist. For example, in an HTTP servlet this method would return
* the values of the specified query string or posted form as an
* array of strings.
*
* @param name the name of the parameter whose value is required.
* @see javax.servlet.ServletRequest#getParameter
*/
public String[] getParameterValues(String name) {
if (!parsedQueryData) {
parseQueryData();
}
if (parameters != null) {
return ((String[]) parameters.get(name));
}
return (null);
}
/**
* Gets any optional extra path information following the servlet
* path of this request's URI, but immediately preceding its query
* string. Same as the CGI variable PATH_INFO.
*
* @return the optional path information following the servlet
* path, but before the query string, in this request's URI; null
* if this request's URI contains no extra path information
*/
public String getPathInfo() {
return (pathInfo);
}
/**
* Gets any optional extra path information following the servlet
* path of this request's URI, but immediately preceding its query
* string, and translates it to a real path. Similar to the CGI
* variable PATH_TRANSLATED
*
* @return extra path information translated to a real path or null
* if no extra path information is in the request's URI
*/
public String getPathTranslated() {
// JSP 1.0 Section B.5
return servletContext.getRealPath(getPathInfo());
}
/**
* Returns the protocol and version of the request as a string of
* the form <code>&lt;protocol&gt;/&lt;major version&gt;.&lt;minor
* version&gt</code>. Same as the CGI variable SERVER_PROTOCOL.
*/
public String getProtocol() {
return (protocol);
}
/**
* Gets any query string that is part of the HTTP request URI.
* Same as the CGI variable QUERY_STRING.
*
* @return query string that is part of this request's URI, or null
* if it contains no query string
*/
public String getQueryString() {
return (queryString);
}
/**
* Returns a buffered reader for reading text in the request body.
* This translates character set encodings as appropriate.
*
* @see #getInputStream()
*
* @exception IllegalStateException if getInputStream has been
* called on this same request.
*/
public BufferedReader getReader() {
if (reader == null) {
synchronized (this) {
if (reader == null) {
if (inputstream != null) {
throw new IllegalStateException();
}
// BUGBUG Must create reader with charset getCharacterEncoding or iso-8859-1 if null.
// Servlet 2.3 Section 4.9
reader = new BufferedReader(new InputStreamReader(servletInputStream.getServletInputStream(getContentLength())));
}
}
}
return (reader);
}
/**
* Applies alias rules to the specified virtual path and returns
* the corresponding real path, or null if the translation can not
* be performed for any reason. For example, an HTTP servlet would
* resolve the path using the virtual docroot, if virtual hosting
* is enabled, and with the default docroot otherwise. Calling
* this method with the string "/" as an argument returns the
* document root.
*
* @param path the virtual path to be translated to a real path
* *deprecated
*/
public String getRealPath(String path) {
return servletContext.getRealPath(path);
}
/**
* Returns the IP address of the agent that sent the request.
* Same as the CGI variable REMOTE_ADDR.
*/
public String getRemoteAddr() {
return (socket.getInetAddress().getHostAddress());
}
/**
* Returns the fully qualified host name of the agent that sent the
* request. Same as the CGI variable REMOTE_HOST.
*/
public String getRemoteHost() {
return (socket.getInetAddress().getHostName());
}
/**
* Gets the name of the user making this request. The user name is
* set with HTTP authentication. Whether the user name will
* continue to be sent with each subsequent communication is
* browser-dependent. Same as the CGI variable REMOTE_USER.
*
* @return the name of the user making this request, or null if not
* known.
*/
public String getRemoteUser() {
return (remoteUser);
}
/**
* Returns the session id specified with this request. This may differ from
* the session id in the current session if the session id given by the
* client was invalid for whatever reason and a new session was created.
* This method will return null if the request does not have a session
* associated with it.
*
* @return the session id specified by this request, or null if the
* request did not specify a session id
*
* @see #isRequestedSessionIdValid */
public String getRequestedSessionId() {
parseCookies(); /* allocate cookies array */
if (requestedSessionId == null) {
String sessionCookieName = HttpSessionImpl.sessionCookieName;
int numCookies = cookies.length;
for (int i = 0; i < numCookies; i++) {
Cookie cookie = cookies[i];
if (sessionCookieName.equals(cookie.getName())) {
requestedSessionId = cookie.getValue();
break;
}
}
}
return (requestedSessionId);
}
/**
* Gets, from the first line of the HTTP request, the part of this
* request's URI that is to the left of any query string.
* For example,
*
* <blockquote>
* <table>
* <tr align=left><th>First line of HTTP request<th>
* <th>Return from <code>getRequestURI</code>
* <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>http://foo.bar/a.html
* <tr><td>HEAD /xyz?a=b HTTP/1.1<td><td>/xyz
* </table>
* </blockquote>
*
* <p>To reconstruct a URL with a URL scheme and host, use the
* method javax.servlet.http.HttpUtils.getRequestURL, which returns
* a StringBuffer.
*
* @return this request's URI
* @see javax.servlet.http.HttpUtils#getRequestURL
*/
public String getRequestURI() {
// BUGBUG this should probably be URI encoded?
// Servlet 2.2 Section 5.4
return (reqURI);
}
/**
* Returns the scheme of the URL used in this request, for example
* "http", "https", or "ftp". Different schemes have different
* rules for constructing URLs, as noted in RFC 1738. The URL used
* to create a request may be reconstructed using this scheme, the
* server name and port, and additional information such as URIs.
*/
public String getScheme() {
return (scheme);
}
/**
* Returns the host name of the server that received the request.
* Same as the CGI variable SERVER_NAME.
*/
public String getServerName() {
if (serverName == null) {
String value = getHeaderUpper("HOST"); //$NON-NLS-1$
if (value != null) {
int n = value.indexOf(':');
if (n < 0) {
serverName = value;
} else {
serverName = value.substring(0, n).trim();
}
} else {
serverName = socket.getLocalAddress().getHostName();
}
}
return serverName;
}
/**
* Returns the port number on which this request was received.
* Same as the CGI variable SERVER_PORT.
*/
public int getServerPort() {
return (socket.getLocalPort());
}
/**
* Gets the part of this request's URI that refers to the servlet
* being invoked. Analogous to the CGI variable SCRIPT_NAME.
*
* @return the servlet being invoked, as contained in this
* request's URI
*/
public String getServletPath() {
return (servletPath);
}
/**
* Returns the current valid session associated with this request.
* A session will be created for the
* request if there is not already a session associated with the request.
*
* To ensure the session is properly
* maintained, the servlet developer must call this method before the
* response is committed.
*
* @return the session associated with this request.
*/
public HttpSession getSession() {
return (getSession(true));
}
/**
* Returns the current valid session associated with this request.
* If there is not already a session associated with the request,
* a session will be created for the request only
* if the argument is true.
*
* To ensure the session is properly
* maintained, the servlet developer must call this method before the
* response is committed.
*
* If the create flag is set to false and no session
* is associated with this request, then this method will return null.
*
* <p><b>Note</b>: to ensure the session is properly maintained,
* the servlet developer must call this method (at least once)
* before any output is written to the response.
*
* <p>Additionally, application-writers need to be aware that newly
* created sessions (that is, sessions for which
* <code>HttpSession.isNew</code> returns true) do not have any
* application-specific state.
*
* @return the session associated with this request or null if
* create was false and no valid session is associated
* with this request.
*/
public synchronized HttpSession getSession(boolean create) {
if (session != null) /* if session cached in this request */
{
/* test to see if the session is still valid */
if (session.isValid(false)) {
return (session);
}
session = null; /* dereference invalid session */
} else {
/* Session is not cached in this request
* Check to see if the client requested a session id.
*/
String sessionId = getRequestedSessionId();
if (sessionId != null) {
session = http.getSession(sessionId);
if (session != null) /* valid session in cache */
{
return (session);
}
}
}
// we didn't get a valid session, so create one if desired
if (create) {
session = new HttpSessionImpl(http);
response.addCookie(session.getCookie());
return (session);
}
// Nothing we did produced a valid session, and the caller
// didn't ask us to create one.
return (null);
}
/**
* Checks whether the session id specified by this request came in
* as a cookie. (The requested session may not be one returned by
* the <code>getSession</code> method.)
*
* @return true if the session id specified by this request came in
* as a cookie; false otherwise
*
* @see #getSession
*/
public boolean isRequestedSessionIdFromCookie() {
/* We always use cookies. If there is a requestedSessionId,
* it came from a Cookie.
*/
return (getRequestedSessionId() != null);
}
/**
* Checks whether the session id specified by this request came in
* as part of the URL. (The requested session may not be the one
* returned by the <code>getSession</code> method.)
*
* @return true if the session id specified by the request for this
* session came in as part of the URL; false otherwise
*
* @see #getSession
*
* @deprecated use isRequestSessionIdFromURL() instead
*/
public boolean isRequestedSessionIdFromUrl() {
return (isRequestedSessionIdFromURL());
}
/**
* Checks whether the session id specified by this request came in
* as part of the URL. (The requested session may not be the one
* returned by the <code>getSession</code> method.)
*
* @return true if the session id specified by the request for this
* session came in as part of the URL; false otherwise
*
* @see #getSession
*/
public boolean isRequestedSessionIdFromURL() {
/* We do not support URL rewriting. We use cookies. */
return (false);
}
/**
* This method checks whether this request is associated with a session
* that is currently valid. If the session used by the request is not valid,
* it will not be returned via the getSession method.
*
* @return true if the request session is valid.
*
* @see #getRequestedSessionId
* @see javax.servlet.http.HttpSessionContext
* @see #getSession
*/
public boolean isRequestedSessionIdValid() {
HttpSession currentSession = getSession(false); /* get current session, if any */
if (currentSession != null) /* if there is a session, see if it the requested session */
{
return (currentSession.getId().equals(getRequestedSessionId()));
}
return (false);
}
protected synchronized void parseCookies() {
if (cookies == null) {
nocookies: {
String cookieHeader = getHeaderUpper("COOKIE"); //$NON-NLS-1$
if (cookieHeader == null) {
break nocookies;
}
Vector cookieVector = new Vector(20);
int cookieVersion = 0;
//parse through cookie header for all cookies
Tokenizer tokenizer = new Tokenizer(cookieHeader);
String name = tokenizer.getToken("="); //$NON-NLS-1$
char c = tokenizer.getChar();
String value;
if (name.equals("$Version")) //$NON-NLS-1$
{
if (c != '=') {
if (Http.DEBUG) {
http.logDebug("Cookie parse error", new Exception()); //$NON-NLS-1$
}
break nocookies;
}
value = tokenizer.getString(";,"); //$NON-NLS-1$
try {
cookieVersion = Integer.parseInt(value);
} catch (NumberFormatException e) {
if (Http.DEBUG) {
http.logDebug("Cookie version error", e); //$NON-NLS-1$
}
}
name = null;
}
parseloop: while (true) {
if (name == null) {
name = tokenizer.getToken("="); //$NON-NLS-1$
c = tokenizer.getChar();
}
if (c != '=') {
if (Http.DEBUG) {
http.logDebug("Cookie parse error", new Exception()); //$NON-NLS-1$
}
break nocookies;
}
value = tokenizer.getString(";,"); //$NON-NLS-1$
c = tokenizer.getChar();
Cookie cookie;
try {
cookie = new Cookie(name, value);
} catch (IllegalArgumentException e) {
if (Http.DEBUG) {
http.logDebug("Cookie constructor error", e); //$NON-NLS-1$
}
break nocookies;
}
cookie.setVersion(cookieVersion);
cookieVector.addElement(cookie);
if (c == '\0') {
break parseloop;
}
name = tokenizer.getToken("="); //$NON-NLS-1$
c = tokenizer.getChar();
if (name.equals("$Path")) //$NON-NLS-1$
{
if (c != '=') {
if (Http.DEBUG) {
http.logDebug("Cookie parse error", new Exception()); //$NON-NLS-1$
}
break nocookies;
}
cookie.setPath(tokenizer.getString(";,")); //$NON-NLS-1$
c = tokenizer.getChar();
if (c == '\0') {
break parseloop;
}
name = tokenizer.getToken("="); //$NON-NLS-1$
c = tokenizer.getChar();
}
if (name.equals("$Domain")) //$NON-NLS-1$
{
if (c != '=') {
if (Http.DEBUG) {
http.logDebug("Cookie parse error", new Exception()); //$NON-NLS-1$
}
break nocookies;
}
cookie.setDomain(tokenizer.getString(";,")); //$NON-NLS-1$
c = tokenizer.getChar();
if (c == '\0') {
break parseloop;
}
name = null;
}
}
if (cookieVector.size() > 0) {
cookies = new Cookie[cookieVector.size()];
cookieVector.copyInto(cookies);
return;
}
}
cookies = new Cookie[0];
}
}
protected void parseHeaders() throws IOException {
headers = new Hashtable(31);
byte[] buffer = new byte[4096];
/* The first line in an http request is always the request-line. */
String line = readHeaderLine(buffer);
if (line.length() == 0) {
throw new InterruptedIOException(HttpMsg.HTTP_NO_HEADER_LINE_READ_EXCEPTION);
}
socket.markActive(); /* indicate we are processing a request */
parseRequestLine(line);
/* Now we get to the headers. */
// BUGBUG Headers can be repeated! getHeader must return the first header
// in the request. The (2.2) getHeaders method can be used to get all
// the headers' values.
// Servlet 2.2 Section 5.3
boolean firstLine = true;
String header = null;
StringBuffer value = new StringBuffer(256);
while (true) {
line = readHeaderLine(buffer);
if (line.length() == 0) { //End of headers
if (!firstLine) /* flush last line */
{
headers.put(header, value.toString().trim());
}
break;
}
// System.out.println(line);
char c = line.charAt(0);
if ((c == ' ') || (c == '\t')) /* continuation */
{
if (firstLine) /* if no previous line */
{
throw new IOException(NLS.bind(HttpMsg.HTTP_INVALID_HEADER_LINE_EXCEPTION, line));
}
value.append(line.substring(1));
continue;
}
if (!firstLine) {
headers.put(header, value.toString().trim());
value.setLength(0); /* clear StringBuffer */
}
//use ':' as a delimeter to separate the key and the value
int colon = line.indexOf(':', 0);
// Our keys are saved as upper case so we can do case-insensitive
// searches on them.
header = line.substring(0, colon).toUpperCase();
value.append(line.substring(colon + 1));
firstLine = false;
}//while
}
/**
* This methods MUST only be called by one of the getParameter methods
* Servlet 2.2 Section 5.1
*/
protected synchronized void parseQueryData() {
if (!parsedQueryData) {
try {
/* Request parameters must come from BOTH the query string
* and the POST data. Query string must be processed before POST data.
* Servlet 2.2 Section 5.1.
*/
if (queryString != null) {
if (parameters == null) {
parameters = new Hashtable();
}
parseQueryString(parameters, queryString, null);
}
/* POST data must only be read if the following conditions are
* true
* 1. getScheme is "http" or "https"
* 2. getMethod is "POST"
* 3. getContextType is "application/x-www-form-urlencoded"
* 4. servlet calls getParameter* method.
* Servlet 2.2 Section 5.1
*/
String content_type = getContentType();
if (content_type != null) {
int index = content_type.indexOf(';', 0);
if (index >= 0) {
content_type = content_type.substring(0, index).trim();
}
}
if ("POST".equals(method) && //$NON-NLS-1$
("http".equals(scheme) || "https".equals(scheme)) && //$NON-NLS-1$ //$NON-NLS-2$
"application/x-www-form-urlencoded".equals(content_type) //$NON-NLS-1$
) {
int content_length = getContentLength();
if (content_length > 0) {
// System.out.println("Read POST data");
/* Read the post data from the ServletInputStream */
ServletInputStream in = getInputStream();
byte buffer[] = new byte[content_length];
int bytesRead = 0;
while (bytesRead < content_length) {
int count;
try {
count = in.read(buffer, bytesRead, content_length - bytesRead);
} catch (IOException e) {
throw new IllegalArgumentException();
}
if (count < 1) {
break;
}
bytesRead += count;
}
String encoding = getCharacterEncoding();
/* Must use charset getCharacterEncoding or iso-8859-1 if null.
* Servlet 2.3 Section 4.9
*/
String postData = URI.convert(buffer, 0, bytesRead, encoding);
if (parameters == null) {
parameters = new Hashtable();
}
parseQueryString(parameters, postData, encoding);
}
}
} catch (Exception e) {
//Bad query string, ignore, log and continue
http.logError(HttpMsg.HTTP_QUERYDATA_PARSE_EXCEPTION, e);
}
parsedQueryData = true;
}
}
/**
* Parses a query string and builds a hashtable of key-value
* pairs, where the values are arrays of strings. The query string
* should have the form of a string packaged by the GET or POST
* method. (For example, it should have its key-value pairs
* delimited by ampersands (&) and its keys separated from its
* values by equal signs (=).)
*
* <p> A key can appear one or more times in the query string.
* Each time a key appears, its corresponding value is inserted
* into its string array in the hash table. (So keys that appear
* once in the query string have, in the hash table, a string array
* of length one as their value, keys that appear twice have a
* string array of length two, etc.)
*
* <p> When the keys and values are moved into the hashtable, any
* plus signs (+) are returned to spaces and characters sent in
* hexadecimal notation (%xx) are converted back to characters.
*
* @param data query string to be parsed
* @param result a hashtable built from the parsed key-value pairs; the
*.hashtable's values are arrays of strings
* @exception IllegalArgumentException if the query string is
* invalid.
*/
protected void parseQueryString(Hashtable result, String data, String encoding) {
if (data == null) {
throw new IllegalArgumentException();
}
// System.out.println("Querystring: " + data);
data = data.trim(); /* Strip CRLF if present */
int len = data.length();
if (len >= 0) {
int begin = 0;
while (true) {
int end = data.indexOf('&', begin);
if (end == -1) {
end = len;
}
int equals = data.indexOf('=', begin);
String key;
String value;
if ((equals >= end) || (equals == -1)) {
key = URI.decode(data, begin, end, encoding);
value = ""; //$NON-NLS-1$
} else {
key = URI.decode(data, begin, equals, encoding);
value = URI.decode(data, equals + 1, end, encoding);
}
String[] values = (String[]) result.get(key);
if (values == null) {
values = new String[1];
values[0] = value;
result.put(key, values);
} else {
int length = values.length;
String[] newvalues = new String[length + 1];
System.arraycopy(values, 0, newvalues, 0, length);
newvalues[length] = value;
result.put(key, newvalues);
}
if (end == len) {
break;
}
begin = end + 1;
}
}
}
/**
* This method was created in VisualAge.
* @param requestLine java.lang.String
*/
protected void parseRequestLine(String requestLine) {
if (Http.DEBUG) {
http.logDebug("Http Request Line=\"" + requestLine + "\""); //$NON-NLS-1$ //$NON-NLS-2$
}
// System.out.println("Http Request Line=\"" + requestLine + "\"");
int space = requestLine.indexOf(' ', 0);
method = requestLine.substring(0, space);
int nextspace = requestLine.lastIndexOf(' ');
protocol = requestLine.substring(nextspace + 1);
int query = requestLine.indexOf('?', space + 1);
if ((query >= nextspace) || (query == -1)) {
reqURI = URI.decode(requestLine, space + 1, nextspace, null);
} else {
reqURI = URI.decode(requestLine, space + 1, query, null);
queryString = requestLine.substring(query + 1, nextspace);
}
}
/**
* This method is only used by the constructor (albiet indirectly)
* @return java.lang.String
*/
protected String readHeaderLine(byte[] buffer) throws IOException {
int read = servletInputStream.readLine(buffer, 0, buffer.length);
if (read <= 0) {
throw new InterruptedIOException(HttpMsg.HTTP_NO_HEADER_LINE_READ_EXCEPTION);
}
// BUGBUG should use 8859_1 encoding to make string
/* create String from byte array using 0 for high byte of chars */
String line = URI.convert(buffer, 0, read, null);
if (line.endsWith("\n")) //$NON-NLS-1$
{
return (line.trim()); /* trim removes trailing CRLF */
}
try {
response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
} finally {
response.close();
}
throw new IOException(NLS.bind(HttpMsg.HTTP_HEADER_LINE_TOO_LONG_EXCEPTION, new Integer(buffer.length)));
}
/**
* This method places an attribute into the request for later use by
* other objects which will have access to this request object such as
* nested servlets.
*
* @param name Attribute name
* @param val Attribute value
*/
public void setAttribute(String name, Object val) {
if (attributes == null) {
synchronized (this) {
if (attributes == null) {
attributes = new Hashtable(31);
}
}
}
if (val == null) {
attributes.remove(name);
} else {
attributes.put(name, val);
}
}
/**
* @see javax.servlet.http.HttpServletRequest#getContextPath()
*/
public String getContextPath() throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.http.HttpServletRequest#getHeaders(String)
*/
public Enumeration getHeaders(String name) throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.http.HttpServletRequest#getRequestURL()
*/
public StringBuffer getRequestURL() throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.http.HttpServletRequest#getUserPrincipal()
*/
public Principal getUserPrincipal() throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.http.HttpServletRequest#isUserInRole(String)
*/
public boolean isUserInRole(String role) throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.ServletRequest#getLocale()
*/
public Locale getLocale() throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.ServletRequest#getLocales()
*/
public Enumeration getLocales() throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.ServletRequest#getParameterMap()
*/
public Map getParameterMap() throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.ServletRequest#getRequestDispatcher(String)
*/
public RequestDispatcher getRequestDispatcher(String path) throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.ServletRequest#isSecure()
*/
public boolean isSecure() throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.ServletRequest#removeAttribute(String)
*/
public void removeAttribute(String name) throws UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/**
* @see javax.servlet.ServletRequest#setCharacterEncoding(String)
*/
public void setCharacterEncoding(String env) throws UnsupportedEncodingException, UnsupportedOperationException {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/* For compilation only. Will not implement.
*
*/
public String getLocalAddr() {
//return(socket.getInetAddress().getLocalHost().getHostAddress());
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/* For compilation only. Will not implement.
*
*/
public String getLocalName() {
//return(socket.getInetAddress().getLocalHost().getHostName());
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/* For compilation only. Will not implement.
*
*/
public int getLocalPort() {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
/* For compilation only. Will not implement.
*
*/
public int getRemotePort() {
throw new UnsupportedOperationException(HttpMsg.HTTP_ONLY_SUPPORTS_2_1);
}
}