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::__anonaaf106690111::Range96 Range() {
97 start = 0;
98 end = 0;
99 }
100
Rangenet::__anonaaf106690111::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 delegate_.RunUntilComplete();
239 }
240
241 // Helper function to make a character array filled with |size| bytes of
242 // test content.
MakeContentOfSize(int size)243 std::string MakeContentOfSize(int size) {
244 EXPECT_GE(size, 0);
245 std::string result;
246 result.reserve(size);
247 for (int i = 0; i < size; i++) {
248 result.append(1, static_cast<char>(i % 256));
249 }
250 return result;
251 }
252
TEST_F(URLRequestTestJobBackedByFileEventsTest,ZeroByteFile)253 TEST_F(URLRequestTestJobBackedByFileEventsTest, ZeroByteFile) {
254 RunSuccessfulRequestWithString(std::string(""), nullptr);
255 }
256
TEST_F(URLRequestTestJobBackedByFileEventsTest,TinyFile)257 TEST_F(URLRequestTestJobBackedByFileEventsTest, TinyFile) {
258 RunSuccessfulRequestWithString(std::string("hello world"), nullptr);
259 }
260
TEST_F(URLRequestTestJobBackedByFileEventsTest,SmallFile)261 TEST_F(URLRequestTestJobBackedByFileEventsTest, SmallFile) {
262 RunSuccessfulRequestWithString(MakeContentOfSize(17 * 1024), nullptr);
263 }
264
TEST_F(URLRequestTestJobBackedByFileEventsTest,BigFile)265 TEST_F(URLRequestTestJobBackedByFileEventsTest, BigFile) {
266 RunSuccessfulRequestWithString(MakeContentOfSize(3 * 1024 * 1024), nullptr);
267 }
268
TEST_F(URLRequestTestJobBackedByFileEventsTest,Range)269 TEST_F(URLRequestTestJobBackedByFileEventsTest, Range) {
270 // Use a 15KB content file and read a range chosen somewhat arbitrarily but
271 // not aligned on any likely page boundaries.
272 int size = 15 * 1024;
273 Range range(1701, (6 * 1024) + 3);
274 RunSuccessfulRequestWithString(MakeContentOfSize(size), &range);
275 }
276
TEST_F(URLRequestTestJobBackedByFileEventsTest,DecodeSvgzFile)277 TEST_F(URLRequestTestJobBackedByFileEventsTest, DecodeSvgzFile) {
278 std::string expected_content("Hello, World!");
279 unsigned char gzip_data[] = {
280 // From:
281 // echo -n 'Hello, World!' | gzip | xxd -i | sed -e 's/^/ /'
282 0x1f, 0x8b, 0x08, 0x00, 0x2b, 0x02, 0x84, 0x55, 0x00, 0x03, 0xf3,
283 0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x08, 0xcf, 0x2f, 0xca, 0x49,
284 0x51, 0x04, 0x00, 0xd0, 0xc3, 0x4a, 0xec, 0x0d, 0x00, 0x00, 0x00};
285 RunSuccessfulRequestWithString(
286 std::string(reinterpret_cast<char*>(gzip_data), sizeof(gzip_data)),
287 expected_content, FILE_PATH_LITERAL("svgz"), nullptr);
288 }
289
TEST_F(URLRequestTestJobBackedByFileEventsTest,OpenNonExistentFile)290 TEST_F(URLRequestTestJobBackedByFileEventsTest, OpenNonExistentFile) {
291 base::FilePath path;
292 base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
293 path = path.Append(
294 FILE_PATH_LITERAL("net/data/url_request_unittest/non-existent.txt"));
295
296 int open_result;
297 int64_t seek_position;
298 bool done_reading;
299 std::string observed_content;
300 RunRequestWithPath(path, std::string(), &open_result, &seek_position,
301 &done_reading, &observed_content);
302
303 EXPECT_EQ(ERR_FILE_NOT_FOUND, open_result);
304 EXPECT_FALSE(done_reading);
305 EXPECT_TRUE(delegate_.request_failed());
306 }
307
TEST_F(URLRequestTestJobBackedByFileEventsTest,MultiRangeRequestNotSupported)308 TEST_F(URLRequestTestJobBackedByFileEventsTest, MultiRangeRequestNotSupported) {
309 base::FilePath path;
310 base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
311 path = path.Append(
312 FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt"));
313
314 int open_result;
315 int64_t seek_position;
316 bool done_reading;
317 std::string observed_content;
318 RunRequestWithPath(path, "bytes=1-5,20-30", &open_result, &seek_position,
319 &done_reading, &observed_content);
320
321 EXPECT_EQ(OK, open_result);
322 EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, seek_position);
323 EXPECT_FALSE(done_reading);
324 EXPECT_TRUE(delegate_.request_failed());
325 }
326
TEST_F(URLRequestTestJobBackedByFileEventsTest,RangeExceedingFileSize)327 TEST_F(URLRequestTestJobBackedByFileEventsTest, RangeExceedingFileSize) {
328 base::FilePath path;
329 base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
330 path = path.Append(
331 FILE_PATH_LITERAL("net/data/url_request_unittest/BullRunSpeech.txt"));
332
333 int open_result;
334 int64_t seek_position;
335 bool done_reading;
336 std::string observed_content;
337 RunRequestWithPath(path, "bytes=50000-", &open_result, &seek_position,
338 &done_reading, &observed_content);
339
340 EXPECT_EQ(OK, open_result);
341 EXPECT_EQ(ERR_REQUEST_RANGE_NOT_SATISFIABLE, seek_position);
342 EXPECT_FALSE(done_reading);
343 EXPECT_TRUE(delegate_.request_failed());
344 }
345
TEST_F(URLRequestTestJobBackedByFileEventsTest,IgnoreRangeParsingError)346 TEST_F(URLRequestTestJobBackedByFileEventsTest, IgnoreRangeParsingError) {
347 base::FilePath path;
348 base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &path);
349 path = path.Append(
350 FILE_PATH_LITERAL("net/data/url_request_unittest/simple.html"));
351
352 int open_result;
353 int64_t seek_position;
354 bool done_reading;
355 std::string observed_content;
356 RunRequestWithPath(path, "bytes=3-z", &open_result, &seek_position,
357 &done_reading, &observed_content);
358
359 EXPECT_EQ(OK, open_result);
360 EXPECT_EQ(0, seek_position);
361 EXPECT_EQ("hello\n", observed_content);
362 EXPECT_TRUE(done_reading);
363 EXPECT_FALSE(delegate_.request_failed());
364 }
365
366 } // namespace
367
368 } // namespace net
369