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

org.glassfish.grizzly.http.server.Request Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2022 Contributors to the Eclipse Foundation
 * Copyright (c) 2008, 2020 Oracle and/or its affiliates. All rights reserved.
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed 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.glassfish.grizzly.http.server;

import java.io.CharConversionException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.Principal;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.security.auth.Subject;

import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.ReadHandler;
import org.glassfish.grizzly.ThreadCache;
import org.glassfish.grizzly.WriteHandler;
import org.glassfish.grizzly.filterchain.FilterChainContext;
import org.glassfish.grizzly.http.Cookie;
import org.glassfish.grizzly.http.Cookies;
import org.glassfish.grizzly.http.HttpRequestPacket;
import org.glassfish.grizzly.http.Method;
import org.glassfish.grizzly.http.Note;
import org.glassfish.grizzly.http.Protocol;
import org.glassfish.grizzly.http.io.InputBuffer;
import org.glassfish.grizzly.http.io.NIOInputStream;
import org.glassfish.grizzly.http.io.NIOReader;
import org.glassfish.grizzly.http.server.http2.PushBuilder;
import org.glassfish.grizzly.http.server.io.ServerInputBuffer;
import org.glassfish.grizzly.http.server.util.Globals;
import org.glassfish.grizzly.http.server.util.MappingData;
import org.glassfish.grizzly.http.server.util.ParameterMap;
import org.glassfish.grizzly.http.server.util.RequestUtils;
import org.glassfish.grizzly.http.server.util.SimpleDateFormats;
import org.glassfish.grizzly.http.server.util.StringParser;
import org.glassfish.grizzly.http.util.Chunk;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.grizzly.http.util.FastHttpDateFormat;
import org.glassfish.grizzly.http.util.Header;
import org.glassfish.grizzly.http.util.Parameters;
import org.glassfish.grizzly.localization.LogMessages;
import org.glassfish.grizzly.utils.Charsets;

import static org.glassfish.grizzly.http.util.Constants.FORM_POST_CONTENT_TYPE;

/**
 * Wrapper object for the Coyote request.
 *
 * @author Remy Maucherat
 * @author Craig R. McClanahan
 * @version $Revision: 1.2 $ $Date: 2007/03/14 02:15:42 $
 */

public class Request {
    // @TODO remove this property support once we're sure nobody
    // relies on this functionality.
    private static final Boolean FORCE_CLIENT_AUTH_ON_GET_USER_PRINCIPAL = Boolean
            .getBoolean(Request.class.getName() + ".force-client-auth-on-get-user-principal");

    private static final Logger LOGGER = Grizzly.logger(Request.class);

    private static final ThreadCache.CachedTypeIndex CACHE_IDX = ThreadCache.obtainIndex(Request.class, 16);

    // Duplicated in http2 Constants. Keep values in sync.
    private static final String HTTP2_PUSH_ENABLED = "http2-push-enabled";

    private static final LocaleParser localeParser = new TagLocaleParser();
    private static final AtomicLong REQUEST_ID_GENERATOR = new AtomicLong(0);

    public static Request create() {
        final Request request = ThreadCache.takeFromCache(CACHE_IDX);
        if (request != null) {
            return request;
        }

        return new Request(new Response());
    }

    /**
     * Request attribute will be associated with a boolean value indicating whether or not it's possible to transfer a
     * {@link java.io.File} using sendfile.
     *
     * @since 2.2
     */
    public static final String SEND_FILE_ENABLED_ATTR = "org.glassfish.grizzly.http.SEND_FILE_ENABLED";

    /**
     * 

* The value of this request attribute, as set by the developer must be a {@link java.io.File} that exists, is not a * directory, and is readable. This {@link java.io.File} will be transferred using sendfile if * {@link #SEND_FILE_ENABLED_ATTR} is true. If sendfile support isn't enabled, an IllegalStateException will be raised * at runtime. The {@link HttpHandler} using this functionality should refrain from writing content via the response. *

* *

* Note that once this attribute is set, the sendfile process will begin. *

* * @since 2.2 */ public static final String SEND_FILE_ATTR = "org.glassfish.grizzly.http.SEND_FILE"; /** *

* The value of this request attribute signifies the starting offset of the file transfer. If not specified, an offset * of zero will be assumed. The type of the value must be {@link Long}. *

* *

* NOTE: In order for this attribute to take effect, it must be set before the {@link #SEND_FILE_ATTR} * is set. *

* * @since 2.2 */ public static final String SEND_FILE_START_OFFSET_ATTR = "org.glassfish.grizzly.http.FILE_START_OFFSET"; /** *

* The value of this request attribute signifies the total number of bytes to transfer. If not specified, the entire * file will be transferred. The type of the value must be {@link Long} *

* *

* NOTE: In order for this attribute to take effect, it must be set before the {@link #SEND_FILE_ATTR} * is set. *

* * @since 2.2 */ public static final String SEND_FILE_WRITE_LEN_ATTR = "org.glassfish.grizzly.http.FILE_WRITE_LEN"; // ------------------------------------------------------------- Properties /** * The match string for identifying a session ID parameter. */ private static final String match = ';' + Globals.SESSION_PARAMETER_NAME + '='; // -------------------------------------------------------------------- // public final MappingData obtainMappingData() { if (cachedMappingData == null) { cachedMappingData = new MappingData(); } return cachedMappingData; } // --------------------------------------------------------------------- // // ----------------------------------------------------- Instance Variables /** * HTTP Request Packet */ protected HttpRequestPacket request; private final String requestId = Long.toString(REQUEST_ID_GENERATOR.incrementAndGet()); protected FilterChainContext ctx; protected HttpServerFilter httpServerFilter; protected final List afterServicesList = new ArrayList<>(4); private Session session; /** * The HTTP request scheme. */ private String scheme; private final PathData contextPath = new PathData(this, "", null); private final PathData httpHandlerPath = new PathData(this); private final PathData pathInfo = new PathData(this); private MappingData cachedMappingData; /** * The set of cookies associated with this Request. */ protected Cookie[] cookies = null; protected Cookies rawCookies; // Session cookie name protected String sessionCookieName; // Session manager protected SessionManager sessionManager; /** * The default Locale if none are specified. */ protected static final Locale defaultLocale = Locale.getDefault(); /** * The preferred Locales associated with this Request. */ protected final ArrayList locales = new ArrayList<>(); /** * The current dispatcher type. */ protected Object dispatcherType = null; /** * The associated input buffer. */ protected final ServerInputBuffer inputBuffer = new ServerInputBuffer(); /** * NIOInputStream. */ private final NIOInputStreamImpl inputStream = new NIOInputStreamImpl(); /** * Reader. */ private final NIOReaderImpl reader = new NIOReaderImpl(); /** * Using stream flag. */ protected boolean usingInputStream = false; /** * Using writer flag. */ protected boolean usingReader = false; /** * User principal. */ protected Principal userPrincipal = null; /** * Session parsed flag. */ protected boolean sessionParsed = false; /** * Request parameters parsed flag. */ protected boolean requestParametersParsed = false; /** * Cookies parsed flag. */ protected boolean cookiesParsed = false; /** * Secure flag. */ protected boolean secure = false; /** * The Subject associated with the current AccessControllerContext */ protected Subject subject = null; /** * Hash map used in the getParametersMap method. */ protected final ParameterMap parameterMap = new ParameterMap(); protected final Parameters parameters = new Parameters(); /** * 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; /** * Parse locales. */ protected boolean localesParsed = false; /** * The string parser we will use for parsing request lines. */ private StringParser parser; // START S1AS 4703023 /** * The current application dispatch depth. */ private int dispatchDepth = 0; /** * The maximum allowed application dispatch depth. */ private static int maxDispatchDepth = Constants.DEFAULT_MAX_DISPATCH_DEPTH; // END S1AS 4703023 // START SJSAS 6346226 private String jrouteId; // END SJSAS 6346226 /** * The {@link RequestExecutorProvider} responsible for executing user's code in * {@link HttpHandler#service(org.glassfish.grizzly.http.server.Request, org.glassfish.grizzly.http.server.Response)} * and notifying {@link ReadHandler}, {@link WriteHandler} registered by the user */ private RequestExecutorProvider requestExecutorProvider; /** * The response with which this request is associated. */ protected final Response response; /** * Trailer headers, if any. */ protected Map trailers; // ----------------------------------------------------------- Constructors /** * Temporarily introduce public constructor to fix GRIZZLY-1782. Just to make request instances proxiable. This * constructor is not intended for client code consumption and should not be used explicitly as it creates an invalid * instance. * * @deprecated */ @Deprecated public Request() { this.response = null; } protected Request(final Response response) { this.response = response; } // --------------------------------------------------------- Public Methods public void initialize(final HttpRequestPacket request, final FilterChainContext ctx, final HttpServerFilter httpServerFilter) { this.request = request; this.ctx = ctx; this.httpServerFilter = httpServerFilter; inputBuffer.initialize(this, ctx); parameters.setHeaders(request.getHeaders()); parameters.setQuery(request.getQueryStringDC()); final DataChunk remoteUser = request.remoteUser(); if (httpServerFilter != null) { final ServerFilterConfiguration configuration = httpServerFilter.getConfiguration(); parameters.setQueryStringEncoding(configuration.getDefaultQueryEncoding()); final BackendConfiguration backendConfiguration = configuration.getBackendConfiguration(); if (backendConfiguration != null) { // Set the protocol scheme based on backend config if (backendConfiguration.getScheme() != null) { scheme = backendConfiguration.getScheme(); } else if (backendConfiguration.getSchemeMapping() != null) { scheme = request.getHeader(backendConfiguration.getSchemeMapping()); } if ("https".equalsIgnoreCase(scheme)) { // this ensures that JSESSIONID cookie has the "Secure" attribute // when using scheme-mapping request.setSecure(true); } if (remoteUser.isNull() && backendConfiguration.getRemoteUserMapping() != null) { remoteUser.setString(request.getHeader(backendConfiguration.getRemoteUserMapping())); } } } if (scheme == null) { scheme = request.isSecure() ? "https" : "http"; } if (!remoteUser.isNull()) { setUserPrincipal(new GrizzlyPrincipal(remoteUser.toString())); } } final HttpServerFilter getServerFilter() { return httpServerFilter; } /** * @return the Coyote request. */ public HttpRequestPacket getRequest() { return this.request; } /** * @return the Response with which this Request is associated. */ public Response getResponse() { return response; } /** * @return session cookie name, if not set default JSESSIONID name will be used */ public String getSessionCookieName() { return obtainSessionCookieName(); } /** * Set the session cookie name, if not set default JSESSIONID name will be used */ public void setSessionCookieName(String sessionCookieName) { this.sessionCookieName = sessionCookieName; } /** * @return true if HTTP/2 push is enabled, otherwise, false. */ public boolean isPushEnabled() { final Boolean result = (Boolean) getContext().getConnection().getAttributes().getAttribute(HTTP2_PUSH_ENABLED); return result != null ? result : false; } /** * @return {@link #sessionCookieName} if set, or the value returned by {@link SessionManager#getSessionCookieName()} if * {@link #sessionCookieName} is not set. */ protected String obtainSessionCookieName() { return sessionCookieName != null ? sessionCookieName : getSessionManager().getSessionCookieName(); } /** * @return {@link SessionManager} */ protected SessionManager getSessionManager() { return sessionManager != null ? sessionManager : DefaultSessionManager.instance(); } /** * Set {@link SessionManager}, null value implies {@link DefaultSessionManager} */ protected void setSessionManager(final SessionManager sessionManager) { this.sessionManager = sessionManager; } /** * @return the {@link Executor} responsible for notifying {@link ReadHandler}, {@link WriteHandler} associated with this * Request processing. */ public Executor getRequestExecutor() { return requestExecutorProvider.getExecutor(this); } /** * Sets @return the {@link RequestExecutorProvider} responsible for executing user's code in * {@link HttpHandler#service(org.glassfish.grizzly.http.server.Request, org.glassfish.grizzly.http.server.Response)} * and notifying {@link ReadHandler}, {@link WriteHandler} registered by the user. * * @param requestExecutorProvider {@link RequestExecutorProvider} */ protected void setRequestExecutorProvider(final RequestExecutorProvider requestExecutorProvider) { this.requestExecutorProvider = requestExecutorProvider; } /** * Add the listener, which will be notified, once Request processing will be finished. * * @param listener the listener, which will be notified, once Request processing will be finished. */ public void addAfterServiceListener(final AfterServiceListener listener) { afterServicesList.add(listener); } /** * Remove the "after-service" listener, which was previously added by * {@link #addAfterServiceListener(org.glassfish.grizzly.http.server.AfterServiceListener)}. * * @param listener the "after-service" listener, which was previously added by * {@link #addAfterServiceListener(org.glassfish.grizzly.http.server.AfterServiceListener)}. */ public void removeAfterServiceListener(final AfterServiceListener listener) { afterServicesList.remove(listener); } protected void onAfterService() { if (!inputBuffer.isFinished()) { inputBuffer.terminate(); } if (!afterServicesList.isEmpty()) { for (final AfterServiceListener anAfterServicesList : afterServicesList) { try { anAfterServicesList.onAfterService(this); } catch (Exception e) { LOGGER.log(Level.WARNING, LogMessages.WARNING_GRIZZLY_HTTP_SERVER_REQUEST_AFTERSERVICE_NOTIFICATION_ERROR(), e); } } } } /** * Release all object references, and initialize instance variables, in preparation for reuse of this object. */ protected void recycle() { scheme = null; contextPath.setPath(""); httpHandlerPath.reset(); pathInfo.reset(); dispatcherType = null; requestDispatcherPath = null; inputBuffer.recycle(); inputStream.recycle(); reader.recycle(); usingInputStream = false; usingReader = false; userPrincipal = null; subject = null; sessionParsed = false; requestParametersParsed = false; cookiesParsed = false; if (rawCookies != null) { rawCookies.recycle(); } locales.clear(); localesParsed = false; secure = false; request.recycle(); request = null; ctx = null; httpServerFilter = null; cookies = null; requestedSessionId = null; sessionCookieName = null; sessionManager = null; session = null; dispatchDepth = 0; // S1AS 4703023 parameterMap.setLocked(false); parameterMap.clear(); parameters.recycle(); requestExecutorProvider = null; trailers = null; afterServicesList.clear(); // Notes holder shouldn't be recycled. // notesHolder.recycle(); if (cachedMappingData != null) { cachedMappingData.recycle(); } ThreadCache.putToCache(CACHE_IDX, this); } // -------------------------------------------------------- Request Methods /** * @return the authorization credentials sent with this request. */ public String getAuthorization() { return request.getHeader(Constants.AUTHORIZATION_HEADER); } // ------------------------------------------------- Request Public Methods /** * @return a new {@link PushBuilder} for issuing server push responses from the current request. If the current * connection does not support server push, or server push has been disabled by the client, it will return * null. */ public PushBuilder newPushBuilder() { return isPushEnabled() ? new PushBuilder(this) : null; } /** * Replays request's payload by setting new payload {@link Buffer}. If request parameters have been parsed based on * prev. request's POST payload - the parameters will be recycled and ready to be parsed again. * * @param buffer payload * * @throws IllegalStateException, if previous request payload has not been read off. */ public void replayPayload(final Buffer buffer) { inputBuffer.replayPayload(buffer); usingReader = false; usingInputStream = false; if (Method.POST.equals(getMethod()) && requestParametersParsed) { requestParametersParsed = false; parameterMap.setLocked(false); parameterMap.clear(); parameters.recycle(); } } /** * Create and return a NIOInputStream to read the content associated with this Request. * * @return {@link NIOInputStream} */ public NIOInputStream createInputStream() { inputStream.setInputBuffer(inputBuffer); return inputStream; } /** * Create a named {@link Note} associated with this Request. * * @param the {@link Note} type. * @param name the {@link Note} name. * @return the {@link Note}. */ @SuppressWarnings({ "unchecked" }) public static Note createNote(final String name) { return HttpRequestPacket.createNote(name); } /** * Return the {@link Note} value associated with this Request, or null if no such binding exists. * Use {@link #createNote(java.lang.String)} to create a new {@link Note}. * * @param note {@link Note} value to be returned */ public E getNote(final Note note) { return request.getNote(note); } /** * Return a {@link Set} containing the String names of all note bindings that exist for this request. Use * {@link #createNote(java.lang.String)} to create a new {@link Note}. * * @return a {@link Set} containing the String names of all note bindings that exist for this request. */ public Set getNoteNames() { return request.getNoteNames(); } /** * Remove the {@link Note} value associated with this request. Use {@link #createNote(java.lang.String)} to create a new * {@link Note}. * * @param note {@link Note} value to be removed */ public E removeNote(final Note note) { return request.removeNote(note); } /** * Bind the {@link Note} value to this Request, replacing any existing binding for this name. Use * {@link #createNote(java.lang.String)} to create a new {@link Note}. * * @param note {@link Note} to which the object should be bound * @param value the {@link Note} value be bound to the specified {@link Note}. */ public void setNote(final Note note, final E value) { request.setNote(note, value); } /** * Set the name of the server (virtual host) to process this request. * * @param name The server name */ public void setServerName(String name) { request.serverName().setString(name); } /** * Set the port number of the server to process this request. * * @param port The server port */ public void setServerPort(int port) { request.setServerPort(port); } /** * @return {@link HttpServerFilter}, which dispatched this request */ public HttpServerFilter getHttpFilter() { return httpServerFilter; } /** * Returns the portion of the request URI that indicates the context of the request. The context path always comes first * in a request URI. The path starts with a "/" character but does not end with a "/" character. For * {@link HttpHandler}s in the default (root) context, this method returns "". The container does not decode this * string. * * @return a String specifying the portion of the request URI that indicates the context of the request */ public String getContextPath() { return contextPath.get(); } protected void setContextPath(final String contextPath) { this.contextPath.setPath(contextPath); } protected void setContextPath(final PathResolver contextPath) { this.contextPath.setResolver(contextPath); } /** * Returns the part of this request's URL that calls the HttpHandler. This includes either the HttpHandler name or a * path to the HttpHandler, but does not include any extra path information or a query string. * * @return a String containing the name or path of the HttpHandler being called, as specified in the request URL * @throws IllegalStateException if HttpHandler path was not set explicitly and attempt to URI-decode * {@link org.glassfish.grizzly.http.util.RequestURIRef#getDecodedURI()} failed. */ public String getHttpHandlerPath() { return httpHandlerPath.get(); } protected void setHttpHandlerPath(final String httpHandlerPath) { this.httpHandlerPath.setPath(httpHandlerPath); } protected void setHttpHandlerPath(final PathResolver httpHandlerPath) { this.httpHandlerPath.setResolver(httpHandlerPath); } /** * Returns any extra path information associated with the URL the client sent when it made this request. The extra path * information follows the HttpHandler path but precedes the query string. This method returns null if there was no * extra path information. * * @return a String specifying extra path information that comes after the HttpHandler path but before the query string * in the request URL; or null if the URL does not have any extra path information */ public String getPathInfo() { return pathInfo.get(); } protected void setPathInfo(final String pathInfo) { this.pathInfo.setPath(pathInfo); } protected void setPathInfo(final PathResolver pathInfo) { this.pathInfo.setResolver(pathInfo); } // ------------------------------------------------- ServletRequest Methods /** * Return the specified request attribute if it exists; otherwise, return null. * * @param name Name of the request attribute to return * @return the specified request attribute if it exists; otherwise, return null. */ public Object getAttribute(final String name) { if (SEND_FILE_ENABLED_ATTR.equals(name)) { assert response != null; return response.isSendFileEnabled(); } Object attribute = request.getAttribute(name); if (attribute != null) { return attribute; } if (Globals.SSL_CERTIFICATE_ATTR.equals(name)) { attribute = RequestUtils.populateCertificateAttribute(this); if (attribute != null) { request.setAttribute(name, attribute); } } else if (isSSLAttribute(name)) { RequestUtils.populateSSLAttributes(this); attribute = request.getAttribute(name); } else if (Globals.DISPATCHER_REQUEST_PATH_ATTR.equals(name)) { return requestDispatcherPath; } return attribute; } /** * Test if a given name is one of the special Servlet-spec SSL attributes. */ static boolean isSSLAttribute(final String name) { return Globals.CERTIFICATES_ATTR.equals(name) || Globals.CIPHER_SUITE_ATTR.equals(name) || Globals.KEY_SIZE_ATTR.equals(name); } /** * Return the names of all request attributes for this Request, or an empty {@link Set} if there are none. */ public Set getAttributeNames() { return request.getAttributeNames(); } /** * Return the character encoding for this Request. */ public String getCharacterEncoding() { return request.getCharacterEncoding(); } /** * Return the content length for this Request. */ public int getContentLength() { return (int) request.getContentLength(); } /** * Return the content length for this Request represented by Java long type. */ public long getContentLengthLong() { return request.getContentLength(); } /** * Return the content type for this Request. */ public String getContentType() { return request.getContentType(); } /** *

* Return the {@link InputStream} for this {@link Request}. *

* * By default the returned {@link NIOInputStream} will work as blocking {@link InputStream}, but it will be possible to * call {@link NIOInputStream#isReady()}, {@link NIOInputStream#available()}, or * {@link NIOInputStream#notifyAvailable(org.glassfish.grizzly.ReadHandler)} to avoid blocking. * * @return the {@link NIOInputStream} for this {@link Request}. * * @exception IllegalStateException if {@link #getReader()} or {@link #getNIOReader()} has already been called for this * request. * * @since 2.2 */ public InputStream getInputStream() { return getNIOInputStream(); } /** *

* Return the {@link NIOInputStream} for this {@link Request}. This stream will not block when reading content. *

* *

* NOTE: For now, in order to use non-blocking functionality, this method must be invoked before the * {@link HttpHandler#service(Request, Response)} method returns. We hope to have this addressed in the next release. *

* * @return the {@link NIOInputStream} for this {@link Request}. * * @exception IllegalStateException if {@link #getReader()} or {@link #getNIOReader()} has already been called for this * request. */ public NIOInputStream getNIOInputStream() { if (usingReader) { throw new IllegalStateException("Illegal attempt to call getInputStream() after getReader() has already been called."); } usingInputStream = true; inputStream.setInputBuffer(inputBuffer); return inputStream; } /** * @return true if this request requires acknowledgment. */ public boolean requiresAcknowledgement() { return request.requiresAcknowledgement(); } /** * 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. */ public Locale getLocale() { if (!localesParsed) { parseLocales(); } if (!locales.isEmpty()) { return locales.get(0); } else { 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. */ public List getLocales() { if (!localesParsed) { parseLocales(); } if (!locales.isEmpty()) { return locales; } final ArrayList results = new ArrayList<>(); results.add(defaultLocale); return results; } /** * Returns the low-level parameters holder for finer control over parameters. * * @return {@link Parameters}. */ public Parameters getParameters() { return parameters; } /** * 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 */ public String getParameter(final String name) { if (!requestParametersParsed) { parseRequestParameters(); } return parameters.getParameter(name); } /** * Returns a {@link java.util.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 {@link java.util.Map} containing parameter names as keys and parameter values as map values. */ public Map getParameterMap() { if (parameterMap.isLocked()) { return parameterMap; } for (final String name : getParameterNames()) { final String[] values = getParameterValues(name); parameterMap.put(name, values); } parameterMap.setLocked(true); return parameterMap; } /** * Return the names of all defined request parameters for this request. */ public Set getParameterNames() { if (!requestParametersParsed) { parseRequestParameters(); } return parameters.getParameterNames(); } /** * Return the defined values for the specified request parameter, if any; otherwise, return null. * * @param name Name of the desired request parameter */ public String[] getParameterValues(String name) { if (!requestParametersParsed) { parseRequestParameters(); } return parameters.getParameterValues(name); } /** * Return the protocol and version used to make this Request. */ public Protocol getProtocol() { return request.getProtocol(); } /** *

* Returns the {@link Reader} associated with this {@link Request}. *

* * By default the returned {@link NIOReader} will work as blocking {@link java.io.Reader}, but it will be possible to * call {@link NIOReader#isReady()} or {@link NIOReader#notifyAvailable(org.glassfish.grizzly.ReadHandler)} to avoid * blocking. * * @return the {@link NIOReader} associated with this {@link Request}. * * @throws IllegalStateException if {@link #getInputStream()} or {@link #getNIOInputStream()} has already been called * for this request. * * @since 2.2 */ public Reader getReader() { return getNIOReader(); } /** *

* Returns the {@link NIOReader} associated with this {@link Request}. This {@link NIOReader} will not block while * reading content. *

* * @return {@link NIOReader} * @throws IllegalStateException if {@link #getInputStream()} or {@link #getNIOInputStream()} has already been called * for this request. */ public NIOReader getNIOReader() { if (usingInputStream) { throw new IllegalStateException("Illegal attempt to call getReader() after getInputStream() has alread been called."); } usingReader = true; inputBuffer.processingChars(); reader.setInputBuffer(inputBuffer); return reader; } /** * Return the remote IP address making this Request. */ public String getRemoteAddr() { return request.getRemoteAddress(); } /** * Return the remote host name making this Request. */ public String getRemoteHost() { return request.getRemoteHost(); } /** * Returns the Internet Protocol (IP) source port of the client or last proxy that sent the request. */ public int getRemotePort() { return request.getRemotePort(); } /** * Returns the host name of the Internet Protocol (IP) interface on which the request was received. */ public String getLocalName() { return request.getLocalName(); } /** * Returns the Internet Protocol (IP) address of the interface on which the request was received. */ public String getLocalAddr() { return request.getLocalAddress(); } /** * Returns the Internet Protocol (IP) port number of the interface on which the request was received. */ public int getLocalPort() { return request.getLocalPort(); } /** * @return the scheme used to make this Request. */ public String getScheme() { return scheme; } /** * @return the server name responding to this Request. */ public String getServerName() { return request.serverName().toString(); } /** * @return the server port responding to this Request. */ public int getServerPort() { return request.getServerPort(); } /** * @return true if this request received on a secure connection */ public boolean isSecure() { return request.isSecure(); } /** * Remove the specified request attribute if it exists. * * @param name Name of the request attribute to remove */ public void removeAttribute(String name) { request.removeAttribute(name); } /** * Set the specified request attribute to the specified value. * * @param name Name of the request attribute to set * @param value The associated value */ public void setAttribute(final String name, final Object value) { // Name cannot be null if (name == null) { throw new IllegalArgumentException("Argument 'name' cannot be null"); } // Null value is the same as removeAttribute() if (value == null) { removeAttribute(name); return; } if (name.equals(Globals.DISPATCHER_TYPE_ATTR)) { dispatcherType = value; return; } else if (name.equals(Globals.DISPATCHER_REQUEST_PATH_ATTR)) { requestDispatcherPath = value; return; } request.setAttribute(name, value); assert response != null; if (response.isSendFileEnabled() && SEND_FILE_ATTR.equals(name)) { RequestUtils.handleSendFile(this); } } /** * 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(). * Otherwise, it has no effect. * * @param encoding String containing the name of the character encoding. * @throws java.io.UnsupportedEncodingException if this ServletRequest is still in a state where a character encoding * may be set, but the specified encoding is invalid * * @since Servlet 2.3 */ public void setCharacterEncoding(final String encoding) throws UnsupportedEncodingException { // START SJSAS 4936855 if (requestParametersParsed || usingReader) { return; } // END SJSAS 4936855 Charsets.lookupCharset(encoding); // Save the validated encoding request.setCharacterEncoding(encoding); } /** * Static setter method for the maximum dispatch depth */ public static void setMaxDispatchDepth(int depth) { maxDispatchDepth = depth; } public static int getMaxDispatchDepth() { return maxDispatchDepth; } /** * Increment the depth of application dispatch */ public int incrementDispatchDepth() { return ++dispatchDepth; } /** * Decrement the depth of application dispatch */ public int decrementDispatchDepth() { return --dispatchDepth; } /** * Check if the application dispatching has reached the maximum */ public boolean isMaxDispatchDepthReached() { return dispatchDepth > maxDispatchDepth; } /** * @return identifier of the request generated by this class. */ public String getRequestId() { return this.requestId; } /** * @return an empty string or identifier managed by the underlying protocol (HTTP2) */ public String getProtocolRequestId() { return this.request.getProtocolRequestId(); } /** * @return underlying connection used by the request */ public Connection getConnection() { return this.request.getConnection(); } // ---------------------------------------------------- HttpRequest Methods /** * Add a Cookie to the set of Cookies associated with this Request. * * @param cookie The new cookie */ public void addCookie(Cookie cookie) { // For compatibility only if (!cookiesParsed) { parseCookies(); } int size = 0; if (cookie != null) { size = cookies.length; } Cookie[] newCookies = new Cookie[size + 1]; 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); } /** * 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 */ public void addParameter(String name, String values[]) { parameters.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. */ 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. */ public void clearParameters() { // Not used } /** * Get the decoded request URI. * * @return the URL decoded request URI */ public String getDecodedRequestURI() throws CharConversionException { return request.getRequestURIRef().getDecodedURI(); } /** * 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) { this.userPrincipal = principal; } // --------------------------------------------- HttpServletRequest Methods /** * Return the authentication type used for this Request. */ public String getAuthType() { return request.authType().toString(); } /** * Return the set of Cookies received with this Request. */ public Cookie[] getCookies() { if (!cookiesParsed) { parseCookies(); } return cookies; } /** * Set the set of cookies received with this Request. */ 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 */ public long getDateHeader(String name) { String value = getHeader(name); if (value == null) { return -1L; } final SimpleDateFormats formats = SimpleDateFormats.create(); try { // Attempt to convert the date header in a variety of formats long result = FastHttpDateFormat.parseDate(value, formats.getFormats()); if (result != -1L) { return result; } throw new IllegalArgumentException(value); } finally { formats.recycle(); } } /** * Return the value of the specified date header, if any; otherwise return -1. * * @param header the requested date {@link Header} * * @exception IllegalArgumentException if the specified header value cannot be converted to a date * * @since 2.1.2 */ public long getDateHeader(Header header) { String value = getHeader(header); if (value == null) { return -1L; } final SimpleDateFormats formats = SimpleDateFormats.create(); try { // Attempt to convert the date header in a variety of formats long result = FastHttpDateFormat.parseDate(value, formats.getFormats()); if (result != -1L) { return result; } throw new IllegalArgumentException(value); } finally { formats.recycle(); } } /** * Return the first value of the specified header, if any; otherwise, return null * * @param name Name of the requested header */ public String getHeader(String name) { return request.getHeader(name); } /** * Return the first value of the specified header, if any; otherwise, return null * * @param header the requested {@link Header} * * @since 2.1.2 */ public String getHeader(final Header header) { return request.getHeader(header); } /** * Return all of the values of the specified header, if any; otherwise, return an empty enumeration. * * @param name Name of the requested header */ public Iterable getHeaders(String name) { return request.getHeaders().values(name); } /** * Return all of the values of the specified header, if any; otherwise, return an empty enumeration. * * @param header the requested {@link Header} * * @since 2.1.2 */ public Iterable getHeaders(final Header header) { return request.getHeaders().values(header); } /** * Get the request trailer headers. Values may only be returned in the case of HTTP/1.1 when the request is using the * transfer-encoding chunked or in HTTP/2 sends a second HEADERS frame * terminating the stream. An empty Map will be returned in all other cases. * * While headers are typical case insensitive, the headers stored in the returned Map will be done so in * lower-case. * * This method should typically be called after the application has read the request body. It is safe to invoke this * method if there is no body content. * * @return A {@link Map} of trailers headers, if any were present. * * @throws IllegalStateException if neither {@link ReadHandler#onAllDataRead} has been called or an EOF indication has * been returned from the {@link #getReader}, {@link #getNIOReader()}, {@link #getInputStream}, * {@link #getNIOInputStream()}. * * @see #areTrailersAvailable() * * @since 2.4.0 */ public Map getTrailers() { if (inputBuffer.isFinished()) { return inputBuffer.getTrailers(); } throw new IllegalStateException(); } /** * @return true if trailers are available to be accessed otherwise returns false. * * @since 2.4.0 */ public boolean areTrailersAvailable() { return inputBuffer.areTrailersAvailable(); } /** * Return the names of all headers received with this request. */ public Iterable getHeaderNames() { return request.getHeaders().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 */ public int getIntHeader(String name) { String value = getHeader(name); if (value == null) { return -1; } else { return Integer.parseInt(value); } } /** * Return the value of the specified header as an integer, or -1 if there is no such header for this request. * * @param header the requested {@link Header} * * @exception IllegalArgumentException if the specified header value cannot be converted to an integer * * @since 2.1.2 */ public int getIntHeader(final Header header) { String value = getHeader(header); if (value == null) { return -1; } else { return Integer.parseInt(value); } } /** * Return the HTTP request method used in this Request. */ public Method getMethod() { return request.getMethod(); } /** * Sets the HTTP request method used in this Request. * * @param method the HTTP request method used in this Request. */ public void setMethod(String method) { request.setMethod(method); } /** * @return the query string associated with this request. */ public String getQueryString() { final String queryString = request.getQueryStringDC().toString(parameters.getQueryStringEncoding()); return queryString == null || queryString.isEmpty() ? null : queryString; } /** * Sets the query string associated with this request. * * @param queryString the query string associated with this request. */ public void setQueryString(String queryString) { request.setQueryString(queryString); } /** * Return the name of the remote user that has been authenticated for this Request. */ public String getRemoteUser() { if (userPrincipal != null) { return userPrincipal.getName(); } else { return null; } } /** * Return the session identifier included in this request, if any. */ public String getRequestedSessionId() { return requestedSessionId; } /** * Return the request URI for this request. */ public String getRequestURI() { return request.getRequestURI(); } /** * Sets the request URI for this request. * * @param uri the request URI for this request. */ public void setRequestURI(String uri) { request.setRequestURI(uri); } /** * 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 StringBuilder, 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 */ public StringBuilder getRequestURL() { final StringBuilder url = new StringBuilder(); return appendRequestURL(this, url); } /** * Appends the reconstructed URL the client used to make the request. The appended URL contains a protocol, server name, * port number, and server path, but it does not include query string parameters. *

* Because this method returns a StringBuilder, 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 StringBuilder object containing the appended reconstructed URL */ public static StringBuilder appendRequestURL(final Request request, final StringBuilder buffer) { final String scheme = request.getScheme(); int port = request.getServerPort(); if (port < 0) { port = 80; // Work around java.net.URL bug } buffer.append(scheme); buffer.append("://"); buffer.append(request.getServerName()); if (scheme.equals("http") && port != 80 || scheme.equals("https") && port != 443) { buffer.append(':'); buffer.append(port); } buffer.append(request.getRequestURI()); return buffer; } /** * Appends the reconstructed URL the client used to make the request. The appended 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 appended reconstructed URL */ public static StringBuffer appendRequestURL(final Request request, final StringBuffer buffer) { final String scheme = request.getScheme(); int port = request.getServerPort(); if (port < 0) { port = 80; // Work around java.net.URL bug } buffer.append(scheme); buffer.append("://"); buffer.append(request.getServerName()); if (scheme.equals("http") && port != 80 || scheme.equals("https") && port != 443) { buffer.append(':'); buffer.append(port); } buffer.append(request.getRequestURI()); return buffer; } /** * Return the principal that has been authenticated for this Request. */ public Principal getUserPrincipal() { if (userPrincipal == null) { if (getRequest().isSecure()) { X509Certificate[] certs = (X509Certificate[]) getAttribute(Globals.CERTIFICATES_ATTR); if (FORCE_CLIENT_AUTH_ON_GET_USER_PRINCIPAL && (certs == null || certs.length < 1)) { // Force SSL re-handshake and request client auth certs = (X509Certificate[]) getAttribute(Globals.SSL_CERTIFICATE_ATTR); } if (certs != null && certs.length > 0) { userPrincipal = certs[0].getSubjectX500Principal(); } } } return userPrincipal; } public FilterChainContext getContext() { return ctx; } protected String unescape(String s) { if (s == null) { return null; } if (s.indexOf('\\') == -1) { return s; } StringBuilder buf = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c != '\\') { buf.append(c); } else { if (++i >= s.length()) { // invalid escape, hence invalid cookie throw new IllegalArgumentException(); } c = s.charAt(i); buf.append(c); } } return buf.toString(); } /** * Parse cookies. */ protected void parseCookies() { cookiesParsed = true; final Cookies serverCookies = getRawCookies(); cookies = serverCookies.get(); } /** * @return the {@link InputBuffer} associated with this request, which is the source for {@link #getInputStream()}, * {@link #getReader()}, {@link #getNIOInputStream()}, and {@link #getNIOReader()} */ public InputBuffer getInputBuffer() { return inputBuffer; } /** * This method may be used if some other entity processed request parameters and wishes to expose them via the request. * When this method is called, it will mark the internal request parameter state as having been processed. * * @param parameters the parameters to expose via this request. * * @since 2.2 */ public void setRequestParameters(final Parameters parameters) { this.requestParametersParsed = true; for (final String name : parameters.getParameterNames()) { this.parameters.addParameterValues(name, parameters.getParameterValues(name)); } } /** * TODO DOCS */ protected Cookies getRawCookies() { if (rawCookies == null) { rawCookies = new Cookies(); } if (!rawCookies.initialized()) { rawCookies.setHeaders(request.getHeaders()); } return rawCookies; } /** * Parse request parameters. */ protected void parseRequestParameters() { // Delay updating requestParametersParsed to TRUE until // after getCharacterEncoding() has been called, because // getCharacterEncoding() may cause setCharacterEncoding() to be // called, and the latter will ignore the specified encoding if // requestParametersParsed is TRUE requestParametersParsed = true; Charset charset = null; if (parameters.getEncoding() == null) { // getCharacterEncoding() may have been overridden to search for // hidden form field containing request encoding charset = lookupCharset(getCharacterEncoding()); parameters.setEncoding(charset); } if (parameters.getQueryStringEncoding() == null) { if (charset == null) { // getCharacterEncoding() may have been overridden to search for // hidden form field containing request encoding charset = lookupCharset(getCharacterEncoding()); } parameters.setQueryStringEncoding(charset); } parameters.handleQueryParameters(); if (usingInputStream || usingReader) { return; } if (!Method.POST.equals(getMethod())) { return; } if (!checkPostContentType(getContentType())) { return; } final int maxFormPostSize = httpServerFilter.getConfiguration().getMaxFormPostSize(); int len = getContentLength(); if (len < 0) { if (!request.isChunked()) { return; } len = maxFormPostSize; } if (maxFormPostSize > 0 && len > maxFormPostSize) { if (LOGGER.isLoggable(Level.WARNING)) { LOGGER.warning(LogMessages.WARNING_GRIZZLY_HTTP_SERVER_REQUEST_POST_TOO_LARGE()); } throw new IllegalStateException(LogMessages.WARNING_GRIZZLY_HTTP_SERVER_REQUEST_POST_TOO_LARGE()); } int read = 0; try { final Buffer formData = getPostBody(len); read = formData.remaining(); parameters.processParameters(formData, formData.position(), read); } catch (Exception ignored) { } finally { try { skipPostBody(read); } catch (Exception e) { LOGGER.log(Level.WARNING, LogMessages.WARNING_GRIZZLY_HTTP_SERVER_REQUEST_BODY_SKIP(), e); } } } private Charset lookupCharset(final String enc) { Charset charset; if (enc != null) { try { charset = Charsets.lookupCharset(enc); } catch (Exception e) { charset = org.glassfish.grizzly.http.util.Constants.DEFAULT_HTTP_CHARSET; } } else { charset = org.glassfish.grizzly.http.util.Constants.DEFAULT_HTTP_CHARSET; } return charset; } private boolean checkPostContentType(final String contentType) { return contentType != null && contentType.trim().startsWith(FORM_POST_CONTENT_TYPE); } /** * Gets the POST body of this request. * * @return The POST body of this request */ public Buffer getPostBody(final int len) throws IOException { inputBuffer.fillFully(len); return inputBuffer.getBuffer(); } /** * Skips the POST body of this request. * * @param len how much of the POST body to skip. */ protected void skipPostBody(final int len) throws IOException { inputBuffer.skip(len); } /** * Parse request locales. */ protected void parseLocales() { localesParsed = true; final Iterable values = getHeaders("accept-language"); for (String value : values) { 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> localLocalesMap = new TreeMap<>(); // Preprocess the value to remove all whitespace int white = value.indexOf(' '); if (white < 0) { white = value.indexOf('\t'); } if (white >= 0) { int len = value.length(); StringBuilder sb = new StringBuilder(len - 1); for (int i = 0; i < len; i++) { char ch = value.charAt(i); if (ch != ' ' && ch != '\t') { sb.append(ch); } } value = sb.toString(); } if (parser == null) { parser = new StringParser(); } // Process each comma-delimited language specification parser.setString(value); // ASSERT: parser is available to us 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) { final String qvalue = entry.substring(semi + 3); // qvalues, according to the RFC, may not contain more // than three values after the decimal. if (qvalue.length() <= 5) { try { quality = Double.parseDouble(qvalue); } catch (NumberFormatException e) { quality = 0.0; } } else { 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 } Locale locale = localeParser.parseLocale(entry); if (locale == null) { continue; } // Add a new Locale to the list of Locales for this quality level Double key = -quality; // Reverse the order List values = localLocalesMap.get(key); if (values == null) { values = new ArrayList<>(); localLocalesMap.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 (List localLocales : localLocalesMap.values()) { for (Locale locale : localLocales) { addLocale(locale); } } } /** * Parses the value of the JROUTE cookie, if present. */ void parseJrouteCookie() { if (!cookiesParsed) { parseCookies(); } final Cookie cookie = getRawCookies().findByName(Constants.JROUTE_COOKIE); if (cookie != null) { setJrouteId(cookie.getValue()); } } /** * @return true if the given string is composed of upper- or lowercase letters only, false * otherwise. */ static boolean isAlpha(String value) { if (value == null) { return false; } 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; } /** * Sets the jroute id of this request. * * @param jrouteId The jroute id */ void setJrouteId(String jrouteId) { this.jrouteId = jrouteId; } /** * Gets the jroute id of this request, which may have been sent as a separate JROUTE cookie or appended to * the session identifier encoded in the URI (if cookies have been disabled). * * @return The jroute id of this request, or null if this request does not carry any jroute id */ public String getJrouteId() { return jrouteId; } // ------------------------------------------------------ Session support --/ /** * Return the session associated with this Request, creating one if necessary. */ public Session getSession() { return doGetSession(true); } /** * 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 getSession(final boolean create) { return doGetSession(create); } /** * Change the session id of the current session associated with this request and return the new session id. * * @return the original session id * * @throws IllegalStateException if there is no session associated with the request * * @since 2.3 */ public String changeSessionId() { Session sessionLocal = doGetSession(false); if (sessionLocal == null) { throw new IllegalStateException("changeSessionId has been called without a session"); } String oldSessionId = getSessionManager().changeSessionId(this, sessionLocal); final String newSessionId = sessionLocal.getIdInternal(); requestedSessionId = newSessionId; if (isRequestedSessionIdFromURL()) { return oldSessionId; } if (response != null) { final Cookie cookie = new Cookie(obtainSessionCookieName(), newSessionId); configureSessionCookie(cookie); response.addSessionCookieInternal(cookie); } return oldSessionId; } protected Session doGetSession(final boolean create) { // Return the current session if it exists and is valid if (session != null && session.isValid()) { return session; } session = null; if (requestedSessionId == null) { final Cookie[] cookiesLocale = getCookies(); assert cookiesLocale != null; final String sessionCookieNameLocal = obtainSessionCookieName(); for (final Cookie c : cookiesLocale) { if (sessionCookieNameLocal.equals(c.getName())) { setRequestedSessionId(c.getValue()); setRequestedSessionCookie(true); break; } } } session = getSessionManager().getSession(this, requestedSessionId); if (session != null && !session.isValid()) { session = null; } if (session != null) { session.access(); return session; } if (!create) { return null; } session = getSessionManager().createSession(this); session.setSessionTimeout(httpServerFilter.getConfiguration().getSessionTimeoutSeconds() * 1000); requestedSessionId = session.getIdInternal(); // Creating a new session cookie based on the newly created session final Cookie cookie = new Cookie(obtainSessionCookieName(), session.getIdInternal()); configureSessionCookie(cookie); assert response != null; response.addCookie(cookie); return session; } /** * @return true if the session identifier included in this request came from a cookie. */ public boolean isRequestedSessionIdFromCookie() { return requestedSessionId != null && requestedSessionCookie; } /** * Return true if the session identifier included in this request came from the request URI. */ public boolean isRequestedSessionIdFromURL() { return requestedSessionId != null && requestedSessionURL; } /** * @return true if the session identifier included in this request identifies a valid session. */ public boolean isRequestedSessionIdValid() { if (requestedSessionId == null) { return false; } if (session != null && requestedSessionId.equals(session.getIdInternal())) { return session.isValid(); } final Session localSession = getSessionManager().getSession(this, requestedSessionId); return localSession != null && localSession.isValid(); } /** * Configures the given session cookie. * * @param cookie The session cookie to be configured */ protected void configureSessionCookie(final Cookie cookie) { cookie.setMaxAge(-1); cookie.setPath("/"); if (isSecure()) { cookie.setSecure(true); } getSessionManager().configureSessionCookie(this, cookie); } /** * Parse session id in URL. */ protected void parseSessionId() { if (sessionParsed) { return; } sessionParsed = true; final DataChunk uriDC = request.getRequestURIRef().getRequestURIBC(); final boolean isUpdated; switch (uriDC.getType()) { case Bytes: isUpdated = parseSessionId(uriDC.getByteChunk()); break; case Buffer: isUpdated = parseSessionId(uriDC.getBufferChunk()); break; case Chars: isUpdated = parseSessionId(uriDC.getCharChunk()); break; case String: isUpdated = parseSessionId(uriDC); break; default: throw new IllegalStateException("Unexpected DataChunk type: " + uriDC.getType()); } if (isUpdated) { uriDC.notifyDirectUpdate(); } } private boolean parseSessionId(final Chunk uriChunk) { final String sessionParamNameMatch = sessionCookieName != null ? ';' + sessionCookieName + '=' : match; boolean isUpdated = false; final int semicolon = uriChunk.indexOf(sessionParamNameMatch, 0); if (semicolon > 0) { // Parse session ID, and extract it from the decoded request URI // final int start = uriChunk.getStart(); final int sessionIdStart = semicolon + sessionParamNameMatch.length(); final int semicolon2 = uriChunk.indexOf(';', sessionIdStart); isUpdated = semicolon2 >= 0; final int end = isUpdated ? semicolon2 : uriChunk.getLength(); final String sessionId = uriChunk.toString(sessionIdStart, end); final int jrouteIndex = sessionId.lastIndexOf(':'); if (jrouteIndex > 0) { setRequestedSessionId(sessionId.substring(0, jrouteIndex)); if (jrouteIndex < sessionId.length() - 1) { setJrouteId(sessionId.substring(jrouteIndex + 1)); } } else { setRequestedSessionId(sessionId); } setRequestedSessionURL(true); uriChunk.delete(semicolon, end); } else { setRequestedSessionId(null); setRequestedSessionURL(false); } return isUpdated; } private boolean parseSessionId(DataChunk dataChunkStr) { assert dataChunkStr.getType() == DataChunk.Type.String; final String uri = dataChunkStr.toString(); final String sessionParamNameMatch = sessionCookieName != null ? ';' + sessionCookieName + '=' : match; boolean isUpdated = false; final int semicolon = uri.indexOf(sessionParamNameMatch); if (semicolon > 0) { // Parse session ID, and extract it from the decoded request URI // final int start = uriChunk.getStart(); final int sessionIdStart = semicolon + sessionParamNameMatch.length(); final int semicolon2 = uri.indexOf(';', sessionIdStart); isUpdated = semicolon2 >= 0; final int end = isUpdated ? semicolon2 : uri.length(); final String sessionId = uri.substring(sessionIdStart, end); final int jrouteIndex = sessionId.lastIndexOf(':'); if (jrouteIndex > 0) { setRequestedSessionId(sessionId.substring(0, jrouteIndex)); if (jrouteIndex < sessionId.length() - 1) { setJrouteId(sessionId.substring(jrouteIndex + 1)); } } else { setRequestedSessionId(sessionId); } setRequestedSessionURL(true); dataChunkStr.setString(uri.substring(0, semicolon)); } else { setRequestedSessionId(null); setRequestedSessionURL(false); } return isUpdated; } /** * 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; } private static class PathData { private final Request request; private String path; private PathResolver resolver; public PathData(Request request) { this.request = request; } public PathData(final Request request, final String path, final PathResolver resolver) { this.request = request; this.path = path; this.resolver = resolver; } public void setPath(final String path) { this.path = path; resolver = null; } public void setResolver(final PathResolver resolver) { this.resolver = resolver; path = null; } public String get() { return path != null ? path : resolver != null ? (path = resolver.resolve(request)) : null; } public void reset() { path = null; resolver = null; } } protected interface PathResolver { String resolve(Request request); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy