static HttpSetCookie parseSetCookie()

in servicetalk-http-api/src/main/java/io/servicetalk/http/api/DefaultHttpSetCookie.java [144:302]


    static HttpSetCookie parseSetCookie(final CharSequence setCookieString, boolean validateContent,
                                        @Nullable CharSequence name, int i) {
        CharSequence value = null;
        CharSequence path = null;
        CharSequence domain = null;
        CharSequence expires = null;
        Long maxAge = null;
        SameSite sameSite = null;
        boolean isWrapped = false;
        boolean isSecure = false;
        boolean isHttpOnly = false;
        int begin;
        ParseState parseState;
        if (name != null) {
            parseState = ParseState.ParsingValue;
            begin = i;
        } else {
            parseState = ParseState.Unknown;
            begin = 0;
        }

        while (i < setCookieString.length()) {
            final char c = setCookieString.charAt(i);
            switch (c) {
                case '=':
                    if (name == null) {
                        if (i <= begin) {
                            throw new IllegalArgumentException("cookie name cannot be null or empty");
                        }
                        name = setCookieString.subSequence(begin, i);
                        if (validateContent) {
                            validateCookieTokenAndHeaderName(name);
                        }
                        parseState = ParseState.ParsingValue;
                    } else if (parseState == ParseState.Unknown) {
                        final CharSequence avName = setCookieString.subSequence(begin, i);
                        final CharSequence newState = AV_FIELD_NAMES.get(avName);
                        if (newState != null) {
                            parseState = ((ParseStateCharSequence) newState).state;
                        }
                    } else {
                        throw new IllegalArgumentException("unexpected = at index: " + i);
                    }
                    ++i;
                    begin = i;
                    break;
                case '"':
                    if (parseState == ParseState.ParsingValue) {
                        if (isWrapped) {
                            parseState = ParseState.Unknown;
                            value = setCookieString.subSequence(begin, i);
                            // Increment by 3 because we are skipping DQUOTE SEMI SP
                            i += 3;
                        } else {
                            isWrapped = true;
                            ++i;
                        }
                        begin = i;
                    } else if (value == null) {
                        throw new IllegalArgumentException("unexpected quote at index: " + i);
                    }
                    ++i;
                    break;
                case '%':
                    if (validateContent) {
                        extractAndValidateCookieHexValue(setCookieString, i);
                    }
                    // Increment by 4 because we are skipping %0x##
                    i += 4;
                    break;
                case ';':
                    // end of value, or end of av-value
                    if (i + 1 == setCookieString.length()) {
                        throw new IllegalArgumentException("unexpected trailing ';'");
                    }
                    switch (parseState) {
                        case ParsingValue:
                            value = setCookieString.subSequence(begin, i);
                            break;
                        case ParsingPath:
                            path = setCookieString.subSequence(begin, i);
                            break;
                        case ParsingDomain:
                            domain = setCookieString.subSequence(begin, i);
                            break;
                        case ParsingExpires:
                            expires = setCookieString.subSequence(begin, i);
                            break;
                        case ParsingMaxAge:
                            maxAge = parseLong(setCookieString.subSequence(begin, i));
                            break;
                        case ParsingSameSite:
                            sameSite = fromSequence(setCookieString, begin, i);
                            break;
                        default:
                            if (name == null) {
                                throw new IllegalArgumentException("cookie value not found at index " + i);
                            }
                            final CharSequence avName = setCookieString.subSequence(begin, i);
                            if (contentEqualsIgnoreCase(avName, "secure")) {
                                isSecure = true;
                            } else if (contentEqualsIgnoreCase(avName, "httponly")) {
                                isHttpOnly = true;
                            }
                            break;
                    }
                    parseState = ParseState.Unknown;
                    i += 2;
                    begin = i;
                    break;
                default:
                    if (validateContent && parseState != ParseState.ParsingExpires) {
                        validateCookieOctetHexValue(c);
                    }
                    ++i;
                    break;
            }
        }

        if (begin < i) {
            // end of value, or end of av-value
            // check for "secure" and "httponly"
            switch (parseState) {
                case ParsingValue:
                    value = setCookieString.subSequence(begin, i);
                    break;
                case ParsingPath:
                    path = setCookieString.subSequence(begin, i);
                    break;
                case ParsingDomain:
                    domain = setCookieString.subSequence(begin, i);
                    break;
                case ParsingExpires:
                    expires = setCookieString.subSequence(begin, i);
                    break;
                case ParsingSameSite:
                    sameSite = fromSequence(setCookieString, begin, i);
                    break;
                case ParsingMaxAge:
                    maxAge = parseLong(setCookieString.subSequence(begin, i));
                    break;
                default:
                    if (name == null) {
                        throw new IllegalArgumentException("cookie value not found at index " + i);
                    }
                    final CharSequence avName = setCookieString.subSequence(begin, i);
                    if (contentEqualsIgnoreCase(avName, "secure")) {
                        isSecure = true;
                    } else if (contentEqualsIgnoreCase(avName, "httponly")) {
                        isHttpOnly = true;
                    }
                    break;
            }
        }

        assert name != null && value != null; // these are checked at runtime in the constructor
        return new DefaultHttpSetCookie(name, value, path, domain, expires, maxAge, sameSite, isWrapped, isSecure,
                isHttpOnly);
    }