• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
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/test/url_request/url_request_test_job_backed_by_file.h"
6 #include "base/memory/raw_ptr.h"
7 
8 #include <memory>
9 
10 #include "base/check.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/run_loop.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "net/base/filename_util.h"
18 #include "net/base/net_errors.h"
19 #include "net/test/test_with_task_environment.h"
20 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
21 #include "net/url_request/url_request.h"
22 #include "net/url_request/url_request_context.h"
23 #include "net/url_request/url_request_context_builder.h"
24 #include "net/url_request/url_request_test_util.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "url/url_constants.h"
27 
28 namespace net {
29 
30 namespace {
31 
32 // A URLRequestTestJobBackedByFile for testing values passed to OnSeekComplete
33 // and OnReadComplete.
34 class TestURLRequestTestJobBackedByFile : public URLRequestTestJobBackedByFile {
35  public:
36   // |seek_position| will be set to the value passed in to OnSeekComplete.
37   // |observed_content| will be set to the concatenated data from all calls to
38   // OnReadComplete.
TestURLRequestTestJobBackedByFile(URLRequest * request,const base::FilePath & file_path,const scoped_refptr<base::TaskRunner> & file_task_runner,int * open_result,int64_t * seek_position,bool * done_reading,std::string * observed_content)39   TestURLRequestTestJobBackedByFile(
40       URLRequest* request,
41       const base::FilePath& file_path,
42       const scoped_refptr<base::TaskRunner>& file_task_runner,
43       int* open_result,
44       int64_t* seek_position,
45       bool* done_reading,
46       std::string* observed_content)
47       : URLRequestTestJobBackedByFile(request,
48                                       file_path,
49                                       file_task_runner),
50         open_result_(open_result),
51         seek_position_(seek_position),
52         done_reading_(done_reading),
53         observed_content_(observed_content) {
54     *open_result_ = ERR_IO_PENDING;
55     *seek_position_ = ERR_IO_PENDING;
56     *done_reading_ = false;
57     observed_content_->clear();
58   }
59 
60   ~TestURLRequestTestJobBackedByFile() override = default;
61 
62  protected:
OnOpenComplete(int result)63   void OnOpenComplete(int result) override {
64     // Should only be called once.
65     ASSERT_EQ(ERR_IO_PENDING, *open_result_);
66     *open_result_ = result;
67   }
68 
OnSeekComplete(int64_t result)69   void OnSeekComplete(int64_t result) override {
70     // Should only call this if open succeeded.
71     EXPECT_EQ(OK, *open_result_);
72     // Should only be called once.
73     ASSERT_EQ(ERR_IO_PENDING, *seek_position_);
74     *seek_position_ = result;
75   }
76 
OnReadComplete(IOBuffer * buf,int result)77   void OnReadComplete(IOBuffer* buf, int result) override {
78     // Should only call this if seek succeeded.
79     EXPECT_GE(*seek_position_, 0);
80     observed_content_->append(std::string(buf->data(), result));
81   }
82 
DoneReading()83   void DoneReading() override { *done_reading_ = true; }
84 
85   const raw_ptr<int> open_result_;
86   const raw_ptr<int64_t> seek_position_;
87   raw_ptr<bool> done_reading_;
88   const raw_ptr<std::string> observed_content_;
89 };
90 
91 // A simple holder for start/end used in http range requests.
92 struct Range {
93   int start;
94   int end;
95 
Rangenet::__anon326743380111::Range96   Range() {
97     start = 0;
98     end = 0;
99   }
100 
Rangenet::__anon326743380111::Range101   Range(int start, int end) {
102     this->start = start;
103     this->end = end;
104   }
105 };
106 
107 // A superclass for tests of the OnReadComplete / OnSeekComplete /
108 // OnReadComplete functions of URLRequestTestJobBackedByFile.
109 class URLRequestTestJobBackedByFileEventsTest : public TestWithTaskEnvironment {
110  public:
111   URLRequestTestJobBackedByFileEventsTest();
112 
113  protected:
114   void TearDown() override;
115 
116   // This creates a file with |content| as the contents, and then creates and
117   // runs a TestURLRequestTestJobBackedByFile job to get the contents out of it,
118   // and makes sure that the callbacks observed the correct bytes. If a Range
119   // is provided, this function will add the appropriate Range http header to
120   // the request and verify that only the bytes in that range (inclusive) were
121   // observed.
122   void RunSuccessfulRequestWithString(const std::string& content,
123                                       const Range* range);
124 
125   // This is the same as the method above it, except that it will make sure
126   // the content matches |expected_content| and allow caller to specify the
127   // extension of the filename in |file_extension|.
128   void RunSuccessfulRequestWithString(
129       const std::string& content,
130       const std::string& expected_content,
131       const base::FilePath::StringPieceType& file_extension,
132       const Range* range);
133 
134   // Creates and runs a TestURLRequestTestJobBackedByFile job to read from file
135   // provided by |path|. If |range| value is provided, it will be passed in the
136   // range header.
137   void RunRequestWithPath(const base::FilePath& path,
138                           const std::string& range,
139                           int* open_result,
140                           int64_t* seek_position,
141                           bool* done_reading,
142                           std::string* observed_content);
143 
144   base::ScopedTempDir directory_;
145   std::unique_ptr<URLRequestContext> context_;
146   TestDelegate delegate_;
147 };
148 
149 URLRequestTestJobBackedByFileEventsTest::
URLRequestTestJobBackedByFileEventsTest()150     URLRequestTestJobBackedByFileEventsTest()
151     : context_(CreateTestURLRequestContextBuilder()->Build()) {}
152 
TearDown()153 void URLRequestTestJobBackedByFileEventsTest::TearDown() {
154   // Gives a chance to close the opening file.
155   base::RunLoop().RunUntilIdle();
156   ASSERT_TRUE(!directory_.IsValid() || directory_.Delete());
157   TestWithTaskEnvironment::TearDown();
158 }
159 
RunSuccessfulRequestWithString(const std::string & content,const Range * range)160 void URLRequestTestJobBackedByFileEventsTest::RunSuccessfulRequestWithString(
161     const std::string& content,
162     const Range* range) {
163   RunSuccessfulRequestWithString(content, content, FILE_PATH_LITERAL(""),
164                                  range);
165 }
166 
RunSuccessfulRequestWithString(const std::string & raw_content,const std::string & expected_content,const base::FilePath::StringPieceType & file_extension,const Range * range)167 void URLRequestTestJobBackedByFileEventsTest::RunSuccessfulRequestWithString(
168     const std::string& raw_content,
169     const std::string& expected_content,
170     const base::FilePath::StringPieceType& file_extension,
171     const Range* range) {
172   ASSERT_TRUE(directory_.CreateUniqueTempDir());
173   base::FilePath path = directory_.GetPath().Append(FILE_PATH_LITERAL("test"));
174   if (!file_extension.empty())
175     path = path.AddExtension(file_extension);
176   ASSERT_TRUE(base::WriteFile(path, raw_content));
177 
178   std::string range_value;
179   if (range) {
180     ASSERT_GE(range->start, 0);
181     ASSERT_GE(range->end, 0);
182     ASSERT_LE(range->start, range->end);
183     ASSERT_LT(static_cast<unsigned int>(range->end), expected_content.length());
184     range_value = base::StringPrintf("bytes=%d-%d", range->start, range->end);
185   }
186 
187   {
188     int open_result;
189     int64_t seek_position;
190     bool done_reading;
191     std::string observed_content;
192     RunRequestWithPath(path, range_value, &open_result, &seek_position,
193                        &done_reading, &observed_content);
194 
195     EXPECT_EQ(OK, open_result);
196     EXPECT_FALSE(delegate_.request_failed());
197     int expected_length =
198         range ? (range->end - range->start + 1) : expected_content.length();
199     EXPECT_EQ(delegate_.bytes_received(), expected_length);
200 
201     std::string expected_data_received;
202     if (range) {
203       expected_data_received.insert(0, expected_content, range->start,
204                                     expected_length);
205       EXPECT_EQ(expected_data_received, observed_content);
206     } else {
207       expected_data_received = expected_content;
208       EXPECT_EQ(raw_content, observed_content);
209     }
210 
211     EXPECT_EQ(expected_data_received, delegate_.data_received());
212     EXPECT_EQ(seek_position, range ? range->start : 0);
213     EXPECT_TRUE(done_reading);
214   }
215 }
216 
RunRequestWithPath(const base::FilePath & path,const std::string & range,int * open_result,int64_t * seek_position,bool * done_reading,std::string * observed_content)217 void URLRequestTestJobBackedByFileEventsTest::RunRequestWithPath(
218     const base::FilePath& path,
219     const std::string& range,
220     int* open_result,
221     int64_t* seek_position,
222     bool* done_reading,
223     std::string* observed_content) {
224   const GURL kUrl("http://intercepted-url/");
225 
226   std::unique_ptr<URLRequest> request(context_->CreateRequest(
227       kUrl, DEFAULT_PRIORITY, &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
228   TestScopedURLInterceptor interceptor(
229       kUrl, std::make_unique<TestURLRequestTestJobBackedByFile>(
230                 request.get(), path,
231                 base::SingleThreadTaskRunner::GetCurrentDefault(), open_result,
232                 seek_position, done_reading, observed_content));
233   if (!range.empty()) {
234     request->SetExtraRequestHeaderByName(HttpRequestHeaders::kRange, range,
235                                          true /*overwrite*/);
236   }
237   request->Start();
238 
239   base::RunLoop().Run();
240 }
241 
242 // Helper function to make a character array filled with |size| bytes of
243 // test content.
MakeContentOfSize(int size)244 std::string MakeContentOfSize(int size) {
245   EXPECT_GE(size, 0);
246   std::string result;
247   result.reserve(size);
248   for (int i = 0; i < size; i++) {
249     result.append(1, static_cast<char>(i % 256));
250   }
251   return result;
252 }
253 
TEST_F(URLRequestTestJobBackedByFileEventsTest,ZeroByteFile)254 TEST_F(URLRequestTestJobBackedByFileEventsTest, ZeroByteFile) {
255   RunSuccessfulRequestWithString(std::string(""), nullptr);
256 }
257 
TEST_F(URLRequestTestJobBackedByFileEventsTest,TinyFile)258 TEST_F(URLRequestTestJobBackedByFileEventsTest, TinyFile) {
259   RunSuccessfulRequestWithString(std::string("hello world"), nullptr);
260 }
261 
TEST_F(URLRequestTestJobBackedByFileEventsTest,SmallFile)262 TEST_F(URLRequestTestJobBackedByFileEventsTest, SmallFile) {
263   RunSuccessfulRequestWithString(MakeContentOfSize(17 * 1024), nullptr);
264 }
265 
TEST_F(URLRequestTestJobBackedByFileEventsTest,BigFile)266 TEST_F(URLRequestTestJobBackedByFileEventsTest, BigFile) {
267   RunSuccessfulRequestWithString(MakeContentOfSize(3 * 1024 * 1024), nullptr);
268 }
269 
TEST_F(URLRequestTestJobBackedByFileEventsTest,Range)270 TEST_F(URLRequestTestJobBackedByFileEventsTest, Range) {
271   // Use a 15KB content file and read a range chosen somewhat arbitrarily but
272   // not aligned on any likely page boundaries.
273   int size = 15 * 1024;
274   Range range(1701, (6 * 1024) + 3);
275   RunSuccessfulRequestWithString(MakeContentOfSize(size), &range);
276 }
277 
TEST_F(URLRequestTestJobBackedByFileEventsTest,DecodeSvgzFile)278 TEST_F(URLRequestTestJobBackedByFileEventsTest, DecodeSvgzFile) {
279   std::string expected_content("Hello, World!");
280   unsigned char gzip_data[] = {
281       // From:
282       //   echo -n 'Hello, World!' | gzip | xxd -i | sed -e 's/^/  /'
283       0x1f, 0x8b, 0x08, 0x00, 0x2b, 0x02, 0x84, 0x55, 0x00, 0x03, 0xf3,
284       0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x08, 0xcf, 0x2f, 0xca, 0x49,
285       0x51, 0x04, 0x00, 0xd0, 0xc3, 0x4a, 0xec, 0x0d, 0x00, 0x00, 0x00};
286   RunSuccessfulRequestWithString(
287       std::string(reinterpret_cast<char*>(gzip_data), sizeof(gzip_data)),
288       expected_content, FILE_PATH_LITERAL("svgz"), nullptr);
289 }
290 
TEST_F(URLRequestTestJobBackedByFileEventsTest,OpenNonExistentFile)291 TEST_F(URLRequestTestJobBackedByFileEventsTest, OpenNonExistentFile) {
292   base::FilePath path;
293   base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
294   path = path.Append(
295       FILE_PATH_LITERAL("net/data/url_request_unittest/non-existent.txt"));
296 
297   int open_result;
298   int64_t seek_position;
299   bool done_reading;
300   std::string observed_content;
301   RunRequestWithPath(path, std::string(), &open_result, &seek_position,
302                      &done_reading, &observed_content);
303 
304   EXPECT_EQ(ERR_FILE_NOT_FOUND, open_result);
305   EXPECT_FALSE(done_reading);
306   EXPECT_TRUE(delegate_.request_failed());
307 }
308 
TEST_F(URLRequestTestJobBackedByFileEventsTest,MultiRangeRequestNotSupported)309 TEST_F(URLRequestTestJobBackedByFileEventsTest, MultiRangeRequestNotSupported) {
310   base::FilePath path;
311   base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
312   path = path.Append(
313       FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt"));
314 
315   int open_result;
316   int64_t seek_position;
317   bool done_reading;
318   std::string observed_content;
319   RunRequestWithPath(path, "bytes=1-5,20-30", &open_result, &seek_position,
320                      &done_reading, &observed_content);
321 
322   EXPECT_EQ(OK, open_result);
323   EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, seek_position);
324   EXPECT_FALSE(done_reading);
325   EXPECT_TRUE(delegate_.request_failed());
326 }
327 
TEST_F(URLRequestTestJobBackedByFileEventsTest,RangeExceedingFileSize)328 TEST_F(URLRequestTestJobBackedByFileEventsTest, RangeExceedingFileSize) {
329   base::FilePath path;
330   base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
331   path = path.Append(
332       FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt"));
333 
334   int open_result;
335   int64_t seek_position;
336   bool done_reading;
337   std::string observed_content;
338   RunRequestWithPath(path, "bytes=50000-", &open_result, &seek_position,
339                      &done_reading, &observed_content);
340 
341   EXPECT_EQ(OK, open_result);
342   EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, seek_position);
343   EXPECT_FALSE(done_reading);
344   EXPECT_TRUE(delegate_.request_failed());
345 }
346 
TEST_F(URLRequestTestJobBackedByFileEventsTest,IgnoreRangeParsingError)347 TEST_F(URLRequestTestJobBackedByFileEventsTest, IgnoreRangeParsingError) {
348   base::FilePath path;
349   base::PathService::Get(base::DIR_SOURCE_ROOT, &path);
350   path = path.Append(
351       FILE_PATH_LITERAL("net/data/url_request_unittest/simple.html"));
352 
353   int open_result;
354   int64_t seek_position;
355   bool done_reading;
356   std::string observed_content;
357   RunRequestWithPath(path, "bytes=3-z", &open_result, &seek_position,
358                      &done_reading, &observed_content);
359 
360   EXPECT_EQ(OK, open_result);
361   EXPECT_EQ(0, seek_position);
362   EXPECT_EQ("hello\n", observed_content);
363   EXPECT_TRUE(done_reading);
364   EXPECT_FALSE(delegate_.request_failed());
365 }
366 
367 }  // namespace
368 
369 }  // namespace net
370