view src/org/eclipse/jetty/server/Request.java @ 1002:35d04ac3fd0b

simplify ssl
author Franklin Schmidt <fschmidt@gmail.com>
date Sat, 22 Oct 2016 21:56:44 -0600
parents 39154cfa58e4
children 6939226e0ac4
line wrap: on
line source

//
//  ========================================================================
//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
//  ------------------------------------------------------------------------
//  All rights reserved. This program and the accompanying materials
//  are made available under the terms of the Eclipse Public License v1.0
//  and Apache License v2.0 which accompanies this distribution.
//
//      The Eclipse Public License is available at
//      http://www.eclipse.org/legal/epl-v10.html
//
//      The Apache License v2.0 is available at
//      http://www.opensource.org/licenses/apache2.0.php
//
//  You may elect to redistribute this code under either of these licenses.
//  ========================================================================
//

package org.eclipse.jetty.server;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.security.Principal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.DispatcherType;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;

import org.eclipse.jetty.http.HttpCookie;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeaders;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersions;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.io.BufferUtil;
import org.eclipse.jetty.io.ByteArrayBuffer;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.nio.DirectNIOBuffer;
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
import org.eclipse.jetty.io.nio.NIOBuffer;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/* ------------------------------------------------------------ */
/**
 * Jetty Request.
 * <p>
 * Implements {@link javax.servlet.http.HttpServletRequest} from the <code>javax.servlet.http</code> package.
 * </p>
 * <p>
 * The standard interface of mostly getters, is extended with setters so that the request is mutable by the handlers that it is passed to. This allows the
 * request object to be as lightweight as possible and not actually implement any significant behavior. For example
 * <ul>
 *
 * <li>The {@link Request#getContextPath()} method will return null, until the request has been passed to a {@link ContextHandler} which matches the
 * {@link Request#getPathInfo()} with a context path and calls {@link Request#setContextPath(String)} as a result.</li>
 *
 * <li>The {@link Request#getServletPath()} method will return null until the request has been passed to a <code>org.eclipse.jetty.servlet.ServletHandler</code>
 * and the pathInfo matched against the servlet URL patterns and {@link Request#setServletPath(String)} called as a result.</li>
 * </ul>
 *
 * A request instance is created for each {@link AbstractHttpConnection} accepted by the server and recycled for each HTTP request received via that connection.
 * An effort is made to avoid reparsing headers and cookies that are likely to be the same for requests from the same connection.
 *
 * <p>
 * The form content that a request can process is limited to protect from Denial of Service attacks. The size in bytes is limited by
 * {@link ContextHandler#getMaxFormContentSize()} or if there is no context then the "org.eclipse.jetty.server.Request.maxFormContentSize" {@link Server}
 * attribute. The number of parameters keys is limited by {@link ContextHandler#getMaxFormKeys()} or if there is no context then the
 * "org.eclipse.jetty.server.Request.maxFormKeys" {@link Server} attribute.
 *
 *
 */
public final class Request implements HttpServletRequest
{
	private static final Logger LOG = LoggerFactory.getLogger(Request.class);

	private static final Collection __defaultLocale = Collections.singleton(Locale.getDefault());
	private static final int __NONE = 0, _STREAM = 1;

	private volatile Attributes _attributes;
	private MultiMap<String> _baseParameters;
	private String _characterEncoding;
	protected final AbstractHttpConnection _connection;
	public ContextHandler _contextHandler = null;
	private String _contextPath;
	private CookieCutter _cookies;
	private boolean _cookiesExtracted = false;
	private final EndPoint _endp;
	private boolean _handled = false;
	private int _inputState = __NONE;
	private String _method;
	private MultiMap<String> _parameters;
	private boolean _paramsExtracted;
	private String _pathInfo;
	private int _port;
	private String _protocol = HttpVersions.HTTP_1_1;
	private String _queryString;
	private String _readerEncoding;
	private String _requestURI;
	private String _scheme = URIUtil.HTTP;
	private String _serverName;
	private long _timeStamp;

	private HttpURI _uri;
	
	public Request(AbstractHttpConnection connection)
	{
		_connection = connection;
		_endp = connection._endp;
	}

	/* ------------------------------------------------------------ */
	/**
	 * Extract Parameters from query string and/or form _content.
	 */
	private void extractParameters()
	{
		if (_baseParameters == null)
			_baseParameters = new MultiMap(16);

		if (_paramsExtracted)
		{
			if (_parameters == null)
				_parameters = _baseParameters;
			return;
		}

		_paramsExtracted = true;

		try
		{
			// Handle query string
			if (_uri != null && _uri.hasQuery())
			{
				_uri.decodeQueryTo(_baseParameters);
			}

			// handle any _content.
			String encoding = getCharacterEncoding();
			String content_type = getContentType();
			if (content_type != null && content_type.length() > 0)
			{
				content_type = HttpFields.valueParameters(content_type,null);

				if (MimeTypes.FORM_ENCODED.equalsIgnoreCase(content_type) && _inputState == __NONE
						&& ("POST".equals(getMethod()) || "PUT".equals(getMethod())))
				{
					int content_length = getContentLength();
					if (content_length != 0)
					{
						try
						{
							int maxFormContentSize = -1;
							int maxFormKeys = -1;

							if (maxFormContentSize < 0)
							{
								maxFormContentSize = 200000;
							}
							
							if (maxFormKeys < 0)
							{
								maxFormKeys = 1000;
							}

							if (content_length > maxFormContentSize && maxFormContentSize > 0)
							{
								throw new IllegalStateException("Form too large " + content_length + ">" + maxFormContentSize);
							}
							InputStream in = getInputStream();

							// Add form params to query params
							UrlEncoded.decodeTo(in,_baseParameters,encoding,content_length < 0?maxFormContentSize:-1,maxFormKeys);
						}
						catch (IOException e)
						{
							if (LOG.isDebugEnabled())
								LOG.warn("",e);
							else
								LOG.warn(e.toString());
						}
					}
				}
			  
			}

			if (_parameters == null)
				_parameters = _baseParameters;
			else if (_parameters != _baseParameters)
			{
				// Merge parameters (needed if parameters extracted after a forward).
				Iterator iter = _baseParameters.entrySet().iterator();
				while (iter.hasNext())
				{
					Map.Entry entry = (Map.Entry)iter.next();
					String name = (String)entry.getKey();
					Object values = entry.getValue();
					for (int i = 0; i < LazyList.size(values); i++)
						_parameters.add(name,LazyList.get(values,i));
				}
			}
		}
		finally
		{
			// ensure params always set (even if empty) after extraction
			if (_parameters == null)
				_parameters = _baseParameters;
		}
	}

	@Override
	public AsyncContext getAsyncContext()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public Object getAttribute(String name)
	{
		return (_attributes == null)?null:_attributes.getAttribute(name);
	}

	@Override
	public Enumeration getAttributeNames()
	{
		if (_attributes == null)
			return Collections.enumeration(Collections.EMPTY_LIST);

		return AttributesMap.getAttributeNamesCopy(_attributes);
	}

	@Override
	public String getAuthType()
	{
		return null;
	}

	@Override
	public String getCharacterEncoding()
	{
		return _characterEncoding;
	}

	@Override
	public int getContentLength()
	{
		return (int)_connection._requestFields.getLongField(HttpHeaders.CONTENT_LENGTH_BUFFER);
	}

	@Override
	public String getContentType()
	{
		return _connection._requestFields.getStringField(HttpHeaders.CONTENT_TYPE_BUFFER);
	}

	@Override
	public String getContextPath()
	{
		return _contextPath;
	}

	@Override
	public Cookie[] getCookies()
	{
		if (_cookiesExtracted)
			return _cookies == null?null:_cookies.getCookies();

		_cookiesExtracted = true;

		Enumeration enm = _connection._requestFields.getValues(HttpHeaders.COOKIE_BUFFER);

		// Handle no cookies
		if (enm != null)
		{
			if (_cookies == null)
				_cookies = new CookieCutter();

			while (enm.hasMoreElements())
			{
				String c = (String)enm.nextElement();
				_cookies.addCookieField(c);
			}
		}

		return _cookies == null?null:_cookies.getCookies();
	}

	@Override
	public long getDateHeader(String name)
	{
		return _connection._requestFields.getDateField(name);
	}

	@Override
	public DispatcherType getDispatcherType()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public String getHeader(String name)
	{
		return _connection._requestFields.getStringField(name);
	}

	@Override
	public Enumeration getHeaderNames()
	{
		return _connection._requestFields.getFieldNames();
	}

	@Override
	public Enumeration getHeaders(String name)
	{
		Enumeration e = _connection._requestFields.getValues(name);
		if (e == null)
			return Collections.enumeration(Collections.EMPTY_LIST);
		return e;
	}

	@Override
	public ServletInputStream getInputStream() throws IOException
	{
		_inputState = _STREAM;
		return _connection.getInputStream();
	}

	@Override
	public int getIntHeader(String name)
	{
		return (int)_connection._requestFields.getLongField(name);
	}

	@Override
	public String getLocalAddr()
	{
		return _endp.getLocalAddr();
	}

	@Override
	public Locale getLocale()
	{
		Enumeration enm = _connection._requestFields.getValues( "Accept-Language", ", \t" );

		// handle no locale
		if (enm == null || !enm.hasMoreElements())
			return Locale.getDefault();

		// sort the list in quality order
		List acceptLanguage = HttpFields.qualityList(enm);
		if (acceptLanguage.size() == 0)
			return Locale.getDefault();

		int size = acceptLanguage.size();

		if (size > 0)
		{
			String language = (String)acceptLanguage.get(0);
			language = HttpFields.valueParameters(language,null);
			String country = "";
			int dash = language.indexOf('-');
			if (dash > -1)
			{
				country = language.substring(dash + 1).trim();
				language = language.substring(0,dash).trim();
			}
			return new Locale(language,country);
		}

		return Locale.getDefault();
	}

	@Override
	public Enumeration getLocales()
	{

		Enumeration enm = _connection._requestFields.getValues( "Accept-Language", ", \t" );

		// handle no locale
		if (enm == null || !enm.hasMoreElements())
			return Collections.enumeration(__defaultLocale);

		// sort the list in quality order
		List acceptLanguage = HttpFields.qualityList(enm);

		if (acceptLanguage.size() == 0)
			return Collections.enumeration(__defaultLocale);

		Object langs = null;
		int size = acceptLanguage.size();

		// convert to locals
		for (int i = 0; i < size; i++)
		{
			String language = (String)acceptLanguage.get(i);
			language = HttpFields.valueParameters(language,null);
			String country = "";
			int dash = language.indexOf('-');
			if (dash > -1)
			{
				country = language.substring(dash + 1).trim();
				language = language.substring(0,dash).trim();
			}
			langs = LazyList.ensureSize(langs,size);
			langs = LazyList.add(langs,new Locale(language,country));
		}

		if (LazyList.size(langs) == 0)
			return Collections.enumeration(__defaultLocale);

		return Collections.enumeration(LazyList.getList(langs));
	}

	@Override
	public String getLocalName()
	{
		String local = _endp.getLocalAddr();
		if (local != null && local.indexOf(':') >= 0)
			local = "[" + local + "]";
		return local;
	}

	@Override
	public int getLocalPort()
	{
		return _endp.getLocalPort();
	}

	@Override
	public String getMethod()
	{
		return _method;
	}

	@Override
	public String getParameter(String name)
	{
		if (!_paramsExtracted)
			extractParameters();
		return (String)_parameters.getValue(name,0);
	}

	@Override
	public Map<String,String[]> getParameterMap()
	{
		if (!_paramsExtracted)
			extractParameters();

		return Collections.unmodifiableMap(_parameters.toStringArrayMap());
	}

	@Override
	public Enumeration getParameterNames()
	{
		if (!_paramsExtracted)
			extractParameters();
		return Collections.enumeration(_parameters.keySet());
	}

	@Override
	public String[] getParameterValues(String name)
	{
		if (!_paramsExtracted)
			extractParameters();
		List<Object> vals = _parameters.getValues(name);
		if (vals == null)
			return null;
		return vals.toArray(new String[vals.size()]);
	}

	@Override
	public String getPathInfo()
	{
		return _pathInfo;
	}

	@Override
	public String getPathTranslated()
	{
		return null;
	}

	@Override
	public String getProtocol()
	{
		return _protocol;
	}

	@Override
	public String getQueryString()
	{
		if (_queryString == null && _uri != null)
		{
			_queryString = _uri.getQuery();
		}
		return _queryString;
	}

	@Override
	public BufferedReader getReader() throws IOException
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public String getRealPath(String path)
	{
		return null;
	}

	@Override
	public String getRemoteAddr()
	{
		return _endp.getRemoteAddr();
	}

	@Override
	public String getRemoteHost()
	{
		return getRemoteAddr();
	}

	@Override
	public int getRemotePort()
	{
		return _endp.getRemotePort();
	}

	@Override
	public String getRemoteUser()
	{
		return null;
	}

	@Override
	public RequestDispatcher getRequestDispatcher(String path)
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public String getRequestedSessionId()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public String getRequestURI()
	{
		if (_requestURI == null && _uri != null)
			_requestURI = _uri.getPathAndParam();
		return _requestURI;
	}

	@Override
	public StringBuffer getRequestURL()
	{
		final StringBuffer url = new StringBuffer(48);
		synchronized (url)
		{
			String scheme = getScheme();
			int port = getServerPort();

			url.append(scheme);
			url.append("://");
			url.append(getServerName());
			if (_port > 0 && ((scheme.equalsIgnoreCase(URIUtil.HTTP) && port != 80) || (scheme.equalsIgnoreCase(URIUtil.HTTPS) && port != 443)))
			{
				url.append(':');
				url.append(_port);
			}

			url.append(getRequestURI());
			return url;
		}
	}

	/* ------------------------------------------------------------ */
	/**
	 * 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 StringBuilder getRootURL()
	{
		StringBuilder url = new StringBuilder(48);
		String scheme = getScheme();
		int port = getServerPort();

		url.append(scheme);
		url.append("://");
		url.append(getServerName());

		if (port > 0 && ((scheme.equalsIgnoreCase("http") && port != 80) || (scheme.equalsIgnoreCase("https") && port != 443)))
		{
			url.append(':');
			url.append(port);
		}
		return url;
	}

	@Override
	public String getScheme()
	{
		return _scheme;
	}

	@Override
	public String getServerName()
	{
		// Return already determined host
		if (_serverName != null)
			return _serverName;

		if (_uri == null)
			throw new IllegalStateException("No uri");

		// Return host from absolute URI
		_serverName = _uri.getHost();
		_port = _uri.getPort();
		if (_serverName != null)
			return _serverName;

		// Return host from header field
		Buffer hostPort = _connection._requestFields.get(HttpHeaders.HOST_BUFFER);
		if (hostPort != null)
		{
			loop: for (int i = hostPort.putIndex(); i-- > hostPort.getIndex();)
			{
				char ch = (char)(0xff & hostPort.peek(i));
				switch (ch)
				{
					case ']':
						break loop;

					case ':':
						_serverName = BufferUtil.to8859_1_String(hostPort.peek(hostPort.getIndex(),i - hostPort.getIndex()));
						try
						{
							_port = BufferUtil.toInt(hostPort.peek(i + 1,hostPort.putIndex() - i - 1));
						}
						catch (NumberFormatException e)
						{
							try
							{
								_connection._generator.sendError(HttpStatus.BAD_REQUEST_400,"Bad Host header",null,true);
							}
							catch (IOException e1)
							{
								throw new RuntimeException(e1);
							}
						}
						return _serverName;
				}
			}
			if (_serverName == null || _port < 0)
			{
				_serverName = BufferUtil.to8859_1_String(hostPort);
				_port = 0;
			}

			return _serverName;
		}

		// Return host from connection
		_serverName = getLocalName();
		_port = getLocalPort();
		if (_serverName != null && !StringUtil.ALL_INTERFACES.equals(_serverName))
			return _serverName;

		// Return the local host
		try
		{
			_serverName = InetAddress.getLocalHost().getHostAddress();
		}
		catch (java.net.UnknownHostException e)
		{
			LOG.trace("",e);
		}
		return _serverName;
	}

	@Override
	public int getServerPort()
	{
		if (_port <= 0)
		{
			if (_serverName == null)
				getServerName();

			if (_port <= 0)
			{
				if (_serverName != null && _uri != null)
					_port = _uri.getPort();
				else
					_port = _endp.getLocalPort();
			}
		}

		if (_port <= 0)
		{
			if (getScheme().equalsIgnoreCase(URIUtil.HTTPS))
				return 443;
			return 80;
		}
		return _port;
	}

	@Override
	public ServletContext getServletContext()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public String getServletPath()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public HttpSession getSession()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public HttpSession getSession(boolean create)
	{
		throw new UnsupportedOperationException();
	}

	/* ------------------------------------------------------------ */
	/**
	 * Get Request TimeStamp
	 *
	 * @return The time that the request was received.
	 */
	public long getTimeStamp()
	{
		return _timeStamp;
	}

	/* ------------------------------------------------------------ */
	/**
	 * @return Returns the uri.
	 */
	public HttpURI getUri()
	{
		return _uri;
	}

	@Override
	public Principal getUserPrincipal()
	{
		return null;
	}

	public boolean isHandled()
	{
		return _handled;
	}

	@Override
	public boolean isAsyncStarted()
	{
	   return false;
	}

	@Override
	public boolean isAsyncSupported()
	{
		return false;
	}

	@Override
	public boolean isRequestedSessionIdFromCookie()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean isRequestedSessionIdFromUrl()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean isRequestedSessionIdFromURL()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean isRequestedSessionIdValid()
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public boolean isSecure()
	{
		return _connection.getConnector().isConfidential();
	}

	@Override
	public boolean isUserInRole(String role)
	{
		return false;
	}

	protected void recycle()
	{
		_handled = false;
		if (_contextHandler != null)
			throw new IllegalStateException("Request in context!");
		if (_attributes != null)
			_attributes.clearAttributes();
		_characterEncoding = null;
		_contextPath = null;
		if (_cookies != null)
			_cookies.reset();
		_cookiesExtracted = false;
		_serverName = null;
		_method = null;
		_pathInfo = null;
		_port = 0;
		_protocol = HttpVersions.HTTP_1_1;
		_queryString = null;
		_requestURI = null;
		_scheme = URIUtil.HTTP;
		_timeStamp = 0;
		_uri = null;
		if (_baseParameters != null)
			_baseParameters.clear();
		_parameters = null;
		_paramsExtracted = false;
		_inputState = __NONE;
	}

	@Override
	public void removeAttribute(String name)
	{
		if (_attributes != null)
			_attributes.removeAttribute(name);
	}

	/* ------------------------------------------------------------ */
	/*
	 * Set a request attribute. if the attribute name is "org.eclipse.jetty.server.server.Request.queryEncoding" then the value is also passed in a call to
	 * {@link #setQueryEncoding}. <p> if the attribute name is "org.eclipse.jetty.server.server.ResponseBuffer", then the response buffer is flushed with @{link
	 * #flushResponseBuffer}.
	 *
	 * @see javax.servlet.ServletRequest#setAttribute(java.lang.String, java.lang.Object)
	 */
	@Override
	public void setAttribute(String name, Object value)
	{
		if (_attributes == null)
			_attributes = new AttributesMap();
		_attributes.setAttribute(name,value);
	}

	@Override
	public void setCharacterEncoding(String encoding) throws UnsupportedEncodingException
	{
		if (_inputState != __NONE)
			return;

		_characterEncoding = encoding;

		// check encoding is supported
		if (!StringUtil.isUTF8(encoding))
			// noinspection ResultOfMethodCallIgnored
			"".getBytes(encoding);
	}

	public void setCharacterEncodingUnchecked(String encoding)
	{
		_characterEncoding = encoding;
	}

	public void setContextPath(String contextPath)
	{
		_contextPath = contextPath;
	}

/*
	public void setCookies(Cookie[] cookies)
	{
		if (_cookies == null)
			_cookies = new CookieCutter();
		_cookies.setCookies(cookies);
	}
*/

	public void setHandled(boolean h)
	{
		_handled = h;
	}

	public void setMethod(String method)
	{
		_method = method;
	}

	private void setParameters(MultiMap<String> parameters)
	{
		_parameters = (parameters == null)?_baseParameters:parameters;
		if (_paramsExtracted && _parameters == null)
			throw new IllegalStateException();
	}

	public void setPathInfo(String pathInfo)
	{
		_pathInfo = pathInfo;
	}

	public void setProtocol(String protocol)
	{
		_protocol = protocol;
	}

	public void setRequestURI(String requestURI)
	{
		_requestURI = requestURI;
	}

	public void setScheme(String scheme)
	{
		_scheme = scheme;
	}

	public void setTimeStamp(long ts)
	{
		_timeStamp = ts;
	}

	public void setUri(HttpURI uri)
	{
		_uri = uri;
	}

	@Override
	public AsyncContext startAsync() throws IllegalStateException
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) throws IllegalStateException
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public String toString()
	{
		return (_handled?"[":"(") + getMethod() + " " + _uri + (_handled?"]@":")@") + hashCode() + " " + super.toString();
	}

	@Override
	public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public Part getPart(String name) throws IOException, ServletException
	{                
		throw new UnsupportedOperationException();
	}

	@Override
	public Collection<Part> getParts() throws IOException, ServletException
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public void login(String username, String password) throws ServletException
	{
		throw new UnsupportedOperationException();
	}

	@Override
	public void logout() throws ServletException
	{
		throw new UnsupportedOperationException();
	}

}