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

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

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2016 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 *
 *
 * This file incorporates work covered by the following copyright and
 * permission notice:
 *
 * 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.
 */
// Portions Copyright [2022] [Payara Foundation and/or its affiliates]
package org.apache.catalina.connector;

import java.io.IOException;
import java.nio.charset.Charset;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.Arrays;
import java.util.stream.Collectors;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import com.sun.appserv.ProxyHandler;
import java.io.CharConversionException;
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
import org.apache.catalina.LogFacade;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ContainerBase;
import org.apache.catalina.util.ServerInfo;
import org.glassfish.grizzly.http.Method;
import org.glassfish.grizzly.http.server.AfterServiceListener;
import org.glassfish.grizzly.http.server.HttpHandler;
import org.glassfish.grizzly.http.Note;
import org.glassfish.grizzly.http.server.util.MappingData;
import org.glassfish.grizzly.http.util.ByteChunk;
import org.glassfish.grizzly.http.util.CharChunk;
import org.glassfish.grizzly.http.util.DataChunk;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.web.valve.GlassFishValve;
import org.glassfish.web.valve.ServletContainerInterceptor;

/**
 * Implementation of a request processor which delegates the processing to a
 * Coyote processor.
 *
 * @author Craig R. McClanahan
 * @author Remy Maucherat
 * @version $Revision: 1.34 $ $Date: 2007/08/24 18:38:28 $
 */

public class CoyoteAdapter extends HttpHandler {

    private static final Logger log = LogFacade.getLogger();
    private static final ResourceBundle rb = log.getResourceBundle();

    // -------------------------------------------------------------- Constants
    private static final String POWERED_BY = "Servlet/3.1 JSP/2.3 " +
            "(" + ServerInfo.getServerInfo() + " Java/" +
            System.getProperty("java.vm.vendor") + "/" +
            System.getProperty("java.specification.version") + ")";


//    protected boolean v3Enabled =
//        Boolean.valueOf(System.getProperty("v3.grizzly.useMapper", "true"));
    
    
//    public static final int ADAPTER_NOTES = 1;

    static final String JVM_ROUTE = System.getProperty("jvmRoute");

    private Collection interceptors = null;

    protected static final boolean ALLOW_BACKSLASH =
        Boolean.valueOf(System.getProperty("org.glassfish.grizzly.tcp.tomcat5.CoyoteAdapter.ALLOW_BACKSLASH", "false"));

    private static final boolean COLLAPSE_ADJACENT_SLASHES =
        Boolean.valueOf(System.getProperty(
            "com.sun.enterprise.web.collapseAdjacentSlashes", "true"));

    /**
     * When mod_jk is used, the adapter must be invoked the same way 
     * Tomcat does by invoking service(...) and the afterService(...). This
     * is a hack to make it compatible with Tomcat 5|6.
     */
    private boolean compatWithTomcat = false;
    
    private String serverName = ServerInfo.getPublicServerInfo();
    
    // Make sure this value is always aligned with {@link ContainerMapper}
    // (@see com.sun.enterprise.v3.service.impl.ContainerMapper)
    protected final static Note MAPPING_DATA =
            org.glassfish.grizzly.http.server.Request.createNote("MappingData");

    static final Note CATALINA_REQUEST_NOTE =
            org.glassfish.grizzly.http.server.Request.createNote(Request.class.getName());
    static final Note CATALINA_RESPONSE_NOTE =
            org.glassfish.grizzly.http.server.Request.createNote(Response.class.getName());

    static final CatalinaAfterServiceListener catalinaAfterServiceListener =
            new CatalinaAfterServiceListener();

    // Make sure this value is always aligned with {@link ContainerMapper}
    // (@see com.sun.enterprise.v3.service.impl.ContainerMapper)
    private final static Note DATA_CHUNK =
            org.glassfish.grizzly.http.server.Request.createNote("DataChunk");
    
    // ----------------------------------------------------------- Constructors


    /**
     * Construct a new CoyoteProcessor associated with the specified connector.
     *
     * @param connector CoyoteConnector that owns this processor
     */
    public CoyoteAdapter(Connector connector) {
        super();
        this.connector = connector;
        initServletInterceptors();
    }


    // ----------------------------------------------------- Instance Variables


    /**
     * The CoyoteConnector with which this processor is associated.
     */
    private Connector connector = null;


    // -------------------------------------------------------- Adapter Methods


    /**
     * Service method.
     */
    @Override
    public void service(org.glassfish.grizzly.http.server.Request req,
                        org.glassfish.grizzly.http.server.Response res)
            throws Exception {

        res.getResponse().setAllowCustomReasonPhrase(Constants.USE_CUSTOM_STATUS_MSG_IN_HEADER);

        Request request = req.getNote(CATALINA_REQUEST_NOTE);
        Response response = req.getNote(CATALINA_RESPONSE_NOTE);
        
        // Grizzly already parsed, decoded, and mapped the request.
        // Let's re-use this info here, before firing the
        // requestStartEvent probe, so that the mapping data will be
        // available to any probe event listener via standard
        // ServletRequest APIs (such as getContextPath())
        MappingData md = req.getNote(MAPPING_DATA);
        final boolean v3Enabled = md != null;
        if (request == null) {

            // Create objects
            request = (Request) connector.createRequest();
            response = (Response) connector.createResponse();

            // Link objects
            request.setResponse(response);
            response.setRequest(request);

            // Set as notes
            req.setNote(CATALINA_REQUEST_NOTE, request);
            req.setNote(CATALINA_RESPONSE_NOTE, response);
//            res.setNote(ADAPTER_NOTES, response);

            // Set query string encoding
            req.getRequest().getRequestURIRef().setDefaultURIEncoding(Charset.forName(connector.getURIEncoding()));
        }

        request.setCoyoteRequest(req);
        response.setCoyoteResponse(res);

        if (v3Enabled && !compatWithTomcat) {
            request.setMappingData(md);
            request.updatePaths(md);
        }

        req.addAfterServiceListener(catalinaAfterServiceListener);
        
        try {
            doService(req, request, res, response, v3Enabled);

            // Request may want to initialize async processing
            request.onExitService();
        } catch (Throwable t) {
            log.log(Level.SEVERE, LogFacade.REQUEST_PROCESSING_EXCEPTION, t);
        }
    }

    private void enteringServletContainer(Request req, Response res) {
        if (interceptors == null)
            return;
        for(ServletContainerInterceptor interceptor:interceptors) {
            try{
                interceptor.preInvoke(req, res);
            } catch (Throwable th) {
                log.log(Level.SEVERE, LogFacade.INTERNAL_ERROR, th);
            }
        }
    }

    private void leavingServletContainer(Request req, Response res) {
        if (interceptors == null)
            return;
        for(ServletContainerInterceptor interceptor:interceptors) {
            try{
                interceptor.postInvoke(req, res);
            } catch (Throwable th) {
                log.log(Level.SEVERE, LogFacade.INTERNAL_ERROR, th);
            }
        }
    }

    private void initServletInterceptors() {
        try {
            ServiceLocator services = org.glassfish.internal.api.Globals.getDefaultHabitat();
            interceptors = services.getAllServices(ServletContainerInterceptor.class);
        } catch (Throwable th) {
            log.log(Level.SEVERE, LogFacade.FAILED_TO_INITIALIZE_THE_INTERCEPTOR, th);
        }
    }
    

    private void doService(final org.glassfish.grizzly.http.server.Request req,
                           final Request request,
                           final org.glassfish.grizzly.http.server.Response res,
                           final Response response,
                           final boolean v3Enabled)
            throws Exception {
        
        // START SJSAS 6331392
        // Check connector for disabled state
        if (!connector.isEnabled()) {
            String msg = MessageFormat.format(rb.getString(LogFacade.HTTP_LISTENER_DISABLED),
                                              String.valueOf(connector.getPort()));
            if (log.isLoggable(Level.FINE)) {
                log.log(Level.FINE, msg);
            }
            response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);
            return;
        }
        // END SJSAS 6331392

////            "X-Powered-By" header is set by GlassfishHttpCodecFilter
//        if (connector.isXpoweredBy()) {
//            response.addHeader("X-Powered-By", POWERED_BY);
//        }


        // Parse and set Catalina and configuration specific 
        // request parameters
        if ( postParseRequest(req, request, res, response, v3Enabled) ) {

            // START S1AS 6188932
            boolean authPassthroughEnabled = 
                connector.getAuthPassthroughEnabled();
            ProxyHandler proxyHandler = connector.getProxyHandler();
            if (authPassthroughEnabled && proxyHandler != null) {

                // START SJSAS 6397218
                if (proxyHandler.getSSLKeysize(
                        (HttpServletRequest)request.getRequest()) > 0) {
                    request.setSecure(true);
                }
                // END SJSAS 6397218

                X509Certificate[] certs = null;
                try {
                    certs = proxyHandler.getSSLClientCertificateChain(
                                request.getRequest());
                } catch (CertificateException ce) {
                    log.log(Level.SEVERE, LogFacade.PARSING_CLIENT_CERT_EXCEPTION,
                            ce);
                }
                if (certs != null) {
                    request.setAttribute(Globals.CERTIFICATES_ATTR,
                                         certs);
                }
                    
            }
            // END S1AS 6188932

////            "Server" header is set by GlassfishHttpCodecFilter
//            if (serverName != null && !serverName.isEmpty()) {
//                response.addHeader("Server", serverName);
//            }
                
            // Invoke the web container
            connector.requestStartEvent(request.getRequest(),
                request.getHost(), request.getContext());
            Container container = connector.getContainer();
            enteringServletContainer(request, response);
            try {
                request.lockSession();
                if (container.getPipeline().hasNonBasicValves() ||
                        container.hasCustomPipeline()) {
                    container.getPipeline().invoke(request, response);
                } else {
                    // Invoke host directly
                    Host host = request.getHost();
                    if (host == null) {
                        response.sendError(HttpServletResponse.SC_BAD_REQUEST);

                        String msg = MessageFormat.format(rb.getString(LogFacade.NO_HOST_MATCHES_SERVER_NAME_INFO),
                                                          request.getRequest().getServerName());
                        response.setDetailMessage(msg);
                        return;
                    }
                    if (host.getPipeline().hasNonBasicValves() ||
                            host.hasCustomPipeline()) {
                        host.getPipeline().invoke(request, response);
                    } else {
                        GlassFishValve hostValve = host.getPipeline().getBasic();
                        hostValve.invoke(request, response);
                        // Error handling

                        // Exclude TRACE from Allow header if allowTrace is false
                        if(!connector.getAllowTrace()) {
                            removeTraceMethod(response);
                        }
                        hostValve.postInvoke(request, response);
                    }
                }
            } finally {
                try {
                    connector.requestEndEvent(request.getRequest(),
                        request.getHost(), request.getContext(),
                        response.getStatus());
                } finally {
                    leavingServletContainer(request, response);
                }
            }
        }

    }
    // ------------------------------------------------------ Protected Methods

    /**
     * Method to remove TRACE method from Response Allow header
     * @param response of type Response to be processed
     */
    private void removeTraceMethod(final Response response) {
        String header = response.getHeader("Allow");
        if (header != null) {
            header = Arrays.stream(header.split(",")).map(s-> s.trim()).filter(s->!s.equals("TRACE")).
                    collect(Collectors.joining(", "));
            response.setHeader("Allow", header);
        }
    }

    /**
     * Parse additional request parameters.
     */
    protected boolean postParseRequest(final org.glassfish.grizzly.http.server.Request req,
                                       final Request request,
                                       final org.glassfish.grizzly.http.server.Response res,
                                       final Response response,
                                       final boolean v3Enabled)
        throws Exception {
        // XXX the processor may have set a correct scheme and port prior to this point, 
        // in ajp13 protocols dont make sense to get the port from the connector...
        // otherwise, use connector configuration
        request.setSecure(req.isSecure());

        // URI decoding
        DataChunk decodedURI;
        try {
            decodedURI = req.getRequest().getRequestURIRef().getDecodedRequestURIBC();
        } catch (CharConversionException cce) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid URI");
            return false;
        }

        // Normalize Decoded URI
        if (!normalize(decodedURI, response)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, response.getDetailMessage());
            return false;
        }
        
        if (compatWithTomcat || !v3Enabled) {
//            decodedURI.duplicate(req.requestURI());
//            try {
//              req.getURLDecoder().convert(decodedURI, false);
//            } catch (IOException ioe) {
//              res.setStatus(400);
//              res.setMessage("Invalid URI: " + ioe.getMessage());
//              return false;
//            }

            /* GlassFish Issue 2339
            // Normalize decoded URI
            if (!normalize(req.decodedURI())) {
                res.setStatus(400);
                res.setMessage("Invalid URI");
                return false;
            }
            */

            // Set the remote principal
            String principal = req.getRemoteUser();
            if (principal != null) {
                request.setUserPrincipal(new CoyotePrincipal(principal));
            }

            // Set the authorization type
            String authtype = req.getAuthType();
            if (authtype != null) {
                request.setAuthType(authtype);
            }

            /* CR 6309511
            // URI character decoding
            convertURI(decodedURI, request);

            // Parse session Id
            parseSessionId(req, request);
             */
            // START CR 6309511
//             URI character decoding
//            request.convertURI(decodedURI);

            // START GlassFish Issue 2339
            // Normalize decoded URI
//            if (!normalize(decodedURI)) {
//                res.setStatus(400);
//                res.setMessage("Invalid URI");
//                return false;
//            }
            // END GlassFish Issue 2339
        }
        // END CR 6309511

        /*
         * Remove any parameters from the URI, so they won't be considered
         * by the mapping algorithm, and save them in a temporary CharChunk,
         * so that any session id param may be parsed once the target
         * context, which may use a custom session parameter name, has been
         * identified
         */
        final CharChunk uriParamsCC = request.getURIParams();
        final CharChunk uriCC = decodedURI.getCharChunk();
        final int semicolon = uriCC.indexOf(';');
        if (semicolon > 0) {
            final int absSemicolon = uriCC.getStart() + semicolon;
            uriParamsCC.setChars(uriCC.getBuffer(), absSemicolon,
                uriCC.getEnd() - absSemicolon);
            decodedURI.setChars(uriCC.getBuffer(), uriCC.getStart(),
                absSemicolon - uriCC.getStart());
        }
 
        if (compatWithTomcat || !v3Enabled) {
            /*mod_jk*/
            DataChunk localDecodedURI = decodedURI;
            if (semicolon > 0) {
                localDecodedURI = req.getNote(DATA_CHUNK);
                if (localDecodedURI == null) {
                    localDecodedURI = DataChunk.newInstance();
                    req.setNote(DATA_CHUNK, localDecodedURI);
                }
                localDecodedURI.duplicate(decodedURI);
            }
            connector.getMapper().map(req.getRequest().serverName(), localDecodedURI,
                                  request.getMappingData());
            MappingData md = request.getMappingData();
            req.setNote(MAPPING_DATA, md);
            request.updatePaths(md);
        }

        // FIXME: the code below doesnt belongs to here,
        // this is only have sense
        // in Http11, not in ajp13..
        // At this point the Host header has been processed.
        // Override if the proxyPort/proxyHost are set
        String proxyName = connector.getProxyName();
        int proxyPort = connector.getProxyPort();
        if (proxyPort != 0) {
            req.setServerPort(proxyPort);
        }
        if (proxyName != null) {
            req.setServerName(proxyName);
        }

        Context ctx = (Context) request.getMappingData().context;

        // Parse session id
        if (ctx != null) {
            if (req.isRequestedSessionIdFromURL() &&
                    Globals.SESSION_PARAMETER_NAME.equals(ctx.getSessionParameterName())) {
                request.obtainSessionId();
            } else if (!uriParamsCC.isNull()) {
//            String sessionParam = ";" + ctx.getSessionParameterName() + "=";
                request.parseSessionId(ctx.getSessionParameterName(), uriParamsCC);
            }
        }

        // START GlassFish 1024
        request.setDefaultContext(request.getMappingData().isDefaultContext);
        // END GlassFish 1024

        // START SJSAS 6253524
        // request.setContext((Context) request.getMappingData().context);
        // END SJSAS 6253524
        // START SJSAS 6253524
        request.setContext(ctx);
        // END SJSAS 6253524

        if (ctx != null && !uriParamsCC.isNull()) {
            request.parseSessionVersion(uriParamsCC);
        }

        if (!uriParamsCC.isNull()) {
            request.parseJReplica(uriParamsCC);
        }

        request.setWrapper((Wrapper) request.getMappingData().wrapper);

        // Filter trace method
        if (!connector.getAllowTrace() && Method.TRACE.equals(req.getMethod())) {
            Wrapper wrapper = request.getWrapper();
            String header = null;
            if (wrapper != null) {
                String[] methods = wrapper.getServletMethods();
                if (methods != null) {
                    for (String method : methods) {
                        // Exclude TRACE from methods returned in Allow header
                        if ("TRACE".equals(method)) {
                            continue;
                        }
                        if (header == null) {
                            header = method;
                        } else {
                            header += ", " + method;
                        }
                    }
                }
            }
            res.setStatus(405, "TRACE method is not allowed");
            res.addHeader("Allow", header);
            return false;
        }

        // Possible redirect
        DataChunk redirectPathMB = request.getMappingData().redirectPath;
        // START SJSAS 6253524
        // if (!redirectPathMB.isNull()) {
        // END SJSAS 6253524
        // START SJSAS 6253524
        if (!redirectPathMB.isNull()
            && (!ctx.hasAdHocPaths()
                || (ctx.getAdHocServletName(((HttpServletRequest)
                        request.getRequest()).getServletPath()) == null))) {
        // END SJSAS 6253524
            String redirectPath = redirectPathMB.toString();
            String query = request.getQueryString();
            if (request.isRequestedSessionIdFromURL()) {
                // This is not optimal, but as this is not very common, it
                // shouldn't matter
                redirectPath = redirectPath + ";" + ctx.getSessionParameterName() + "=" 
                    + request.getRequestedSessionId();
            }            
            // START GlassFish 936
            redirectPath = response.encode(redirectPath);
            // END GlassFish 936
            if (query != null) {
                // This is not optimal, but as this is not very common, it
                // shouldn't matter
                redirectPath = redirectPath + "?" + query;
            }

            // START CR 6590921
            boolean authPassthroughEnabled = 
                connector.getAuthPassthroughEnabled();
            ProxyHandler proxyHandler = connector.getProxyHandler();
            if (authPassthroughEnabled && proxyHandler != null) {

                if (proxyHandler.getSSLKeysize(
                        (HttpServletRequest)request.getRequest()) > 0) {
                    request.setSecure(true);
                }
            }
            // END CR 6590921
            // Issue a permanent redirect
            response.sendRedirect(redirectPath, false);

            return false;
        }

        // Parse session Id
        /* CR 6309511
        parseSessionCookiesId(req, request);
         */
        // START CR 6309511
        request.parseSessionCookiesId();
        // END CR 6309511

        // START SJSAS 6346226
        request.parseJrouteCookie();
        // END SJSAS 6346226

        return true;
    }


    /**
     * Normalize URI.
     * 

* This method normalizes "\", "//", "/./" and "/../". This method will * throw an error when trying to go above the root, or if the URI contains * a null byte. * * @param uriDC URI DataChunk to be normalized * @param response The Catalina Response that a detail error message will be added to if an error is encountered */ public static boolean normalize(DataChunk uriDC, Response response) { DataChunk.Type type = uriDC.getType(); if (type == DataChunk.Type.Chars) { return normalizeChars(uriDC, response); } return normalizeBytes(uriDC, response); } private static boolean normalizeBytes(DataChunk uriDC, Response response) { ByteChunk uriBC = uriDC.getByteChunk(); byte[] b = uriBC.getBytes(); int start = uriBC.getStart(); int end = uriBC.getEnd(); // An empty URL is not acceptable if (start == end) { response.setDetailMessage("Invalid URI: Empty URL"); return false; } // URL * is acceptable if ((end - start == 1) && b[start] == (byte) '*') { return true; } int pos = 0; int index = 0; // Replace '\' with '/' // Check for null byte for (pos = start; pos < end; pos++) { if (b[pos] == (byte) '\\') { if (ALLOW_BACKSLASH) { b[pos] = (byte) '/'; } else { response.setDetailMessage("Invalid URI: Backslashes not allowed"); return false; } } if (b[pos] == (byte) 0) { response.setDetailMessage("Invalid URI: Null byte found during request normalization"); return false; } } // The URL must start with '/' if (b[start] != (byte) '/') { response.setDetailMessage("Invalid URI: Request must start with /"); return false; } // Replace "//" with "/" if (COLLAPSE_ADJACENT_SLASHES) { for (pos = start; pos < (end - 1); pos++) { if (b[pos] == (byte) '/') { while ((pos + 1 < end) && (b[pos + 1] == (byte) '/')) { copyBytes(b, pos, pos + 1, end - pos - 1); end--; } } } } // If the URI ends with "/." or "/..", then we append an extra "/" // Note: It is possible to extend the URI by 1 without any side effect // as the next character is a non-significant WS. if (((end - start) > 2) && (b[end - 1] == (byte) '.')) { if ((b[end - 2] == (byte) '/') || ((b[end - 2] == (byte) '.') && (b[end - 3] == (byte) '/'))) { b[end] = (byte) '/'; end++; } } uriBC.setEnd(end); index = 0; // Resolve occurrences of "/./" in the normalized path while (true) { index = uriBC.indexOf("/./", 0, 3, index); if (index < 0) { break; } copyBytes(b, start + index, start + index + 2, end - start - index - 2); end = end - 2; uriBC.setEnd(end); } index = 0; // Resolve occurrences of "/../" in the normalized path while (true) { index = uriBC.indexOf("/../", 0, 4, index); if (index < 0) { break; } // Prevent from going outside our context if (index == 0) { response.setDetailMessage("Invalid URI: Request traversed outside of allowed context"); return false; } int index2 = -1; for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) { if (b[pos] == (byte) '/') { index2 = pos; } } copyBytes(b, start + index2, start + index + 3, end - start - index - 3); end = end + index2 - index - 3; uriBC.setEnd(end); index = index2; } uriBC.setBytes(b, start, end); return true; } private static boolean normalizeChars(DataChunk uriDC, Response response) { CharChunk uriCC = uriDC.getCharChunk(); char[] c = uriCC.getChars(); int start = uriCC.getStart(); int end = uriCC.getEnd(); // URL * is acceptable if ((end - start == 1) && c[start] == '*') { return true; } int pos = 0; int index = 0; // Replace '\' with '/' // Check for null char for (pos = start; pos < end; pos++) { if (c[pos] == '\\') { if (ALLOW_BACKSLASH) { c[pos] = '/'; } else { response.setDetailMessage("Invalid URI: Backslashes not allowed"); return false; } } if (c[pos] == (char) 0) { response.setDetailMessage("Invalid URI: Null byte found during request normalization"); return false; } } // The URL must start with '/' if (c[start] != '/') { response.setDetailMessage("Invalid URI: Request must start with /"); return false; } // Replace "//" with "/" if (COLLAPSE_ADJACENT_SLASHES) { for (pos = start; pos < (end - 1); pos++) { if (c[pos] == '/') { while ((pos + 1 < end) && (c[pos + 1] == (char) '/')) { copyChars(c, pos, pos + 1, end - pos - 1); end--; } } } } // If the URI ends with "/." or "/..", then we append an extra "/" // Note: It is possible to extend the URI by 1 without any side effect // as the next character is a non-significant WS. if (((end - start) > 2) && (c[end - 1] == (char) '.')) { if ((c[end - 2] == '/') || ((c[end - 2] == '.') && (c[end - 3] == '/'))) { c[end] = '/'; end++; } } uriCC.setEnd(end); index = 0; // Resolve occurrences of "/./" in the normalized path while (true) { index = uriCC.indexOf("/./", 0, 3, index); if (index < 0) { break; } copyChars(c, start + index, start + index + 2, end - start - index - 2); end = end - 2; uriCC.setEnd(end); } index = 0; // Resolve occurrences of "/../" in the normalized path while (true) { index = uriCC.indexOf("/../", 0, 4, index); if (index < 0) { break; } // Prevent from going outside our context if (index == 0) { response.setDetailMessage("Invalid URI: Request traversed outside of allowed context"); return false; } int index2 = -1; for (pos = start + index - 1; (pos >= 0) && (index2 < 0); pos --) { if (c[pos] == '/') { index2 = pos; } } copyChars(c, start + index2, start + index + 3, end - start - index - 3); end = end + index2 - index - 3; uriCC.setEnd(end); index = index2; } uriCC.setChars(c, start, end); return true; } // ------------------------------------------------------ Protected Methods /** * Copy an array of bytes to a different position. Used during * normalization. */ protected static void copyBytes(byte[] b, int dest, int src, int len) { for (int pos = 0; pos < len; pos++) { b[pos + dest] = b[pos + src]; } } /** * Copy an array of chars to a different position. Used during * normalization. */ private static void copyChars(char[] c, int dest, int src, int len) { for (int pos = 0; pos < len; pos++) { c[pos + dest] = c[pos + src]; } } /** * Log a message on the Logger associated with our Container (if any) * * @param message Message to be logged */ protected void log(String message) { log.log(Level.INFO, message); } /** * Log a message on the Logger associated with our Container (if any) * * @param message Message to be logged * @param throwable Associated exception */ protected void log(String message, Throwable throwable) { log.log(Level.SEVERE, message, throwable); } /** * Character conversion of the a US-ASCII MessageBytes. */ /* CR 6309511 protected void convertMB(MessageBytes mb) { // This is of course only meaningful for bytes if (mb.getType() != MessageBytes.T_BYTES) return; ByteChunk bc = mb.getByteChunk(); CharChunk cc = mb.getCharChunk(); cc.allocate(bc.getLength(), -1); // Default encoding: fast conversion byte[] bbuf = bc.getBuffer(); char[] cbuf = cc.getBuffer(); int start = bc.getStart(); for (int i = 0; i < bc.getLength(); i++) { cbuf[i] = (char) (bbuf[i + start] & 0xff); } mb.setChars(cbuf, 0, bc.getLength()); } */ // START SJSAS 6349248 /** * Notify all container event listeners that a particular event has * occurred for this Adapter. The default implementation performs * this notification synchronously using the calling thread. * * @param type Event type * @param data Event data */ public void fireAdapterEvent(String type, Object data) { if ( connector != null && connector.getContainer() != null) { try{ ((ContainerBase)connector.getContainer()) .fireContainerEvent(type,data); } catch (Throwable t){ log.log(Level.SEVERE, LogFacade.REQUEST_PROCESSING_EXCEPTION, t); } } } // END SJSAS 6349248 /** * Return true when an instance is executed the same way it does in Tomcat. */ public boolean isCompatWithTomcat() { return compatWithTomcat; } /** * true if this class needs to be compatible with Tomcat * Adapter class. Since Tomcat Adapter implementation doesn't support * the afterService method, the afterService method must be invoked * inside the service method. */ public void setCompatWithTomcat(boolean compatWithTomcat) { this.compatWithTomcat = compatWithTomcat; // Add server header if (compatWithTomcat){ serverName = "Apache/" + serverName; } else { // Recalculate. serverName = ServerInfo.getPublicServerInfo(); } } /** * Gets the port of this CoyoteAdapter. * * @return the port of this CoyoteAdapter */ public int getPort() { return connector.getPort(); } /** * AfterServiceListener, which is responsible for recycle catalina request and response * objects. */ static final class CatalinaAfterServiceListener implements AfterServiceListener { @Override public void onAfterService(final org.glassfish.grizzly.http.server.Request request) { final Request servletRequest = request.getNote(CATALINA_REQUEST_NOTE); final Response servletResponse = request.getNote(CATALINA_RESPONSE_NOTE); if (servletRequest != null) { try { if (!servletRequest.isUpgrade()) { servletResponse.finishResponse(); } else { servletResponse.setUpgrade(servletRequest.isUpgrade()); } } catch (Exception e) { log.log(Level.SEVERE, LogFacade.REQUEST_PROCESSING_EXCEPTION, e); } finally { try { servletRequest.unlockSession(); } finally { servletRequest.recycle(); servletResponse.recycle(); } } } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy