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 <algorithm> 6 7 #include "base/format_macros.h" 8 #include "base/stringprintf.h" 9 #include "net/base/net_errors.h" 10 #include "net/http/http_util.h" 11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 12 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrameClient.h" 13 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLError.h" 14 #include "third_party/WebKit/Source/WebKit/chromium/public/WebURLResponse.h" 15 #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" 16 #include "webkit/glue/media/buffered_resource_loader.h" 17 #include "webkit/mocks/mock_webframe.h" 18 #include "webkit/mocks/mock_weburlloader.h" 19 20 using ::testing::_; 21 using ::testing::Assign; 22 using ::testing::AtLeast; 23 using ::testing::DeleteArg; 24 using ::testing::DoAll; 25 using ::testing::InSequence; 26 using ::testing::Invoke; 27 using ::testing::InvokeWithoutArgs; 28 using ::testing::NotNull; 29 using ::testing::Return; 30 using ::testing::ReturnRef; 31 using ::testing::SetArgumentPointee; 32 using ::testing::StrictMock; 33 using ::testing::NiceMock; 34 using ::testing::WithArgs; 35 36 using WebKit::WebURLError; 37 using WebKit::WebFrameClient; 38 using WebKit::WebURLResponse; 39 using WebKit::WebView; 40 41 namespace webkit_glue { 42 43 static const char* kHttpUrl = "http://test"; 44 static const char kHttpRedirectToSameDomainUrl1[] = "http://test/ing"; 45 static const char kHttpRedirectToSameDomainUrl2[] = "http://test/ing2"; 46 static const char kHttpRedirectToDifferentDomainUrl1[] = "http://test2"; 47 static const char kHttpRedirectToDifferentDomainUrl2[] = "http://test2/ing"; 48 49 static const int kDataSize = 1024; 50 static const int kHttpOK = 200; 51 static const int kHttpPartialContent = 206; 52 53 enum NetworkState { 54 NONE, 55 LOADED, 56 LOADING 57 }; 58 59 // Submit a request completed event to the resource loader due to request 60 // being canceled. Pretending the event is from external. 61 ACTION_P(RequestCanceled, loader) { 62 WebURLError error; 63 error.reason = net::ERR_ABORTED; 64 error.domain = WebString::fromUTF8(net::kErrorDomain); 65 loader->didFail(NULL, error); 66 } 67 68 class BufferedResourceLoaderTest : public testing::Test { 69 public: 70 BufferedResourceLoaderTest() { 71 for (int i = 0; i < kDataSize; ++i) 72 data_[i] = i; 73 } 74 75 virtual ~BufferedResourceLoaderTest() { 76 } 77 78 void Initialize(const char* url, int first_position, int last_position) { 79 gurl_ = GURL(url); 80 first_position_ = first_position; 81 last_position_ = last_position; 82 83 frame_.reset(new NiceMock<MockWebFrame>()); 84 85 url_loader_ = new NiceMock<MockWebURLLoader>(); 86 loader_ = new BufferedResourceLoader(gurl_, 87 first_position_, last_position_); 88 loader_->SetURLLoaderForTest(url_loader_); 89 } 90 91 void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) { 92 loader_->buffer_.reset( 93 new media::SeekableBuffer(backward_capacity, forward_capacity)); 94 } 95 96 void Start() { 97 InSequence s; 98 EXPECT_CALL(*url_loader_, loadAsynchronously(_, loader_.get())); 99 loader_->Start( 100 NewCallback(this, &BufferedResourceLoaderTest::StartCallback), 101 NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback), 102 frame_.get()); 103 } 104 105 void FullResponse(int64 instance_size) { 106 FullResponse(instance_size, net::OK); 107 } 108 109 void FullResponse(int64 instance_size, int status) { 110 EXPECT_CALL(*this, StartCallback(status)); 111 if (status != net::OK) { 112 EXPECT_CALL(*url_loader_, cancel()) 113 .WillOnce(RequestCanceled(loader_)); 114 } 115 116 WebURLResponse response(gurl_); 117 response.setHTTPHeaderField(WebString::fromUTF8("Content-Length"), 118 WebString::fromUTF8(base::StringPrintf("%" 119 PRId64, instance_size))); 120 response.setExpectedContentLength(instance_size); 121 response.setHTTPStatusCode(kHttpOK); 122 loader_->didReceiveResponse(url_loader_, response); 123 124 if (status == net::OK) { 125 EXPECT_EQ(instance_size, loader_->content_length()); 126 EXPECT_EQ(instance_size, loader_->instance_size()); 127 } 128 129 EXPECT_FALSE(loader_->range_supported()); 130 } 131 132 void PartialResponse(int64 first_position, int64 last_position, 133 int64 instance_size) { 134 PartialResponse(first_position, last_position, instance_size, false, true); 135 } 136 137 void PartialResponse(int64 first_position, int64 last_position, 138 int64 instance_size, bool chunked, bool accept_ranges) { 139 EXPECT_CALL(*this, StartCallback(net::OK)); 140 141 WebURLResponse response(gurl_); 142 response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"), 143 WebString::fromUTF8(base::StringPrintf("bytes " 144 "%" PRId64 "-%" PRId64 "/%" PRId64, 145 first_position, 146 last_position, 147 instance_size))); 148 149 // HTTP 1.1 doesn't permit Content-Length with Transfer-Encoding: chunked. 150 int64 content_length = -1; 151 if (chunked) { 152 response.setHTTPHeaderField(WebString::fromUTF8("Transfer-Encoding"), 153 WebString::fromUTF8("chunked")); 154 } else { 155 content_length = last_position - first_position + 1; 156 } 157 response.setExpectedContentLength(content_length); 158 159 // A server isn't required to return Accept-Ranges even though it might. 160 if (accept_ranges) { 161 response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"), 162 WebString::fromUTF8("bytes")); 163 } 164 165 response.setHTTPStatusCode(kHttpPartialContent); 166 loader_->didReceiveResponse(url_loader_, response); 167 168 // XXX: what's the difference between these two? For example in the chunked 169 // range request case, Content-Length is unspecified (because it's chunked) 170 // but Content-Range: a-b/c can be returned, where c == Content-Length 171 // 172 // Can we eliminate one? 173 EXPECT_EQ(content_length, loader_->content_length()); 174 EXPECT_EQ(instance_size, loader_->instance_size()); 175 176 // A valid partial response should always result in this being true. 177 EXPECT_TRUE(loader_->range_supported()); 178 } 179 180 void Redirect(const char* url) { 181 GURL redirectUrl(url); 182 WebKit::WebURLRequest newRequest(redirectUrl); 183 WebKit::WebURLResponse redirectResponse(gurl_); 184 185 loader_->willSendRequest(url_loader_, newRequest, redirectResponse); 186 187 MessageLoop::current()->RunAllPending(); 188 } 189 190 void StopWhenLoad() { 191 InSequence s; 192 EXPECT_CALL(*url_loader_, cancel()) 193 .WillOnce(RequestCanceled(loader_)); 194 loader_->Stop(); 195 loader_ = NULL; 196 } 197 198 // Helper method to write to |loader_| from |data_|. 199 void WriteLoader(int position, int size) { 200 EXPECT_CALL(*this, NetworkCallback()) 201 .RetiresOnSaturation(); 202 loader_->didReceiveData(url_loader_, 203 reinterpret_cast<char*>(data_ + position), 204 size, 205 size); 206 } 207 208 // Helper method to read from |loader_|. 209 void ReadLoader(int64 position, int size, uint8* buffer) { 210 loader_->Read(position, size, buffer, 211 NewCallback(this, &BufferedResourceLoaderTest::ReadCallback)); 212 } 213 214 // Verifis that data in buffer[0...size] is equal to data_[pos...pos+size]. 215 void VerifyBuffer(uint8* buffer, int pos, int size) { 216 EXPECT_EQ(0, memcmp(buffer, data_ + pos, size)); 217 } 218 219 void ConfirmLoaderDeferredState(bool expectedVal) { 220 EXPECT_EQ(loader_->deferred_, expectedVal); 221 } 222 223 MOCK_METHOD1(StartCallback, void(int error)); 224 MOCK_METHOD1(ReadCallback, void(int error)); 225 MOCK_METHOD0(NetworkCallback, void()); 226 227 protected: 228 GURL gurl_; 229 int64 first_position_; 230 int64 last_position_; 231 232 scoped_refptr<BufferedResourceLoader> loader_; 233 NiceMock<MockWebURLLoader>* url_loader_; 234 scoped_ptr<NiceMock<MockWebFrame> > frame_; 235 236 uint8 data_[kDataSize]; 237 238 private: 239 DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoaderTest); 240 }; 241 242 TEST_F(BufferedResourceLoaderTest, StartStop) { 243 Initialize(kHttpUrl, -1, -1); 244 Start(); 245 StopWhenLoad(); 246 } 247 248 // Tests that a bad HTTP response is recived, e.g. file not found. 249 TEST_F(BufferedResourceLoaderTest, BadHttpResponse) { 250 Initialize(kHttpUrl, -1, -1); 251 Start(); 252 253 EXPECT_CALL(*this, StartCallback(net::ERR_FAILED)); 254 EXPECT_CALL(*url_loader_, cancel()) 255 .WillOnce(RequestCanceled(loader_)); 256 257 WebURLResponse response(gurl_); 258 response.setHTTPStatusCode(404); 259 response.setHTTPStatusText("Not Found\n"); 260 loader_->didReceiveResponse(url_loader_, response); 261 } 262 263 // Tests that partial content is requested but not fulfilled. 264 TEST_F(BufferedResourceLoaderTest, NotPartialResponse) { 265 Initialize(kHttpUrl, 100, -1); 266 Start(); 267 FullResponse(1024, net::ERR_INVALID_RESPONSE); 268 } 269 270 // Tests that a 200 response is received. 271 TEST_F(BufferedResourceLoaderTest, FullResponse) { 272 Initialize(kHttpUrl, -1, -1); 273 Start(); 274 FullResponse(1024); 275 StopWhenLoad(); 276 } 277 278 // Tests that a partial content response is received. 279 TEST_F(BufferedResourceLoaderTest, PartialResponse) { 280 Initialize(kHttpUrl, 100, 200); 281 Start(); 282 PartialResponse(100, 200, 1024); 283 StopWhenLoad(); 284 } 285 286 TEST_F(BufferedResourceLoaderTest, PartialResponse_Chunked) { 287 Initialize(kHttpUrl, 100, 200); 288 Start(); 289 PartialResponse(100, 200, 1024, true, true); 290 StopWhenLoad(); 291 } 292 293 TEST_F(BufferedResourceLoaderTest, PartialResponse_NoAcceptRanges) { 294 Initialize(kHttpUrl, 100, 200); 295 Start(); 296 PartialResponse(100, 200, 1024, false, false); 297 StopWhenLoad(); 298 } 299 300 TEST_F(BufferedResourceLoaderTest, PartialResponse_ChunkedNoAcceptRanges) { 301 Initialize(kHttpUrl, 100, 200); 302 Start(); 303 PartialResponse(100, 200, 1024, true, false); 304 StopWhenLoad(); 305 } 306 307 // Tests that an invalid partial response is received. 308 TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) { 309 Initialize(kHttpUrl, 0, 10); 310 Start(); 311 312 EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE)); 313 EXPECT_CALL(*url_loader_, cancel()) 314 .WillOnce(RequestCanceled(loader_)); 315 316 WebURLResponse response(gurl_); 317 response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"), 318 WebString::fromUTF8(base::StringPrintf("bytes " 319 "%d-%d/%d", 1, 10, 1024))); 320 response.setExpectedContentLength(10); 321 response.setHTTPStatusCode(kHttpPartialContent); 322 loader_->didReceiveResponse(url_loader_, response); 323 } 324 325 // Tests the logic of sliding window for data buffering and reading. 326 TEST_F(BufferedResourceLoaderTest, BufferAndRead) { 327 Initialize(kHttpUrl, 10, 29); 328 loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer); 329 Start(); 330 PartialResponse(10, 29, 30); 331 332 uint8 buffer[10]; 333 InSequence s; 334 335 // Writes 10 bytes and read them back. 336 WriteLoader(10, 10); 337 EXPECT_CALL(*this, ReadCallback(10)); 338 ReadLoader(10, 10, buffer); 339 VerifyBuffer(buffer, 10, 10); 340 341 // Writes 10 bytes and read 2 times. 342 WriteLoader(20, 10); 343 EXPECT_CALL(*this, ReadCallback(5)); 344 ReadLoader(20, 5, buffer); 345 VerifyBuffer(buffer, 20, 5); 346 EXPECT_CALL(*this, ReadCallback(5)); 347 ReadLoader(25, 5, buffer); 348 VerifyBuffer(buffer, 25, 5); 349 350 // Read backward within buffer. 351 EXPECT_CALL(*this, ReadCallback(10)); 352 ReadLoader(10, 10, buffer); 353 VerifyBuffer(buffer, 10, 10); 354 355 // Read backward outside buffer. 356 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 357 ReadLoader(9, 10, buffer); 358 359 // Response has completed. 360 EXPECT_CALL(*this, NetworkCallback()); 361 loader_->didFinishLoading(url_loader_, 0); 362 363 // Try to read 10 from position 25 will just return with 5 bytes. 364 EXPECT_CALL(*this, ReadCallback(5)); 365 ReadLoader(25, 10, buffer); 366 VerifyBuffer(buffer, 25, 5); 367 368 // Try to read outside buffered range after request has completed. 369 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 370 ReadLoader(5, 10, buffer); 371 372 // Try to read beyond the instance size. 373 EXPECT_CALL(*this, ReadCallback(0)); 374 ReadLoader(30, 10, buffer); 375 } 376 377 TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) { 378 Initialize(kHttpUrl, 10, 0x00FFFFFF); 379 loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer); 380 Start(); 381 PartialResponse(10, 0x00FFFFFF, 0x01000000); 382 383 uint8 buffer[10]; 384 InSequence s; 385 386 // Read very far aheard will get a cache miss. 387 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 388 ReadLoader(0x00FFFFFF, 1, buffer); 389 390 // The following call will not call ReadCallback() because it is waiting for 391 // data to arrive. 392 ReadLoader(10, 10, buffer); 393 394 // Writing to loader will fulfill the read request. 395 EXPECT_CALL(*this, ReadCallback(10)); 396 WriteLoader(10, 20); 397 VerifyBuffer(buffer, 10, 10); 398 399 // The following call cannot be fulfilled now. 400 ReadLoader(25, 10, buffer); 401 402 EXPECT_CALL(*this, ReadCallback(5)); 403 EXPECT_CALL(*this, NetworkCallback()); 404 loader_->didFinishLoading(url_loader_, 0); 405 } 406 407 TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) { 408 Initialize(kHttpUrl, 10, 29); 409 Start(); 410 PartialResponse(10, 29, 30); 411 412 uint8 buffer[10]; 413 InSequence s; 414 415 ReadLoader(10, 10, buffer); 416 EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED)); 417 EXPECT_CALL(*this, NetworkCallback()); 418 WebURLError error; 419 error.reason = net::ERR_FAILED; 420 loader_->didFail(url_loader_, error); 421 } 422 423 // Tests the data buffering logic of NeverDefer strategy. 424 TEST_F(BufferedResourceLoaderTest, NeverDeferStrategy) { 425 Initialize(kHttpUrl, 10, 99); 426 SetLoaderBuffer(10, 20); 427 loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer); 428 Start(); 429 PartialResponse(10, 99, 100); 430 431 uint8 buffer[10]; 432 433 // Read past the buffer size; should not defer regardless. 434 WriteLoader(10, 10); 435 WriteLoader(20, 50); 436 ConfirmLoaderDeferredState(false); 437 438 // Should move past window. 439 EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); 440 ReadLoader(10, 10, buffer); 441 442 StopWhenLoad(); 443 } 444 445 // Tests the data buffering logic of ReadThenDefer strategy. 446 TEST_F(BufferedResourceLoaderTest, ReadThenDeferStrategy) { 447 Initialize(kHttpUrl, 10, 99); 448 SetLoaderBuffer(10, 20); 449 loader_->UpdateDeferStrategy(BufferedResourceLoader::kReadThenDefer); 450 Start(); 451 PartialResponse(10, 99, 100); 452 453 uint8 buffer[10]; 454 455 // Make an outstanding read request. 456 // We should disable deferring after the read request, so expect 457 // a network event. 458 EXPECT_CALL(*this, NetworkCallback()); 459 ReadLoader(10, 10, buffer); 460 461 // Receive almost enough data to cover, shouldn't defer. 462 WriteLoader(10, 9); 463 ConfirmLoaderDeferredState(false); 464 465 // As soon as we have received enough data to fulfill the read, defer. 466 EXPECT_CALL(*this, NetworkCallback()); 467 EXPECT_CALL(*this, ReadCallback(10)); 468 WriteLoader(19, 1); 469 470 ConfirmLoaderDeferredState(true); 471 VerifyBuffer(buffer, 10, 10); 472 473 StopWhenLoad(); 474 } 475 476 // Tests the data buffering logic of ThresholdDefer strategy. 477 TEST_F(BufferedResourceLoaderTest, ThresholdDeferStrategy) { 478 Initialize(kHttpUrl, 10, 99); 479 SetLoaderBuffer(10, 20); 480 loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer); 481 Start(); 482 PartialResponse(10, 99, 100); 483 484 uint8 buffer[10]; 485 486 WriteLoader(10, 5); 487 // Haven't reached threshold, don't defer. 488 ConfirmLoaderDeferredState(false); 489 490 // We're at the threshold now, let's defer. 491 EXPECT_CALL(*this, NetworkCallback()); 492 WriteLoader(15, 5); 493 ConfirmLoaderDeferredState(true); 494 495 // Now we've read over half of the buffer, disable deferring. 496 EXPECT_CALL(*this, ReadCallback(6)); 497 EXPECT_CALL(*this, NetworkCallback()); 498 ReadLoader(10, 6, buffer); 499 500 ConfirmLoaderDeferredState(false); 501 VerifyBuffer(buffer, 10, 6); 502 503 StopWhenLoad(); 504 } 505 506 // NOTE: This test will need to be reworked a little once 507 // http://code.google.com/p/chromium/issues/detail?id=72578 508 // is fixed. 509 TEST_F(BufferedResourceLoaderTest, HasSingleOrigin) { 510 // Make sure no redirect case works as expected. 511 Initialize(kHttpUrl, -1, -1); 512 Start(); 513 FullResponse(1024); 514 EXPECT_TRUE(loader_->HasSingleOrigin()); 515 StopWhenLoad(); 516 517 // Test redirect to the same domain. 518 Initialize(kHttpUrl, -1, -1); 519 Start(); 520 Redirect(kHttpRedirectToSameDomainUrl1); 521 FullResponse(1024); 522 EXPECT_TRUE(loader_->HasSingleOrigin()); 523 StopWhenLoad(); 524 525 // Test redirect twice to the same domain. 526 Initialize(kHttpUrl, -1, -1); 527 Start(); 528 Redirect(kHttpRedirectToSameDomainUrl1); 529 Redirect(kHttpRedirectToSameDomainUrl2); 530 FullResponse(1024); 531 EXPECT_TRUE(loader_->HasSingleOrigin()); 532 StopWhenLoad(); 533 534 // Test redirect to a different domain. 535 Initialize(kHttpUrl, -1, -1); 536 Start(); 537 Redirect(kHttpRedirectToDifferentDomainUrl1); 538 EXPECT_FALSE(loader_->HasSingleOrigin()); 539 StopWhenLoad(); 540 541 // Test redirect to the same domain and then to a different domain. 542 Initialize(kHttpUrl, -1, -1); 543 Start(); 544 Redirect(kHttpRedirectToSameDomainUrl1); 545 Redirect(kHttpRedirectToDifferentDomainUrl1); 546 EXPECT_FALSE(loader_->HasSingleOrigin()); 547 StopWhenLoad(); 548 } 549 550 // TODO(hclam): add unit test for defer loading. 551 552 } // namespace webkit_glue 553