/*
* 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.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.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javax.naming.NamingException;
import javax.security.auth.Subject;
import jakarta.servlet.AsyncContext;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.FilterChain;
import jakarta.servlet.MultipartConfigElement;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletRequestAttributeEvent;
import jakarta.servlet.ServletRequestAttributeListener;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.SessionTrackingMode;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletMapping;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpUpgradeHandler;
import jakarta.servlet.http.Part;
import jakarta.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.EncodedSolidusHandling;
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.FileUploadException;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.impl.InvalidContentTypeException;
import org.apache.tomcat.util.http.fileupload.impl.SizeException;
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.http.parser.Upgrade;
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 String HTTP_UPGRADE_HEADER_NAME = "upgrade";
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;
}
// ------------------------------------------------------------- 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
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 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 (getDiscardFacades()) {
parameterMap = new ParameterMap<>();
} else {
parameterMap.setLocked(false);
parameterMap.clear();
}
mappingData.recycle();
applicationMapping.recycle();
applicationRequest = null;
if (getDiscardFacades()) {
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;
}
Get the recycling strategy of the facade objects.
Returns: the value of the flag as set on the connector, or
true
if no connector is associated with this request
/**
* Get the recycling strategy of the facade objects.
* @return the value of the flag as set on the connector, or
* <code>true</code> if no connector is associated with this request
*/
public boolean getDiscardFacades() {
return (connector == null) ? true : connector.getDiscardFacades();
}
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: - IOException – if an input/output error occurs
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 (!sslAttributesParsed && 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:
Globals.DISPATCHER_TYPE_ATTR
Globals.DISPATCHER_REQUEST_PATH_ATTR
Globals.ASYNC_SUPPORTED_ATTR
Globals.CERTIFICATES_ATTR
(SSL connections only)
Globals.CIPHER_SUITE_ATTR
(SSL connections only)
Globals.KEY_SIZE_ATTR
(SSL connections only)
Globals.SSL_SESSION_ID_ATTR
(SSL connections only)
Globals.SSL_SESSION_MGR_ATTR
(SSL connections only)
Globals.PARAMETER_PARSE_FAILED_ATTR
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<>(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: - IllegalStateException – if
getReader()
has
already been called for this request - IOException – if an input/output error occurs
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: - IllegalStateException – if
getInputStream()
has already been called for this request - IOException – if an input/output error occurs
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 (Object o : listeners) {
if (!(o instanceof ServletRequestAttributeListener)) {
continue;
}
ServletRequestAttributeListener listener = (ServletRequestAttributeListener) o;
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 (Object o : listeners) {
if (!(o instanceof ServletRequestAttributeListener)) {
continue;
}
ServletRequestAttributeListener listener = (ServletRequestAttributeListener) o;
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: - UnsupportedEncodingException – if the specified encoding
is not supported
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<>(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,
getUpgradeProtocolName(httpUpgradeHandlerClass));
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;
}
private String getUpgradeProtocolName(Class<? extends HttpUpgradeHandler> httpUpgradeHandlerClass) {
// Ideal - the caller has already explicitly set the selected protocol
// on the response
String result = response.getHeader(HTTP_UPGRADE_HEADER_NAME);
if (result == null) {
// If the request's upgrade header contains a single protocol that
// is the protocol that must have been selected
List<Upgrade> upgradeProtocols = Upgrade.parse(getHeaders(HTTP_UPGRADE_HEADER_NAME));
if (upgradeProtocols != null && upgradeProtocols.size() == 1) {
result = upgradeProtocols.get(0).toString();
}
}
if (result == null) {
// Ugly but use the class name - it is better than nothing
result = httpUpgradeHandlerClass.getName();
}
return result;
}
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 (connector.getEncodedSolidusHandlingInternal() == EncodedSolidusHandling.DECODE &&
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: - IllegalArgumentException – if the specified header value
cannot be converted to a date
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: - IllegalArgumentException – if the specified header value
cannot be converted to an integer
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);
}
}
@Override
public String changeSessionId() {
Session session = this.getSessionInternal(false);
if (session == null) {
throw new IllegalStateException(
sm.getString("coyoteRequest.changeSessionId"));
}
Manager manager = this.getContext().getManager();
String newSessionId = manager.rotateSessionId(session);
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 (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) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e);
} else {
log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage()));
}
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: - IOException – if an IO exception occurred
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: - IOException – if an IO exception occurred
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
}
});
specialAttributes.put(Globals.CONNECTION_ID,
new SpecialAttributeAdapter() {
@Override
public Object get(Request request, String name) {
AtomicReference<Object> result = new AtomicReference<>();
request.getCoyoteRequest().action(ActionCode.CONNECTION_ID, result);
return result.get();
}
@Override
public void set(Request request, String name, Object value) {
// NO-OP
}
});
specialAttributes.put(Globals.STREAM_ID,
new SpecialAttributeAdapter() {
@Override
public Object get(Request request, String name) {
AtomicReference<Object> result = new AtomicReference<>();
request.getCoyoteRequest().action(ActionCode.STREAM_ID, result);
return result.get();
}
@Override
public void set(Request request, String name, Object value) {
// NO-OP
}
});
}
}