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

io.undertow.server.protocol.ajp.AjpRequestParser Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 34.0.0.Final
Show newest version
/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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 io.undertow.server.protocol.ajp;

import static io.undertow.util.Methods.ACL;
import static io.undertow.util.Methods.BASELINE_CONTROL;
import static io.undertow.util.Methods.CHECKIN;
import static io.undertow.util.Methods.CHECKOUT;
import static io.undertow.util.Methods.COPY;
import static io.undertow.util.Methods.DELETE;
import static io.undertow.util.Methods.GET;
import static io.undertow.util.Methods.HEAD;
import static io.undertow.util.Methods.LABEL;
import static io.undertow.util.Methods.LOCK;
import static io.undertow.util.Methods.MERGE;
import static io.undertow.util.Methods.MKACTIVITY;
import static io.undertow.util.Methods.MKCOL;
import static io.undertow.util.Methods.MKWORKSPACE;
import static io.undertow.util.Methods.MOVE;
import static io.undertow.util.Methods.OPTIONS;
import static io.undertow.util.Methods.POST;
import static io.undertow.util.Methods.PROPFIND;
import static io.undertow.util.Methods.PROPPATCH;
import static io.undertow.util.Methods.PUT;
import static io.undertow.util.Methods.REPORT;
import static io.undertow.util.Methods.SEARCH;
import static io.undertow.util.Methods.TRACE;
import static io.undertow.util.Methods.UNCHECKOUT;
import static io.undertow.util.Methods.UNLOCK;
import static io.undertow.util.Methods.UPDATE;
import static io.undertow.util.Methods.VERSION_CONTROL;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.security.impl.ExternalAuthenticationMechanism;
import io.undertow.server.Connectors;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.BadRequestException;
import io.undertow.util.Headers;
import io.undertow.util.HttpString;
import io.undertow.util.ParameterLimitException;
import io.undertow.util.URLUtils;
import io.undertow.util.UrlDecodeException;

/**
 * @author Stuart Douglas
 */
public class AjpRequestParser {

    private final String encoding;
    private final boolean doDecode;
    private final boolean slashDecodingFlag;
    private final int maxParameters;
    private final int maxHeaders;
    private final boolean allowUnescapedCharactersInUrl;
    private final Pattern allowedRequestAttributesPattern;

    private static final HttpString[] HTTP_HEADERS;

    public static final int FORWARD_REQUEST = 2;
    public static final int CPONG = 9;
    public static final int CPING = 10;
    public static final int SHUTDOWN = 7;


    private static final HttpString[] HTTP_METHODS;
    private static final String[] ATTRIBUTES;
    private static final Set ATTR_SET;

    public static final String QUERY_STRING = "query_string";

    public static final String SSL_CERT = "ssl_cert";

    public static final String CONTEXT = "context";

    public static final String SERVLET_PATH = "servlet_path";

    public static final String REMOTE_USER = "remote_user";

    public static final String AUTH_TYPE = "auth_type";

    public static final String ROUTE = "route";

    public static final String SSL_CIPHER = "ssl_cipher";

    public static final String SSL_SESSION = "ssl_session";

    public static final String REQ_ATTRIBUTE = "req_attribute";

    public static final String SSL_KEY_SIZE = "ssl_key_size";

    public static final String SECRET = "secret";

    public static final String STORED_METHOD = "stored_method";

    public static final String AJP_REMOTE_PORT = "AJP_REMOTE_PORT";

    static {
        HTTP_METHODS = new HttpString[28];
        HTTP_METHODS[1] = OPTIONS;
        HTTP_METHODS[2] = GET;
        HTTP_METHODS[3] = HEAD;
        HTTP_METHODS[4] = POST;
        HTTP_METHODS[5] = PUT;
        HTTP_METHODS[6] = DELETE;
        HTTP_METHODS[7] = TRACE;
        HTTP_METHODS[8] = PROPFIND;
        HTTP_METHODS[9] = PROPPATCH;
        HTTP_METHODS[10] = MKCOL;
        HTTP_METHODS[11] = COPY;
        HTTP_METHODS[12] = MOVE;
        HTTP_METHODS[13] = LOCK;
        HTTP_METHODS[14] = UNLOCK;
        HTTP_METHODS[15] = ACL;
        HTTP_METHODS[16] = REPORT;
        HTTP_METHODS[17] = VERSION_CONTROL;
        HTTP_METHODS[18] = CHECKIN;
        HTTP_METHODS[19] = CHECKOUT;
        HTTP_METHODS[20] = UNCHECKOUT;
        HTTP_METHODS[21] = SEARCH;
        HTTP_METHODS[22] = MKWORKSPACE;
        HTTP_METHODS[23] = UPDATE;
        HTTP_METHODS[24] = LABEL;
        HTTP_METHODS[25] = MERGE;
        HTTP_METHODS[26] = BASELINE_CONTROL;
        HTTP_METHODS[27] = MKACTIVITY;

        HTTP_HEADERS = new HttpString[0xF];
        HTTP_HEADERS[1] = Headers.ACCEPT;
        HTTP_HEADERS[2] = Headers.ACCEPT_CHARSET;
        HTTP_HEADERS[3] = Headers.ACCEPT_ENCODING;
        HTTP_HEADERS[4] = Headers.ACCEPT_LANGUAGE;
        HTTP_HEADERS[5] = Headers.AUTHORIZATION;
        HTTP_HEADERS[6] = Headers.CONNECTION;
        HTTP_HEADERS[7] = Headers.CONTENT_TYPE;
        HTTP_HEADERS[8] = Headers.CONTENT_LENGTH;
        HTTP_HEADERS[9] = Headers.COOKIE;
        HTTP_HEADERS[0xA] = Headers.COOKIE2;
        HTTP_HEADERS[0xB] = Headers.HOST;
        HTTP_HEADERS[0xC] = Headers.PRAGMA;
        HTTP_HEADERS[0xD] = Headers.REFERER;
        HTTP_HEADERS[0xE] = Headers.USER_AGENT;

        ATTRIBUTES = new String[0xE];
        ATTRIBUTES[1] = CONTEXT;
        ATTRIBUTES[2] = SERVLET_PATH;
        ATTRIBUTES[3] = REMOTE_USER;
        ATTRIBUTES[4] = AUTH_TYPE;
        ATTRIBUTES[5] = QUERY_STRING;
        ATTRIBUTES[6] = ROUTE;
        ATTRIBUTES[7] = SSL_CERT;
        ATTRIBUTES[8] = SSL_CIPHER;
        ATTRIBUTES[9] = SSL_SESSION;
        ATTRIBUTES[10] = REQ_ATTRIBUTE;
        ATTRIBUTES[11] = SSL_KEY_SIZE;
        ATTRIBUTES[12] = SECRET;
        ATTRIBUTES[13] = STORED_METHOD;
        ATTR_SET = new HashSet<>(Arrays.asList(ATTRIBUTES));
    }

    public AjpRequestParser(String encoding, boolean doDecode, int maxParameters, int maxHeaders, boolean slashDecodingFlag, boolean allowUnescapedCharactersInUrl) {
        this(encoding, doDecode, maxParameters, maxHeaders, slashDecodingFlag, allowUnescapedCharactersInUrl, null);
    }

    public AjpRequestParser(String encoding, boolean doDecode, int maxParameters, int maxHeaders, boolean slashDecodingFlag, boolean allowUnescapedCharactersInUrl, String allowedRequestAttributesPattern) {
        this.encoding = encoding;
        this.doDecode = doDecode;
        this.maxParameters = maxParameters;
        this.maxHeaders = maxHeaders;
        this.slashDecodingFlag = slashDecodingFlag;
        this.allowUnescapedCharactersInUrl = allowUnescapedCharactersInUrl;
        if (allowedRequestAttributesPattern != null && !allowedRequestAttributesPattern.isEmpty()) {
            this.allowedRequestAttributesPattern = Pattern.compile(allowedRequestAttributesPattern);
        } else {
            this.allowedRequestAttributesPattern = null;
        }
    }


    public void parse(final ByteBuffer buf, final AjpRequestParseState state, final HttpServerExchange exchange) throws IOException, BadRequestException {
        if (!buf.hasRemaining()) {
            return;
        }
        switch (state.state) {
            case AjpRequestParseState.BEGIN: {
                IntegerHolder result = parse16BitInteger(buf, state);
                if (!result.readComplete) {
                    return;
                } else {
                    if (result.value != 0x1234) {
                        throw new BadRequestException(UndertowMessages.MESSAGES.wrongMagicNumber(result.value));
                    }
                }
            }
            case AjpRequestParseState.READING_DATA_SIZE: {
                IntegerHolder result = parse16BitInteger(buf, state);
                if (!result.readComplete) {
                    state.state = AjpRequestParseState.READING_DATA_SIZE;
                    return;
                } else {
                    state.dataSize = result.value;
                }
            }
            case AjpRequestParseState.READING_PREFIX_CODE: {
                if (!buf.hasRemaining()) {
                    state.state = AjpRequestParseState.READING_PREFIX_CODE;
                    return;
                } else {
                    final byte prefix = buf.get();
                    state.prefix = prefix;
                    if (prefix != 2) {
                        state.state = AjpRequestParseState.DONE;
                        return;
                    }
                }
            }
            case AjpRequestParseState.READING_METHOD: {
                if (!buf.hasRemaining()) {
                    state.state = AjpRequestParseState.READING_METHOD;
                    return;
                } else {
                    int method = buf.get();
                    if (method > 0 && method < 28) {
                        exchange.setRequestMethod(HTTP_METHODS[method]);
                    } else if((method & 0xFF) != 0xFF) {
                        throw new BadRequestException("Unknown method type " + method);
                    }
                }
            }
            case AjpRequestParseState.READING_PROTOCOL: {
                StringHolder result = parseString(buf, state, StringType.OTHER);
                if (result.readComplete) {
                    //TODO: more efficient way of doing this
                    exchange.setProtocol(HttpString.tryFromString(result.value));
                } else {
                    state.state = AjpRequestParseState.READING_PROTOCOL;
                    return;
                }
            }
            case AjpRequestParseState.READING_REQUEST_URI: {
                StringHolder result = parseString(buf, state, StringType.URL);
                if (result.readComplete) {
                    int colon = result.value.indexOf(';');
                    if (colon == -1) {
                        String res = decode(result.value, result.containsUrlCharacters);
                        if(result.containsUnencodedCharacters || (allowUnescapedCharactersInUrl && result.containsUrlCharacters)) {
                            //we decode if the URL was non-compliant, and contained incorrectly encoded characters
                            //there is not really a 'correct' thing to do in this situation, but this seems the least incorrect
                            exchange.setRequestURI(res);
                        } else {
                            exchange.setRequestURI(result.value);
                        }
                        exchange.setRequestPath(res);
                        exchange.setRelativePath(res);
                    } else {
                        final StringBuilder resBuilder = new StringBuilder();
                        int pathParamParsingIndex = 0;
                        try {
                            do {
                                final String url = result.value.substring(pathParamParsingIndex, colon);
                                resBuilder.append(decode(url, result.containsUrlCharacters));
                                pathParamParsingIndex = colon + 1 + URLUtils.parsePathParams(result.value.substring(colon + 1), exchange, encoding, doDecode && result.containsUrlCharacters, maxParameters);
                                colon = result.value.indexOf(';', pathParamParsingIndex + 1);
                            } while (pathParamParsingIndex < result.value.length() && colon != -1);
                        } catch (ParameterLimitException e) {
                            UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(e);
                            state.badRequest = true;
                        }
                        if (pathParamParsingIndex < result.value.length()) {
                            final String url = result.value.substring(pathParamParsingIndex);
                            resBuilder.append(decode(url, result.containsUrlCharacters));
                        }
                        final String res = resBuilder.toString();
                        if(result.containsUnencodedCharacters) {
                            exchange.setRequestURI(res);
                        } else {
                            exchange.setRequestURI(result.value);
                        }
                        exchange.setRequestPath(res);
                        exchange.setRelativePath(res);
                    }
                } else {
                    state.state = AjpRequestParseState.READING_REQUEST_URI;
                    return;
                }
            }
            case AjpRequestParseState.READING_REMOTE_ADDR: {
                StringHolder result = parseString(buf, state, StringType.OTHER);
                if (result.readComplete) {
                    state.remoteAddress = result.value;
                } else {
                    state.state = AjpRequestParseState.READING_REMOTE_ADDR;
                    return;
                }
            }
            case AjpRequestParseState.READING_REMOTE_HOST: {
                StringHolder result = parseString(buf, state, StringType.OTHER);
                if (!result.readComplete) {
                    state.state = AjpRequestParseState.READING_REMOTE_HOST;
                    return;
                }
            }
            case AjpRequestParseState.READING_SERVER_NAME: {
                StringHolder result = parseString(buf, state, StringType.OTHER);
                if (result.readComplete) {
                    state.serverAddress = result.value;
                } else {
                    state.state = AjpRequestParseState.READING_SERVER_NAME;
                    return;
                }
            }
            case AjpRequestParseState.READING_SERVER_PORT: {
                IntegerHolder result = parse16BitInteger(buf, state);
                if (result.readComplete) {
                    state.serverPort = result.value;
                } else {
                    state.state = AjpRequestParseState.READING_SERVER_PORT;
                    return;
                }
            }
            case AjpRequestParseState.READING_IS_SSL: {
                if (!buf.hasRemaining()) {
                    state.state = AjpRequestParseState.READING_IS_SSL;
                    return;
                } else {
                    final byte isSsl = buf.get();
                    if (isSsl != 0) {
                        exchange.setRequestScheme("https");
                    } else {
                        exchange.setRequestScheme("http");
                    }
                }
            }
            case AjpRequestParseState.READING_NUM_HEADERS: {
                IntegerHolder result = parse16BitInteger(buf, state);
                if (!result.readComplete) {
                    state.state = AjpRequestParseState.READING_NUM_HEADERS;
                    return;
                } else {
                    state.numHeaders = result.value;
                    if(state.numHeaders > maxHeaders) {
                        UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(new BadRequestException(UndertowMessages.MESSAGES.tooManyHeaders(maxHeaders)));
                        state.badRequest = true;
                    }
                }
            }
            case AjpRequestParseState.READING_HEADERS: {
                int readHeaders = state.readHeaders;
                while (readHeaders < state.numHeaders) {
                    if (state.currentHeader == null) {
                        StringHolder result = parseString(buf, state, StringType.HEADER);
                        if (!result.readComplete) {
                            state.state = AjpRequestParseState.READING_HEADERS;
                            state.readHeaders = readHeaders;
                            return;
                        }
                        if (result.header != null) {
                            state.currentHeader = result.header;
                        } else {
                            state.currentHeader = HttpString.tryFromString(result.value);
                            Connectors.verifyToken(state.currentHeader);
                        }
                    }
                    StringHolder result = parseString(buf, state, StringType.OTHER);
                    if (!result.readComplete) {
                        state.state = AjpRequestParseState.READING_HEADERS;
                        state.readHeaders = readHeaders;
                        return;
                    }
                    if(!state.badRequest) {
                        exchange.getRequestHeaders().add(state.currentHeader, result.value);
                    }
                    state.currentHeader = null;
                    ++readHeaders;
                }
            }
            case AjpRequestParseState.READING_ATTRIBUTES: {
                for (; ; ) {
                    if (state.currentAttribute == null && state.currentIntegerPart == -1) {
                        if (!buf.hasRemaining()) {
                            state.state = AjpRequestParseState.READING_ATTRIBUTES;
                            return;
                        }
                        int val = (0xFF & buf.get());
                        if (val == 0xFF) {
                            state.state = AjpRequestParseState.DONE;
                            return;
                        } else if (val == 0x0A) {
                            //we need to read the name. We overload currentIntegerPart to avoid adding another state field
                            state.currentIntegerPart = 1;
                        } else {
                            if(val == 0 || val >= ATTRIBUTES.length) {
                                //ignore unknown codes for compatibility
                                continue;
                            }
                            state.currentAttribute = ATTRIBUTES[val];
                        }

                    }
                    if (state.currentIntegerPart == 1) {
                        StringHolder result = parseString(buf, state, StringType.OTHER);
                        if (!result.readComplete) {
                            state.state = AjpRequestParseState.READING_ATTRIBUTES;
                            return;
                        }
                        state.currentAttribute = result.value;
                        state.currentIntegerPart = -1;
                    }
                    String result;
                    boolean decodingAlreadyDone = false;
                    if (state.currentAttribute.equals(SSL_KEY_SIZE)) {
                        IntegerHolder resultHolder = parse16BitInteger(buf, state);
                        if (!resultHolder.readComplete) {
                            state.state = AjpRequestParseState.READING_ATTRIBUTES;
                            return;
                        }
                        result = Integer.toString(resultHolder.value);
                    } else {
                        StringHolder resultHolder = parseString(buf, state, state.currentAttribute.equals(QUERY_STRING) ? StringType.QUERY_STRING : StringType.OTHER);
                        if (!resultHolder.readComplete) {
                            state.state = AjpRequestParseState.READING_ATTRIBUTES;
                            return;
                        }
                        if(resultHolder.containsUnencodedCharacters || (resultHolder.containsUrlCharacters && allowUnescapedCharactersInUrl)) {
                            try {
                                result = decode(resultHolder.value, true);
                            } catch (UrlDecodeException | UnsupportedEncodingException e) {
                                UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(e);
                                state.badRequest = true;
                                result = resultHolder.value;
                            }
                            decodingAlreadyDone = true;
                        } else {
                            result = resultHolder.value;
                        }
                    }
                    //query string.
                    if (state.currentAttribute.equals(QUERY_STRING)) {
                        String resultAsQueryString = result == null ? "" : result;
                        exchange.setQueryString(resultAsQueryString);
                        try {
                            URLUtils.parseQueryString(resultAsQueryString, exchange, encoding, doDecode && !decodingAlreadyDone, maxParameters);
                        } catch (ParameterLimitException | IllegalArgumentException e) {
                            UndertowLogger.REQUEST_IO_LOGGER.failedToParseRequest(e);
                            state.badRequest = true;
                        }
                    } else if (state.currentAttribute.equals(REMOTE_USER)) {
                        exchange.putAttachment(ExternalAuthenticationMechanism.EXTERNAL_PRINCIPAL, result);
                        exchange.putAttachment(HttpServerExchange.REMOTE_USER, result);
                    } else if (state.currentAttribute.equals(AUTH_TYPE)) {
                        exchange.putAttachment(ExternalAuthenticationMechanism.EXTERNAL_AUTHENTICATION_TYPE, result);
                    } else if (state.currentAttribute.equals(STORED_METHOD)) {
                        HttpString requestMethod = new HttpString(result);
                        Connectors.verifyToken(requestMethod);
                        exchange.setRequestMethod(requestMethod);
                    } else if (state.currentAttribute.equals(AJP_REMOTE_PORT)) {
                        state.remotePort = Integer.parseInt(result);
                    } else if (state.currentAttribute.equals(SSL_SESSION)) {
                        state.sslSessionId = result;
                    } else if (state.currentAttribute.equals(SSL_CIPHER)) {
                        state.sslCipher = result;
                    } else if (state.currentAttribute.equals(SSL_CERT)) {
                        state.sslCert = result;
                    } else if (state.currentAttribute.equals(SSL_KEY_SIZE)) {
                        state.sslKeySize = result;
                    } else {
                        // other attributes
                        if (state.attributes == null) {
                            state.attributes = new TreeMap<>();
                        }
                        if (ATTR_SET.contains(state.currentAttribute)) {
                            // known attirubtes
                            state.attributes.put(state.currentAttribute, result);
                        } else if (allowedRequestAttributesPattern != null) {
                            // custom allowed attributes
                            Matcher m = allowedRequestAttributesPattern.matcher(state.currentAttribute);
                            if (m.matches()) {
                                state.attributes.put(state.currentAttribute, result);
                            }
                        }
                    }
                    state.currentAttribute = null;
                }
            }
        }
        state.state = AjpRequestParseState.DONE;
    }

    private String decode(String url, final boolean containsUrlCharacters) throws UnsupportedEncodingException {
        if (doDecode && containsUrlCharacters) {
            try {
                final StringBuilder decodeBuffer = new StringBuilder();
                return URLUtils.decode(url, this.encoding, slashDecodingFlag, false, decodeBuffer);
            } catch (Exception e) {
                throw UndertowMessages.MESSAGES.failedToDecodeURL(url, encoding, e);
            }
        }
        return url;
    }

    protected HttpString headers(int offset) {
        return HTTP_HEADERS[offset];
    }

    public static final int STRING_LENGTH_MASK = 1 << 31;

    protected IntegerHolder parse16BitInteger(ByteBuffer buf, AjpRequestParseState state) {
        if (!buf.hasRemaining()) {
            return new IntegerHolder(-1, false);
        }
        int number = state.currentIntegerPart;
        if (number == -1) {
            number = (buf.get() & 0xFF);
        }
        if (buf.hasRemaining()) {
            final byte b = buf.get();
            int result = ((0xFF & number) << 8) + (b & 0xFF);
            state.currentIntegerPart = -1;
            return new IntegerHolder(result, true);
        } else {
            state.currentIntegerPart = number;
            return new IntegerHolder(-1, false);
        }
    }

    protected StringHolder parseString(ByteBuffer buf, AjpRequestParseState state, StringType type) throws UnsupportedEncodingException, BadRequestException {
        boolean containsUrlCharacters = state.containsUrlCharacters;
        boolean containsUnencodedUrlCharacters = state.containsUnencodedUrlCharacters;
        if (!buf.hasRemaining()) {
            return new StringHolder(null, false, false, false);
        }
        int stringLength = state.stringLength;
        if (stringLength == -1) {
            int number = buf.get() & 0xFF;
            if (buf.hasRemaining()) {
                final byte b = buf.get();
                stringLength = ((0xFF & number) << 8) + (b & 0xFF);
            } else {
                state.stringLength = number | STRING_LENGTH_MASK;
                return new StringHolder(null, false, false, false);
            }
        } else if ((stringLength & STRING_LENGTH_MASK) != 0) {
            int number = stringLength & ~STRING_LENGTH_MASK;
            stringLength = ((0xFF & number) << 8) + (buf.get() & 0xFF);
        }
        if (type == StringType.HEADER && (stringLength & 0xFF00) != 0) {
            state.stringLength = -1;
            return new StringHolder(headers(stringLength & 0xFF));
        }
        if (stringLength == 0xFFFF) {
            //OxFFFF means null
            state.stringLength = -1;
            return new StringHolder(null, true, false, false);
        }
        int length = state.getCurrentStringLength();
        while (length < stringLength) {
            if (!buf.hasRemaining()) {
                state.stringLength = stringLength;
                state.containsUrlCharacters = containsUrlCharacters;
                state.containsUnencodedUrlCharacters = containsUnencodedUrlCharacters;
                return new StringHolder(null, false, false, false);
            }
            byte c = buf.get();
            if(type == StringType.QUERY_STRING && (c == '+' || c == '%' || c < 0 || c > 127 )) {
                if (c < 0 || c > 127) {
                    if (!allowUnescapedCharactersInUrl) {
                        throw new BadRequestException();
                    } else {
                        containsUnencodedUrlCharacters = true;
                    }
                }
                containsUrlCharacters = true;
            } else if(type == StringType.URL && (c == '%' || c < 0 || c > 127 )) {
                if(c < 0 ) {
                    if(!allowUnescapedCharactersInUrl) {
                        throw new BadRequestException();
                    } else {
                        containsUnencodedUrlCharacters = true;
                    }
                }
                containsUrlCharacters = true;
            }
            state.addStringByte(c);
            ++length;
        }

        if (buf.hasRemaining()) {
            buf.get(); //null terminator
            String value = state.getStringAndClear();
            state.stringLength = -1;
            state.containsUrlCharacters = false;
            state.containsUnencodedUrlCharacters = containsUnencodedUrlCharacters;
            return new StringHolder(value, true, containsUrlCharacters, containsUnencodedUrlCharacters);
        } else {
            state.stringLength = stringLength;
            state.containsUrlCharacters = containsUrlCharacters;
            state.containsUnencodedUrlCharacters = containsUnencodedUrlCharacters;
            return new StringHolder(null, false, false, false);
        }
    }

    protected static class IntegerHolder {
        public final int value;
        public final boolean readComplete;

        private IntegerHolder(int value, boolean readComplete) {
            this.value = value;
            this.readComplete = readComplete;
        }
    }

    protected static class StringHolder {
        public final String value;
        public final HttpString header;
        final boolean readComplete;
        final boolean containsUrlCharacters;
        final boolean containsUnencodedCharacters;

        private StringHolder(String value, boolean readComplete, boolean containsUrlCharacters, boolean containsUnencodedCharacters) {
            this.value = value;
            this.readComplete = readComplete;
            this.containsUrlCharacters = containsUrlCharacters;
            this.containsUnencodedCharacters = containsUnencodedCharacters;
            this.header = null;
        }

        private StringHolder(HttpString value) {
            this.value = null;
            this.readComplete = true;
            this.header = value;
            this.containsUrlCharacters = false;
            this.containsUnencodedCharacters = false;
        }
    }

    enum StringType {
        HEADER,
        URL,
        QUERY_STRING,
        OTHER
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy