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