diff --git a/src/base.h b/src/base.h index ea040f46..2fe60b6b 100644 --- a/src/base.h +++ b/src/base.h @@ -47,7 +47,6 @@ typedef struct { /* strings to the header */ buffer *http_host; /* not alloced */ - const char *http_range; const char *http_content_type; const char *http_if_modified_since; const char *http_if_none_match; @@ -58,9 +57,6 @@ typedef struct { off_t content_length; /* returned by strtoll() */ off_t te_chunked; - /* internal representation */ - int accept_encoding; - /* internal */ buffer *pathinfo; } request; diff --git a/src/connections.c b/src/connections.c index 24365687..f06c89ea 100644 --- a/src/connections.c +++ b/src/connections.c @@ -685,7 +685,6 @@ int connection_reset(server *srv, connection *con) { con->request.x = NULL; CLEAN(http_host); - CLEAN(http_range); CLEAN(http_content_type); #undef CLEAN con->request.content_length = 0; diff --git a/src/http-header-glue.c b/src/http-header-glue.c index 63057ee3..5d4f1f5f 100644 --- a/src/http-header-glue.c +++ b/src/http-header-glue.c @@ -229,7 +229,7 @@ int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) { } -static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce) { +static int http_response_parse_range(server *srv, connection *con, buffer *path, stat_cache_entry *sce, const char *range) { int multipart = 0; int error; off_t start, end; @@ -247,11 +247,26 @@ static int http_response_parse_range(server *srv, connection *con, buffer *path, content_type = ds->value; } - for (s = con->request.http_range, error = 0; + for (s = range, error = 0; !error && *s && NULL != (minus = strchr(s, '-')); ) { char *err; off_t la, le; + if (s != minus) { + la = strtoll(s, &err, 10); + if (err != minus) { + /* should not have multiple range-unit in Range, but + * handle just in case multiple Range headers merged */ + while (*s == ' ' || *s == '\t') ++s; + if (0 != strncmp(s, "bytes=", 6)) return -1; + s += 6; + if (s != minus) { + la = strtoll(s, &err, 10); + if (err != minus) return -1; + } + } + } + if (s == minus) { /* - */ @@ -281,9 +296,6 @@ static int http_response_parse_range(server *srv, connection *con, buffer *path, } else if (*(minus+1) == '\0' || *(minus+1) == ',') { /* - */ - la = strtoll(s, &err, 10); - - if (err == minus) { /* ok */ if (*(err + 1) == '\0') { @@ -301,16 +313,9 @@ static int http_response_parse_range(server *srv, connection *con, buffer *path, } else { error = 1; } - } else { - /* error */ - error = 1; - } } else { /* - */ - la = strtoll(s, &err, 10); - - if (err == minus) { le = strtoll(minus+1, &err, 10); /* RFC 2616 - 14.35.1 */ @@ -335,11 +340,6 @@ static int http_response_parse_range(server *srv, connection *con, buffer *path, error = 1; } - } else { - /* error */ - - error = 1; - } } if (!error) { @@ -516,9 +516,11 @@ void http_response_send_file (server *srv, connection *con, buffer *path) { } } - if (con->request.http_range && con->conf.range_requests + if (con->conf.range_requests && (200 == con->http_status || 0 == con->http_status) + && NULL != (ds = (data_string *)array_get_element(con->request.headers, "Range")) && NULL == array_get_element(con->response.headers, "Content-Encoding")) { + buffer *range = ds->value; int do_range_request = 1; /* check if we have a conditional GET */ @@ -547,11 +549,14 @@ void http_response_send_file (server *srv, connection *con, buffer *path) { } } - if (do_range_request) { + if (do_range_request + && !buffer_string_is_empty(range) + && 0 == strncmp(range->ptr, "bytes=", 6)) { + /* support only "bytes" byte-unit */ /* content prepared, I'm done */ con->file_finished = 1; - if (0 == http_response_parse_range(srv, con, path, sce)) { + if (0 == http_response_parse_range(srv, con, path, sce, range->ptr+6)) { con->http_status = 206; } return; diff --git a/src/request.c b/src/request.c index 4e7b1792..213a87e1 100644 --- a/src/request.c +++ b/src/request.c @@ -1118,30 +1118,6 @@ int http_request_parse(server *srv, connection *con) { ds->free((data_unset*) ds); ds = NULL; } - } else if (cmp > 0 && 0 == (cmp = buffer_caseless_compare(CONST_BUF_LEN(ds->key), CONST_STR_LEN("Range")))) { - if (!con->request.http_range) { - /* bytes=.*-.* */ - - if (0 == strncasecmp(ds->value->ptr, "bytes=", 6) && - NULL != strchr(ds->value->ptr+6, '-')) { - - /* if dup, only the first one will survive */ - con->request.http_range = ds->value->ptr + 6; - } - } else { - con->http_status = 400; - con->keep_alive = 0; - - if (srv->srvconf.log_request_header_on_error) { - log_error_write(srv, __FILE__, __LINE__, "s", - "duplicate Range-header -> 400"); - log_error_write(srv, __FILE__, __LINE__, "Sb", - "request-header:\n", - con->request.request); - } - array_insert_unique(con->request.headers, (data_unset *)ds); - return 0; - } } if (ds) array_insert_unique(con->request.headers, (data_unset *)ds); diff --git a/src/t/test_request.c b/src/t/test_request.c index 3b6671b4..c7734a0d 100644 --- a/src/t/test_request.c +++ b/src/t/test_request.c @@ -13,7 +13,6 @@ static void test_request_connection_reset(connection *con) con->request.http_method = HTTP_METHOD_UNSET; con->request.http_version = HTTP_VERSION_UNSET; con->request.http_host = NULL; - con->request.http_range = NULL; con->request.http_content_type = NULL; con->request.http_if_modified_since = NULL; con->request.http_if_none_match = NULL; @@ -372,13 +371,40 @@ static void test_request_http_request_parse(server *srv, connection *con) "Content-Type: 4\r\n" "\r\n")); - run_http_request_parse(srv, con, __LINE__, 400, - "Duplicate Range headers", + /* (not actually testing Range here anymore; parsing deferred until use) */ + + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers (get appended)", CONST_STR_LEN("GET / HTTP/1.0\r\n" "Range: bytes=5-6\r\n" "Range: bytes=5-9\r\n" "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (a)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: bytes=0\r\n" + "Range: bytes=5-9\r\n" + "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (b)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: bytes=5-9\r\n" + "Range: bytes=0\r\n" + "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (c)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: 0\r\n" + "Range: bytes=5-9\r\n" + "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, + "Duplicate Range headers with invalid range (d)", + CONST_STR_LEN("GET / HTTP/1.0\r\n" + "Range: bytes=5-9\r\n" + "Range: 0\r\n" + "\r\n")); + run_http_request_parse(srv, con, __LINE__, 0, "Duplicate If-None-Match headers", CONST_STR_LEN("GET / HTTP/1.0\r\n" diff --git a/tests/request.t b/tests/request.t index a9fe3f6a..c1e8f472 100755 --- a/tests/request.t +++ b/tests/request.t @@ -8,7 +8,7 @@ BEGIN { use strict; use IO::Socket; -use Test::More tests => 50; +use Test::More tests => 52; use LightyTest; my $tf = LightyTest->new(); @@ -391,6 +391,26 @@ EOF $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; ok($tf->handle_http($t) == 0, 'GET, Range with range-requests-disabled'); +$t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => "12345\n" } ]; +ok($tf->handle_http($t) == 0, 'GET, Range invalid range-unit (first)'); + +$t->{REQUEST} = ( <{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 206 } ]; +ok($tf->handle_http($t) == 0, 'GET, Range ignore invalid range (second)'); + $t->{REQUEST} = ( <