[go: nahoru, domu]

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) {