summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/net/chrome_url_request_context.cc50
-rw-r--r--chrome/browser/net/chrome_url_request_context.h10
-rw-r--r--chrome/browser/profile.cc31
-rw-r--r--chrome/browser/profile.h7
-rw-r--r--chrome/common/chrome_constants.cc3
-rw-r--r--chrome/common/chrome_constants.h2
-rw-r--r--chrome/test/testing_profile.h3
-rw-r--r--net/disk_cache/disk_cache.h24
-rw-r--r--net/disk_cache/entry_impl.cc55
-rw-r--r--net/disk_cache/entry_impl.h8
-rw-r--r--net/disk_cache/entry_unittest.cc98
-rw-r--r--net/disk_cache/mem_entry_impl.h8
-rw-r--r--net/http/http_cache.cc49
-rw-r--r--net/http/http_cache.h27
-rw-r--r--net/http/http_cache_unittest.cc99
-rw-r--r--net/http/http_network_layer.cc28
-rw-r--r--net/http/http_network_layer.h12
-rw-r--r--net/http/http_response_info.h5
-rw-r--r--webkit/glue/resource_loader_bridge.h2
19 files changed, 505 insertions, 16 deletions
diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc
index d98f2d6..748c497 100644
--- a/chrome/browser/net/chrome_url_request_context.cc
+++ b/chrome/browser/net/chrome_url_request_context.cc
@@ -16,6 +16,7 @@
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
#include "net/http/http_cache.h"
+#include "net/http/http_network_layer.h"
#include "net/http/http_util.h"
#include "net/proxy/proxy_service.h"
#include "webkit/glue/webkit_glue.h"
@@ -76,6 +77,46 @@ ChromeURLRequestContext* ChromeURLRequestContext::CreateOriginal(
}
// static
+ChromeURLRequestContext* ChromeURLRequestContext::CreateOriginalForMedia(
+ Profile* profile, const FilePath& disk_cache_path) {
+ DCHECK(!profile->IsOffTheRecord());
+ URLRequestContext* original_context =
+ profile->GetOriginalProfile()->GetRequestContext();
+ ChromeURLRequestContext* context = new ChromeURLRequestContext(profile);
+ // Share the same proxy service of the common profile.
+ context->proxy_service_ = original_context->proxy_service();
+ // Also share the cookie store of the common profile.
+ context->cookie_store_ = original_context->cookie_store();
+
+ // Create a media cache with maximum size of kint32max (2GB).
+ // TODO(hclam): make the maximum size of media cache configurable.
+ net::HttpCache* original_cache =
+ original_context->http_transaction_factory()->GetCache();
+ net::HttpCache* cache;
+ if (original_cache) {
+ // Try to reuse HttpNetworkSession in the original context, assuming that
+ // HttpTransactionFactory (network_layer()) of HttpCache is implemented
+ // by HttpNetworkLayer so we can reuse HttpNetworkSession within it. This
+ // assumption will be invalid if the original HttpCache is constructed with
+ // HttpCache(HttpTransactionFactory*, disk_cache::Backend*) constructor.
+ net::HttpNetworkLayer* original_network_layer =
+ static_cast<net::HttpNetworkLayer*>(original_cache->network_layer());
+ cache = new net::HttpCache(original_network_layer->GetSession(),
+ disk_cache_path.ToWStringHack(), kint32max);
+ } else {
+ // If original HttpCache doesn't exist, simply construct one with a whole
+ // new set of network stack.
+ cache = new net::HttpCache(original_context->proxy_service(),
+ disk_cache_path.ToWStringHack(), kint32max);
+ }
+ // Set the cache type to media.
+ cache->set_type(net::HttpCache::MEDIA);
+
+ context->http_transaction_factory_ = cache;
+ return context;
+}
+
+// static
ChromeURLRequestContext* ChromeURLRequestContext::CreateOffTheRecord(
Profile* profile) {
DCHECK(profile->IsOffTheRecord());
@@ -94,6 +135,15 @@ ChromeURLRequestContext* ChromeURLRequestContext::CreateOffTheRecord(
return context;
}
+// static
+ChromeURLRequestContext* ChromeURLRequestContext::CreateOffTheRecordForMedia(
+ Profile* profile, const FilePath& disk_cache_path) {
+ // TODO(hclam): since we don't have an implementation of disk cache backend
+ // for media files in OTR mode, we use the original context first. Change this
+ // to the proper backend later.
+ return CreateOriginalForMedia(profile, disk_cache_path);
+}
+
ChromeURLRequestContext::ChromeURLRequestContext(Profile* profile)
: prefs_(profile->GetPrefs()),
is_off_the_record_(profile->IsOffTheRecord()) {
diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h
index 15d53e8..df8cc9b 100644
--- a/chrome/browser/net/chrome_url_request_context.h
+++ b/chrome/browser/net/chrome_url_request_context.h
@@ -28,10 +28,20 @@ class ChromeURLRequestContext : public URLRequestContext,
Profile* profile, const FilePath& cookie_store_path,
const FilePath& disk_cache_path);
+ // Create an instance for an original profile for media. This is expected to
+ // get called on UI thread. This method takes a profile and reuses the
+ // 'original' URLRequestContext for common files.
+ static ChromeURLRequestContext* CreateOriginalForMedia(Profile *profile,
+ const FilePath& disk_cache_path);
+
// Create an instance for use with an OTR profile. This is expected to get
// called on the UI thread.
static ChromeURLRequestContext* CreateOffTheRecord(Profile* profile);
+ // Create an instance of request context for OTR profile for media resources.
+ static ChromeURLRequestContext* CreateOffTheRecordForMedia(Profile* profile,
+ const FilePath& disk_cache_path);
+
// Clean up UI thread resources. This is expected to get called on the UI
// thread before the instance is deleted on the IO thread.
void CleanupOnUIThread();
diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc
index 7c7041e..710949f 100644
--- a/chrome/browser/profile.cc
+++ b/chrome/browser/profile.cc
@@ -207,6 +207,20 @@ class OffTheRecordProfileImpl : public Profile,
return request_context_;
}
+ virtual URLRequestContext* GetRequestContextForMedia() {
+ if (!media_request_context_) {
+ FilePath cache_path = GetPath();
+ cache_path.Append(chrome::kOffTheRecordMediaCacheDirname);
+ media_request_context_ =
+ ChromeURLRequestContext::CreateOffTheRecordForMedia(
+ this, cache_path);
+ media_request_context_->AddRef();
+
+ DCHECK(media_request_context_->cookie_store());
+ }
+ return media_request_context_;
+ }
+
virtual SessionService* GetSessionService() {
// Don't save any sessions when off the record.
return NULL;
@@ -311,6 +325,9 @@ class OffTheRecordProfileImpl : public Profile,
// The context to use for requests made from this OTR session.
ChromeURLRequestContext* request_context_;
+ // The context for requests for media resources.
+ ChromeURLRequestContext* media_request_context_;
+
// The download manager that only stores downloaded items in memory.
scoped_refptr<DownloadManager> download_manager_;
@@ -586,6 +603,20 @@ URLRequestContext* ProfileImpl::GetRequestContext() {
return request_context_;
}
+URLRequestContext* ProfileImpl::GetRequestContextForMedia() {
+ if (!media_request_context_) {
+ FilePath cache_path = GetPath();
+ cache_path.Append(chrome::kMediaCacheDirname);
+ media_request_context_ = ChromeURLRequestContext::CreateOriginalForMedia(
+ this, cache_path);
+ media_request_context_->AddRef();
+
+ DCHECK(media_request_context_->cookie_store());
+ }
+
+ return media_request_context_;
+}
+
HistoryService* ProfileImpl::GetHistoryService(ServiceAccessType sat) {
if (!history_service_created_) {
history_service_created_ = true;
diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h
index 8f513ae..8c93886 100644
--- a/chrome/browser/profile.h
+++ b/chrome/browser/profile.h
@@ -162,6 +162,10 @@ class Profile {
// keep it alive longer than the profile) must Release() it on the I/O thread.
virtual URLRequestContext* GetRequestContext() = 0;
+ // Returns the request context for media resources asociated with this
+ // profile.
+ virtual URLRequestContext* GetRequestContextForMedia() = 0;
+
// Returns the session service for this profile. This may return NULL. If
// this profile supports a session service (it isn't off the record), and
// the session service hasn't yet been created, this forces creation of
@@ -277,6 +281,7 @@ class ProfileImpl : public Profile,
virtual DownloadManager* GetDownloadManager();
virtual bool HasCreatedDownloadManager() const;
virtual URLRequestContext* GetRequestContext();
+ virtual URLRequestContext* GetRequestContextForMedia();
virtual SessionService* GetSessionService();
virtual void ShutdownSessionService();
virtual bool HasSessionService() const;
@@ -342,6 +347,8 @@ class ProfileImpl : public Profile,
ChromeURLRequestContext* request_context_;
+ ChromeURLRequestContext* media_request_context_;
+
scoped_refptr<DownloadManager> download_manager_;
scoped_refptr<HistoryService> history_service_;
scoped_refptr<WebDataService> web_data_service_;
diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc
index 94319c8..5deca64 100644
--- a/chrome/common/chrome_constants.cc
+++ b/chrome/common/chrome_constants.cc
@@ -32,6 +32,9 @@ const FilePath::CharType kExtensionFileExtension[] = FPL("crx");
// filenames
const wchar_t kArchivedHistoryFilename[] = L"Archived History";
const FilePath::CharType kCacheDirname[] = FPL("Cache");
+const FilePath::CharType kMediaCacheDirname[] = FPL("Media Cache");
+const FilePath::CharType kOffTheRecordMediaCacheDirname[] =
+ FPL("Incognito Media Cache");
const wchar_t kChromePluginDataDirname[] = L"Plugin Data";
const FilePath::CharType kCookieFilename[] = FPL("Cookies");
const FilePath::CharType kHistoryFilename[] = FPL("History");
diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h
index 07933f4..a5d6cd0 100644
--- a/chrome/common/chrome_constants.h
+++ b/chrome/common/chrome_constants.h
@@ -26,6 +26,8 @@ extern const FilePath::CharType kExtensionFileExtension[];
// filenames
extern const wchar_t kArchivedHistoryFilename[];
extern const FilePath::CharType kCacheDirname[];
+extern const FilePath::CharType kMediaCacheDirname[];
+extern const FilePath::CharType kOffTheRecordMediaCacheDirname[];
extern const wchar_t kChromePluginDataDirname[];
extern const FilePath::CharType kCookieFilename[];
extern const FilePath::CharType kHistoryFilename[];
diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h
index 43913ad..4601173 100644
--- a/chrome/test/testing_profile.h
+++ b/chrome/test/testing_profile.h
@@ -123,6 +123,9 @@ class TestingProfile : public Profile {
virtual URLRequestContext* GetRequestContext() {
return NULL;
}
+ virtual URLRequestContext* GetRequestContextForMedia() {
+ return NULL;
+ }
void set_session_service(SessionService* session_service) {
session_service_ = session_service;
}
diff --git a/net/disk_cache/disk_cache.h b/net/disk_cache/disk_cache.h
index 59d2ad2..7d6e41a 100644
--- a/net/disk_cache/disk_cache.h
+++ b/net/disk_cache/disk_cache.h
@@ -12,6 +12,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/platform_file.h"
#include "base/time.h"
#include "net/base/completion_callback.h"
@@ -153,6 +154,29 @@ class Entry {
net::CompletionCallback* completion_callback,
bool truncate) = 0;
+ // Prepares a target stream as an external file, returns a corresponding
+ // base::PlatformFile if successful, returns base::kInvalidPlatformFileValue
+ // if fails. If this call returns a valid base::PlatformFile value (i.e.
+ // not base::kInvalidPlatformFileValue), there is no guarantee that the file
+ // is truncated. Implementor can always return base::kInvalidPlatformFileValue
+ // if external file is not available in that particular implementation.
+ // Caller should never close the file handle returned by this method, since
+ // the handle should be managed by the implementor of this class. Caller
+ // should never save the handle for future use.
+ // With a stream prepared as an external file, the stream would always be
+ // kept in an external file since creation, even if the stream has 0 bytes.
+ // So we need to be cautious about using this option for preparing a stream or
+ // we will end up having a lot of empty cache files. Calling this method also
+ // means that all data written to the stream will always be written to file
+ // directly *without* buffering.
+ virtual base::PlatformFile UseExternalFile(int index) = 0;
+
+ // Returns a read file handle for the cache stream referenced by |index|.
+ // Caller should never close the handle returned by this method and should
+ // not save it for future use. The lifetime of the base::PlatformFile handle
+ // is managed by the implementor of this class.
+ virtual base::PlatformFile GetPlatformFile(int index) = 0;
+
protected:
virtual ~Entry() {}
};
diff --git a/net/disk_cache/entry_impl.cc b/net/disk_cache/entry_impl.cc
index b64073a..9db5647 100644
--- a/net/disk_cache/entry_impl.cc
+++ b/net/disk_cache/entry_impl.cc
@@ -78,8 +78,10 @@ EntryImpl::EntryImpl(BackendImpl* backend, Addr address)
entry_.LazyInit(backend->File(address), address);
doomed_ = false;
backend_ = backend;
- for (int i = 0; i < NUM_STREAMS; i++)
+ for (int i = 0; i < NUM_STREAMS; i++) {
unreported_size_[i] = 0;
+ need_file_[i] = false;
+ }
}
// When an entry is deleted from the cache, we clean up all the data associated
@@ -318,7 +320,9 @@ int EntryImpl::WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
backend_->OnEvent(Stats::WRITE_DATA);
- if (user_buffers_[index].get()) {
+ // If we have prepared the cache as an external file, we should never use
+ // user_buffers_ and always write to file directly.
+ if (!need_file_[index] && user_buffers_[index].get()) {
// Complete the operation locally.
if (!buf_len)
return 0;
@@ -365,6 +369,43 @@ int EntryImpl::WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
return (completed || !completion_callback) ? buf_len : net::ERR_IO_PENDING;
}
+base::PlatformFile EntryImpl::UseExternalFile(int index) {
+ DCHECK(index >= 0 && index < NUM_STREAMS);
+
+ Addr address(entry_.Data()->data_addr[index]);
+
+ // We will not prepare the cache file since the entry is already initialized,
+ // just return the platform file backing the cache.
+ if (address.is_initialized())
+ return GetPlatformFile(index);
+
+ if (!backend_->CreateExternalFile(&address))
+ return base::kInvalidPlatformFileValue;
+
+ entry_.Data()->data_addr[index] = address.value();
+ entry_.Store();
+
+ // Set the flag for this stream so we never use user_buffer_.
+ // TODO(hclam): do we need to save this information to EntryStore?
+ need_file_[index] = true;
+
+ return GetPlatformFile(index);
+}
+
+base::PlatformFile EntryImpl::GetPlatformFile(int index) {
+ DCHECK(index >= 0 && index < NUM_STREAMS);
+
+ Addr address(entry_.Data()->data_addr[index]);
+ if (!address.is_initialized() || !address.is_separate_file())
+ return base::kInvalidPlatformFileValue;
+
+ File* cache_file = GetExternalFile(address, index);
+ if (!cache_file)
+ return base::kInvalidPlatformFileValue;
+
+ return cache_file->platform_file();
+}
+
uint32 EntryImpl::GetHash() {
return entry_.Data()->hash;
}
@@ -603,6 +644,16 @@ File* EntryImpl::GetExternalFile(Addr address, int index) {
bool EntryImpl::PrepareTarget(int index, int offset, int buf_len,
bool truncate) {
Addr address(entry_.Data()->data_addr[index]);
+
+ // If we are instructed to use an external file, we should never buffer when
+ // writing. We are done with preparation of the target automatically, since
+ // we have already created the external file for writing.
+ if (need_file_[index]) {
+ // Make sure the stream is initialized and is kept in an external file.
+ DCHECK(address.is_initialized() && address.is_separate_file());
+ return true;
+ }
+
if (address.is_initialized() || user_buffers_[index].get())
return GrowUserBuffer(index, offset, buf_len, truncate);
diff --git a/net/disk_cache/entry_impl.h b/net/disk_cache/entry_impl.h
index 0b08a58..6d3d614 100644
--- a/net/disk_cache/entry_impl.h
+++ b/net/disk_cache/entry_impl.h
@@ -32,6 +32,8 @@ class EntryImpl : public Entry, public base::RefCounted<EntryImpl> {
virtual int WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
net::CompletionCallback* completion_callback,
bool truncate);
+ virtual base::PlatformFile UseExternalFile(int index);
+ virtual base::PlatformFile GetPlatformFile(int index);
inline CacheEntryBlock* entry() {
return &entry_;
@@ -45,8 +47,7 @@ class EntryImpl : public Entry, public base::RefCounted<EntryImpl> {
// Performs the initialization of a EntryImpl that will be added to the
// cache.
- bool CreateEntry(Addr node_address, const std::string& key,
- uint32 hash);
+ bool CreateEntry(Addr node_address, const std::string& key, uint32 hash);
// Returns true if this entry matches the lookup arguments.
bool IsSameEntry(const std::string& key, uint32 hash);
@@ -140,7 +141,8 @@ class EntryImpl : public Entry, public base::RefCounted<EntryImpl> {
// data and key.
int unreported_size_[NUM_STREAMS]; // Bytes not reported yet to the backend.
bool doomed_; // True if this entry was removed from the cache.
-
+ bool need_file_[NUM_STREAMS]; // True if stream is prepared as an external
+ // file.
DISALLOW_EVIL_CONSTRUCTORS(EntryImpl);
};
diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc
index 47b70d0..bb7cc2e 100644
--- a/net/disk_cache/entry_unittest.cc
+++ b/net/disk_cache/entry_unittest.cc
@@ -833,3 +833,101 @@ TEST_F(DiskCacheEntryTest, MemoryOnlyDoomedEntry) {
DoomEntry();
}
+// Check that we can hint an entry to use external file and the return value
+// is a valid file handle.
+TEST_F(DiskCacheEntryTest, UseExternalFile) {
+ InitCache();
+
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache_->CreateEntry("key", &entry));
+ base::PlatformFile cache_file = entry->UseExternalFile(0);
+
+ // We should have a valid file handle.
+ EXPECT_NE(base::kInvalidPlatformFileValue, cache_file);
+ scoped_refptr<disk_cache::File> file(new disk_cache::File(cache_file));
+
+ // 4KB.
+ size_t kDataSize = 0x1000;
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kDataSize);
+
+ CacheTestFillBuffer(buffer->data(), kDataSize, false);
+ ASSERT_EQ(0U, file->GetLength());
+ ASSERT_EQ(kDataSize, static_cast<size_t>(
+ entry->WriteData(0, 0, buffer, kDataSize, NULL, false)));
+ ASSERT_EQ(kDataSize, file->GetLength());
+ entry->Close();
+}
+
+// Make sure we can use Entry::GetPlatformFile on an entry stored in an external
+// file and get a valid file handle.
+TEST_F(DiskCacheEntryTest, GetPlatformFile) {
+ InitCache();
+
+ disk_cache::Entry* entry;
+ ASSERT_TRUE(cache_->CreateEntry("key", &entry));
+ EXPECT_NE(base::kInvalidPlatformFileValue, entry->UseExternalFile(0));
+
+ size_t kDataSize = 50;
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kDataSize);
+
+ // Fill the data buffer and write it to cache.
+ CacheTestFillBuffer(buffer->data(), kDataSize, false);
+ ASSERT_EQ(kDataSize,static_cast<size_t>(
+ entry->WriteData(0, 0, buffer, kDataSize, NULL, false)));
+
+ // Close the entry.
+ entry->Close();
+
+ // Open the entry again and get it's file handle.
+ ASSERT_TRUE(cache_->OpenEntry("key", &entry));
+ base::PlatformFile cache_file = entry->GetPlatformFile(0);
+
+ // Make sure it's a valid file handle and verify the size of the file.
+ scoped_refptr<disk_cache::File> file(new disk_cache::File(cache_file));
+ ASSERT_EQ(kDataSize, file->GetLength());
+
+ entry->Close();
+}
+
+// Test the behavior of EntryImpl that small entries are kept in block files
+// or buffer, and only entries above certain size would be stored in an
+// external file, make sure GetPlatformFile() works with both cases without
+// using UseExternalFile().
+TEST_F(DiskCacheEntryTest, GetPlatformFileVariableEntrySize) {
+ InitCache();
+
+ disk_cache::Entry* entry;
+
+ // Make the buffer just larger than disk_cache::kMaxBlockSize.
+ const size_t kLargeDataSize = disk_cache::kMaxBlockSize + 1;
+ const size_t kSmallDataSize = kLargeDataSize / 2;
+ scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kLargeDataSize);
+
+ // 1. First test with small entry.
+ ASSERT_TRUE(cache_->CreateEntry("small_entry", &entry));
+
+ CacheTestFillBuffer(buffer->data(), kSmallDataSize, false);
+ ASSERT_EQ(kSmallDataSize, static_cast<size_t>(
+ entry->WriteData(0, 0, buffer, kSmallDataSize, NULL, false)));
+
+ // Make sure we don't get an external file.
+ ASSERT_EQ(base::kInvalidPlatformFileValue, entry->GetPlatformFile(0));
+
+ entry->Close();
+
+ // 2. Test with large entry.
+ ASSERT_TRUE(cache_->CreateEntry("large_entry", &entry));
+
+ CacheTestFillBuffer(buffer->data(), kLargeDataSize, false);
+ ASSERT_EQ(kLargeDataSize, static_cast<size_t>(
+ entry->WriteData(0, 0, buffer, kLargeDataSize, NULL, false)));
+
+ base::PlatformFile cache_file = entry->GetPlatformFile(0);
+ EXPECT_NE(base::kInvalidPlatformFileValue, cache_file);
+
+ // Make sure it's a valid file handle and verify the size of the file.
+ scoped_refptr<disk_cache::File> file(new disk_cache::File(cache_file));
+ ASSERT_EQ(kLargeDataSize, file->GetLength());
+
+ entry->Close();
+}
diff --git a/net/disk_cache/mem_entry_impl.h b/net/disk_cache/mem_entry_impl.h
index cd8c6e7..63235f9 100644
--- a/net/disk_cache/mem_entry_impl.h
+++ b/net/disk_cache/mem_entry_impl.h
@@ -29,6 +29,14 @@ class MemEntryImpl : public Entry {
virtual int WriteData(int index, int offset, net::IOBuffer* buf, int buf_len,
net::CompletionCallback* completion_callback,
bool truncate);
+ virtual base::PlatformFile UseExternalFile(int index) {
+ // MemEntryImpl doesn't support caching to an external file.
+ return base::kInvalidPlatformFileValue;
+ }
+ virtual base::PlatformFile GetPlatformFile(int index) {
+ // MemEntryImpl doesn't support caching to an external file.
+ return base::kInvalidPlatformFileValue;
+ }
// Performs the initialization of a EntryImpl that will be added to the
// cache.
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index c54b6d4..913dc00 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -286,6 +286,9 @@ class HttpCache::Transaction
// Called to write response_ to the cache entry.
void WriteResponseInfoToEntry();
+ // Called to truncate response content in the entry.
+ void TruncateResponseData();
+
// Called to append response data to the cache entry.
void AppendResponseDataToEntry(IOBuffer* data, int data_len);
@@ -790,6 +793,12 @@ int HttpCache::Transaction::ReadResponseInfoFromEntry() {
if (!HttpCache::ReadResponseInfo(entry_->disk_entry, &response_))
return ERR_FAILED;
+ // If the cache object is used for media file, we want the file handle of
+ // response data.
+ if (cache_->type() == HttpCache::MEDIA)
+ response_.response_data_file =
+ entry_->disk_entry->GetPlatformFile(kResponseContentIndex);
+
return OK;
}
@@ -846,6 +855,27 @@ void HttpCache::Transaction::AppendResponseDataToEntry(IOBuffer* data,
WriteToEntry(kResponseContentIndex, current_size, data, data_len);
}
+void HttpCache::Transaction::TruncateResponseData() {
+ if (!entry_)
+ return;
+
+ // If the cache is for media files, we try to prepare the response data
+ // file as an external file and truncate it afterwards.
+ // Recipient of ResponseInfo should judge from |response_.response_data_file|
+ // to tell whether an external file of response data is available for reading
+ // or not.
+ // TODO(hclam): we should prepare the target stream as extern file only
+ // if we get a valid response from server, i.e. 200. We don't want empty
+ // cache files for redirection or external files for erroneous requests.
+ response_.response_data_file = base::kInvalidPlatformFileValue;
+ if (cache_->type() == HttpCache::MEDIA)
+ response_.response_data_file =
+ entry_->disk_entry->UseExternalFile(kResponseContentIndex);
+
+ // Truncate the stream.
+ WriteToEntry(kResponseContentIndex, 0, NULL, 0);
+}
+
void HttpCache::Transaction::DoneWritingToEntry(bool success) {
if (!entry_)
return;
@@ -902,8 +932,8 @@ void HttpCache::Transaction::OnNetworkInfoAvailable(int result) {
response_ = *new_response;
WriteResponseInfoToEntry();
- // Truncate the response data
- WriteToEntry(kResponseContentIndex, 0, NULL, 0);
+ // Truncate response data
+ TruncateResponseData();
// If this response is a redirect, then we can stop writing now. (We
// don't need to cache the response body of a redirect.)
@@ -958,14 +988,28 @@ HttpCache::HttpCache(ProxyService* proxy_service,
int cache_size)
: disk_cache_dir_(cache_dir),
mode_(NORMAL),
+ type_(COMMON),
network_layer_(HttpNetworkLayer::CreateFactory(proxy_service)),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
in_memory_cache_(false),
cache_size_(cache_size) {
}
+HttpCache::HttpCache(HttpNetworkSession* session,
+ const std::wstring& cache_dir,
+ int cache_size)
+ : disk_cache_dir_(cache_dir),
+ mode_(NORMAL),
+ type_(COMMON),
+ network_layer_(HttpNetworkLayer::CreateFactory(session)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
+ in_memory_cache_(false),
+ cache_size_(cache_size) {
+}
+
HttpCache::HttpCache(ProxyService* proxy_service, int cache_size)
: mode_(NORMAL),
+ type_(COMMON),
network_layer_(HttpNetworkLayer::CreateFactory(proxy_service)),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
in_memory_cache_(true),
@@ -975,6 +1019,7 @@ HttpCache::HttpCache(ProxyService* proxy_service, int cache_size)
HttpCache::HttpCache(HttpTransactionFactory* network_layer,
disk_cache::Backend* disk_cache)
: mode_(NORMAL),
+ type_(COMMON),
network_layer_(network_layer),
disk_cache_(disk_cache),
ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)),
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 51991f5..528c964 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -29,6 +29,7 @@ class Entry;
namespace net {
+class HttpNetworkSession;
class HttpRequestInfo;
class HttpResponseInfo;
class ProxyService;
@@ -48,13 +49,33 @@ class HttpCache : public HttpTransactionFactory {
PLAYBACK
};
- // Initialize the cache from the directory where its data is stored. The
+ // The type of an HttpCache object, essentially describe what an HttpCache
+ // object is for.
+ enum Type {
+ // An HttpCache object for common objects, e.g. html pages, images, fonts,
+ // css files, js files and other common web resources.
+ COMMON = 0,
+ // A cache system for media file, e.g. video and audio files. These files
+ // are huge and has special requirement for access.
+ MEDIA
+ };
+
+ // Initialize the cache from the directory where its data is stored. The
// disk cache is initialized lazily (by CreateTransaction) in this case. If
// |cache_size| is zero, a default value will be calculated automatically.
HttpCache(ProxyService* proxy_service,
const std::wstring& cache_dir,
int cache_size);
+ // Initialize the cache from the directory where its data is stored. The
+ // disk cache is initialized lazily (by CreateTransaction) in this case. If
+ // |cache_size| is zero, a default value will be calculated automatically.
+ // Provide an existing HttpNetworkSession, the cache can construct a
+ // network layer with a shared HttpNetworkSession in order for multiple
+ // network layers to share information (e.g. authenication data).
+ HttpCache(HttpNetworkSession* session, const std::wstring& cache_dir,
+ int cache_size);
+
// Initialize using an in-memory cache. The cache is initialized lazily
// (by CreateTransaction) in this case. If |cache_size| is zero, a default
// value will be calculated automatically.
@@ -91,6 +112,9 @@ class HttpCache : public HttpTransactionFactory {
void set_mode(Mode value) { mode_ = value; }
Mode mode() { return mode_; }
+ void set_type(Type type) { type_ = type; }
+ Type type() { return type_; }
+
private:
// Types --------------------------------------------------------------------
@@ -146,6 +170,7 @@ class HttpCache : public HttpTransactionFactory {
std::wstring disk_cache_dir_;
Mode mode_;
+ Type type_;
scoped_ptr<HttpTransactionFactory> network_layer_;
scoped_ptr<disk_cache::Backend> disk_cache_;
diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc
index b52f2a4..5db229b 100644
--- a/net/http/http_cache_unittest.cc
+++ b/net/http/http_cache_unittest.cc
@@ -6,6 +6,7 @@
#include "base/hash_tables.h"
#include "base/message_loop.h"
+#include "base/platform_file.h"
#include "base/string_util.h"
#include "net/base/net_errors.h"
#include "net/base/load_flags.h"
@@ -26,10 +27,12 @@ namespace {
class MockDiskEntry : public disk_cache::Entry,
public base::RefCounted<MockDiskEntry> {
public:
- MockDiskEntry() : test_mode_(0), doomed_(false) {
+ MockDiskEntry()
+ : test_mode_(0), doomed_(false), platform_file_(global_platform_file_) {
}
- MockDiskEntry(const std::string& key) : key_(key), doomed_(false) {
+ MockDiskEntry(const std::string& key)
+ : key_(key), doomed_(false), platform_file_(global_platform_file_) {
const MockTransaction* t = FindMockTransaction(GURL(key));
DCHECK(t);
test_mode_ = t->test_mode;
@@ -98,6 +101,18 @@ class MockDiskEntry : public disk_cache::Entry,
return buf_len;
}
+ base::PlatformFile UseExternalFile(int index) {
+ return platform_file_;
+ }
+
+ base::PlatformFile GetPlatformFile(int index) {
+ return platform_file_;
+ }
+
+ static void set_global_platform_file(base::PlatformFile platform_file) {
+ global_platform_file_ = platform_file;
+ }
+
private:
// Unlike the callbacks for MockHttpTransaction, we want this one to run even
// if the consumer called Close on the MockDiskEntry. We achieve that by
@@ -114,8 +129,13 @@ class MockDiskEntry : public disk_cache::Entry,
std::vector<char> data_[2];
int test_mode_;
bool doomed_;
+ base::PlatformFile platform_file_;
+ static base::PlatformFile global_platform_file_;
};
+base::PlatformFile MockDiskEntry::global_platform_file_ =
+ base::kInvalidPlatformFileValue;
+
class MockDiskCache : public disk_cache::Backend {
public:
MockDiskCache() : open_count_(0), create_count_(0), fail_requests_(0) {
@@ -1184,3 +1204,78 @@ TEST(HttpCache, OutlivedTransactions) {
delete cache;
delete trans;
}
+
+// Make sure Entry::UseExternalFile is called when a new entry is created in
+// a HttpCache with MEDIA type. Also make sure Entry::GetPlatformFile is called
+// when an entry is loaded from a HttpCache with MEDIA type. Also confirm we
+// will receive a file handle in ResponseInfo from a media cache.
+TEST(HttpCache, SimpleGET_MediaCache) {
+ // Initialize the HttpCache with MEDIA type.
+ MockHttpCache cache;
+ cache.http_cache()->set_type(net::HttpCache::MEDIA);
+
+ // Define some fake file handles for testing.
+ base::PlatformFile kFakePlatformFile1, kFakePlatformFile2;
+#if defined(OS_WIN)
+ kFakePlatformFile1 = reinterpret_cast<base::PlatformFile>(1);
+ kFakePlatformFile2 = reinterpret_cast<base::PlatformFile>(2);
+#else
+ kFakePlatformFile1 = 1;
+ kFakePlatformFile2 = 2;
+#endif
+
+ ScopedMockTransaction trans_info(kSimpleGET_Transaction);
+ TestCompletionCallback callback;
+
+ {
+ // Set the fake file handle to MockDiskEntry so cache is written with an
+ // entry created with our fake file handle.
+ MockDiskEntry::set_global_platform_file(kFakePlatformFile1);
+
+ scoped_ptr<net::HttpTransaction> trans(
+ cache.http_cache()->CreateTransaction());
+ ASSERT_TRUE(trans.get());
+
+ MockHttpRequest request(trans_info);
+
+ int rv = trans->Start(&request, &callback);
+ if (rv == net::ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ ASSERT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response);
+
+ ASSERT_EQ(kFakePlatformFile1, response->response_data_file);
+
+ ReadAndVerifyTransaction(trans.get(), trans_info);
+ }
+
+ // Load only from cache so we would get the same file handle.
+ trans_info.load_flags |= net::LOAD_ONLY_FROM_CACHE;
+
+ {
+ // Set a different file handle value to MockDiskEntry so any new entry
+ // created in the cache won't have the same file handle value.
+ MockDiskEntry::set_global_platform_file(kFakePlatformFile2);
+
+ scoped_ptr<net::HttpTransaction> trans(
+ cache.http_cache()->CreateTransaction());
+ ASSERT_TRUE(trans.get());
+
+ MockHttpRequest request(trans_info);
+
+ int rv = trans->Start(&request, &callback);
+ if (rv == net::ERR_IO_PENDING)
+ rv = callback.WaitForResult();
+ ASSERT_EQ(net::OK, rv);
+
+ const net::HttpResponseInfo* response = trans->GetResponseInfo();
+ ASSERT_TRUE(response);
+
+ // Make sure we get the same file handle as in the first request.
+ ASSERT_EQ(kFakePlatformFile1, response->response_data_file);
+
+ ReadAndVerifyTransaction(trans.get(), trans_info);
+ }
+}
diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc
index 23f53f7..8ba6bfc 100644
--- a/net/http/http_network_layer.cc
+++ b/net/http/http_network_layer.cc
@@ -21,13 +21,26 @@ HttpTransactionFactory* HttpNetworkLayer::CreateFactory(
return new HttpNetworkLayer(proxy_service);
}
+// static
+HttpTransactionFactory* HttpNetworkLayer::CreateFactory(
+ HttpNetworkSession* session) {
+ DCHECK(session);
+
+ return new HttpNetworkLayer(session);
+}
+
//-----------------------------------------------------------------------------
HttpNetworkLayer::HttpNetworkLayer(ProxyService* proxy_service)
- : proxy_service_(proxy_service), suspended_(false) {
+ : proxy_service_(proxy_service), session_(NULL), suspended_(false) {
DCHECK(proxy_service_);
}
+HttpNetworkLayer::HttpNetworkLayer(HttpNetworkSession* session)
+ : proxy_service_(NULL), session_(session), suspended_(false) {
+ DCHECK(session_.get());
+}
+
HttpNetworkLayer::~HttpNetworkLayer() {
}
@@ -35,11 +48,8 @@ HttpTransaction* HttpNetworkLayer::CreateTransaction() {
if (suspended_)
return NULL;
- if (!session_)
- session_ = new HttpNetworkSession(proxy_service_);
-
return new HttpNetworkTransaction(
- session_, ClientSocketFactory::GetDefaultFactory());
+ GetSession(), ClientSocketFactory::GetDefaultFactory());
}
HttpCache* HttpNetworkLayer::GetCache() {
@@ -53,5 +63,13 @@ void HttpNetworkLayer::Suspend(bool suspend) {
session_->connection_pool()->CloseIdleSockets();
}
+HttpNetworkSession* HttpNetworkLayer::GetSession() {
+ if (!session_) {
+ DCHECK(proxy_service_);
+ session_ = new HttpNetworkSession(proxy_service_);
+ }
+ return session_;
+}
+
} // namespace net
diff --git a/net/http/http_network_layer.h b/net/http/http_network_layer.h
index 8424b2f..a2bb491 100644
--- a/net/http/http_network_layer.h
+++ b/net/http/http_network_layer.h
@@ -19,17 +19,29 @@ class HttpNetworkLayer : public HttpTransactionFactory {
public:
// |proxy_service| must remain valid for the lifetime of HttpNetworkLayer.
explicit HttpNetworkLayer(ProxyService* proxy_service);
+ // Construct a HttpNetworkLayer with an existing HttpNetworkSession which
+ // contains a valid ProxyService.
+ explicit HttpNetworkLayer(HttpNetworkSession* session);
~HttpNetworkLayer();
// This function hides the details of how a network layer gets instantiated
// and allows other implementations to be substituted.
static HttpTransactionFactory* CreateFactory(ProxyService* proxy_service);
+ // Create a transaction factory that instantiate a network layer over an
+ // existing network session. Network session contains some valuable
+ // information (e.g. authentication data) that we want to share across
+ // multiple network layers. This method exposes the implementation details
+ // of a network layer, use this method with an existing network layer only
+ // when network session is shared.
+ static HttpTransactionFactory* CreateFactory(HttpNetworkSession* session);
// HttpTransactionFactory methods:
virtual HttpTransaction* CreateTransaction();
virtual HttpCache* GetCache();
virtual void Suspend(bool suspend);
+ HttpNetworkSession* GetSession();
+
private:
// The proxy service being used for the session.
ProxyService* proxy_service_;
diff --git a/net/http/http_response_info.h b/net/http/http_response_info.h
index 671a390..caa49df 100644
--- a/net/http/http_response_info.h
+++ b/net/http/http_response_info.h
@@ -5,6 +5,7 @@
#ifndef NET_HTTP_HTTP_RESPONSE_INFO_H_
#define NET_HTTP_HTTP_RESPONSE_INFO_H_
+#include "base/platform_file.h"
#include "base/time.h"
#include "net/base/auth.h"
#include "net/base/ssl_info.h"
@@ -48,6 +49,10 @@ class HttpResponseInfo {
// The "Vary" header data for this response.
HttpVaryData vary_data;
+
+ // Platform specific file handle to the response data, if response data is
+ // not in a standalone file, its value is base::kInvalidPlatformFileValue.
+ base::PlatformFile response_data_file;
};
} // namespace net
diff --git a/webkit/glue/resource_loader_bridge.h b/webkit/glue/resource_loader_bridge.h
index 2698cb4..948bc74 100644
--- a/webkit/glue/resource_loader_bridge.h
+++ b/webkit/glue/resource_loader_bridge.h
@@ -110,7 +110,7 @@ class ResourceLoaderBridge {
virtual void OnReceivedData(const char* data, int len) = 0;
// Called when the response is complete. This method signals completion of
- // the resource load.
+ // the resource load.ff
virtual void OnCompletedRequest(const URLRequestStatus& status) = 0;
// Returns the URL of the request, which allows us to display it in