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