This source file includes following definitions.
- PushOverLine
 
- ParseHeaders
 
- FindBoundary
 
- boundary
 
- data
 
- willSendRequest
 
- didSendData
 
- didReceiveResponse
 
- didReceiveData
 
- didFinishLoading
 
- didFail
 
- Reset
 
- GetResponseHeader
 
- TEST
 
- TEST
 
- TEST
 
- VariousChunkSizesTest
 
- TEST
 
- TEST
 
- TEST
 
- TEST
 
- TEST
 
- TEST
 
- TEST
 
- TEST
 
#include <vector>
#include "base/basictypes.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebString.h"
#include "third_party/WebKit/public/platform/WebURL.h"
#include "third_party/WebKit/public/platform/WebURLLoaderClient.h"
#include "third_party/WebKit/public/platform/WebURLResponse.h"
#include "webkit/child/multipart_response_delegate.h"
using std::string;
using blink::WebString;
using blink::WebURL;
using blink::WebURLError;
using blink::WebURLLoader;
using blink::WebURLLoaderClient;
using blink::WebURLRequest;
using blink::WebURLResponse;
using webkit_glue::MultipartResponseDelegate;
using webkit_glue::MultipartResponseDelegateTester;
namespace webkit_glue {
class MultipartResponseDelegateTester {
 public:
  MultipartResponseDelegateTester(MultipartResponseDelegate* delegate)
      : delegate_(delegate) {
  }
  int PushOverLine(const std::string& data, size_t pos) {
    return delegate_->PushOverLine(data, pos);
  }
  bool ParseHeaders() { return delegate_->ParseHeaders(); }
  size_t FindBoundary() { return delegate_->FindBoundary(); }
  std::string& boundary() { return delegate_->boundary_; }
  std::string& data() { return delegate_->data_; }
 private:
  MultipartResponseDelegate* delegate_;
};
}  
namespace {
class MultipartResponseTest : public testing::Test {
};
class MockWebURLLoaderClient : public WebURLLoaderClient {
 public:
  MockWebURLLoaderClient() { Reset(); }
  virtual void willSendRequest(
      WebURLLoader*, WebURLRequest&, const WebURLResponse&) {}
  virtual void didSendData(
      WebURLLoader*, unsigned long long, unsigned long long) {}
  virtual void didReceiveResponse(WebURLLoader* loader,
                                  const WebURLResponse& response) {
    ++received_response_;
    response_ = response;
    data_.clear();
  }
  virtual void didReceiveData(
      blink::WebURLLoader* loader,
      const char* data,
      int data_length,
      int encoded_data_length) {
    ++received_data_;
    data_.append(data, data_length);
    total_encoded_data_length_ += encoded_data_length;
  }
  virtual void didFinishLoading(
      WebURLLoader*, double finishTime, int64_t total_encoded_data_length) {}
  virtual void didFail(WebURLLoader*, const WebURLError&) {}
  void Reset() {
    received_response_ = received_data_ = total_encoded_data_length_ = 0;
    data_.clear();
    response_.reset();
  }
  string GetResponseHeader(const char* name) const {
    return string(response_.httpHeaderField(WebString::fromUTF8(name)).utf8());
  }
  int received_response_, received_data_, total_encoded_data_length_;
  string data_;
  WebURLResponse response_;
};
TEST(MultipartResponseTest, Functions) {
  
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Foo", "Bar");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");
  MultipartResponseDelegateTester delegate_tester(&delegate);
  struct {
    const char* input;
    const int position;
    const int expected;
  } line_tests[] = {
    { "Line", 0, 0 },
    { "Line", 2, 0 },
    { "Line", 10, 0 },
    { "\r\nLine", 0, 2 },
    { "\nLine", 0, 1 },
    { "\n\nLine", 0, 2 },
    { "\rLine", 0, 1 },
    { "Line\r\nLine", 4, 2 },
    { "Line\nLine", 4, 1 },
    { "Line\n\nLine", 4, 2 },
    { "Line\rLine", 4, 1 },
    { "Line\r\rLine", 4, 1 },
  };
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(line_tests); ++i) {
    EXPECT_EQ(line_tests[i].expected,
              delegate_tester.PushOverLine(line_tests[i].input,
                                           line_tests[i].position));
  }
  
  struct {
    const char* data;
    const bool rv;
    const int received_response_calls;
    const char* newdata;
  } header_tests[] = {
    { "This is junk", false, 0, "This is junk" },
    { "Foo: bar\nBaz:\n\nAfter:\n", true, 1, "After:\n" },
    { "Foo: bar\nBaz:\n", false, 0, "Foo: bar\nBaz:\n" },
    { "Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 1, "After:\r\n" },
    { "Foo: bar\r\nBaz:\r\n", false, 0, "Foo: bar\r\nBaz:\r\n" },
    { "Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 1, "After:\n\n" },
    { "Foo: bar\r\nBaz:\n", false, 0, "Foo: bar\r\nBaz:\n" },
    { "\r\n", true, 1, "" },
  };
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(header_tests); ++i) {
    client.Reset();
    delegate_tester.data().assign(header_tests[i].data);
    EXPECT_EQ(header_tests[i].rv,
              delegate_tester.ParseHeaders());
    EXPECT_EQ(header_tests[i].received_response_calls,
              client.received_response_);
    EXPECT_EQ(string(header_tests[i].newdata),
              delegate_tester.data());
  }
  
  
  client.Reset();
  string test_header("content-type: image/png\ncontent-length: 10\n\n");
  delegate_tester.data().assign(test_header);
  EXPECT_TRUE(delegate_tester.ParseHeaders());
  EXPECT_TRUE(delegate_tester.data().length() == 0);
  EXPECT_EQ(string("image/png"), client.GetResponseHeader("Content-Type"));
  EXPECT_EQ(string("10"), client.GetResponseHeader("content-length"));
  
  EXPECT_EQ(string("Bar"), client.GetResponseHeader("foo"));
  
  client.Reset();
  string test_header2("content-type: text/html; charset=utf-8\n\n");
  delegate_tester.data().assign(test_header2);
  EXPECT_TRUE(delegate_tester.ParseHeaders());
  EXPECT_TRUE(delegate_tester.data().length() == 0);
  EXPECT_EQ(string("text/html; charset=utf-8"),
            client.GetResponseHeader("Content-Type"));
  EXPECT_EQ(string("utf-8"),
            string(client.response_.textEncodingName().utf8()));
  
  struct {
    const char* boundary;
    const char* data;
    const size_t position;
  } boundary_tests[] = {
    { "bound", "bound", 0 },
    { "bound", "--bound", 0 },
    { "bound", "junkbound", 4 },
    { "bound", "junk--bound", 4 },
    { "foo", "bound", string::npos },
    { "bound", "--boundbound", 0 },
  };
  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(boundary_tests); ++i) {
    delegate_tester.boundary().assign(boundary_tests[i].boundary);
    delegate_tester.data().assign(boundary_tests[i].data);
    EXPECT_EQ(boundary_tests[i].position,
              delegate_tester.FindBoundary());
  }
}
TEST(MultipartResponseTest, MissingBoundaries) {
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Foo", "Bar");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");
  
  string no_start_boundary(
    "Content-type: text/plain\n\n"
    "This is a sample response\n"
    "--bound--"
    "ignore junk after end token --bound\n\nTest2\n");
  delegate.OnReceivedData(no_start_boundary.c_str(),
                          static_cast<int>(no_start_boundary.length()),
                          static_cast<int>(no_start_boundary.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response"), client.data_);
  EXPECT_EQ(static_cast<int>(no_start_boundary.length()),
            client.total_encoded_data_length_);
  delegate.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  
  client.Reset();
  MultipartResponseDelegate delegate2(&client, NULL, response, "bound");
  string no_end_boundary(
    "bound\nContent-type: text/plain\n\n"
    "This is a sample response\n");
  delegate2.OnReceivedData(no_end_boundary.c_str(),
                          static_cast<int>(no_end_boundary.length()),
                          static_cast<int>(no_end_boundary.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ("This is a sample response\n", client.data_);
  EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
            client.total_encoded_data_length_);
  delegate2.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response\n"), client.data_);
  EXPECT_EQ(static_cast<int>(no_end_boundary.length()),
            client.total_encoded_data_length_);
  
  client.Reset();
  MultipartResponseDelegate delegate3(&client, NULL, response, "bound");
  string no_boundaries(
    "Content-type: text/plain\n\n"
    "This is a sample response\n");
  delegate3.OnReceivedData(no_boundaries.c_str(),
                           static_cast<int>(no_boundaries.length()),
                           static_cast<int>(no_boundaries.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ("This is a sample response\n", client.data_);
  EXPECT_EQ(static_cast<int>(no_boundaries.length()),
            client.total_encoded_data_length_);
  delegate3.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response\n"), client.data_);
  EXPECT_EQ(static_cast<int>(no_boundaries.length()),
            client.total_encoded_data_length_);
}
TEST(MultipartResponseTest, MalformedBoundary) {
  
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Foo", "Bar");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "--bound");
  string data(
    "--bound\n"
    "Content-type: text/plain\n\n"
    "This is a sample response\n"
    "--bound--"
    "ignore junk after end token --bound\n\nTest2\n");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("This is a sample response"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
  delegate.OnCompletedRequest();
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
}
struct TestChunk {
  const int start_pos;  
  const int end_pos;    
  const int expected_responses;
  const int expected_received_data;
  const char* expected_data;
  const int expected_encoded_data_length;
};
void VariousChunkSizesTest(const TestChunk chunks[], int chunks_size,
                           int responses, int received_data,
                           const char* completed_data,
                           int completed_encoded_data_length) {
  const string data(
    "--bound\n"                    
    "Content-type: image/png\n\n"  
    "datadatadatadatadata"         
    "--bound\n"                    
    "Content-type: image/jpg\n\n"  
    "foofoofoofoofoo"              
    "--bound--");                  
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");
  for (int i = 0; i < chunks_size; ++i) {
    ASSERT_TRUE(chunks[i].start_pos < chunks[i].end_pos);
    string chunk = data.substr(chunks[i].start_pos,
                               chunks[i].end_pos - chunks[i].start_pos);
    delegate.OnReceivedData(
        chunk.c_str(),
        static_cast<int>(chunk.length()),
        static_cast<int>(chunk.length()));
    EXPECT_EQ(chunks[i].expected_responses, client.received_response_);
    EXPECT_EQ(chunks[i].expected_received_data, client.received_data_);
    EXPECT_EQ(string(chunks[i].expected_data), client.data_);
    EXPECT_EQ(chunks[i].expected_encoded_data_length,
              client.total_encoded_data_length_);
  }
  
  delegate.OnCompletedRequest();
  EXPECT_EQ(responses, client.received_response_);
  EXPECT_EQ(received_data, client.received_data_);
  string completed_data_string(completed_data);
  EXPECT_EQ(completed_data_string, client.data_);
  EXPECT_EQ(completed_encoded_data_length, client.total_encoded_data_length_);
}
TEST(MultipartResponseTest, BreakInBoundary) {
  
  const TestChunk bound1[] = {
    { 0, 4, 0, 0, "", 0 },
    { 4, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(bound1, arraysize(bound1),
                        2, 2, "foofoofoofoofoo", 110);
  
  const TestChunk bound2[] = {
    { 0, 4, 0, 0, "", 0 },
    { 4, 55, 1, 1, "datadatadatadat", 55 },
    { 55, 65, 1, 2, "datadatadatadatadata", 65 },
    { 65, 110, 2, 3, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(bound2, arraysize(bound2),
                        2, 3, "foofoofoofoofoo", 110);
  
  const TestChunk bound3[] = {
    { 0, 55, 1, 1, "datadatadatadat", 55 },
    { 55, 110, 2, 3, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(bound3, arraysize(bound3),
                        2, 3, "foofoofoofoofoo", 110);
}
TEST(MultipartResponseTest, BreakInHeaders) {
  
  const TestChunk header1[] = {
    { 0, 10, 0, 0, "", 0 },
    { 10, 35, 1, 0, "", 0 },
    { 35, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(header1, arraysize(header1),
                        2, 2, "foofoofoofoofoo", 110);
  
  const TestChunk header2[] = {
    { 0, 10, 0, 0, "", 0 },
    { 10, 65, 1, 1, "datadatadatadatadata", 65 },
    { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(header2, arraysize(header2),
                        2, 2, "foofoofoofoofoo", 110);
  
  const TestChunk header3[] = {
    { 0, 33, 1, 0, "", 0 },
    { 33, 65, 1, 1, "datadatadatadatadata", 65 },
    { 65, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(header3, arraysize(header3),
                        2, 2, "foofoofoofoofoo", 110);
}
TEST(MultipartResponseTest, BreakInData) {
  
  const TestChunk data1[] = {
    { 0, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(data1, arraysize(data1),
                        2, 2, "foofoofoofoofoo", 110);
  
  const TestChunk data2[] = {
    { 0, 35, 1, 0, "", 0 },
    { 35, 65, 1, 1, "datadatadatadatadata", 65 },
    { 65, 90, 2, 1, "", 65 },
    { 90, 110, 2, 2, "foofoofoofoofoo", 110 },
  };
  VariousChunkSizesTest(data2, arraysize(data2),
                        2, 2, "foofoofoofoofoo", 110);
  
  const TestChunk data3[] = {
    { 0, 35, 1, 0, "", 0 },
    { 35, 90, 2, 1, "", 90 },
  };
  VariousChunkSizesTest(data3, arraysize(data3),
                        2, 2, "foof", 90);
}
TEST(MultipartResponseTest, SmallChunk) {
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  response.setHTTPHeaderField("Content-type", "text/plain");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");
  
  string data(
    "--boundContent-type: text/plain\n\n"
    "\n--boundContent-type: text/plain\n\n"
    "\n\n--boundContent-type: text/plain\n\n"
    "--boundContent-type: text/plain\n\n"
    "end--bound--");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(4, client.received_response_);
  EXPECT_EQ(2, client.received_data_);
  EXPECT_EQ(string("end"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
  delegate.OnCompletedRequest();
  EXPECT_EQ(4, client.received_response_);
  EXPECT_EQ(2, client.received_data_);
}
TEST(MultipartResponseTest, MultipleBoundaries) {
  
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");
  string data("--bound\r\n\r\n--bound\r\n\r\nfoofoo--bound--");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(2, client.received_response_);
  EXPECT_EQ(1, client.received_data_);
  EXPECT_EQ(string("foofoo"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
}
TEST(MultipartResponseTest, MultipartByteRangeParsingTest) {
  
  WebURLResponse response1;
  response1.initialize();
  response1.setMIMEType("multipart/x-mixed-replace");
  response1.setHTTPHeaderField("Content-Length", "200");
  response1.setHTTPHeaderField("Content-type",
                               "multipart/byteranges; boundary=--bound--");
  std::string multipart_boundary;
  bool result = MultipartResponseDelegate::ReadMultipartBoundary(
      response1, &multipart_boundary);
  EXPECT_EQ(result, true);
  EXPECT_EQ(string("--bound--"),
            multipart_boundary);
  WebURLResponse response2;
  response2.initialize();
  response2.setMIMEType("image/png");
  response2.setHTTPHeaderField("Content-Length", "300");
  response2.setHTTPHeaderField("Last-Modified",
                               "Mon, 04 Apr 2005 20:36:01 GMT");
  response2.setHTTPHeaderField("Date", "Thu, 11 Sep 2008 18:21:42 GMT");
  multipart_boundary.clear();
  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response2, &multipart_boundary);
  EXPECT_EQ(result, false);
  WebURLResponse response3;
  response3.initialize();
  response3.setMIMEType("multipart/byteranges");
  response3.setHTTPHeaderField("Content-Length", "300");
  response3.setHTTPHeaderField("Last-Modified",
                               "Mon, 04 Apr 2005 20:36:01 GMT");
  response3.setHTTPHeaderField("Date", "Thu, 11 Sep 2008 18:21:42 GMT");
  response3.setHTTPHeaderField("Content-type", "multipart/byteranges");
  multipart_boundary.clear();
  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response3, &multipart_boundary);
  EXPECT_EQ(result, false);
  EXPECT_EQ(multipart_boundary.length(), 0U);
  WebURLResponse response4;
  response4.initialize();
  response4.setMIMEType("multipart/byteranges");
  response4.setHTTPHeaderField("Content-Length", "200");
  response4.setHTTPHeaderField("Content-type",
      "multipart/byteranges; boundary=--bound--; charSet=utf8");
  multipart_boundary.clear();
  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response4, &multipart_boundary);
  EXPECT_EQ(result, true);
  EXPECT_EQ(string("--bound--"), multipart_boundary);
  WebURLResponse response5;
  response5.initialize();
  response5.setMIMEType("multipart/byteranges");
  response5.setHTTPHeaderField("Content-Length", "200");
  response5.setHTTPHeaderField("Content-type",
      "multipart/byteranges; boundary=\"--bound--\"; charSet=utf8");
  multipart_boundary.clear();
  result = MultipartResponseDelegate::ReadMultipartBoundary(
      response5, &multipart_boundary);
  EXPECT_EQ(result, true);
  EXPECT_EQ(string("--bound--"), multipart_boundary);
}
TEST(MultipartResponseTest, MultipartContentRangesTest) {
  WebURLResponse response1;
  response1.initialize();
  response1.setMIMEType("application/pdf");
  response1.setHTTPHeaderField("Content-Length", "200");  
  
  response1.setHTTPHeaderField("Content-Range",
                               "bytes 5000000000-5000000050/6000000000");
  int64 content_range_lower_bound = 0;
  int64 content_range_upper_bound = 0;
  int64 content_range_instance_size = 0;
  bool result = MultipartResponseDelegate::ReadContentRanges(
      response1, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);
  EXPECT_EQ(result, true);
  EXPECT_EQ(content_range_lower_bound, 5e9);
  EXPECT_EQ(content_range_upper_bound, 5e9+50);
  EXPECT_EQ(content_range_instance_size, 6e9);
  WebURLResponse response2;
  response2.initialize();
  response2.setMIMEType("application/pdf");
  response2.setHTTPHeaderField("Content-Length", "200");
  response2.setHTTPHeaderField("Content-Range", "bytes 1000/1050");
  content_range_lower_bound = 0;
  content_range_upper_bound = 0;
  content_range_instance_size = 0;
  result = MultipartResponseDelegate::ReadContentRanges(
      response2, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);
  EXPECT_EQ(result, false);
  WebURLResponse response3;
  response3.initialize();
  response3.setMIMEType("application/pdf");
  response3.setHTTPHeaderField("Content-Length", "200");
  response3.setHTTPHeaderField("Range", "bytes 1000-1050/5000");
  content_range_lower_bound = 0;
  content_range_upper_bound = 0;
  content_range_instance_size = 0;
  result = MultipartResponseDelegate::ReadContentRanges(
      response3, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);
  EXPECT_EQ(result, true);
  EXPECT_EQ(content_range_lower_bound, 1000);
  EXPECT_EQ(content_range_upper_bound, 1050);
  WebURLResponse response4;
  response4.initialize();
  response4.setMIMEType("application/pdf");
  response4.setHTTPHeaderField("Content-Length", "200");
  content_range_lower_bound = 0;
  content_range_upper_bound = 0;
  content_range_instance_size = 0;
  result = MultipartResponseDelegate::ReadContentRanges(
      response4, &content_range_lower_bound,
      &content_range_upper_bound,
      &content_range_instance_size);
  EXPECT_EQ(result, false);
}
TEST(MultipartResponseTest, MultipartPayloadSet) {
  WebURLResponse response;
  response.initialize();
  response.setMIMEType("multipart/x-mixed-replace");
  MockWebURLLoaderClient client;
  MultipartResponseDelegate delegate(&client, NULL, response, "bound");
  string data(
      "--bound\n"
      "Content-type: text/plain\n\n"
      "response data\n"
      "--bound\n");
  delegate.OnReceivedData(data.c_str(),
                          static_cast<int>(data.length()),
                          static_cast<int>(data.length()));
  EXPECT_EQ(1, client.received_response_);
  EXPECT_EQ(string("response data"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()), client.total_encoded_data_length_);
  EXPECT_FALSE(client.response_.isMultipartPayload());
  string data2(
      "Content-type: text/plain\n\n"
      "response data2\n"
      "--bound\n");
  delegate.OnReceivedData(data2.c_str(),
                          static_cast<int>(data2.length()),
                          static_cast<int>(data2.length()));
  EXPECT_EQ(2, client.received_response_);
  EXPECT_EQ(string("response data2"), client.data_);
  EXPECT_EQ(static_cast<int>(data.length()) + static_cast<int>(data2.length()),
            client.total_encoded_data_length_);
  EXPECT_TRUE(client.response_.isMultipartPayload());
}
}