diff options
-rw-r--r-- | chrome/browser/net/chrome_url_request_context.cc | 50 | ||||
-rw-r--r-- | chrome/browser/net/chrome_url_request_context.h | 10 | ||||
-rw-r--r-- | chrome/browser/profile.cc | 31 | ||||
-rw-r--r-- | chrome/browser/profile.h | 7 | ||||
-rw-r--r-- | chrome/common/chrome_constants.cc | 3 | ||||
-rw-r--r-- | chrome/common/chrome_constants.h | 2 | ||||
-rw-r--r-- | chrome/test/testing_profile.h | 3 | ||||
-rw-r--r-- | net/disk_cache/disk_cache.h | 24 | ||||
-rw-r--r-- | net/disk_cache/entry_impl.cc | 55 | ||||
-rw-r--r-- | net/disk_cache/entry_impl.h | 8 | ||||
-rw-r--r-- | net/disk_cache/entry_unittest.cc | 98 | ||||
-rw-r--r-- | net/disk_cache/mem_entry_impl.h | 8 | ||||
-rw-r--r-- | net/http/http_cache.cc | 49 | ||||
-rw-r--r-- | net/http/http_cache.h | 27 | ||||
-rw-r--r-- | net/http/http_cache_unittest.cc | 99 | ||||
-rw-r--r-- | net/http/http_network_layer.cc | 28 | ||||
-rw-r--r-- | net/http/http_network_layer.h | 12 | ||||
-rw-r--r-- | net/http/http_response_info.h | 5 | ||||
-rw-r--r-- | webkit/glue/resource_loader_bridge.h | 2 |
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 |