Progress on Set-Cookie header parser
This commit is contained in:
+116
-94
@@ -1424,8 +1424,8 @@ static int parse_date(Scanner *s, HTTP_Date *out)
|
|||||||
out->year
|
out->year
|
||||||
= (s->src[s->cur+0] - '0') * 1000
|
= (s->src[s->cur+0] - '0') * 1000
|
||||||
+ (s->src[s->cur+1] - '0') * 100
|
+ (s->src[s->cur+1] - '0') * 100
|
||||||
+ (s->src[s->cur+1] - '0') * 10
|
+ (s->src[s->cur+2] - '0') * 10
|
||||||
+ (s->src[s->cur+1] - '0') * 1;
|
+ (s->src[s->cur+3] - '0') * 1;
|
||||||
s->cur += 4;
|
s->cur += 4;
|
||||||
|
|
||||||
if (s->cur == s->len || s->src[s->cur] != ' ')
|
if (s->cur == s->len || s->src[s->cur] != ' ')
|
||||||
@@ -1469,46 +1469,57 @@ static bool is_cookie_octet(char c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
HTTP_String name;
|
HTTP_String name;
|
||||||
HTTP_String value;
|
HTTP_String value;
|
||||||
|
|
||||||
bool secure;
|
bool secure;
|
||||||
|
bool http_only;
|
||||||
|
|
||||||
|
bool have_date;
|
||||||
|
HTTP_Date date;
|
||||||
|
|
||||||
|
bool have_max_age;
|
||||||
|
uint32_t max_age;
|
||||||
|
|
||||||
|
bool have_domain;
|
||||||
|
HTTP_String domain;
|
||||||
|
|
||||||
} HTTP_SetCookie;
|
} HTTP_SetCookie;
|
||||||
|
|
||||||
void http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
void http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
||||||
{
|
{
|
||||||
char *src = str.ptr;
|
Scanner s = { str.ptr, str.len, 0 };
|
||||||
int len = str.len;
|
|
||||||
int cur = 0;
|
|
||||||
|
|
||||||
// cookie-name = token
|
// cookie-name = token
|
||||||
if (cur == len || !is_tchar(src[cur]))
|
if (s.cur == s.len || !is_tchar(s.src[s.cur]))
|
||||||
return -1;
|
return -1;
|
||||||
int off = cur;
|
int off = s.cur;
|
||||||
do
|
do
|
||||||
cur++;
|
s.cur++;
|
||||||
while (cur < len && is_tchar(src[cur]));
|
while (s.cur < s.len && is_tchar(s.src[s.cur]));
|
||||||
out->name = (HTTP_String) { src + off, cur - off };
|
out->name = (HTTP_String) { s.src + off, s.cur - off };
|
||||||
|
|
||||||
// cookie-pair = cookie-name "=" cookie-value
|
// cookie-pair = cookie-name "=" cookie-value
|
||||||
if (cur == len || src[cur] != '=')
|
if (s.cur == s.len || s.src[s.cur] != '=')
|
||||||
return -1;
|
return -1;
|
||||||
cur++;
|
s.cur++;
|
||||||
|
|
||||||
// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
|
// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
|
||||||
if (cur < len && src[cur] == '"') {
|
if (s.cur < s.len && s.src[s.cur] == '"') {
|
||||||
cur++; // Consume opening double quote
|
s.cur++; // Consume opening double quote
|
||||||
int off = cur;
|
int off = s.cur;
|
||||||
while (cur < len && is_cookie_octet(src[cur]))
|
while (s.cur < s.len && is_cookie_octet(s.src[s.cur]))
|
||||||
cur++;
|
s.cur++;
|
||||||
if (cur == len || src[cur] != '"')
|
if (s.cur == s.len || s.src[s.cur] != '"')
|
||||||
return -1; // Missing closing double quote
|
return -1; // Missing closing double quote
|
||||||
out->value = (HTTP_String) { src + off, cur - off };
|
out->value = (HTTP_String) { s.src + off, s.cur - off };
|
||||||
cur++; // Consume closing double quote
|
s.cur++; // Consume closing double quote
|
||||||
} else {
|
} else {
|
||||||
int off = cur;
|
int off = s.cur;
|
||||||
while (cur < len && is_cookie_octet(src[cur]))
|
while (s.cur < s.len && is_cookie_octet(s.src[s.cur]))
|
||||||
cur++;
|
s.cur++;
|
||||||
out->value = (HTTP_String) { src + off, cur - off };
|
out->value = (HTTP_String) { s.src + off, s.cur - off };
|
||||||
}
|
}
|
||||||
|
|
||||||
// *( ";" SP cookie-av )
|
// *( ";" SP cookie-av )
|
||||||
@@ -1516,93 +1527,104 @@ void http_parse_set_cookie(HTTP_String str, HTTP_SetCookie *out)
|
|||||||
// cookie-av = expires-av / max-age-av / domain-av /
|
// cookie-av = expires-av / max-age-av / domain-av /
|
||||||
// path-av / secure-av / httponly-av /
|
// path-av / secure-av / httponly-av /
|
||||||
// extension-av
|
// extension-av
|
||||||
while (cur < len && src[cur] == ';') {
|
out->secure = false;
|
||||||
cur++; // Consume semicolon
|
out->http_only = false;
|
||||||
|
out->have_date = false;
|
||||||
if (cur == len || src[cur] != ' ')
|
out->have_max_age = false;
|
||||||
return -1; // Missing space after semicolon
|
out->have_domain = false;
|
||||||
cur++;
|
while (consume_str(&s, HTTP_STR("; "))) {
|
||||||
|
if (consume_str(&s, HTTP_STR("Expires="))) {
|
||||||
|
|
||||||
// expires-av = "Expires=" sane-cookie-date
|
// expires-av = "Expires=" sane-cookie-date
|
||||||
if (7 < len - cur
|
if (parse_date(&s, &out->date) < 0)
|
||||||
&& src[cur+0] == 'E'
|
return -1;
|
||||||
&& src[cur+1] == 'x'
|
out->have_date = true;
|
||||||
&& src[cur+2] == 'p'
|
|
||||||
&& src[cur+3] == 'i'
|
|
||||||
&& src[cur+4] == 'r'
|
|
||||||
&& src[cur+5] == 'e'
|
|
||||||
&& src[cur+6] == 's'
|
|
||||||
&& src[cur+7] == '=') {
|
|
||||||
cur += 8;
|
|
||||||
|
|
||||||
// TODO
|
} else if (consume_str(&s, HTTP_STR("Max-Age="))) {
|
||||||
|
|
||||||
// max-age-av = "Max-Age=" non-zero-digit *DIGIT
|
// max-age-av = "Max-Age=" non-zero-digit *DIGIT
|
||||||
} else if (7 < len - cur
|
|
||||||
&& src[cur+0] == 'M'
|
uint32_t value = 0;
|
||||||
&& src[cur+1] == 'a'
|
if (s.cur == s.len || !is_digit(s.src[s.cur]))
|
||||||
&& src[cur+2] == 'x'
|
return -1;
|
||||||
&& src[cur+3] == '-'
|
do {
|
||||||
&& src[cur+4] == 'A'
|
int d = s.src[s.cur++] - '0';
|
||||||
&& src[cur+5] == 'g'
|
if (value > (UINT32_MAX - d) / 10)
|
||||||
&& src[cur+6] == 'e'
|
return -1;
|
||||||
&& src[cur+7] == '=') {
|
value = value * 10 + d;
|
||||||
cur += 8;
|
} while (s.cur < s.len && is_digit(s.src[s.cur]));
|
||||||
|
|
||||||
|
out->have_max_age = true;
|
||||||
|
out->max_age = value;
|
||||||
|
|
||||||
|
} else if (consume_str(&s, HTTP_STR("Domain="))) {
|
||||||
|
|
||||||
// domain-av = "Domain=" domain-value
|
// domain-av = "Domain=" domain-value
|
||||||
} else if (6 < len - cur
|
|
||||||
&& src[cur+0] == 'D'
|
|
||||||
&& src[cur+1] == 'o'
|
|
||||||
&& src[cur+2] == 'm'
|
|
||||||
&& src[cur+3] == 'a'
|
|
||||||
&& src[cur+4] == 'i'
|
|
||||||
&& src[cur+5] == 'n'
|
|
||||||
&& src[cur+6] == '=') {
|
|
||||||
cur += 7;
|
|
||||||
|
|
||||||
// domain-value = <subdomain>
|
// domain-value = <subdomain>
|
||||||
// ; defined in RFC 1034, Section 3.5
|
// ; defined in RFC 1034, Section 3.5
|
||||||
// TODO
|
//
|
||||||
|
// From RFC 1034:
|
||||||
|
// <subdomain> ::= <label> | <subdomain> "." <label>
|
||||||
|
// <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
|
||||||
|
// <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
|
||||||
|
// <let-dig-hyp> ::= <let-dig> | "-"
|
||||||
|
// <let-dig> ::= <letter> | <digit>
|
||||||
|
// <letter> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case
|
||||||
|
// <digit> ::= any one of the ten digits 0 through 9
|
||||||
|
//
|
||||||
|
// If my understanding is correct, a domain is a list of labels
|
||||||
|
// concatenated by dots. Each label may contain letters, digits,
|
||||||
|
// hyphens, but the first character must be a letter and the last
|
||||||
|
// one can't be a hyphen.
|
||||||
|
|
||||||
|
int off = s.cur;
|
||||||
|
if (s.cur == s.len || !is_alpha(s.src[s.cur]))
|
||||||
|
return -1;
|
||||||
|
do
|
||||||
|
s.cur++;
|
||||||
|
while (s.cur < s.len && (
|
||||||
|
is_digit(s.src[s.cur]) ||
|
||||||
|
is_alpha(s.src[s.cur]) ||
|
||||||
|
s.src[s.cur] == '-'));
|
||||||
|
|
||||||
|
if (s.src[s.cur-1] == '-')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while (s.cur < s.len && s.src[s.cur] == '.') {
|
||||||
|
s.cur++; // Consume dot
|
||||||
|
|
||||||
|
if (s.cur == s.len || !is_alpha(s.src[s.cur]))
|
||||||
|
return -1;
|
||||||
|
do
|
||||||
|
s.cur++;
|
||||||
|
while (s.cur < s.len && (
|
||||||
|
is_digit(s.src[s.cur]) ||
|
||||||
|
is_alpha(s.src[s.cur]) ||
|
||||||
|
s.src[s.cur] == '-'));
|
||||||
|
|
||||||
|
if (s.src[s.cur-1] == '-')
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
out->have_domain = true;
|
||||||
|
out->domain = (HTTP_String) { s.src + off, s.cur - off };
|
||||||
|
|
||||||
|
} else if (consume_str(&s, HTTP_STR("Path="))) {
|
||||||
|
|
||||||
// path-av = "Path=" path-value
|
// path-av = "Path=" path-value
|
||||||
|
|
||||||
} else if (4 < len - cur
|
|
||||||
&& src[cur+0] == 'P'
|
|
||||||
&& src[cur+1] == 'a'
|
|
||||||
&& src[cur+2] == 't'
|
|
||||||
&& src[cur+3] == 'h'
|
|
||||||
&& src[cur+4] == '=') {
|
|
||||||
cur += 5;
|
|
||||||
|
|
||||||
// path-value = <any CHAR except CTLs or ";">
|
// path-value = <any CHAR except CTLs or ";">
|
||||||
//
|
|
||||||
// TODO
|
assert(0); // TODO
|
||||||
|
|
||||||
|
} else if (consume_str(&s, HTTP_STR("Secure"))) {
|
||||||
|
|
||||||
// secure-av = "Secure"
|
// secure-av = "Secure"
|
||||||
} else if (5 < len - cur
|
out->secure = true;
|
||||||
&& src[cur+0] == 'S'
|
|
||||||
&& src[cur+1] == 'e'
|
|
||||||
&& src[cur+2] == 'c'
|
|
||||||
&& src[cur+3] == 'u'
|
|
||||||
&& src[cur+4] == 'r'
|
|
||||||
&& src[cur+5] == 'e') {
|
|
||||||
cur += 6;
|
|
||||||
|
|
||||||
// TODO
|
} else if (consume_str(&s, HTTP_STR("HttpOnly"))) {
|
||||||
|
|
||||||
// httponly-av = "HttpOnly"
|
// httponly-av = "HttpOnly"
|
||||||
} else if (
|
out->http_only = false;
|
||||||
&& src[cur+0] == 'H'
|
|
||||||
&& src[cur+1] == 't'
|
|
||||||
&& src[cur+2] == 't'
|
|
||||||
&& src[cur+3] == 'p'
|
|
||||||
&& src[cur+4] == 'O'
|
|
||||||
&& src[cur+5] == 'n'
|
|
||||||
&& src[cur+6] == 'l'
|
|
||||||
&& src[cur+7] == 'y') {
|
|
||||||
cur += 8;
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return -1; // Invalid attribute
|
return -1; // Invalid attribute
|
||||||
|
|||||||
Reference in New Issue
Block a user