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/async_file_test_helper.h"
24 #include "content/public/test/test_file_system_context.h"
25 #include "net/base/load_flags.h"
26 #include "net/base/mime_util.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/net_util.h"
29 #include "net/base/request_priority.h"
30 #include "net/http/http_byte_range.h"
31 #include "net/http/http_request_headers.h"
32 #include "net/url_request/url_request.h"
33 #include "net/url_request/url_request_context.h"
34 #include "net/url_request/url_request_test_util.h"
35 #include "testing/gtest/include/gtest/gtest.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"
40 using content::AsyncFileTestHelper;
41 using fileapi::FileSystemContext;
42 using fileapi::FileSystemURL;
43 using fileapi::FileSystemURLRequestJob;
48 // We always use the TEMPORARY FileSystem in this test.
49 const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/";
50 const char kTestFileData[] = "0123456789";
52 void FillBuffer(char* buffer, size_t len) {
53 base::RandBytes(buffer, len);
58 class FileSystemURLRequestJobTest : public testing::Test {
60 FileSystemURLRequestJobTest() : weak_factory_(this) {
63 virtual void SetUp() OVERRIDE {
64 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
66 // We use the main thread so that we can get the root path synchronously.
67 // TODO(adamk): Run this on the FILE thread we've created as well.
68 file_system_context_ =
69 CreateFileSystemContextForTesting(NULL, temp_dir_.path());
71 file_system_context_->OpenFileSystem(
72 GURL("http://remote/"), fileapi::kFileSystemTypeTemporary,
73 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
74 base::Bind(&FileSystemURLRequestJobTest::OnOpenFileSystem,
75 weak_factory_.GetWeakPtr()));
76 base::RunLoop().RunUntilIdle();
78 net::URLRequest::Deprecated::RegisterProtocolFactory(
79 "filesystem", &FileSystemURLRequestJobFactory);
82 virtual void TearDown() OVERRIDE {
83 net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL);
85 if (pending_job_.get()) {
89 // FileReader posts a task to close the file in destructor.
90 base::RunLoop().RunUntilIdle();
93 void OnOpenFileSystem(const GURL& root_url,
94 const std::string& name,
95 base::File::Error result) {
96 ASSERT_EQ(base::File::FILE_OK, result);
99 void TestRequestHelper(const GURL& url,
100 const net::HttpRequestHeaders* headers,
101 bool run_to_completion,
102 FileSystemContext* file_system_context) {
103 delegate_.reset(new net::TestDelegate());
104 // Make delegate_ exit the MessageLoop when the request is done.
105 delegate_->set_quit_on_complete(true);
106 delegate_->set_quit_on_redirect(true);
107 request_ = empty_context_.CreateRequest(
108 url, net::DEFAULT_PRIORITY, delegate_.get());
110 request_->SetExtraRequestHeaders(*headers);
112 job_ = new FileSystemURLRequestJob(
113 request_.get(), NULL, file_system_context);
117 ASSERT_TRUE(request_->is_pending()); // verify that we're starting async
118 if (run_to_completion)
119 base::MessageLoop::current()->Run();
122 void TestRequest(const GURL& url) {
123 TestRequestHelper(url, NULL, true, file_system_context_.get());
126 void TestRequestWithContext(const GURL& url,
127 FileSystemContext* file_system_context) {
128 TestRequestHelper(url, NULL, true, file_system_context);
131 void TestRequestWithHeaders(const GURL& url,
132 const net::HttpRequestHeaders* headers) {
133 TestRequestHelper(url, headers, true, file_system_context_.get());
136 void TestRequestNoRun(const GURL& url) {
137 TestRequestHelper(url, NULL, false, file_system_context_.get());
140 void CreateDirectory(const base::StringPiece& dir_name) {
141 FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
142 GURL("http://remote"),
143 fileapi::kFileSystemTypeTemporary,
144 base::FilePath().AppendASCII(dir_name));
145 ASSERT_EQ(base::File::FILE_OK, AsyncFileTestHelper::CreateDirectory(
146 file_system_context_, url));
149 void WriteFile(const base::StringPiece& file_name,
150 const char* buf, int buf_size) {
151 FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
152 GURL("http://remote"),
153 fileapi::kFileSystemTypeTemporary,
154 base::FilePath().AppendASCII(file_name));
155 ASSERT_EQ(base::File::FILE_OK,
156 AsyncFileTestHelper::CreateFileWithData(
157 file_system_context_, url, buf, buf_size));
160 GURL CreateFileSystemURL(const std::string& path) {
161 return GURL(kFileSystemURLPrefix + path);
164 static net::URLRequestJob* FileSystemURLRequestJobFactory(
165 net::URLRequest* request,
166 net::NetworkDelegate* network_delegate,
167 const std::string& scheme) {
169 net::URLRequestJob* temp = job_;
174 static void ClearUnusedJob() {
176 scoped_refptr<net::URLRequestJob> deleter = job_;
181 // Put the message loop at the top, so that it's the last thing deleted.
182 base::MessageLoopForIO message_loop_;
184 base::ScopedTempDir temp_dir_;
185 scoped_refptr<fileapi::FileSystemContext> file_system_context_;
186 base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_;
188 net::URLRequestContext empty_context_;
190 // NOTE: order matters, request must die before delegate
191 scoped_ptr<net::TestDelegate> delegate_;
192 scoped_ptr<net::URLRequest> request_;
194 scoped_refptr<net::URLRequestJob> pending_job_;
195 static net::URLRequestJob* job_;
199 net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL;
203 TEST_F(FileSystemURLRequestJobTest, FileTest) {
204 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
205 TestRequest(CreateFileSystemURL("file1.dat"));
207 ASSERT_FALSE(request_->is_pending());
208 EXPECT_EQ(1, delegate_->response_started_count());
209 EXPECT_FALSE(delegate_->received_data_before_response());
210 EXPECT_EQ(kTestFileData, delegate_->data_received());
211 EXPECT_EQ(200, request_->GetResponseCode());
212 std::string cache_control;
213 request_->GetResponseHeaderByName("cache-control", &cache_control);
214 EXPECT_EQ("no-cache", cache_control);
217 TEST_F(FileSystemURLRequestJobTest, FileTestFullSpecifiedRange) {
218 const size_t buffer_size = 4000;
219 scoped_ptr<char[]> buffer(new char[buffer_size]);
220 FillBuffer(buffer.get(), buffer_size);
221 WriteFile("bigfile", buffer.get(), buffer_size);
223 const size_t first_byte_position = 500;
224 const size_t last_byte_position = buffer_size - first_byte_position;
225 std::string partial_buffer_string(buffer.get() + first_byte_position,
226 buffer.get() + last_byte_position + 1);
228 net::HttpRequestHeaders headers;
230 net::HttpRequestHeaders::kRange,
231 net::HttpByteRange::Bounded(
232 first_byte_position, last_byte_position).GetHeaderValue());
233 TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
235 ASSERT_FALSE(request_->is_pending());
236 EXPECT_EQ(1, delegate_->response_started_count());
237 EXPECT_FALSE(delegate_->received_data_before_response());
238 EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
241 TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) {
242 const size_t buffer_size = 4000;
243 scoped_ptr<char[]> buffer(new char[buffer_size]);
244 FillBuffer(buffer.get(), buffer_size);
245 WriteFile("bigfile", buffer.get(), buffer_size);
247 const size_t first_byte_position = 500;
248 std::string partial_buffer_string(buffer.get() + first_byte_position,
249 buffer.get() + buffer_size);
251 net::HttpRequestHeaders headers;
253 net::HttpRequestHeaders::kRange,
254 net::HttpByteRange::RightUnbounded(first_byte_position).GetHeaderValue());
255 TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers);
256 ASSERT_FALSE(request_->is_pending());
257 EXPECT_EQ(1, delegate_->response_started_count());
258 EXPECT_FALSE(delegate_->received_data_before_response());
259 // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed.
260 EXPECT_TRUE(partial_buffer_string == delegate_->data_received());
264 TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) {
265 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
266 net::HttpRequestHeaders headers;
267 headers.SetHeader(net::HttpRequestHeaders::kRange,
268 "bytes=0-5,10-200,200-300");
269 TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
270 EXPECT_TRUE(delegate_->request_failed());
271 EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
272 request_->status().error());
275 TEST_F(FileSystemURLRequestJobTest, RangeOutOfBounds) {
276 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
277 net::HttpRequestHeaders headers;
279 net::HttpRequestHeaders::kRange,
280 net::HttpByteRange::Bounded(500, 1000).GetHeaderValue());
281 TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers);
283 ASSERT_FALSE(request_->is_pending());
284 EXPECT_TRUE(delegate_->request_failed());
285 EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
286 request_->status().error());
289 TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) {
290 CreateDirectory("dir");
291 TestRequest(CreateFileSystemURL("dir"));
293 EXPECT_EQ(1, delegate_->received_redirect_count());
294 EXPECT_TRUE(request_->status().is_success());
295 EXPECT_FALSE(delegate_->request_failed());
297 // We've deferred the redirect; now cancel the request to avoid following it.
299 base::MessageLoop::current()->Run();
302 TEST_F(FileSystemURLRequestJobTest, InvalidURL) {
303 TestRequest(GURL("filesystem:/foo/bar/baz"));
304 ASSERT_FALSE(request_->is_pending());
305 EXPECT_TRUE(delegate_->request_failed());
306 EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error());
309 TEST_F(FileSystemURLRequestJobTest, NoSuchRoot) {
310 TestRequest(GURL("filesystem:http://remote/persistent/somefile"));
311 ASSERT_FALSE(request_->is_pending());
312 EXPECT_TRUE(delegate_->request_failed());
313 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
316 TEST_F(FileSystemURLRequestJobTest, NoSuchFile) {
317 TestRequest(CreateFileSystemURL("somefile"));
318 ASSERT_FALSE(request_->is_pending());
319 EXPECT_TRUE(delegate_->request_failed());
320 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
323 TEST_F(FileSystemURLRequestJobTest, Cancel) {
324 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1);
325 TestRequestNoRun(CreateFileSystemURL("file1.dat"));
327 // Run StartAsync() and only StartAsync().
328 base::MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release());
329 base::RunLoop().RunUntilIdle();
330 // If we get here, success! we didn't crash!
333 TEST_F(FileSystemURLRequestJobTest, GetMimeType) {
334 const char kFilename[] = "hoge.html";
336 std::string mime_type_direct;
337 base::FilePath::StringType extension =
338 base::FilePath().AppendASCII(kFilename).Extension();
339 if (!extension.empty())
340 extension = extension.substr(1);
341 EXPECT_TRUE(net::GetWellKnownMimeTypeFromExtension(
342 extension, &mime_type_direct));
344 TestRequest(CreateFileSystemURL(kFilename));
346 std::string mime_type_from_job;
347 request_->GetMimeType(&mime_type_from_job);
348 EXPECT_EQ(mime_type_direct, mime_type_from_job);
351 TEST_F(FileSystemURLRequestJobTest, Incognito) {
352 WriteFile("file", kTestFileData, arraysize(kTestFileData) - 1);
354 // Creates a new filesystem context for incognito mode.
355 scoped_refptr<FileSystemContext> file_system_context =
356 CreateIncognitoFileSystemContextForTesting(NULL, temp_dir_.path());
358 // The request should return NOT_FOUND error if it's in incognito mode.
359 TestRequestWithContext(CreateFileSystemURL("file"),
360 file_system_context.get());
361 ASSERT_FALSE(request_->is_pending());
362 EXPECT_TRUE(delegate_->request_failed());
363 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error());
365 // Make sure it returns success with regular (non-incognito) context.
366 TestRequest(CreateFileSystemURL("file"));
367 ASSERT_FALSE(request_->is_pending());
368 EXPECT_EQ(kTestFileData, delegate_->data_received());
369 EXPECT_EQ(200, request_->GetResponseCode());
373 } // namespace content