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. 4 5 #include "webkit/browser/fileapi/file_system_url_request_job.h" 6 7 #include <string> 8 9 #include "base/bind.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" 39 40 namespace fileapi { 41 namespace { 42 43 // We always use the TEMPORARY FileSystem in this test. 44 const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/"; 45 const char kTestFileData[] = "0123456789"; 46 47 void FillBuffer(char* buffer, size_t len) { 48 base::RandBytes(buffer, len); 49 } 50 51 } // namespace 52 53 class FileSystemURLRequestJobTest : public testing::Test { 54 protected: 55 FileSystemURLRequestJobTest() : weak_factory_(this) { 56 } 57 58 virtual void SetUp() OVERRIDE { 59 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 60 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()); 65 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(); 72 73 net::URLRequest::Deprecated::RegisterProtocolFactory( 74 "filesystem", &FileSystemURLRequestJobFactory); 75 } 76 77 virtual void TearDown() OVERRIDE { 78 net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL); 79 ClearUnusedJob(); 80 if (pending_job_.get()) { 81 pending_job_->Kill(); 82 pending_job_ = NULL; 83 } 84 // FileReader posts a task to close the file in destructor. 85 base::RunLoop().RunUntilIdle(); 86 } 87 88 void OnOpenFileSystem(const GURL& root_url, 89 const std::string& name, 90 base::PlatformFileError result) { 91 ASSERT_EQ(base::PLATFORM_FILE_OK, result); 92 } 93 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()); 104 if (headers) 105 request_->SetExtraRequestHeaders(*headers); 106 ASSERT_TRUE(!job_); 107 job_ = new FileSystemURLRequestJob( 108 request_.get(), NULL, file_system_context); 109 pending_job_ = job_; 110 111 request_->Start(); 112 ASSERT_TRUE(request_->is_pending()); // verify that we're starting async 113 if (run_to_completion) 114 base::MessageLoop::current()->Run(); 115 } 116 117 void TestRequest(const GURL& url) { 118 TestRequestHelper(url, NULL, true, file_system_context_.get()); 119 } 120 121 void TestRequestWithContext(const GURL& url, 122 FileSystemContext* file_system_context) { 123 TestRequestHelper(url, NULL, true, file_system_context); 124 } 125 126 void TestRequestWithHeaders(const GURL& url, 127 const net::HttpRequestHeaders* headers) { 128 TestRequestHelper(url, headers, true, file_system_context_.get()); 129 } 130 131 void TestRequestNoRun(const GURL& url) { 132 TestRequestHelper(url, NULL, false, file_system_context_.get()); 133 } 134 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)); 142 } 143 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)); 153 } 154 155 GURL CreateFileSystemURL(const std::string& path) { 156 return GURL(kFileSystemURLPrefix + path); 157 } 158 159 static net::URLRequestJob* FileSystemURLRequestJobFactory( 160 net::URLRequest* request, 161 net::NetworkDelegate* network_delegate, 162 const std::string& scheme) { 163 DCHECK(job_); 164 net::URLRequestJob* temp = job_; 165 job_ = NULL; 166 return temp; 167 } 168 169 static void ClearUnusedJob() { 170 if (job_) { 171 scoped_refptr<net::URLRequestJob> deleter = job_; 172 job_ = NULL; 173 } 174 } 175 176 // Put the message loop at the top, so that it's the last thing deleted. 177 base::MessageLoopForIO message_loop_; 178 179 base::ScopedTempDir temp_dir_; 180 scoped_refptr<FileSystemContext> file_system_context_; 181 base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_; 182 183 net::URLRequestContext empty_context_; 184 185 // NOTE: order matters, request must die before delegate 186 scoped_ptr<net::TestDelegate> delegate_; 187 scoped_ptr<net::URLRequest> request_; 188 189 scoped_refptr<net::URLRequestJob> pending_job_; 190 static net::URLRequestJob* job_; 191 }; 192 193 // static 194 net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL; 195 196 namespace { 197 198 TEST_F(FileSystemURLRequestJobTest, FileTest) { 199 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); 200 TestRequest(CreateFileSystemURL("file1.dat")); 201 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); 210 } 211 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); 217 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); 222 223 net::HttpRequestHeaders headers; 224 headers.SetHeader( 225 net::HttpRequestHeaders::kRange, 226 net::HttpByteRange::Bounded( 227 first_byte_position, last_byte_position).GetHeaderValue()); 228 TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers); 229 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()); 234 } 235 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); 241 242 const size_t first_byte_position = 500; 243 std::string partial_buffer_string(buffer.get() + first_byte_position, 244 buffer.get() + buffer_size); 245 246 net::HttpRequestHeaders headers; 247 headers.SetHeader( 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()); 256 } 257 258 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()); 268 } 269 270 TEST_F(FileSystemURLRequestJobTest, RangeOutOfBounds) { 271 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); 272 net::HttpRequestHeaders headers; 273 headers.SetHeader( 274 net::HttpRequestHeaders::kRange, 275 net::HttpByteRange::Bounded(500, 1000).GetHeaderValue()); 276 TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers); 277 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()); 282 } 283 284 TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) { 285 CreateDirectory("dir"); 286 TestRequest(CreateFileSystemURL("dir")); 287 288 EXPECT_EQ(1, delegate_->received_redirect_count()); 289 EXPECT_TRUE(request_->status().is_success()); 290 EXPECT_FALSE(delegate_->request_failed()); 291 292 // We've deferred the redirect; now cancel the request to avoid following it. 293 request_->Cancel(); 294 base::MessageLoop::current()->Run(); 295 } 296 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()); 302 } 303 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()); 309 } 310 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()); 316 } 317 318 TEST_F(FileSystemURLRequestJobTest, Cancel) { 319 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); 320 TestRequestNoRun(CreateFileSystemURL("file1.dat")); 321 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! 326 } 327 328 TEST_F(FileSystemURLRequestJobTest, GetMimeType) { 329 const char kFilename[] = "hoge.html"; 330 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)); 338 339 TestRequest(CreateFileSystemURL(kFilename)); 340 341 std::string mime_type_from_job; 342 request_->GetMimeType(&mime_type_from_job); 343 EXPECT_EQ(mime_type_direct, mime_type_from_job); 344 } 345 346 TEST_F(FileSystemURLRequestJobTest, Incognito) { 347 WriteFile("file", kTestFileData, arraysize(kTestFileData) - 1); 348 349 // Creates a new filesystem context for incognito mode. 350 scoped_refptr<FileSystemContext> file_system_context = 351 CreateIncognitoFileSystemContextForTesting(NULL, temp_dir_.path()); 352 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()); 359 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()); 365 } 366 367 } // namespace 368 } // namespace fileapi 369