Improve HTTP Origin response headers

Re-introduce "Origin: null" handling, because actually it is in the
spec.
This commit is contained in:
Andrew Bettison 2015-10-13 18:44:46 +10:30
parent cee5241951
commit 4564e955e3
3 changed files with 48 additions and 22 deletions

View File

@ -269,6 +269,7 @@ static void _mover_mem(char *dst, const char *src, size_t len)
memmove(dst, src, len);
}
#if 0
/* Allocate space from the start of the request buffer to hold the given substring plus a
* terminating NUL.
*
@ -281,6 +282,7 @@ static int _reserve_substring(struct http_request *r, const char **resp, struct
assert(strnchr(str.start, len, '\0') == NULL);
return _reserve(r, resp, str.start, len, _mover_mem);
}
#endif
/* The same as _reserve(), but takes a NUL-terminated string as a source argument instead of a
* substring.
@ -744,21 +746,18 @@ static int _parse_origin(struct http_request *r, struct http_origin *origin, siz
{
char *start = r->cursor;
char *end = start + header_bytes;
if (_skip_literal(r, "http://")) {
origin->scheme = "http";
} else if (_skip_literal(r, "https://")) {
origin->scheme = "https";
} else if (_skip_literal(r, "file://")) {
origin->scheme = "file";
} else {
IDEBUGF(r->debug, "Ignoring HTTP Origin with unsupported URI scheme: %s", alloca_toprint(50, start, header_bytes));
r->cursor = end;
bzero(origin, sizeof *origin);
if (_skip_literal(r, "null") && (r->cursor == end || _skip_space(r))) {
origin->null = 1;
return 1;
}
origin->hostname = "";
origin->port = 0;
r->cursor = start;
struct substring scheme;
struct substring hostname;
if (_skip_word_printable(r, &hostname, '/') > 0) {
if ( _skip_word_printable(r, &scheme, ':')
&& _skip_literal(r, "://")
&& _skip_word_printable(r, &hostname, '/')
) {
const char *port = hostname.end - 1;
while (port > hostname.start && isdigit(*port))
--port;
@ -770,8 +769,15 @@ static int _parse_origin(struct http_request *r, struct http_origin *origin, siz
}
}
assert(hostname.end > hostname.start);
if (!_reserve_substring(r, &origin->hostname, hostname))
return 0; // error
strbuf sb = strbuf_local_buf(origin->scheme);
strbuf_ncat(sb, scheme.start, scheme.end - scheme.start);
strbuf sh = strbuf_local_buf(origin->hostname);
strbuf_ncat(sh, hostname.start, hostname.end - hostname.start);
if (strbuf_overrun(sb) || strbuf_overrun(sh)) {
IDEBUGF(r->debug, "Ignoring HTTP Origin with over-long scheme: %s", alloca_toprint(50, start, header_bytes));
r->cursor = end;
return 1;
}
}
_skip_literal(r, "/");
return 1;
@ -1103,7 +1109,7 @@ static int http_request_parse_header(struct http_request *r)
}
_rewind(r);
if (_skip_literal_nocase(r, "Origin:")) {
if (r->request_header.origin.scheme) {
if (r->request_header.origin.null || r->request_header.origin.scheme[0]) {
IDEBUGF(r->debug, "Skipping duplicate HTTP header Origin: %s", alloca_toprint(50, sol, r->end - sol));
r->cursor = nextline;
_commit(r);
@ -2117,14 +2123,24 @@ static int _render_response(struct http_request *r)
}
if (hr.header.content_length != CONTENT_LENGTH_UNKNOWN)
strbuf_sprintf(sb, "Content-Length: %"PRIhttp_size_t"\r\n", hr.header.content_length);
if (hr.header.allow_origin)
strbuf_sprintf(sb, "Access-Control-Allow-Origin: %s\r\n", hr.header.allow_origin);
if (hr.header.allow_origin.null || hr.header.allow_origin.scheme[0]) {
strbuf_puts(sb, "Access-Control-Allow-Origin: ");
if (hr.header.allow_origin.null) {
strbuf_puts(sb, "null");
} else {
assert(hr.header.allow_origin.hostname[0]);
strbuf_puts(sb, hr.header.allow_origin.scheme);
strbuf_puts(sb, "://");
strbuf_puts(sb, hr.header.allow_origin.hostname);
if (hr.header.allow_origin.port)
strbuf_sprintf(sb, ":%u", hr.header.allow_origin.port);
}
strbuf_puts(sb, "\r\n");
}
if (hr.header.allow_methods)
strbuf_sprintf(sb, "Access-Control-Allow-Methods: %s\r\n", hr.header.allow_methods);
if (hr.header.allow_headers)
strbuf_sprintf(sb, "Access-Control-Allow-Headers: %s\r\n", hr.header.allow_headers);
const char *scheme = NULL;
switch (hr.header.www_authenticate.scheme) {
case NOAUTH: break;

View File

@ -88,8 +88,9 @@ struct http_www_authenticate {
};
struct http_origin {
const char *scheme;
const char *hostname;
uint8_t null;
char scheme[10]; // enough for "https"
char hostname[40]; // enough for "localhost"
uint16_t port;
};
@ -108,7 +109,7 @@ struct http_response_headers {
http_size_t resource_length; // size of entire resource
const char *content_type; // "type/subtype"
const char *boundary;
char allow_origin[24]; // max supported str (for now) "https://localhost:65537"
struct http_origin allow_origin;
const char *allow_methods;
const char *allow_headers;
struct http_www_authenticate www_authenticate;

View File

@ -136,6 +136,15 @@ test_CORS_Request(){
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
assertStdoutIs '200'
assertGrep http.headers "^Access-Control-Allow-Origin: http://localhost:1234$CR\$"
executeOk curl \
--silent --fail --show-error --write-out '%{http_code}' \
--output http.output \
--dump-header http.headers \
--header "Origin: null" \
--request "OPTIONS" \
"http://$addr_localhost:$PORTA/restful/rhizome/bundlelist.json"
assertStdoutIs '200'
assertGrep http.headers "^Access-Control-Allow-Origin: null$CR\$"
executeOk curl \
--silent --show-error --write-out '%{http_code}' \
--output http.output \