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.
5 #include "net/url_request/url_request_file_job.h"
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/thread_task_runner_handle.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "net/base/filename_util.h"
15 #include "net/base/net_util.h"
16 #include "net/url_request/url_request.h"
17 #include "net/url_request/url_request_test_util.h"
18 #include "testing/gtest/include/gtest/gtest.h"
24 // A URLRequestFileJob for testing OnSeekComplete / OnReadComplete callbacks.
25 class URLRequestFileJobWithCallbacks
: public URLRequestFileJob
{
27 URLRequestFileJobWithCallbacks(
29 NetworkDelegate
* network_delegate
,
30 const base::FilePath
& file_path
,
31 const scoped_refptr
<base::TaskRunner
>& file_task_runner
)
32 : URLRequestFileJob(request
,
39 int64
seek_position() { return seek_position_
; }
40 const std::vector
<std::string
>& data_chunks() { return data_chunks_
; }
43 ~URLRequestFileJobWithCallbacks() override
{}
45 void OnSeekComplete(int64 result
) override
{
46 ASSERT_EQ(seek_position_
, 0);
47 seek_position_
= result
;
50 void OnReadComplete(IOBuffer
* buf
, int result
) override
{
51 data_chunks_
.push_back(std::string(buf
->data(), result
));
55 std::vector
<std::string
> data_chunks_
;
58 // A URLRequestJobFactory that will return URLRequestFileJobWithCallbacks
59 // instances for file:// scheme URLs.
60 class CallbacksJobFactory
: public URLRequestJobFactory
{
64 virtual void OnJobCreated(URLRequestFileJobWithCallbacks
* job
) = 0;
67 CallbacksJobFactory(const base::FilePath
& path
, JobObserver
* observer
)
68 : path_(path
), observer_(observer
) {
71 ~CallbacksJobFactory() override
{}
73 URLRequestJob
* MaybeCreateJobWithProtocolHandler(
74 const std::string
& scheme
,
76 NetworkDelegate
* network_delegate
) const override
{
77 URLRequestFileJobWithCallbacks
* job
= new URLRequestFileJobWithCallbacks(
78 request
, network_delegate
, path_
, base::ThreadTaskRunnerHandle::Get());
79 observer_
->OnJobCreated(job
);
83 URLRequestJob
* MaybeInterceptRedirect(URLRequest
* request
,
84 NetworkDelegate
* network_delegate
,
85 const GURL
& location
) const override
{
89 URLRequestJob
* MaybeInterceptResponse(
91 NetworkDelegate
* network_delegate
) const override
{
95 bool IsHandledProtocol(const std::string
& scheme
) const override
{
96 return scheme
== "file";
99 bool IsHandledURL(const GURL
& url
) const override
{
100 return IsHandledProtocol(url
.scheme());
103 bool IsSafeRedirectTarget(const GURL
& location
) const override
{
108 base::FilePath path_
;
109 JobObserver
* observer_
;
112 // Helper function to create a file in |directory| filled with
113 // |content|. Returns true on succes and fills in |path| with the full path to
115 bool CreateTempFileWithContent(const std::string
& content
,
116 const base::ScopedTempDir
& directory
,
117 base::FilePath
* path
) {
118 if (!directory
.IsValid())
121 if (!base::CreateTemporaryFileInDir(directory
.path(), path
))
124 return base::WriteFile(*path
, content
.c_str(), content
.length());
127 class JobObserverImpl
: public CallbacksJobFactory::JobObserver
{
129 void OnJobCreated(URLRequestFileJobWithCallbacks
* job
) override
{
130 jobs_
.push_back(job
);
133 typedef std::vector
<scoped_refptr
<URLRequestFileJobWithCallbacks
> > JobList
;
135 const JobList
& jobs() { return jobs_
; }
141 // A simple holder for start/end used in http range requests.
151 Range(int start
, int end
) {
157 // A superclass for tests of the OnSeekComplete / OnReadComplete functions of
158 // URLRequestFileJob.
159 class URLRequestFileJobEventsTest
: public testing::Test
{
161 URLRequestFileJobEventsTest();
164 // This creates a file with |content| as the contents, and then creates and
165 // runs a URLRequestFileJobWithCallbacks job to get the contents out of it,
166 // and makes sure that the callbacks observed the correct bytes. If a Range
167 // is provided, this function will add the appropriate Range http header to
168 // the request and verify that only the bytes in that range (inclusive) were
170 void RunRequest(const std::string
& content
, const Range
* range
);
172 JobObserverImpl observer_
;
173 TestURLRequestContext context_
;
174 TestDelegate delegate_
;
177 URLRequestFileJobEventsTest::URLRequestFileJobEventsTest() {}
179 void URLRequestFileJobEventsTest::RunRequest(const std::string
& content
,
180 const Range
* range
) {
181 base::ScopedTempDir directory
;
182 ASSERT_TRUE(directory
.CreateUniqueTempDir());
184 ASSERT_TRUE(CreateTempFileWithContent(content
, directory
, &path
));
185 CallbacksJobFactory
factory(path
, &observer_
);
186 context_
.set_job_factory(&factory
);
188 scoped_ptr
<URLRequest
> request(context_
.CreateRequest(
189 FilePathToFileURL(path
), DEFAULT_PRIORITY
, &delegate_
));
191 ASSERT_GE(range
->start
, 0);
192 ASSERT_GE(range
->end
, 0);
193 ASSERT_LE(range
->start
, range
->end
);
194 ASSERT_LT(static_cast<unsigned int>(range
->end
), content
.length());
195 std::string range_value
=
196 base::StringPrintf("bytes=%d-%d", range
->start
, range
->end
);
197 request
->SetExtraRequestHeaderByName(
198 HttpRequestHeaders::kRange
, range_value
, true /*overwrite*/);
205 EXPECT_FALSE(delegate_
.request_failed());
206 int expected_length
=
207 range
? (range
->end
- range
->start
+ 1) : content
.length();
208 EXPECT_EQ(delegate_
.bytes_received(), expected_length
);
210 std::string expected_content
;
212 expected_content
.insert(0, content
, range
->start
, expected_length
);
214 expected_content
= content
;
216 EXPECT_TRUE(delegate_
.data_received() == expected_content
);
218 ASSERT_EQ(observer_
.jobs().size(), 1u);
219 ASSERT_EQ(observer_
.jobs().at(0)->seek_position(), range
? range
->start
: 0);
221 std::string observed_content
;
222 const std::vector
<std::string
>& chunks
=
223 observer_
.jobs().at(0)->data_chunks();
224 for (std::vector
<std::string
>::const_iterator i
= chunks
.begin();
227 observed_content
.append(*i
);
229 EXPECT_EQ(expected_content
, observed_content
);
232 // Helper function to make a character array filled with |size| bytes of
234 std::string
MakeContentOfSize(int size
) {
237 result
.reserve(size
);
238 for (int i
= 0; i
< size
; i
++) {
239 result
.append(1, static_cast<char>(i
% 256));
244 TEST_F(URLRequestFileJobEventsTest
, TinyFile
) {
245 RunRequest(std::string("hello world"), NULL
);
248 TEST_F(URLRequestFileJobEventsTest
, SmallFile
) {
249 RunRequest(MakeContentOfSize(17 * 1024), NULL
);
252 TEST_F(URLRequestFileJobEventsTest
, BigFile
) {
253 RunRequest(MakeContentOfSize(3 * 1024 * 1024), NULL
);
256 TEST_F(URLRequestFileJobEventsTest
, Range
) {
257 // Use a 15KB content file and read a range chosen somewhat arbitrarily but
258 // not aligned on any likely page boundaries.
259 int size
= 15 * 1024;
260 Range
range(1701, (6 * 1024) + 3);
261 RunRequest(MakeContentOfSize(size
), &range
);