summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/SConscript5
-rw-r--r--net/base/file_input_stream.h92
-rw-r--r--net/base/file_input_stream_posix.cc55
-rw-r--r--net/base/file_input_stream_unittest.cc205
-rw-r--r--net/base/file_input_stream_win.cc232
-rw-r--r--net/base/net_error_list.h3
-rw-r--r--net/base/upload_data_stream.cc97
-rw-r--r--net/base/upload_data_stream.h11
-rw-r--r--net/build/net.vcproj12
-rw-r--r--net/build/net_unittests.vcproj4
-rw-r--r--net/net.xcodeproj/project.pbxproj4
-rw-r--r--net/url_request/url_request_file_job.cc301
-rw-r--r--net/url_request/url_request_file_job.h54
-rw-r--r--net/url_request/url_request_unittest.cc19
-rw-r--r--net/url_request/url_request_unittest.h3
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.";