| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/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.h" | |
| 16 #include "base/message_loop_proxy.h" | |
| 17 #include "base/platform_file.h" | |
| 18 #include "base/rand_util.h" | |
| 19 #include "base/stringprintf.h" | |
| 20 #include "base/strings/string_piece.h" | |
| 21 #include "base/utf_string_conversions.h" | |
| 22 #include "net/base/load_flags.h" | |
| 23 #include "net/base/mime_util.h" | |
| 24 #include "net/base/net_errors.h" | |
| 25 #include "net/base/net_util.h" | |
| 26 #include "net/http/http_request_headers.h" | |
| 27 #include "net/url_request/url_request.h" | |
| 28 #include "net/url_request/url_request_context.h" | |
| 29 #include "net/url_request/url_request_test_util.h" | |
| 30 #include "testing/gtest/include/gtest/gtest.h" | |
| 31 #include "webkit/fileapi/file_system_context.h" | |
| 32 #include "webkit/fileapi/file_system_file_util.h" | |
| 33 #include "webkit/fileapi/file_system_operation_context.h" | |
| 34 #include "webkit/fileapi/mock_file_system_context.h" | |
| 35 #include "webkit/fileapi/sandbox_mount_point_provider.h" | |
| 36 | |
| 37 namespace fileapi { | |
| 38 namespace { | |
| 39 | |
| 40 // We always use the TEMPORARY FileSystem in this test. | |
| 41 const char kFileSystemURLPrefix[] = "filesystem:http://remote/temporary/"; | |
| 42 const char kTestFileData[] = "0123456789"; | |
| 43 | |
| 44 void FillBuffer(char* buffer, size_t len) { | |
| 45 base::RandBytes(buffer, len); | |
| 46 } | |
| 47 | |
| 48 } // namespace | |
| 49 | |
| 50 class FileSystemURLRequestJobTest : public testing::Test { | |
| 51 protected: | |
| 52 FileSystemURLRequestJobTest() | |
| 53 : message_loop_(base::MessageLoop::TYPE_IO), // simulate an IO thread | |
| 54 weak_factory_(this) { | |
| 55 } | |
| 56 | |
| 57 virtual void SetUp() OVERRIDE { | |
| 58 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); | |
| 59 | |
| 60 // We use the main thread so that we can get the root path synchronously. | |
| 61 // TODO(adamk): Run this on the FILE thread we've created as well. | |
| 62 file_system_context_ = | |
| 63 CreateFileSystemContextForTesting(NULL, temp_dir_.path()); | |
| 64 | |
| 65 file_system_context_->sandbox_provider()->ValidateFileSystemRoot( | |
| 66 GURL("http://remote/"), kFileSystemTypeTemporary, true, // create | |
| 67 base::Bind(&FileSystemURLRequestJobTest::OnValidateFileSystem, | |
| 68 weak_factory_.GetWeakPtr())); | |
| 69 base::MessageLoop::current()->RunUntilIdle(); | |
| 70 | |
| 71 net::URLRequest::Deprecated::RegisterProtocolFactory( | |
| 72 "filesystem", &FileSystemURLRequestJobFactory); | |
| 73 } | |
| 74 | |
| 75 virtual void TearDown() OVERRIDE { | |
| 76 net::URLRequest::Deprecated::RegisterProtocolFactory("filesystem", NULL); | |
| 77 ClearUnusedJob(); | |
| 78 if (pending_job_) { | |
| 79 pending_job_->Kill(); | |
| 80 pending_job_ = NULL; | |
| 81 } | |
| 82 // FileReader posts a task to close the file in destructor. | |
| 83 base::MessageLoop::current()->RunUntilIdle(); | |
| 84 } | |
| 85 | |
| 86 void OnValidateFileSystem(base::PlatformFileError result) { | |
| 87 ASSERT_EQ(base::PLATFORM_FILE_OK, result); | |
| 88 } | |
| 89 | |
| 90 void TestRequestHelper(const GURL& url, | |
| 91 const net::HttpRequestHeaders* headers, | |
| 92 bool run_to_completion) { | |
| 93 delegate_.reset(new net::TestDelegate()); | |
| 94 // Make delegate_ exit the MessageLoop when the request is done. | |
| 95 delegate_->set_quit_on_complete(true); | |
| 96 delegate_->set_quit_on_redirect(true); | |
| 97 request_.reset(empty_context_.CreateRequest(url, delegate_.get())); | |
| 98 if (headers) | |
| 99 request_->SetExtraRequestHeaders(*headers); | |
| 100 ASSERT_TRUE(!job_); | |
| 101 job_ = new FileSystemURLRequestJob( | |
| 102 request_.get(), NULL, file_system_context_.get()); | |
| 103 pending_job_ = job_; | |
| 104 | |
| 105 request_->Start(); | |
| 106 ASSERT_TRUE(request_->is_pending()); // verify that we're starting async | |
| 107 if (run_to_completion) | |
| 108 base::MessageLoop::current()->Run(); | |
| 109 } | |
| 110 | |
| 111 void TestRequest(const GURL& url) { | |
| 112 TestRequestHelper(url, NULL, true); | |
| 113 } | |
| 114 | |
| 115 void TestRequestWithHeaders(const GURL& url, | |
| 116 const net::HttpRequestHeaders* headers) { | |
| 117 TestRequestHelper(url, headers, true); | |
| 118 } | |
| 119 | |
| 120 void TestRequestNoRun(const GURL& url) { | |
| 121 TestRequestHelper(url, NULL, false); | |
| 122 } | |
| 123 | |
| 124 void CreateDirectory(const base::StringPiece& dir_name) { | |
| 125 FileSystemFileUtil* file_util = file_system_context_-> | |
| 126 sandbox_provider()->GetFileUtil(kFileSystemTypeTemporary); | |
| 127 FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( | |
| 128 GURL("http://remote"), | |
| 129 kFileSystemTypeTemporary, | |
| 130 base::FilePath().AppendASCII(dir_name)); | |
| 131 | |
| 132 FileSystemOperationContext context(file_system_context_); | |
| 133 context.set_allowed_bytes_growth(1024); | |
| 134 | |
| 135 ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateDirectory( | |
| 136 &context, | |
| 137 url, | |
| 138 false /* exclusive */, | |
| 139 false /* recursive */)); | |
| 140 } | |
| 141 | |
| 142 void WriteFile(const base::StringPiece& file_name, | |
| 143 const char* buf, int buf_size) { | |
| 144 FileSystemFileUtil* file_util = file_system_context_-> | |
| 145 sandbox_provider()->GetFileUtil(kFileSystemTypeTemporary); | |
| 146 FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL( | |
| 147 GURL("http://remote"), | |
| 148 kFileSystemTypeTemporary, | |
| 149 base::FilePath().AppendASCII(file_name)); | |
| 150 | |
| 151 FileSystemOperationContext context(file_system_context_); | |
| 152 context.set_allowed_bytes_growth(1024); | |
| 153 | |
| 154 base::PlatformFile handle = base::kInvalidPlatformFileValue; | |
| 155 bool created = false; | |
| 156 ASSERT_EQ(base::PLATFORM_FILE_OK, file_util->CreateOrOpen( | |
| 157 &context, | |
| 158 url, | |
| 159 base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, | |
| 160 &handle, | |
| 161 &created)); | |
| 162 EXPECT_TRUE(created); | |
| 163 ASSERT_NE(base::kInvalidPlatformFileValue, handle); | |
| 164 ASSERT_EQ(buf_size, | |
| 165 base::WritePlatformFile(handle, 0 /* offset */, buf, buf_size)); | |
| 166 base::ClosePlatformFile(handle); | |
| 167 } | |
| 168 | |
| 169 GURL CreateFileSystemURL(const std::string& path) { | |
| 170 return GURL(kFileSystemURLPrefix + path); | |
| 171 } | |
| 172 | |
| 173 static net::URLRequestJob* FileSystemURLRequestJobFactory( | |
| 174 net::URLRequest* request, | |
| 175 net::NetworkDelegate* network_delegate, | |
| 176 const std::string& scheme) { | |
| 177 DCHECK(job_); | |
| 178 net::URLRequestJob* temp = job_; | |
| 179 job_ = NULL; | |
| 180 return temp; | |
| 181 } | |
| 182 | |
| 183 static void ClearUnusedJob() { | |
| 184 if (job_) { | |
| 185 scoped_refptr<net::URLRequestJob> deleter = job_; | |
| 186 job_ = NULL; | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 // Put the message loop at the top, so that it's the last thing deleted. | |
| 191 base::MessageLoop message_loop_; | |
| 192 | |
| 193 base::ScopedTempDir temp_dir_; | |
| 194 scoped_refptr<FileSystemContext> file_system_context_; | |
| 195 base::WeakPtrFactory<FileSystemURLRequestJobTest> weak_factory_; | |
| 196 | |
| 197 net::URLRequestContext empty_context_; | |
| 198 | |
| 199 // NOTE: order matters, request must die before delegate | |
| 200 scoped_ptr<net::TestDelegate> delegate_; | |
| 201 scoped_ptr<net::URLRequest> request_; | |
| 202 | |
| 203 scoped_refptr<net::URLRequestJob> pending_job_; | |
| 204 static net::URLRequestJob* job_; | |
| 205 }; | |
| 206 | |
| 207 // static | |
| 208 net::URLRequestJob* FileSystemURLRequestJobTest::job_ = NULL; | |
| 209 | |
| 210 namespace { | |
| 211 | |
| 212 TEST_F(FileSystemURLRequestJobTest, FileTest) { | |
| 213 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); | |
| 214 TestRequest(CreateFileSystemURL("file1.dat")); | |
| 215 | |
| 216 ASSERT_FALSE(request_->is_pending()); | |
| 217 EXPECT_EQ(1, delegate_->response_started_count()); | |
| 218 EXPECT_FALSE(delegate_->received_data_before_response()); | |
| 219 EXPECT_EQ(kTestFileData, delegate_->data_received()); | |
| 220 EXPECT_EQ(200, request_->GetResponseCode()); | |
| 221 std::string cache_control; | |
| 222 request_->GetResponseHeaderByName("cache-control", &cache_control); | |
| 223 EXPECT_EQ("no-cache", cache_control); | |
| 224 } | |
| 225 | |
| 226 TEST_F(FileSystemURLRequestJobTest, FileTestFullSpecifiedRange) { | |
| 227 const size_t buffer_size = 4000; | |
| 228 scoped_ptr<char[]> buffer(new char[buffer_size]); | |
| 229 FillBuffer(buffer.get(), buffer_size); | |
| 230 WriteFile("bigfile", buffer.get(), buffer_size); | |
| 231 | |
| 232 const size_t first_byte_position = 500; | |
| 233 const size_t last_byte_position = buffer_size - first_byte_position; | |
| 234 std::string partial_buffer_string(buffer.get() + first_byte_position, | |
| 235 buffer.get() + last_byte_position + 1); | |
| 236 | |
| 237 net::HttpRequestHeaders headers; | |
| 238 headers.SetHeader(net::HttpRequestHeaders::kRange, | |
| 239 base::StringPrintf( | |
| 240 "bytes=%" PRIuS "-%" PRIuS, | |
| 241 first_byte_position, last_byte_position)); | |
| 242 TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers); | |
| 243 | |
| 244 ASSERT_FALSE(request_->is_pending()); | |
| 245 EXPECT_EQ(1, delegate_->response_started_count()); | |
| 246 EXPECT_FALSE(delegate_->received_data_before_response()); | |
| 247 EXPECT_TRUE(partial_buffer_string == delegate_->data_received()); | |
| 248 } | |
| 249 | |
| 250 TEST_F(FileSystemURLRequestJobTest, FileTestHalfSpecifiedRange) { | |
| 251 const size_t buffer_size = 4000; | |
| 252 scoped_ptr<char[]> buffer(new char[buffer_size]); | |
| 253 FillBuffer(buffer.get(), buffer_size); | |
| 254 WriteFile("bigfile", buffer.get(), buffer_size); | |
| 255 | |
| 256 const size_t first_byte_position = 500; | |
| 257 std::string partial_buffer_string(buffer.get() + first_byte_position, | |
| 258 buffer.get() + buffer_size); | |
| 259 | |
| 260 net::HttpRequestHeaders headers; | |
| 261 headers.SetHeader(net::HttpRequestHeaders::kRange, | |
| 262 base::StringPrintf("bytes=%" PRIuS "-", | |
| 263 first_byte_position)); | |
| 264 TestRequestWithHeaders(CreateFileSystemURL("bigfile"), &headers); | |
| 265 ASSERT_FALSE(request_->is_pending()); | |
| 266 EXPECT_EQ(1, delegate_->response_started_count()); | |
| 267 EXPECT_FALSE(delegate_->received_data_before_response()); | |
| 268 // Don't use EXPECT_EQ, it will print out a lot of garbage if check failed. | |
| 269 EXPECT_TRUE(partial_buffer_string == delegate_->data_received()); | |
| 270 } | |
| 271 | |
| 272 | |
| 273 TEST_F(FileSystemURLRequestJobTest, FileTestMultipleRangesNotSupported) { | |
| 274 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); | |
| 275 net::HttpRequestHeaders headers; | |
| 276 headers.SetHeader(net::HttpRequestHeaders::kRange, | |
| 277 "bytes=0-5,10-200,200-300"); | |
| 278 TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers); | |
| 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, RangeOutOfBounds) { | |
| 285 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); | |
| 286 net::HttpRequestHeaders headers; | |
| 287 headers.SetHeader(net::HttpRequestHeaders::kRange, "bytes=500-1000"); | |
| 288 TestRequestWithHeaders(CreateFileSystemURL("file1.dat"), &headers); | |
| 289 | |
| 290 ASSERT_FALSE(request_->is_pending()); | |
| 291 EXPECT_TRUE(delegate_->request_failed()); | |
| 292 EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, | |
| 293 request_->status().error()); | |
| 294 } | |
| 295 | |
| 296 TEST_F(FileSystemURLRequestJobTest, FileDirRedirect) { | |
| 297 CreateDirectory("dir"); | |
| 298 TestRequest(CreateFileSystemURL("dir")); | |
| 299 | |
| 300 EXPECT_EQ(1, delegate_->received_redirect_count()); | |
| 301 EXPECT_TRUE(request_->status().is_success()); | |
| 302 EXPECT_FALSE(delegate_->request_failed()); | |
| 303 | |
| 304 // We've deferred the redirect; now cancel the request to avoid following it. | |
| 305 request_->Cancel(); | |
| 306 base::MessageLoop::current()->Run(); | |
| 307 } | |
| 308 | |
| 309 TEST_F(FileSystemURLRequestJobTest, InvalidURL) { | |
| 310 TestRequest(GURL("filesystem:/foo/bar/baz")); | |
| 311 ASSERT_FALSE(request_->is_pending()); | |
| 312 EXPECT_TRUE(delegate_->request_failed()); | |
| 313 EXPECT_EQ(net::ERR_INVALID_URL, request_->status().error()); | |
| 314 } | |
| 315 | |
| 316 TEST_F(FileSystemURLRequestJobTest, NoSuchRoot) { | |
| 317 TestRequest(GURL("filesystem:http://remote/persistent/somefile")); | |
| 318 ASSERT_FALSE(request_->is_pending()); | |
| 319 EXPECT_TRUE(delegate_->request_failed()); | |
| 320 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); | |
| 321 } | |
| 322 | |
| 323 TEST_F(FileSystemURLRequestJobTest, NoSuchFile) { | |
| 324 TestRequest(CreateFileSystemURL("somefile")); | |
| 325 ASSERT_FALSE(request_->is_pending()); | |
| 326 EXPECT_TRUE(delegate_->request_failed()); | |
| 327 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request_->status().error()); | |
| 328 } | |
| 329 | |
| 330 TEST_F(FileSystemURLRequestJobTest, Cancel) { | |
| 331 WriteFile("file1.dat", kTestFileData, arraysize(kTestFileData) - 1); | |
| 332 TestRequestNoRun(CreateFileSystemURL("file1.dat")); | |
| 333 | |
| 334 // Run StartAsync() and only StartAsync(). | |
| 335 base::MessageLoop::current()->DeleteSoon(FROM_HERE, request_.release()); | |
| 336 base::MessageLoop::current()->RunUntilIdle(); | |
| 337 // If we get here, success! we didn't crash! | |
| 338 } | |
| 339 | |
| 340 TEST_F(FileSystemURLRequestJobTest, GetMimeType) { | |
| 341 const char kFilename[] = "hoge.html"; | |
| 342 | |
| 343 std::string mime_type_direct; | |
| 344 base::FilePath::StringType extension = | |
| 345 base::FilePath().AppendASCII(kFilename).Extension(); | |
| 346 if (!extension.empty()) | |
| 347 extension = extension.substr(1); | |
| 348 EXPECT_TRUE(net::GetWellKnownMimeTypeFromExtension( | |
| 349 extension, &mime_type_direct)); | |
| 350 | |
| 351 TestRequest(CreateFileSystemURL(kFilename)); | |
| 352 | |
| 353 std::string mime_type_from_job; | |
| 354 request_->GetMimeType(&mime_type_from_job); | |
| 355 EXPECT_EQ(mime_type_direct, mime_type_from_job); | |
| 356 } | |
| 357 | |
| 358 } // namespace | |
| 359 } // namespace fileapi | |
| OLD | NEW |