private void prepareRequest()

in java/org/apache/coyote/ajp/AjpProcessor.java [635:860]


    private void prepareRequest() {

        // Translate the HTTP method code to a String.
        byte methodCode = requestHeaderMessage.getByte();
        if (methodCode != Constants.SC_M_JK_STORED) {
            String methodName = Constants.getMethodForCode(methodCode - 1);
            request.method().setString(methodName);
        }

        requestHeaderMessage.getBytes(request.protocol());
        requestHeaderMessage.getBytes(request.requestURI());

        requestHeaderMessage.getBytes(request.remoteAddr());
        requestHeaderMessage.getBytes(request.remoteHost());
        requestHeaderMessage.getBytes(request.localName());
        request.setLocalPort(requestHeaderMessage.getInt());

        if (socketWrapper != null) {
            request.peerAddr().setString(socketWrapper.getRemoteAddr());
        }

        boolean isSSL = requestHeaderMessage.getByte() != 0;
        if (isSSL) {
            request.scheme().setString("https");
        }

        // Decode headers
        MimeHeaders headers = request.getMimeHeaders();

        // Set this every time in case limit has been changed via JMX
        headers.setLimit(protocol.getMaxHeaderCount());

        boolean contentLengthSet = false;
        int hCount = requestHeaderMessage.getInt();
        for (int i = 0; i < hCount; i++) {
            String hName;

            // Header names are encoded as either an integer code starting
            // with 0xA0, or as a normal string (in which case the first
            // two bytes are the length).
            int isc = requestHeaderMessage.peekInt();
            int hId = isc & 0xFF;

            MessageBytes vMB;
            isc &= 0xFF00;
            if (0xA000 == isc) {
                requestHeaderMessage.getInt(); // To advance the read position
                hName = Constants.getHeaderForCode(hId - 1);
                vMB = headers.addValue(hName);
            } else {
                // reset hId -- if the header currently being read
                // happens to be 7 or 8 bytes long, the code below
                // will think it's the content-type header or the
                // content-length header - SC_REQ_CONTENT_TYPE=7,
                // SC_REQ_CONTENT_LENGTH=8 - leading to unexpected
                // behaviour. see bug 5861 for more information.
                hId = -1;
                requestHeaderMessage.getBytes(tmpMB);
                ByteChunk bc = tmpMB.getByteChunk();
                vMB = headers.addValue(bc.getBuffer(), bc.getStart(), bc.getLength());
            }

            requestHeaderMessage.getBytes(vMB);

            if (hId == Constants.SC_REQ_CONTENT_LENGTH || (hId == -1 && tmpMB.equalsIgnoreCase("Content-Length"))) {
                long cl = vMB.getLong();
                if (contentLengthSet) {
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    setErrorState(ErrorState.CLOSE_CLEAN, null);
                } else {
                    contentLengthSet = true;
                    // Set the content-length header for the request
                    request.setContentLength(cl);
                }
            } else if (hId == Constants.SC_REQ_CONTENT_TYPE || (hId == -1 && tmpMB.equalsIgnoreCase("Content-Type"))) {
                // just read the content-type header, so set it
                ByteChunk bchunk = vMB.getByteChunk();
                request.contentType().setBytes(bchunk.getBytes(), bchunk.getStart(), bchunk.getLength());
            }
        }

        // Decode extra attributes
        String secret = protocol.getSecret();
        boolean secretPresentInRequest = false;
        byte attributeCode;
        while ((attributeCode = requestHeaderMessage.getByte()) != Constants.SC_A_ARE_DONE) {

            switch (attributeCode) {
                case Constants.SC_A_REQ_ATTRIBUTE -> {
                    requestHeaderMessage.getBytes(tmpMB);
                    String n = tmpMB.toString();
                    requestHeaderMessage.getBytes(tmpMB);
                    String v = tmpMB.toString();
                    /*
                     * AJP13 misses to forward the local IP address and the remote port. Allow the AJP connector to add
                     * this info via private request attributes. We will accept the forwarded data and remove it from
                     * the public list of request attributes.
                     */
                    if (n.equals(Constants.SC_A_REQ_LOCAL_ADDR)) {
                        request.localAddr().setString(v);
                    } else if (n.equals(Constants.SC_A_REQ_REMOTE_PORT)) {
                        try {
                            request.setRemotePort(Integer.parseInt(v));
                        } catch (NumberFormatException nfe) {
                            // Ignore invalid value
                        }
                    } else if (n.equals(Constants.SC_A_SSL_PROTOCOL)) {
                        request.setAttribute(SSLSupport.SECURE_PROTOCOL_KEY, v);
                    } else if (n.equals("JK_LB_ACTIVATION")) {
                        request.setAttribute(n, v);
                    } else if (jakartaAttributeMapping.containsKey(n)) {
                        // AJP uses the Java Servlet attribute names.
                        // Need to convert these to Jakarta Servlet.
                        request.setAttribute(jakartaAttributeMapping.get(n), v);
                    } else if (iisTlsAttributes.contains(n)) {
                        // Allow IIS TLS attributes
                        request.setAttribute(n, v);
                    } else {
                        // All 'known' attributes will be processed by the previous
                        // blocks. Any remaining attribute is an 'arbitrary' one.
                        Pattern pattern = protocol.getAllowedRequestAttributesPatternInternal();
                        if (pattern != null && pattern.matcher(n).matches()) {
                            request.setAttribute(n, v);
                        } else {
                            log.warn(sm.getString("ajpprocessor.unknownAttribute", n));
                            response.setStatus(403);
                            setErrorState(ErrorState.CLOSE_CLEAN, null);
                        }
                    }
                }
                case Constants.SC_A_CONTEXT -> requestHeaderMessage.getBytes(tmpMB);

                // nothing
                case Constants.SC_A_SERVLET_PATH -> requestHeaderMessage.getBytes(tmpMB);

                // nothing
                case Constants.SC_A_REMOTE_USER -> {
                    boolean tomcatAuthorization = protocol.getTomcatAuthorization();
                    if (tomcatAuthorization || !protocol.getTomcatAuthentication()) {
                        // Implies tomcatAuthentication == false
                        requestHeaderMessage.getBytes(request.getRemoteUser());
                        request.setRemoteUserNeedsAuthorization(tomcatAuthorization);
                    } else {
                        // Ignore user information from reverse proxy
                        requestHeaderMessage.getBytes(tmpMB);
                    }
                }
                case Constants.SC_A_AUTH_TYPE -> {
                    if (protocol.getTomcatAuthorization() || !protocol.getTomcatAuthentication()) {
                        // Implies tomcatAuthentication == false
                        requestHeaderMessage.getBytes(request.getAuthType());
                    } else {
                        // Ignore user information from reverse proxy
                        requestHeaderMessage.getBytes(tmpMB);
                    }
                }
                case Constants.SC_A_QUERY_STRING -> requestHeaderMessage.getBytes(request.queryString());
                case Constants.SC_A_JVM_ROUTE -> requestHeaderMessage.getBytes(tmpMB);

                // nothing
                case Constants.SC_A_SSL_CERT ->
                    // SSL certificate extraction is lazy, moved to JkCoyoteHandler
                    requestHeaderMessage.getBytes(certificates);
                case Constants.SC_A_SSL_CIPHER -> {
                    requestHeaderMessage.getBytes(tmpMB);
                    request.setAttribute(SSLSupport.CIPHER_SUITE_KEY, tmpMB.toString());
                }
                case Constants.SC_A_SSL_SESSION -> {
                    requestHeaderMessage.getBytes(tmpMB);
                    request.setAttribute(SSLSupport.SESSION_ID_KEY, tmpMB.toString());
                }
                case Constants.SC_A_SSL_KEY_SIZE ->
                    request.setAttribute(SSLSupport.KEY_SIZE_KEY, Integer.valueOf(requestHeaderMessage.getInt()));
                case Constants.SC_A_STORED_METHOD -> requestHeaderMessage.getBytes(request.method());
                case Constants.SC_A_SECRET -> {
                    requestHeaderMessage.getBytes(tmpMB);
                    if (secret != null && !secret.isEmpty()) {
                        secretPresentInRequest = true;
                        if (!tmpMB.equals(secret)) {
                            response.setStatus(403);
                            setErrorState(ErrorState.CLOSE_CLEAN, null);
                        }
                    }
                }
                default -> {
                }
                // Ignore unknown attribute for backward compatibility
            }

        }

        // Check if secret was submitted if required
        if (secret != null && !secret.isEmpty() && !secretPresentInRequest) {
            response.setStatus(403);
            setErrorState(ErrorState.CLOSE_CLEAN, null);
        }

        // Check for a full URI (including protocol://host:port/)
        ByteChunk uriBC = request.requestURI().getByteChunk();
        if (uriBC.startsWithIgnoreCase("http", 0)) {

            int pos = uriBC.indexOf("://", 0, 3, 4);
            int uriBCStart = uriBC.getStart();
            if (pos != -1) {
                byte[] uriB = uriBC.getBytes();
                int slashPos = uriBC.indexOf('/', pos + 3);
                if (slashPos == -1) {
                    slashPos = uriBC.getLength();
                    // Set URI as "/"
                    request.requestURI().setBytes(uriB, uriBCStart + pos + 1, 1);
                } else {
                    request.requestURI().setBytes(uriB, uriBCStart + slashPos, uriBC.getLength() - slashPos);
                }
                MessageBytes hostMB = headers.setValue("host");
                hostMB.setBytes(uriB, uriBCStart + pos + 3, slashPos - pos - 3);
            }

        }

        MessageBytes valueMB = request.getMimeHeaders().getValue("host");
        parseHost(valueMB);

        if (!getErrorState().isIoAllowed()) {
            getAdapter().log(request, response, 0);
        }
    }