| /* |
| * 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 |
| * @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. |
| * |
| * @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. |
| */ |
| protected org.apache.coyote.Request coyoteRequest; |
| |
| /** |
| * 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. |
| * |
| * @return the Coyote request object |
| */ |
| public org.apache.coyote.Request getCoyoteRequest() { |
| return this.coyoteRequest; |
| } |
| |
| |
| // ----------------------------------------------------- Variables |
| |
| /** |
| * The string manager for this package. |
| */ |
| protected static final StringManager sm = StringManager.getManager(Request.class); |
| |
| |
| /** |
| * The set of cookies associated with this Request. |
| */ |
| protected Cookie[] cookies = null; |
| |
| |
| /** |
| * The default Locale if none are specified. |
| */ |
| protected static final Locale defaultLocale = Locale.getDefault(); |
| |
| |
| /** |
| * 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 {@link Request#getAttributeNames()}. |
| */ |
| protected boolean sslAttributesParsed = false; |
| |
| |
| /** |
| * 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. |
| */ |
| private final transient HashMap<String, Object> notes = new HashMap<>(); |
| |
| |
| /** |
| * Authentication type. |
| */ |
| protected String authType = null; |
| |
| |
| /** |
| * The current dispatcher type. |
| */ |
| protected DispatcherType internalDispatcherType = null; |
| |
| |
| /** |
| * The associated input buffer. |
| */ |
| protected final InputBuffer inputBuffer = new InputBuffer(); |
| |
| |
| /** |
| * ServletInputStream. |
| */ |
| protected CoyoteInputStream inputStream = |
| new CoyoteInputStream(inputBuffer); |
| |
| |
| /** |
| * Reader. |
| */ |
| protected CoyoteReader reader = new CoyoteReader(inputBuffer); |
| |
| |
| /** |
| * Using stream flag. |
| */ |
| protected boolean usingInputStream = false; |
| |
| |
| /** |
| * Using reader flag. |
| */ |
| protected boolean usingReader = false; |
| |
| |
| /** |
| * User principal. |
| */ |
| protected Principal userPrincipal = null; |
| |
| |
| /** |
| * Request parameters parsed flag. |
| */ |
| protected boolean parametersParsed = false; |
| |
| |
| /** |
| * 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. |
| */ |
| protected boolean cookiesConverted = false; |
| |
| |
| /** |
| * Secure flag. |
| */ |
| protected boolean secure = false; |
| |
| |
| /** |
| * The Subject associated with the current AccessControlContext |
| */ |
| protected transient Subject subject = null; |
| |
| |
| /** |
| * Post data buffer. |
| */ |
| protected static final int CACHED_POST_LEN = 8192; |
| protected byte[] postData = null; |
| |
| |
| /** |
| * Hash map used in the getParametersMap method. |
| */ |
| protected ParameterMap<String, String[]> parameterMap = new ParameterMap<>(); |
| |
| |
| /** |
| * The parts, if any, uploaded with this request. |
| */ |
| protected Collection<Part> parts = null; |
| |
| |
| /** |
| * The exception thrown, if any when parsing the parts. |
| */ |
| protected Exception partsParseException = null; |
| |
| |
| /** |
| * The currently active session for this request. |
| */ |
| protected Session session = null; |
| |
| |
| /** |
| * The current request dispatcher path. |
| */ |
| protected Object requestDispatcherPath = null; |
| |
| |
| /** |
| * Was the requested session ID received in a cookie? |
| */ |
| protected boolean requestedSessionCookie = false; |
| |
| |
| /** |
| * The requested session ID (if any) for this request. |
| */ |
| protected String requestedSessionId = null; |
| |
| |
| /** |
| * Was the requested session ID received in a URL? |
| */ |
| protected boolean requestedSessionURL = false; |
| |
| |
| /** |
| * Was the requested session ID obtained from the SSL session? |
| */ |
| protected boolean requestedSessionSSL = false; |
| |
| |
| /** |
| * Parse locales. |
| */ |
| protected boolean localesParsed = false; |
| |
| |
| /** |
| * Local port |
| */ |
| protected int localPort = -1; |
| |
| /** |
| * Remote address. |
| */ |
| protected String remoteAddr = null; |
| |
| |
| /** |
| * Connection peer address. |
| */ |
| protected String peerAddr = null; |
| |
| |
| /** |
| * Remote host. |
| */ |
| protected String remoteHost = null; |
| |
| |
| /** |
| * Remote port |
| */ |
| protected int remotePort = -1; |
| |
| /** |
| * Local address |
| */ |
| protected String localAddr = null; |
| |
| |
| /** |
| * Local address |
| */ |
| protected String localName = null; |
| |
| /** |
| * 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. |
| */ |
| 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; |
| peerAddr = 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. |
| */ |
| protected final Connector connector; |
| |
| /** |
| * @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. |
| * <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. |
| * @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. |
| */ |
| protected FilterChain filterChain = null; |
| |
| /** |
| * 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. |
| * |
| * @param filterChain new filter chain |
| */ |
| public void setFilterChain(FilterChain filterChain) { |
| this.filterChain = filterChain; |
| } |
| |
| |
| /** |
| * @return the Host within which this Request is being processed. |
| */ |
| public Host getHost() { |
| return mappingData.host; |
| } |
| |
| |
| /** |
| * Mapping data. |
| */ |
| protected final MappingData mappingData = new MappingData(); |
| private final ApplicationMapping applicationMapping = new ApplicationMapping(mappingData); |
| |
| /** |
| * @return mapping data. |
| */ |
| public MappingData getMappingData() { |
| return mappingData; |
| } |
| |
| |
| /** |
| * The facade associated with this request. |
| */ |
| protected RequestFacade facade = null; |
| |
| |
| /** |
| * @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 |
| * {@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. |
| */ |
| protected org.apache.catalina.connector.Response response = null; |
| |
| /** |
| * @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. |
| * |
| * @param response The new associated response |
| */ |
| public void setResponse(org.apache.catalina.connector.Response response) { |
| this.response = response; |
| } |
| |
| /** |
| * @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. |
| */ |
| protected B2CConverter URIConverter = null; |
| |
| /** |
| * @return the URI converter. |
| */ |
| protected B2CConverter getURIConverter() { |
| return URIConverter; |
| } |
| |
| /** |
| * Set the URI converter. |
| * |
| * @param URIConverter the new URI converter |
| */ |
| protected void setURIConverter(B2CConverter URIConverter) { |
| this.URIConverter = URIConverter; |
| } |
| |
| |
| /** |
| * @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. |
| * |
| * @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. |
| * |
| * @exception IOException if an input/output error occurs |
| */ |
| public void finishRequest() throws IOException { |
| if (response.getStatus() == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE) { |
| checkSwallowInput(); |
| } |
| } |
| |
| |
| /** |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @param remoteHost The remote host name |
| */ |
| public void setRemoteHost(String remoteHost) { |
| this.remoteHost = remoteHost; |
| } |
| |
| |
| /** |
| * 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. |
| * |
| * @param port The server port |
| */ |
| public void setServerPort(int port) { |
| coyoteRequest.setServerPort(port); |
| } |
| |
| |
| // ------------------------------------------------- ServletRequest Methods |
| |
| /** |
| * @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 = coyoteRequest.getAttribute(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY); |
| if (attr != null) { |
| attributes.put(SSLSupport.REQUESTED_PROTOCOL_VERSIONS_KEY, attr); |
| } |
| attr = coyoteRequest.getAttribute(SSLSupport.REQUESTED_CIPHERS_KEY); |
| if (attr != null) { |
| attributes.put(SSLSupport.REQUESTED_CIPHERS_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 <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); |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @return the content length for this Request. |
| */ |
| @Override |
| public int getContentLength() { |
| return coyoteRequest.getContentLength(); |
| } |
| |
| |
| /** |
| * @return the content type for this Request. |
| */ |
| @Override |
| public String getContentType() { |
| return coyoteRequest.getContentType(); |
| } |
| |
| |
| /** |
| * Set the content type for this Request. |
| * |
| * @param contentType The content type |
| */ |
| public void setContentType(String contentType) { |
| coyoteRequest.setContentType(contentType); |
| } |
| |
| |
| /** |
| * @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; |
| |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @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); |
| |
| } |
| |
| |
| /** |
| * @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 <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; |
| |
| } |
| |
| |
| /** |
| * @return the names of all defined request parameters for this request. |
| */ |
| @Override |
| public Enumeration<String> getParameterNames() { |
| |
| if (!parametersParsed) { |
| parseParameters(); |
| } |
| |
| return coyoteRequest.getParameters().getParameterNames(); |
| |
| } |
| |
| |
| /** |
| * @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); |
| |
| } |
| |
| |
| /** |
| * @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 <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; |
| } |
| |
| |
| /** |
| * @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; |
| } |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @return the connection peer IP address making this Request. |
| */ |
| public String getPeerAddr() { |
| if (peerAddr == null) { |
| coyoteRequest.action(ActionCode.REQ_PEER_ADDR_ATTRIBUTE, coyoteRequest); |
| peerAddr = coyoteRequest.peerAddr().toString(); |
| } |
| return peerAddr; |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| /** |
| * @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; |
| } |
| |
| /** |
| * @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; |
| } |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| /** |
| * @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); |
| } |
| |
| |
| /** |
| * @return the scheme used to make this Request. |
| */ |
| @Override |
| public String getScheme() { |
| return coyoteRequest.scheme().toString(); |
| } |
| |
| |
| /** |
| * @return the server name responding to this Request. |
| */ |
| @Override |
| public String getServerName() { |
| return coyoteRequest.serverName().toString(); |
| } |
| |
| |
| /** |
| * @return the server port responding to this Request. |
| */ |
| @Override |
| public int getServerPort() { |
| return coyoteRequest.getServerPort(); |
| } |
| |
| |
| /** |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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 <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. |
| * |
| * @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(). |
| * |
| * @param locale The new preferred Locale |
| */ |
| public void addLocale(Locale locale) { |
| locales.add(locale); |
| } |
| |
| |
| /** |
| * 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. |
| */ |
| public void clearLocales() { |
| locales.clear(); |
| } |
| |
| |
| /** |
| * 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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @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. |
| * |
| * @param flag The new flag |
| */ |
| public void setRequestedSessionSSL(boolean flag) { |
| |
| this.requestedSessionSSL = flag; |
| |
| } |
| |
| |
| /** |
| * Get the decoded request URI. |
| * |
| * @return the URL decoded request URI |
| */ |
| public String getDecodedRequestURI() { |
| return coyoteRequest.decodedURI().toString(); |
| } |
| |
| |
| /** |
| * 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 |
| * <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. |
| */ |
| @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. |
| */ |
| @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. |
| * |
| * @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. |
| * |
| * @return the server cookies |
| */ |
| public ServerCookies getServerCookies() { |
| parseCookies(); |
| return coyoteRequest.getCookies(); |
| } |
| |
| |
| /** |
| * 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 <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. |
| * |
| * @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); |
| } |
| |
| |
| /** |
| * @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. |
| * |
| * @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(); |
| } |
| |
| |
| /** |
| * @return the HTTP request method used in this Request. |
| */ |
| @Override |
| public String getMethod() { |
| return coyoteRequest.method().toString(); |
| } |
| |
| |
| /** |
| * @return the path information associated with this Request. |
| */ |
| @Override |
| public String getPathInfo() { |
| return mappingData.pathInfo.toString(); |
| } |
| |
| |
| /** |
| * @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()); |
| } |
| |
| |
| /** |
| * @return the query string associated with this request. |
| */ |
| @Override |
| public String getQueryString() { |
| return coyoteRequest.queryString().toString(); |
| } |
| |
| |
| /** |
| * @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. |
| * |
| * @return the request path |
| */ |
| public MessageBytes getRequestPathMB() { |
| return mappingData.requestPath; |
| } |
| |
| |
| /** |
| * @return the session identifier included in this request, if any. |
| */ |
| @Override |
| public String getRequestedSessionId() { |
| return requestedSessionId; |
| } |
| |
| |
| /** |
| * @return the request URI for this request. |
| */ |
| @Override |
| public String getRequestURI() { |
| return coyoteRequest.requestURI().toString(); |
| } |
| |
| |
| @Override |
| public StringBuffer getRequestURL() { |
| return RequestUtil.getRequestURL(this); |
| } |
| |
| |
| /** |
| * @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(); |
| } |
| |
| |
| /** |
| * @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(); |
| } |
| |
| |
| /** |
| * @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(); |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @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(); |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @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); |
| } |
| |
| |
| /** |
| * @return the principal that has been authenticated for this Request. |
| */ |
| public Principal getPrincipal() { |
| return userPrincipal; |
| } |
| |
| |
| /** |
| * @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; |
| } |
| |
| |
| /** |
| * @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. |
| * |
| * @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; |
| } |
| |
| /** |
| * @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); |
| } |
| |
| |
| /** |
| * @return <code>true</code> if we have parsed parameters |
| */ |
| public boolean isParametersParsed() { |
| return parametersParsed; |
| } |
| |
| |
| /** |
| * @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. |
| */ |
| protected void checkSwallowInput() { |
| Context context = getContext(); |
| if (context != null && !context.getSwallowAbortedUploads()) { |
| coyoteRequest.action(ActionCode.DISABLE_SWALLOW_INPUT, null); |
| } |
| } |
| |
| /** |
| * {@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} |
| */ |
| @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} |
| */ |
| @Override |
| public void logout() throws ServletException { |
| getContext().getAuthenticator().logout(this); |
| } |
| |
| /** |
| * {@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} |
| */ |
| @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. |
| */ |
| 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. |
| */ |
| 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. |
| */ |
| 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. |
| * |
| * @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. |
| * |
| * @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. |
| */ |
| 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. |
| * |
| * @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 |
| } |
| }); |
| } |
| } |