From 6f77afd486bdf07e800d47eddc7cf6249e5386e8 Mon Sep 17 00:00:00 2001 From: Robert Newson Date: Wed, 25 Aug 2010 08:17:19 +0000 Subject: COUCHDB-161 - range support. Adhere closer to the spec. correct range parsing error in mochiweb. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@988866 13f79535-47bb-0310-9956-ffa450edef68 --- share/www/script/test/attachment_ranges.js | 40 ++++++++++++++++++++++-------- src/couchdb/couch_httpd_db.erl | 28 ++++++++------------- src/mochiweb/mochiweb_http.erl | 2 -- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/share/www/script/test/attachment_ranges.js b/share/www/script/test/attachment_ranges.js index 807a13c8..748a979e 100644 --- a/share/www/script/test/attachment_ranges.js +++ b/share/www/script/test/attachment_ranges.js @@ -37,28 +37,29 @@ couchTests.attachment_ranges = function(debug) { "Range": "bytes=0-28" } }); - TEquals(206, xhr.status); + TEquals(206, xhr.status, "fetch 0-28"); TEquals("This is a base64 encoded text", xhr.responseText); TEquals("bytes 0-28/29", xhr.getResponseHeader("Content-Range")); TEquals("29", xhr.getResponseHeader("Content-Length")); - // Fetch the whole entity without an end offset is a 200. + // Fetch the whole entity without an end offset is a 206. var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt", { headers: { "Range": "bytes=0-" } }); - TEquals(200, xhr.status); + TEquals(206, xhr.status, "fetch 0-"); TEquals("This is a base64 encoded text", xhr.responseText); + TEquals("bytes 0-28/29", xhr.getResponseHeader("Content-Range")); TEquals("29", xhr.getResponseHeader("Content-Length")); - // Badly formed range header is a 400. + // Badly formed range header is a 200. var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt", { headers: { "Range": "bytes:0-" } }); - TEquals(400, xhr.status); + TEquals(200, xhr.status, "fetch with bad range header"); // Fetch the end of an entity without an end offset is a 206. var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt", { @@ -66,18 +67,20 @@ couchTests.attachment_ranges = function(debug) { "Range": "bytes=2-" } }); - TEquals(206, xhr.status); + TEquals(206, xhr.status, "fetch 2-"); TEquals("is is a base64 encoded text", xhr.responseText); TEquals("bytes 2-28/29", xhr.getResponseHeader("Content-Range")); TEquals("27", xhr.getResponseHeader("Content-Length")); - // Fetch past the end of the entity is a 416 + // Fetch past the end of the entity is a 206 var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt", { headers: { "Range": "bytes=0-29" } }); - TEquals(416, xhr.status); + TEquals(206, xhr.status, "fetch 0-29"); + TEquals("bytes 0-28/29", xhr.getResponseHeader("Content-Range")); + TEquals("29", xhr.getResponseHeader("Content-Length")); // Fetch first part of entity is a 206 var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt", { @@ -85,7 +88,7 @@ couchTests.attachment_ranges = function(debug) { "Range": "bytes=0-3" } }); - TEquals(206, xhr.status); + TEquals(206, xhr.status, "fetch 0-3"); TEquals("This", xhr.responseText); TEquals("4", xhr.getResponseHeader("Content-Length")); TEquals("bytes 0-3/29", xhr.getResponseHeader("Content-Range")); @@ -96,7 +99,7 @@ couchTests.attachment_ranges = function(debug) { "Range": "bytes=10-15" } }); - TEquals(206, xhr.status); + TEquals(206, xhr.status, "fetch 10-15"); TEquals("base64", xhr.responseText); TEquals("6", xhr.getResponseHeader("Content-Length")); TEquals("bytes 10-15/29", xhr.getResponseHeader("Content-Range")); @@ -107,10 +110,25 @@ couchTests.attachment_ranges = function(debug) { "Range": "bytes=-3" } }); - TEquals(206, xhr.status); + TEquals(206, xhr.status, "fetch -3"); TEquals("ext", xhr.responseText); TEquals("3", xhr.getResponseHeader("Content-Length")); TEquals("bytes 26-28/29", xhr.getResponseHeader("Content-Range")); + + // backward range is 416 + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt", { + headers: { + "Range": "bytes=5-3" + } + }); + TEquals(416, xhr.status, "fetch 5-3"); + // range completely outside of entity is 416 + var xhr = CouchDB.request("GET", "/test_suite_db/bin_doc/foo.txt", { + headers: { + "Range": "bytes=300-310" + } + }); + TEquals(416, xhr.status, "fetch 300-310"); }; diff --git a/src/couchdb/couch_httpd_db.erl b/src/couchdb/couch_httpd_db.erl index 0479c9c1..4002a6a9 100644 --- a/src/couchdb/couch_httpd_db.erl +++ b/src/couchdb/couch_httpd_db.erl @@ -1086,29 +1086,23 @@ db_attachment_req(Req, _Db, _DocId, _FileNameParts) -> parse_ranges(undefined, _Len) -> undefined; -parse_ranges(fail, _Len) -> - throw(bad_request); +parse_ranges(fail, Len) -> + undefined; parse_ranges(Ranges, Len) -> parse_ranges(Ranges, Len, []). parse_ranges([], _Len, Acc) -> lists:reverse(Acc); +parse_ranges([{From, To}|_], Len, _Acc) when is_integer(From) andalso is_integer(To) andalso To < From -> + throw(requested_range_not_satisfiable); +parse_ranges([{From, To}|Rest], Len, Acc) when is_integer(To) andalso To >= Len -> + parse_ranges([{From, Len-1}] ++ Rest, Len, Acc); +parse_ranges([{none, To}|Rest], Len, Acc) -> + parse_ranges([{Len - To, Len - 1}] ++ Rest, Len, Acc); +parse_ranges([{From, none}|Rest], Len, Acc) -> + parse_ranges([{From, Len - 1}] ++ Rest, Len, Acc); parse_ranges([{From,To}|Rest], Len, Acc) -> - {From1, To1} = case {From, To} of - {none, To} -> - {Len - To, Len - 1}; - {From, none} -> - {From, Len - 1}; - _ -> - {From, To} - end, - if - From < 0 orelse To1 >= Len -> - throw(requested_range_not_satisfiable); - true -> - ok - end, - parse_ranges(Rest, Len, [{From1, To1}] ++ Acc). + parse_ranges(Rest, Len, [{From, To}] ++ Acc). get_md5_header(Req) -> ContentMD5 = couch_httpd:header_value(Req, "Content-MD5"), diff --git a/src/mochiweb/mochiweb_http.erl b/src/mochiweb/mochiweb_http.erl index 24140994..ab0af7e8 100644 --- a/src/mochiweb/mochiweb_http.erl +++ b/src/mochiweb/mochiweb_http.erl @@ -162,8 +162,6 @@ after_response(Body, Req) -> ?MODULE:loop(Socket, Body) end. -parse_range_request("bytes=0-") -> - undefined; parse_range_request(RawRange) when is_list(RawRange) -> try "bytes=" ++ RangeString = RawRange, -- cgit v1.2.3