Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/Java/Tomcat/java/org/apache/catalina/connector/   (Apache Software Stiftung Version 2.4.65©)  Datei vom 10.10.2023 mit Größe 110 kB image not shown  

Quelle  Request.java   Sprache: JAVA

 
/*
 * 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.EOFException;
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 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.ServletConnection;
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.Rfc6265CookieProcessor;
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.FileUpload;
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.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 (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.warn(sm.getString("coyoteRequest.deletePartFailed", part.getName()), t);
                }
            }
            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 Response response = null;

    /**
     * @return the Response with which this Request is associated.
     */

    public Response getResponse() {
        return this.response;
    }

    /**
     * Set the Response with which this Request is associated.
     *
     * @param response The new associated response
     */

    public void setResponse(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().toStringType();
    }


    /**
     * 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 context
            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 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().toStringType();
    }


    /**
     * @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;
    }


    @Override
    public String getRequestId() {
        return coyoteRequest.getRequestId();
    }


    @Override
    public String getProtocolRequestId() {
        return coyoteRequest.getProtocolRequestId();
    }


    @Override
    public ServletConnection getServletConnection() {
        return coyoteRequest.getServletConnection();
    }


    // ---------------------------------------------------- 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 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
     *
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=99 G=97

¤ Dauer der Verarbeitung: 0.69 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.