1 // Copyright 2013 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 "webkit/browser/fileapi/file_system_url_request_job.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/format_macros.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/message_loop/message_loop_proxy.h"
17 #include "base/platform_file.h"
18 #include "base/rand_util.h"
19 #include "base/run_loop.h"
20 #include "base/strings/string_piece.h"
21 #include "base/strings/stringprintf.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "content/public/test/test_file_system_context.h"
24 #include "net/base/load_flags.h"
25 #include "net/base/mime_util.h"
26 #include "net/base/net_errors.h"
27 #include "net/base/net_util.h"
28 #include "net/base/request_priority.h"
29 #include "net/http/http_byte_range.h"
30 #include "net/http/http_request_headers.h"
31 #include "net/url_request/url_request.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_test_util.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35 #include "webkit/browser/fileapi/async_file_test_helper.h"
36 #include "webkit/browser/fileapi/external_mount_points.h"
37 #include "webkit/browser/fileapi/file_system_context.h"
38 #include "webkit/browser/fileapi/file_system_file_util.h"
43 // We always use the TEMPORARY FileSystem in this test.
44 const char kFileSystemURLPrefix
[] = "filesystem:http://remote/temporary/";
45 const char kTestFileData
[] = "0123456789";
47 void FillBuffer(char* buffer
, size_t len
) {
48 base::RandBytes(buffer
, len
);
53 class FileSystemURLRequestJobTest
: public testing::Test
{
55 FileSystemURLRequestJobTest() : weak_factory_(this) {
58 virtual void SetUp() OVERRIDE
{
59 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
61 // We use the main thread so that we can get the root path synchronously.
62 // TODO(adamk): Run this on the FILE thread we've created as well.
63 file_system_context_
=
64 CreateFileSystemContextForTesting(NULL
, temp_dir_
.path());
66 file_system_context_
->OpenFileSystem(
67 GURL("http://remote/"), kFileSystemTypeTemporary
,
68 OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT
,
69 base::Bind(&FileSystemURLRequestJobTest::OnOpenFileSystem
,
70 weak_factory_
.GetWeakPtr()));
71 base::RunLoop().RunUntilIdle();
73 net::URLRequest::Deprecated::RegisterProtocolFactory(
74 "filesystem", &FileSystemURLRequestJobFactory
);
77 virtual void TearDown() OVERRIDE
{
78 net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL
);
80 if (pending_job_
.get()) {
84 // FileReader posts a task to close the file in destructor.
85 base::RunLoop().RunUntilIdle();
88 void OnOpenFileSystem(const GURL
& root_url
,
89 const std::string
& name
,
90 base::PlatformFileError result
) {
91 ASSERT_EQ(base::PLATFORM_FILE_OK
, result
);
94 void TestRequestHelper(const GURL
& url
,
95 const net::HttpRequestHeaders
* headers
,
96 bool run_to_completion
,
97 FileSystemContext
* file_system_context
) {
98 delegate_
.reset(new net::TestDelegate());
99 // Make delegate_ exit the MessageLoop when the request is done.
100 delegate_
->set_quit_on_complete(true);
101 delegate_
->set_quit_on_redirect(true);
102 request_
= empty_context_
.CreateRequest(
103 url
, net::DEFAULT_PRIORITY
, delegate_
.get());
105 request_
->SetExtraRequestHeaders(*headers
);
107 job_
= new FileSystemURLRequestJob(
108 request_
.get(), NULL
, file_system_context
);
112 ASSERT_TRUE(request_
->is_pending()); // verify that we're starting async
113 if (run_to_completion
)
114 base::MessageLoop::current()->Run();
117 void TestRequest(const GURL
& url
) {
118 TestRequestHelper(url
, NULL
, true, file_system_context_
.get());
121 void TestRequestWithContext(const GURL
& url
,
122 FileSystemContext
* file_system_context
) {
123 TestRequestHelper(url
, NULL
, true, file_system_context
);
126 void TestRequestWithHeaders(const GURL
& url
,
127 const net::HttpRequestHeaders
* headers
) {
128 TestRequestHelper(url
, headers
, true, file_system_context_
.get());
131 void TestRequestNoRun(const GURL
& url
) {
132 TestRequestHelper(url
, NULL
, false, file_system_context_
.get());
135 void CreateDirectory(const base::StringPiece
& dir_name
) {
136 FileSystemURL url
= file_system_context_
->CreateCrackedFileSystemURL(
137 GURL("http://remote"),
138 kFileSystemTypeTemporary
,
139 base::FilePath().AppendASCII(dir_name
));
140 ASSERT_EQ(base::PLATFORM_FILE_OK
, AsyncFileTestHelper::CreateDirectory(
141 file_system_context_
, url
));
144 void WriteFile(const base::StringPiece
& file_name
,
145 const char* buf
, int buf_size
) {
146 FileSystemURL url
= file_system_context_
->CreateCrackedFileSystemURL(
147 GURL("http://remote"),
148 kFileSystemTypeTemporary
,
149 base::FilePath().AppendASCII(file_name
));
150 ASSERT_EQ(base::PLATFORM_FILE_OK
,
151 AsyncFileTestHelper::CreateFileWithData(
152 file_system_context_
, url
, buf
, buf_size
));
155 GURL
CreateFileSystemURL(const std::string
& path
) {
156 return GURL(kFileSystemURLPrefix
+ path
);
159 static net::URLRequestJob
* FileSystemURLRequestJobFactory(
160 net::URLRequest
* request
,
161 net::NetworkDelegate
* network_delegate
,
162 const std::string
& scheme
) {
164 net::URLRequestJob
* temp
= job_
;
169 static void ClearUnusedJob() {
171 scoped_refptr
<net::URLRequestJob
> deleter
= job_
;
176 // Put the message loop at the top, so that it's the last thing deleted.
177 base::MessageLoopForIO message_loop_
;
179 base::ScopedTempDir temp_dir_
;
180 scoped_refptr
<FileSystemContext
> file_system_context_
;
181 base::WeakPtrFactory
<FileSystemURLRequestJobTest
> weak_factory_
;
183 net::URLRequestContext empty_context_
;
185 // NOTE: order matters, request must die before delegate
186 scoped_ptr
<net::TestDelegate
> delegate_
;
187 scoped_ptr
<net::URLRequest
> request_
;
189 scoped_refptr
<net::URLRequestJob
> pending_job_
;
190 static net::URLRequestJob
* job_
;
194 net::URLRequestJob
* FileSystemURLRequestJobTest::job_
= NULL
;
198 TEST_F(FileSystemURLRequestJobTest
, FileTest
) {
199 WriteFile("file1.dat", kTestFileData
, arraysize(kTestFileData
) - 1);
200 TestRequest(CreateFileSystemURL("file1.dat"));
202 ASSERT_FALSE(request_
->is_pending());
203 EXPECT_EQ(1, delegate_
->response_started_count());
204 EXPECT_FALSE(delegate_
->received_data_before_response());
205 EXPECT_EQ(kTestFileData
, delegate_
->data_received());
206 EXPECT_EQ(200, request_
->GetResponseCode());
207 std::string cache_control
;
208 request_
->GetResponseHeaderByName("cache-control", &cache_control
);
209 EXPECT_EQ("no-cache", cache_control
);
212 TEST_F(FileSystemURLRequestJobTest
, FileTestFullSpecifiedRange
) {
213 const size_t buffer_size
= 4000;
214 scoped_ptr
<char[]> buffer(new char[buffer_size
]);
215 FillBuffer(buffer
.get(), buffer_size
);
216 WriteFile("bigfile", buffer
.get(), buffer_size
);
218 const size_t first_byte_position
= 500;
219 const size_t last_byte_position
= buffer_size
- first_byte_position
;
220 std::string
partial_buffer_string(buffer
.get() + first_byte_position
,
221 buffer
.get() + last_byte_position
+ 1);
223 net::HttpRequestHeaders headers
;
225 net::HttpRequestHeaders::kRange
,
226 net::HttpByteRange::Bounded(
227 first_byte_position
, last_byte_position
).GetHeaderValue());
228 TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers
);
230 ASSERT_FALSE(request_
->is_pending());
231 EXPECT_EQ(1, delegate_
->response_started_count());
232 EXPECT_FALSE(delegate_
->received_data_before_response());
233 EXPECT_TRUE(partial_buffer_string
== delegate_
->data_received());
236 TEST_F(FileSystemURLRequestJobTest
, FileTestHalfSpecifiedRange
) {
237 const size_t buffer_size
= 4000;
238 scoped_ptr
<char[]> buffer(new char[buffer_size
]);
239 FillBuffer(buffer
.get(), buffer_size
);
240 WriteFile("bigfile", buffer
.get(), buffer_size
);
242 const size_t first_byte_position
= 500;
243 std::string
partial_buffer_string(buffer
.get() + first_byte_position
,
244 buffer
.get() + buffer_size
);
246 net::HttpRequestHeaders headers
;
248 net::HttpRequestHeaders::kRange
,
249 net::HttpByteRange::RightUnbounded(first_byte_position
).GetHeaderValue());
250 TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers
);
251 ASSERT_FALSE(request_
->is_pending());
252 EXPECT_EQ(1, delegate_
->response_started_count());
253 EXPECT_FALSE(delegate_
->received_data_before_response());
254 // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
255 EXPECT_TRUE(partial_buffer_string
== delegate_
->data_received());
259 TEST_F(FileSystemURLRequestJobTest
, FileTestMultipleRangesNotSupported
) {
260 WriteFile("file1.dat", kTestFileData
, arraysize(kTestFileData
) - 1);
261 net::HttpRequestHeaders headers
;
262 headers
.SetHeader(net::HttpRequestHeaders::kRange
,
263 "bytes=0-5,10-200,200-300");
264 TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers
);
265 EXPECT_TRUE(delegate_
->request_failed());
266 EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
,
267 request_
->status().error());
270 TEST_F(FileSystemURLRequestJobTest
, RangeOutOfBounds
) {
271 WriteFile("file1.dat", kTestFileData
, arraysize(kTestFileData
) - 1);
272 net::HttpRequestHeaders headers
;
274 net::HttpRequestHeaders::kRange
,
275 net::HttpByteRange::Bounded(500, 1000).GetHeaderValue());
276 TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers
);
278 ASSERT_FALSE(request_
->is_pending());
279 EXPECT_TRUE(delegate_
->request_failed());
280 EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
,
281 request_
->status().error());
284 TEST_F(FileSystemURLRequestJobTest
, FileDirRedirect
) {
285 CreateDirectory("dir");
286 TestRequest(CreateFileSystemURL("dir"));
288 EXPECT_EQ(1, delegate_
->received_redirect_count());
289 EXPECT_TRUE(request_
->status().is_success());
290 EXPECT_FALSE(delegate_
->request_failed());
292 // We've deferred the redirect; now cancel the request to avoid following it.
294 base::MessageLoop::current()->Run();
297 TEST_F(FileSystemURLRequestJobTest
, InvalidURL
) {
298 TestRequest(GURL("filesystem:/foo/bar/baz"));
299 ASSERT_FALSE(request_
->is_pending());
300 EXPECT_TRUE(delegate_
->request_failed());
301 EXPECT_EQ(net::ERR_INVALID_URL
, request_
->status().error());
304 TEST_F(FileSystemURLRequestJobTest
, NoSuchRoot
) {
305 TestRequest(GURL("filesystem:http://remote/persistent/somefile"));
306 ASSERT_FALSE(request_
->is_pending());
307 EXPECT_TRUE(delegate_
->request_failed());
308 EXPECT_EQ(net::ERR_FILE_NOT_FOUND
, request_
->status().error());
311 TEST_F(FileSystemURLRequestJobTest
, NoSuchFile
) {
312 TestRequest(CreateFileSystemURL("somefile"));
313 ASSERT_FALSE(request_
->is_pending());
314 EXPECT_TRUE(delegate_
->request_failed());
315 EXPECT_EQ(net::ERR_FILE_NOT_FOUND
, request_
->status().error());
318 TEST_F(FileSystemURLRequestJobTest
, Cancel
) {
319 WriteFile("file1.dat", kTestFileData
, arraysize(kTestFileData
) - 1);
320 TestRequestNoRun(CreateFileSystemURL("file1.dat"));
322 // Run StartAsync() and only StartAsync().
323 base::MessageLoop::current()->DeleteSoon(FROM_HERE
, request_
.release());
324 base::RunLoop().RunUntilIdle();
325 // If we get here, success! we didn't crash!
328 TEST_F(FileSystemURLRequestJobTest
, GetMimeType
) {
329 const char kFilename
[] = "hoge.html";
331 std::string mime_type_direct
;
332 base::FilePath::StringType extension
=
333 base::FilePath().AppendASCII(kFilename
).Extension();
334 if (!extension
.empty())
335 extension
= extension
.substr(1);
336 EXPECT_TRUE(net::GetWellKnownMimeTypeFromExtension(
337 extension
, &mime_type_direct
));
339 TestRequest(CreateFileSystemURL(kFilename
));
341 std::string mime_type_from_job
;
342 request_
->GetMimeType(&mime_type_from_job
);
343 EXPECT_EQ(mime_type_direct
, mime_type_from_job
);
346 TEST_F(FileSystemURLRequestJobTest
, Incognito
) {
347 WriteFile("file", kTestFileData
, arraysize(kTestFileData
) - 1);
349 // Creates a new filesystem context for incognito mode.
350 scoped_refptr
<FileSystemContext
> file_system_context
=
351 CreateIncognitoFileSystemContextForTesting(NULL
, temp_dir_
.path());
353 // The request should return NOT_FOUND error if it's in incognito mode.
354 TestRequestWithContext(CreateFileSystemURL("file"),
355 file_system_context
.get());
356 ASSERT_FALSE(request_
->is_pending());
357 EXPECT_TRUE(delegate_
->request_failed());
358 EXPECT_EQ(net::ERR_FILE_NOT_FOUND
, request_
->status().error());
360 // Make sure it returns success with regular (non-incognito) context.
361 TestRequest(CreateFileSystemURL("file"));
362 ASSERT_FALSE(request_
->is_pending());
363 EXPECT_EQ(kTestFileData
, delegate_
->data_received());
364 EXPECT_EQ(200, request_
->GetResponseCode());
368 } // namespace fileapi