Home | History | Annotate | Download | only in child
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/child/multipart_response_delegate.h"
      6 
      7 #include <vector>
      8 
      9 #include "testing/gtest/include/gtest/gtest.h"
     10 #include "third_party/WebKit/public/platform/WebString.h"
     11 #include "third_party/WebKit/public/platform/WebURL.h"
     12 #include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
     13 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     14 
     15 using blink::WebString;
     16 using blink::WebURL;
     17 using blink::WebURLError;
     18 using blink::WebURLLoader;
     19 using blink::WebURLLoaderClient;
     20 using blink::WebURLRequest;
     21 using blink::WebURLResponse;
     22 using std::string;
     23 
     24 namespace content {
     25 
     26 class MultipartResponseDelegateTester {
     27  public:
     28   MultipartResponseDelegateTester(MultipartResponseDelegate* delegate)
     29       : delegate_(delegate) {
     30   }
     31 
     32   int PushOverLine(const std::string& data, size_t pos) {
     33     return delegate_->PushOverLine(data, pos);
     34   }
     35 
     36   bool ParseHeaders() { return delegate_->ParseHeaders(); }
     37   size_t FindBoundary() { return delegate_->FindBoundary(); }
     38   std::string& boundary() { return delegate_->boundary_; }
     39   std::string& data() { return delegate_->data_; }
     40 
     41  private:
     42   MultipartResponseDelegate* delegate_;
     43 };
     44 
     45 namespace {
     46 
     47 class MultipartResponseTest : public testing::Test {
     48 };
     49 
     50 class MockWebURLLoaderClient : public WebURLLoaderClient {
     51  public:
     52   MockWebURLLoaderClient() { Reset(); }
     53 
     54   virtual void willSendRequest(
     55       WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
     56   virtual void didSendData(
     57       WebURLLoader*, unsigned long long, unsigned long long) {}
     58 
     59   virtual void didReceiveResponse(WebURLLoader* loader,
     60                                   const WebURLResponse& response) {
     61     ++received_response_;
     62     response_ = response;
     63     data_.clear();
     64   }
     65   virtual void didReceiveData(
     66       blink::WebURLLoader* loader,
     67       const char* data,
     68       int data_length,
     69       int encoded_data_length) {
     70     ++received_data_;
     71     data_.append(data, data_length);
     72     total_encoded_data_length_ += encoded_data_length;
     73   }
     74   virtual void didFinishLoading(
     75       WebURLLoader*, double finishTime, int64_t total_encoded_data_length) {}
     76   virtual void didFail(WebURLLoader*, const WebURLError&) {}
     77 
     78   void Reset() {
     79     received_response_ = received_data_ = total_encoded_data_length_ = 0;
     80     data_.clear();
     81     response_.reset();
     82   }
     83 
     84   string GetResponseHeader(const char* name) const {
     85     return string(response_.httpHeaderField(WebString::fromUTF8(name)).utf8());
     86   }
     87 
     88   int received_response_, received_data_, total_encoded_data_length_;
     89   string data_;
     90   WebURLResponse response_;
     91 };
     92 
     93 // We can't put this in an anonymous function because it's a friend class for
     94 // access to private members.
     95 TEST(MultipartResponseTest, Functions) {
     96   // PushOverLine tests
     97 
     98   WebURLResponse response;
     99   response.initialize();
    100   response.setMIMEType("multipart/x-mixed-replace");
    101   response.setHTTPHeaderField("Foo", "Bar");
    102   response.setHTTPHeaderField("Content-type", "text/plain");
    103   MockWebURLLoaderClient client;
    104   MultipartResponseDelegate delegate(&client, NULL, response, "bound");
    105   MultipartResponseDelegateTester delegate_tester(&delegate);
    106 
    107   struct {
    108     const char* input;
    109     const int position;
    110     const int expected;
    111   } line_tests[] = {
    112     { "Line", 0, 0 },
    113     { "Line", 2, 0 },
    114     { "Line", 10, 0 },
    115     { "\r\nLine", 0, 2 },
    116     { "\nLine", 0, 1 },
    117     { "\n\nLine", 0, 2 },
    118     { "\rLine", 0, 1 },
    119     { "Line\r\nLine", 4, 2 },
    120     { "Line\nLine", 4, 1 },
    121     { "Line\n\nLine", 4, 2 },
    122     { "Line\rLine", 4, 1 },
    123     { "Line\r\rLine", 4, 1 },
    124   };
    125   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(line_tests); ++i) {
    126     EXPECT_EQ(line_tests[i].expected,
    127               delegate_tester.PushOverLine(line_tests[i].input,
    128                                            line_tests[i].position));
    129   }
    130 
    131   // ParseHeaders tests
    132   struct {
    133     const char* data;
    134     const bool rv;
    135     const int received_response_calls;
    136     const char* newdata;
    137   } header_tests[] = {
    138     { "This is junk", false, 0, "This is junk" },
    139     { "Foo: bar\nBaz:\n\nAfter:\n", true, 1, "After:\n" },
    140     { "Foo: bar\nBaz:\n", false, 0, "Foo: bar\nBaz:\n" },
    141     { "Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 1, "After:\r\n" },
    142     { "Foo: bar\r\nBaz:\r\n", false, 0, "Foo: bar\r\nBaz:\r\n" },
    143     { "Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 1, "After:\n\n" },
    144     { "Foo: bar\r\nBaz:\n", false, 0, "Foo: bar\r\nBaz:\n" },
    145     { "\r\n", true, 1, "" },
    146   };
    147   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(header_tests); ++i) {
    148     client.Reset();
    149     delegate_tester.data().assign(header_tests[i].data);
    150     EXPECT_EQ(header_tests[i].rv,
    151               delegate_tester.ParseHeaders());
    152     EXPECT_EQ(header_tests[i].received_response_calls,
    153               client.received_response_);
    154     EXPECT_EQ(string(header_tests[i].newdata),
    155               delegate_tester.data());
    156   }
    157   // Test that the resource response is filled in correctly when parsing
    158   // headers.
    159   client.Reset();
    160   string test_header("content-type: image/png\ncontent-length: 10\n\n");
    161   delegate_tester.data().assign(test_header);
    162   EXPECT_TRUE(delegate_tester.ParseHeaders());
    163   EXPECT_TRUE(delegate_tester.data().length() == 0);
    164   EXPECT_EQ(string("image/png"), client.GetResponseHeader("Content-Type"));
    165   EXPECT_EQ(string("10"), client.GetResponseHeader("content-length"));
    166   // This header is passed from the original request.
    167   EXPECT_EQ(string("Bar"), client.GetResponseHeader("foo"));
    168 
    169   // Make sure we parse the right mime-type if a charset is provided.
    170   client.Reset();
    171   string test_header2("content-type: text/html; charset=utf-8\n\n");
    172   delegate_tester.data().assign(test_header2);
    173   EXPECT_TRUE(delegate_tester.ParseHeaders());
    174   EXPECT_TRUE(delegate_tester.data().length() == 0);
    175   EXPECT_EQ(string("text/html; charset=utf-8"),
    176             client.GetResponseHeader("Content-Type"));
    177   EXPECT_EQ(string("utf-8"),
    178             string(client.response_.textEncodingName().utf8()));
    179 
    180   // FindBoundary tests
    181   struct {
    182     const char* boundary;
    183     const char* data;
    184     const size_t position;
    185   } boundary_tests[] = {
    186     { "bound", "bound", 0 },
    187     { "bound", "--bound", 0 },
    188     { "bound", "junkbound", 4 },
    189     { "bound", "junk--bound", 4 },
    190     { "foo", "bound", string::npos },
    191     { "bound", "--boundbound", 0 },
    192   };
    193   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(boundary_tests); ++i) {
    194     delegate_tester.boundary().assign(boundary_tests[i].boundary);
    195     delegate_tester.data().assign(boundary_tests[i].data);
    196     EXPECT_EQ(boundary_tests[i].position,
    197               delegate_tester.FindBoundary());
    198   }
    199 }
    200 
    201 TEST(MultipartResponseTest, MissingBoundaries) {
    202   WebURLResponse response;
    203   response.initialize();
    204   response.setMIMEType("multipart/x-mixed-replace");
    205   response.setHTTPHeaderField("Foo", "Bar");
    206   response.setHTTPHeaderField("Content-type", "text/plain");
    207   MockWebURLLoaderClient client;
    208   MultipartResponseDelegate delegate(&client, NULL, response, "bound");
    209 
    210   // No start boundary
    211   string no_start_boundary(
    212     "Content-type: text/plain\n\n"
    213     "This is a sample response\n"
    214     "--bound--"
    215     "ignore junk after end token --bound\n\nTest2\n");
    216   delegate.OnReceivedData(no_start_boundary.c_str(),
    217                           static_cast<int>(no_start_boundary.length()),
    218                           static_cast<int>(no_start_boundary.length()));
    219   EXPECT_EQ(1, client.received_response_);
    220   EXPECT_EQ(1, client.received_data_);
    221   EXPECT_EQ(string("This is a sample response"), client.data_);
    222   EXPECT_EQ(static_cast<int>(no_start_boundary.length()),
    223             client.total_encoded_data_length_);
    224 
    225   delegate.OnCompletedRequest();
    226   EXPECT_EQ(1, client.received_response_);
    227   EXPECT_EQ(1, client.received_data_);
    228 
    229   // No end boundary
    230   client.Reset();
    231   MultipartResponseDelegate delegate2(&client, NULL, response, "bound");
    232   string no_end_boundary(
    233     "bound\nContent-type: text/plain\n\n"
    234     "This is a sample response\n");
    235   delegate2.OnReceivedData(no_end_boundary.c_str(),
    236                           static_cast<int>(no_end_boundary.length()),
    237                           static_cast<int>(no_end_boundary.length()));
    238   EXPECT_EQ(1, client.received_response_);
    239   EXPECT_EQ(1, client.received_data_);
    240   EXPECT_EQ("This is a sample response\n", client.data_);
    241   EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
    242             client.total_encoded_data_length_);
    243 
    244   delegate2.OnCompletedRequest();
    245   EXPECT_EQ(1, client.received_response_);
    246   EXPECT_EQ(1, client.received_data_);
    247   EXPECT_EQ(string("This is a sample response\n"), client.data_);
    248   EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
    249             client.total_encoded_data_length_);
    250 
    251   // Neither boundary
    252   client.Reset();
    253   MultipartResponseDelegate delegate3(&client, NULL, response, "bound");
    254   string no_boundaries(
    255     "Content-type: text/plain\n\n"
    256     "This is a sample response\n");
    257   delegate3.OnReceivedData(no_boundaries.c_str(),
    258                            static_cast<int>(no_boundaries.length()),
    259                            static_cast<int>(no_boundaries.length()));
    260   EXPECT_EQ(1, client.received_response_);
    261   EXPECT_EQ(1, client.received_data_);
    262   EXPECT_EQ("This is a sample response\n", client.data_);
    263   EXPECT_EQ(static_cast<int>(no_boundaries.length()),
    264             client.total_encoded_data_length_);
    265 
    266   delegate3.OnCompletedRequest();
    267   EXPECT_EQ(1, client.received_response_);
    268   EXPECT_EQ(1, client.received_data_);
    269   EXPECT_EQ(string("This is a sample response\n"), client.data_);
    270   EXPECT_EQ(static_cast<int>(no_boundaries.length()),
    271             client.total_encoded_data_length_);
    272 }
    273 
    274 TEST(MultipartResponseTest, MalformedBoundary) {
    275   // Some servers send a boundary that is prefixed by "--".  See bug 5786.
    276 
    277   WebURLResponse response;
    278   response.initialize();
    279   response.setMIMEType("multipart/x-mixed-replace");
    280   response.setHTTPHeaderField("Foo", "Bar");
    281   response.setHTTPHeaderField("Content-type", "text/plain");
    282   MockWebURLLoaderClient client;
    283   MultipartResponseDelegate delegate(&client, NULL, response, "--bound");
    284 
    285   string data(
    286     "--bound\n"
    287     "Content-type: text/plain\n\n"
    288     "This is a sample response\n"
    289     "--bound--"
    290     "ignore junk after end token --bound\n\nTest2\n");
    291   delegate.OnReceivedData(data.c_str(),
    292                           static_cast<int>(data.length()),
    293                           static_cast<int>(data.length()));
    294   EXPECT_EQ(1, client.received_response_);
    295   EXPECT_EQ(1, client.received_data_);
    296   EXPECT_EQ(string("This is a sample response"), client.data_);
    297   EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
    298 
    299   delegate.OnCompletedRequest();
    300   EXPECT_EQ(1, client.received_response_);
    301   EXPECT_EQ(1, client.received_data_);
    302 }
    303 
    304 
    305 // Used in for tests that break the data in various places.
    306 struct TestChunk {
    307   const int start_pos;  // offset in data
    308   const int end_pos;    // end offset in data
    309   const int expected_responses;
    310   const int expected_received_data;
    311   const char* expected_data;
    312   const int expected_encoded_data_length;
    313 };
    314 
    315 void VariousChunkSizesTest(const TestChunk chunks[], int chunks_size,
    316                            int responses, int received_data,
    317                            const char* completed_data,
    318                            int completed_encoded_data_length) {
    319   const string data(
    320     "--bound\n"                    // 0-7
    321     "Content-type: image/png\n\n"  // 8-32
    322     "datadatadatadatadata"         // 33-52
    323     "--bound\n"                    // 53-60
    324     "Content-type: image/jpg\n\n"  // 61-85
    325     "foofoofoofoofoo"              // 86-100
    326     "--bound--");                  // 101-109
    327 
    328   WebURLResponse response;
    329   response.initialize();
    330   response.setMIMEType("multipart/x-mixed-replace");
    331   MockWebURLLoaderClient client;
    332   MultipartResponseDelegate delegate(&client, NULL, response, "bound");
    333 
    334   for (int i = 0; i < chunks_size; ++i) {
    335     ASSERT_TRUE(chunks[i].start_pos < chunks[i].end_pos);
    336     string chunk = data.substr(chunks[i].start_pos,
    337                                chunks[i].end_pos - chunks[i].start_pos);
    338     delegate.OnReceivedData(
    339         chunk.c_str(),
    340         static_cast<int>(chunk.length()),
    341         static_cast<int>(chunk.length()));
    342     EXPECT_EQ(chunks[i].expected_responses, client.received_response_);
    343     EXPECT_EQ(chunks[i].expected_received_data, client.received_data_);
    344     EXPECT_EQ(string(chunks[i].expected_data), client.data_);
    345     EXPECT_EQ(chunks[i].expected_encoded_data_length,
    346               client.total_encoded_data_length_);
    347   }
    348   // Check final state
    349   delegate.OnCompletedRequest();
    350   EXPECT_EQ(responses, client.received_response_);
    351   EXPECT_EQ(received_data, client.received_data_);
    352   string completed_data_string(completed_data);
    353   EXPECT_EQ(completed_data_string, client.data_);
    354   EXPECT_EQ(completed_encoded_data_length, client.total_encoded_data_length_);
    355 }
    356 
    357 TEST(MultipartResponseTest, BreakInBoundary) {
    358   // Break in the first boundary
    359   const TestChunk bound1[] = {
    360     { 0, 4, 0, 0, "", 0 },
    361     { 4, 110, 2, 2, "foofoofoofoofoo", 110 },
    362   };
    363   VariousChunkSizesTest(bound1, arraysize(bound1),
    364                         2, 2, "foofoofoofoofoo", 110);
    365 
    366   // Break in first and second
    367   const TestChunk bound2[] = {
    368     { 0, 4, 0, 0, "", 0 },
    369     { 4, 55, 1, 1, "datadatadatadat", 55 },
    370     { 55, 65, 1, 2, "datadatadatadatadata", 65 },
    371     { 65, 110, 2, 3, "foofoofoofoofoo", 110 },
    372   };
    373   VariousChunkSizesTest(bound2, arraysize(bound2),
    374                         2, 3, "foofoofoofoofoo", 110);
    375 
    376   // Break in second only
    377   const TestChunk bound3[] = {
    378     { 0, 55, 1, 1, "datadatadatadat", 55 },
    379     { 55, 110, 2, 3, "foofoofoofoofoo", 110 },
    380   };
    381   VariousChunkSizesTest(bound3, arraysize(bound3),
    382                         2, 3, "foofoofoofoofoo", 110);
    383 }
    384 
    385 TEST(MultipartResponseTest, BreakInHeaders) {
    386   // Break in first header
    387   const TestChunk header1[] = {
    388     { 0, 10, 0, 0, "", 0 },
    389     { 10, 35, 1, 0, "", 0 },
    390     { 35, 110, 2, 2, "foofoofoofoofoo", 110 },
    391   };
    392   VariousChunkSizesTest(header1, arraysize(header1),
    393                         2, 2, "foofoofoofoofoo", 110);
    394 
    395   // Break in both headers
    396   const TestChunk header2[] = {
    397     { 0, 10, 0, 0, "", 0 },
    398     { 10, 65, 1, 1, "datadatadatadatadata", 65 },
    399     { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
    400   };
    401   VariousChunkSizesTest(header2, arraysize(header2),
    402                         2, 2, "foofoofoofoofoo", 110);
    403 
    404   // Break at end of a header
    405   const TestChunk header3[] = {
    406     { 0, 33, 1, 0, "", 0 },
    407     { 33, 65, 1, 1, "datadatadatadatadata", 65 },
    408     { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
    409   };
    410   VariousChunkSizesTest(header3, arraysize(header3),
    411                         2, 2, "foofoofoofoofoo", 110);
    412 }
    413 
    414 TEST(MultipartResponseTest, BreakInData) {
    415   // All data as one chunk
    416   const TestChunk data1[] = {
    417     { 0, 110, 2, 2, "foofoofoofoofoo", 110 },
    418   };
    419   VariousChunkSizesTest(data1, arraysize(data1),
    420                         2, 2, "foofoofoofoofoo", 110);
    421 
    422   // breaks in data segment
    423   const TestChunk data2[] = {
    424     { 0, 35, 1, 0, "", 0 },
    425     { 35, 65, 1, 1, "datadatadatadatadata", 65 },
    426     { 65, 90, 2, 1, "", 65 },
    427     { 90, 110, 2, 2, "foofoofoofoofoo", 110 },
    428   };
    429   VariousChunkSizesTest(data2, arraysize(data2),
    430                         2, 2, "foofoofoofoofoo", 110);
    431 
    432   // Incomplete send
    433   const TestChunk data3[] = {
    434     { 0, 35, 1, 0, "", 0 },
    435     { 35, 90, 2, 1, "", 90 },
    436   };
    437   VariousChunkSizesTest(data3, arraysize(data3),
    438                         2, 2, "foof", 90);
    439 }
    440 
    441 TEST(MultipartResponseTest, SmallChunk) {
    442   WebURLResponse response;
    443   response.initialize();
    444   response.setMIMEType("multipart/x-mixed-replace");
    445   response.setHTTPHeaderField("Content-type", "text/plain");
    446   MockWebURLLoaderClient client;
    447   MultipartResponseDelegate delegate(&client, NULL, response, "bound");
    448 
    449   // Test chunks of size 1, 2, and 0.
    450   string data(
    451     "--boundContent-type: text/plain\n\n"
    452     "\n--boundContent-type: text/plain\n\n"
    453     "\n\n--boundContent-type: text/plain\n\n"
    454     "--boundContent-type: text/plain\n\n"
    455     "end--bound--");
    456   delegate.OnReceivedData(data.c_str(),
    457                           static_cast<int>(data.length()),
    458                           static_cast<int>(data.length()));
    459   EXPECT_EQ(4, client.received_response_);
    460   EXPECT_EQ(2, client.received_data_);
    461   EXPECT_EQ(string("end"), client.data_);
    462   EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
    463 
    464   delegate.OnCompletedRequest();
    465   EXPECT_EQ(4, client.received_response_);
    466   EXPECT_EQ(2, client.received_data_);
    467 }
    468 
    469 TEST(MultipartResponseTest, MultipleBoundaries) {
    470   // Test multiple boundaries back to back
    471   WebURLResponse response;
    472   response.initialize();
    473   response.setMIMEType("multipart/x-mixed-replace");
    474   MockWebURLLoaderClient client;
    475   MultipartResponseDelegate delegate(&client, NULL, response, "bound");
    476 
    477   string data("--bound\r\n\r\n--bound\r\n\r\nfoofoo--bound--");
    478   delegate.OnReceivedData(data.c_str(),
    479                           static_cast<int>(data.length()),
    480                           static_cast<int>(data.length()));
    481   EXPECT_EQ(2, client.received_response_);
    482   EXPECT_EQ(1, client.received_data_);
    483   EXPECT_EQ(string("foofoo"), client.data_);
    484   EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
    485 }
    486 
    487 TEST(MultipartResponseTest, MultipartByteRangeParsingTest) {
    488   // Test multipart/byteranges based boundary parsing.
    489   WebURLResponse response1;
    490   response1.initialize();
    491   response1.setMIMEType("multipart/x-mixed-replace");
    492   response1.setHTTPHeaderField("Content-Length", "200");
    493   response1.setHTTPHeaderField("Content-type",
    494                                "multipart/byteranges; boundary=--bound--");
    495 
    496   std::string multipart_boundary;
    497   bool result = MultipartResponseDelegate::ReadMultipartBoundary(
    498       response1, &multipart_boundary);
    499   EXPECT_EQ(result, true);
    500   EXPECT_EQ(string("--bound--"),
    501             multipart_boundary);
    502 
    503   WebURLResponse response2;
    504   response2.initialize();
    505   response2.setMIMEType("image/png");
    506 
    507   response2.setHTTPHeaderField("Content-Length", "300");
    508   response2.setHTTPHeaderField("Last-Modified",
    509                                "Mon, 04 Apr 2005 20:36:01 GMT");
    510   response2.setHTTPHeaderField("Date", "Thu, 11 Sep 2008 18:21:42 GMT");
    511 
    512   multipart_boundary.clear();
    513   result = MultipartResponseDelegate::ReadMultipartBoundary(
    514       response2, &multipart_boundary);
    515   EXPECT_EQ(result, false);
    516 
    517   WebURLResponse response3;
    518   response3.initialize();
    519   response3.setMIMEType("multipart/byteranges");
    520 
    521   response3.setHTTPHeaderField("Content-Length", "300");
    522   response3.setHTTPHeaderField("Last-Modified",
    523                                "Mon, 04 Apr 2005 20:36:01 GMT");
    524   response3.setHTTPHeaderField("Date", "Thu, 11 Sep 2008 18:21:42 GMT");
    525   response3.setHTTPHeaderField("Content-type", "multipart/byteranges");
    526 
    527   multipart_boundary.clear();
    528   result = MultipartResponseDelegate::ReadMultipartBoundary(
    529       response3, &multipart_boundary);
    530   EXPECT_EQ(result, false);
    531   EXPECT_EQ(multipart_boundary.length(), 0U);
    532 
    533   WebURLResponse response4;
    534   response4.initialize();
    535   response4.setMIMEType("multipart/byteranges");
    536   response4.setHTTPHeaderField("Content-Length", "200");
    537   response4.setHTTPHeaderField("Content-type",
    538       "multipart/byteranges; boundary=--bound--; charSet=utf8");
    539 
    540   multipart_boundary.clear();
    541 
    542   result = MultipartResponseDelegate::ReadMultipartBoundary(
    543       response4, &multipart_boundary);
    544   EXPECT_EQ(result, true);
    545   EXPECT_EQ(string("--bound--"), multipart_boundary);
    546 
    547   WebURLResponse response5;
    548   response5.initialize();
    549   response5.setMIMEType("multipart/byteranges");
    550   response5.setHTTPHeaderField("Content-Length", "200");
    551   response5.setHTTPHeaderField("Content-type",
    552       "multipart/byteranges; boundary=\"--bound--\"; charSet=utf8");
    553 
    554   multipart_boundary.clear();
    555 
    556   result = MultipartResponseDelegate::ReadMultipartBoundary(
    557       response5, &multipart_boundary);
    558   EXPECT_EQ(result, true);
    559   EXPECT_EQ(string("--bound--"), multipart_boundary);
    560 }
    561 
    562 TEST(MultipartResponseTest, MultipartContentRangesTest) {
    563   WebURLResponse response1;
    564   response1.initialize();
    565   response1.setMIMEType("application/pdf");
    566   response1.setHTTPHeaderField("Content-Length", "200");  // Ignored!
    567   // Use intentionally >32bit values to check they are handled correctly.
    568   response1.setHTTPHeaderField("Content-Range",
    569                                "bytes 5000000000-5000000050/6000000000");
    570 
    571   int64 content_range_lower_bound = 0;
    572   int64 content_range_upper_bound = 0;
    573   int64 content_range_instance_size = 0;
    574 
    575   bool result = MultipartResponseDelegate::ReadContentRanges(
    576       response1, &content_range_lower_bound,
    577       &content_range_upper_bound,
    578       &content_range_instance_size);
    579 
    580   EXPECT_EQ(result, true);
    581   EXPECT_EQ(content_range_lower_bound, 5e9);
    582   EXPECT_EQ(content_range_upper_bound, 5e9+50);
    583   EXPECT_EQ(content_range_instance_size, 6e9);
    584 
    585   WebURLResponse response2;
    586   response2.initialize();
    587   response2.setMIMEType("application/pdf");
    588   response2.setHTTPHeaderField("Content-Length", "200");
    589   response2.setHTTPHeaderField("Content-Range", "bytes 1000/1050");
    590 
    591   content_range_lower_bound = 0;
    592   content_range_upper_bound = 0;
    593   content_range_instance_size = 0;
    594 
    595   result = MultipartResponseDelegate::ReadContentRanges(
    596       response2, &content_range_lower_bound,
    597       &content_range_upper_bound,
    598       &content_range_instance_size);
    599 
    600   EXPECT_EQ(result, false);
    601 
    602   WebURLResponse response3;
    603   response3.initialize();
    604   response3.setMIMEType("application/pdf");
    605   response3.setHTTPHeaderField("Content-Length", "200");
    606   response3.setHTTPHeaderField("Range", "bytes 1000-1050/5000");
    607 
    608   content_range_lower_bound = 0;
    609   content_range_upper_bound = 0;
    610   content_range_instance_size = 0;
    611 
    612   result = MultipartResponseDelegate::ReadContentRanges(
    613       response3, &content_range_lower_bound,
    614       &content_range_upper_bound,
    615       &content_range_instance_size);
    616 
    617   EXPECT_EQ(result, true);
    618   EXPECT_EQ(content_range_lower_bound, 1000);
    619   EXPECT_EQ(content_range_upper_bound, 1050);
    620 
    621   WebURLResponse response4;
    622   response4.initialize();
    623   response4.setMIMEType("application/pdf");
    624   response4.setHTTPHeaderField("Content-Length", "200");
    625 
    626   content_range_lower_bound = 0;
    627   content_range_upper_bound = 0;
    628   content_range_instance_size = 0;
    629 
    630   result = MultipartResponseDelegate::ReadContentRanges(
    631       response4, &content_range_lower_bound,
    632       &content_range_upper_bound,
    633       &content_range_instance_size);
    634 
    635   EXPECT_EQ(result, false);
    636 }
    637 
    638 TEST(MultipartResponseTest, MultipartPayloadSet) {
    639   WebURLResponse response;
    640   response.initialize();
    641   response.setMIMEType("multipart/x-mixed-replace");
    642   MockWebURLLoaderClient client;
    643   MultipartResponseDelegate delegate(&client, NULL, response, "bound");
    644 
    645   string data(
    646       "--bound\n"
    647       "Content-type: text/plain\n\n"
    648       "response data\n"
    649       "--bound\n");
    650   delegate.OnReceivedData(data.c_str(),
    651                           static_cast<int>(data.length()),
    652                           static_cast<int>(data.length()));
    653   EXPECT_EQ(1, client.received_response_);
    654   EXPECT_EQ(string("response data"), client.data_);
    655   EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
    656   EXPECT_FALSE(client.response_.isMultipartPayload());
    657 
    658   string data2(
    659       "Content-type: text/plain\n\n"
    660       "response data2\n"
    661       "--bound\n");
    662   delegate.OnReceivedData(data2.c_str(),
    663                           static_cast<int>(data2.length()),
    664                           static_cast<int>(data2.length()));
    665   EXPECT_EQ(2, client.received_response_);
    666   EXPECT_EQ(string("response data2"), client.data_);
    667   EXPECT_EQ(static_cast<int>(data.length()) + static_cast<int>(data2.length()),
    668             client.total_encoded_data_length_);
    669   EXPECT_TRUE(client.response_.isMultipartPayload());
    670 }
    671 
    672 }  // namespace
    673 
    674 }  // namespace content
    675