Tomcat servlet read request parameters

Time:2020-10-6

Read querystring on URL

org/apache/tomcat/embed/tomcat-embed-core/8.0.33/tomcat-embed-core-8.0.33-sources.jar!/org/apache/catalina/core/ApplicationDispatcher.java

private void doDispatch(ServletRequest request, ServletResponse response) throws ServletException, IOException 
private void doForward(ServletRequest request, ServletResponse response)
        throws ServletException, IOException
private void doInclude(ServletRequest request, ServletResponse response)
            throws ServletException, IOException        

Here’s dodispatch

private void doDispatch(ServletRequest request, ServletResponse response)
            throws ServletException, IOException {

        // Set up to handle the specified request and response
        State state = new State(request, response, false);

        // Create a wrapped response to use for this request
        wrapResponse(state);

        ApplicationHttpRequest wrequest =
            (ApplicationHttpRequest) wrapRequest(state);

        if (queryString != null) {
            wrequest.setQueryParams(queryString);
        }

        wrequest.setAttribute(Globals.DISPATCHER_TYPE_ATTR,
                DispatcherType.ASYNC);
        wrequest.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
                getCombinedPath());

        wrequest.setContextPath(context.getPath());
        wrequest.setRequestURI(requestURI);
        wrequest.setServletPath(servletPath);
        wrequest.setPathInfo(pathInfo);
        if (queryString != null) {
            wrequest.setQueryString(queryString);
            wrequest.setQueryParams(queryString);
        }

        invoke(state.outerRequest, state.outerResponse, state);
    }

ApplicationDispatcher.doDispatch–>ApplicationHttpRequest.setQueryString –>ApplicationHttpRequest.parseParameters –>mergeParameters

private void mergeParameters() {

        if ((queryParamString == null) || (queryParamString.length() < 1))
            return;

        // Parse the query string from the dispatch target
        Parameters paramParser = new Parameters();
        MessageBytes queryMB = MessageBytes.newInstance();
        queryMB.setString(queryParamString);

        String encoding = getCharacterEncoding();
        // No need to process null value, as ISO-8859-1 is the default encoding
        // in MessageBytes.toBytes().
        if (encoding != null) {
            try {
                queryMB.setCharset(B2CConverter.getCharset(encoding));
            } catch (UnsupportedEncodingException ignored) {
                // Fall-back to ISO-8859-1
            }
        }

        paramParser.setQuery(queryMB);
        paramParser.setQueryStringEncoding(encoding);
        paramParser.handleQueryParameters();

        // Insert the additional parameters from the dispatch target
        Enumeration<String> dispParamNames = paramParser.getParameterNames();
        while (dispParamNames.hasMoreElements()) {
            String dispParamName = dispParamNames.nextElement();
            String[] dispParamValues = paramParser.getParameterValues(dispParamName);
            String[] originalValues = parameters.get(dispParamName);
            if (originalValues == null) {
                parameters.put(dispParamName, dispParamValues);
                continue;
            }
            parameters.put(dispParamName, mergeValues(dispParamValues, originalValues));
        }
    }

Read the parameters of post and body

org/apache/tomcat/embed/tomcat-embed-core/8.0.33/tomcat-embed-core-8.0.33-sources.jar!/org/apache/catalina/connector/Request.java

/**
     * Parse request parameters.
     */
    protected void parseParameters() {

        parametersParsed = true;

        Parameters parameters = coyoteRequest.getParameters();
        boolean success = false;
        try {
            // Set this every time in case limit has been changed via JMX
            parameters.setLimit(getConnector().getMaxParameterCount());

            // getCharacterEncoding() may have been overridden to search for
            // hidden form field containing request encoding
            String enc = getCharacterEncoding();

            boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
            if (enc != null) {
                parameters.setEncoding(enc);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding(enc);
                }
            } else {
                parameters.setEncoding
                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                if (useBodyEncodingForURI) {
                    parameters.setQueryStringEncoding
                        (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
                }
            }

            parameters.handleQueryParameters();

            if (usingInputStream || usingReader) {
                success = true;
                return;
            }

            if( !getConnector().isParseBodyMethod(getMethod()) ) {
                success = true;
                return;
            }

            String contentType = getContentType();
            if (contentType == null) {
                contentType = "";
            }
            int semicolon = contentType.indexOf(';');
            if (semicolon >= 0) {
                contentType = contentType.substring(0, semicolon).trim();
            } else {
                contentType = contentType.trim();
            }

            if ("multipart/form-data".equals(contentType)) {
                parseParts(false);
                success = true;
                return;
            }

            if (!("application/x-www-form-urlencoded".equals(contentType))) {
                success = true;
                return;
            }

            int len = getContentLength();

            if (len > 0) {
                int maxPostSize = connector.getMaxPostSize();
                if ((maxPostSize >= 0) && (len > maxPostSize)) {
                    Context context = getContext();
                    if (context != null && context.getLogger().isDebugEnabled()) {
                        context.getLogger().debug(
                                sm.getString("coyoteRequest.postTooLarge"));
                    }
                    checkSwallowInput();
                    parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
                    return;
                }
                byte[] formData = null;
                if (len < CACHED_POST_LEN) {
                    if (postData == null) {
                        postData = new byte[CACHED_POST_LEN];
                    }
                    formData = postData;
                } else {
                    formData = new byte[len];
                }
                try {
                    if (readPostBody(formData, len) != len) {
                        parameters.setParseFailedReason(FailReason.REQUEST_BODY_INCOMPLETE);
                        return;
                    }
                } catch (IOException e) {
                    // Client disconnect
                    Context context = getContext();
                    if (context != null && context.getLogger().isDebugEnabled()) {
                        context.getLogger().debug(
                                sm.getString("coyoteRequest.parseParameters"),
                                e);
                    }
                    parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
                    return;
                }
                parameters.processParameters(formData, 0, len);
            } else if ("chunked".equalsIgnoreCase(
                    coyoteRequest.getHeader("transfer-encoding"))) {
                byte[] formData = null;
                try {
                    formData = readChunkedPostBody();
                } catch (IllegalStateException ise) {
                    // chunkedPostTooLarge error
                    parameters.setParseFailedReason(FailReason.POST_TOO_LARGE);
                    Context context = getContext();
                    if (context != null && context.getLogger().isDebugEnabled()) {
                        context.getLogger().debug(
                                sm.getString("coyoteRequest.parseParameters"),
                                ise);
                    }
                    return;
                } catch (IOException e) {
                    // Client disconnect
                    parameters.setParseFailedReason(FailReason.CLIENT_DISCONNECT);
                    Context context = getContext();
                    if (context != null && context.getLogger().isDebugEnabled()) {
                        context.getLogger().debug(
                                sm.getString("coyoteRequest.parseParameters"),
                                e);
                    }
                    return;
                }
                if (formData != null) {
                    parameters.processParameters(formData, 0, formData.length);
                }
            }
            success = true;
        } finally {
            if (!success) {
                parameters.setParseFailedReason(FailReason.UNKNOWN);
            }
        }

    }

readPostBody

/**
     * Read post body in an array.
     */
    protected int readPostBody(byte body[], int len)
        throws IOException {

        int offset = 0;
        do {
            int inputLen = getStream().read(body, offset, len - offset);
            if (inputLen <= 0) {
                return offset;
            }
            offset += inputLen;
        } while ((len - offset) > 0);
        return len;

    }

readChunkedPostBody

/**
     * Read chunked post body.
     */
    protected byte[] readChunkedPostBody() throws IOException {
        ByteChunk body = new ByteChunk();

        byte[] buffer = new byte[CACHED_POST_LEN];

        int len = 0;
        while (len > -1) {
            len = getStream().read(buffer, 0, CACHED_POST_LEN);
            if (connector.getMaxPostSize() >= 0 &&
                    (body.getLength() + len) > connector.getMaxPostSize()) {
                // Too much data
                checkSwallowInput();
                throw new IllegalStateException(
                        sm.getString("coyoteRequest.chunkedPostTooLarge"));
            }
            if (len > 0) {
                body.append(buffer, 0, len);
            }
        }
        if (body.getLength() == 0) {
            return null;
        }
        if (body.getLength() < body.getBuffer().length) {
            int length = body.getLength();
            byte[] result = new byte[length];
            System.arraycopy(body.getBuffer(), 0, result, 0, length);
            return result;
        }

        return body.getBuffer();
    }

parameters.processParameters(formData, 0, formData.length);

–>addParameter

public void addParameter( String key, String value )
            throws IllegalStateException {

        if( key==null ) {
            return;
        }

        parameterCount ++;
        if (limit > -1 && parameterCount > limit) {
            // Processing this parameter will push us over the limit. ISE is
            // what Request.parseParts() uses for requests that are too big
            setParseFailedReason(FailReason.TOO_MANY_PARAMETERS);
            throw new IllegalStateException(sm.getString(
                    "parameters.maxCountFail", Integer.valueOf(limit)));
        }

        ArrayList<String> values = paramHashValues.get(key);
        if (values == null) {
            values = new ArrayList<>(1);
            paramHashValues.put(key, values);
        }
        values.add(value);
    }

And then call it here

 public Enumeration<String> getParameterNames() {
        handleQueryParameters();
        return Collections.enumeration(paramHashValues.keySet());
    }

The getparameternames of request delegate the getparameternames of parameters

public Enumeration<String> getParameterNames() {

        if (!parametersParsed) {
            parseParameters();
        }

        return coyoteRequest.getParameters().getParameterNames();

    }

Finally, there is a unified merge process. See merge parameters above.

Usingreader and usinginputstream flag bits

getReader

@Override
    public BufferedReader getReader() throws IOException {

        if (usingInputStream) {
            throw new IllegalStateException
                (sm.getString("coyoteRequest.getReader.ise"));
        }

        usingReader = true;
        inputBuffer.checkConverter();
        if (reader == null) {
            reader = new CoyoteReader(inputBuffer);
        }
        return reader;

    }

getInputStream

@Override
    public ServletInputStream getInputStream() throws IOException {

        if (usingReader) {
            throw new IllegalStateException
                (sm.getString("coyoteRequest.getInputStream.ise"));
        }

        usingInputStream = true;
        if (inputStream == null) {
            inputStream = new CoyoteInputStream(inputBuffer);
        }
        return inputStream;

    }

When getinputstream() getreader() getparameter() of servlet request reads the parameters of post method, any one of them is used. If the other two methods are called, the parameters cannot be read. Getparameter can be used repeatedly on a single thread. If it is multithreaded, exceptions may occur because the method relies on parametersparsed, and the parameters of the Boolean are not atomic.

docs

  • Getinputstream() getreader() getparameter() of servlet request