All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.catalina.connector.Request Maven / Gradle / Ivy

There is a newer version: 11.0.0-M26
Show newest version
/*
 * 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.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.security.auth.Subject;
import javax.servlet.AsyncContext;
import javax.servlet.DispatcherType;
import javax.servlet.FilterChain;
import javax.servlet.MultipartConfigElement;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.ServletResponse;
import javax.servlet.SessionTrackingMode;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.http.Part;

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.Wrapper;
import org.apache.catalina.core.ApplicationPart;
import org.apache.catalina.core.ApplicationSessionCookieConfig;
import org.apache.catalina.core.AsyncContextImpl;
import org.apache.catalina.realm.GenericPrincipal;
import org.apache.catalina.util.ParameterMap;
import org.apache.catalina.util.StringParser;
import org.apache.coyote.ActionCode;
import org.apache.coyote.http11.upgrade.UpgradeInbound;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.buf.B2CConverter;
import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes;
import org.apache.tomcat.util.http.Cookies;
import org.apache.tomcat.util.http.FastHttpDateFormat;
import org.apache.tomcat.util.http.Parameters;
import org.apache.tomcat.util.http.ServerCookie;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.FileUploadBase;
import org.apache.tomcat.util.http.fileupload.FileUploadBase.InvalidContentTypeException;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.apache.tomcat.util.http.mapper.MappingData;
import org.apache.tomcat.util.res.StringManager;


/**
 * Wrapper object for the Coyote request.
 *
 * @author Remy Maucherat
 * @author Craig R. McClanahan
 * @version $Id: Request.java 1356509 2012-07-02 22:43:32Z kkolinko $
 */

public class Request
    implements HttpServletRequest {

    private static final Log log = LogFactory.getLog(Request.class);

    // ----------------------------------------------------------- Constructors


    public Request() {

        formats[0].setTimeZone(GMT_ZONE);
        formats[1].setTimeZone(GMT_ZONE);
        formats[2].setTimeZone(GMT_ZONE);

    }


    // ------------------------------------------------------------- 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.
     */
    public org.apache.coyote.Request getCoyoteRequest() {
        return (this.coyoteRequest);
    }


    // ----------------------------------------------------- Variables


    protected static final TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");


    /**
     * The string manager for this package.
     */
    protected static final StringManager sm =
        StringManager.getManager(Constants.Package);


    /**
     * The set of cookies associated with this Request.
     */
    protected Cookie[] cookies = null;


    /**
     * The set of SimpleDateFormat formats to use in getDateHeader().
     *
     * Notice that because SimpleDateFormat is not thread-safe, we can't
     * declare formats[] as a static variable.
     */
    protected SimpleDateFormat formats[] = {
        new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US),
        new SimpleDateFormat("EEEEEE, dd-MMM-yy HH:mm:ss zzz", Locale.US),
        new SimpleDateFormat("EEE MMMM d HH:mm:ss yyyy", Locale.US)
    };


    /**
     * The default Locale if none are specified.
     */
    protected static Locale defaultLocale = Locale.getDefault();


    /**
     * The attributes associated with this Request, keyed by attribute name.
     */
    protected HashMap attributes =
        new HashMap();


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

    /**
     * List of read only attributes for this Request.
     */
    private final HashMap readOnlyAttributes =
        new HashMap();


    /**
     * The preferred Locales associated with this Request.
     */
    protected ArrayList locales = new ArrayList();


    /**
     * Internal notes associated with this request by Catalina components
     * and event listeners.
     */
    private transient HashMap notes = new HashMap();


    /**
     * Authentication type.
     */
    protected String authType = null;


    /**
     * Associated event.
     */
    protected CometEventImpl event = null;


    /**
     * Comet state
     */
    protected boolean comet = false;


    /**
     * The current dispatcher type.
     */
    protected DispatcherType internalDispatcherType = null;


    /**
     * The associated input buffer.
     */
    protected 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 writer flag.
     */
    protected boolean usingReader = false;


    /**
     * User principal.
     */
    protected Principal userPrincipal = null;


    /**
     * Session parsed flag.
     */
    @Deprecated
    protected boolean sessionParsed = false;


    /**
     * Request parameters parsed flag.
     */
    protected boolean parametersParsed = false;


    /**
     * Cookies parsed flag.
     */
    protected boolean cookiesParsed = false;


    /**
     * Secure flag.
     */
    protected boolean secure = false;


    /**
     * The Subject associated with the current AccessControllerContext
     */
    protected transient Subject subject = null;


    /**
     * Post data buffer.
     */
    protected static int CACHED_POST_LEN = 8192;
    protected byte[] postData = null;


    /**
     * Hash map used in the getParametersMap method.
     */
    protected ParameterMap parameterMap = new ParameterMap();


    /**
     * The parts, if any, uploaded with this request.
     */
    protected Collection 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;


    /**
     * The string parser we will use for parsing request lines.
     */
    private final StringParser parser = new StringParser();


    /**
     * Local port
     */
    protected int localPort = -1;

    /**
     * Remote address.
     */
    protected String remoteAddr = 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
     */
    protected volatile AsyncContextImpl asyncContext = null;

    protected Boolean asyncSupported = null;


    /**
     * Path parameters
     */
    protected Map pathParameters = new HashMap();

    // --------------------------------------------------------- Public Methods


    protected void addPathParameter(String name, String value) {
        pathParameters.put(name, value);
    }

    protected String getPathParameter(String name) {
        return pathParameters.get(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() {

        context = null;
        wrapper = null;

        internalDispatcherType = null;
        requestDispatcherPath = null;

        comet = false;
        if (event != null) {
            event.clear();
            event = null;
        }

        authType = null;
        inputBuffer.recycle();
        usingInputStream = false;
        usingReader = false;
        userPrincipal = null;
        subject = null;
        sessionParsed = false;
        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;
        cookiesParsed = false;
        locales.clear();
        localesParsed = false;
        secure = false;
        remoteAddr = null;
        remoteHost = null;
        remotePort = -1;
        localPort = -1;
        localAddr = null;
        localName = null;

        attributes.clear();
        sslAttributesParsed = false;
        notes.clear();
        cookies = null;

        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;

        if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
            parameterMap = new ParameterMap();
        } else {
            parameterMap.setLocked(false);
            parameterMap.clear();
        }

        mappingData.recycle();

        if (Globals.IS_SECURITY_ENABLED || Connector.RECYCLE_FACADES) {
            if (facade != null) {
                facade.clear();
                facade = null;
            }
            if (inputStream != null) {
                inputStream.clear();
                inputStream = null;
            }
            if (reader != null) {
                reader.clear();
                reader = null;
            }
        }

        asyncSupported = null;
        if (asyncContext!=null) {
            asyncContext.recycle();
        }
        asyncContext = null;

        pathParameters.clear();
    }

    @Deprecated
    protected boolean isProcessing() {
        return coyoteRequest.isProcessing();
    }

    /**
     * Clear cached encoders (to save memory for Comet requests).
     */
    public void clearEncoders() {
        inputBuffer.clearEncoders();
    }


    /**
     * Clear cached encoders (to save memory for Comet requests).
     */
    public boolean read()
        throws IOException {
        return (inputBuffer.realReadBytes(null, 0, 0) > 0);
    }


    // -------------------------------------------------------- Request Methods


    /**
     * Associated Catalina connector.
     */
    protected Connector connector;

    /**
     * Return the Connector through which this Request was received.
     */
    public Connector getConnector() {
        return this.connector;
    }

    /**
     * Set the Connector through which this Request was received.
     *
     * @param connector The new connector
     */
    public void setConnector(Connector connector) {
        this.connector = connector;
    }


    /**
     * Associated context.
     */
    protected Context context = null;

    /**
     * Return the Context within which this Request is being processed.
     */
    public Context getContext() {
        return this.context;
    }


    /**
     * Set the Context within which this Request is being processed.  This
     * must be called as soon as the appropriate Context is identified, because
     * it identifies the value to be returned by getContextPath(),
     * and thus enables parsing of the request URI.
     *
     * @param context The newly associated Context
     */
    public void setContext(Context context) {
        this.context = context;
    }


    /**
     * Filter chain associated with the request.
     */
    protected FilterChain filterChain = null;

    /**
     * Get filter chain associated with the request.
     */
    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 ((Host) mappingData.host);
    }


    /**
     * Set the Host within which this Request is being processed.  This
     * must be called as soon as the appropriate Host is identified, and
     * before the Request is passed to a context.
     *
     * @param host The newly associated Host
     */
    @Deprecated
    public void setHost(Host host) {
        mappingData.host = host;
    }


    /**
     * Descriptive information about this Request implementation.
     */
    protected static final String info =
        "org.apache.coyote.catalina.CoyoteRequest/1.0";

    /**
     * Return descriptive information about this Request implementation and
     * the corresponding version number, in the format
     * <description>/<version>.
     */
    public String getInfo() {
        return info;
    }


    /**
     * Mapping data.
     */
    protected MappingData mappingData = new MappingData();

    /**
     * Return mapping data.
     */
    public MappingData getMappingData() {
        return mappingData;
    }


    /**
     * The facade associated with this request.
     */
    protected RequestFacade facade = null;

    /**
     * Return the ServletRequest 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);
        }
        return facade;
    }


    /**
     * 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 (not recycled).
     */
    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;
    }


    /**
     * Associated wrapper.
     */
    protected Wrapper wrapper = null;

    /**
     * Return the Wrapper within which this Request is being processed.
     */
    public Wrapper getWrapper() {
        return this.wrapper;
    }


    /**
     * Set the Wrapper within which this Request is being processed.  This
     * must be called as soon as the appropriate Wrapper is identified, and
     * before the Request is ultimately passed to an application servlet.
     * @param wrapper The newly associated Wrapper
     */
    public void setWrapper(Wrapper wrapper) {
        this.wrapper = wrapper;
    }


    // ------------------------------------------------- Request Public Methods


    /**
     * Create and return a ServletInputStream to read the content
     * associated with this Request.
     *
     * @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 {
        // Optionally disable swallowing of additional request data.
        Context context = getContext();
        if (context != null &&
                response.getStatus() == HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE &&
                !context.getSwallowAbortedUploads()) {
            coyoteRequest.action(ActionCode.DISABLE_SWALLOW_INPUT, null);
        }
    }


    /**
     * Return the object bound with the specified name to the internal notes
     * for this request, or null if no such binding exists.
     *
     * @param name Name of the note to be returned
     */
    public Object getNote(String name) {
        return notes.get(name);
    }


    /**
     * Return an Iterator containing the String names of all notes bindings
     * that exist for this request.
     */
    @Deprecated
    public Iterator getNoteNames() {
        return notes.keySet().iterator();
    }


    /**
     * 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 isSecure()
     * for this Request.
     *
     * @param secure The new isSecure value
     */
    public void setSecure(boolean secure) {
        this.secure = secure;
    }


    /**
     * Set the name of the server (virtual host) to process this request.
     *
     * @param name The server name
     */
    @Deprecated
    public void setServerName(String name) {
        coyoteRequest.serverName().setString(name);
    }


    /**
     * 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
     * null.
     *
     * @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( isSSLAttribute(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);
                attributes.put(Globals.SSL_SESSION_ID_TOMCAT_ATTR, attr);
            }
            attr = coyoteRequest.getAttribute(Globals.SSL_SESSION_MGR_ATTR);
            if(attr != null) {
                attributes.put(Globals.SSL_SESSION_MGR_ATTR, attr);
            }
            attr = attributes.get(name);
            sslAttributesParsed = true;
        }
        return attr;
    }


    /**
     * Test if a given name is one of the special Servlet-spec SSL attributes.
     */
    static boolean isSSLAttribute(String name) {
        return Globals.CERTIFICATES_ATTR.equals(name) ||
            Globals.CIPHER_SUITE_ATTR.equals(name) ||
            Globals.KEY_SIZE_ATTR.equals(name)  ||
            Globals.SSL_SESSION_ID_ATTR.equals(name) ||
            Globals.SSL_SESSION_ID_TOMCAT_ATTR.equals(name) ||
            Globals.SSL_SESSION_MGR_ATTR.equals(name);
    }

    /**
     * Return the names of all request attributes for this Request, or an
     * empty Enumeration if there are none. Note that the attribute
     * names returned will only be those for the attributes set via
     * {@link #setAttribute(String, Object)}. Tomcat internal attributes will
     * not be included although they are accessible via
     * {@link #getAttribute(String)}. The Tomcat internal attributes include:
     * 
    *
  • {@link Globals#DISPATCHER_TYPE_ATTR}
  • *
  • {@link Globals#DISPATCHER_REQUEST_PATH_ATTR}
  • *
  • {@link Globals#ASYNC_SUPPORTED_ATTR}
  • *
  • {@link Globals#CERTIFICATES_ATTR} (SSL connections only)
  • *
  • {@link Globals#CIPHER_SUITE_ATTR} (SSL connections only)
  • *
  • {@link Globals#KEY_SIZE_ATTR} (SSL connections only)
  • *
  • {@link Globals#SSL_SESSION_ID_ATTR} (SSL connections only)
  • *
  • {@link Globals#SSL_SESSION_ID_TOMCAT_ATTR} (SSL connections only) *
  • *
  • {@link Globals#SSL_SESSION_MGR_ATTR} (SSL connections only)
  • *
  • {@link Globals#PARAMETER_PARSE_FAILED_ATTR}
  • *
* The underlying connector may also expose request attributes. These all * have names starting with "org.apache.tomcat" and include: *
    *
  • {@link Globals#SENDFILE_SUPPORTED_ATTR}
  • *
  • {@link Globals#COMET_SUPPORTED_ATTR}
  • *
  • {@link Globals#COMET_TIMEOUT_SUPPORTED_ATTR}
  • *
* Connector implementations may return some, all or none of these * attributes and may also support additional attributes. */ @Override public Enumeration getAttributeNames() { if (isSecure() && !sslAttributesParsed) { getAttribute(Globals.CERTIFICATES_ATTR); } // Take a copy to prevent ConncurrentModificationExceptions if used to // remove attributes Set names = new HashSet(); names.addAll(attributes.keySet()); return Collections.enumeration(names); } /** * Return the character encoding for this Request. */ @Override public String getCharacterEncoding() { return coyoteRequest.getCharacterEncoding(); } /** * 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(); } /** * Return the servlet input stream for this Request. The default * implementation returns a servlet input stream created by * createInputStream(). * * @exception IllegalStateException if getReader() 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 Accept-Language 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 Accept-Language * headers that were encountered. If the request did not specify a * preferred language, the server's default Locale is returned. */ @Override public Enumeration getLocales() { if (!localesParsed) { parseLocales(); } if (locales.size() > 0) { return Collections.enumeration(locales); } ArrayList results = new ArrayList(); results.add(defaultLocale); return Collections.enumeration(results); } /** * Return the value of the specified request parameter, if any; otherwise, * return null. If there is more than one value defined, * return only the first one. * * @param name Name of the desired request parameter */ @Override public String getParameter(String name) { if (!parametersParsed) { parseParameters(); } return coyoteRequest.getParameters().getParameter(name); } /** * Returns a Map of the parameters of this request. * Request parameters are extra information sent with the request. * For HTTP servlets, parameters are contained in the query string * or posted form data. * * @return A Map containing parameter names as keys * and parameter values as map values. */ @Override public Map getParameterMap() { if (parameterMap.isLocked()) { return parameterMap; } Enumeration 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 getParameterNames() { if (!parametersParsed) { parseParameters(); } return coyoteRequest.getParameters().getParameterNames(); } /** * Return the defined values for the specified request parameter, if any; * otherwise, return null. * * @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 BufferedReader around the * servlet input stream returned by createInputStream(). * * @exception IllegalStateException if getInputStream() * 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")); } 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 * ServletContext.getRealPath(). */ @Override @Deprecated public String getRealPath(String path) { 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 remote host name making this Request. */ @Override public String getRemoteHost() { if (remoteHost == null) { if (!connector.getEnableLookups()) { remoteHost = getRemoteAddr(); } else { coyoteRequest.action (ActionCode.REQ_HOST_ATTRIBUTE, coyoteRequest); remoteHost = coyoteRequest.remoteHost().toString(); } } return remoteHost; } /** * Returns the Internet Protocol (IP) source port of the client * or last proxy that sent the request. */ @Override public int getRemotePort(){ if (remotePort == -1) { coyoteRequest.action (ActionCode.REQ_REMOTEPORT_ATTRIBUTE, coyoteRequest); remotePort = coyoteRequest.getRemotePort(); } return remotePort; } /** * Returns the host name of the Internet Protocol (IP) interface on * which the request was received. */ @Override public String getLocalName(){ if (localName == null) { coyoteRequest.action (ActionCode.REQ_LOCAL_NAME_ATTRIBUTE, coyoteRequest); localName = coyoteRequest.localName().toString(); } return localName; } /** * Returns the Internet Protocol (IP) address of the interface on * which the request was received. */ @Override public String getLocalAddr(){ if (localAddr == null) { coyoteRequest.action (ActionCode.REQ_LOCAL_ADDR_ATTRIBUTE, coyoteRequest); localAddr = coyoteRequest.localAddr().toString(); } return localAddr; } /** * Returns the Internet Protocol (IP) port number of the interface * on which the request was received. */ @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) { if (context == null) { return null; } // If the path is already context-relative, just pass it through if (path == null) { return null; } else if (path.startsWith("/")) { return (context.getServletContext().getRequestDispatcher(path)); } // 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 (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(); } /** * Was this request 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 // Check for read only attribute // requests are per thread so synchronization unnecessary if (readOnlyAttributes.containsKey(name)) { return; } // 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); } else { return; } } /** * 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 // Check for read only attribute // requests are per thread so synchronization unnecessary if (readOnlyAttributes.containsKey(name)) { return; } // 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. */ private void notifyAttributeAssigned(String name, Object value, Object oldValue) { Object listeners[] = context.getApplicationEventListeners(); if ((listeners == null) || (listeners.length == 0)) { return; } boolean replaced = (oldValue != null); ServletRequestAttributeEvent event = null; if (replaced) { event = new ServletRequestAttributeEvent( context.getServletContext(), getRequest(), name, oldValue); } else { event = new ServletRequestAttributeEvent( context.getServletContext(), getRequest(), name, value); } for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof ServletRequestAttributeListener)) { continue; } ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners[i]; try { if (replaced) { listener.attributeReplaced(event); } else { listener.attributeAdded(event); } } catch (Throwable t) { ExceptionUtils.handleThrowable(t); context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t); // Error valve will pick this exception up and display it to user attributes.put(RequestDispatcher.ERROR_EXCEPTION, t); } } } /** * Notify interested listeners that attribute has been removed. */ private void notifyAttributeRemoved(String name, Object value) { Object listeners[] = context.getApplicationEventListeners(); if ((listeners == null) || (listeners.length == 0)) { return; } ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(context.getServletContext(), getRequest(), name, value); for (int i = 0; i < listeners.length; i++) { if (!(listeners[i] instanceof ServletRequestAttributeListener)) { continue; } ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners[i]; try { listener.attributeRemoved(event); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); context.getLogger().error(sm.getString("coyoteRequest.attributeEvent"), t); // Error valve will pick this exception up and display it to user attributes.put(RequestDispatcher.ERROR_EXCEPTION, t); } } } /** * Overrides the name of the character encoding used in the body of * this request. This method must be called prior to reading request * parameters or reading input using getReader(). * * @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; } // Ensure that the specified encoding is valid byte buffer[] = new byte[1]; buffer[0] = (byte) 'a'; // Confirm that the encoding name is valid B2CConverter.getCharset(enc); // Save the validated encoding coyoteRequest.setCharacterEncoding(enc); } @Override public ServletContext getServletContext() { return context.getServletContext(); } @Override public AsyncContext startAsync() { return startAsync(getRequest(),response.getResponse()); } @Override public AsyncContext startAsync(ServletRequest request, ServletResponse response) { if (!isAsyncSupported()) { throw new IllegalStateException("Not supported."); } if (asyncContext == null) { asyncContext = new AsyncContextImpl(this); } asyncContext.setStarted(getContext(), request, response, request==getRequest() && response==getResponse().getResponse()); asyncContext.setTimeout(getConnector().getAsyncTimeout()); return asyncContext; } @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 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() { return this.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 (!cookiesParsed) { parseCookies(); } int size = 0; if (cookies != null) { size = cookies.length; } Cookie[] newCookies = new Cookie[size + 1]; for (int i = 0; i < size; i++) { newCookies[i] = cookies[i]; } 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); } /** * Add a parameter name and corresponding set of values to this Request. * (This is used when restoring the original request on a form based * login). * * @param name Name of this request parameter * @param values Corresponding values for this request parameter */ @Deprecated public void addParameter(String name, String values[]) { coyoteRequest.getParameters().addParameterValues(name, values); } /** * Clear the collection of Cookies associated with this Request. */ public void clearCookies() { cookiesParsed = true; cookies = null; } /** * Clear the collection of Headers associated with this Request. */ @Deprecated public void clearHeaders() { // Not used } /** * Clear the collection of Locales associated with this Request. */ public void clearLocales() { locales.clear(); } /** * Clear the collection of parameters associated with this Request. */ @Deprecated public void clearParameters() { // Not used } /** * Set the authentication type used for this request, if any; otherwise * set the type to null. Typical values are "BASIC", * "DIGEST", or "SSL". * * @param type The authentication type used */ public void setAuthType(String type) { this.authType = type; } /** * Set the context path for this Request. This will normally be called * when the associated Context is mapping the Request to a particular * Wrapper. * * @param path The context path */ @Deprecated public void setContextPath(String path) { if (path == null) { mappingData.contextPath.setString(""); } else { mappingData.contextPath.setString(path); } } /** * 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 */ @Deprecated public MessageBytes getDecodedRequestURIMB() { return coyoteRequest.decodedURI(); } /** * Set the servlet path for this Request. This will normally be called * when the associated Context is mapping the Request to a particular * Wrapper. * * @param path The servlet path */ @Deprecated public void setServletPath(String path) { if (path != null) { mappingData.wrapperPath.setString(path); } } /** * Set the Principal who has been authenticated for this Request. This * value is also used to calculate the value to be returned by the * getRemoteUser() method. * * @param principal The user Principal */ public void setUserPrincipal(Principal principal) { if (Globals.IS_SECURITY_ENABLED){ HttpSession session = getSession(false); if ( (subject != null) && (!subject.getPrincipals().contains(principal)) ){ subject.getPrincipals().add(principal); } else if (session != null && session.getAttribute(Globals.SUBJECT_ATTR) == null) { subject = new Subject(); subject.getPrincipals().add(principal); } if (session != null){ session.setAttribute(Globals.SUBJECT_ATTR, subject); } } this.userPrincipal = principal; } // --------------------------------------------- HttpServletRequest Methods /** * 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. */ @Override public String getContextPath() { return mappingData.contextPath.toString(); } /** * Get the context path. * * @return the context path */ @Deprecated public MessageBytes getContextPathMB() { return mappingData.contextPath; } /** * Return the set of Cookies received with this Request. */ @Override public Cookie[] getCookies() { if (!cookiesParsed) { parseCookies(); } return cookies; } /** * Set the set of cookies received with this Request. */ @Deprecated public void setCookies(Cookie[] cookies) { this.cookies = cookies; } /** * Return the value of the specified date header, if any; otherwise * return -1. * * @param name Name of the requested date header * * @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, formats); if (result != (-1L)) { return result; } throw new IllegalArgumentException(value); } /** * Return the first value of the specified header, if any; otherwise, * return null * * @param name Name of the requested header */ @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 */ @Override public Enumeration getHeaders(String name) { return coyoteRequest.getMimeHeaders().values(name); } /** * Return the names of all headers received with this request. */ @Override public Enumeration 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 * * @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); } /** * 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(); } /** * Get the path info. * * @return the path info */ @Deprecated public MessageBytes getPathInfoMB() { return mappingData.pathInfo; } /** * Return the extra path information for this request, translated * to a real path. */ @Override public String getPathTranslated() { 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(); } /** * Reconstructs the URL the client used to make the request. * The returned URL contains a protocol, server name, port * number, and server path, but it does not include query * string parameters. *

* Because this method returns a StringBuffer, * not a String, you can modify the URL easily, * for example, to append query parameters. *

* This method is useful for creating redirect messages and * for reporting errors. * * @return A StringBuffer object containing the * reconstructed URL */ @Override public StringBuffer getRequestURL() { StringBuffer url = new StringBuffer(); String scheme = getScheme(); int port = getServerPort(); if (port < 0) { port = 80; // Work around java.net.URL bug } url.append(scheme); url.append("://"); url.append(getServerName()); if ((scheme.equals("http") && (port != 80)) || (scheme.equals("https") && (port != 443))) { url.append(':'); url.append(port); } url.append(getRequestURI()); return url; } /** * 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()); } /** * Get the servlet path. * * @return the servlet path */ @Deprecated public MessageBytes getServletPathMB() { return mappingData.wrapperPath; } /** * 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 true if the session identifier included in this * request came from a cookie. */ @Override public boolean isRequestedSessionIdFromCookie() { if (requestedSessionId == null) { return false; } return requestedSessionCookie; } /** * Return true 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 true if the session identifier included in this * request came from the request URI. * * @deprecated As of Version 2.1 of the Java Servlet API, use * isRequestedSessionIdFromURL() instead. */ @Override @Deprecated public boolean isRequestedSessionIdFromUrl() { return (isRequestedSessionIdFromURL()); } /** * Return true if the session identifier included in this * request identifies a valid session. */ @Override public boolean isRequestedSessionIdValid() { if (requestedSessionId == null) { return false; } 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 = (Context) getMappingData().contexts[i - 1]; try { if (ctxt.getManager().findSession(requestedSessionId) != null) { return true; } } catch (IOException e) { // Ignore } } return false; } } return true; } /** * Return true 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 if (context == null) { return false; } Realm realm = context.getRealm(); if (realm == null) { return false; } // Check for a role defined directly as a return (realm.hasRole(wrapper, 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 GenericPrincipal) { return ((GenericPrincipal) 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; } if (context != null && !context.getServletContext() .getEffectiveSessionTrackingModes().contains( SessionTrackingMode.COOKIE)) { return; } if (response != null) { Cookie newCookie = ApplicationSessionCookieConfig.createSessionCookie(context, newSessionId, secure); response.addSessionCookieInternal(newCookie); } } /** * 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); } /** * Get the event associated with the request. * @return the event */ public CometEventImpl getEvent() { if (event == null) { event = new CometEventImpl(this, response); } return event; } /** * Return true if the current request is handling Comet traffic. */ public boolean isComet() { return comet; } /** * Set comet state. */ public void setComet(boolean comet) { this.comet = comet; } /** * return true if we have parsed parameters */ public boolean isParametersParsed() { return parametersParsed; } /** * Return true if bytes are available. */ public boolean getAvailable() { return (inputBuffer.available() > 0); } /** * Disable swallowing of remaining input if configured */ protected void checkSwallowInput() { Context context = getContext(); if (context != null && !context.getSwallowAbortedUploads()) { coyoteRequest.action(ActionCode.DISABLE_SWALLOW_INPUT, null); } } public void cometClose() { coyoteRequest.action(ActionCode.COMET_CLOSE,getEvent()); setComet(false); } public void setCometTimeout(long timeout) { coyoteRequest.action(ActionCode.COMET_SETTIMEOUT, Long.valueOf(timeout)); } /** * Not part of Servlet 3 spec but probably should be. * @return true if the requested session ID was obtained from the SSL session */ @Deprecated public boolean isRequestedSessionIdFromSSL() { return requestedSessionSSL; } /** * @throws IOException If an I/O error occurs * @throws IllegalStateException If the response has been committed * @throws ServletException If the caller is responsible for handling the * error and the container has NOT set the HTTP response code etc. */ @Override public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { if (response.isCommitted()) { throw new IllegalStateException( sm.getString("coyoteRequest.authenticate.ise")); } return context.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")); } if (context.getAuthenticator() == null) { throw new ServletException("no authenticator"); } context.getAuthenticator().login(username, password, this); } /** * {@inheritDoc} */ @Override public void logout() throws ServletException { context.getAuthenticator().logout(this); } /** * {@inheritDoc} */ @Override public Collection getParts() throws IOException, IllegalStateException, ServletException { parseParts(); 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() { // Return immediately if the parts have already been parsed if (parts != null || partsParseException != null) { return; } MultipartConfigElement mce = getWrapper().getMultipartConfigElement(); if (mce == null) { if(getContext().getAllowCasualMultipartParsing()) { mce = new MultipartConfigElement(null, connector.getMaxPostSize(), connector.getMaxPostSize(), connector.getMaxPostSize()); } 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.isDirectory()) { 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) { 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 items = upload.parseRequest(this); int maxPostSize = getConnector().getMaxPostSize(); int postSize = 0; String enc = getCharacterEncoding(); Charset charset = null; if (enc != null) { try { charset = B2CConverter.getCharset(enc); } catch (UnsupportedEncodingException e) { // Ignore } } for (FileItem item : items) { ApplicationPart part = new ApplicationPart(item, mce); parts.add(part); if (part.getFilename() == null) { String name = part.getName(); String value = null; try { String encoding = parameters.getEncoding(); if (encoding == null) { encoding = Parameters.DEFAULT_ENCODING; } value = part.getString(encoding); } catch (UnsupportedEncodingException uee) { try { value = part.getString(Parameters.DEFAULT_ENCODING); } catch (UnsupportedEncodingException e) { // Should not be possible } } if (maxPostSize > 0) { // Have to calculate equivalent size. Not completely // accurate but close enough. if (charset == null) { // Name length postSize += name.getBytes().length; } else { postSize += name.getBytes(charset).length; } if (value != null) { // Equals sign postSize++; // Value length postSize += part.getSize(); } // Value separator postSize++; if (postSize > maxPostSize) { throw new IllegalStateException(sm.getString( "coyoteRequest.maxPostSizeExceeded")); } } parameters.addParameter(name, value); } } success = true; } catch (InvalidContentTypeException e) { partsParseException = new ServletException(e); } catch (FileUploadBase.SizeException e) { checkSwallowInput(); partsParseException = new IllegalStateException(e); } catch (FileUploadException e) { partsParseException = new IOException(e); } catch (IllegalStateException e) { checkSwallowInput(); partsParseException = e; } } finally { if (partsParseException != null || !success) { parameters.setParseFailed(true); } } } /** * {@inheritDoc} */ @Override public Part getPart(String name) throws IOException, IllegalStateException, ServletException { Collection c = getParts(); Iterator iterator = c.iterator(); while (iterator.hasNext()) { Part part = iterator.next(); if (name.equals(part.getName())) { return part; } } return null; } // --------------------------------------------------------- Upgrade Methods public void doUpgrade(UpgradeInbound inbound) throws IOException { coyoteRequest.action(ActionCode.UPGRADE, inbound); // Output required by RFC2616. Protocol specific headers should have // already been set. response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS); response.flushBuffer(); } // ------------------------------------------------------ Protected Methods protected Session doGetSession(boolean create) { // There cannot be a session if no context has been assigned yet 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 = null; if (context != null) { manager = context.getManager(); } if (manager == null) { return (null); // Sessions are not supported } if (requestedSessionId != null) { try { session = manager.findSession(requestedSessionId); } catch (IOException e) { session = null; } if ((session != null) && !session.isValid()) { session = null; } if (session != null) { session.access(); return (session); } } // Create a new session if requested and the response is not committed if (!create) { return (null); } if ((context != null) && (response != null) && context.getServletContext().getEffectiveSessionTrackingModes(). contains(SessionTrackingMode.COOKIE) && response.getResponse().isCommitted()) { throw new IllegalStateException (sm.getString("coyoteRequest.sessionCreateCommitted")); } // Attempt to reuse session id if one was submitted in a cookie // Do not reuse the session id if it is from a URL, to prevent possible // phishing attacks // Use the SSL session ID if one is present. if (("/".equals(context.getSessionCookiePath()) && isRequestedSessionIdFromCookie()) || requestedSessionSSL ) { session = manager.createSession(getRequestedSessionId()); } else { session = manager.createSession(null); } // Creating a new session cookie based on that session if ((session != null) && (getContext() != null) && getContext().getServletContext(). getEffectiveSessionTrackingModes().contains( SessionTrackingMode.COOKIE)) { 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()) { throw new IllegalArgumentException();//invalid escape, hence invalid cookie } c = s.charAt(i); buf.append(c); } } return buf.toString(); } /** * Parse cookies. */ protected void parseCookies() { cookiesParsed = true; Cookies serverCookies = coyoteRequest.getCookies(); 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); 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 String enc = getCharacterEncoding(); boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI(); if (enc != null) { parameters.setEncoding(enc); if (useBodyEncodingForURI) { parameters.setQueryStringEncoding(enc); } } else { parameters.setEncoding (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING); if (useBodyEncodingForURI) { parameters.setQueryStringEncoding (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING); } } parameters.handleQueryParameters(); if (usingInputStream || usingReader) { success = true; return; } if( !getConnector().isParseBodyMethod(getMethod()) ) { 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(); 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)) { if (context.getLogger().isDebugEnabled()) { context.getLogger().debug( sm.getString("coyoteRequest.postTooLarge")); } checkSwallowInput(); 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) { return; } } catch (IOException e) { // Client disconnect if (context.getLogger().isDebugEnabled()) { context.getLogger().debug( sm.getString("coyoteRequest.parseParameters"), e); } return; } parameters.processParameters(formData, 0, len); } else if ("chunked".equalsIgnoreCase( coyoteRequest.getHeader("transfer-encoding"))) { byte[] formData = null; try { formData = readChunkedPostBody(); } catch (IOException e) { // Client disconnect or chunkedPostTooLarge error if (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.setParseFailed(true); } } } /** * Read post body in an array. */ 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. */ 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 IOException( 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; Enumeration values = getHeaders("accept-language"); while (values.hasMoreElements()) { String value = values.nextElement(); parseLocalesHeader(value); } } /** * Parse accept-language header value. */ protected void parseLocalesHeader(String value) { // 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> locales = new TreeMap>(); // Preprocess the value to remove all whitespace int white = value.indexOf(' '); if (white < 0) { white = value.indexOf('\t'); } if (white >= 0) { StringBuilder sb = new StringBuilder(); int len = value.length(); for (int i = 0; i < len; i++) { char ch = value.charAt(i); if ((ch != ' ') && (ch != '\t')) { sb.append(ch); } } parser.setString(sb.toString()); } else { parser.setString(value); } // Process each comma-delimited language specification int length = parser.getLength(); while (true) { // Extract the next comma-delimited entry int start = parser.getIndex(); if (start >= length) { break; } int end = parser.findChar(','); String entry = parser.extract(start, end).trim(); parser.advance(); // For the following entry // Extract the quality factor for this entry double quality = 1.0; int semi = entry.indexOf(";q="); if (semi >= 0) { try { String strQuality = entry.substring(semi + 3); if (strQuality.length() <= 5) { quality = Double.parseDouble(strQuality); } else { quality = 0.0; } } catch (NumberFormatException e) { quality = 0.0; } entry = entry.substring(0, semi); } // Skip entries we are not going to keep track of if (quality < 0.00005) { continue; // Zero (or effectively zero) quality factors } if ("*".equals(entry)) { continue; // FIXME - "*" entries are not handled } // Extract the language and country for this entry String language = null; String country = null; String variant = null; int dash = entry.indexOf('-'); if (dash < 0) { language = entry; country = ""; variant = ""; } else { language = entry.substring(0, dash); country = entry.substring(dash + 1); int vDash = country.indexOf('-'); if (vDash > 0) { String cTemp = country.substring(0, vDash); variant = country.substring(vDash + 1); country = cTemp; } else { variant = ""; } } if (!isAlpha(language) || !isAlpha(country) || !isAlpha(variant)) { continue; } // Add a new Locale to the list of Locales for this quality level Locale locale = new Locale(language, country, variant); Double key = new Double(-quality); // Reverse the order ArrayList values = locales.get(key); if (values == null) { values = new ArrayList(); locales.put(key, values); } values.add(locale); } // Process the quality values in highest->lowest order (due to // negating the Double value when creating the key) for (ArrayList list : locales.values()) { for (Locale locale : list) { addLocale(locale); } } } protected static final boolean isAlpha(String value) { for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) { return false; } } return true; } // ----------------------------------------------------- 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 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 GenericPrincipal) { return ((GenericPrincipal) 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 } }); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy