Mercurial Hosting > luan
view src/org/eclipse/jetty/server/Request.java @ 1007:945568ee77ac
remove HttpBuffersImpl
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Sun, 23 Oct 2016 17:40:20 -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(); } }