Home | History | Annotate | Download | only in url_request
      1 // Copyright 2014 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 "net/url_request/url_request_file_job.h"
      6 
      7 #include "base/files/file_util.h"
      8 #include "base/files/scoped_temp_dir.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/run_loop.h"
     11 #include "base/strings/stringprintf.h"
     12 #include "base/threading/sequenced_worker_pool.h"
     13 #include "net/base/filename_util.h"
     14 #include "net/base/net_util.h"
     15 #include "net/url_request/url_request.h"
     16 #include "net/url_request/url_request_test_util.h"
     17 #include "testing/gtest/include/gtest/gtest.h"
     18 
     19 namespace net {
     20 
     21 namespace {
     22 
     23 // A URLRequestFileJob for testing OnSeekComplete / OnReadComplete callbacks.
     24 class URLRequestFileJobWithCallbacks : public URLRequestFileJob {
     25  public:
     26   URLRequestFileJobWithCallbacks(
     27       URLRequest* request,
     28       NetworkDelegate* network_delegate,
     29       const base::FilePath& file_path,
     30       const scoped_refptr<base::TaskRunner>& file_task_runner)
     31       : URLRequestFileJob(request,
     32                           network_delegate,
     33                           file_path,
     34                           file_task_runner),
     35         seek_position_(0) {
     36   }
     37 
     38   int64 seek_position() { return seek_position_; }
     39   const std::vector<std::string>& data_chunks() { return data_chunks_; }
     40 
     41  protected:
     42   virtual ~URLRequestFileJobWithCallbacks() {}
     43 
     44   virtual void OnSeekComplete(int64 result) OVERRIDE {
     45     ASSERT_EQ(seek_position_, 0);
     46     seek_position_ = result;
     47   }
     48 
     49   virtual void OnReadComplete(IOBuffer* buf, int result) OVERRIDE {
     50     data_chunks_.push_back(std::string(buf->data(), result));
     51   }
     52 
     53   int64 seek_position_;
     54   std::vector<std::string> data_chunks_;
     55 };
     56 
     57 // A URLRequestJobFactory that will return URLRequestFileJobWithCallbacks
     58 // instances for file:// scheme URLs.
     59 class CallbacksJobFactory : public URLRequestJobFactory {
     60  public:
     61   class JobObserver {
     62    public:
     63     virtual void OnJobCreated(URLRequestFileJobWithCallbacks* job) = 0;
     64   };
     65 
     66   CallbacksJobFactory(const base::FilePath& path, JobObserver* observer)
     67       : path_(path), observer_(observer) {
     68   }
     69 
     70   virtual ~CallbacksJobFactory() {}
     71 
     72   virtual URLRequestJob* MaybeCreateJobWithProtocolHandler(
     73       const std::string& scheme,
     74       URLRequest* request,
     75       NetworkDelegate* network_delegate) const OVERRIDE {
     76     URLRequestFileJobWithCallbacks* job = new URLRequestFileJobWithCallbacks(
     77         request,
     78         network_delegate,
     79         path_,
     80         base::MessageLoop::current()->message_loop_proxy());
     81     observer_->OnJobCreated(job);
     82     return job;
     83   }
     84 
     85   virtual bool IsHandledProtocol(const std::string& scheme) const OVERRIDE {
     86     return scheme == "file";
     87   }
     88 
     89   virtual bool IsHandledURL(const GURL& url) const OVERRIDE {
     90     return IsHandledProtocol(url.scheme());
     91   }
     92 
     93   virtual bool IsSafeRedirectTarget(const GURL& location) const OVERRIDE {
     94     return false;
     95   }
     96 
     97  private:
     98   base::FilePath path_;
     99   JobObserver* observer_;
    100 };
    101 
    102 // Helper function to create a file in |directory| filled with
    103 // |content|. Returns true on succes and fills in |path| with the full path to
    104 // the file.
    105 bool CreateTempFileWithContent(const std::string& content,
    106                                const base::ScopedTempDir& directory,
    107                                base::FilePath* path) {
    108   if (!directory.IsValid())
    109     return false;
    110 
    111   if (!base::CreateTemporaryFileInDir(directory.path(), path))
    112     return false;
    113 
    114   return base::WriteFile(*path, content.c_str(), content.length());
    115 }
    116 
    117 class JobObserverImpl : public CallbacksJobFactory::JobObserver {
    118  public:
    119   virtual void OnJobCreated(URLRequestFileJobWithCallbacks* job) OVERRIDE {
    120     jobs_.push_back(job);
    121   }
    122 
    123   typedef std::vector<scoped_refptr<URLRequestFileJobWithCallbacks> > JobList;
    124 
    125   const JobList& jobs() { return jobs_; }
    126 
    127  protected:
    128   JobList jobs_;
    129 };
    130 
    131 // A simple holder for start/end used in http range requests.
    132 struct Range {
    133   int start;
    134   int end;
    135 
    136   Range() {
    137     start = 0;
    138     end = 0;
    139   }
    140 
    141   Range(int start, int end) {
    142     this->start = start;
    143     this->end = end;
    144   }
    145 };
    146 
    147 // A superclass for tests of the OnSeekComplete / OnReadComplete functions of
    148 // URLRequestFileJob.
    149 class URLRequestFileJobEventsTest : public testing::Test {
    150  public:
    151   URLRequestFileJobEventsTest();
    152 
    153  protected:
    154   // This creates a file with |content| as the contents, and then creates and
    155   // runs a URLRequestFileJobWithCallbacks job to get the contents out of it,
    156   // and makes sure that the callbacks observed the correct bytes. If a Range
    157   // is provided, this function will add the appropriate Range http header to
    158   // the request and verify that only the bytes in that range (inclusive) were
    159   // observed.
    160   void RunRequest(const std::string& content, const Range* range);
    161 
    162   JobObserverImpl observer_;
    163   TestURLRequestContext context_;
    164   TestDelegate delegate_;
    165 };
    166 
    167 URLRequestFileJobEventsTest::URLRequestFileJobEventsTest() {}
    168 
    169 void URLRequestFileJobEventsTest::RunRequest(const std::string& content,
    170                                              const Range* range) {
    171   base::ScopedTempDir directory;
    172   ASSERT_TRUE(directory.CreateUniqueTempDir());
    173   base::FilePath path;
    174   ASSERT_TRUE(CreateTempFileWithContent(content, directory, &path));
    175   CallbacksJobFactory factory(path, &observer_);
    176   context_.set_job_factory(&factory);
    177 
    178   scoped_ptr<URLRequest> request(context_.CreateRequest(
    179       FilePathToFileURL(path), DEFAULT_PRIORITY, &delegate_, NULL));
    180   if (range) {
    181     ASSERT_GE(range->start, 0);
    182     ASSERT_GE(range->end, 0);
    183     ASSERT_LE(range->start, range->end);
    184     ASSERT_LT(static_cast<unsigned int>(range->end), content.length());
    185     std::string range_value =
    186         base::StringPrintf("bytes=%d-%d", range->start, range->end);
    187     request->SetExtraRequestHeaderByName(
    188         HttpRequestHeaders::kRange, range_value, true /*overwrite*/);
    189   }
    190   request->Start();
    191 
    192   base::RunLoop loop;
    193   loop.Run();
    194 
    195   EXPECT_FALSE(delegate_.request_failed());
    196   int expected_length =
    197       range ? (range->end - range->start + 1) : content.length();
    198   EXPECT_EQ(delegate_.bytes_received(), expected_length);
    199 
    200   std::string expected_content;
    201   if (range) {
    202     expected_content.insert(0, content, range->start, expected_length);
    203   } else {
    204     expected_content = content;
    205   }
    206   EXPECT_TRUE(delegate_.data_received() == expected_content);
    207 
    208   ASSERT_EQ(observer_.jobs().size(), 1u);
    209   ASSERT_EQ(observer_.jobs().at(0)->seek_position(), range ? range->start : 0);
    210 
    211   std::string observed_content;
    212   const std::vector<std::string>& chunks =
    213       observer_.jobs().at(0)->data_chunks();
    214   for (std::vector<std::string>::const_iterator i = chunks.begin();
    215        i != chunks.end();
    216        ++i) {
    217     observed_content.append(*i);
    218   }
    219   EXPECT_EQ(expected_content, observed_content);
    220 }
    221 
    222 // Helper function to make a character array filled with |size| bytes of
    223 // test content.
    224 std::string MakeContentOfSize(int size) {
    225   EXPECT_GE(size, 0);
    226   std::string result;
    227   result.reserve(size);
    228   for (int i = 0; i < size; i++) {
    229     result.append(1, static_cast<char>(i % 256));
    230   }
    231   return result;
    232 }
    233 
    234 TEST_F(URLRequestFileJobEventsTest, TinyFile) {
    235   RunRequest(std::string("hello world"), NULL);
    236 }
    237 
    238 TEST_F(URLRequestFileJobEventsTest, SmallFile) {
    239   RunRequest(MakeContentOfSize(17 * 1024), NULL);
    240 }
    241 
    242 TEST_F(URLRequestFileJobEventsTest, BigFile) {
    243   RunRequest(MakeContentOfSize(3 * 1024 * 1024), NULL);
    244 }
    245 
    246 TEST_F(URLRequestFileJobEventsTest, Range) {
    247   // Use a 15KB content file and read a range chosen somewhat arbitrarily but
    248   // not aligned on any likely page boundaries.
    249   int size = 15 * 1024;
    250   Range range(1701, (6 * 1024) + 3);
    251   RunRequest(MakeContentOfSize(size), &range);
    252 }
    253 
    254 }  // namespace
    255 
    256 }  // namespace net
    257