1 // Copyright 2013 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 "base/bind.h" 6 #include "base/file_util.h" 7 #include "base/files/file_path.h" 8 #include "base/files/scoped_temp_dir.h" 9 #include "base/memory/ref_counted.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/run_loop.h" 12 #include "base/time/time.h" 13 #include "content/public/test/test_file_system_context.h" 14 #include "net/base/io_buffer.h" 15 #include "net/base/request_priority.h" 16 #include "net/http/http_byte_range.h" 17 #include "net/http/http_request_headers.h" 18 #include "net/http/http_response_headers.h" 19 #include "net/url_request/url_request.h" 20 #include "net/url_request/url_request_context.h" 21 #include "net/url_request/url_request_job_factory_impl.h" 22 #include "testing/gtest/include/gtest/gtest.h" 23 #include "webkit/browser/blob/blob_url_request_job.h" 24 #include "webkit/browser/fileapi/async_file_test_helper.h" 25 #include "webkit/browser/fileapi/file_system_context.h" 26 #include "webkit/browser/fileapi/file_system_operation_context.h" 27 #include "webkit/browser/fileapi/file_system_url.h" 28 #include "webkit/common/blob/blob_data.h" 29 30 namespace webkit_blob { 31 32 namespace { 33 34 const int kBufferSize = 1024; 35 const char kTestData1[] = "Hello"; 36 const char kTestData2[] = "Here it is data."; 37 const char kTestFileData1[] = "0123456789"; 38 const char kTestFileData2[] = "This is sample file."; 39 const char kTestFileSystemFileData1[] = "abcdefghijklmnop"; 40 const char kTestFileSystemFileData2[] = "File system file test data."; 41 const char kTestContentType[] = "foo/bar"; 42 const char kTestContentDisposition[] = "attachment; filename=foo.txt"; 43 44 const char kFileSystemURLOrigin[] = "http://remote"; 45 const fileapi::FileSystemType kFileSystemType = 46 fileapi::kFileSystemTypeTemporary; 47 48 } // namespace 49 50 class BlobURLRequestJobTest : public testing::Test { 51 public: 52 53 // Test Harness ------------------------------------------------------------- 54 // TODO(jianli): share this test harness with AppCacheURLRequestJobTest 55 56 class MockURLRequestDelegate : public net::URLRequest::Delegate { 57 public: 58 MockURLRequestDelegate() 59 : received_data_(new net::IOBuffer(kBufferSize)) {} 60 61 virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE { 62 if (request->status().is_success()) { 63 EXPECT_TRUE(request->response_headers()); 64 ReadSome(request); 65 } else { 66 RequestComplete(); 67 } 68 } 69 70 virtual void OnReadCompleted(net::URLRequest* request, 71 int bytes_read) OVERRIDE { 72 if (bytes_read > 0) 73 ReceiveData(request, bytes_read); 74 else 75 RequestComplete(); 76 } 77 78 const std::string& response_data() const { return response_data_; } 79 80 private: 81 void ReadSome(net::URLRequest* request) { 82 if (!request->is_pending()) { 83 RequestComplete(); 84 return; 85 } 86 87 int bytes_read = 0; 88 if (!request->Read(received_data_.get(), kBufferSize, &bytes_read)) { 89 if (!request->status().is_io_pending()) { 90 RequestComplete(); 91 } 92 return; 93 } 94 95 ReceiveData(request, bytes_read); 96 } 97 98 void ReceiveData(net::URLRequest* request, int bytes_read) { 99 if (bytes_read) { 100 response_data_.append(received_data_->data(), 101 static_cast<size_t>(bytes_read)); 102 ReadSome(request); 103 } else { 104 RequestComplete(); 105 } 106 } 107 108 void RequestComplete() { 109 base::MessageLoop::current()->Quit(); 110 } 111 112 scoped_refptr<net::IOBuffer> received_data_; 113 std::string response_data_; 114 }; 115 116 // A simple ProtocolHandler implementation to create BlobURLRequestJob. 117 class MockProtocolHandler : 118 public net::URLRequestJobFactory::ProtocolHandler { 119 public: 120 MockProtocolHandler(BlobURLRequestJobTest* test) : test_(test) {} 121 122 // net::URLRequestJobFactory::ProtocolHandler override. 123 virtual net::URLRequestJob* MaybeCreateJob( 124 net::URLRequest* request, 125 net::NetworkDelegate* network_delegate) const OVERRIDE { 126 return new BlobURLRequestJob(request, 127 network_delegate, 128 test_->blob_data_.get(), 129 test_->file_system_context_.get(), 130 base::MessageLoopProxy::current().get()); 131 } 132 133 private: 134 BlobURLRequestJobTest* test_; 135 }; 136 137 BlobURLRequestJobTest() 138 : blob_data_(new BlobData()), 139 expected_status_code_(0) {} 140 141 virtual void SetUp() { 142 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 143 144 temp_file1_ = temp_dir_.path().AppendASCII("BlobFile1.dat"); 145 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData1) - 1), 146 file_util::WriteFile(temp_file1_, kTestFileData1, 147 arraysize(kTestFileData1) - 1)); 148 base::PlatformFileInfo file_info1; 149 base::GetFileInfo(temp_file1_, &file_info1); 150 temp_file_modification_time1_ = file_info1.last_modified; 151 152 temp_file2_ = temp_dir_.path().AppendASCII("BlobFile2.dat"); 153 ASSERT_EQ(static_cast<int>(arraysize(kTestFileData2) - 1), 154 file_util::WriteFile(temp_file2_, kTestFileData2, 155 arraysize(kTestFileData2) - 1)); 156 base::PlatformFileInfo file_info2; 157 base::GetFileInfo(temp_file2_, &file_info2); 158 temp_file_modification_time2_ = file_info2.last_modified; 159 160 url_request_job_factory_.SetProtocolHandler("blob", 161 new MockProtocolHandler(this)); 162 url_request_context_.set_job_factory(&url_request_job_factory_); 163 } 164 165 virtual void TearDown() { 166 } 167 168 void SetUpFileSystem() { 169 // Prepare file system. 170 file_system_context_ = fileapi::CreateFileSystemContextForTesting( 171 NULL, temp_dir_.path()); 172 173 file_system_context_->OpenFileSystem( 174 GURL(kFileSystemURLOrigin), 175 kFileSystemType, 176 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT, 177 base::Bind(&BlobURLRequestJobTest::OnValidateFileSystem, 178 base::Unretained(this))); 179 base::RunLoop().RunUntilIdle(); 180 ASSERT_TRUE(file_system_root_url_.is_valid()); 181 182 // Prepare files on file system. 183 const char kFilename1[] = "FileSystemFile1.dat"; 184 temp_file_system_file1_ = GetFileSystemURL(kFilename1); 185 WriteFileSystemFile(kFilename1, kTestFileSystemFileData1, 186 arraysize(kTestFileSystemFileData1) - 1, 187 &temp_file_system_file_modification_time1_); 188 const char kFilename2[] = "FileSystemFile2.dat"; 189 temp_file_system_file2_ = GetFileSystemURL(kFilename2); 190 WriteFileSystemFile(kFilename2, kTestFileSystemFileData2, 191 arraysize(kTestFileSystemFileData2) - 1, 192 &temp_file_system_file_modification_time2_); 193 } 194 195 GURL GetFileSystemURL(const std::string& filename) { 196 return GURL(file_system_root_url_.spec() + filename); 197 } 198 199 void WriteFileSystemFile(const std::string& filename, 200 const char* buf, int buf_size, 201 base::Time* modification_time) { 202 fileapi::FileSystemURL url = 203 file_system_context_->CreateCrackedFileSystemURL( 204 GURL(kFileSystemURLOrigin), 205 kFileSystemType, 206 base::FilePath().AppendASCII(filename)); 207 208 ASSERT_EQ(base::PLATFORM_FILE_OK, 209 fileapi::AsyncFileTestHelper::CreateFileWithData( 210 file_system_context_, url, buf, buf_size)); 211 212 base::PlatformFileInfo file_info; 213 ASSERT_EQ(base::PLATFORM_FILE_OK, 214 fileapi::AsyncFileTestHelper::GetMetadata( 215 file_system_context_, url, &file_info)); 216 if (modification_time) 217 *modification_time = file_info.last_modified; 218 } 219 220 void OnValidateFileSystem(const GURL& root, 221 const std::string& name, 222 base::PlatformFileError result) { 223 ASSERT_EQ(base::PLATFORM_FILE_OK, result); 224 ASSERT_TRUE(root.is_valid()); 225 file_system_root_url_ = root; 226 } 227 228 void TestSuccessRequest(const std::string& expected_response) { 229 expected_status_code_ = 200; 230 expected_response_ = expected_response; 231 TestRequest("GET", net::HttpRequestHeaders()); 232 } 233 234 void TestErrorRequest(int expected_status_code) { 235 expected_status_code_ = expected_status_code; 236 expected_response_ = ""; 237 TestRequest("GET", net::HttpRequestHeaders()); 238 } 239 240 void TestRequest(const std::string& method, 241 const net::HttpRequestHeaders& extra_headers) { 242 request_ = url_request_context_.CreateRequest( 243 GURL("blob:blah"), net::DEFAULT_PRIORITY, &url_request_delegate_); 244 request_->set_method(method); 245 if (!extra_headers.IsEmpty()) 246 request_->SetExtraRequestHeaders(extra_headers); 247 request_->Start(); 248 249 base::MessageLoop::current()->Run(); 250 251 // Verify response. 252 EXPECT_TRUE(request_->status().is_success()); 253 EXPECT_EQ(expected_status_code_, 254 request_->response_headers()->response_code()); 255 EXPECT_EQ(expected_response_, url_request_delegate_.response_data()); 256 } 257 258 void BuildComplicatedData(std::string* expected_result) { 259 blob_data_->AppendData(kTestData1 + 1, 2); 260 blob_data_->AppendFile(temp_file1_, 2, 3, temp_file_modification_time1_); 261 blob_data_->AppendFileSystemFile(temp_file_system_file1_, 3, 4, 262 temp_file_system_file_modification_time1_); 263 blob_data_->AppendData(kTestData2 + 4, 5); 264 blob_data_->AppendFile(temp_file2_, 5, 6, temp_file_modification_time2_); 265 blob_data_->AppendFileSystemFile(temp_file_system_file2_, 6, 7, 266 temp_file_system_file_modification_time2_); 267 *expected_result = std::string(kTestData1 + 1, 2); 268 *expected_result += std::string(kTestFileData1 + 2, 3); 269 *expected_result += std::string(kTestFileSystemFileData1 + 3, 4); 270 *expected_result += std::string(kTestData2 + 4, 5); 271 *expected_result += std::string(kTestFileData2 + 5, 6); 272 *expected_result += std::string(kTestFileSystemFileData2 + 6, 7); 273 } 274 275 protected: 276 base::ScopedTempDir temp_dir_; 277 base::FilePath temp_file1_; 278 base::FilePath temp_file2_; 279 base::Time temp_file_modification_time1_; 280 base::Time temp_file_modification_time2_; 281 GURL file_system_root_url_; 282 GURL temp_file_system_file1_; 283 GURL temp_file_system_file2_; 284 base::Time temp_file_system_file_modification_time1_; 285 base::Time temp_file_system_file_modification_time2_; 286 287 base::MessageLoopForIO message_loop_; 288 scoped_refptr<fileapi::FileSystemContext> file_system_context_; 289 scoped_refptr<BlobData> blob_data_; 290 net::URLRequestJobFactoryImpl url_request_job_factory_; 291 net::URLRequestContext url_request_context_; 292 MockURLRequestDelegate url_request_delegate_; 293 scoped_ptr<net::URLRequest> request_; 294 295 int expected_status_code_; 296 std::string expected_response_; 297 }; 298 299 TEST_F(BlobURLRequestJobTest, TestGetSimpleDataRequest) { 300 blob_data_->AppendData(kTestData1); 301 TestSuccessRequest(kTestData1); 302 } 303 304 TEST_F(BlobURLRequestJobTest, TestGetSimpleFileRequest) { 305 blob_data_->AppendFile(temp_file1_, 0, -1, base::Time()); 306 TestSuccessRequest(kTestFileData1); 307 } 308 309 TEST_F(BlobURLRequestJobTest, TestGetLargeFileRequest) { 310 base::FilePath large_temp_file = 311 temp_dir_.path().AppendASCII("LargeBlob.dat"); 312 std::string large_data; 313 large_data.reserve(kBufferSize * 5); 314 for (int i = 0; i < kBufferSize * 5; ++i) 315 large_data.append(1, static_cast<char>(i % 256)); 316 ASSERT_EQ(static_cast<int>(large_data.size()), 317 file_util::WriteFile(large_temp_file, large_data.data(), 318 large_data.size())); 319 blob_data_->AppendFile(large_temp_file, 0, -1, base::Time()); 320 TestSuccessRequest(large_data); 321 } 322 323 TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileRequest) { 324 base::FilePath non_existent_file = 325 temp_file1_.InsertBeforeExtension(FILE_PATH_LITERAL("-na")); 326 blob_data_->AppendFile(non_existent_file, 0, -1, base::Time()); 327 TestErrorRequest(404); 328 } 329 330 TEST_F(BlobURLRequestJobTest, TestGetChangedFileRequest) { 331 base::Time old_time = 332 temp_file_modification_time1_ - base::TimeDelta::FromSeconds(10); 333 blob_data_->AppendFile(temp_file1_, 0, 3, old_time); 334 TestErrorRequest(404); 335 } 336 337 TEST_F(BlobURLRequestJobTest, TestGetSlicedFileRequest) { 338 blob_data_->AppendFile(temp_file1_, 2, 4, temp_file_modification_time1_); 339 std::string result(kTestFileData1 + 2, 4); 340 TestSuccessRequest(result); 341 } 342 343 TEST_F(BlobURLRequestJobTest, TestGetSimpleFileSystemFileRequest) { 344 SetUpFileSystem(); 345 blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0, -1, 346 base::Time()); 347 TestSuccessRequest(kTestFileSystemFileData1); 348 } 349 350 TEST_F(BlobURLRequestJobTest, TestGetLargeFileSystemFileRequest) { 351 SetUpFileSystem(); 352 std::string large_data; 353 large_data.reserve(kBufferSize * 5); 354 for (int i = 0; i < kBufferSize * 5; ++i) 355 large_data.append(1, static_cast<char>(i % 256)); 356 357 const char kFilename[] = "LargeBlob.dat"; 358 WriteFileSystemFile(kFilename, large_data.data(), large_data.size(), NULL); 359 360 blob_data_->AppendFileSystemFile(GetFileSystemURL(kFilename), 361 0, -1, base::Time()); 362 TestSuccessRequest(large_data); 363 } 364 365 TEST_F(BlobURLRequestJobTest, TestGetNonExistentFileSystemFileRequest) { 366 SetUpFileSystem(); 367 GURL non_existent_file = GetFileSystemURL("non-existent.dat"); 368 blob_data_->AppendFileSystemFile(non_existent_file, 0, -1, base::Time()); 369 TestErrorRequest(404); 370 } 371 372 TEST_F(BlobURLRequestJobTest, TestGetChangedFileSystemFileRequest) { 373 SetUpFileSystem(); 374 base::Time old_time = 375 temp_file_system_file_modification_time1_ - 376 base::TimeDelta::FromSeconds(10); 377 blob_data_->AppendFileSystemFile(temp_file_system_file1_, 0, 3, old_time); 378 TestErrorRequest(404); 379 } 380 381 TEST_F(BlobURLRequestJobTest, TestGetSlicedFileSystemFileRequest) { 382 SetUpFileSystem(); 383 blob_data_->AppendFileSystemFile(temp_file_system_file1_, 2, 4, 384 temp_file_system_file_modification_time1_); 385 std::string result(kTestFileSystemFileData1 + 2, 4); 386 TestSuccessRequest(result); 387 } 388 389 TEST_F(BlobURLRequestJobTest, TestGetComplicatedDataAndFileRequest) { 390 SetUpFileSystem(); 391 std::string result; 392 BuildComplicatedData(&result); 393 TestSuccessRequest(result); 394 } 395 396 TEST_F(BlobURLRequestJobTest, TestGetRangeRequest1) { 397 SetUpFileSystem(); 398 std::string result; 399 BuildComplicatedData(&result); 400 net::HttpRequestHeaders extra_headers; 401 extra_headers.SetHeader(net::HttpRequestHeaders::kRange, 402 net::HttpByteRange::Bounded(5, 10).GetHeaderValue()); 403 expected_status_code_ = 206; 404 expected_response_ = result.substr(5, 10 - 5 + 1); 405 TestRequest("GET", extra_headers); 406 } 407 408 TEST_F(BlobURLRequestJobTest, TestGetRangeRequest2) { 409 SetUpFileSystem(); 410 std::string result; 411 BuildComplicatedData(&result); 412 net::HttpRequestHeaders extra_headers; 413 extra_headers.SetHeader(net::HttpRequestHeaders::kRange, 414 net::HttpByteRange::Suffix(10).GetHeaderValue()); 415 expected_status_code_ = 206; 416 expected_response_ = result.substr(result.length() - 10); 417 TestRequest("GET", extra_headers); 418 } 419 420 TEST_F(BlobURLRequestJobTest, TestExtraHeaders) { 421 blob_data_->set_content_type(kTestContentType); 422 blob_data_->set_content_disposition(kTestContentDisposition); 423 blob_data_->AppendData(kTestData1); 424 expected_status_code_ = 200; 425 expected_response_ = kTestData1; 426 TestRequest("GET", net::HttpRequestHeaders()); 427 428 std::string content_type; 429 EXPECT_TRUE(request_->response_headers()->GetMimeType(&content_type)); 430 EXPECT_EQ(kTestContentType, content_type); 431 void* iter = NULL; 432 std::string content_disposition; 433 EXPECT_TRUE(request_->response_headers()->EnumerateHeader( 434 &iter, "Content-Disposition", &content_disposition)); 435 EXPECT_EQ(kTestContentDisposition, content_disposition); 436 } 437 438 } // namespace webkit_blob 439