Cleanup: have common HttpResponseHeaders routine to update with range
Currently we do this header-fixup in two places: PartialData and AppCache,
and I'm planning to do the very same header-fixup in yet another module,
ServiceWorker. I want to have a common utility in net/http/http_util
to do this so that all 3 modules can share it.
BUG=349319
TEST=HttpResponseHeadersTest.UpdateWithNewRange
Review URL: https://codereview.chromium.org/187583002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@255905 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc
index 2d74b38e..dbe09a6 100644
--- a/net/http/http_response_headers.cc
+++ b/net/http/http_response_headers.cc
@@ -11,6 +11,7 @@
#include <algorithm>
+#include "base/format_macros.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/pickle.h"
@@ -21,6 +22,7 @@
#include "base/time/time.h"
#include "base/values.h"
#include "net/base/escape.h"
+#include "net/http/http_byte_range.h"
#include "net/http/http_util.h"
using base::StringPiece;
@@ -374,6 +376,32 @@
MergeWithHeaders(new_raw_headers, empty_to_remove);
}
+void HttpResponseHeaders::UpdateWithNewRange(
+ const HttpByteRange& byte_range,
+ int64 resource_size,
+ bool replace_status_line) {
+ DCHECK(byte_range.IsValid());
+ DCHECK(byte_range.HasFirstBytePosition());
+ DCHECK(byte_range.HasLastBytePosition());
+
+ const char kLengthHeader[] = "Content-Length";
+ const char kRangeHeader[] = "Content-Range";
+
+ RemoveHeader(kLengthHeader);
+ RemoveHeader(kRangeHeader);
+
+ int64 start = byte_range.first_byte_position();
+ int64 end = byte_range.last_byte_position();
+ int64 range_len = end - start + 1;
+
+ if (replace_status_line)
+ ReplaceStatusLine("HTTP/1.1 206 Partial Content");
+
+ AddHeader(base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
+ kRangeHeader, start, end, resource_size));
+ AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, range_len));
+}
+
void HttpResponseHeaders::Parse(const std::string& raw_input) {
raw_headers_.reserve(raw_input.size());
diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h
index deba3b6..2eb0c67 100644
--- a/net/http/http_response_headers.h
+++ b/net/http/http_response_headers.h
@@ -26,6 +26,8 @@
namespace net {
+class HttpByteRange;
+
// HttpResponseHeaders: parses and holds HTTP response headers.
class NET_EXPORT HttpResponseHeaders
: public base::RefCountedThreadSafe<HttpResponseHeaders> {
@@ -83,6 +85,15 @@
// not have any EOL).
void ReplaceStatusLine(const std::string& new_status);
+ // Updates headers (Content-Length and Content-Range) in the |headers| to
+ // include the right content length and range for |byte_range|. This also
+ // updates HTTP status line if |replace_status_line| is true.
+ // |byte_range| must have a valid, bounded range (i.e. coming from a valid
+ // response or should be usable for a response).
+ void UpdateWithNewRange(const HttpByteRange& byte_range,
+ int64 resource_size,
+ bool replace_status_line);
+
// Creates a normalized header string. The output will be formatted exactly
// like so:
// HTTP/<version> <status_code> <status_text>\n
diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc
index c433e1d..0b17b8f 100644
--- a/net/http/http_response_headers_unittest.cc
+++ b/net/http/http_response_headers_unittest.cc
@@ -9,6 +9,7 @@
#include "base/pickle.h"
#include "base/time/time.h"
#include "base/values.h"
+#include "net/http/http_byte_range.h"
#include "net/http/http_response_headers.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -1851,6 +1852,58 @@
}
}
+TEST(HttpResponseHeadersTest, UpdateWithNewRange) {
+ const struct {
+ const char* orig_headers;
+ const char* expected_headers;
+ const char* expected_headers_with_replaced_status;
+ } tests[] = {
+ { "HTTP/1.1 200 OK\n"
+ "Content-Length: 450\n",
+
+ "HTTP/1.1 200 OK\n"
+ "Content-Range: bytes 3-5/450\n"
+ "Content-Length: 3\n",
+
+ "HTTP/1.1 206 Partial Content\n"
+ "Content-Range: bytes 3-5/450\n"
+ "Content-Length: 3\n",
+ },
+ { "HTTP/1.1 200 OK\n"
+ "Content-Length: 5\n",
+
+ "HTTP/1.1 200 OK\n"
+ "Content-Range: bytes 3-5/5\n"
+ "Content-Length: 3\n",
+
+ "HTTP/1.1 206 Partial Content\n"
+ "Content-Range: bytes 3-5/5\n"
+ "Content-Length: 3\n",
+ },
+ };
+ const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5);
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) {
+ std::string orig_headers(tests[i].orig_headers);
+ std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0');
+ scoped_refptr<net::HttpResponseHeaders> parsed(
+ new net::HttpResponseHeaders(orig_headers + '\0'));
+ int64 content_size = parsed->GetContentLength();
+ std::string resulting_headers;
+
+ // Update headers without replacing status line.
+ parsed->UpdateWithNewRange(range, content_size, false);
+ parsed->GetNormalizedHeaders(&resulting_headers);
+ EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers);
+
+ // Replace status line too.
+ parsed->UpdateWithNewRange(range, content_size, true);
+ parsed->GetNormalizedHeaders(&resulting_headers);
+ EXPECT_EQ(std::string(tests[i].expected_headers_with_replaced_status),
+ resulting_headers);
+ }
+}
+
TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) {
std::string headers("HTTP/1.1 404\n"
"Content-Length: 450\n"
diff --git a/net/http/partial_data.cc b/net/http/partial_data.cc
index ee3678b5..a05c218 100644
--- a/net/http/partial_data.cc
+++ b/net/http/partial_data.cc
@@ -382,40 +382,26 @@
if (truncated_)
return;
+ if (byte_range_.IsValid() && success) {
+ headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_);
+ return;
+ }
+
headers->RemoveHeader(kLengthHeader);
headers->RemoveHeader(kRangeHeader);
- int64 range_len, start, end;
if (byte_range_.IsValid()) {
- if (success) {
- if (!sparse_entry_)
- headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content");
-
- DCHECK(byte_range_.HasFirstBytePosition());
- DCHECK(byte_range_.HasLastBytePosition());
- start = byte_range_.first_byte_position();
- end = byte_range_.last_byte_position();
- range_len = end - start + 1;
- } else {
- headers->ReplaceStatusLine(
- "HTTP/1.1 416 Requested Range Not Satisfiable");
- start = 0;
- end = 0;
- range_len = 0;
- }
-
- headers->AddHeader(
- base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64,
- kRangeHeader, start, end, resource_size_));
+ headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable");
+ headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64,
+ kRangeHeader, resource_size_));
+ headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader));
} else {
// TODO(rvargas): Is it safe to change the protocol version?
headers->ReplaceStatusLine("HTTP/1.1 200 OK");
DCHECK_NE(resource_size_, 0);
- range_len = resource_size_;
+ headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
+ resource_size_));
}
-
- headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader,
- range_len));
}
void PartialData::FixContentLength(HttpResponseHeaders* headers) {