diff options
-rw-r--r-- | net/SConscript | 5 | ||||
-rw-r--r-- | net/base/file_input_stream.h | 92 | ||||
-rw-r--r-- | net/base/file_input_stream_posix.cc | 55 | ||||
-rw-r--r-- | net/base/file_input_stream_unittest.cc | 205 | ||||
-rw-r--r-- | net/base/file_input_stream_win.cc | 232 | ||||
-rw-r--r-- | net/base/net_error_list.h | 3 | ||||
-rw-r--r-- | net/base/upload_data_stream.cc | 97 | ||||
-rw-r--r-- | net/base/upload_data_stream.h | 11 | ||||
-rw-r--r-- | net/build/net.vcproj | 12 | ||||
-rw-r--r-- | net/build/net_unittests.vcproj | 4 | ||||
-rw-r--r-- | net/net.xcodeproj/project.pbxproj | 4 | ||||
-rw-r--r-- | net/url_request/url_request_file_job.cc | 301 | ||||
-rw-r--r-- | net/url_request/url_request_file_job.h | 54 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.cc | 19 | ||||
-rw-r--r-- | net/url_request/url_request_unittest.h | 3 |
15 files changed, 779 insertions, 318 deletions
diff --git a/net/SConscript b/net/SConscript index 1df0dac..c8f879d 100644 --- a/net/SConscript +++ b/net/SConscript @@ -100,7 +100,6 @@ if env['PLATFORM'] == 'win32': 'base/upload_data_stream.cc', 'base/wininet_util.cc', 'base/winsock_init.cc', - 'base/x509_certificate_win.cc', 'http/http_network_layer.cc', 'http/http_network_transaction.cc', 'http/http_transaction_winhttp.cc', @@ -118,8 +117,10 @@ if env['PLATFORM'] == 'win32': if env['PLATFORM'] == 'win32': input_files.extend([ + 'base/file_input_stream_win.cc', 'base/net_util_win.cc', 'base/platform_mime_util_win.cc', + 'base/x509_certificate_win.cc', 'disk_cache/cache_util_win.cc', 'disk_cache/file_win.cc', 'disk_cache/mapped_file_win.cc', @@ -140,6 +141,7 @@ if env['PLATFORM'] == 'posix': if env['PLATFORM'] in ('darwin', 'posix'): input_files.extend([ + 'base/file_input_stream_posix.cc', 'base/net_util_posix.cc', 'base/tcp_client_socket_libevent.cc', 'disk_cache/cache_util_posix.cc', @@ -258,6 +260,7 @@ unittest_files = [ if env['PLATFORM'] == 'win32': unittest_files.extend([ 'base/directory_lister_unittest.cc', + 'base/file_input_stream_unittest.cc', 'base/sdch_filter_unitest.cc', 'base/ssl_config_service_unittest.cc', 'base/ssl_client_socket_unittest.cc', diff --git a/net/base/file_input_stream.h b/net/base/file_input_stream.h new file mode 100644 index 0000000..efebadc --- /dev/null +++ b/net/base/file_input_stream.h @@ -0,0 +1,92 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +// This file defines FileInputStream, a basic interface for reading files +// synchronously or asynchronously with support for seeking to an offset. + +#ifndef NET_BASE_FILE_INPUT_STREAM_H_ +#define NET_BASE_FILE_INPUT_STREAM_H_ + +#include "net/base/completion_callback.h" + +#if defined(OS_WIN) +typedef void* HANDLE; +#endif + +namespace net { + +// TODO(darin): Move this to a more generic location. +enum Whence { + FROM_BEGIN, + FROM_CURRENT, + FROM_END +}; + +class FileInputStream { + public: + FileInputStream(); + ~FileInputStream(); + + // Call this method to close the FileInputStream. It is OK to call Close + // multiple times. Redundant calls are ignored. + void Close(); + + // Call this method to open the FileInputStream. The remaining methods + // cannot be used unless this method returns OK. If the file cannot be + // opened then an error code is returned. + // + // NOTE: The file input stream is opened with non-exclusive access to the + // underlying file. + // + int Open(const std::wstring& path, bool asynchronous_mode); + + // Returns true if Open succeeded and Close has not been called. + bool IsOpen() const; + + // Adjust the position from where data is read. Upon success, the stream + // position relative to the start of the file is returned. Otherwise, an + // error code is returned. It is not valid to call Seek while a Read call + // has a pending completion. + int64 Seek(Whence whence, int64 offset); + + // Returns the number of bytes available to read from the current stream + // position until the end of the file. Otherwise, an error code is returned. + int64 Available(); + + // Call this method to read data from the current stream position. Up to + // buf_len bytes will be copied into buf. (In other words, partial reads are + // allowed.) Returns the number of bytes copied, 0 if at end-of-file, or an + // error code if the operation could not be performed. + // + // If opened with |asynchronous_mode| set to true, then a non-null callback + // must be passed to this method. In asynchronous mode, if the read could + // not complete synchronously, then ERR_IO_PENDING is returned, and the + // callback will be notified on the current thread (via the MessageLoop) when + // the read has completed. + // + // In the case of an asychronous read, the memory pointed to by |buf| must + // remain valid until the callback is notified. However, it is valid to + // destroy or close the file stream while there is an asynchronous read in + // progress. That will cancel the read and allow the buffer to be freed. + // + int Read(char* buf, int buf_len, CompletionCallback* callback); + + private: + class AsyncContext; + friend class AsyncContext; + + // This member is used to support asynchronous reads. It is non-null when + // the FileInputStream was opened with asynchronous_mode set to true. + scoped_ptr<AsyncContext> async_context_; + +#if defined(OS_WIN) + HANDLE handle_; +#endif + + DISALLOW_COPY_AND_ASSIGN(FileInputStream); +}; + +} // namespace net + +#endif // NET_BASE_FILE_INPUT_STREAM_H_ diff --git a/net/base/file_input_stream_posix.cc b/net/base/file_input_stream_posix.cc new file mode 100644 index 0000000..335805c --- /dev/null +++ b/net/base/file_input_stream_posix.cc @@ -0,0 +1,55 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "net/base/file_input_stream.h" + +#include "base/logging.h" +#include "net/base/net_errors.h" + +namespace net { + +// FileInputStream::AsyncContext ---------------------------------------------- + +class FileInputStream::AsyncContext { +}; + +// FileInputStream ------------------------------------------------------------ + +FileInputStream::FileInputStream() { +} + +FileInputStream::~FileInputStream() { + Close(); +} + +void FileInputStream::Close() { +} + +int FileInputStream::Open(const std::wstring& path, bool asynchronous_mode) { + NOTIMPLEMENTED(); + return ERR_FAILED; +} + +bool FileInputStream::IsOpen() const { + NOTIMPLEMENTED(); + return false; +} + +int64 FileInputStream::Seek(Whence whence, int64 offset) { + NOTIMPLEMENTED(); + return ERR_FAILED; +} + +int64 FileInputStream::Available() { + NOTIMPLEMENTED(); + return ERR_FAILED; +} + +int FileInputStream::Read( + char* buf, int buf_len, CompletionCallback* callback) { + NOTIMPLEMENTED(); + return ERR_FAILED; +} + +} // namespace net diff --git a/net/base/file_input_stream_unittest.cc b/net/base/file_input_stream_unittest.cc new file mode 100644 index 0000000..b259de8 --- /dev/null +++ b/net/base/file_input_stream_unittest.cc @@ -0,0 +1,205 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/platform_test.h" +#include "net/base/file_input_stream.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const char kTestData[] = "0123456789"; + +class FileInputStreamTest : public PlatformTest { + public: + virtual void SetUp() { + PlatformTest::SetUp(); + + file_util::CreateTemporaryFileName(&temp_file_path_); + file_util::WriteFile(temp_file_path_, kTestData, arraysize(kTestData)-1); + } + virtual void TearDown() { + file_util::Delete(temp_file_path_, false); + + PlatformTest::TearDown(); + } + const std::wstring temp_file_path() const { return temp_file_path_; } + private: + std::wstring temp_file_path_; +}; + +TEST_F(FileInputStreamTest, BasicOpenClose) { + net::FileInputStream stream; + int rv = stream.Open(temp_file_path(), false); + EXPECT_EQ(net::OK, rv); +} + +TEST_F(FileInputStreamTest, UseClosedStream) { + net::FileInputStream stream; + + EXPECT_FALSE(stream.IsOpen()); + + // Try seeking... + int64 new_offset = stream.Seek(net::FROM_BEGIN, 5); + EXPECT_EQ(net::ERR_UNEXPECTED, new_offset); + + // Try available... + int64 avail = stream.Available(); + EXPECT_EQ(net::ERR_UNEXPECTED, avail); + + // Try reading... + char buf[10]; + int rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_EQ(net::ERR_UNEXPECTED, rv); +} + +TEST_F(FileInputStreamTest, BasicRead) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileInputStream stream; + int rv = stream.Open(temp_file_path(), false); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); +} + +TEST_F(FileInputStreamTest, AsyncRead) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileInputStream stream; + int rv = stream.Open(temp_file_path(), true); + EXPECT_EQ(net::OK, rv); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size, total_bytes_avail); + + TestCompletionCallback callback; + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size, total_bytes_read); + EXPECT_TRUE(data_read == kTestData); +} + +TEST_F(FileInputStreamTest, BasicRead_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileInputStream stream; + int rv = stream.Open(temp_file_path(), false); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size - kOffset, total_bytes_avail); + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), NULL); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size - kOffset, total_bytes_read); + EXPECT_TRUE(data_read == kTestData + kOffset); +} + +TEST_F(FileInputStreamTest, AsyncRead_FromOffset) { + int64 file_size; + bool ok = file_util::GetFileSize(temp_file_path(), &file_size); + EXPECT_TRUE(ok); + + net::FileInputStream stream; + int rv = stream.Open(temp_file_path(), true); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + int64 total_bytes_avail = stream.Available(); + EXPECT_EQ(file_size - kOffset, total_bytes_avail); + + TestCompletionCallback callback; + + int64 total_bytes_read = 0; + + std::string data_read; + for (;;) { + char buf[4]; + rv = stream.Read(buf, sizeof(buf), &callback); + if (rv == net::ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_LE(0, rv); + if (rv <= 0) + break; + total_bytes_read += rv; + data_read.append(buf, rv); + } + EXPECT_EQ(file_size - kOffset, total_bytes_read); + EXPECT_TRUE(data_read == kTestData + kOffset); +} + +TEST_F(FileInputStreamTest, SeekAround) { + net::FileInputStream stream; + int rv = stream.Open(temp_file_path(), true); + EXPECT_EQ(net::OK, rv); + + const int64 kOffset = 3; + int64 new_offset = stream.Seek(net::FROM_BEGIN, kOffset); + EXPECT_EQ(kOffset, new_offset); + + new_offset = stream.Seek(net::FROM_CURRENT, kOffset); + EXPECT_EQ(2 * kOffset, new_offset); + + new_offset = stream.Seek(net::FROM_CURRENT, -kOffset); + EXPECT_EQ(kOffset, new_offset); + + const int kTestDataLen = arraysize(kTestData) - 1; + + new_offset = stream.Seek(net::FROM_END, -kTestDataLen); + EXPECT_EQ(0, new_offset); +} diff --git a/net/base/file_input_stream_win.cc b/net/base/file_input_stream_win.cc new file mode 100644 index 0000000..5eadc9d --- /dev/null +++ b/net/base/file_input_stream_win.cc @@ -0,0 +1,232 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. Use of this +// source code is governed by a BSD-style license that can be found in the +// LICENSE file. + +#include "net/base/file_input_stream.h" + +#include <windows.h> + +#include "base/logging.h" +#include "base/message_loop.h" +#include "net/base/net_errors.h" + +namespace net { + +// Ensure that we can just use our Whence values directly. +COMPILE_ASSERT(FROM_BEGIN == FILE_BEGIN, bad_whence_begin); +COMPILE_ASSERT(FROM_CURRENT == FILE_CURRENT, bad_whence_current); +COMPILE_ASSERT(FROM_END == FILE_END, bad_whence_end); + +static void SetOffset(OVERLAPPED* overlapped, const LARGE_INTEGER& offset) { + overlapped->Offset = offset.LowPart; + overlapped->OffsetHigh = offset.HighPart; +} + +static void IncrementOffset(OVERLAPPED* overlapped, DWORD count) { + LARGE_INTEGER offset; + offset.LowPart = overlapped->Offset; + offset.HighPart = overlapped->OffsetHigh; + offset.QuadPart += static_cast<LONGLONG>(count); + SetOffset(overlapped, offset); +} + +static int MapErrorCode(DWORD err) { + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + return ERR_FILE_NOT_FOUND; + case ERROR_ACCESS_DENIED: + return ERR_ACCESS_DENIED; + case ERROR_SUCCESS: + return OK; + default: + LOG(WARNING) << "Unknown error " << err << " mapped to net::ERR_FAILED"; + return ERR_FAILED; + } +} + +// FileInputStream::AsyncContext ---------------------------------------------- + +class FileInputStream::AsyncContext : public MessageLoopForIO::Watcher { + public: + AsyncContext(FileInputStream* owner) + : owner_(owner), overlapped_(), callback_(NULL) { + overlapped_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + } + + ~AsyncContext() { + if (callback_) + MessageLoopForIO::current()->WatchObject(overlapped_.hEvent, NULL); + CloseHandle(overlapped_.hEvent); + } + + void IOCompletionIsPending(CompletionCallback* callback); + + OVERLAPPED* overlapped() { return &overlapped_; } + CompletionCallback* callback() const { return callback_; } + + private: + // MessageLoopForIO::Watcher implementation: + virtual void OnObjectSignaled(HANDLE object); + + FileInputStream* owner_; + OVERLAPPED overlapped_; + CompletionCallback* callback_; +}; + +void FileInputStream::AsyncContext::IOCompletionIsPending( + CompletionCallback* callback) { + DCHECK(!callback_); + callback_ = callback; + + MessageLoopForIO::current()->WatchObject(overlapped_.hEvent, this); +} + +void FileInputStream::AsyncContext::OnObjectSignaled(HANDLE object) { + DCHECK(overlapped_.hEvent == object); + DCHECK(callback_); + + MessageLoopForIO::current()->WatchObject(overlapped_.hEvent, NULL); + + HANDLE handle = owner_->handle_; + + int result; + + DWORD bytes_read; + if (!GetOverlappedResult(handle, &overlapped_, &bytes_read, FALSE)) { + DWORD err = GetLastError(); + if (err == ERROR_HANDLE_EOF) { + result = OK; // Successfully read all data. + } else { + result = MapErrorCode(err); + } + } else { + IncrementOffset(&overlapped_, bytes_read); + result = static_cast<int>(bytes_read); + } + + CompletionCallback* temp = NULL; + std::swap(temp, callback_); + temp->Run(result); +} + +// FileInputStream ------------------------------------------------------------ + +FileInputStream::FileInputStream() : handle_(INVALID_HANDLE_VALUE) { +} + +FileInputStream::~FileInputStream() { + Close(); +} + +void FileInputStream::Close() { + if (handle_ != INVALID_HANDLE_VALUE) { + CloseHandle(handle_); + handle_ = INVALID_HANDLE_VALUE; + } + async_context_.reset(); +} + +int FileInputStream::Open(const std::wstring& path, bool asynchronous_mode) { + if (IsOpen()) { + DLOG(FATAL) << "File is already open!"; + return ERR_UNEXPECTED; + } + + // Optimize for streaming, not seeking. If someone does a lot of random + // access operations, then we should consider revising this. + DWORD create_file_flags = FILE_FLAG_SEQUENTIAL_SCAN; + + if (asynchronous_mode) + create_file_flags |= FILE_FLAG_OVERLAPPED; + + handle_ = + CreateFile(path.c_str(), GENERIC_READ | SYNCHRONIZE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, create_file_flags, NULL); + if (handle_ == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + LOG(WARNING) << "Failed to open file: " << error; + return MapErrorCode(error); + } + + if (asynchronous_mode) + async_context_.reset(new AsyncContext(this)); + + return OK; +} + +bool FileInputStream::IsOpen() const { + return handle_ != INVALID_HANDLE_VALUE; +} + +int64 FileInputStream::Seek(Whence whence, int64 offset) { + if (!IsOpen()) + return ERR_UNEXPECTED; + DCHECK(!async_context_.get() || !async_context_->callback()); + + LARGE_INTEGER distance, result; + distance.QuadPart = offset; + DWORD move_method = static_cast<DWORD>(whence); + if (!SetFilePointerEx(handle_, distance, &result, move_method)) { + DWORD error = GetLastError(); + LOG(WARNING) << "SetFilePointerEx failed: " << error; + return MapErrorCode(error); + } + if (async_context_.get()) + SetOffset(async_context_->overlapped(), result); + return result.QuadPart; +} + +int64 FileInputStream::Available() { + if (!IsOpen()) + return ERR_UNEXPECTED; + + int64 cur_pos = Seek(FROM_CURRENT, 0); + if (cur_pos < 0) + return cur_pos; + + LARGE_INTEGER file_size; + if (!GetFileSizeEx(handle_, &file_size)) { + DWORD error = GetLastError(); + LOG(WARNING) << "GetFileSizeEx failed: " << error; + return MapErrorCode(error); + } + + return file_size.QuadPart - cur_pos; +} + +int FileInputStream::Read( + char* buf, int buf_len, CompletionCallback* callback) { + if (!IsOpen()) + return ERR_UNEXPECTED; + + OVERLAPPED* overlapped = NULL; + if (async_context_.get()) { + DCHECK(!async_context_->callback()); + overlapped = async_context_->overlapped(); + } + + int rv; + + DWORD bytes_read; + if (!ReadFile(handle_, buf, buf_len, &bytes_read, overlapped)) { + DWORD error = GetLastError(); + if (async_context_.get() && error == ERROR_IO_PENDING) { + async_context_->IOCompletionIsPending(callback); + rv = ERR_IO_PENDING; + } else if (error == ERROR_HANDLE_EOF) { + rv = 0; // Report EOF by returning 0 bytes read. + } else { + LOG(WARNING) << "ReadFile failed: " << error; + rv = MapErrorCode(error); + } + } else { + if (overlapped) + IncrementOffset(overlapped, bytes_read); + rv = static_cast<int>(bytes_read); + } + return rv; +} + +} // namespace net diff --git a/net/base/net_error_list.h b/net/base/net_error_list.h index 9fbb942..e146c7a 100644 --- a/net/base/net_error_list.h +++ b/net/base/net_error_list.h @@ -35,6 +35,9 @@ NET_ERROR(FILE_TOO_BIG, -8) // invalid assumption. NET_ERROR(UNEXPECTED, -9) +// Permission to access a resource was denied. +NET_ERROR(ACCESS_DENIED, -10) + // A connection was closed (corresponding to a TCP FIN). NET_ERROR(CONNECTION_CLOSED, -100) diff --git a/net/base/upload_data_stream.cc b/net/base/upload_data_stream.cc index 702aeca..df5ef0f 100644 --- a/net/base/upload_data_stream.cc +++ b/net/base/upload_data_stream.cc @@ -5,24 +5,18 @@ #include "net/base/upload_data_stream.h" #include "base/logging.h" +#include "net/base/net_errors.h" namespace net { UploadDataStream::UploadDataStream(const UploadData* data) : data_(data), -#if defined(OS_WIN) - next_element_handle_(INVALID_HANDLE_VALUE), -#endif total_size_(data->GetContentLength()) { Reset(); FillBuf(); } UploadDataStream::~UploadDataStream() { -#if defined(OS_WIN) - if (next_element_handle_ != INVALID_HANDLE_VALUE) - CloseHandle(next_element_handle_); -#endif } void UploadDataStream::DidConsume(size_t num_bytes) { @@ -38,12 +32,7 @@ void UploadDataStream::DidConsume(size_t num_bytes) { } void UploadDataStream::Reset() { -#if defined(OS_WIN) - if (next_element_handle_ != INVALID_HANDLE_VALUE) { - CloseHandle(next_element_handle_); - next_element_handle_ = INVALID_HANDLE_VALUE; - } -#endif + next_element_stream_.Close(); buf_len_ = 0; next_element_ = data_->elements().begin(); next_element_offset_ = 0; @@ -58,9 +47,11 @@ void UploadDataStream::FillBuf() { while (buf_len_ < kBufSize && next_element_ != end) { bool advance_to_next_element = false; + const UploadData::Element& element = *next_element_; + size_t size_remaining = kBufSize - buf_len_; - if ((*next_element_).type() == UploadData::TYPE_BYTES) { - const std::vector<char>& d = (*next_element_).bytes(); + if (element.type() == UploadData::TYPE_BYTES) { + const std::vector<char>& d = element.bytes(); size_t count = d.size() - next_element_offset_; size_t bytes_copied = std::min(count, size_remaining); @@ -74,69 +65,43 @@ void UploadDataStream::FillBuf() { next_element_offset_ += bytes_copied; } } else { - DCHECK((*next_element_).type() == UploadData::TYPE_FILE); - -#if defined(OS_WIN) - if (next_element_handle_ == INVALID_HANDLE_VALUE) { - next_element_handle_ = CreateFile((*next_element_).file_path().c_str(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); + DCHECK(element.type() == UploadData::TYPE_FILE); + + if (!next_element_stream_.IsOpen()) { + int rv = next_element_stream_.Open(element.file_path(), false); // If the file does not exist, that's technically okay.. we'll just // upload an empty file. This is for consistency with Mozilla. - DLOG_IF(WARNING, next_element_handle_ == INVALID_HANDLE_VALUE) << - "Unable to open file \"" << (*next_element_).file_path() << - "\" for reading: " << GetLastError(); - - next_element_remaining_ = (*next_element_).file_range_length(); - - if ((*next_element_).file_range_offset()) { - LARGE_INTEGER offset; - offset.QuadPart = (*next_element_).file_range_offset(); - if (!SetFilePointerEx(next_element_handle_, offset, - NULL, FILE_BEGIN)) { - DLOG(WARNING) << - "Unable to set file position for file \"" << - (*next_element_).file_path() << "\": " << GetLastError(); - next_element_remaining_ = 0; + DLOG_IF(WARNING, rv != OK) << "Failed to open \"" << + element.file_path() << "\" for reading: " << rv; + + next_element_remaining_ = 0; // Default to reading nothing. + if (rv == OK) { + uint64 offset = element.file_range_offset(); + if (offset && next_element_stream_.Seek(FROM_BEGIN, offset) < 0) { + DLOG(WARNING) << "Failed to seek \"" << element.file_path() << + "\" to offset: " << offset; + } else { + next_element_remaining_ = element.file_range_length(); } } } - // ReadFile will happily fail if given an invalid handle. - BOOL ok = FALSE; - DWORD bytes_read = 0; - uint64 amount_to_read = std::min(static_cast<uint64>(size_remaining), - next_element_remaining_); - if ((amount_to_read > 0) && - (ok = ReadFile(next_element_handle_, buf_ + buf_len_, - static_cast<DWORD>(amount_to_read), &bytes_read, - NULL))) { - buf_len_ += bytes_read; - next_element_remaining_ -= bytes_read; - } - - if (!ok || bytes_read == 0) + int rv = 0; + int count = static_cast<int>(std::min( + static_cast<uint64>(size_remaining), next_element_remaining_)); + if (count > 0 && + (rv = next_element_stream_.Read(buf_ + buf_len_, count, NULL)) > 0) { + buf_len_ += rv; + next_element_remaining_ -= rv; + } else { advance_to_next_element = true; -#elif defined(OS_POSIX) - // TODO(pinkerton): unify the file upload handling for all platforms once - // we have a cross-platform file representation. There shouldn't be any - // difference among them. - NOTIMPLEMENTED(); - advance_to_next_element = true; -#endif + } } if (advance_to_next_element) { ++next_element_; next_element_offset_ = 0; -#if defined(OS_WIN) - if (next_element_handle_ != INVALID_HANDLE_VALUE) { - CloseHandle(next_element_handle_); - next_element_handle_ = INVALID_HANDLE_VALUE; - } -#endif + next_element_stream_.Close(); } } } diff --git a/net/base/upload_data_stream.h b/net/base/upload_data_stream.h index f31cb80..9d22d2e 100644 --- a/net/base/upload_data_stream.h +++ b/net/base/upload_data_stream.h @@ -5,7 +5,7 @@ #ifndef NET_BASE_UPLOAD_DATA_STREAM_H_ #define NET_BASE_UPLOAD_DATA_STREAM_H_ -#include "build/build_config.h" +#include "net/base/file_input_stream.h" #include "net/base/upload_data.h" namespace net { @@ -51,12 +51,9 @@ class UploadDataStream { // a TYPE_BYTES element. size_t next_element_offset_; -#if defined(OS_WIN) - // A handle to the currently open file (or INVALID_HANDLE_VALUE) for - // next_element_ if the next element is a TYPE_FILE element. - // TODO(pinkerton): when we get a cross-platform file class, replace this - HANDLE next_element_handle_; -#endif + // A stream to the currently open file, for next_element_ if the next element + // is a TYPE_FILE element. + FileInputStream next_element_stream_; // The number of bytes remaining to be read from the currently open file // if the next element is of TYPE_FILE. diff --git a/net/build/net.vcproj b/net/build/net.vcproj index 938c1e4..b56b05f 100644 --- a/net/build/net.vcproj +++ b/net/build/net.vcproj @@ -269,6 +269,14 @@ > </File> <File + RelativePath="..\base\file_input_stream.h" + > + </File> + <File + RelativePath="..\base\file_input_stream_win.cc" + > + </File> + <File RelativePath="..\base\filter.cc" > </File> @@ -497,11 +505,11 @@ > </File> <File - RelativePath="..\base\x509_certificate_win.cc" + RelativePath="..\base\x509_certificate.h" > </File> <File - RelativePath="..\base\x509_certificate.h" + RelativePath="..\base\x509_certificate_win.cc" > </File> </Filter> diff --git a/net/build/net_unittests.vcproj b/net/build/net_unittests.vcproj index f2fbb7d..21550c1 100644 --- a/net/build/net_unittests.vcproj +++ b/net/build/net_unittests.vcproj @@ -323,6 +323,10 @@ > </File> <File + RelativePath="..\base\file_input_stream_unittest.cc" + > + </File> + <File RelativePath="..\base\gzip_filter_unittest.cc" > </File> diff --git a/net/net.xcodeproj/project.pbxproj b/net/net.xcodeproj/project.pbxproj index cab4aa1..c36167d 100644 --- a/net/net.xcodeproj/project.pbxproj +++ b/net/net.xcodeproj/project.pbxproj @@ -145,6 +145,7 @@ 825C2FCC0E5C968B00FDEAB7 /* ev_root_ca_metadata.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32BE0E5A181C00A747DB /* ev_root_ca_metadata.cc */; }; 827E139D0E81611D00183614 /* x509_certificate_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32800E5A181C00A747DB /* x509_certificate_mac.cc */; }; 82ECB3090E5B651D00A913E3 /* mime_sniffer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED32AD0E5A181C00A747DB /* mime_sniffer.cc */; }; + 93D11DCE0E91463000C36437 /* file_input_stream_posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 93D11DCD0E91463000C36437 /* file_input_stream_posix.cc */; }; B5F622260E805FC40076681A /* url_request_job_manager.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED33A30E5A198600A747DB /* url_request_job_manager.cc */; }; BAA46E3B0E5CE99A00E77460 /* net_util_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BED329F0E5A181C00A747DB /* net_util_unittest.cc */; }; DFEE18270E882E3600666107 /* stats_histogram.cc in Sources */ = {isa = PBXBuildFile; fileRef = DFEE18250E882E3600666107 /* stats_histogram.cc */; }; @@ -652,6 +653,7 @@ 82113A1C0E8434EE00E3848F /* x509_certificate_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x509_certificate_unittest.cc; sourceTree = "<group>"; }; 82113A270E84360200E3848F /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = "<group>"; }; 82113BBC0E892E5800E3848F /* x509_certificate.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = x509_certificate.cc; sourceTree = "<group>"; }; + 93D11DCD0E91463000C36437 /* file_input_stream_posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_input_stream_posix.cc; sourceTree = "<group>"; }; DFEE18250E882E3600666107 /* stats_histogram.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stats_histogram.cc; sourceTree = "<group>"; }; DFEE18260E882E3600666107 /* stats_histogram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stats_histogram.h; sourceTree = "<group>"; }; E47E933E0E8924DC00CA613E /* tcp_client_socket_libevent.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tcp_client_socket_libevent.cc; sourceTree = "<group>"; }; @@ -895,6 +897,7 @@ 7BED32BF0E5A181C00A747DB /* escape_unittest.cc */, 7BED32BE0E5A181C00A747DB /* ev_root_ca_metadata.cc */, 7BED32BD0E5A181C00A747DB /* ev_root_ca_metadata.h */, + 93D11DCD0E91463000C36437 /* file_input_stream_posix.cc */, 7BED32BC0E5A181C00A747DB /* filter.cc */, 7BED32BB0E5A181C00A747DB /* filter.h */, 7BED32BA0E5A181C00A747DB /* gzip_filter.cc */, @@ -1421,6 +1424,7 @@ 7B8504100E5B2DF000730B43 /* entry_impl.cc in Sources */, 7B8504120E5B2DF000730B43 /* escape.cc in Sources */, 825C2FCC0E5C968B00FDEAB7 /* ev_root_ca_metadata.cc in Sources */, + 93D11DCE0E91463000C36437 /* file_input_stream_posix.cc in Sources */, 7B8504140E5B2DF000730B43 /* file_lock.cc in Sources */, 7B8504150E5B2DF000730B43 /* file_posix.cc in Sources */, 7BA0154C0E5A1C0400044150 /* filter.cc in Sources */, diff --git a/net/url_request/url_request_file_job.cc b/net/url_request/url_request_file_job.cc index 4e23d78..6139827 100644 --- a/net/url_request/url_request_file_job.cc +++ b/net/url_request/url_request_file_job.cc @@ -17,38 +17,60 @@ // signal from the OS that the overlapped read has completed. It does so by // leveraging the MessageLoop::WatchObject API. -#include <process.h> -#include <windows.h> - #include "net/url_request/url_request_file_job.h" -#include "base/file_util.h" +#include "base/compiler_specific.h" +#include "base/message_loop.h" #include "base/string_util.h" +#include "base/worker_pool.h" #include "googleurl/src/gurl.h" #include "net/base/mime_util.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" -#include "net/base/wininet_util.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_file_dir_job.h" -using net::WinInetUtil; +#if defined(OS_WIN) +class URLRequestFileJob::AsyncResolver : + public base::RefCountedThreadSafe<URLRequestFileJob::AsyncResolver> { + public: + explicit AsyncResolver(URLRequestFileJob* owner) + : owner_(owner), owner_loop_(MessageLoop::current()) { + } + + void Resolve(const std::wstring& file_path) { + file_util::FileInfo file_info; + bool exists = file_util::GetFileInfo(file_path, &file_info); + AutoLock locked(lock_); + if (owner_loop_) { + owner_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &AsyncResolver::ReturnResults, exists, file_info)); + } + } + + void Cancel() { + owner_ = NULL; -namespace { + AutoLock locked(lock_); + owner_loop_ = NULL; + } -// Thread used to run ResolveFile. The parameter is a pointer to the -// URLRequestFileJob object. -DWORD WINAPI NetworkFileThread(LPVOID param) { - URLRequestFileJob* job = reinterpret_cast<URLRequestFileJob*>(param); - job->ResolveFile(); - return 0; -} + private: + void ReturnResults(bool exists, const file_util::FileInfo& file_info) { + if (owner_) + owner_->DidResolve(exists, file_info); + } + + URLRequestFileJob* owner_; -} // namespace + Lock lock_; + MessageLoop* owner_loop_; +}; +#endif // static -URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, - const std::string& scheme) { +URLRequestJob* URLRequestFileJob::Factory( + URLRequest* request, const std::string& scheme) { std::wstring file_path; if (net::FileURLToFilePath(request->url(), &file_path)) { if (file_path[file_path.size() - 1] == file_util::kPathSeparator) { @@ -66,131 +88,65 @@ URLRequestJob* URLRequestFileJob::Factory(URLRequest* request, URLRequestFileJob::URLRequestFileJob(URLRequest* request) : URLRequestJob(request), - handle_(INVALID_HANDLE_VALUE), - is_waiting_(false), - is_directory_(false), - is_not_found_(false), - network_file_thread_(NULL), - loop_(NULL) { - memset(&overlapped_, 0, sizeof(overlapped_)); + ALLOW_THIS_IN_INITIALIZER_LIST( + io_callback_(this, &URLRequestFileJob::DidRead)), + is_directory_(false) { } URLRequestFileJob::~URLRequestFileJob() { - CloseHandles(); - - // The thread might still be running. We need to kill it because it holds - // a reference to this object. - if (network_file_thread_) { - TerminateThread(network_file_thread_, 0); - CloseHandle(network_file_thread_); - } -} - -// This function can be called on the main thread or on the network file thread. -// When the request is done, we call StartAsync on the main thread. -void URLRequestFileJob::ResolveFile() { - WIN32_FILE_ATTRIBUTE_DATA file_attributes = {0}; - if (!GetFileAttributesEx(file_path_.c_str(), - GetFileExInfoStandard, - &file_attributes)) { - is_not_found_ = true; - } else { - if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - is_directory_ = true; - } else { - // Set the file size as expected size to be read. - ULARGE_INTEGER file_size; - file_size.HighPart = file_attributes.nFileSizeHigh; - file_size.LowPart = file_attributes.nFileSizeLow; - - set_expected_content_size(file_size.QuadPart); - } - } - - - // We need to protect the loop_ pointer with a lock because if we are running - // on the network file thread, it is possible that the main thread is - // executing Kill() at this moment. Kill() sets the loop_ to NULL because it - // might be going away. - AutoLock lock(loop_lock_); - if (loop_) { - // Start reading asynchronously so that all error reporting and data - // callbacks happen as they would for network requests. - loop_->PostTask(FROM_HERE, NewRunnableMethod( - this, &URLRequestFileJob::StartAsync)); - } +#if defined(OS_WIN) + DCHECK(!async_resolver_); +#endif } void URLRequestFileJob::Start() { - // This is the loop on which we should execute StartAsync. This is used in - // ResolveFile(). - loop_ = MessageLoop::current(); - + // Resolve UNC paths on a background thread. if (!file_path_.compare(0, 2, L"\\\\")) { - // This is on a network share. It might be slow to check if it's a directory - // or a file. We need to do it in another thread. - network_file_thread_ = CreateThread(NULL, 0, &NetworkFileThread, this, 0, - NULL); + DCHECK(!async_resolver_); + async_resolver_ = new AsyncResolver(this); + WorkerPool::PostTask(FROM_HERE, NewRunnableMethod( + async_resolver_.get(), &AsyncResolver::Resolve, file_path_), true); } else { - // We can call the function directly because it's going to be fast. - ResolveFile(); + file_util::FileInfo file_info; + bool exists = file_util::GetFileInfo(file_path_, &file_info); + + // Continue asynchronously. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestFileJob::DidResolve, exists, file_info)); } } void URLRequestFileJob::Kill() { - // If we are killed while waiting for an overlapped result... - if (is_waiting_) { - MessageLoopForIO::current()->WatchObject(overlapped_.hEvent, NULL); - is_waiting_ = false; - Release(); + stream_.Close(); + +#if defined(OS_WIN) + if (async_resolver_) { + async_resolver_->Cancel(); + async_resolver_ = NULL; } - CloseHandles(); - URLRequestJob::Kill(); +#endif - // It is possible that the network file thread is running and will invoke - // StartAsync() on loop_. We set loop_ to NULL here because the message loop - // might be going away and we don't want the other thread to call StartAsync() - // on this loop anymore. We protect loop_ with a lock in case the other thread - // is currenly invoking the call. - AutoLock lock(loop_lock_); - loop_ = NULL; + URLRequestJob::Kill(); } -bool URLRequestFileJob::ReadRawData(char* dest, int dest_size, - int *bytes_read) { +bool URLRequestFileJob::ReadRawData( + char* dest, int dest_size, int *bytes_read) { DCHECK_NE(dest_size, 0); DCHECK(bytes_read); - DCHECK(!is_waiting_); - - *bytes_read = 0; - DWORD bytes; - if (ReadFile(handle_, dest, dest_size, &bytes, &overlapped_)) { - // data is immediately available - overlapped_.Offset += bytes; - *bytes_read = static_cast<int>(bytes); - DCHECK(!is_waiting_); - DCHECK(!is_done()); + int rv = stream_.Read(dest, dest_size, &io_callback_); + if (rv >= 0) { + // Data is immediately available. + *bytes_read = rv; return true; } - // otherwise, a read error occured. - DWORD err = GetLastError(); - if (err == ERROR_IO_PENDING) { - // OK, wait for the object to become signaled - MessageLoopForIO::current()->WatchObject(overlapped_.hEvent, this); - is_waiting_ = true; + // Otherwise, a read error occured. We may just need to wait... + if (rv == net::ERR_IO_PENDING) { SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); - AddRef(); - return false; - } - if (err == ERROR_HANDLE_EOF) { - // OK, nothing more to read - return true; + } else { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); } - - NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(err))); return false; } @@ -199,99 +155,49 @@ bool URLRequestFileJob::GetMimeType(std::string* mime_type) { return net::GetMimeTypeFromFile(file_path_, mime_type); } -void URLRequestFileJob::CloseHandles() { - DCHECK(!is_waiting_); - if (handle_ != INVALID_HANDLE_VALUE) { - CloseHandle(handle_); - handle_ = INVALID_HANDLE_VALUE; - } - if (overlapped_.hEvent) { - CloseHandle(overlapped_.hEvent); - overlapped_.hEvent = NULL; - } -} - -void URLRequestFileJob::StartAsync() { - if (network_file_thread_) { - CloseHandle(network_file_thread_); - network_file_thread_ = NULL; - } - - // The request got killed, we need to stop. - if (!loop_) - return; +void URLRequestFileJob::DidResolve( + bool exists, const file_util::FileInfo& file_info) { +#if defined(OS_WIN) + async_resolver_ = NULL; +#endif // We may have been orphaned... if (!request_) return; - // This is not a file, this is a directory. - if (is_directory_) { - NotifyHeadersComplete(); - return; + is_directory_ = file_info.is_directory; + + int rv = net::OK; + if (!exists) { + rv = net::ERR_FILE_NOT_FOUND; + } else if (!is_directory_) { + rv = stream_.Open(file_path_, true); } - if (is_not_found_) { - // some kind of invalid file URI - NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, - net::ERR_FILE_NOT_FOUND)); + if (rv == net::OK) { + set_expected_content_size(file_info.size); + NotifyHeadersComplete(); } else { - handle_ = CreateFile(file_path_.c_str(), - GENERIC_READ|SYNCHRONIZE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, - NULL); - if (handle_ == INVALID_HANDLE_VALUE) { - NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(GetLastError()))); - } else { - // Get setup for overlapped reads (w/ a manual reset event) - overlapped_.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - } + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); } - - NotifyHeadersComplete(); } -void URLRequestFileJob::OnObjectSignaled(HANDLE object) { - DCHECK(overlapped_.hEvent == object); - DCHECK(is_waiting_); - - // We'll resume watching this handle if need be when we do - // another IO. - MessageLoopForIO::current()->WatchObject(object, NULL); - is_waiting_ = false; - - DWORD bytes_read = 0; - if (!GetOverlappedResult(handle_, &overlapped_, &bytes_read, FALSE)) { - DWORD err = GetLastError(); - if (err == ERROR_HANDLE_EOF) { - // successfully read all data - NotifyDone(URLRequestStatus()); - } else { - NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, - WinInetUtil::OSErrorToNetError(err))); - } - } else if (bytes_read) { - overlapped_.Offset += bytes_read; - // clear the IO_PENDING status - SetStatus(URLRequestStatus()); - } else { - // there was no more data so we're done +void URLRequestFileJob::DidRead(int result) { + if (result > 0) { + SetStatus(URLRequestStatus()); // Clear the IO_PENDING status + } else if (result == 0) { NotifyDone(URLRequestStatus()); + } else { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, result)); } - NotifyReadComplete(bytes_read); - - Release(); // Balance with AddRef from FillBuf; may destroy |this| + NotifyReadComplete(result); } -bool URLRequestFileJob::IsRedirectResponse(GURL* location, - int* http_status_code) { +bool URLRequestFileJob::IsRedirectResponse( + GURL* location, int* http_status_code) { if (is_directory_) { - // This happens when we discovered the file is a directory, so needs a slash - // at the end of the path. + // This happens when we discovered the file is a directory, so needs a + // slash at the end of the path. std::string new_path = request_->url().path(); new_path.push_back('/'); GURL::Replacements replacements; @@ -302,6 +208,8 @@ bool URLRequestFileJob::IsRedirectResponse(GURL* location, return true; } +#if defined(OS_WIN) + // Follow a Windows shortcut. size_t found; found = file_path_.find_last_of('.'); @@ -321,5 +229,8 @@ bool URLRequestFileJob::IsRedirectResponse(GURL* location, *location = net::FilePathToFileURL(new_path); *http_status_code = 301; return true; +#else + return false; +#endif } diff --git a/net/url_request/url_request_file_job.h b/net/url_request/url_request_file_job.h index 638f4ff..92e326c 100644 --- a/net/url_request/url_request_file_job.h +++ b/net/url_request/url_request_file_job.h @@ -5,16 +5,14 @@ #ifndef NET_URL_REQUEST_URL_REQUEST_FILE_JOB_H_ #define NET_URL_REQUEST_URL_REQUEST_FILE_JOB_H_ -#include "base/lock.h" -#include "base/message_loop.h" -#include "base/thread.h" +#include "base/file_util.h" +#include "net/base/completion_callback.h" +#include "net/base/file_input_stream.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_job.h" -#include "testing/gtest/include/gtest/gtest_prod.h" // A request job that handles reading file URLs -class URLRequestFileJob : public URLRequestJob, - protected MessageLoopForIO::Watcher { +class URLRequestFileJob : public URLRequestJob { public: URLRequestFileJob(URLRequest* request); virtual ~URLRequestFileJob(); @@ -23,13 +21,8 @@ class URLRequestFileJob : public URLRequestJob, virtual void Kill(); virtual bool ReadRawData(char* buf, int buf_size, int *bytes_read); virtual bool IsRedirectResponse(GURL* location, int* http_status_code); - virtual bool GetMimeType(std::string* mime_type); - // Checks the status of the file. Set is_directory_ and is_not_found_ - // accordingly. Call StartAsync on the main message loop when it's done. - void ResolveFile(); - static URLRequest::ProtocolFactory Factory; protected: @@ -37,37 +30,20 @@ class URLRequestFileJob : public URLRequestJob, std::wstring file_path_; private: - // The net util test wants to run our FileURLToFilePath function. - FRIEND_TEST(NetUtilTest, FileURLConversion); - - void CloseHandles(); - void StartAsync(); - - // MessageLoop::Watcher callback - virtual void OnObjectSignaled(HANDLE object); + void DidResolve(bool exists, const file_util::FileInfo& file_info); + void DidRead(int result); - // We use overlapped reads to ensure that reads from network file systems do - // not hang the application thread. - HANDLE handle_; - OVERLAPPED overlapped_; - bool is_waiting_; // true when waiting for overlapped result - bool is_directory_; // true when the file request is for a direcotry. - bool is_not_found_; // true when the file requested does not exist. + net::CompletionCallbackImpl<URLRequestFileJob> io_callback_; + net::FileInputStream stream_; + bool is_directory_; - // This lock ensure that the network_file_thread is not using the loop_ after - // is has been set to NULL in Kill(). - Lock loop_lock_; +#if defined(OS_WIN) + class AsyncResolver; + friend class AsyncResolver; + scoped_refptr<AsyncResolver> async_resolver_; +#endif - // Main message loop where StartAsync has to be called. - MessageLoop* loop_; - - // Thread used to query the attributes of files on the network. - // We need to do it on a separate thread because it can be really - // slow. - HANDLE network_file_thread_; - - DISALLOW_EVIL_CONSTRUCTORS(URLRequestFileJob); + DISALLOW_COPY_AND_ASSIGN(URLRequestFileJob); }; #endif // NET_URL_REQUEST_URL_REQUEST_FILE_JOB_H_ - diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index cfffe46..9bf467f 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -2,13 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/url_request/url_request_unittest.h" + +#if defined(OS_WIN) #include <windows.h> #include <shlobj.h> +#endif + #include <algorithm> #include <string> -#include "net/url_request/url_request_unittest.h" - #include "base/message_loop.h" #include "base/path_service.h" #include "base/process_util.h" @@ -53,7 +56,7 @@ std::string TestNetResourceProvider(int key) { return "header"; } -} +} // namespace TEST(URLRequestTest, GetTest_NoCache) { TestServer server(L""); @@ -308,7 +311,7 @@ TEST(URLRequestTest, PostFileTest) { std::wstring dir; PathService::Get(base::DIR_EXE, &dir); - _wchdir(dir.c_str()); + file_util::SetCurrentDirectory(dir); std::wstring path; PathService::Get(base::DIR_SOURCE_ROOT, &path); @@ -390,13 +393,13 @@ TEST(URLRequestTest, FileTest) { MessageLoop::current()->Run(); - WIN32_FILE_ATTRIBUTE_DATA data; - GetFileAttributesEx(app_path.c_str(), GetFileExInfoStandard, &data); + int64 file_size; + file_util::GetFileSize(app_path, &file_size); EXPECT_TRUE(!r.is_pending()); EXPECT_EQ(1, d.response_started_count()); EXPECT_FALSE(d.received_data_before_response()); - EXPECT_EQ(d.bytes_received(), data.nFileSizeLow); + EXPECT_EQ(d.bytes_received(), static_cast<int>(file_size)); } #ifndef NDEBUG DCHECK_EQ(url_request_metrics.object_count,0); @@ -510,6 +513,7 @@ TEST(URLRequestTest, BZip2ContentTest_IncrementalHeader) { EXPECT_EQ(got_content, got_bz2_content); } +#if defined(OS_WIN) TEST(URLRequestTest, ResolveShortcutTest) { std::wstring app_path; PathService::Get(base::DIR_SOURCE_ROOT, &app_path); @@ -580,6 +584,7 @@ TEST(URLRequestTest, ResolveShortcutTest) { DCHECK_EQ(url_request_metrics.object_count,0); #endif } +#endif // defined(OS_WIN) TEST(URLRequestTest, ContentTypeNormalizationTest) { TestServer server(L"net/data/url_request_unittest"); diff --git a/net/url_request/url_request_unittest.h b/net/url_request/url_request_unittest.h index f17e7fc..a7214fd 100644 --- a/net/url_request/url_request_unittest.h +++ b/net/url_request/url_request_unittest.h @@ -11,6 +11,7 @@ #include "base/file_util.h" #include "base/message_loop.h" #include "base/path_service.h" +#include "base/platform_thread.h" #include "base/process_util.h" #include "base/string_util.h" #include "base/thread.h" @@ -297,7 +298,7 @@ class TestServer : public process_util::ProcessFilter { bool success; while ((success = MakeGETRequest("hello.html")) == false && retries > 0) { retries--; - ::Sleep(500); + PlatformThread::Sleep(500); } ASSERT_TRUE(success) << "Webserver not starting properly."; |