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);
}