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