/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.catalina.connector;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.naming.NamingException;
import javax.security.auth.Subject;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.FilterChain;
import javax.servlet.MultipartConfigElement;
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.ServletResponse;
import javax.servlet.SessionTrackingMode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpUpgradeHandler;
import javax.servlet.http.Part;
import javax.servlet.http.PushBuilder;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
import org.apache.catalina.Manager;
import org.apache.catalina.Realm;
import org.apache.catalina.Session;
import org.apache.catalina.TomcatPrincipal;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ApplicationFilterChain;
import org.apache.catalina.core.ApplicationMapping;
import org.apache.catalina.core.ApplicationPart;
import org.apache.catalina.core.ApplicationPushBuilder;
import org.apache.catalina.core.ApplicationSessionCookieConfig;
import org.apache.catalina.core.AsyncContextImpl;
import org.apache.catalina.mapper.MappingData;
import org.apache.catalina.util.ParameterMap;
import org.apache.catalina.util.RequestUtil;
import org.apache.catalina.util.TLSUtil;
import org.apache.catalina.util.URLEncoder;
import org.apache.coyote.ActionCode;
import org.apache.coyote.UpgradeToken;
import org.apache.coyote.http11.upgrade.InternalHttpUpgradeHandler;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.InstanceManager;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.buf.StringUtils;
import org.apache.tomcat.util.buf.UDecoder;
import org.apache.tomcat.util.http.CookieProcessor;
import org.apache.tomcat.util.http.FastHttpDateFormat;
import org.apache.tomcat.util.http.Parameters;
import org.apache.tomcat.util.http.Parameters.FailReason;
import org.apache.tomcat.util.http.ServerCookie;
import org.apache.tomcat.util.http.ServerCookies;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileUploadBase;
import org.apache.tomcat.util.http.fileupload.FileUploadBase.InvalidContentTypeException;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.fileupload.servlet.ServletRequestContext;
import org.apache.tomcat.util.http.parser.AcceptLanguage;
import org.apache.tomcat.util.net.SSLSupport;
import org.apache.tomcat.util.res.StringManager;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;

Wrapper object for the Coyote request.
Author:Remy Maucherat, Craig R. McClanahan
/** * Wrapper object for the Coyote request. * * @author Remy Maucherat * @author Craig R. McClanahan */
public class Request implements HttpServletRequest { private static final Log log = LogFactory.getLog(Request.class);
Create a new Request object associated with the given Connector.
Params:
  • connector – The Connector with which this Request object will always be associated. In normal usage this must be non-null. In some test scenarios, it may be possible to use a null Connector without triggering an NPE.
/** * Create a new Request object associated with the given Connector. * * @param connector The Connector with which this Request object will always * be associated. In normal usage this must be non-null. In * some test scenarios, it may be possible to use a null * Connector without triggering an NPE. */
public Request(Connector connector) { this.connector = connector; formats = new SimpleDateFormat[formatsTemplate.length]; for(int i = 0; i < formats.length; i++) { formats[i] = (SimpleDateFormat) formatsTemplate[i].clone(); } } // ------------------------------------------------------------- Properties
Coyote request.
/** * Coyote request. */
protected org.apache.coyote.Request coyoteRequest;
Set the Coyote request.
Params:
  • coyoteRequest – The Coyote request
/** * Set the Coyote request. * * @param coyoteRequest The Coyote request */
public void setCoyoteRequest(org.apache.coyote.Request coyoteRequest) { this.coyoteRequest = coyoteRequest; inputBuffer.setRequest(coyoteRequest); }
Get the Coyote request.
Returns:the Coyote request object
/** * Get the Coyote request. * * @return the Coyote request object */
public org.apache.coyote.Request getCoyoteRequest() { return this.coyoteRequest; } // ----------------------------------------------------- Variables
Deprecated:Unused. This will be removed in Tomcat 10.
/** * @deprecated Unused. This will be removed in Tomcat 10. */
@Deprecated protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
The string manager for this package.
/** * The string manager for this package. */
protected static final StringManager sm = StringManager.getManager(Request.class);
The set of cookies associated with this Request.
/** * The set of cookies associated with this Request. */
protected Cookie[] cookies = null;
The set of SimpleDateFormat formats to use in getDateHeader(). Notice that because SimpleDateFormat is not thread-safe, we can't declare formats[] as a static variable.
Deprecated:Unused. This will be removed in Tomcat 10
/** * The set of SimpleDateFormat formats to use in getDateHeader(). * * Notice that because SimpleDateFormat is not thread-safe, we can't * declare formats[] as a static variable. * * @deprecated Unused. This will be removed in Tomcat 10 */
@Deprecated protected final SimpleDateFormat formats[]; @Deprecated private static final SimpleDateFormat formatsTemplate[] = { new SimpleDateFormat(FastHttpDateFormat.RFC1123_DATE, Locale.US), new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US), new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US) };
The default Locale if none are specified.
/** * The default Locale if none are specified. */
protected static final Locale defaultLocale = Locale.getDefault();
The attributes associated with this Request, keyed by attribute name.
/** * The attributes associated with this Request, keyed by attribute name. */
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
Flag that indicates if SSL attributes have been parsed to improve performance for applications (usually frameworks) that make multiple calls to getAttributeNames().
/** * Flag that indicates if SSL attributes have been parsed to improve * performance for applications (usually frameworks) that make multiple * calls to {@link Request#getAttributeNames()}. */
protected boolean sslAttributesParsed = false;
The preferred Locales associated with this Request.
/** * The preferred Locales associated with this Request. */
protected final ArrayList<Locale> locales = new ArrayList<>();
Internal notes associated with this request by Catalina components and event listeners.
/** * Internal notes associated with this request by Catalina components * and event listeners. */
private final transient HashMap<String, Object> notes = new HashMap<>();
Authentication type.
/** * Authentication type. */
protected String authType = null;
The current dispatcher type.
/** * The current dispatcher type. */
protected DispatcherType internalDispatcherType = null;
The associated input buffer.
/** * The associated input buffer. */
protected final InputBuffer inputBuffer = new InputBuffer();
ServletInputStream.
/** * ServletInputStream. */
protected CoyoteInputStream inputStream = new CoyoteInputStream(inputBuffer);
Reader.
/** * Reader. */
protected CoyoteReader reader = new CoyoteReader(inputBuffer);
Using stream flag.
/** * Using stream flag. */
protected boolean usingInputStream = false;
Using reader flag.
/** * Using reader flag. */
protected boolean usingReader = false;
User principal.
/** * User principal. */
protected Principal userPrincipal = null;
Request parameters parsed flag.
/** * Request parameters parsed flag. */
protected boolean parametersParsed = false;
Cookie headers parsed flag. Indicates that the cookie headers have been parsed into ServerCookies.
/** * Cookie headers parsed flag. Indicates that the cookie headers have been * parsed into ServerCookies. */
protected boolean cookiesParsed = false;
Cookie parsed flag. Indicates that the ServerCookies have been converted into user facing Cookie objects.
/** * Cookie parsed flag. Indicates that the ServerCookies have been converted * into user facing Cookie objects. */
protected boolean cookiesConverted = false;
Secure flag.
/** * Secure flag. */
protected boolean secure = false;
The Subject associated with the current AccessControlContext
/** * The Subject associated with the current AccessControlContext */
protected transient Subject subject = null;
Post data buffer.
/** * Post data buffer. */
protected static final int CACHED_POST_LEN = 8192; protected byte[] postData = null;
Hash map used in the getParametersMap method.
/** * Hash map used in the getParametersMap method. */
protected ParameterMap<String, String[]> parameterMap = new ParameterMap<>();
The parts, if any, uploaded with this request.
/** * The parts, if any, uploaded with this request. */
protected Collection<Part> parts = null;
The exception thrown, if any when parsing the parts.
/** * The exception thrown, if any when parsing the parts. */
protected Exception partsParseException = null;
The currently active session for this request.
/** * The currently active session for this request. */
protected Session session = null;
The current request dispatcher path.
/** * The current request dispatcher path. */
protected Object requestDispatcherPath = null;
Was the requested session ID received in a cookie?
/** * Was the requested session ID received in a cookie? */
protected boolean requestedSessionCookie = false;
The requested session ID (if any) for this request.
/** * The requested session ID (if any) for this request. */
protected String requestedSessionId = null;
Was the requested session ID received in a URL?
/** * Was the requested session ID received in a URL? */
protected boolean requestedSessionURL = false;
Was the requested session ID obtained from the SSL session?
/** * Was the requested session ID obtained from the SSL session? */
protected boolean requestedSessionSSL = false;
Parse locales.
/** * Parse locales. */
protected boolean localesParsed = false;
Local port
/** * Local port */
protected int localPort = -1;
Remote address.
/** * Remote address. */
protected String remoteAddr = null;
Remote host.
/** * Remote host. */
protected String remoteHost = null;
Remote port
/** * Remote port */
protected int remotePort = -1;
Local address
/** * Local address */
protected String localAddr = null;
Local address
/** * Local address */
protected String localName = null;
AsyncContext
/** * AsyncContext */
private volatile AsyncContextImpl asyncContext = null; protected Boolean asyncSupported = null; private HttpServletRequest applicationRequest = null; // --------------------------------------------------------- Public Methods protected void addPathParameter(String name, String value) { coyoteRequest.addPathParameter(name, value); } protected String getPathParameter(String name) { return coyoteRequest.getPathParameter(name); } public void setAsyncSupported(boolean asyncSupported) { this.asyncSupported = Boolean.valueOf(asyncSupported); }
Release all object references, and initialize instance variables, in preparation for reuse of this object.
/** * Release all object references, and initialize instance variables, in * preparation for reuse of this object. */
public void recycle() { internalDispatcherType = null; requestDispatcherPath = null; authType = null; inputBuffer.recycle(); usingInputStream = false; usingReader = false; userPrincipal = null; subject = null; parametersParsed = false; if (parts != null) { for (Part part: parts) { try { part.delete(); } catch (IOException ignored) { // ApplicationPart.delete() never throws an IOEx } } parts = null; } partsParseException = null; locales.clear(); localesParsed = false; secure = false; remoteAddr = null; remoteHost = null; remotePort = -1; localPort = -1; localAddr = null; localName = null; attributes.clear(); sslAttributesParsed = false; notes.clear(); recycleSessionInfo(); recycleCookieInfo(false); if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) { parameterMap = new ParameterMap<>(); } else { parameterMap.setLocked(false); parameterMap.clear(); } mappingData.recycle(); applicationMapping.recycle(); applicationRequest = null; if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) { if (facade != null) { facade.clear(); facade = null; } if (inputStream != null) { inputStream.clear(); inputStream = null; } if (reader != null) { reader.clear(); reader = null; } } asyncSupported = null; if (asyncContext!=null) { asyncContext.recycle(); } asyncContext = null; } protected void recycleSessionInfo() { if (session != null) { try { session.endAccess(); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); log.warn(sm.getString("coyoteRequest.sessionEndAccessFail"), t); } } session = null; requestedSessionCookie = false; requestedSessionId = null; requestedSessionURL = false; requestedSessionSSL = false; } protected void recycleCookieInfo(boolean recycleCoyote) { cookiesParsed = false; cookiesConverted = false; cookies = null; if (recycleCoyote) { getCoyoteRequest().getCookies().recycle(); } } // -------------------------------------------------------- Request Methods
Associated Catalina connector.
/** * Associated Catalina connector. */
protected final Connector connector;
Returns:the Connector through which this Request was received.
/** * @return the Connector through which this Request was received. */
public Connector getConnector() { return this.connector; }
Return the Context within which this Request is being processed.

This is available as soon as the appropriate Context is identified. Note that availability of a Context allows getContextPath() to return a value, and thus enables parsing of the request URI.

Returns:the Context mapped with the request
/** * Return the Context within which this Request is being processed. * <p> * This is available as soon as the appropriate Context is identified. * Note that availability of a Context allows <code>getContextPath()</code> * to return a value, and thus enables parsing of the request URI. * * @return the Context mapped with the request */
public Context getContext() { return mappingData.context; }
Filter chain associated with the request.
/** * Filter chain associated with the request. */
protected FilterChain filterChain = null;
Get filter chain associated with the request.
Returns:the associated filter chain
/** * Get filter chain associated with the request. * * @return the associated filter chain */
public FilterChain getFilterChain() { return this.filterChain; }
Set filter chain associated with the request.
Params:
  • filterChain – new filter chain
/** * Set filter chain associated with the request. * * @param filterChain new filter chain */
public void setFilterChain(FilterChain filterChain) { this.filterChain = filterChain; }
Returns:the Host within which this Request is being processed.
/** * @return the Host within which this Request is being processed. */
public Host getHost() { return mappingData.host; }
Mapping data.
/** * Mapping data. */
protected final MappingData mappingData = new MappingData(); private final ApplicationMapping applicationMapping = new ApplicationMapping(mappingData);
Returns:mapping data.
/** * @return mapping data. */
public MappingData getMappingData() { return mappingData; }
The facade associated with this request.
/** * The facade associated with this request. */
protected RequestFacade facade = null;
Returns:the ServletRequest for which this object is the facade. This method must be implemented by a subclass.
/** * @return the <code>ServletRequest</code> for which this object * is the facade. This method must be implemented by a subclass. */
public HttpServletRequest getRequest() { if (facade == null) { facade = new RequestFacade(this); } if (applicationRequest == null) { applicationRequest = facade; } return applicationRequest; }
Set a wrapped HttpServletRequest to pass to the application. Components wishing to wrap the request should obtain the request via getRequest(), wrap it and then call this method with the wrapped request.
Params:
  • applicationRequest – The wrapped request to pass to the application
/** * Set a wrapped HttpServletRequest to pass to the application. Components * wishing to wrap the request should obtain the request via * {@link #getRequest()}, wrap it and then call this method with the * wrapped request. * * @param applicationRequest The wrapped request to pass to the application */
public void setRequest(HttpServletRequest applicationRequest) { // Check the wrapper wraps this request ServletRequest r = applicationRequest; while (r instanceof HttpServletRequestWrapper) { r = ((HttpServletRequestWrapper) r).getRequest(); } if (r != facade) { throw new IllegalArgumentException(sm.getString("request.illegalWrap")); } this.applicationRequest = applicationRequest; }
The response with which this request is associated.
/** * The response with which this request is associated. */
protected org.apache.catalina.connector.Response response = null;
Returns:the Response with which this Request is associated.
/** * @return the Response with which this Request is associated. */
public org.apache.catalina.connector.Response getResponse() { return this.response; }
Set the Response with which this Request is associated.
Params:
  • response – The new associated response
/** * Set the Response with which this Request is associated. * * @param response The new associated response */
public void setResponse(org.apache.catalina.connector.Response response) { this.response = response; }
Returns:the input stream associated with this Request.
/** * @return the input stream associated with this Request. */
public InputStream getStream() { if (inputStream == null) { inputStream = new CoyoteInputStream(inputBuffer); } return inputStream; }
URI byte to char converter.
/** * URI byte to char converter. */
protected B2CConverter URIConverter = null;
Returns:the URI converter.
/** * @return the URI converter. */
protected B2CConverter getURIConverter() { return URIConverter; }
Set the URI converter.
Params:
  • URIConverter – the new URI converter
/** * Set the URI converter. * * @param URIConverter the new URI converter */
protected void setURIConverter(B2CConverter URIConverter) { this.URIConverter = URIConverter; }
Returns:the Wrapper within which this Request is being processed.
/** * @return the Wrapper within which this Request is being processed. */
public Wrapper getWrapper() { return mappingData.wrapper; } // ------------------------------------------------- Request Public Methods
Create and return a ServletInputStream to read the content associated with this Request.
Throws:
Returns:the created input stream
/** * Create and return a ServletInputStream to read the content * associated with this Request. * * @return the created input stream * @exception IOException if an input/output error occurs */
public ServletInputStream createInputStream() throws IOException { if (inputStream == null) { inputStream = new CoyoteInputStream(inputBuffer); } return inputStream; }
Perform whatever actions are required to flush and close the input stream or reader, in a single operation.
Throws:
  • IOException – if an input/output error occurs
/** * Perform whatever actions are required to flush and close the input * stream or reader, in a single operation. * * @exception IOException if an input/output error occurs */
public void finishRequest() throws IOException { if (response.getStatus() == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE) { checkSwallowInput(); } }
Params:
  • name – Name of the note to be returned
Returns:the object bound with the specified name to the internal notes for this request, or null if no such binding exists.
/** * @return the object bound with the specified name to the internal notes * for this request, or <code>null</code> if no such binding exists. * * @param name Name of the note to be returned */
public Object getNote(String name) { return notes.get(name); }
Remove any object bound to the specified name in the internal notes for this request.
Params:
  • name – Name of the note to be removed
/** * Remove any object bound to the specified name in the internal notes * for this request. * * @param name Name of the note to be removed */
public void removeNote(String name) { notes.remove(name); }
Set the port number of the server to process this request.
Params:
  • port – The server port
/** * Set the port number of the server to process this request. * * @param port The server port */
public void setLocalPort(int port) { localPort = port; }
Bind an object to a specified name in the internal notes associated with this request, replacing any existing binding for this name.
Params:
  • name – Name to which the object should be bound
  • value – Object to be bound to the specified name
/** * Bind an object to a specified name in the internal notes associated * with this request, replacing any existing binding for this name. * * @param name Name to which the object should be bound * @param value Object to be bound to the specified name */
public void setNote(String name, Object value) { notes.put(name, value); }
Set the IP address of the remote client associated with this Request.
Params:
  • remoteAddr – The remote IP address
/** * Set the IP address of the remote client associated with this Request. * * @param remoteAddr The remote IP address */
public void setRemoteAddr(String remoteAddr) { this.remoteAddr = remoteAddr; }
Set the fully qualified name of the remote client associated with this Request.
Params:
  • remoteHost – The remote host name
/** * Set the fully qualified name of the remote client associated with this * Request. * * @param remoteHost The remote host name */
public void setRemoteHost(String remoteHost) { this.remoteHost = remoteHost; }
Set the value to be returned by isSecure() for this Request.
Params:
  • secure – The new isSecure value
/** * Set the value to be returned by <code>isSecure()</code> * for this Request. * * @param secure The new isSecure value */
public void setSecure(boolean secure) { this.secure = secure; }
Set the port number of the server to process this request.
Params:
  • port – The server port
/** * Set the port number of the server to process this request. * * @param port The server port */
public void setServerPort(int port) { coyoteRequest.setServerPort(port); } // ------------------------------------------------- ServletRequest Methods
Params:
  • name – Name of the request attribute to return
Returns:the specified request attribute if it exists; otherwise, return null.
/** * @return the specified request attribute if it exists; otherwise, return * <code>null</code>. * * @param name Name of the request attribute to return */
@Override public Object getAttribute(String name) { // Special attributes SpecialAttributeAdapter adapter = specialAttributes.get(name); if (adapter != null) { return adapter.get(this, name); } Object attr = attributes.get(name); if (attr != null) { return attr; } attr = coyoteRequest.getAttribute(name); if (attr != null) { return attr; } if (TLSUtil.isTLSRequestAttribute(name)) { coyoteRequest.action(ActionCode.REQ_SSL_ATTRIBUTE, coyoteRequest); attr = coyoteRequest.getAttribute(Globals.CERTIFICATES_ATTR); if (attr != null) { attributes.put(Globals.CERTIFICATES_ATTR, attr); } attr = coyoteRequest.getAttribute(Globals.CIPHER_SUITE_ATTR); if (attr != null) { attributes.put(Globals.CIPHER_SUITE_ATTR, attr); } attr = coyoteRequest.getAttribute(Globals.KEY_SIZE_ATTR); if (attr != null) { attributes.put(Globals.KEY_SIZE_ATTR, attr); } attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_ID_ATTR); if (attr != null) { attributes.put(Globals.SSL_SESSION_ID_ATTR, attr); } attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_MGR_ATTR); if (attr != null) { attributes.put(Globals.SSL_SESSION_MGR_ATTR, attr); } attr = coyoteRequest.getAttribute(SSLSupport.PROTOCOL_VERSION_KEY); if (attr != null) { attributes.put(SSLSupport.PROTOCOL_VERSION_KEY, attr); } attr = attributes.get(name); sslAttributesParsed = true; } return attr; } @Override public long getContentLengthLong() { return coyoteRequest.getContentLengthLong(); }
Return the names of all request attributes for this Request, or an empty Enumeration if there are none. Note that the attribute names returned will only be those for the attributes set via setAttribute(String, Object). Tomcat internal attributes will not be included although they are accessible via getAttribute(String). The Tomcat internal attributes include: The underlying connector may also expose request attributes. These all have names starting with "org.apache.tomcat" and include: Connector implementations may return some, all or none of these attributes and may also support additional attributes.
Returns:the attribute names enumeration
/** * Return the names of all request attributes for this Request, or an * empty <code>Enumeration</code> if there are none. Note that the attribute * names returned will only be those for the attributes set via * {@link #setAttribute(String, Object)}. Tomcat internal attributes will * not be included although they are accessible via * {@link #getAttribute(String)}. The Tomcat internal attributes include: * <ul> * <li>{@link Globals#DISPATCHER_TYPE_ATTR}</li> * <li>{@link Globals#DISPATCHER_REQUEST_PATH_ATTR}</li> * <li>{@link Globals#ASYNC_SUPPORTED_ATTR}</li> * <li>{@link Globals#CERTIFICATES_ATTR} (SSL connections only)</li> * <li>{@link Globals#CIPHER_SUITE_ATTR} (SSL connections only)</li> * <li>{@link Globals#KEY_SIZE_ATTR} (SSL connections only)</li> * <li>{@link Globals#SSL_SESSION_ID_ATTR} (SSL connections only)</li> * <li>{@link Globals#SSL_SESSION_MGR_ATTR} (SSL connections only)</li> * <li>{@link Globals#PARAMETER_PARSE_FAILED_ATTR}</li> * </ul> * The underlying connector may also expose request attributes. These all * have names starting with "org.apache.tomcat" and include: * <ul> * <li>{@link Globals#SENDFILE_SUPPORTED_ATTR}</li> * </ul> * Connector implementations may return some, all or none of these * attributes and may also support additional attributes. * * @return the attribute names enumeration */
@Override public Enumeration<String> getAttributeNames() { if (isSecure() && !sslAttributesParsed) { getAttribute(Globals.CERTIFICATES_ATTR); } // Take a copy to prevent ConcurrentModificationExceptions if used to // remove attributes Set<String> names = new HashSet<>(); names.addAll(attributes.keySet()); return Collections.enumeration(names); }
Returns:the character encoding for this Request.
/** * @return the character encoding for this Request. */
@Override public String getCharacterEncoding() { String characterEncoding = coyoteRequest.getCharacterEncoding(); if (characterEncoding != null) { return characterEncoding; } Context context = getContext(); if (context != null) { return context.getRequestCharacterEncoding(); } return null; } private Charset getCharset() { Charset charset = null; try { charset = coyoteRequest.getCharset(); } catch (UnsupportedEncodingException e) { // Ignore } if (charset != null) { return charset; } Context context = getContext(); if (context != null) { String encoding = context.getRequestCharacterEncoding(); if (encoding != null) { try { return B2CConverter.getCharset(encoding); } catch (UnsupportedEncodingException e) { // Ignore } } } return org.apache.coyote.Constants.DEFAULT_BODY_CHARSET; }
Returns:the content length for this Request.
/** * @return the content length for this Request. */
@Override public int getContentLength() { return coyoteRequest.getContentLength(); }
Returns:the content type for this Request.
/** * @return the content type for this Request. */
@Override public String getContentType() { return coyoteRequest.getContentType(); }
Set the content type for this Request.
Params:
  • contentType – The content type
/** * Set the content type for this Request. * * @param contentType The content type */
public void setContentType(String contentType) { coyoteRequest.setContentType(contentType); }
Throws:
Returns:the servlet input stream for this Request. The default implementation returns a servlet input stream created by createInputStream().
/** * @return the servlet input stream for this Request. The default * implementation returns a servlet input stream created by * <code>createInputStream()</code>. * * @exception IllegalStateException if <code>getReader()</code> has * already been called for this request * @exception IOException if an input/output error occurs */
@Override public ServletInputStream getInputStream() throws IOException { if (usingReader) { throw new IllegalStateException(sm.getString("coyoteRequest.getInputStream.ise")); } usingInputStream = true; if (inputStream == null) { inputStream = new CoyoteInputStream(inputBuffer); } return inputStream; }
Returns:the preferred Locale that the client will accept content in, based on the value for the first Accept-Language header that was encountered. If the request did not specify a preferred language, the server's default Locale is returned.
/** * @return the preferred Locale that the client will accept content in, * based on the value for the first <code>Accept-Language</code> header * that was encountered. If the request did not specify a preferred * language, the server's default Locale is returned. */
@Override public Locale getLocale() { if (!localesParsed) { parseLocales(); } if (locales.size() > 0) { return locales.get(0); } return defaultLocale; }
Returns:the set of preferred Locales that the client will accept content in, based on the values for any Accept-Language headers that were encountered. If the request did not specify a preferred language, the server's default Locale is returned.
/** * @return the set of preferred Locales that the client will accept * content in, based on the values for any <code>Accept-Language</code> * headers that were encountered. If the request did not specify a * preferred language, the server's default Locale is returned. */
@Override public Enumeration<Locale> getLocales() { if (!localesParsed) { parseLocales(); } if (locales.size() > 0) { return Collections.enumeration(locales); } ArrayList<Locale> results = new ArrayList<>(); results.add(defaultLocale); return Collections.enumeration(results); }
Params:
  • name – Name of the desired request parameter
Returns:the value of the specified request parameter, if any; otherwise, return null. If there is more than one value defined, return only the first one.
/** * @return the value of the specified request parameter, if any; otherwise, * return <code>null</code>. If there is more than one value defined, * return only the first one. * * @param name Name of the desired request parameter */
@Override public String getParameter(String name) { if (!parametersParsed) { parseParameters(); } return coyoteRequest.getParameters().getParameter(name); }
Returns a Map of the parameters of this request. Request parameters are extra information sent with the request. For HTTP servlets, parameters are contained in the query string or posted form data.
Returns:A Map containing parameter names as keys and parameter values as map values.
/** * Returns a <code>Map</code> of the parameters of this request. * Request parameters are extra information sent with the request. * For HTTP servlets, parameters are contained in the query string * or posted form data. * * @return A <code>Map</code> containing parameter names as keys * and parameter values as map values. */
@Override public Map<String, String[]> getParameterMap() { if (parameterMap.isLocked()) { return parameterMap; } Enumeration<String> enumeration = getParameterNames(); while (enumeration.hasMoreElements()) { String name = enumeration.nextElement(); String[] values = getParameterValues(name); parameterMap.put(name, values); } parameterMap.setLocked(true); return parameterMap; }
Returns:the names of all defined request parameters for this request.
/** * @return the names of all defined request parameters for this request. */
@Override public Enumeration<String> getParameterNames() { if (!parametersParsed) { parseParameters(); } return coyoteRequest.getParameters().getParameterNames(); }
Params:
  • name – Name of the desired request parameter
Returns:the defined values for the specified request parameter, if any; otherwise, return null.
/** * @return the defined values for the specified request parameter, if any; * otherwise, return <code>null</code>. * * @param name Name of the desired request parameter */
@Override public String[] getParameterValues(String name) { if (!parametersParsed) { parseParameters(); } return coyoteRequest.getParameters().getParameterValues(name); }
Returns:the protocol and version used to make this Request.
/** * @return the protocol and version used to make this Request. */
@Override public String getProtocol() { return coyoteRequest.protocol().toString(); }
Read the Reader wrapping the input stream for this Request. The default implementation wraps a BufferedReader around the servlet input stream returned by createInputStream().
Throws:
Returns:a buffered reader for the request
/** * Read the Reader wrapping the input stream for this Request. The * default implementation wraps a <code>BufferedReader</code> around the * servlet input stream returned by <code>createInputStream()</code>. * * @return a buffered reader for the request * @exception IllegalStateException if <code>getInputStream()</code> * has already been called for this request * @exception IOException if an input/output error occurs */
@Override public BufferedReader getReader() throws IOException { if (usingInputStream) { throw new IllegalStateException(sm.getString("coyoteRequest.getReader.ise")); } // InputBuffer has no easily accessible reference chain to the Context // to check for a default request character encoding at the Context. // Therefore, if a Context default should be used, it is set explicitly // here. Need to do this before setting usingReader. if (coyoteRequest.getCharacterEncoding() == null) { // Nothing currently set explicitly. // Check the content Context context = getContext(); if (context != null) { String enc = context.getRequestCharacterEncoding(); if (enc != null) { // Explicitly set the context default so it is visible to // InputBuffer when creating the Reader. setCharacterEncoding(enc); } } } usingReader = true; inputBuffer.checkConverter(); if (reader == null) { reader = new CoyoteReader(inputBuffer); } return reader; }
Params:
  • path – Path to be translated
Returns:the real path of the specified virtual path.
Deprecated:As of version 2.1 of the Java Servlet API, use ServletContext.getRealPath().
/** * @return the real path of the specified virtual path. * * @param path Path to be translated * * @deprecated As of version 2.1 of the Java Servlet API, use * <code>ServletContext.getRealPath()</code>. */
@Override @Deprecated public String getRealPath(String path) { Context context = getContext(); if (context == null) { return null; } ServletContext servletContext = context.getServletContext(); if (servletContext == null) { return null; } try { return servletContext.getRealPath(path); } catch (IllegalArgumentException e) { return null; } }
Returns:the remote IP address making this Request.
/** * @return the remote IP address making this Request. */
@Override public String getRemoteAddr() { if (remoteAddr == null) { coyoteRequest.action(ActionCode.REQ_HOST_ADDR_ATTRIBUTE, coyoteRequest); remoteAddr = coyoteRequest.remoteAddr().toString(); } return remoteAddr; }
Returns:the remote host name making this Request.
/** * @return the remote host name making this Request. */
@Override public String getRemoteHost() { if (remoteHost == null) { if (!connector.getEnableLookups()) { remoteHost = getRemoteAddr(); } else { coyoteRequest.action(ActionCode.REQ_HOST_ATTRIBUTE, coyoteRequest); remoteHost = coyoteRequest.remoteHost().toString(); } } return remoteHost; }
Returns:the Internet Protocol (IP) source port of the client or last proxy that sent the request.
/** * @return the Internet Protocol (IP) source port of the client * or last proxy that sent the request. */
@Override public int getRemotePort(){ if (remotePort == -1) { coyoteRequest.action(ActionCode.REQ_REMOTEPORT_ATTRIBUTE, coyoteRequest); remotePort = coyoteRequest.getRemotePort(); } return remotePort; }
Returns:the host name of the Internet Protocol (IP) interface on which the request was received.
/** * @return the host name of the Internet Protocol (IP) interface on * which the request was received. */
@Override public String getLocalName(){ if (localName == null) { coyoteRequest.action(ActionCode.REQ_LOCAL_NAME_ATTRIBUTE, coyoteRequest); localName = coyoteRequest.localName().toString(); } return localName; }
Returns:the Internet Protocol (IP) address of the interface on which the request was received.
/** * @return the Internet Protocol (IP) address of the interface on * which the request was received. */
@Override public String getLocalAddr(){ if (localAddr == null) { coyoteRequest.action(ActionCode.REQ_LOCAL_ADDR_ATTRIBUTE, coyoteRequest); localAddr = coyoteRequest.localAddr().toString(); } return localAddr; }
Returns:the Internet Protocol (IP) port number of the interface on which the request was received.
/** * @return the Internet Protocol (IP) port number of the interface * on which the request was received. */
@Override public int getLocalPort(){ if (localPort == -1){ coyoteRequest.action(ActionCode.REQ_LOCALPORT_ATTRIBUTE, coyoteRequest); localPort = coyoteRequest.getLocalPort(); } return localPort; }
Params:
  • path – Path of the resource to be wrapped
Returns:a RequestDispatcher that wraps the resource at the specified path, which may be interpreted as relative to the current request path.
/** * @return a RequestDispatcher that wraps the resource at the specified * path, which may be interpreted as relative to the current request path. * * @param path Path of the resource to be wrapped */
@Override public RequestDispatcher getRequestDispatcher(String path) { Context context = getContext(); if (context == null) { return null; } if (path == null) { return null; } int fragmentPos = path.indexOf('#'); if (fragmentPos > -1) { log.warn(sm.getString("request.fragmentInDispatchPath", path)); path = path.substring(0, fragmentPos); } // If the path is already context-relative, just pass it through if (path.startsWith("/")) { return context.getServletContext().getRequestDispatcher(path); } /* * Relative to what, exactly? * * From the Servlet 4.0 Javadoc: * - The pathname specified may be relative, although it cannot extend * outside the current servlet context. * - If it is relative, it must be relative against the current servlet * * From Section 9.1 of the spec: * - The servlet container uses information in the request object to * transform the given relative path against the current servlet to a * complete path. * * It is undefined whether the requestURI is used or whether servletPath * and pathInfo are used. Given that the RequestURI includes the * contextPath (and extracting that is messy) , using the servletPath and * pathInfo looks to be the more reasonable choice. */ // Convert a request-relative path to a context-relative one String servletPath = (String) getAttribute( RequestDispatcher.INCLUDE_SERVLET_PATH); if (servletPath == null) { servletPath = getServletPath(); } // Add the path info, if there is any String pathInfo = getPathInfo(); String requestPath = null; if (pathInfo == null) { requestPath = servletPath; } else { requestPath = servletPath + pathInfo; } int pos = requestPath.lastIndexOf('/'); String relative = null; if (context.getDispatchersUseEncodedPaths()) { if (pos >= 0) { relative = URLEncoder.DEFAULT.encode( requestPath.substring(0, pos + 1), StandardCharsets.UTF_8) + path; } else { relative = URLEncoder.DEFAULT.encode(requestPath, StandardCharsets.UTF_8) + path; } } else { if (pos >= 0) { relative = requestPath.substring(0, pos + 1) + path; } else { relative = requestPath + path; } } return context.getServletContext().getRequestDispatcher(relative); }
Returns:the scheme used to make this Request.
/** * @return the scheme used to make this Request. */
@Override public String getScheme() { return coyoteRequest.scheme().toString(); }
Returns:the server name responding to this Request.
/** * @return the server name responding to this Request. */
@Override public String getServerName() { return coyoteRequest.serverName().toString(); }
Returns:the server port responding to this Request.
/** * @return the server port responding to this Request. */
@Override public int getServerPort() { return coyoteRequest.getServerPort(); }
Returns:true if this request was received on a secure connection.
/** * @return <code>true</code> if this request was received on a secure connection. */
@Override public boolean isSecure() { return secure; }
Remove the specified request attribute if it exists.
Params:
  • name – Name of the request attribute to remove
/** * Remove the specified request attribute if it exists. * * @param name Name of the request attribute to remove */
@Override public void removeAttribute(String name) { // Remove the specified attribute // Pass special attributes to the native layer if (name.startsWith("org.apache.tomcat.")) { coyoteRequest.getAttributes().remove(name); } boolean found = attributes.containsKey(name); if (found) { Object value = attributes.get(name); attributes.remove(name); // Notify interested application event listeners notifyAttributeRemoved(name, value); } }
Set the specified request attribute to the specified value.
Params:
  • name – Name of the request attribute to set
  • value – The associated value
/** * Set the specified request attribute to the specified value. * * @param name Name of the request attribute to set * @param value The associated value */
@Override public void setAttribute(String name, Object value) { // Name cannot be null if (name == null) { throw new IllegalArgumentException(sm.getString("coyoteRequest.setAttribute.namenull")); } // Null value is the same as removeAttribute() if (value == null) { removeAttribute(name); return; } // Special attributes SpecialAttributeAdapter adapter = specialAttributes.get(name); if (adapter != null) { adapter.set(this, name, value); return; } // Add or replace the specified attribute // Do the security check before any updates are made if (Globals.IS_SECURITY_ENABLED && name.equals(Globals.SENDFILE_FILENAME_ATTR)) { // Use the canonical file name to avoid any possible symlink and // relative path issues String canonicalPath; try { canonicalPath = new File(value.toString()).getCanonicalPath(); } catch (IOException e) { throw new SecurityException(sm.getString( "coyoteRequest.sendfileNotCanonical", value), e); } // Sendfile is performed in Tomcat's security context so need to // check if the web app is permitted to access the file while still // in the web app's security context System.getSecurityManager().checkRead(canonicalPath); // Update the value so the canonical path is used value = canonicalPath; } Object oldValue = attributes.put(name, value); // Pass special attributes to the native layer if (name.startsWith("org.apache.tomcat.")) { coyoteRequest.setAttribute(name, value); } // Notify interested application event listeners notifyAttributeAssigned(name, value, oldValue); }
Notify interested listeners that attribute has been assigned a value.
Params:
  • name – Attribute name
  • value – New attribute value
  • oldValue – Old attribute value
/** * Notify interested listeners that attribute has been assigned a value. * * @param name Attribute name * @param value New attribute value * @param oldValue Old attribute value */
private void notifyAttributeAssigned(String name, Object value, Object oldValue) { Context context = getContext(); if (context == null) { return; } Object listeners[] = context.getApplicationEventListeners(); if ((listeners == null) || (listeners.length == 0)) { return; } boolean replaced = (oldValue != null); ServletRequestAttributeEvent event = null; if (replaced) { event = new ServletRequestAttributeEvent( context.getServletContext(), getRequest(), name, oldValue); } else { event = new ServletRequestAttributeEvent( context.getServletContext(), getRequest(), name, value); } for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof ServletRequestAttributeListener)) { continue; } ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners[i]; try { if (replaced) { listener.attributeReplaced(event); } else { listener.attributeAdded(event); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // Error valve will pick this exception up and display it to user attributes.put(RequestDispatcher.ERROR_EXCEPTION, t); context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t); } } }
Notify interested listeners that attribute has been removed.
Params:
  • name – Attribute name
  • value – Attribute value
/** * Notify interested listeners that attribute has been removed. * * @param name Attribute name * @param value Attribute value */
private void notifyAttributeRemoved(String name, Object value) { Context context = getContext(); Object listeners[] = context.getApplicationEventListeners(); if ((listeners == null) || (listeners.length == 0)) { return; } ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(context.getServletContext(), getRequest(), name, value); for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof ServletRequestAttributeListener)) { continue; } ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners[i]; try { listener.attributeRemoved(event); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); // Error valve will pick this exception up and display it to user attributes.put(RequestDispatcher.ERROR_EXCEPTION, t); context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t); } } }
Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader().
Params:
  • enc – The character encoding to be used
Throws:
Since:Servlet 2.3
/** * Overrides the name of the character encoding used in the body of * this request. This method must be called prior to reading request * parameters or reading input using <code>getReader()</code>. * * @param enc The character encoding to be used * * @exception UnsupportedEncodingException if the specified encoding * is not supported * * @since Servlet 2.3 */
@Override public void setCharacterEncoding(String enc) throws UnsupportedEncodingException { if (usingReader) { return; } // Confirm that the encoding name is valid Charset charset = B2CConverter.getCharset(enc); // Save the validated encoding coyoteRequest.setCharset(charset); } @Override public ServletContext getServletContext() { return getContext().getServletContext(); } @Override public AsyncContext startAsync() { return startAsync(getRequest(),response.getResponse()); } @Override public AsyncContext startAsync(ServletRequest request, ServletResponse response) { if (!isAsyncSupported()) { IllegalStateException ise = new IllegalStateException(sm.getString("request.asyncNotSupported")); log.warn(sm.getString("coyoteRequest.noAsync", StringUtils.join(getNonAsyncClassNames())), ise); throw ise; } if (asyncContext == null) { asyncContext = new AsyncContextImpl(this); } asyncContext.setStarted(getContext(), request, response, request==getRequest() && response==getResponse().getResponse()); asyncContext.setTimeout(getConnector().getAsyncTimeout()); return asyncContext; } private Set<String> getNonAsyncClassNames() { Set<String> result = new HashSet<>(); Wrapper wrapper = getWrapper(); if (!wrapper.isAsyncSupported()) { result.add(wrapper.getServletClass()); } FilterChain filterChain = getFilterChain(); if (filterChain instanceof ApplicationFilterChain) { ((ApplicationFilterChain) filterChain).findNonAsyncFilters(result); } else { result.add(sm.getString("coyoteRequest.filterAsyncSupportUnknown")); } Container c = wrapper; while (c != null) { c.getPipeline().findNonAsyncValves(result); c = c.getParent(); } return result; } @Override public boolean isAsyncStarted() { if (asyncContext == null) { return false; } return asyncContext.isStarted(); } public boolean isAsyncDispatching() { if (asyncContext == null) { return false; } AtomicBoolean result = new AtomicBoolean(false); coyoteRequest.action(ActionCode.ASYNC_IS_DISPATCHING, result); return result.get(); } public boolean isAsyncCompleting() { if (asyncContext == null) { return false; } AtomicBoolean result = new AtomicBoolean(false); coyoteRequest.action(ActionCode.ASYNC_IS_COMPLETING, result); return result.get(); } public boolean isAsync() { if (asyncContext == null) { return false; } AtomicBoolean result = new AtomicBoolean(false); coyoteRequest.action(ActionCode.ASYNC_IS_ASYNC, result); return result.get(); } @Override public boolean isAsyncSupported() { if (this.asyncSupported == null) { return true; } return asyncSupported.booleanValue(); } @Override public AsyncContext getAsyncContext() { if (!isAsyncStarted()) { throw new IllegalStateException(sm.getString("request.notAsync")); } return asyncContext; } public AsyncContextImpl getAsyncContextInternal() { return asyncContext; } @Override public DispatcherType getDispatcherType() { if (internalDispatcherType == null) { return DispatcherType.REQUEST; } return this.internalDispatcherType; } // ---------------------------------------------------- HttpRequest Methods
Add a Cookie to the set of Cookies associated with this Request.
Params:
  • cookie – The new cookie
/** * Add a Cookie to the set of Cookies associated with this Request. * * @param cookie The new cookie */
public void addCookie(Cookie cookie) { if (!cookiesConverted) { convertCookies(); } int size = 0; if (cookies != null) { size = cookies.length; } Cookie[] newCookies = new Cookie[size + 1]; if (cookies != null) { System.arraycopy(cookies, 0, newCookies, 0, size); } newCookies[size] = cookie; cookies = newCookies; }
Add a Locale to the set of preferred Locales for this Request. The first added Locale will be the first one returned by getLocales().
Params:
  • locale – The new preferred Locale
/** * Add a Locale to the set of preferred Locales for this Request. The * first added Locale will be the first one returned by getLocales(). * * @param locale The new preferred Locale */
public void addLocale(Locale locale) { locales.add(locale); }
Clear the collection of Cookies associated with this Request.
/** * Clear the collection of Cookies associated with this Request. */
public void clearCookies() { cookiesParsed = true; cookiesConverted = true; cookies = null; }
Clear the collection of Locales associated with this Request.
/** * Clear the collection of Locales associated with this Request. */
public void clearLocales() { locales.clear(); }
Set the authentication type used for this request, if any; otherwise set the type to null. Typical values are "BASIC", "DIGEST", or "SSL".
Params:
  • type – The authentication type used
/** * Set the authentication type used for this request, if any; otherwise * set the type to <code>null</code>. Typical values are "BASIC", * "DIGEST", or "SSL". * * @param type The authentication type used */
public void setAuthType(String type) { this.authType = type; }
Set the path information for this Request. This will normally be called when the associated Context is mapping the Request to a particular Wrapper.
Params:
  • path – The path information
/** * Set the path information for this Request. This will normally be called * when the associated Context is mapping the Request to a particular * Wrapper. * * @param path The path information */
public void setPathInfo(String path) { mappingData.pathInfo.setString(path); }
Set a flag indicating whether or not the requested session ID for this request came in through a cookie. This is normally called by the HTTP Connector, when it parses the request headers.
Params:
  • flag – The new flag
/** * Set a flag indicating whether or not the requested session ID for this * request came in through a cookie. This is normally called by the * HTTP Connector, when it parses the request headers. * * @param flag The new flag */
public void setRequestedSessionCookie(boolean flag) { this.requestedSessionCookie = flag; }
Set the requested session ID for this request. This is normally called by the HTTP Connector, when it parses the request headers.
Params:
  • id – The new session id
/** * Set the requested session ID for this request. This is normally called * by the HTTP Connector, when it parses the request headers. * * @param id The new session id */
public void setRequestedSessionId(String id) { this.requestedSessionId = id; }
Set a flag indicating whether or not the requested session ID for this request came in through a URL. This is normally called by the HTTP Connector, when it parses the request headers.
Params:
  • flag – The new flag
/** * Set a flag indicating whether or not the requested session ID for this * request came in through a URL. This is normally called by the * HTTP Connector, when it parses the request headers. * * @param flag The new flag */
public void setRequestedSessionURL(boolean flag) { this.requestedSessionURL = flag; }
Set a flag indicating whether or not the requested session ID for this request came in through SSL. This is normally called by the HTTP Connector, when it parses the request headers.
Params:
  • flag – The new flag
/** * Set a flag indicating whether or not the requested session ID for this * request came in through SSL. This is normally called by the * HTTP Connector, when it parses the request headers. * * @param flag The new flag */
public void setRequestedSessionSSL(boolean flag) { this.requestedSessionSSL = flag; }
Get the decoded request URI.
Returns:the URL decoded request URI
/** * Get the decoded request URI. * * @return the URL decoded request URI */
public String getDecodedRequestURI() { return coyoteRequest.decodedURI().toString(); }
Get the decoded request URI.
Returns:the URL decoded request URI
/** * Get the decoded request URI. * * @return the URL decoded request URI */
public MessageBytes getDecodedRequestURIMB() { return coyoteRequest.decodedURI(); }
Set the Principal who has been authenticated for this Request. This value is also used to calculate the value to be returned by the getRemoteUser() method.
Params:
  • principal – The user Principal
/** * Set the Principal who has been authenticated for this Request. This * value is also used to calculate the value to be returned by the * <code>getRemoteUser()</code> method. * * @param principal The user Principal */
public void setUserPrincipal(final Principal principal) { if (Globals.IS_SECURITY_ENABLED && principal != null) { if (subject == null) { final HttpSession session = getSession(false); if (session == null) { // Cache the subject in the request subject = newSubject(principal); } else { // Cache the subject in the request and the session subject = (Subject) session.getAttribute(Globals.SUBJECT_ATTR); if (subject == null) { subject = newSubject(principal); session.setAttribute(Globals.SUBJECT_ATTR, subject); } else { subject.getPrincipals().add(principal); } } } else { subject.getPrincipals().add(principal); } } userPrincipal = principal; } private Subject newSubject(final Principal principal) { final Subject result = new Subject(); result.getPrincipals().add(principal); return result; } // --------------------------------------------- HttpServletRequest Methods @Override public boolean isTrailerFieldsReady() { return coyoteRequest.isTrailerFieldsReady(); } @Override public Map<String, String> getTrailerFields() { if (!isTrailerFieldsReady()) { throw new IllegalStateException(sm.getString("coyoteRequest.trailersNotReady")); } Map<String,String> result = new HashMap<>(); result.putAll(coyoteRequest.getTrailerFields()); return result; } @Override public PushBuilder newPushBuilder() { return newPushBuilder(this); } public PushBuilder newPushBuilder(HttpServletRequest request) { AtomicBoolean result = new AtomicBoolean(); coyoteRequest.action(ActionCode.IS_PUSH_SUPPORTED, result); if (result.get()) { return new ApplicationPushBuilder(this, request); } else { return null; } } @SuppressWarnings("unchecked") @Override public <T extends HttpUpgradeHandler> T upgrade( Class<T> httpUpgradeHandlerClass) throws java.io.IOException, ServletException { T handler; InstanceManager instanceManager = null; try { // Do not go through the instance manager for internal Tomcat classes since they don't // need injection if (InternalHttpUpgradeHandler.class.isAssignableFrom(httpUpgradeHandlerClass)) { handler = httpUpgradeHandlerClass.getConstructor().newInstance(); } else { instanceManager = getContext().getInstanceManager(); handler = (T) instanceManager.newInstance(httpUpgradeHandlerClass); } } catch (ReflectiveOperationException | NamingException | IllegalArgumentException | SecurityException e) { throw new ServletException(e); } UpgradeToken upgradeToken = new UpgradeToken(handler, getContext(), instanceManager); coyoteRequest.action(ActionCode.UPGRADE, upgradeToken); // Output required by RFC2616. Protocol specific headers should have // already been set. response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS); return handler; }
Return the authentication type used for this Request.
/** * Return the authentication type used for this Request. */
@Override public String getAuthType() { return authType; }
Return the portion of the request URI used to select the Context of the Request. The value returned is not decoded which also implies it is not normalised.
/** * Return the portion of the request URI used to select the Context * of the Request. The value returned is not decoded which also implies it * is not normalised. */
@Override public String getContextPath() { int lastSlash = mappingData.contextSlashCount; // Special case handling for the root context if (lastSlash == 0) { return ""; } String canonicalContextPath = getServletContext().getContextPath(); String uri = getRequestURI(); int pos = 0; if (!getContext().getAllowMultipleLeadingForwardSlashInPath()) { // Ensure that the returned value only starts with a single '/'. // This prevents the value being misinterpreted as a protocol- // relative URI if used with sendRedirect(). do { pos++; } while (pos < uri.length() && uri.charAt(pos) == '/'); pos--; uri = uri.substring(pos); } char[] uriChars = uri.toCharArray(); // Need at least the number of slashes in the context path while (lastSlash > 0) { pos = nextSlash(uriChars, pos + 1); if (pos == -1) { break; } lastSlash--; } // Now allow for path parameters, normalization and/or encoding. // Essentially, keep extending the candidate path up to the next slash // until the decoded and normalized candidate path (with the path // parameters removed) is the same as the canonical path. String candidate; if (pos == -1) { candidate = uri; } else { candidate = uri.substring(0, pos); } candidate = removePathParameters(candidate); candidate = UDecoder.URLDecode(candidate, connector.getURICharset()); candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); boolean match = canonicalContextPath.equals(candidate); while (!match && pos != -1) { pos = nextSlash(uriChars, pos + 1); if (pos == -1) { candidate = uri; } else { candidate = uri.substring(0, pos); } candidate = removePathParameters(candidate); candidate = UDecoder.URLDecode(candidate, connector.getURICharset()); candidate = org.apache.tomcat.util.http.RequestUtil.normalize(candidate); match = canonicalContextPath.equals(candidate); } if (match) { if (pos == -1) { return uri; } else { return uri.substring(0, pos); } } else { // Should never happen throw new IllegalStateException(sm.getString( "coyoteRequest.getContextPath.ise", canonicalContextPath, uri)); } } private String removePathParameters(String input) { int nextSemiColon = input.indexOf(';'); // Shortcut if (nextSemiColon == -1) { return input; } StringBuilder result = new StringBuilder(input.length()); result.append(input.substring(0, nextSemiColon)); while (true) { int nextSlash = input.indexOf('/', nextSemiColon); if (nextSlash == -1) { break; } nextSemiColon = input.indexOf(';', nextSlash); if (nextSemiColon == -1) { result.append(input.substring(nextSlash)); break; } else { result.append(input.substring(nextSlash, nextSemiColon)); } } return result.toString(); } private int nextSlash(char[] uri, int startPos) { int len = uri.length; int pos = startPos; while (pos < len) { if (uri[pos] == '/') { return pos; } else if (UDecoder.ALLOW_ENCODED_SLASH && uri[pos] == '%' && pos + 2 < len && uri[pos+1] == '2' && (uri[pos + 2] == 'f' || uri[pos + 2] == 'F')) { return pos; } pos++; } return -1; }
Return the set of Cookies received with this Request. Triggers parsing of the Cookie HTTP headers followed by conversion to Cookie objects if this has not already been performed.
Returns:the array of cookies
/** * Return the set of Cookies received with this Request. Triggers parsing of * the Cookie HTTP headers followed by conversion to Cookie objects if this * has not already been performed. * * @return the array of cookies */
@Override public Cookie[] getCookies() { if (!cookiesConverted) { convertCookies(); } return cookies; }
Return the server representation of the cookies associated with this request. Triggers parsing of the Cookie HTTP headers (but not conversion to Cookie objects) if the headers have not yet been parsed.
Returns:the server cookies
/** * Return the server representation of the cookies associated with this * request. Triggers parsing of the Cookie HTTP headers (but not conversion * to Cookie objects) if the headers have not yet been parsed. * * @return the server cookies */
public ServerCookies getServerCookies() { parseCookies(); return coyoteRequest.getCookies(); }
Return the value of the specified date header, if any; otherwise return -1.
Params:
  • name – Name of the requested date header
Throws:
Returns:the date as a long
/** * Return the value of the specified date header, if any; otherwise * return -1. * * @param name Name of the requested date header * @return the date as a long * * @exception IllegalArgumentException if the specified header value * cannot be converted to a date */
@Override public long getDateHeader(String name) { String value = getHeader(name); if (value == null) { return -1L; } // Attempt to convert the date header in a variety of formats long result = FastHttpDateFormat.parseDate(value); if (result != (-1L)) { return result; } throw new IllegalArgumentException(value); }
Return the first value of the specified header, if any; otherwise, return null
Params:
  • name – Name of the requested header
Returns:the header value
/** * Return the first value of the specified header, if any; otherwise, * return <code>null</code> * * @param name Name of the requested header * @return the header value */
@Override public String getHeader(String name) { return coyoteRequest.getHeader(name); }
Return all of the values of the specified header, if any; otherwise, return an empty enumeration.
Params:
  • name – Name of the requested header
Returns:the enumeration with the header values
/** * Return all of the values of the specified header, if any; otherwise, * return an empty enumeration. * * @param name Name of the requested header * @return the enumeration with the header values */
@Override public Enumeration<String> getHeaders(String name) { return coyoteRequest.getMimeHeaders().values(name); }
Returns:the names of all headers received with this request.
/** * @return the names of all headers received with this request. */
@Override public Enumeration<String> getHeaderNames() { return coyoteRequest.getMimeHeaders().names(); }
Return the value of the specified header as an integer, or -1 if there is no such header for this request.
Params:
  • name – Name of the requested header
Throws:
Returns:the header value as an int
/** * Return the value of the specified header as an integer, or -1 if there * is no such header for this request. * * @param name Name of the requested header * @return the header value as an int * * @exception IllegalArgumentException if the specified header value * cannot be converted to an integer */
@Override public int getIntHeader(String name) { String value = getHeader(name); if (value == null) { return -1; } return Integer.parseInt(value); } @Override public HttpServletMapping getHttpServletMapping() { return applicationMapping.getHttpServletMapping(); }
Returns:the HTTP request method used in this Request.
/** * @return the HTTP request method used in this Request. */
@Override public String getMethod() { return coyoteRequest.method().toString(); }
Returns:the path information associated with this Request.
/** * @return the path information associated with this Request. */
@Override public String getPathInfo() { return mappingData.pathInfo.toString(); }
Returns:the extra path information for this request, translated to a real path.
/** * @return the extra path information for this request, translated * to a real path. */
@Override public String getPathTranslated() { Context context = getContext(); if (context == null) { return null; } if (getPathInfo() == null) { return null; } return context.getServletContext().getRealPath(getPathInfo()); }
Returns:the query string associated with this request.
/** * @return the query string associated with this request. */
@Override public String getQueryString() { return coyoteRequest.queryString().toString(); }
Returns:the name of the remote user that has been authenticated for this Request.
/** * @return the name of the remote user that has been authenticated * for this Request. */
@Override public String getRemoteUser() { if (userPrincipal == null) { return null; } return userPrincipal.getName(); }
Get the request path.
Returns:the request path
/** * Get the request path. * * @return the request path */
public MessageBytes getRequestPathMB() { return mappingData.requestPath; }
Returns:the session identifier included in this request, if any.
/** * @return the session identifier included in this request, if any. */
@Override public String getRequestedSessionId() { return requestedSessionId; }
Returns:the request URI for this request.
/** * @return the request URI for this request. */
@Override public String getRequestURI() { return coyoteRequest.requestURI().toString(); } @Override public StringBuffer getRequestURL() { return RequestUtil.getRequestURL(this); }
Returns:the portion of the request URI used to select the servlet that will process this request.
/** * @return the portion of the request URI used to select the servlet * that will process this request. */
@Override public String getServletPath() { return mappingData.wrapperPath.toString(); }
Returns:the session associated with this Request, creating one if necessary.
/** * @return the session associated with this Request, creating one * if necessary. */
@Override public HttpSession getSession() { Session session = doGetSession(true); if (session == null) { return null; } return session.getSession(); }
Params:
  • create – Create a new session if one does not exist
Returns:the session associated with this Request, creating one if necessary and requested.
/** * @return the session associated with this Request, creating one * if necessary and requested. * * @param create Create a new session if one does not exist */
@Override public HttpSession getSession(boolean create) { Session session = doGetSession(create); if (session == null) { return null; } return session.getSession(); }
Returns:true if the session identifier included in this request came from a cookie.
/** * @return <code>true</code> if the session identifier included in this * request came from a cookie. */
@Override public boolean isRequestedSessionIdFromCookie() { if (requestedSessionId == null) { return false; } return requestedSessionCookie; }
Returns:true if the session identifier included in this request came from the request URI.
/** * @return <code>true</code> if the session identifier included in this * request came from the request URI. */
@Override public boolean isRequestedSessionIdFromURL() { if (requestedSessionId == null) { return false; } return requestedSessionURL; }
Returns:true if the session identifier included in this request came from the request URI.
Deprecated:As of Version 2.1 of the Java Servlet API, use isRequestedSessionIdFromURL() instead.
/** * @return <code>true</code> if the session identifier included in this * request came from the request URI. * * @deprecated As of Version 2.1 of the Java Servlet API, use * <code>isRequestedSessionIdFromURL()</code> instead. */
@Override @Deprecated public boolean isRequestedSessionIdFromUrl() { return isRequestedSessionIdFromURL(); }
Returns:true if the session identifier included in this request identifies a valid session.
/** * @return <code>true</code> if the session identifier included in this * request identifies a valid session. */
@Override public boolean isRequestedSessionIdValid() { if (requestedSessionId == null) { return false; } Context context = getContext(); if (context == null) { return false; } Manager manager = context.getManager(); if (manager == null) { return false; } Session session = null; try { session = manager.findSession(requestedSessionId); } catch (IOException e) { // Can't find the session } if ((session == null) || !session.isValid()) { // Check for parallel deployment contexts if (getMappingData().contexts == null) { return false; } else { for (int i = (getMappingData().contexts.length); i > 0; i--) { Context ctxt = getMappingData().contexts[i - 1]; try { if (ctxt.getManager().findSession(requestedSessionId) != null) { return true; } } catch (IOException e) { // Ignore } } return false; } } return true; }
Params:
  • role – Role name to be validated
Returns:true if the authenticated user principal possesses the specified role name.
/** * @return <code>true</code> if the authenticated user principal * possesses the specified role name. * * @param role Role name to be validated */
@Override public boolean isUserInRole(String role) { // Have we got an authenticated principal at all? if (userPrincipal == null) { return false; } // Identify the Realm we will use for checking role assignments Context context = getContext(); if (context == null) { return false; } // If the role is "*" then the return value must be false // Servlet 31, section 13.3 if ("*".equals(role)) { return false; } // If the role is "**" then, unless the application defines a role with // that name, only check if the user is authenticated if ("**".equals(role) && !context.findSecurityRole("**")) { return userPrincipal != null; } Realm realm = context.getRealm(); if (realm == null) { return false; } // Check for a role defined directly as a <security-role> return realm.hasRole(getWrapper(), userPrincipal, role); }
Returns:the principal that has been authenticated for this Request.
/** * @return the principal that has been authenticated for this Request. */
public Principal getPrincipal() { return userPrincipal; }
Returns:the principal that has been authenticated for this Request.
/** * @return the principal that has been authenticated for this Request. */
@Override public Principal getUserPrincipal() { if (userPrincipal instanceof TomcatPrincipal) { GSSCredential gssCredential = ((TomcatPrincipal) userPrincipal).getGssCredential(); if (gssCredential != null) { int left = -1; try { left = gssCredential.getRemainingLifetime(); } catch (GSSException e) { log.warn(sm.getString("coyoteRequest.gssLifetimeFail", userPrincipal.getName()), e); } if (left == 0) { // GSS credential has expired. Need to re-authenticate. try { logout(); } catch (ServletException e) { // Should never happen (no code called by logout() // throws a ServletException } return null; } } return ((TomcatPrincipal) userPrincipal).getUserPrincipal(); } return userPrincipal; }
Returns:the session associated with this Request, creating one if necessary.
/** * @return the session associated with this Request, creating one * if necessary. */
public Session getSessionInternal() { return doGetSession(true); }
Change the ID of the session that this request is associated with. There are several things that may trigger an ID change. These include moving between nodes in a cluster and session fixation prevention during the authentication process.
Params:
  • newSessionId – The session to change the session ID for
/** * Change the ID of the session that this request is associated with. There * are several things that may trigger an ID change. These include moving * between nodes in a cluster and session fixation prevention during the * authentication process. * * @param newSessionId The session to change the session ID for */
public void changeSessionId(String newSessionId) { // This should only ever be called if there was an old session ID but // double check to be sure if (requestedSessionId != null && requestedSessionId.length() > 0) { requestedSessionId = newSessionId; } Context context = getContext(); if (context != null && !context.getServletContext() .getEffectiveSessionTrackingModes() .contains(SessionTrackingMode.COOKIE)) { return; } if (response != null) { Cookie newCookie = ApplicationSessionCookieConfig.createSessionCookie(context, newSessionId, isSecure()); response.addSessionCookieInternal(newCookie); } }
Changes the session ID of the session associated with this request.
See Also:
Returns:the old session ID before it was changed
Since:Servlet 3.1
/** * Changes the session ID of the session associated with this request. * * @return the old session ID before it was changed * @see javax.servlet.http.HttpSessionIdListener * @since Servlet 3.1 */
@Override public String changeSessionId() { Session session = this.getSessionInternal(false); if (session == null) { throw new IllegalStateException( sm.getString("coyoteRequest.changeSessionId")); } Manager manager = this.getContext().getManager(); manager.changeSessionId(session); String newSessionId = session.getId(); this.changeSessionId(newSessionId); return newSessionId; }
Params:
  • create – Create a new session if one does not exist
Returns:the session associated with this Request, creating one if necessary and requested.
/** * @return the session associated with this Request, creating one * if necessary and requested. * * @param create Create a new session if one does not exist */
public Session getSessionInternal(boolean create) { return doGetSession(create); }
Returns:true if we have parsed parameters
/** * @return <code>true</code> if we have parsed parameters */
public boolean isParametersParsed() { return parametersParsed; }
Returns:true if an attempt has been made to read the request body and all of the request body has been read.
/** * @return <code>true</code> if an attempt has been made to read the request * body and all of the request body has been read. */
public boolean isFinished() { return coyoteRequest.isFinished(); }
Check the configuration for aborted uploads and if configured to do so, disable the swallowing of any remaining input and close the connection once the response has been written.
/** * Check the configuration for aborted uploads and if configured to do so, * disable the swallowing of any remaining input and close the connection * once the response has been written. */
protected void checkSwallowInput() { Context context = getContext(); if (context != null && !context.getSwallowAbortedUploads()) { coyoteRequest.action(ActionCode.DISABLE_SWALLOW_INPUT, null); } }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { if (response.isCommitted()) { throw new IllegalStateException( sm.getString("coyoteRequest.authenticate.ise")); } return getContext().getAuthenticator().authenticate(this, response); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void login(String username, String password) throws ServletException { if (getAuthType() != null || getRemoteUser() != null || getUserPrincipal() != null) { throw new ServletException( sm.getString("coyoteRequest.alreadyAuthenticated")); } getContext().getAuthenticator().login(username, password, this); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public void logout() throws ServletException { getContext().getAuthenticator().logout(this); }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public Collection<Part> getParts() throws IOException, IllegalStateException, ServletException { parseParts(true); if (partsParseException != null) { if (partsParseException instanceof IOException) { throw (IOException) partsParseException; } else if (partsParseException instanceof IllegalStateException) { throw (IllegalStateException) partsParseException; } else if (partsParseException instanceof ServletException) { throw (ServletException) partsParseException; } } return parts; } private void parseParts(boolean explicit) { // Return immediately if the parts have already been parsed if (parts != null || partsParseException != null) { return; } Context context = getContext(); MultipartConfigElement mce = getWrapper().getMultipartConfigElement(); if (mce == null) { if(context.getAllowCasualMultipartParsing()) { mce = new MultipartConfigElement(null, connector.getMaxPostSize(), connector.getMaxPostSize(), connector.getMaxPostSize()); } else { if (explicit) { partsParseException = new IllegalStateException( sm.getString("coyoteRequest.noMultipartConfig")); return; } else { parts = Collections.emptyList(); return; } } } Parameters parameters = coyoteRequest.getParameters(); parameters.setLimit(getConnector().getMaxParameterCount()); boolean success = false; try { File location; String locationStr = mce.getLocation(); if (locationStr == null || locationStr.length() == 0) { location = ((File) context.getServletContext().getAttribute( ServletContext.TEMPDIR)); } else { // If relative, it is relative to TEMPDIR location = new File(locationStr); if (!location.isAbsolute()) { location = new File( (File) context.getServletContext().getAttribute(ServletContext.TEMPDIR), locationStr).getAbsoluteFile(); } } if (!location.exists() && context.getCreateUploadTargets()) { log.warn(sm.getString("coyoteRequest.uploadCreate", location.getAbsolutePath(), getMappingData().wrapper.getName())); if (!location.mkdirs()) { log.warn(sm.getString("coyoteRequest.uploadCreateFail", location.getAbsolutePath())); } } if (!location.isDirectory()) { parameters.setParseFailedReason(FailReason.MULTIPART_CONFIG_INVALID); partsParseException = new IOException( sm.getString("coyoteRequest.uploadLocationInvalid", location)); return; } // Create a new file upload handler DiskFileItemFactory factory = new DiskFileItemFactory(); try { factory.setRepository(location.getCanonicalFile()); } catch (IOException ioe) { parameters.setParseFailedReason(FailReason.IO_ERROR); partsParseException = ioe; return; } factory.setSizeThreshold(mce.getFileSizeThreshold()); ServletFileUpload upload = new ServletFileUpload(); upload.setFileItemFactory(factory); upload.setFileSizeMax(mce.getMaxFileSize()); upload.setSizeMax(mce.getMaxRequestSize()); parts = new ArrayList<>(); try { List<FileItem> items = upload.parseRequest(new ServletRequestContext(this)); int maxPostSize = getConnector().getMaxPostSize(); int postSize = 0; Charset charset = getCharset(); for (FileItem item : items) { ApplicationPart part = new ApplicationPart(item, location); parts.add(part); if (part.getSubmittedFileName() == null) { String name = part.getName(); String value = null; try { value = part.getString(charset.name()); } catch (UnsupportedEncodingException uee) { // Not possible } if (maxPostSize >= 0) { // Have to calculate equivalent size. Not completely // accurate but close enough. postSize += name.getBytes(charset).length; if (value != null) { // Equals sign postSize++; // Value length postSize += part.getSize(); } // Value separator postSize++; if (postSize > maxPostSize) { parameters.setParseFailedReason(FailReason.POST_TOO_LARGE); throw new IllegalStateException(sm.getString( "coyoteRequest.maxPostSizeExceeded")); } } parameters.addParameter(name, value); } } success = true; } catch (InvalidContentTypeException e) { parameters.setParseFailedReason(FailReason.INVALID_CONTENT_TYPE); partsParseException = new ServletException(e); } catch (FileUploadBase.SizeException e) { parameters.setParseFailedReason(FailReason.POST_TOO_LARGE); checkSwallowInput(); partsParseException = new IllegalStateException(e); } catch (FileUploadException e) { parameters.setParseFailedReason(FailReason.IO_ERROR); partsParseException = new IOException(e); } catch (IllegalStateException e) { // addParameters() will set parseFailedReason checkSwallowInput(); partsParseException = e; } } finally { // This might look odd but is correct. setParseFailedReason() only // sets the failure reason if none is currently set. This code could // be more efficient but it is written this way to be robust with // respect to changes in the remainder of the method. if (partsParseException != null || !success) { parameters.setParseFailedReason(FailReason.UNKNOWN); } } }
{@inheritDoc}
/** * {@inheritDoc} */
@Override public Part getPart(String name) throws IOException, IllegalStateException, ServletException { for (Part part : getParts()) { if (name.equals(part.getName())) { return part; } } return null; } // ------------------------------------------------------ Protected Methods protected Session doGetSession(boolean create) { // There cannot be a session if no context has been assigned yet Context context = getContext(); if (context == null) { return null; } // Return the current session if it exists and is valid if ((session != null) && !session.isValid()) { session = null; } if (session != null) { return session; } // Return the requested session if it exists and is valid Manager manager = context.getManager(); if (manager == null) { return null; // Sessions are not supported } if (requestedSessionId != null) { try { session = manager.findSession(requestedSessionId); } catch (IOException e) { session = null; } if ((session != null) && !session.isValid()) { session = null; } if (session != null) { session.access(); return session; } } // Create a new session if requested and the response is not committed if (!create) { return null; } boolean trackModesIncludesCookie = context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE); if (trackModesIncludesCookie && response.getResponse().isCommitted()) { throw new IllegalStateException(sm.getString("coyoteRequest.sessionCreateCommitted")); } // Re-use session IDs provided by the client in very limited // circumstances. String sessionId = getRequestedSessionId(); if (requestedSessionSSL) { // If the session ID has been obtained from the SSL handshake then // use it. } else if (("/".equals(context.getSessionCookiePath()) && isRequestedSessionIdFromCookie())) { /* This is the common(ish) use case: using the same session ID with * multiple web applications on the same host. Typically this is * used by Portlet implementations. It only works if sessions are * tracked via cookies. The cookie must have a path of "/" else it * won't be provided for requests to all web applications. * * Any session ID provided by the client should be for a session * that already exists somewhere on the host. Check if the context * is configured for this to be confirmed. */ if (context.getValidateClientProvidedNewSessionId()) { boolean found = false; for (Container container : getHost().findChildren()) { Manager m = ((Context) container).getManager(); if (m != null) { try { if (m.findSession(sessionId) != null) { found = true; break; } } catch (IOException e) { // Ignore. Problems with this manager will be // handled elsewhere. } } } if (!found) { sessionId = null; } } } else { sessionId = null; } session = manager.createSession(sessionId); // Creating a new session cookie based on that session if (session != null && trackModesIncludesCookie) { Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie( context, session.getIdInternal(), isSecure()); response.addSessionCookieInternal(cookie); } if (session == null) { return null; } session.access(); return session; } protected String unescape(String s) { if (s==null) { return null; } if (s.indexOf('\\') == -1) { return s; } StringBuilder buf = new StringBuilder(); for (int i=0; i<s.length(); i++) { char c = s.charAt(i); if (c!='\\') { buf.append(c); } else { if (++i >= s.length()) { throw new IllegalArgumentException();//invalid escape, hence invalid cookie } c = s.charAt(i); buf.append(c); } } return buf.toString(); }
Parse cookies. This only parses the cookies into the memory efficient ServerCookies structure. It does not populate the Cookie objects.
/** * Parse cookies. This only parses the cookies into the memory efficient * ServerCookies structure. It does not populate the Cookie objects. */
protected void parseCookies() { if (cookiesParsed) { return; } cookiesParsed = true; ServerCookies serverCookies = coyoteRequest.getCookies(); serverCookies.setLimit(connector.getMaxCookieCount()); CookieProcessor cookieProcessor = getContext().getCookieProcessor(); cookieProcessor.parseCookieHeader(coyoteRequest.getMimeHeaders(), serverCookies); }
Converts the parsed cookies (parsing the Cookie headers first if they have not been parsed) into Cookie objects.
/** * Converts the parsed cookies (parsing the Cookie headers first if they * have not been parsed) into Cookie objects. */
protected void convertCookies() { if (cookiesConverted) { return; } cookiesConverted = true; if (getContext() == null) { return; } parseCookies(); ServerCookies serverCookies = coyoteRequest.getCookies(); CookieProcessor cookieProcessor = getContext().getCookieProcessor(); int count = serverCookies.getCookieCount(); if (count <= 0) { return; } cookies = new Cookie[count]; int idx=0; for (int i = 0; i < count; i++) { ServerCookie scookie = serverCookies.getCookie(i); try { // We must unescape the '\\' escape character Cookie cookie = new Cookie(scookie.getName().toString(),null); int version = scookie.getVersion(); cookie.setVersion(version); scookie.getValue().getByteChunk().setCharset(cookieProcessor.getCharset()); cookie.setValue(unescape(scookie.getValue().toString())); cookie.setPath(unescape(scookie.getPath().toString())); String domain = scookie.getDomain().toString(); if (domain!=null) { cookie.setDomain(unescape(domain));//avoid NPE } String comment = scookie.getComment().toString(); cookie.setComment(version==1?unescape(comment):null); cookies[idx++] = cookie; } catch(IllegalArgumentException e) { // Ignore bad cookie } } if( idx < count ) { Cookie [] ncookies = new Cookie[idx]; System.arraycopy(cookies, 0, ncookies, 0, idx); cookies = ncookies; } }
Parse request parameters.
/** * Parse request parameters. */
protected void parseParameters() { parametersParsed = true; Parameters parameters = coyoteRequest.getParameters(); boolean success = false; try { // Set this every time in case limit has been changed via JMX parameters.setLimit(getConnector().getMaxParameterCount()); // getCharacterEncoding() may have been overridden to search for // hidden form field containing request encoding Charset charset = getCharset(); boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); parameters.setCharset(charset); if (useBodyEncodingForURI) { parameters.setQueryStringCharset(charset); } // Note: If !useBodyEncodingForURI, the query string encoding is // that set towards the start of CoyoyeAdapter.service() parameters.handleQueryParameters(); if (usingInputStream || usingReader) { success = true; return; } String contentType = getContentType(); if (contentType == null) { contentType = ""; } int semicolon = contentType.indexOf(';'); if (semicolon >= 0) { contentType = contentType.substring(0, semicolon).trim(); } else { contentType = contentType.trim(); } if ("multipart/form-data".equals(contentType)) { parseParts(false); success = true; return; } if( !getConnector().isParseBodyMethod(getMethod()) ) { success = true; return; } if (!("application/x-www-form-urlencoded".equals(contentType))) { success = true; return; } int len = getContentLength(); if (len > 0) { int maxPostSize = connector.getMaxPostSize(); if ((maxPostSize >= 0) && (len > maxPostSize)) { Context context = getContext(); if (context != null && context.getLogger().isDebugEnabled()) { context.getLogger().debug( sm.getString("coyoteRequest.postTooLarge")); } checkSwallowInput(); parameters.setParseFailedReason(FailReason.POST_TOO_LARGE); return; } byte[] formData = null; if (len < CACHED_POST_LEN) { if (postData == null) { postData = new byte[CACHED_POST_LEN]; } formData = postData; } else { formData = new byte[len]; } try { if (readPostBody(formData, len) != len) { parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE); return; } } catch (IOException e) { // Client disconnect Context context = getContext(); if (context != null && context.getLogger().isDebugEnabled()) { context.getLogger().debug( sm.getString("coyoteRequest.parseParameters"), e); } parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT); return; } parameters.processParameters(formData, 0, len); } else if ("chunked".equalsIgnoreCase( coyoteRequest.getHeader("transfer-encoding"))) { byte[] formData = null; try { formData = readChunkedPostBody(); } catch (IllegalStateException ise) { // chunkedPostTooLarge error parameters.setParseFailedReason(FailReason.POST_TOO_LARGE); Context context = getContext(); if (context != null && context.getLogger().isDebugEnabled()) { context.getLogger().debug( sm.getString("coyoteRequest.parseParameters"), ise); } return; } catch (IOException e) { // Client disconnect parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT); Context context = getContext(); if (context != null && context.getLogger().isDebugEnabled()) { context.getLogger().debug( sm.getString("coyoteRequest.parseParameters"), e); } return; } if (formData != null) { parameters.processParameters(formData, 0, formData.length); } } success = true; } finally { if (!success) { parameters.setParseFailedReason(FailReason.UNKNOWN); } } }
Read post body in an array.
Params:
  • body – The bytes array in which the body will be read
  • len – The body length
Throws:
Returns:the bytes count that has been read
/** * Read post body in an array. * * @param body The bytes array in which the body will be read * @param len The body length * @return the bytes count that has been read * @throws IOException if an IO exception occurred */
protected int readPostBody(byte[] body, int len) throws IOException { int offset = 0; do { int inputLen = getStream().read(body, offset, len - offset); if (inputLen <= 0) { return offset; } offset += inputLen; } while ((len - offset) > 0); return len; }
Read chunked post body.
Throws:
Returns:the post body as a bytes array
/** * Read chunked post body. * * @return the post body as a bytes array * @throws IOException if an IO exception occurred */
protected byte[] readChunkedPostBody() throws IOException { ByteChunk body = new ByteChunk(); byte[] buffer = new byte[CACHED_POST_LEN]; int len = 0; while (len > -1) { len = getStream().read(buffer, 0, CACHED_POST_LEN); if (connector.getMaxPostSize() >= 0 && (body.getLength() + len) > connector.getMaxPostSize()) { // Too much data checkSwallowInput(); throw new IllegalStateException( sm.getString("coyoteRequest.chunkedPostTooLarge")); } if (len > 0) { body.append(buffer, 0, len); } } if (body.getLength() == 0) { return null; } if (body.getLength() < body.getBuffer().length) { int length = body.getLength(); byte[] result = new byte[length]; System.arraycopy(body.getBuffer(), 0, result, 0, length); return result; } return body.getBuffer(); }
Parse request locales.
/** * Parse request locales. */
protected void parseLocales() { localesParsed = true; // Store the accumulated languages that have been requested in // a local collection, sorted by the quality value (so we can // add Locales in descending order). The values will be ArrayLists // containing the corresponding Locales to be added TreeMap<Double, ArrayList<Locale>> locales = new TreeMap<>(); Enumeration<String> values = getHeaders("accept-language"); while (values.hasMoreElements()) { String value = values.nextElement(); parseLocalesHeader(value, locales); } // Process the quality values in highest->lowest order (due to // negating the Double value when creating the key) for (ArrayList<Locale> list : locales.values()) { for (Locale locale : list) { addLocale(locale); } } }
Parse accept-language header value.
Params:
  • value – the header value
  • locales – the map that will hold the result
/** * Parse accept-language header value. * * @param value the header value * @param locales the map that will hold the result */
protected void parseLocalesHeader(String value, TreeMap<Double, ArrayList<Locale>> locales) { List<AcceptLanguage> acceptLanguages; try { acceptLanguages = AcceptLanguage.parse(new StringReader(value)); } catch (IOException e) { // Mal-formed headers are ignore. Do the same in the unlikely event // of an IOException. return; } for (AcceptLanguage acceptLanguage : acceptLanguages) { // Add a new Locale to the list of Locales for this quality level Double key = Double.valueOf(-acceptLanguage.getQuality()); // Reverse the order ArrayList<Locale> values = locales.get(key); if (values == null) { values = new ArrayList<>(); locales.put(key, values); } values.add(acceptLanguage.getLocale()); } } // ----------------------------------------------------- Special attributes handling private static interface SpecialAttributeAdapter { Object get(Request request, String name); void set(Request request, String name, Object value); // None of special attributes support removal // void remove(Request request, String name); } private static final Map<String, SpecialAttributeAdapter> specialAttributes = new HashMap<>(); static { specialAttributes.put(Globals.DISPATCHER_TYPE_ATTR, new SpecialAttributeAdapter() { @Override public Object get(Request request, String name) { return (request.internalDispatcherType == null) ? DispatcherType.REQUEST : request.internalDispatcherType; } @Override public void set(Request request, String name, Object value) { request.internalDispatcherType = (DispatcherType) value; } }); specialAttributes.put(Globals.DISPATCHER_REQUEST_PATH_ATTR, new SpecialAttributeAdapter() { @Override public Object get(Request request, String name) { return (request.requestDispatcherPath == null) ? request .getRequestPathMB().toString() : request.requestDispatcherPath.toString(); } @Override public void set(Request request, String name, Object value) { request.requestDispatcherPath = value; } }); specialAttributes.put(Globals.ASYNC_SUPPORTED_ATTR, new SpecialAttributeAdapter() { @Override public Object get(Request request, String name) { return request.asyncSupported; } @Override public void set(Request request, String name, Object value) { Boolean oldValue = request.asyncSupported; request.asyncSupported = (Boolean)value; request.notifyAttributeAssigned(name, value, oldValue); } }); specialAttributes.put(Globals.GSS_CREDENTIAL_ATTR, new SpecialAttributeAdapter() { @Override public Object get(Request request, String name) { if (request.userPrincipal instanceof TomcatPrincipal) { return ((TomcatPrincipal) request.userPrincipal) .getGssCredential(); } return null; } @Override public void set(Request request, String name, Object value) { // NO-OP } }); specialAttributes.put(Globals.PARAMETER_PARSE_FAILED_ATTR, new SpecialAttributeAdapter() { @Override public Object get(Request request, String name) { if (request.getCoyoteRequest().getParameters() .isParseFailed()) { return Boolean.TRUE; } return null; } @Override public void set(Request request, String name, Object value) { // NO-OP } }); specialAttributes.put(Globals.PARAMETER_PARSE_FAILED_REASON_ATTR, new SpecialAttributeAdapter() { @Override public Object get(Request request, String name) { return request.getCoyoteRequest().getParameters().getParseFailedReason(); } @Override public void set(Request request, String name, Object value) { // NO-OP } }); specialAttributes.put(Globals.SENDFILE_SUPPORTED_ATTR, new SpecialAttributeAdapter() { @Override public Object get(Request request, String name) { return Boolean.valueOf( request.getConnector().getProtocolHandler( ).isSendfileSupported() && request.getCoyoteRequest().getSendfile()); } @Override public void set(Request request, String name, Object value) { // NO-OP } }); for (SimpleDateFormat sdf : formatsTemplate) { sdf.setTimeZone(GMT_ZONE); } } }