diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/net/http | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/net/http')
141 files changed, 6439 insertions, 9829 deletions
diff --git a/chromium/net/http/disk_cache_based_quic_server_info.cc b/chromium/net/http/disk_cache_based_quic_server_info.cc new file mode 100644 index 00000000000..4cf036de3ba --- /dev/null +++ b/chromium/net/http/disk_cache_based_quic_server_info.cc @@ -0,0 +1,295 @@ +// Copyright 2014 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/http/disk_cache_based_quic_server_info.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "net/base/completion_callback.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/http/http_cache.h" +#include "net/http/http_network_session.h" +#include "net/quic/quic_server_id.h" + +namespace net { + +// Some APIs inside disk_cache take a handle that the caller must keep alive +// until the API has finished its asynchronous execution. +// +// Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the +// operation completes causing a use-after-free. +// +// This data shim struct is meant to provide a location for the disk_cache +// APIs to write into even if the originating DiskCacheBasedQuicServerInfo +// object has been deleted. The lifetime for instances of this struct +// should be bound to the CompletionCallback that is passed to the disk_cache +// API. We do this by binding an instance of this struct to an unused +// parameter for OnIOComplete() using base::Owned(). +// +// This is a hack. A better fix is to make it so that the disk_cache APIs +// take a Callback to a mutator for setting the output value rather than +// writing into a raw handle. Then the caller can just pass in a Callback +// bound to WeakPtr for itself. This callback would correctly "no-op" itself +// when the DiskCacheBasedQuicServerInfo object is deleted. +// +// TODO(ajwong): Change disk_cache's API to return results via Callback. +struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim { + CacheOperationDataShim() : backend(NULL), entry(NULL) {} + + disk_cache::Backend* backend; + disk_cache::Entry* entry; +}; + +DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo( + const QuicServerId& server_id, + HttpCache* http_cache) + : QuicServerInfo(server_id), + weak_factory_(this), + data_shim_(new CacheOperationDataShim()), + io_callback_( + base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete, + weak_factory_.GetWeakPtr(), + base::Owned(data_shim_))), // Ownership assigned. + state_(GET_BACKEND), + ready_(false), + found_entry_(false), + server_id_(server_id), + http_cache_(http_cache), + backend_(NULL), + entry_(NULL) { +} + +void DiskCacheBasedQuicServerInfo::Start() { + DCHECK(CalledOnValidThread()); + DCHECK_EQ(GET_BACKEND, state_); + DoLoop(OK); +} + +int DiskCacheBasedQuicServerInfo::WaitForDataReady( + const CompletionCallback& callback) { + DCHECK(CalledOnValidThread()); + DCHECK_NE(GET_BACKEND, state_); + + if (ready_) + return OK; + + if (!callback.is_null()) { + // Prevent a new callback for WaitForDataReady overwriting an existing + // pending callback (|user_callback_|). + if (!user_callback_.is_null()) + return ERR_INVALID_ARGUMENT; + user_callback_ = callback; + } + + return ERR_IO_PENDING; +} + +bool DiskCacheBasedQuicServerInfo::IsDataReady() { + return ready_; +} + +bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() { + // The data can be persisted if it has been loaded from the disk cache + // and there are no pending writes. + return ready_ && new_data_.empty(); +} + +void DiskCacheBasedQuicServerInfo::Persist() { + DCHECK(CalledOnValidThread()); + DCHECK_NE(GET_BACKEND, state_); + + DCHECK(new_data_.empty()); + CHECK(ready_); + DCHECK(user_callback_.is_null()); + new_data_ = Serialize(); + + if (!backend_) + return; + + state_ = CREATE_OR_OPEN; + DoLoop(OK); +} + +DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() { + DCHECK(user_callback_.is_null()); + if (entry_) + entry_->Close(); +} + +std::string DiskCacheBasedQuicServerInfo::key() const { + return "quicserverinfo:" + server_id_.ToString(); +} + +void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused, + int rv) { + DCHECK_NE(NONE, state_); + rv = DoLoop(rv); + if (rv != ERR_IO_PENDING && !user_callback_.is_null()) { + CompletionCallback callback = user_callback_; + user_callback_.Reset(); + callback.Run(rv); + } +} + +int DiskCacheBasedQuicServerInfo::DoLoop(int rv) { + do { + switch (state_) { + case GET_BACKEND: + rv = DoGetBackend(); + break; + case GET_BACKEND_COMPLETE: + rv = DoGetBackendComplete(rv); + break; + case OPEN: + rv = DoOpen(); + break; + case OPEN_COMPLETE: + rv = DoOpenComplete(rv); + break; + case READ: + rv = DoRead(); + break; + case READ_COMPLETE: + rv = DoReadComplete(rv); + break; + case WAIT_FOR_DATA_READY_DONE: + rv = DoWaitForDataReadyDone(); + break; + case CREATE_OR_OPEN: + rv = DoCreateOrOpen(); + break; + case CREATE_OR_OPEN_COMPLETE: + rv = DoCreateOrOpenComplete(rv); + break; + case WRITE: + rv = DoWrite(); + break; + case WRITE_COMPLETE: + rv = DoWriteComplete(rv); + break; + case SET_DONE: + rv = DoSetDone(); + break; + default: + rv = OK; + NOTREACHED(); + } + } while (rv != ERR_IO_PENDING && state_ != NONE); + + return rv; +} + +int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) { + if (rv == OK) { + backend_ = data_shim_->backend; + state_ = OPEN; + } else { + state_ = WAIT_FOR_DATA_READY_DONE; + } + return OK; +} + +int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) { + if (rv == OK) { + entry_ = data_shim_->entry; + state_ = READ; + found_entry_ = true; + } else { + state_ = WAIT_FOR_DATA_READY_DONE; + } + + return OK; +} + +int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) { + if (rv > 0) + data_.assign(read_buffer_->data(), rv); + + state_ = WAIT_FOR_DATA_READY_DONE; + return OK; +} + +int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) { + state_ = SET_DONE; + return OK; +} + +int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) { + if (rv != OK) { + state_ = SET_DONE; + } else { + entry_ = data_shim_->entry; + state_ = WRITE; + } + return OK; +} + +int DiskCacheBasedQuicServerInfo::DoGetBackend() { + state_ = GET_BACKEND_COMPLETE; + return http_cache_->GetBackend(&data_shim_->backend, io_callback_); +} + +int DiskCacheBasedQuicServerInfo::DoOpen() { + state_ = OPEN_COMPLETE; + return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); +} + +int DiskCacheBasedQuicServerInfo::DoRead() { + const int32 size = entry_->GetDataSize(0 /* index */); + if (!size) { + state_ = WAIT_FOR_DATA_READY_DONE; + return OK; + } + + read_buffer_ = new IOBuffer(size); + state_ = READ_COMPLETE; + return entry_->ReadData( + 0 /* index */, 0 /* offset */, read_buffer_, size, io_callback_); +} + +int DiskCacheBasedQuicServerInfo::DoWrite() { + write_buffer_ = new IOBuffer(new_data_.size()); + memcpy(write_buffer_->data(), new_data_.data(), new_data_.size()); + state_ = WRITE_COMPLETE; + + return entry_->WriteData( + 0 /* index */, 0 /* offset */, write_buffer_, new_data_.size(), + io_callback_, true /* truncate */); +} + +int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() { + DCHECK(entry_ == NULL); + state_ = CREATE_OR_OPEN_COMPLETE; + if (found_entry_) { + return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); + } + + return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_); +} + +int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() { + DCHECK(!ready_); + state_ = NONE; + ready_ = true; + // We close the entry because, if we shutdown before ::Persist is called, + // then we might leak a cache reference, which causes a DCHECK on shutdown. + if (entry_) + entry_->Close(); + entry_ = NULL; + Parse(data_); + return OK; +} + +int DiskCacheBasedQuicServerInfo::DoSetDone() { + if (entry_) + entry_->Close(); + entry_ = NULL; + new_data_.clear(); + state_ = NONE; + return OK; +} + +} // namespace net diff --git a/chromium/net/http/disk_cache_based_quic_server_info.h b/chromium/net/http/disk_cache_based_quic_server_info.h new file mode 100644 index 00000000000..2a18ef5abc8 --- /dev/null +++ b/chromium/net/http/disk_cache_based_quic_server_info.h @@ -0,0 +1,106 @@ +// Copyright 2014 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. + +#ifndef NET_HTTP_DISK_CACHE_BASED_QUIC_SERVER_INFO_H_ +#define NET_HTTP_DISK_CACHE_BASED_QUIC_SERVER_INFO_H_ + +#include <string> + +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "base/threading/non_thread_safe.h" +#include "net/base/completion_callback.h" +#include "net/disk_cache/disk_cache.h" +#include "net/quic/crypto/quic_server_info.h" + +namespace net { + +class HttpCache; +class IOBuffer; +class QuicServerId; + +// DiskCacheBasedQuicServerInfo fetches information about a QUIC server from +// our standard disk cache. Since the information is defined to be +// non-sensitive, it's ok for us to keep it on disk. +class NET_EXPORT_PRIVATE DiskCacheBasedQuicServerInfo + : public QuicServerInfo, + public NON_EXPORTED_BASE(base::NonThreadSafe) { + public: + DiskCacheBasedQuicServerInfo(const QuicServerId& server_id, + HttpCache* http_cache); + + // QuicServerInfo implementation. + virtual void Start() OVERRIDE; + virtual int WaitForDataReady(const CompletionCallback& callback) OVERRIDE; + virtual bool IsDataReady() OVERRIDE; + virtual bool IsReadyToPersist() OVERRIDE; + virtual void Persist() OVERRIDE; + + private: + struct CacheOperationDataShim; + enum State { + GET_BACKEND, + GET_BACKEND_COMPLETE, + OPEN, + OPEN_COMPLETE, + READ, + READ_COMPLETE, + WAIT_FOR_DATA_READY_DONE, + CREATE_OR_OPEN, + CREATE_OR_OPEN_COMPLETE, + WRITE, + WRITE_COMPLETE, + SET_DONE, + NONE, + }; + + virtual ~DiskCacheBasedQuicServerInfo(); + + std::string key() const; + + // The |unused| parameter is a small hack so that we can have the + // CacheOperationDataShim object owned by the Callback that is created for + // this method. See comment above CacheOperationDataShim for details. + void OnIOComplete(CacheOperationDataShim* unused, int rv); + + int DoLoop(int rv); + + int DoGetBackendComplete(int rv); + int DoOpenComplete(int rv); + int DoReadComplete(int rv); + int DoWriteComplete(int rv); + int DoCreateOrOpenComplete(int rv); + + int DoGetBackend(); + int DoOpen(); + int DoRead(); + int DoWrite(); + int DoCreateOrOpen(); + + // DoWaitForDataReadyDone is the terminal state of the read operation. + int DoWaitForDataReadyDone(); + + // DoSetDone is the terminal state of the write operation. + int DoSetDone(); + + base::WeakPtrFactory<DiskCacheBasedQuicServerInfo> weak_factory_; + CacheOperationDataShim* data_shim_; // Owned by |io_callback_|. + CompletionCallback io_callback_; + State state_; + bool ready_; + bool found_entry_; // Controls the behavior of DoCreateOrOpen. + std::string new_data_; + const QuicServerId server_id_; + HttpCache* const http_cache_; + disk_cache::Backend* backend_; + disk_cache::Entry* entry_; + CompletionCallback user_callback_; + scoped_refptr<IOBuffer> read_buffer_; + scoped_refptr<IOBuffer> write_buffer_; + std::string data_; +}; + +} // namespace net + +#endif // NET_HTTP_DISK_CACHE_BASED_QUIC_SERVER_INFO_H_ diff --git a/chromium/net/http/disk_cache_based_quic_server_info_unittest.cc b/chromium/net/http/disk_cache_based_quic_server_info_unittest.cc new file mode 100644 index 00000000000..52b832dbbda --- /dev/null +++ b/chromium/net/http/disk_cache_based_quic_server_info_unittest.cc @@ -0,0 +1,282 @@ +// Copyright 2014 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/http/disk_cache_based_quic_server_info.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "net/base/net_errors.h" +#include "net/http/mock_http_cache.h" +#include "net/quic/crypto/quic_server_info.h" +#include "net/quic/quic_server_id.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace { + +// This is an empty transaction, needed to register the URL and the test mode. +const MockTransaction kHostInfoTransaction1 = { + "quicserverinfo:https://www.google.com:443", + "", + base::Time(), + "", + LOAD_NORMAL, + "", + "", + base::Time(), + "", + TEST_MODE_NORMAL, + NULL, + 0 +}; + +const MockTransaction kHostInfoTransaction2 = { + "quicserverinfo:http://www.google.com:80", + "", + base::Time(), + "", + LOAD_NORMAL, + "", + "", + base::Time(), + "", + TEST_MODE_NORMAL, + NULL, + 0 +}; + +} // namespace + +// Tests that we can delete a DiskCacheBasedQuicServerInfo object in a +// completion callback for DiskCacheBasedQuicServerInfo::WaitForDataReady. +TEST(DiskCacheBasedQuicServerInfo, DeleteInCallback) { + // Use the blocking mock backend factory to force asynchronous completion + // of quic_server_info->WaitForDataReady(), so that the callback will run. + MockBlockingBackendFactory* factory = new MockBlockingBackendFactory(); + MockHttpCache cache(factory); + QuicServerId server_id("www.verisign.com", 443, true, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + TestCompletionCallback callback; + int rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(ERR_IO_PENDING, rv); + // Now complete the backend creation and let the callback run. + factory->FinishCreation(); + EXPECT_EQ(OK, callback.GetResult(rv)); +} + +// Tests the basic logic of storing, retrieving and updating data. +TEST(DiskCacheBasedQuicServerInfo, Update) { + MockHttpCache cache; + AddMockTransaction(&kHostInfoTransaction1); + TestCompletionCallback callback; + + QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + int rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + + QuicServerInfo::State* state = quic_server_info->mutable_state(); + EXPECT_TRUE(state->certs.empty()); + const string server_config_a = "server_config_a"; + const string source_address_token_a = "source_address_token_a"; + const string server_config_sig_a = "server_config_sig_a"; + const string cert_a = "cert_a"; + const string cert_b = "cert_b"; + + state->server_config = server_config_a; + state->source_address_token = source_address_token_a; + state->server_config_sig = server_config_sig_a; + state->certs.push_back(cert_a); + quic_server_info->Persist(); + + // Wait until Persist() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + // Open the stored QuicServerInfo. + quic_server_info.reset( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + + // And now update the data. + state = quic_server_info->mutable_state(); + state->certs.push_back(cert_b); + + // Fail instead of DCHECKing double creates. + cache.disk_cache()->set_double_create_check(false); + quic_server_info->Persist(); + base::MessageLoop::current()->RunUntilIdle(); + + // Verify that the state was updated. + quic_server_info.reset( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + const QuicServerInfo::State& state1 = quic_server_info->state(); + EXPECT_EQ(server_config_a, state1.server_config); + EXPECT_EQ(source_address_token_a, state1.source_address_token); + EXPECT_EQ(server_config_sig_a, state1.server_config_sig); + EXPECT_EQ(2U, state1.certs.size()); + EXPECT_EQ(cert_a, state1.certs[0]); + EXPECT_EQ(cert_b, state1.certs[1]); + + RemoveMockTransaction(&kHostInfoTransaction1); +} + +// Test that demonstrates different info is returned when the ports differ. +TEST(DiskCacheBasedQuicServerInfo, UpdateDifferentPorts) { + MockHttpCache cache; + AddMockTransaction(&kHostInfoTransaction1); + AddMockTransaction(&kHostInfoTransaction2); + TestCompletionCallback callback; + + // Persist data for port 443. + QuicServerId server_id1("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info1( + new DiskCacheBasedQuicServerInfo(server_id1, cache.http_cache())); + quic_server_info1->Start(); + int rv = quic_server_info1->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + + QuicServerInfo::State* state1 = quic_server_info1->mutable_state(); + EXPECT_TRUE(state1->certs.empty()); + const string server_config_a = "server_config_a"; + const string source_address_token_a = "source_address_token_a"; + const string server_config_sig_a = "server_config_sig_a"; + const string cert_a = "cert_a"; + + state1->server_config = server_config_a; + state1->source_address_token = source_address_token_a; + state1->server_config_sig = server_config_sig_a; + state1->certs.push_back(cert_a); + quic_server_info1->Persist(); + + // Wait until Persist() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + // Persist data for port 80. + QuicServerId server_id2("www.google.com", 80, false, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info2( + new DiskCacheBasedQuicServerInfo(server_id2, cache.http_cache())); + quic_server_info2->Start(); + rv = quic_server_info2->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + + QuicServerInfo::State* state2 = quic_server_info2->mutable_state(); + EXPECT_TRUE(state2->certs.empty()); + const string server_config_b = "server_config_b"; + const string source_address_token_b = "source_address_token_b"; + const string server_config_sig_b = "server_config_sig_b"; + const string cert_b = "cert_b"; + + state2->server_config = server_config_b; + state2->source_address_token = source_address_token_b; + state2->server_config_sig = server_config_sig_b; + state2->certs.push_back(cert_b); + quic_server_info2->Persist(); + + // Wait until Persist() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + // Verify the stored QuicServerInfo for port 443. + scoped_ptr<QuicServerInfo> quic_server_info( + new DiskCacheBasedQuicServerInfo(server_id1, cache.http_cache())); + quic_server_info->Start(); + rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + const QuicServerInfo::State& state_a = quic_server_info->state(); + EXPECT_EQ(server_config_a, state_a.server_config); + EXPECT_EQ(source_address_token_a, state_a.source_address_token); + EXPECT_EQ(server_config_sig_a, state_a.server_config_sig); + EXPECT_EQ(1U, state_a.certs.size()); + EXPECT_EQ(cert_a, state_a.certs[0]); + + // Verify the stored QuicServerInfo for port 80. + quic_server_info.reset( + new DiskCacheBasedQuicServerInfo(server_id2, cache.http_cache())); + quic_server_info->Start(); + rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + const QuicServerInfo::State& state_b = quic_server_info->state(); + EXPECT_EQ(server_config_b, state_b.server_config); + EXPECT_EQ(source_address_token_b, state_b.source_address_token); + EXPECT_EQ(server_config_sig_b, state_b.server_config_sig); + EXPECT_EQ(1U, state_b.certs.size()); + EXPECT_EQ(cert_b, state_b.certs[0]); + + RemoveMockTransaction(&kHostInfoTransaction2); + RemoveMockTransaction(&kHostInfoTransaction1); +} + +// Test IsReadyToPersist when there is a pending write. +TEST(DiskCacheBasedQuicServerInfo, IsReadyToPersist) { + MockHttpCache cache; + AddMockTransaction(&kHostInfoTransaction1); + TestCompletionCallback callback; + + QuicServerId server_id("www.google.com", 443, true, PRIVACY_MODE_DISABLED); + scoped_ptr<QuicServerInfo> quic_server_info( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + EXPECT_FALSE(quic_server_info->IsDataReady()); + quic_server_info->Start(); + int rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + QuicServerInfo::State* state = quic_server_info->mutable_state(); + EXPECT_TRUE(state->certs.empty()); + const string server_config_a = "server_config_a"; + const string source_address_token_a = "source_address_token_a"; + const string server_config_sig_a = "server_config_sig_a"; + const string cert_a = "cert_a"; + + state->server_config = server_config_a; + state->source_address_token = source_address_token_a; + state->server_config_sig = server_config_sig_a; + state->certs.push_back(cert_a); + EXPECT_TRUE(quic_server_info->IsReadyToPersist()); + quic_server_info->Persist(); + + // Once we call Persist, IsReadyToPersist should return false until Persist + // has completed. + EXPECT_FALSE(quic_server_info->IsReadyToPersist()); + + // Wait until Persist() does the work. + base::MessageLoop::current()->RunUntilIdle(); + + EXPECT_TRUE(quic_server_info->IsReadyToPersist()); + + // Verify that the state was updated. + quic_server_info.reset( + new DiskCacheBasedQuicServerInfo(server_id, cache.http_cache())); + quic_server_info->Start(); + rv = quic_server_info->WaitForDataReady(callback.callback()); + EXPECT_EQ(OK, callback.GetResult(rv)); + EXPECT_TRUE(quic_server_info->IsDataReady()); + + const QuicServerInfo::State& state1 = quic_server_info->state(); + EXPECT_EQ(server_config_a, state1.server_config); + EXPECT_EQ(source_address_token_a, state1.source_address_token); + EXPECT_EQ(server_config_sig_a, state1.server_config_sig); + EXPECT_EQ(1U, state1.certs.size()); + EXPECT_EQ(cert_a, state1.certs[0]); + + RemoveMockTransaction(&kHostInfoTransaction1); +} + +} // namespace net diff --git a/chromium/net/http/failing_http_transaction_factory.cc b/chromium/net/http/failing_http_transaction_factory.cc new file mode 100644 index 00000000000..d1d1612966d --- /dev/null +++ b/chromium/net/http/failing_http_transaction_factory.cc @@ -0,0 +1,191 @@ +// Copyright 2014 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/http/failing_http_transaction_factory.h" + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "net/base/load_timing_info.h" +#include "net/base/upload_progress.h" + +namespace net { + +class AuthCredentials; +class BoundNetLog; +class HttpRequestHeaders; +class IOBuffer; +class X509Certificate; + +struct HttpRequestInfo; + +namespace { + +// A simple class to interpose between the cache and network http layers. +// These transactions can be generated by the FailingHttpTransactionFactory +// to test interactions between cache and network. +class FailingHttpTransaction : public HttpTransaction { + public: + explicit FailingHttpTransaction(Error error); + virtual ~FailingHttpTransaction(); + + // HttpTransaction + virtual int Start(const HttpRequestInfo* request_info, + const CompletionCallback& callback, + const BoundNetLog& net_log) OVERRIDE; + virtual int RestartIgnoringLastError( + const CompletionCallback& callback) OVERRIDE; + virtual int RestartWithCertificate( + X509Certificate* client_cert, + const CompletionCallback& callback) OVERRIDE; + virtual int RestartWithAuth( + const AuthCredentials& credentials, + const CompletionCallback& callback) OVERRIDE; + virtual bool IsReadyToRestartForAuth() OVERRIDE; + virtual int Read(IOBuffer* buf, int buf_len, + const CompletionCallback& callback) OVERRIDE; + virtual void StopCaching() OVERRIDE; + virtual bool GetFullRequestHeaders( + HttpRequestHeaders* headers) const OVERRIDE; + virtual int64 GetTotalReceivedBytes() const OVERRIDE; + virtual void DoneReading() OVERRIDE; + virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; + virtual LoadState GetLoadState() const OVERRIDE; + virtual UploadProgress GetUploadProgress() const OVERRIDE; + virtual void SetQuicServerInfo( + net::QuicServerInfo* quic_server_info) OVERRIDE; + virtual bool GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const OVERRIDE; + virtual void SetPriority(RequestPriority priority) OVERRIDE; + virtual void SetWebSocketHandshakeStreamCreateHelper( + WebSocketHandshakeStreamBase::CreateHelper* create_helper) OVERRIDE; + virtual void SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) OVERRIDE; + virtual int ResumeNetworkStart() OVERRIDE; + + private: + Error error_; +}; + +FailingHttpTransaction::FailingHttpTransaction(Error error) : error_(error) { + DCHECK_LT(error, OK); +} + +FailingHttpTransaction::~FailingHttpTransaction() {} + +int FailingHttpTransaction::Start(const HttpRequestInfo* request_info, + const CompletionCallback& callback, + const BoundNetLog& net_log) { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(callback, error_)); + return ERR_IO_PENDING; +} + +int FailingHttpTransaction::RestartIgnoringLastError( + const CompletionCallback& callback) { + return ERR_FAILED; +} + +int FailingHttpTransaction::RestartWithCertificate( + X509Certificate* client_cert, + const CompletionCallback& callback) { + return ERR_FAILED; +} + +int FailingHttpTransaction::RestartWithAuth( + const AuthCredentials& credentials, + const CompletionCallback& callback) { + return ERR_FAILED; +} + +bool FailingHttpTransaction::IsReadyToRestartForAuth() { + return false; +} + +int FailingHttpTransaction::Read(IOBuffer* buf, int buf_len, + const CompletionCallback& callback) { + NOTREACHED(); + return ERR_FAILED; +} + +void FailingHttpTransaction::StopCaching() {} + +bool FailingHttpTransaction::GetFullRequestHeaders( + HttpRequestHeaders* headers) const { + return false; +} + +int64 FailingHttpTransaction::GetTotalReceivedBytes() const { + return 0; +} + +void FailingHttpTransaction::DoneReading() { + NOTREACHED(); +} + +const HttpResponseInfo* FailingHttpTransaction::GetResponseInfo() const { + return NULL; +} + +LoadState FailingHttpTransaction::GetLoadState() const { + return LOAD_STATE_IDLE; +} + +UploadProgress FailingHttpTransaction::GetUploadProgress() const { + return UploadProgress(); +} + +void FailingHttpTransaction::SetQuicServerInfo( + net::QuicServerInfo* quic_server_info) {} + +bool FailingHttpTransaction::GetLoadTimingInfo( + LoadTimingInfo* load_timing_info) const { + return false; +} + +void FailingHttpTransaction::SetPriority(RequestPriority priority) {} + +void FailingHttpTransaction::SetWebSocketHandshakeStreamCreateHelper( + WebSocketHandshakeStreamBase::CreateHelper* create_helper) { + NOTREACHED(); +} + +void FailingHttpTransaction::SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) { +} + +int FailingHttpTransaction::ResumeNetworkStart() { + NOTREACHED(); + return ERR_FAILED; +} + +} // namespace + +FailingHttpTransactionFactory::FailingHttpTransactionFactory( + HttpNetworkSession* session, + Error error) : session_(session), error_(error) { + DCHECK_LT(error, OK); +} + +FailingHttpTransactionFactory::~FailingHttpTransactionFactory() {} + +// HttpTransactionFactory: +int FailingHttpTransactionFactory::CreateTransaction( + RequestPriority priority, + scoped_ptr<HttpTransaction>* trans) { + trans->reset(new FailingHttpTransaction(error_)); + return OK; +} + +HttpCache* FailingHttpTransactionFactory::GetCache() { + return NULL; +} + +HttpNetworkSession* FailingHttpTransactionFactory::GetSession() { + return session_; +} + +} // namespace net + diff --git a/chromium/net/http/failing_http_transaction_factory.h b/chromium/net/http/failing_http_transaction_factory.h new file mode 100644 index 00000000000..84d87a7a3d3 --- /dev/null +++ b/chromium/net/http/failing_http_transaction_factory.h @@ -0,0 +1,42 @@ +// Copyright 2014 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. + +#ifndef NET_FAILING_HTTP_TRANSACTION_FACTORY_H_ +#define NET_FAILING_HTTP_TRANSACTION_FACTORY_H_ + +#include "base/memory/scoped_ptr.h" +#include "net/base/net_errors.h" +#include "net/base/request_priority.h" +#include "net/http/http_transaction.h" +#include "net/http/http_transaction_factory.h" + +namespace net { + +class HttpCache; +class HttpNetworkSession; + +// Creates transactions that always (asynchronously) return a specified +// error. The error is returned asynchronously, just after the transaction is +// started. +class NET_EXPORT FailingHttpTransactionFactory : public HttpTransactionFactory { + public: + // The caller must guarantee that |session| outlives this object. + FailingHttpTransactionFactory(HttpNetworkSession* session, Error error); + virtual ~FailingHttpTransactionFactory(); + + // HttpTransactionFactory: + virtual int CreateTransaction( + RequestPriority priority, + scoped_ptr<HttpTransaction>* trans) OVERRIDE; + virtual HttpCache* GetCache() OVERRIDE; + virtual HttpNetworkSession* GetSession() OVERRIDE; + + private: + HttpNetworkSession* session_; + Error error_; +}; + +} // namespace net + +#endif // NET_FAILING_HTTP_TRANSACTION_FACTORY_H_ diff --git a/chromium/net/http/http_auth.cc b/chromium/net/http/http_auth.cc index 3cc9db1a122..4c6d3e8dda3 100644 --- a/chromium/net/http/http_auth.cc +++ b/chromium/net/http/http_auth.cc @@ -10,6 +10,7 @@ #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_handler.h" #include "net/http/http_auth_handler_factory.h" #include "net/http/http_request_headers.h" @@ -74,7 +75,7 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse( HttpAuth::AuthorizationResult authorization_result = HttpAuth::AUTHORIZATION_RESULT_INVALID; while (headers->EnumerateHeader(&iter, header_name, &challenge)) { - HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end()); + HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end()); if (!LowerCaseEqualsASCII(props.scheme(), current_scheme_name.c_str())) continue; authorization_result = handler->HandleAnotherChallenge(&props); @@ -87,56 +88,6 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse( return HttpAuth::AUTHORIZATION_RESULT_REJECT; } -HttpAuth::ChallengeTokenizer::ChallengeTokenizer( - std::string::const_iterator begin, - std::string::const_iterator end) - : begin_(begin), - end_(end), - scheme_begin_(begin), - scheme_end_(begin), - params_begin_(end), - params_end_(end) { - Init(begin, end); -} - -HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs() - const { - return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ','); -} - -std::string HttpAuth::ChallengeTokenizer::base64_param() const { - // Strip off any padding. - // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.) - // - // Our base64 decoder requires that the length be a multiple of 4. - int encoded_length = params_end_ - params_begin_; - while (encoded_length > 0 && encoded_length % 4 != 0 && - params_begin_[encoded_length - 1] == '=') { - --encoded_length; - } - return std::string(params_begin_, params_begin_ + encoded_length); -} - -void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin, - std::string::const_iterator end) { - // The first space-separated token is the auth-scheme. - // NOTE: we are more permissive than RFC 2617 which says auth-scheme - // is separated by 1*SP. - base::StringTokenizer tok(begin, end, HTTP_LWS); - if (!tok.GetNext()) { - // Default param and scheme iterators provide empty strings - return; - } - - // Save the scheme's position. - scheme_begin_ = tok.token_begin(); - scheme_end_ = tok.token_end(); - - params_begin_ = scheme_end_; - params_end_ = end; - HttpUtil::TrimLWS(¶ms_begin_, ¶ms_end_); -} - // static std::string HttpAuth::GetChallengeHeaderName(Target target) { switch (target) { diff --git a/chromium/net/http/http_auth.h b/chromium/net/http/http_auth.h index 1a03bb1fe1f..5a2c7ac6606 100644 --- a/chromium/net/http/http_auth.h +++ b/chromium/net/http/http_auth.h @@ -167,48 +167,6 @@ class NET_EXPORT_PRIVATE HttpAuth { Target target, const std::set<Scheme>& disabled_schemes, std::string* challenge_used); - - // Breaks up a challenge string into the the auth scheme and parameter list, - // according to RFC 2617 Sec 1.2: - // challenge = auth-scheme 1*SP 1#auth-param - // - // Depending on the challenge scheme, it may be appropriate to interpret the - // parameters as either a base-64 encoded string or a comma-delimited list - // of name-value pairs. param_pairs() and base64_param() methods are provided - // to support either usage. - class NET_EXPORT_PRIVATE ChallengeTokenizer { - public: - ChallengeTokenizer(std::string::const_iterator begin, - std::string::const_iterator end); - - // Get the original text. - std::string challenge_text() const { - return std::string(begin_, end_); - } - - // Get the auth scheme of the challenge. - std::string::const_iterator scheme_begin() const { return scheme_begin_; } - std::string::const_iterator scheme_end() const { return scheme_end_; } - std::string scheme() const { - return std::string(scheme_begin_, scheme_end_); - } - - HttpUtil::NameValuePairsIterator param_pairs() const; - std::string base64_param() const; - - private: - void Init(std::string::const_iterator begin, - std::string::const_iterator end); - - std::string::const_iterator begin_; - std::string::const_iterator end_; - - std::string::const_iterator scheme_begin_; - std::string::const_iterator scheme_end_; - - std::string::const_iterator params_begin_; - std::string::const_iterator params_end_; - }; }; } // namespace net diff --git a/chromium/net/http/http_auth_cache.cc b/chromium/net/http/http_auth_cache.cc index 63bad076df1..d989830b2aa 100644 --- a/chromium/net/http/http_auth_cache.cc +++ b/chromium/net/http/http_auth_cache.cc @@ -5,6 +5,7 @@ #include "net/http/http_auth_cache.h" #include "base/logging.h" +#include "base/metrics/histogram.h" #include "base/strings/string_util.h" namespace { @@ -57,6 +58,14 @@ struct IsEnclosedBy { const std::string& path; }; +void RecordLookupPosition(int position) { + UMA_HISTOGRAM_COUNTS_100("Net.HttpAuthCacheLookupPosition", position); +} + +void RecordLookupByPathPosition(int position) { + UMA_HISTOGRAM_COUNTS_100("Net.HttpAuthCacheLookupByPathPosition", position); +} + } // namespace namespace net { @@ -73,12 +82,18 @@ HttpAuthCache::Entry* HttpAuthCache::Lookup(const GURL& origin, HttpAuth::Scheme scheme) { CheckOriginIsValid(origin); + int entries_examined = 0; // Linear scan through the realm entries. for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) { + ++entries_examined; if (it->origin() == origin && it->realm() == realm && - it->scheme() == scheme) + it->scheme() == scheme) { + it->last_use_time_ = base::TimeTicks::Now(); + RecordLookupPosition(entries_examined); return &(*it); + } } + RecordLookupPosition(0); return NULL; // No realm entry found. } @@ -89,6 +104,7 @@ HttpAuthCache::Entry* HttpAuthCache::LookupByPath(const GURL& origin, const std::string& path) { HttpAuthCache::Entry* best_match = NULL; size_t best_match_length = 0; + int best_match_position = 0; CheckOriginIsValid(origin); CheckPathIsValid(path); @@ -98,15 +114,21 @@ HttpAuthCache::Entry* HttpAuthCache::LookupByPath(const GURL& origin, // within the protection space ... std::string parent_dir = GetParentDirectory(path); + int entries_examined = 0; // Linear scan through the realm entries. for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) { + ++entries_examined; size_t len = 0; if (it->origin() == origin && it->HasEnclosingPath(parent_dir, &len) && (!best_match || len > best_match_length)) { - best_match_length = len; best_match = &(*it); + best_match_length = len; + best_match_position = entries_examined; } } + if (best_match) + best_match->last_use_time_ = base::TimeTicks::Now(); + RecordLookupByPathPosition(best_match_position); return best_match; } @@ -119,20 +141,30 @@ HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin, CheckOriginIsValid(origin); CheckPathIsValid(path); + base::TimeTicks now = base::TimeTicks::Now(); + // Check for existing entry (we will re-use it if present). HttpAuthCache::Entry* entry = Lookup(origin, realm, scheme); if (!entry) { + bool evicted = false; // Failsafe to prevent unbounded memory growth of the cache. if (entries_.size() >= kMaxNumRealmEntries) { LOG(WARNING) << "Num auth cache entries reached limit -- evicting"; + UMA_HISTOGRAM_LONG_TIMES("Net.HttpAuthCacheAddEvictedCreation", + now - entries_.back().creation_time_); + UMA_HISTOGRAM_LONG_TIMES("Net.HttpAuthCacheAddEvictedLastUse", + now - entries_.back().last_use_time_); entries_.pop_back(); + evicted = true; } + UMA_HISTOGRAM_BOOLEAN("Net.HttpAuthCacheAddEvicted", evicted); entries_.push_front(Entry()); entry = &entries_.front(); entry->origin_ = origin; entry->realm_ = realm; entry->scheme_ = scheme; + entry->creation_time_ = now; } DCHECK_EQ(origin, entry->origin_); DCHECK_EQ(realm, entry->realm_); @@ -142,6 +174,7 @@ HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin, entry->credentials_ = credentials; entry->nonce_count_ = 1; entry->AddPath(path); + entry->last_use_time_ = now; return entry; } @@ -166,12 +199,15 @@ void HttpAuthCache::Entry::AddPath(const std::string& path) { // Remove any entries that have been subsumed by the new entry. paths_.remove_if(IsEnclosedBy(parent_dir)); + bool evicted = false; // Failsafe to prevent unbounded memory growth of the cache. if (paths_.size() >= kMaxNumPathsPerRealmEntry) { LOG(WARNING) << "Num path entries for " << origin() << " has grown too large -- evicting"; paths_.pop_back(); + evicted = true; } + UMA_HISTOGRAM_BOOLEAN("Net.HttpAuthCacheAddPathEvicted", evicted); // Add new path. paths_.push_front(parent_dir); @@ -221,6 +257,7 @@ bool HttpAuthCache::UpdateStaleChallenge(const GURL& origin, if (!entry) return false; entry->UpdateStaleChallenge(auth_challenge); + entry->last_use_time_ = base::TimeTicks::Now(); return true; } diff --git a/chromium/net/http/http_auth_cache.h b/chromium/net/http/http_auth_cache.h index 75b379f25bd..707a571db96 100644 --- a/chromium/net/http/http_auth_cache.h +++ b/chromium/net/http/http_auth_cache.h @@ -10,6 +10,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" +#include "base/time/time.h" #include "net/base/net_export.h" #include "net/http/http_auth.h" #include "url/gurl.h" @@ -96,6 +97,11 @@ class NET_EXPORT_PRIVATE HttpAuthCache { // List of paths that define the realm's protection space. PathList paths_; + + // Times the entry was created and last used (by looking up, adding a path, + // or updating the challenge.) + base::TimeTicks creation_time_; + base::TimeTicks last_use_time_; }; // Prevent unbounded memory growth. These are safeguards for abuse; it is diff --git a/chromium/net/http/http_auth_cache_unittest.cc b/chromium/net/http/http_auth_cache_unittest.cc index a4ea1c6da03..e925c714316 100644 --- a/chromium/net/http/http_auth_cache_unittest.cc +++ b/chromium/net/http/http_auth_cache_unittest.cc @@ -13,6 +13,8 @@ #include "net/http/http_auth_handler.h" #include "testing/gtest/include/gtest/gtest.h" +using base::ASCIIToUTF16; + namespace net { namespace { @@ -31,12 +33,12 @@ class MockAuthHandler : public HttpAuthHandler { } virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) OVERRIDE { + HttpAuthChallengeTokenizer* challenge) OVERRIDE { return HttpAuth::AUTHORIZATION_RESULT_REJECT; } protected: - virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE { + virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE { return false; // Unused. } diff --git a/chromium/net/http/http_auth_challenge_tokenizer.cc b/chromium/net/http/http_auth_challenge_tokenizer.cc new file mode 100644 index 00000000000..528566c8664 --- /dev/null +++ b/chromium/net/http/http_auth_challenge_tokenizer.cc @@ -0,0 +1,61 @@ +// Copyright 2014 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/http/http_auth_challenge_tokenizer.h" + +#include "base/strings/string_tokenizer.h" + +namespace net { + +HttpAuthChallengeTokenizer::HttpAuthChallengeTokenizer( + std::string::const_iterator begin, + std::string::const_iterator end) + : begin_(begin), + end_(end), + scheme_begin_(begin), + scheme_end_(begin), + params_begin_(end), + params_end_(end) { + Init(begin, end); +} + +HttpUtil::NameValuePairsIterator HttpAuthChallengeTokenizer::param_pairs() + const { + return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ','); +} + +std::string HttpAuthChallengeTokenizer::base64_param() const { + // Strip off any padding. + // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.) + // + // Our base64 decoder requires that the length be a multiple of 4. + int encoded_length = params_end_ - params_begin_; + while (encoded_length > 0 && encoded_length % 4 != 0 && + params_begin_[encoded_length - 1] == '=') { + --encoded_length; + } + return std::string(params_begin_, params_begin_ + encoded_length); +} + +void HttpAuthChallengeTokenizer::Init(std::string::const_iterator begin, + std::string::const_iterator end) { + // The first space-separated token is the auth-scheme. + // NOTE: we are more permissive than RFC 2617 which says auth-scheme + // is separated by 1*SP. + base::StringTokenizer tok(begin, end, HTTP_LWS); + if (!tok.GetNext()) { + // Default param and scheme iterators provide empty strings + return; + } + + // Save the scheme's position. + scheme_begin_ = tok.token_begin(); + scheme_end_ = tok.token_end(); + + params_begin_ = scheme_end_; + params_end_ = end; + HttpUtil::TrimLWS(¶ms_begin_, ¶ms_end_); +} + +} // namespace net diff --git a/chromium/net/http/http_auth_challenge_tokenizer.h b/chromium/net/http/http_auth_challenge_tokenizer.h new file mode 100644 index 00000000000..a73f19205b6 --- /dev/null +++ b/chromium/net/http/http_auth_challenge_tokenizer.h @@ -0,0 +1,61 @@ +// Copyright 2014 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. + +#ifndef NET_HTTP_HTTP_AUTH_CHALLENGE_TOKENIZER_ +#define NET_HTTP_HTTP_AUTH_CHALLENGE_TOKENIZER_ + +#include <string> + +#include "net/base/net_export.h" +#include "net/http/http_util.h" + +namespace net { + +// Breaks up a challenge string into the the auth scheme and parameter list, +// according to RFC 2617 Sec 1.2: +// challenge = auth-scheme 1*SP 1#auth-param +// +// Depending on the challenge scheme, it may be appropriate to interpret the +// parameters as either a base-64 encoded string or a comma-delimited list +// of name-value pairs. param_pairs() and base64_param() methods are provided +// to support either usage. +class NET_EXPORT_PRIVATE HttpAuthChallengeTokenizer { + public: + HttpAuthChallengeTokenizer(std::string::const_iterator begin, + std::string::const_iterator end); + + // Get the original text. + std::string challenge_text() const { + return std::string(begin_, end_); + } + + // Get the auth scheme of the challenge. + std::string::const_iterator scheme_begin() const { return scheme_begin_; } + std::string::const_iterator scheme_end() const { return scheme_end_; } + std::string scheme() const { + return std::string(scheme_begin_, scheme_end_); + } + + std::string::const_iterator params_begin() const { return params_begin_; } + std::string::const_iterator params_end() const { return params_end_; } + HttpUtil::NameValuePairsIterator param_pairs() const; + std::string base64_param() const; + + private: + void Init(std::string::const_iterator begin, + std::string::const_iterator end); + + std::string::const_iterator begin_; + std::string::const_iterator end_; + + std::string::const_iterator scheme_begin_; + std::string::const_iterator scheme_end_; + + std::string::const_iterator params_begin_; + std::string::const_iterator params_end_; +}; + +} // namespace net + +#endif // NET_HTTP_HTTP_AUTH_CHALLENGE_TOKENIZER_ diff --git a/chromium/net/http/http_auth_challenge_tokenizer_unittest.cc b/chromium/net/http/http_auth_challenge_tokenizer_unittest.cc new file mode 100644 index 00000000000..2cf657a17f5 --- /dev/null +++ b/chromium/net/http/http_auth_challenge_tokenizer_unittest.cc @@ -0,0 +1,177 @@ +// Copyright 2014 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/http/http_auth_challenge_tokenizer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +TEST(HttpAuthChallengeTokenizerTest, Basic) { + std::string challenge_str = "Basic realm=\"foobar\""; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Basic"), challenge.scheme()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("realm"), parameters.name()); + EXPECT_EQ(std::string("foobar"), parameters.value()); + EXPECT_FALSE(parameters.GetNext()); +} + +// Use a name=value property with no quote marks. +TEST(HttpAuthChallengeTokenizerTest, NoQuotes) { + std::string challenge_str = "Basic realm=foobar@baz.com"; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Basic"), challenge.scheme()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("realm"), parameters.name()); + EXPECT_EQ(std::string("foobar@baz.com"), parameters.value()); + EXPECT_FALSE(parameters.GetNext()); +} + +// Use a name=value property with mismatching quote marks. +TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotes) { + std::string challenge_str = "Basic realm=\"foobar@baz.com"; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Basic"), challenge.scheme()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("realm"), parameters.name()); + EXPECT_EQ(std::string("foobar@baz.com"), parameters.value()); + EXPECT_FALSE(parameters.GetNext()); +} + +// Use a name= property without a value and with mismatching quote marks. +TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotesNoValue) { + std::string challenge_str = "Basic realm=\""; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Basic"), challenge.scheme()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("realm"), parameters.name()); + EXPECT_EQ(std::string(), parameters.value()); + EXPECT_FALSE(parameters.GetNext()); +} + +// Use a name=value property with mismatching quote marks and spaces in the +// value. +TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotesSpaces) { + std::string challenge_str = "Basic realm=\"foo bar"; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Basic"), challenge.scheme()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("realm"), parameters.name()); + EXPECT_EQ(std::string("foo bar"), parameters.value()); + EXPECT_FALSE(parameters.GetNext()); +} + +// Use multiple name=value properties with mismatching quote marks in the last +// value. +TEST(HttpAuthChallengeTokenizerTest, MismatchedQuotesMultiple) { + std::string challenge_str = "Digest qop=auth-int, algorithm=md5, realm=\"foo"; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Digest"), challenge.scheme()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("qop"), parameters.name()); + EXPECT_EQ(std::string("auth-int"), parameters.value()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("algorithm"), parameters.name()); + EXPECT_EQ(std::string("md5"), parameters.value()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("realm"), parameters.name()); + EXPECT_EQ(std::string("foo"), parameters.value()); + EXPECT_FALSE(parameters.GetNext()); +} + +// Use a name= property which has no value. +TEST(HttpAuthChallengeTokenizerTest, NoValue) { + std::string challenge_str = "Digest qop="; + HttpAuthChallengeTokenizer challenge( + challenge_str.begin(), challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Digest"), challenge.scheme()); + EXPECT_FALSE(parameters.GetNext()); + EXPECT_FALSE(parameters.valid()); +} + +// Specify multiple properties, comma separated. +TEST(HttpAuthChallengeTokenizerTest, Multiple) { + std::string challenge_str = + "Digest algorithm=md5, realm=\"Oblivion\", qop=auth-int"; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("Digest"), challenge.scheme()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("algorithm"), parameters.name()); + EXPECT_EQ(std::string("md5"), parameters.value()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("realm"), parameters.name()); + EXPECT_EQ(std::string("Oblivion"), parameters.value()); + EXPECT_TRUE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("qop"), parameters.name()); + EXPECT_EQ(std::string("auth-int"), parameters.value()); + EXPECT_FALSE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); +} + +// Use a challenge which has no property. +TEST(HttpAuthChallengeTokenizerTest, NoProperty) { + std::string challenge_str = "NTLM"; + HttpAuthChallengeTokenizer challenge( + challenge_str.begin(), challenge_str.end()); + HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); + + EXPECT_TRUE(parameters.valid()); + EXPECT_EQ(std::string("NTLM"), challenge.scheme()); + EXPECT_FALSE(parameters.GetNext()); +} + +// Use a challenge with Base64 encoded token. +TEST(HttpAuthChallengeTokenizerTest, Base64) { + std::string challenge_str = "NTLM SGVsbG8sIFdvcmxkCg==="; + HttpAuthChallengeTokenizer challenge(challenge_str.begin(), + challenge_str.end()); + + EXPECT_EQ(std::string("NTLM"), challenge.scheme()); + // Notice the two equal statements below due to padding removal. + EXPECT_EQ(std::string("SGVsbG8sIFdvcmxkCg=="), challenge.base64_param()); +} + +} // namespace net diff --git a/chromium/net/http/http_auth_controller_unittest.cc b/chromium/net/http/http_auth_controller_unittest.cc index 2a18369c1f7..1a59b8d4f68 100644 --- a/chromium/net/http/http_auth_controller_unittest.cc +++ b/chromium/net/http/http_auth_controller_unittest.cc @@ -9,6 +9,7 @@ #include "net/base/net_log.h" #include "net/base/test_completion_callback.h" #include "net/http/http_auth_cache.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_handler_mock.h" #include "net/http/http_request_info.h" #include "net/http/http_response_headers.h" @@ -127,7 +128,7 @@ TEST(HttpAuthControllerTest, NoExplicitCredentialsAllowed) { } protected: - virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE { + virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE { HttpAuthHandlerMock::Init(challenge); set_allows_default_credentials(true); set_allows_explicit_credentials(false); @@ -224,7 +225,7 @@ TEST(HttpAuthControllerTest, NoExplicitCredentialsAllowed) { ASSERT_EQ(OK, controller->HandleAuthChallenge(headers, false, false, dummy_log)); ASSERT_TRUE(controller->HaveAuthHandler()); - controller->ResetAuth(AuthCredentials(ASCIIToUTF16("Hello"), + controller->ResetAuth(AuthCredentials(base::ASCIIToUTF16("Hello"), base::string16())); EXPECT_TRUE(controller->HaveAuth()); EXPECT_TRUE(controller->IsAuthSchemeDisabled(HttpAuth::AUTH_SCHEME_MOCK)); diff --git a/chromium/net/http/http_auth_filter_win.h b/chromium/net/http/http_auth_filter_win.h index 24305b511ec..d2c0af4a09d 100644 --- a/chromium/net/http/http_auth_filter_win.h +++ b/chromium/net/http/http_auth_filter_win.h @@ -20,13 +20,13 @@ enum RegistryHiveType { namespace http_auth { // The common path to all the registry keys containing domain zone information. -extern const char16 kRegistryInternetSettings[]; -extern const char16 kSettingsMachineOnly[]; -extern const char16* kRegistryEntries[3]; // L"http", L"https", and L"*" +extern const base::char16 kRegistryInternetSettings[]; +extern const base::char16 kSettingsMachineOnly[]; +extern const base::char16* kRegistryEntries[3]; // L"http", L"https", and L"*" -extern const char16* GetRegistryWhitelistKey(); +extern const base::char16* GetRegistryWhitelistKey(); // Override the whitelist key. Passing in NULL restores the default value. -extern void SetRegistryWhitelistKey(const char16* new_whitelist_key); +extern void SetRegistryWhitelistKey(const base::char16* new_whitelist_key); extern bool UseOnlyMachineSettings(); } // namespace http_auth diff --git a/chromium/net/http/http_auth_gssapi_posix.cc b/chromium/net/http/http_auth_gssapi_posix.cc index 41cbcdbcdc0..a4b8c0cd839 100644 --- a/chromium/net/http/http_auth_gssapi_posix.cc +++ b/chromium/net/http/http_auth_gssapi_posix.cc @@ -16,6 +16,7 @@ #include "base/threading/thread_restrictions.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "net/http/http_auth_challenge_tokenizer.h" // These are defined for the GSSAPI library: // Paraphrasing the comments from gssapi.h: @@ -684,7 +685,7 @@ void HttpAuthGSSAPI::Delegate() { } HttpAuth::AuthorizationResult HttpAuthGSSAPI::ParseChallenge( - HttpAuth::ChallengeTokenizer* tok) { + HttpAuthChallengeTokenizer* tok) { // Verify the challenge's auth-scheme. if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str())) return HttpAuth::AUTHORIZATION_RESULT_INVALID; diff --git a/chromium/net/http/http_auth_gssapi_posix.h b/chromium/net/http/http_auth_gssapi_posix.h index db603f65da8..ddaf518736b 100644 --- a/chromium/net/http/http_auth_gssapi_posix.h +++ b/chromium/net/http/http_auth_gssapi_posix.h @@ -22,6 +22,8 @@ namespace net { +class HttpAuthChallengeTokenizer; + // Mechanism OID for GSSAPI. We always use SPNEGO. NET_EXPORT_PRIVATE extern gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC; @@ -240,7 +242,7 @@ class NET_EXPORT_PRIVATE HttpAuthGSSAPI { bool AllowsExplicitCredentials() const; HttpAuth::AuthorizationResult ParseChallenge( - HttpAuth::ChallengeTokenizer* tok); + HttpAuthChallengeTokenizer* tok); // Generates an authentication token. // The return value is an error code. If it's not |OK|, the value of diff --git a/chromium/net/http/http_auth_gssapi_posix_unittest.cc b/chromium/net/http/http_auth_gssapi_posix_unittest.cc index 48d17a3ad64..6f933349d7e 100644 --- a/chromium/net/http/http_auth_gssapi_posix_unittest.cc +++ b/chromium/net/http/http_auth_gssapi_posix_unittest.cc @@ -9,6 +9,7 @@ #include "base/memory/scoped_ptr.h" #include "base/native_library.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/mock_gssapi_library_posix.h" #include "testing/gtest/include/gtest/gtest.h" @@ -181,8 +182,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_FirstRound) { HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate", CHROME_GSS_SPNEGO_MECH_OID_DESC); std::string challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(), - challenge_text.end()); + HttpAuthChallengeTokenizer challenge(challenge_text.begin(), + challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_gssapi.ParseChallenge(&challenge)); } @@ -194,8 +195,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) { HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate", CHROME_GSS_SPNEGO_MECH_OID_DESC); std::string first_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(), - first_challenge_text.end()); + HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(), + first_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_gssapi.ParseChallenge(&first_challenge)); @@ -206,8 +207,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_TwoRounds) { &auth_token)); std::string second_challenge_text = "Negotiate Zm9vYmFy"; - HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(), - second_challenge_text.end()); + HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(), + second_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_gssapi.ParseChallenge(&second_challenge)); } @@ -219,8 +220,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_UnexpectedTokenFirstRound) { HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate", CHROME_GSS_SPNEGO_MECH_OID_DESC); std::string challenge_text = "Negotiate Zm9vYmFy"; - HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(), - challenge_text.end()); + HttpAuthChallengeTokenizer challenge(challenge_text.begin(), + challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID, auth_gssapi.ParseChallenge(&challenge)); } @@ -232,8 +233,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) { HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate", CHROME_GSS_SPNEGO_MECH_OID_DESC); std::string first_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(), - first_challenge_text.end()); + HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(), + first_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_gssapi.ParseChallenge(&first_challenge)); @@ -242,8 +243,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_MissingTokenSecondRound) { EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com", &auth_token)); std::string second_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(), - second_challenge_text.end()); + HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(), + second_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT, auth_gssapi.ParseChallenge(&second_challenge)); } @@ -255,8 +256,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) { HttpAuthGSSAPI auth_gssapi(&mock_library, "Negotiate", CHROME_GSS_SPNEGO_MECH_OID_DESC); std::string first_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(), - first_challenge_text.end()); + HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(), + first_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_gssapi.ParseChallenge(&first_challenge)); @@ -265,8 +266,8 @@ TEST(HttpAuthGSSAPITest, ParseChallenge_NonBase64EncodedToken) { EXPECT_EQ(OK, auth_gssapi.GenerateAuthToken(NULL, "HTTP/intranet.google.com", &auth_token)); std::string second_challenge_text = "Negotiate =happyjoy="; - HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(), - second_challenge_text.end()); + HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(), + second_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID, auth_gssapi.ParseChallenge(&second_challenge)); } diff --git a/chromium/net/http/http_auth_handler.cc b/chromium/net/http/http_auth_handler.cc index ab809faefae..7369ea5425f 100644 --- a/chromium/net/http/http_auth_handler.cc +++ b/chromium/net/http/http_auth_handler.cc @@ -8,6 +8,7 @@ #include "base/bind_helpers.h" #include "base/logging.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_challenge_tokenizer.h" namespace net { @@ -22,7 +23,7 @@ HttpAuthHandler::~HttpAuthHandler() { } bool HttpAuthHandler::InitFromChallenge( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, const BoundNetLog& net_log) { diff --git a/chromium/net/http/http_auth_handler.h b/chromium/net/http/http_auth_handler.h index 638bb44aed3..dfb50d43a60 100644 --- a/chromium/net/http/http_auth_handler.h +++ b/chromium/net/http/http_auth_handler.h @@ -14,6 +14,7 @@ namespace net { +class HttpAuthChallengeTokenizer; struct HttpRequestInfo; // HttpAuthHandler is the interface for the authentication schemes @@ -29,7 +30,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandler { // authentication scheme, but none of the tokens occurring after the // authentication scheme. |target| and |origin| are both stored // for later use, and are not part of the initial challenge. - bool InitFromChallenge(HttpAuth::ChallengeTokenizer* challenge, + bool InitFromChallenge(HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, const BoundNetLog& net_log); @@ -48,7 +49,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandler { // authentication scheme, but none of the tokens occurring after the // authentication scheme. virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) = 0; + HttpAuthChallengeTokenizer* challenge) = 0; // Generates an authentication token, potentially asynchronously. // @@ -151,7 +152,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandler { // authentication scheme. // Implementations are expected to initialize the following members: // scheme_, realm_, score_, properties_ - virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) = 0; + virtual bool Init(HttpAuthChallengeTokenizer* challenge) = 0; // |GenerateAuthTokenImpl()} is the auth-scheme specific implementation // of generating the next auth token. Callers should use |GenerateAuthToken()| diff --git a/chromium/net/http/http_auth_handler_basic.cc b/chromium/net/http/http_auth_handler_basic.cc index e445c93100c..48371389d1a 100644 --- a/chromium/net/http/http_auth_handler_basic.cc +++ b/chromium/net/http/http_auth_handler_basic.cc @@ -7,11 +7,12 @@ #include <string> #include "base/base64.h" -#include "base/i18n/icu_string_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" +#include "net/base/net_string_util.h" #include "net/http/http_auth.h" +#include "net/http/http_auth_challenge_tokenizer.h" namespace net { @@ -33,7 +34,7 @@ namespace { // // TODO(cbentzel): Realm may need to be decoded using RFC 2047 rules as // well, see http://crbug.com/25790. -bool ParseRealm(const HttpAuth::ChallengeTokenizer& tokenizer, +bool ParseRealm(const HttpAuthChallengeTokenizer& tokenizer, std::string* realm) { CHECK(realm); realm->clear(); @@ -42,16 +43,17 @@ bool ParseRealm(const HttpAuth::ChallengeTokenizer& tokenizer, if (!LowerCaseEqualsASCII(parameters.name(), "realm")) continue; - if (!base::ConvertToUtf8AndNormalize( - parameters.value(), base::kCodepageLatin1, realm)) + if (!net::ConvertToUtf8AndNormalize(parameters.value(), kCharsetLatin1, + realm)) { return false; + } } return parameters.valid(); } } // namespace -bool HttpAuthHandlerBasic::Init(HttpAuth::ChallengeTokenizer* challenge) { +bool HttpAuthHandlerBasic::Init(HttpAuthChallengeTokenizer* challenge) { auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC; score_ = 1; properties_ = 0; @@ -59,7 +61,7 @@ bool HttpAuthHandlerBasic::Init(HttpAuth::ChallengeTokenizer* challenge) { } bool HttpAuthHandlerBasic::ParseChallenge( - HttpAuth::ChallengeTokenizer* challenge) { + HttpAuthChallengeTokenizer* challenge) { // Verify the challenge's auth-scheme. if (!LowerCaseEqualsASCII(challenge->scheme(), "basic")) return false; @@ -73,7 +75,7 @@ bool HttpAuthHandlerBasic::ParseChallenge( } HttpAuth::AuthorizationResult HttpAuthHandlerBasic::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) { + HttpAuthChallengeTokenizer* challenge) { // Basic authentication is always a single round, so any responses // should be treated as a rejection. However, if the new challenge // is for a different realm, then indicate the realm change. @@ -91,8 +93,8 @@ int HttpAuthHandlerBasic::GenerateAuthTokenImpl( DCHECK(credentials); // TODO(eroman): is this the right encoding of username/password? std::string base64_username_password; - base::Base64Encode(UTF16ToUTF8(credentials->username()) + ":" + - UTF16ToUTF8(credentials->password()), + base::Base64Encode(base::UTF16ToUTF8(credentials->username()) + ":" + + base::UTF16ToUTF8(credentials->password()), &base64_username_password); *auth_token = "Basic " + base64_username_password; return OK; @@ -105,7 +107,7 @@ HttpAuthHandlerBasic::Factory::~Factory() { } int HttpAuthHandlerBasic::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, diff --git a/chromium/net/http/http_auth_handler_basic.h b/chromium/net/http/http_auth_handler_basic.h index ce56152b09f..5d786f9de86 100644 --- a/chromium/net/http/http_auth_handler_basic.h +++ b/chromium/net/http/http_auth_handler_basic.h @@ -22,7 +22,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerBasic : public HttpAuthHandler { virtual ~Factory(); virtual int CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, @@ -32,10 +32,10 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerBasic : public HttpAuthHandler { }; virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + HttpAuthChallengeTokenizer* challenge) OVERRIDE; protected: - virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE; virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials, const HttpRequestInfo* request, @@ -45,7 +45,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerBasic : public HttpAuthHandler { private: virtual ~HttpAuthHandlerBasic() {} - bool ParseChallenge(HttpAuth::ChallengeTokenizer* challenge); + bool ParseChallenge(HttpAuthChallengeTokenizer* challenge); }; } // namespace net diff --git a/chromium/net/http/http_auth_handler_basic_unittest.cc b/chromium/net/http/http_auth_handler_basic_unittest.cc index 5e05b76d8b5..6f112b1b32f 100644 --- a/chromium/net/http/http_auth_handler_basic_unittest.cc +++ b/chromium/net/http/http_auth_handler_basic_unittest.cc @@ -9,6 +9,7 @@ #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_handler_basic.h" #include "net/http/http_request_info.h" #include "testing/gtest/include/gtest/gtest.h" @@ -36,8 +37,8 @@ TEST(HttpAuthHandlerBasicTest, GenerateAuthToken) { scoped_ptr<HttpAuthHandler> basic; EXPECT_EQ(OK, factory.CreateAuthHandlerFromString( challenge, HttpAuth::AUTH_SERVER, origin, BoundNetLog(), &basic)); - AuthCredentials credentials(ASCIIToUTF16(tests[i].username), - ASCIIToUTF16(tests[i].password)); + AuthCredentials credentials(base::ASCIIToUTF16(tests[i].username), + base::ASCIIToUTF16(tests[i].password)); HttpRequestInfo request_info; std::string auth_token; int rv = basic->GenerateAuthToken(&credentials, &request_info, @@ -91,8 +92,8 @@ TEST(HttpAuthHandlerBasicTest, HandleAnotherChallenge) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { std::string challenge(tests[i].challenge); - HttpAuth::ChallengeTokenizer tok(challenge.begin(), - challenge.end()); + HttpAuthChallengeTokenizer tok(challenge.begin(), + challenge.end()); EXPECT_EQ(tests[i].expected_rv, basic->HandleAnotherChallenge(&tok)); } } diff --git a/chromium/net/http/http_auth_handler_digest.cc b/chromium/net/http/http_auth_handler_digest.cc index a8e301a02e8..7dee081ddbe 100644 --- a/chromium/net/http/http_auth_handler_digest.cc +++ b/chromium/net/http/http_auth_handler_digest.cc @@ -6,7 +6,6 @@ #include <string> -#include "base/i18n/icu_string_conversions.h" #include "base/logging.h" #include "base/md5.h" #include "base/rand_util.h" @@ -14,8 +13,10 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" +#include "net/base/net_string_util.h" #include "net/base/net_util.h" #include "net/http/http_auth.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_request_info.h" #include "net/http/http_util.h" #include "url/gurl.h" @@ -89,7 +90,7 @@ void HttpAuthHandlerDigest::Factory::set_nonce_generator( } int HttpAuthHandlerDigest::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, @@ -107,7 +108,7 @@ int HttpAuthHandlerDigest::Factory::CreateAuthHandler( } HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) { + HttpAuthChallengeTokenizer* challenge) { // Even though Digest is not connection based, a "second round" is parsed // to differentiate between stale and rejected responses. // Note that the state of the current handler is not mutated - this way if @@ -133,7 +134,7 @@ HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge( HttpAuth::AUTHORIZATION_RESULT_REJECT; } -bool HttpAuthHandlerDigest::Init(HttpAuth::ChallengeTokenizer* challenge) { +bool HttpAuthHandlerDigest::Init(HttpAuthChallengeTokenizer* challenge) { return ParseChallenge(challenge); } @@ -186,7 +187,7 @@ HttpAuthHandlerDigest::~HttpAuthHandlerDigest() { // send the realm (See http://crbug.com/20984 for an instance where a // webserver was not sending the realm with a BASIC challenge). bool HttpAuthHandlerDigest::ParseChallenge( - HttpAuth::ChallengeTokenizer* challenge) { + HttpAuthChallengeTokenizer* challenge) { auth_scheme_ = HttpAuth::AUTH_SCHEME_DIGEST; score_ = 2; properties_ = ENCRYPTS_IDENTITY; @@ -226,7 +227,7 @@ bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name, const std::string& value) { if (LowerCaseEqualsASCII(name, "realm")) { std::string realm; - if (!base::ConvertToUtf8AndNormalize(value, base::kCodepageLatin1, &realm)) + if (!net::ConvertToUtf8AndNormalize(value, kCharsetLatin1, &realm)) return false; realm_ = realm; original_realm_ = value; @@ -322,9 +323,9 @@ std::string HttpAuthHandlerDigest::AssembleResponseDigest( const std::string& nc) const { // ha1 = MD5(A1) // TODO(eroman): is this the right encoding? - std::string ha1 = base::MD5String(UTF16ToUTF8(credentials.username()) + ":" + - original_realm_ + ":" + - UTF16ToUTF8(credentials.password())); + std::string ha1 = base::MD5String(base::UTF16ToUTF8(credentials.username()) + + ":" + original_realm_ + ":" + + base::UTF16ToUTF8(credentials.password())); if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS) ha1 = base::MD5String(ha1 + ":" + nonce_ + ":" + cnonce); @@ -352,7 +353,7 @@ std::string HttpAuthHandlerDigest::AssembleCredentials( // TODO(eroman): is this the right encoding? std::string authorization = (std::string("Digest username=") + HttpUtil::Quote( - UTF16ToUTF8(credentials.username()))); + base::UTF16ToUTF8(credentials.username()))); authorization += ", realm=" + HttpUtil::Quote(original_realm_); authorization += ", nonce=" + HttpUtil::Quote(nonce_); authorization += ", uri=" + HttpUtil::Quote(path); diff --git a/chromium/net/http/http_auth_handler_digest.h b/chromium/net/http/http_auth_handler_digest.h index 7edac166c6d..6a960d9cba7 100644 --- a/chromium/net/http/http_auth_handler_digest.h +++ b/chromium/net/http/http_auth_handler_digest.h @@ -65,7 +65,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerDigest : public HttpAuthHandler { void set_nonce_generator(const NonceGenerator* nonce_generator); virtual int CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, @@ -78,10 +78,10 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerDigest : public HttpAuthHandler { }; virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + HttpAuthChallengeTokenizer* challenge) OVERRIDE; protected: - virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE; virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials, const HttpRequestInfo* request, @@ -124,7 +124,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerDigest : public HttpAuthHandler { // Parse the challenge, saving the results into this instance. // Returns true on success. - bool ParseChallenge(HttpAuth::ChallengeTokenizer* challenge); + bool ParseChallenge(HttpAuthChallengeTokenizer* challenge); // Parse an individual property. Returns true on success. bool ParseChallengeProperty(const std::string& name, diff --git a/chromium/net/http/http_auth_handler_digest_unittest.cc b/chromium/net/http/http_auth_handler_digest_unittest.cc index dee44ebd5d0..5c3941c9efe 100644 --- a/chromium/net/http/http_auth_handler_digest_unittest.cc +++ b/chromium/net/http/http_auth_handler_digest_unittest.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_handler_digest.h" #include "net/http/http_request_info.h" #include "testing/gtest/include/gtest/gtest.h" @@ -67,7 +68,8 @@ bool RespondToChallenge(HttpAuth::Target target, TestCompletionCallback callback; scoped_ptr<HttpRequestInfo> request(new HttpRequestInfo()); request->url = GURL(request_url); - AuthCredentials credentials(ASCIIToUTF16("foo"), ASCIIToUTF16("bar")); + AuthCredentials credentials(base::ASCIIToUTF16("foo"), + base::ASCIIToUTF16("bar")); int rv_generate = handler->GenerateAuthToken( &credentials, request.get(), callback.callback(), token); if (rv_generate != OK) { @@ -530,8 +532,8 @@ TEST(HttpAuthHandlerDigestTest, AssembleCredentials) { digest->AssembleCredentials(tests[i].req_method, tests[i].req_path, AuthCredentials( - ASCIIToUTF16(tests[i].username), - ASCIIToUTF16(tests[i].password)), + base::ASCIIToUTF16(tests[i].username), + base::ASCIIToUTF16(tests[i].password)), tests[i].cnonce, tests[i].nonce_count); @@ -551,27 +553,27 @@ TEST(HttpAuthHandlerDigest, HandleAnotherChallenge) { &handler); EXPECT_EQ(OK, rv); ASSERT_TRUE(handler.get() != NULL); - HttpAuth::ChallengeTokenizer tok_default(default_challenge.begin(), - default_challenge.end()); + HttpAuthChallengeTokenizer tok_default(default_challenge.begin(), + default_challenge.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT, handler->HandleAnotherChallenge(&tok_default)); std::string stale_challenge = default_challenge + ", stale=true"; - HttpAuth::ChallengeTokenizer tok_stale(stale_challenge.begin(), - stale_challenge.end()); + HttpAuthChallengeTokenizer tok_stale(stale_challenge.begin(), + stale_challenge.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_STALE, handler->HandleAnotherChallenge(&tok_stale)); std::string stale_false_challenge = default_challenge + ", stale=false"; - HttpAuth::ChallengeTokenizer tok_stale_false(stale_false_challenge.begin(), - stale_false_challenge.end()); + HttpAuthChallengeTokenizer tok_stale_false(stale_false_challenge.begin(), + stale_false_challenge.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT, handler->HandleAnotherChallenge(&tok_stale_false)); std::string realm_change_challenge = "Digest realm=\"SomethingElse\", nonce=\"nonce-value2\""; - HttpAuth::ChallengeTokenizer tok_realm_change(realm_change_challenge.begin(), - realm_change_challenge.end()); + HttpAuthChallengeTokenizer tok_realm_change(realm_change_challenge.begin(), + realm_change_challenge.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM, handler->HandleAnotherChallenge(&tok_realm_change)); } diff --git a/chromium/net/http/http_auth_handler_factory.cc b/chromium/net/http/http_auth_handler_factory.cc index f94bd33d666..9867bb99c1b 100644 --- a/chromium/net/http/http_auth_handler_factory.cc +++ b/chromium/net/http/http_auth_handler_factory.cc @@ -7,6 +7,7 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_filter.h" #include "net/http/http_auth_handler_basic.h" #include "net/http/http_auth_handler_digest.h" @@ -24,7 +25,7 @@ int HttpAuthHandlerFactory::CreateAuthHandlerFromString( const GURL& origin, const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler) { - HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end()); + HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end()); return CreateAuthHandler(&props, target, origin, CREATE_CHALLENGE, 1, net_log, handler); } @@ -36,7 +37,7 @@ int HttpAuthHandlerFactory::CreatePreemptiveAuthHandlerFromString( int digest_nonce_count, const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler) { - HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end()); + HttpAuthChallengeTokenizer props(challenge.begin(), challenge.end()); return CreateAuthHandler(&props, target, origin, CREATE_PREEMPTIVE, digest_nonce_count, net_log, handler); } @@ -172,7 +173,7 @@ HttpAuthHandlerRegistryFactory* HttpAuthHandlerRegistryFactory::Create( } int HttpAuthHandlerRegistryFactory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, diff --git a/chromium/net/http/http_auth_handler_factory.h b/chromium/net/http/http_auth_handler_factory.h index 27627514979..e712aafcb0a 100644 --- a/chromium/net/http/http_auth_handler_factory.h +++ b/chromium/net/http/http_auth_handler_factory.h @@ -20,6 +20,7 @@ namespace net { class BoundNetLog; class HostResolver; +class HttpAuthChallengeTokenizer; class HttpAuthHandler; class HttpAuthHandlerRegistryFactory; @@ -73,7 +74,7 @@ class NET_EXPORT HttpAuthHandlerFactory { // NOTE: This will apply to ALL |origin| values if the filters are empty. // // |*challenge| should not be reused after a call to |CreateAuthHandler()|, - virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, + virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason create_reason, @@ -182,7 +183,7 @@ class NET_EXPORT HttpAuthHandlerRegistryFactory // Creates an auth handler by dispatching out to the registered factories // based on the first token in |challenge|. - virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, + virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, diff --git a/chromium/net/http/http_auth_handler_factory_unittest.cc b/chromium/net/http/http_auth_handler_factory_unittest.cc index 8e69919c0bf..06e8933f25a 100644 --- a/chromium/net/http/http_auth_handler_factory_unittest.cc +++ b/chromium/net/http/http_auth_handler_factory_unittest.cc @@ -21,7 +21,7 @@ class MockHttpAuthHandlerFactory : public HttpAuthHandlerFactory { return_code_(return_code) {} virtual ~MockHttpAuthHandlerFactory() {} - virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, + virtual int CreateAuthHandler(HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, diff --git a/chromium/net/http/http_auth_handler_mock.cc b/chromium/net/http/http_auth_handler_mock.cc index 826c3d6a12b..52990eef923 100644 --- a/chromium/net/http/http_auth_handler_mock.cc +++ b/chromium/net/http/http_auth_handler_mock.cc @@ -8,6 +8,7 @@ #include "base/message_loop/message_loop.h" #include "base/strings/string_util.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_request_info.h" #include "testing/gtest/include/gtest/gtest.h" @@ -77,7 +78,7 @@ void HttpAuthHandlerMock::SetGenerateExpectation(bool async, int rv) { } HttpAuth::AuthorizationResult HttpAuthHandlerMock::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) { + HttpAuthChallengeTokenizer* challenge) { // If we receive an empty challenge for a connection based scheme, or a second // challenge for a non connection based scheme, assume it's a rejection. if (!is_connection_based() || challenge->base64_param().empty()) @@ -99,7 +100,7 @@ bool HttpAuthHandlerMock::AllowsExplicitCredentials() { return allows_explicit_credentials_; } -bool HttpAuthHandlerMock::Init(HttpAuth::ChallengeTokenizer* challenge) { +bool HttpAuthHandlerMock::Init(HttpAuthChallengeTokenizer* challenge) { auth_scheme_ = HttpAuth::AUTH_SCHEME_MOCK; score_ = 1; properties_ = connection_based_ ? IS_CONNECTION_BASED : 0; @@ -164,7 +165,7 @@ void HttpAuthHandlerMock::Factory::AddMockHandler( } int HttpAuthHandlerMock::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, diff --git a/chromium/net/http/http_auth_handler_mock.h b/chromium/net/http/http_auth_handler_mock.h index 7cc441a70cc..a4642dfe0f0 100644 --- a/chromium/net/http/http_auth_handler_mock.h +++ b/chromium/net/http/http_auth_handler_mock.h @@ -43,7 +43,7 @@ class HttpAuthHandlerMock : public HttpAuthHandler { // HttpAuthHandlerFactory: virtual int CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, @@ -88,13 +88,13 @@ class HttpAuthHandlerMock : public HttpAuthHandler { // HttpAuthHandler: virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + HttpAuthChallengeTokenizer* challenge) OVERRIDE; virtual bool NeedsIdentity() OVERRIDE; virtual bool AllowsDefaultCredentials() OVERRIDE; virtual bool AllowsExplicitCredentials() OVERRIDE; protected: - virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE; virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials, const HttpRequestInfo* request, diff --git a/chromium/net/http/http_auth_handler_negotiate.cc b/chromium/net/http/http_auth_handler_negotiate.cc index 788b06750b6..422ddd729a2 100644 --- a/chromium/net/http/http_auth_handler_negotiate.cc +++ b/chromium/net/http/http_auth_handler_negotiate.cc @@ -37,7 +37,7 @@ void HttpAuthHandlerNegotiate::Factory::set_host_resolver( } int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, @@ -161,7 +161,7 @@ std::string HttpAuthHandlerNegotiate::CreateSPN( } HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) { + HttpAuthChallengeTokenizer* challenge) { return auth_system_.ParseChallenge(challenge); } @@ -184,7 +184,7 @@ bool HttpAuthHandlerNegotiate::AllowsExplicitCredentials() { // The Negotiate challenge header looks like: // WWW-Authenticate: NEGOTIATE auth-data -bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { +bool HttpAuthHandlerNegotiate::Init(HttpAuthChallengeTokenizer* challenge) { #if defined(OS_POSIX) if (!auth_system_.Init()) { VLOG(1) << "can't initialize GSSAPI library"; diff --git a/chromium/net/http/http_auth_handler_negotiate.h b/chromium/net/http/http_auth_handler_negotiate.h index d028178ed51..90bd16ce958 100644 --- a/chromium/net/http/http_auth_handler_negotiate.h +++ b/chromium/net/http/http_auth_handler_negotiate.h @@ -70,7 +70,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNegotiate : public HttpAuthHandler { } virtual int CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, @@ -107,13 +107,13 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNegotiate : public HttpAuthHandler { // HttpAuthHandler: virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + HttpAuthChallengeTokenizer* challenge) OVERRIDE; virtual bool NeedsIdentity() OVERRIDE; virtual bool AllowsDefaultCredentials() OVERRIDE; virtual bool AllowsExplicitCredentials() OVERRIDE; protected: - virtual bool Init(HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + virtual bool Init(HttpAuthChallengeTokenizer* challenge) OVERRIDE; virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials, const HttpRequestInfo* request, diff --git a/chromium/net/http/http_auth_handler_ntlm.cc b/chromium/net/http/http_auth_handler_ntlm.cc index 922800c71e1..de0fe290a3c 100644 --- a/chromium/net/http/http_auth_handler_ntlm.cc +++ b/chromium/net/http/http_auth_handler_ntlm.cc @@ -12,15 +12,16 @@ #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" +#include "net/http/http_auth_challenge_tokenizer.h" namespace net { HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) { + HttpAuthChallengeTokenizer* challenge) { return ParseChallenge(challenge, false); } -bool HttpAuthHandlerNTLM::Init(HttpAuth::ChallengeTokenizer* tok) { +bool HttpAuthHandlerNTLM::Init(HttpAuthChallengeTokenizer* tok) { auth_scheme_ = HttpAuth::AUTH_SCHEME_NTLM; score_ = 3; properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; @@ -100,7 +101,7 @@ int HttpAuthHandlerNTLM::GenerateAuthTokenImpl( // The NTLM challenge header looks like: // WWW-Authenticate: NTLM auth-data HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::ParseChallenge( - HttpAuth::ChallengeTokenizer* tok, bool initial_challenge) { + HttpAuthChallengeTokenizer* tok, bool initial_challenge) { #if defined(NTLM_SSPI) // auth_sspi_ contains state for whether or not this is the initial challenge. return auth_sspi_.ParseChallenge(tok); diff --git a/chromium/net/http/http_auth_handler_ntlm.h b/chromium/net/http/http_auth_handler_ntlm.h index 98bd362d543..9e2abc6254a 100644 --- a/chromium/net/http/http_auth_handler_ntlm.h +++ b/chromium/net/http/http_auth_handler_ntlm.h @@ -42,7 +42,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNTLM : public HttpAuthHandler { virtual ~Factory(); virtual int CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, @@ -109,14 +109,14 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNTLM : public HttpAuthHandler { virtual bool AllowsDefaultCredentials() OVERRIDE; virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) OVERRIDE; + HttpAuthChallengeTokenizer* challenge) OVERRIDE; protected: // This function acquires a credentials handle in the SSPI implementation. // It does nothing in the portable implementation. int InitializeBeforeFirstChallenge(); - virtual bool Init(HttpAuth::ChallengeTokenizer* tok) OVERRIDE; + virtual bool Init(HttpAuthChallengeTokenizer* tok) OVERRIDE; virtual int GenerateAuthTokenImpl(const AuthCredentials* credentials, const HttpRequestInfo* request, @@ -135,7 +135,7 @@ class NET_EXPORT_PRIVATE HttpAuthHandlerNTLM : public HttpAuthHandler { // Parse the challenge, saving the results into this instance. HttpAuth::AuthorizationResult ParseChallenge( - HttpAuth::ChallengeTokenizer* tok, bool initial_challenge); + HttpAuthChallengeTokenizer* tok, bool initial_challenge); // Given an input token received from the server, generate the next output // token to be sent to the server. diff --git a/chromium/net/http/http_auth_handler_ntlm_portable.cc b/chromium/net/http/http_auth_handler_ntlm_portable.cc index d1fbf23c59f..035a6dc8017 100644 --- a/chromium/net/http/http_auth_handler_ntlm_portable.cc +++ b/chromium/net/http/http_auth_handler_ntlm_portable.cc @@ -70,12 +70,10 @@ namespace net { * * ***** END LICENSE BLOCK ***** */ -// Discover the endianness by testing processor architecture. -#if defined(ARCH_CPU_X86) || defined(ARCH_CPU_X86_64)\ - || defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_MIPSEL) +#if defined(ARCH_CPU_LITTLE_ENDIAN) #define IS_LITTLE_ENDIAN 1 #undef IS_BIG_ENDIAN -#elif defined(ARCH_CPU_MIPSEB) +#elif defined(ARCH_CPU_BIG_ENDIAN) #define IS_BIG_ENDIAN 1 #undef IS_LITTLE_ENDIAN #else @@ -211,7 +209,8 @@ static void* WriteSecBuf(void* buf, uint16 length, uint32 offset) { * to pass the same buffer as both input and output, which is a handy way to * convert the unicode buffer to little-endian on big-endian platforms. */ -static void* WriteUnicodeLE(void* buf, const char16* str, uint32 str_len) { +static void* WriteUnicodeLE( + void* buf, const base::char16* str, uint32 str_len) { // Convert input string from BE to LE. uint8* cursor = static_cast<uint8*>(buf); const uint8* input = reinterpret_cast<const uint8*>(str); @@ -257,7 +256,7 @@ static void LM_Hash(const base::string16& password, uint8* hash) { // Convert password to OEM character set. We'll just use the native // filesystem charset. - std::string passbuf = base::SysWideToNativeMB(UTF16ToWide(password)); + std::string passbuf = base::SysWideToNativeMB(base::UTF16ToWide(password)); StringToUpperASCII(&passbuf); passbuf.resize(14, '\0'); @@ -480,14 +479,15 @@ static int GenerateType3Msg(const base::string16& domain, ucs_domain_buf = domain; domain_ptr = ucs_domain_buf.data(); domain_len = ucs_domain_buf.length() * 2; - WriteUnicodeLE(const_cast<void*>(domain_ptr), (const char16*) domain_ptr, + WriteUnicodeLE(const_cast<void*>(domain_ptr), + (const base::char16*) domain_ptr, ucs_domain_buf.length()); #else domain_ptr = domain.data(); domain_len = domain.length() * 2; #endif } else { - oem_domain_buf = base::SysWideToNativeMB(UTF16ToWide(domain)); + oem_domain_buf = base::SysWideToNativeMB(base::UTF16ToWide(domain)); domain_ptr = oem_domain_buf.data(); domain_len = oem_domain_buf.length(); } @@ -500,14 +500,14 @@ static int GenerateType3Msg(const base::string16& domain, ucs_user_buf = username; user_ptr = ucs_user_buf.data(); user_len = ucs_user_buf.length() * 2; - WriteUnicodeLE(const_cast<void*>(user_ptr), (const char16*) user_ptr, + WriteUnicodeLE(const_cast<void*>(user_ptr), (const base::char16*) user_ptr, ucs_user_buf.length()); #else user_ptr = username.data(); user_len = username.length() * 2; #endif } else { - oem_user_buf = base::SysWideToNativeMB(UTF16ToWide(username)); + oem_user_buf = base::SysWideToNativeMB(base::UTF16ToWide(username)); user_ptr = oem_user_buf.data(); user_len = oem_user_buf.length(); } @@ -521,7 +521,7 @@ static int GenerateType3Msg(const base::string16& domain, host_ptr = ucs_host_buf.data(); host_len = ucs_host_buf.length() * 2; #ifdef IS_BIG_ENDIAN - WriteUnicodeLE(const_cast<void*>(host_ptr), (const char16*) host_ptr, + WriteUnicodeLE(const_cast<void*>(host_ptr), (const base::char16*) host_ptr, ucs_host_buf.length()); #endif } else { @@ -707,7 +707,7 @@ int HttpAuthHandlerNTLM::GetNextToken(const void* in_token, } int HttpAuthHandlerNTLM::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, diff --git a/chromium/net/http/http_auth_handler_ntlm_win.cc b/chromium/net/http/http_auth_handler_ntlm_win.cc index 041522f79e6..acc6fc15e2e 100644 --- a/chromium/net/http/http_auth_handler_ntlm_win.cc +++ b/chromium/net/http/http_auth_handler_ntlm_win.cc @@ -52,7 +52,7 @@ HttpAuthHandlerNTLM::Factory::~Factory() { } int HttpAuthHandlerNTLM::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, + HttpAuthChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, CreateReason reason, diff --git a/chromium/net/http/http_auth_handler_unittest.cc b/chromium/net/http/http_auth_handler_unittest.cc index f8928fcc428..a53573d6b47 100644 --- a/chromium/net/http/http_auth_handler_unittest.cc +++ b/chromium/net/http/http_auth_handler_unittest.cc @@ -10,6 +10,7 @@ #include "net/base/net_errors.h" #include "net/base/net_log_unittest.h" #include "net/base/test_completion_callback.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_handler_mock.h" #include "net/http/http_request_info.h" #include "testing/gtest/include/gtest/gtest.h" @@ -19,7 +20,8 @@ namespace net { TEST(HttpAuthHandlerTest, NetLog) { GURL origin("http://www.example.com"); std::string challenge = "Mock asdf"; - AuthCredentials credentials(ASCIIToUTF16("user"), ASCIIToUTF16("pass")); + AuthCredentials credentials(base::ASCIIToUTF16("user"), + base::ASCIIToUTF16("pass")); std::string auth_token; HttpRequestInfo request; @@ -33,7 +35,7 @@ TEST(HttpAuthHandlerTest, NetLog) { (k == 0) ? HttpAuth::AUTH_PROXY : HttpAuth::AUTH_SERVER; NetLog::EventType event_type = (k == 0) ? NetLog::TYPE_AUTH_PROXY : NetLog::TYPE_AUTH_SERVER; - HttpAuth::ChallengeTokenizer tokenizer( + HttpAuthChallengeTokenizer tokenizer( challenge.begin(), challenge.end()); HttpAuthHandlerMock mock_handler; CapturingNetLog capturing_net_log; diff --git a/chromium/net/http/http_auth_sspi_win.cc b/chromium/net/http/http_auth_sspi_win.cc index 5718f9ef753..8a0e49fff11 100644 --- a/chromium/net/http/http_auth_sspi_win.cc +++ b/chromium/net/http/http_auth_sspi_win.cc @@ -13,6 +13,7 @@ #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" #include "net/http/http_auth.h" +#include "net/http/http_auth_challenge_tokenizer.h" namespace net { @@ -226,7 +227,7 @@ void HttpAuthSSPI::ResetSecurityContext() { } HttpAuth::AuthorizationResult HttpAuthSSPI::ParseChallenge( - HttpAuth::ChallengeTokenizer* tok) { + HttpAuthChallengeTokenizer* tok) { // Verify the challenge's auth-scheme. if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str())) return HttpAuth::AUTHORIZATION_RESULT_INVALID; diff --git a/chromium/net/http/http_auth_sspi_win.h b/chromium/net/http/http_auth_sspi_win.h index bc7da46c312..dd0b1772b16 100644 --- a/chromium/net/http/http_auth_sspi_win.h +++ b/chromium/net/http/http_auth_sspi_win.h @@ -22,6 +22,8 @@ namespace net { +class HttpAuthChallengeTokenizer; + // SSPILibrary is introduced so unit tests can mock the calls to Windows' SSPI // implementation. The default implementation simply passes the arguments on to // the SSPI implementation provided by Secur32.dll. @@ -134,7 +136,7 @@ class NET_EXPORT_PRIVATE HttpAuthSSPI { bool AllowsExplicitCredentials() const; HttpAuth::AuthorizationResult ParseChallenge( - HttpAuth::ChallengeTokenizer* tok); + HttpAuthChallengeTokenizer* tok); // Generates an authentication token for the service specified by the // Service Principal Name |spn| and stores the value in |*auth_token|. diff --git a/chromium/net/http/http_auth_sspi_win_unittest.cc b/chromium/net/http/http_auth_sspi_win_unittest.cc index 09fe6fa25ef..586822d2ef7 100644 --- a/chromium/net/http/http_auth_sspi_win_unittest.cc +++ b/chromium/net/http/http_auth_sspi_win_unittest.cc @@ -4,6 +4,7 @@ #include "base/basictypes.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_sspi_win.h" #include "net/http/mock_sspi_library_win.h" #include "testing/gtest/include/gtest/gtest.h" @@ -62,8 +63,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_FirstRound) { HttpAuthSSPI auth_sspi(&mock_library, "Negotiate", NEGOSSP_NAME, kMaxTokenLength); std::string challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(), - challenge_text.end()); + HttpAuthChallengeTokenizer challenge(challenge_text.begin(), + challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_sspi.ParseChallenge(&challenge)); } @@ -75,8 +76,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_TwoRounds) { HttpAuthSSPI auth_sspi(&mock_library, "Negotiate", NEGOSSP_NAME, kMaxTokenLength); std::string first_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(), - first_challenge_text.end()); + HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(), + first_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_sspi.ParseChallenge(&first_challenge)); @@ -86,8 +87,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_TwoRounds) { &auth_token)); std::string second_challenge_text = "Negotiate Zm9vYmFy"; - HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(), - second_challenge_text.end()); + HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(), + second_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_sspi.ParseChallenge(&second_challenge)); } @@ -99,8 +100,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_UnexpectedTokenFirstRound) { HttpAuthSSPI auth_sspi(&mock_library, "Negotiate", NEGOSSP_NAME, kMaxTokenLength); std::string challenge_text = "Negotiate Zm9vYmFy"; - HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(), - challenge_text.end()); + HttpAuthChallengeTokenizer challenge(challenge_text.begin(), + challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID, auth_sspi.ParseChallenge(&challenge)); } @@ -112,8 +113,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_MissingTokenSecondRound) { HttpAuthSSPI auth_sspi(&mock_library, "Negotiate", NEGOSSP_NAME, kMaxTokenLength); std::string first_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(), - first_challenge_text.end()); + HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(), + first_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_sspi.ParseChallenge(&first_challenge)); @@ -121,8 +122,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_MissingTokenSecondRound) { EXPECT_EQ(OK, auth_sspi.GenerateAuthToken(NULL, "HTTP/intranet.google.com", &auth_token)); std::string second_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(), - second_challenge_text.end()); + HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(), + second_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_REJECT, auth_sspi.ParseChallenge(&second_challenge)); } @@ -134,8 +135,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_NonBase64EncodedToken) { HttpAuthSSPI auth_sspi(&mock_library, "Negotiate", NEGOSSP_NAME, kMaxTokenLength); std::string first_challenge_text = "Negotiate"; - HttpAuth::ChallengeTokenizer first_challenge(first_challenge_text.begin(), - first_challenge_text.end()); + HttpAuthChallengeTokenizer first_challenge(first_challenge_text.begin(), + first_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_ACCEPT, auth_sspi.ParseChallenge(&first_challenge)); @@ -143,8 +144,8 @@ TEST(HttpAuthSSPITest, ParseChallenge_NonBase64EncodedToken) { EXPECT_EQ(OK, auth_sspi.GenerateAuthToken(NULL, "HTTP/intranet.google.com", &auth_token)); std::string second_challenge_text = "Negotiate =happyjoy="; - HttpAuth::ChallengeTokenizer second_challenge(second_challenge_text.begin(), - second_challenge_text.end()); + HttpAuthChallengeTokenizer second_challenge(second_challenge_text.begin(), + second_challenge_text.end()); EXPECT_EQ(HttpAuth::AUTHORIZATION_RESULT_INVALID, auth_sspi.ParseChallenge(&second_challenge)); } diff --git a/chromium/net/http/http_auth_unittest.cc b/chromium/net/http/http_auth_unittest.cc index 6f1471d472a..cf68fa8039d 100644 --- a/chromium/net/http/http_auth_unittest.cc +++ b/chromium/net/http/http_auth_unittest.cc @@ -11,6 +11,7 @@ #include "net/base/net_errors.h" #include "net/dns/mock_host_resolver.h" #include "net/http/http_auth.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_filter.h" #include "net/http/http_auth_handler.h" #include "net/http/http_auth_handler_factory.h" @@ -28,7 +29,7 @@ HttpAuthHandlerMock* CreateMockHandler(bool connection_based) { HttpAuthHandlerMock* auth_handler = new HttpAuthHandlerMock(); auth_handler->set_connection_based(connection_based); std::string challenge_text = "Basic"; - HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(), + HttpAuthChallengeTokenizer challenge(challenge_text.begin(), challenge_text.end()); GURL origin("www.example.com"); EXPECT_TRUE(auth_handler->InitFromChallenge(&challenge, @@ -245,173 +246,6 @@ TEST(HttpAuthTest, HandleChallengeResponse) { EXPECT_EQ("Mock token_a", challenge_used); } -TEST(HttpAuthTest, ChallengeTokenizer) { - std::string challenge_str = "Basic realm=\"foobar\""; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Basic"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("foobar"), parameters.value()); - EXPECT_FALSE(parameters.GetNext()); -} - -// Use a name=value property with no quote marks. -TEST(HttpAuthTest, ChallengeTokenizerNoQuotes) { - std::string challenge_str = "Basic realm=foobar@baz.com"; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Basic"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("foobar@baz.com"), parameters.value()); - EXPECT_FALSE(parameters.GetNext()); -} - -// Use a name=value property with mismatching quote marks. -TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotes) { - std::string challenge_str = "Basic realm=\"foobar@baz.com"; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Basic"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("foobar@baz.com"), parameters.value()); - EXPECT_FALSE(parameters.GetNext()); -} - -// Use a name= property without a value and with mismatching quote marks. -TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesNoValue) { - std::string challenge_str = "Basic realm=\""; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Basic"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string(), parameters.value()); - EXPECT_FALSE(parameters.GetNext()); -} - -// Use a name=value property with mismatching quote marks and spaces in the -// value. -TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesSpaces) { - std::string challenge_str = "Basic realm=\"foo bar"; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Basic"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("foo bar"), parameters.value()); - EXPECT_FALSE(parameters.GetNext()); -} - -// Use multiple name=value properties with mismatching quote marks in the last -// value. -TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesMultiple) { - std::string challenge_str = "Digest qop=auth-int, algorithm=md5, realm=\"foo"; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Digest"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("qop"), parameters.name()); - EXPECT_EQ(std::string("auth-int"), parameters.value()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("algorithm"), parameters.name()); - EXPECT_EQ(std::string("md5"), parameters.value()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("foo"), parameters.value()); - EXPECT_FALSE(parameters.GetNext()); -} - -// Use a name= property which has no value. -TEST(HttpAuthTest, ChallengeTokenizerNoValue) { - std::string challenge_str = "Digest qop="; - HttpAuth::ChallengeTokenizer challenge( - challenge_str.begin(), challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Digest"), challenge.scheme()); - EXPECT_FALSE(parameters.GetNext()); - EXPECT_FALSE(parameters.valid()); -} - -// Specify multiple properties, comma separated. -TEST(HttpAuthTest, ChallengeTokenizerMultiple) { - std::string challenge_str = - "Digest algorithm=md5, realm=\"Oblivion\", qop=auth-int"; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("Digest"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("algorithm"), parameters.name()); - EXPECT_EQ(std::string("md5"), parameters.value()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("Oblivion"), parameters.value()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("qop"), parameters.name()); - EXPECT_EQ(std::string("auth-int"), parameters.value()); - EXPECT_FALSE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); -} - -// Use a challenge which has no property. -TEST(HttpAuthTest, ChallengeTokenizerNoProperty) { - std::string challenge_str = "NTLM"; - HttpAuth::ChallengeTokenizer challenge( - challenge_str.begin(), challenge_str.end()); - HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); - - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("NTLM"), challenge.scheme()); - EXPECT_FALSE(parameters.GetNext()); -} - -// Use a challenge with Base64 encoded token. -TEST(HttpAuthTest, ChallengeTokenizerBase64) { - std::string challenge_str = "NTLM SGVsbG8sIFdvcmxkCg==="; - HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), - challenge_str.end()); - - EXPECT_EQ(std::string("NTLM"), challenge.scheme()); - // Notice the two equal statements below due to padding removal. - EXPECT_EQ(std::string("SGVsbG8sIFdvcmxkCg=="), challenge.base64_param()); -} - TEST(HttpAuthTest, GetChallengeHeaderName) { std::string name; diff --git a/chromium/net/http/http_basic_stream.cc b/chromium/net/http/http_basic_stream.cc index 87f6469ad61..0d14e0f9a0e 100644 --- a/chromium/net/http/http_basic_stream.cc +++ b/chromium/net/http/http_basic_stream.cc @@ -42,10 +42,6 @@ int HttpBasicStream::ReadResponseHeaders(const CompletionCallback& callback) { return parser()->ReadResponseHeaders(callback); } -const HttpResponseInfo* HttpBasicStream::GetResponseInfo() const { - return parser()->GetResponseInfo(); -} - int HttpBasicStream::ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) { @@ -86,7 +82,9 @@ bool HttpBasicStream::IsConnectionReusable() const { } int64 HttpBasicStream::GetTotalReceivedBytes() const { - return parser()->received_bytes(); + if (parser()) + return parser()->received_bytes(); + return 0; } bool HttpBasicStream::GetLoadTimingInfo( diff --git a/chromium/net/http/http_basic_stream.h b/chromium/net/http/http_basic_stream.h index 4a67ccaac00..00253c37db4 100644 --- a/chromium/net/http/http_basic_stream.h +++ b/chromium/net/http/http_basic_stream.h @@ -46,8 +46,6 @@ class HttpBasicStream : public HttpStream { virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE; - virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; - virtual int ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) OVERRIDE; diff --git a/chromium/net/http/http_byte_range.h b/chromium/net/http/http_byte_range.h index 98708721a4f..63c02f1ba5c 100644 --- a/chromium/net/http/http_byte_range.h +++ b/chromium/net/http/http_byte_range.h @@ -13,8 +13,8 @@ namespace net { // A container class that represents a "range" specified for range request -// specified by RFC 2616 Section 14.35.1. -// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 +// specified by RFC 7233 Section 2.1. +// https://tools.ietf.org/html/rfc7233#section-2.1 class NET_EXPORT HttpByteRange { public: HttpByteRange(); diff --git a/chromium/net/http/http_cache.cc b/chromium/net/http/http_cache.cc index 49591da8ff4..3ae69c80a6f 100644 --- a/chromium/net/http/http_cache.cc +++ b/chromium/net/http/http_cache.cc @@ -33,6 +33,7 @@ #include "net/base/net_errors.h" #include "net/base/upload_data_stream.h" #include "net/disk_cache/disk_cache.h" +#include "net/http/disk_cache_based_quic_server_info.h" #include "net/http/http_cache_transaction.h" #include "net/http/http_network_layer.h" #include "net/http/http_network_session.h" @@ -40,6 +41,7 @@ #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/http/http_util.h" +#include "net/quic/crypto/quic_server_info.h" namespace { @@ -267,23 +269,44 @@ void HttpCache::MetadataWriter::OnIOComplete(int result) { //----------------------------------------------------------------------------- +class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory { + public: + QuicServerInfoFactoryAdaptor(HttpCache* http_cache) + : http_cache_(http_cache) { + } + + virtual QuicServerInfo* GetForServer( + const QuicServerId& server_id) OVERRIDE { + return new DiskCacheBasedQuicServerInfo(server_id, http_cache_); + } + + private: + HttpCache* const http_cache_; +}; + +//----------------------------------------------------------------------------- HttpCache::HttpCache(const net::HttpNetworkSession::Params& params, BackendFactory* backend_factory) : net_log_(params.net_log), backend_factory_(backend_factory), building_backend_(false), mode_(NORMAL), - network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))) { + network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))), + weak_factory_(this) { + SetupQuicServerInfoFactory(network_layer_->GetSession()); } +// This call doesn't change the shared |session|'s QuicServerInfoFactory because +// |session| is shared. HttpCache::HttpCache(HttpNetworkSession* session, BackendFactory* backend_factory) : net_log_(session->net_log()), backend_factory_(backend_factory), building_backend_(false), mode_(NORMAL), - network_layer_(new HttpNetworkLayer(session)) { + network_layer_(new HttpNetworkLayer(session)), + weak_factory_(this) { } HttpCache::HttpCache(HttpTransactionFactory* network_layer, @@ -293,10 +316,16 @@ HttpCache::HttpCache(HttpTransactionFactory* network_layer, backend_factory_(backend_factory), building_backend_(false), mode_(NORMAL), - network_layer_(network_layer) { + network_layer_(network_layer), + weak_factory_(this) { + SetupQuicServerInfoFactory(network_layer_->GetSession()); } HttpCache::~HttpCache() { + // Transactions should see an invalid cache after this point; otherwise they + // could see an inconsistent object (half destroyed). + weak_factory_.InvalidateWeakPtrs(); + // If we have any active entries remaining, then we need to deactivate them. // We may have some pending calls to OnProcessPendingQueue, but since those // won't run (due to our destruction), we can simply ignore the corresponding @@ -379,7 +408,7 @@ void HttpCache::WriteMetadata(const GURL& url, } HttpCache::Transaction* trans = - new HttpCache::Transaction(priority, this, NULL); + new HttpCache::Transaction(priority, this); MetadataWriter* writer = new MetadataWriter(trans); // The writer will self destruct when done. @@ -387,17 +416,13 @@ void HttpCache::WriteMetadata(const GURL& url, } void HttpCache::CloseAllConnections() { - net::HttpNetworkLayer* network = - static_cast<net::HttpNetworkLayer*>(network_layer_.get()); - HttpNetworkSession* session = network->GetSession(); + HttpNetworkSession* session = GetSession(); if (session) session->CloseAllConnections(); } void HttpCache::CloseIdleConnections() { - net::HttpNetworkLayer* network = - static_cast<net::HttpNetworkLayer*>(network_layer_.get()); - HttpNetworkSession* session = network->GetSession(); + HttpNetworkSession* session = GetSession(); if (session) session->CloseIdleConnections(); } @@ -421,15 +446,14 @@ void HttpCache::InitializeInfiniteCache(const base::FilePath& path) { } int HttpCache::CreateTransaction(RequestPriority priority, - scoped_ptr<HttpTransaction>* trans, - HttpTransactionDelegate* delegate) { + scoped_ptr<HttpTransaction>* trans) { // Do lazy initialization of disk cache if needed. if (!disk_cache_.get()) { // We don't care about the result. CreateBackend(NULL, net::CompletionCallback()); } - trans->reset(new HttpCache::Transaction(priority, this, delegate)); + trans->reset(new HttpCache::Transaction(priority, this)); return OK; } @@ -438,9 +462,15 @@ HttpCache* HttpCache::GetCache() { } HttpNetworkSession* HttpCache::GetSession() { - net::HttpNetworkLayer* network = - static_cast<net::HttpNetworkLayer*>(network_layer_.get()); - return network->GetSession(); + return network_layer_->GetSession(); +} + +scoped_ptr<HttpTransactionFactory> +HttpCache::SetHttpNetworkTransactionFactoryForTesting( + scoped_ptr<HttpTransactionFactory> new_network_layer) { + scoped_ptr<HttpTransactionFactory> old_network_layer(network_layer_.Pass()); + network_layer_ = new_network_layer.Pass(); + return old_network_layer.Pass(); } //----------------------------------------------------------------------------- @@ -468,7 +498,7 @@ int HttpCache::CreateBackend(disk_cache::Backend** backend, pending_op->writer = item.release(); pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, - AsWeakPtr(), pending_op); + GetWeakPtr(), pending_op); int rv = backend_factory_->CreateBackend(net_log_, &pending_op->backend, pending_op->callback); @@ -583,7 +613,7 @@ int HttpCache::AsyncDoomEntry(const std::string& key, Transaction* trans) { pending_op->writer = item; pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, - AsWeakPtr(), pending_op); + GetWeakPtr(), pending_op); int rv = disk_cache_->DoomEntry(key, pending_op->callback); if (rv != ERR_IO_PENDING) { @@ -723,7 +753,7 @@ int HttpCache::OpenEntry(const std::string& key, ActiveEntry** entry, pending_op->writer = item; pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, - AsWeakPtr(), pending_op); + GetWeakPtr(), pending_op); int rv = disk_cache_->OpenEntry(key, &(pending_op->disk_entry), pending_op->callback); @@ -752,7 +782,7 @@ int HttpCache::CreateEntry(const std::string& key, ActiveEntry** entry, pending_op->writer = item; pending_op->callback = base::Bind(&HttpCache::OnPendingOpComplete, - AsWeakPtr(), pending_op); + GetWeakPtr(), pending_op); int rv = disk_cache_->CreateEntry(key, &(pending_op->disk_entry), pending_op->callback); @@ -968,6 +998,16 @@ bool HttpCache::RemovePendingTransactionFromPendingOp(PendingOp* pending_op, return false; } +void HttpCache::SetupQuicServerInfoFactory(HttpNetworkSession* session) { + if (session && session->params().enable_quic_persist_server_info && + !session->quic_stream_factory()->has_quic_server_info_factory()) { + DCHECK(!quic_server_info_factory_); + quic_server_info_factory_.reset(new QuicServerInfoFactoryAdaptor(this)); + session->quic_stream_factory()->set_quic_server_info_factory( + quic_server_info_factory_.get()); + } +} + void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { // Multiple readers may finish with an entry at once, so we want to batch up // calls to OnProcessPendingQueue. This flag also tells us that we should @@ -978,7 +1018,7 @@ void HttpCache::ProcessPendingQueue(ActiveEntry* entry) { base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&HttpCache::OnProcessPendingQueue, AsWeakPtr(), entry)); + base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry)); } void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) { @@ -1135,8 +1175,8 @@ void HttpCache::OnBackendCreated(int result, PendingOp* pending_op) { base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind( - &HttpCache::OnBackendCreated, AsWeakPtr(), result, pending_op)); + base::Bind(&HttpCache::OnBackendCreated, GetWeakPtr(), + result, pending_op)); } else { building_backend_ = false; DeletePendingOp(pending_op); diff --git a/chromium/net/http/http_cache.h b/chromium/net/http/http_cache.h index db2db6906d5..732a9c09c75 100644 --- a/chromium/net/http/http_cache.h +++ b/chromium/net/http/http_cache.h @@ -60,7 +60,6 @@ class ViewCacheHelper; struct HttpRequestInfo; class NET_EXPORT HttpCache : public HttpTransactionFactory, - public base::SupportsWeakPtr<HttpCache>, NON_EXPORTED_BASE(public base::NonThreadSafe) { public: // The cache mode of operation. @@ -125,17 +124,16 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, HttpCache(const net::HttpNetworkSession::Params& params, BackendFactory* backend_factory); - // The disk cache is initialized lazily (by CreateTransaction) in this case. + // The disk cache is initialized lazily (by CreateTransaction) in this case. // 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. authentication data). The // HttpCache takes ownership of the |backend_factory|. HttpCache(HttpNetworkSession* session, BackendFactory* backend_factory); - // Initialize the cache from its component parts, which is useful for - // testing. The lifetime of the network_layer and backend_factory are managed - // by the HttpCache and will be destroyed using |delete| when the HttpCache is - // destroyed. + // Initialize the cache from its component parts. The lifetime of the + // |network_layer| and |backend_factory| are managed by the HttpCache and + // will be destroyed using |delete| when the HttpCache is destroyed. HttpCache(HttpTransactionFactory* network_layer, NetLog* net_log, BackendFactory* backend_factory); @@ -191,12 +189,23 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, // HttpTransactionFactory implementation: virtual int CreateTransaction(RequestPriority priority, - scoped_ptr<HttpTransaction>* trans, - HttpTransactionDelegate* delegate) OVERRIDE; + scoped_ptr<HttpTransaction>* trans) OVERRIDE; virtual HttpCache* GetCache() OVERRIDE; virtual HttpNetworkSession* GetSession() OVERRIDE; - protected: + base::WeakPtr<HttpCache> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } + + // Resets the network layer to allow for tests that probe + // network changes (e.g. host unreachable). The old network layer is + // returned to allow for filter patterns that only intercept + // some creation requests. Note ownership exchange. + scoped_ptr<HttpTransactionFactory> + SetHttpNetworkTransactionFactoryForTesting( + scoped_ptr<HttpTransactionFactory> new_network_layer); + + private: + // Types -------------------------------------------------------------------- + // Disk cache entry data indices. enum { kResponseInfoIndex = 0, @@ -206,15 +215,13 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, // Must remain at the end of the enum. kNumCacheEntryDataIndices }; - friend class ViewCacheHelper; - - private: - // Types -------------------------------------------------------------------- class MetadataWriter; + class QuicServerInfoFactoryAdaptor; class Transaction; class WorkItem; friend class Transaction; + friend class ViewCacheHelper; struct PendingOp; // Info for an entry under construction. typedef std::list<Transaction*> TransactionList; @@ -347,6 +354,9 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, bool RemovePendingTransactionFromPendingOp(PendingOp* pending_op, Transaction* trans); + // Instantiates and sets QUIC server info factory. + void SetupQuicServerInfoFactory(HttpNetworkSession* session); + // Resumes processing the pending list of |entry|. void ProcessPendingQueue(ActiveEntry* entry); @@ -382,7 +392,10 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, Mode mode_; - const scoped_ptr<HttpTransactionFactory> network_layer_; + scoped_ptr<QuicServerInfoFactoryAdaptor> quic_server_info_factory_; + + scoped_ptr<HttpTransactionFactory> network_layer_; + scoped_ptr<disk_cache::Backend> disk_cache_; // The set of active entries indexed by cache key. @@ -396,6 +409,8 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory, scoped_ptr<PlaybackCacheMap> playback_cache_map_; + base::WeakPtrFactory<HttpCache> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(HttpCache); }; diff --git a/chromium/net/http/http_cache_transaction.cc b/chromium/net/http/http_cache_transaction.cc index 88fb53a69c4..efb21e1df17 100644 --- a/chromium/net/http/http_cache_transaction.cc +++ b/chromium/net/http/http_cache_transaction.cc @@ -36,7 +36,6 @@ #include "net/http/http_request_info.h" #include "net/http/http_response_headers.h" #include "net/http/http_transaction.h" -#include "net/http/http_transaction_delegate.h" #include "net/http/http_util.h" #include "net/http/partial_data.h" #include "net/ssl/ssl_cert_request_info.h" @@ -118,6 +117,15 @@ void RecordVaryHeaderHistogram(const net::HttpResponseInfo* response) { UMA_HISTOGRAM_ENUMERATION("HttpCache.Vary", vary, VARY_MAX); } +void RecordNoStoreHeaderHistogram(int load_flags, + const net::HttpResponseInfo* response) { + if (load_flags & net::LOAD_MAIN_FRAME) { + UMA_HISTOGRAM_BOOLEAN( + "Net.MainFrameNoStore", + response->headers->HasHeaderValue("cache-control", "no-store")); + } +} + } // namespace namespace net { @@ -184,12 +192,11 @@ static bool HeaderMatches(const HttpRequestHeaders& headers, HttpCache::Transaction::Transaction( RequestPriority priority, - HttpCache* cache, - HttpTransactionDelegate* transaction_delegate) + HttpCache* cache) : next_state_(STATE_NONE), request_(NULL), priority_(priority), - cache_(cache->AsWeakPtr()), + cache_(cache->GetWeakPtr()), entry_(NULL), new_entry_(NULL), new_response_(NULL), @@ -213,7 +220,7 @@ HttpCache::Transaction::Transaction( io_callback_(base::Bind(&Transaction::OnIOComplete, weak_factory_.GetWeakPtr())), transaction_pattern_(PATTERN_UNDEFINED), - transaction_delegate_(transaction_delegate), + total_received_bytes_(0), websocket_handshake_stream_base_create_helper_(NULL) { COMPILE_ASSERT(HttpCache::Transaction::kNumValidationHeaders == arraysize(kValidationHeaders), @@ -225,8 +232,6 @@ HttpCache::Transaction::~Transaction() { // after this point. callback_.Reset(); - transaction_delegate_ = NULL; - if (cache_) { if (entry_) { bool cancel_request = reading_ && response_.headers; @@ -450,6 +455,9 @@ void HttpCache::Transaction::StopCaching() { // entry how it is (it will be marked as truncated at destruction), and let // the next piece of code that executes know that we are now reading directly // from the net. + // TODO(mmenke): This doesn't release the lock on the cache entry, so a + // future request for the resource will be blocked on this one. + // Fix this. if (cache_.get() && entry_ && (mode_ & WRITE) && network_trans_.get() && !is_sparse_ && !range_requested_) { mode_ = NONE; @@ -465,6 +473,13 @@ bool HttpCache::Transaction::GetFullRequestHeaders( return false; } +int64 HttpCache::Transaction::GetTotalReceivedBytes() const { + int64 total_received_bytes = total_received_bytes_; + if (network_trans_) + total_received_bytes += network_trans_->GetTotalReceivedBytes(); + return total_received_bytes; +} + void HttpCache::Transaction::DoneReading() { if (cache_.get() && entry_) { DCHECK_NE(mode_, UPDATE); @@ -507,6 +522,9 @@ UploadProgress HttpCache::Transaction::GetUploadProgress() const { return final_upload_progress_; } +void HttpCache::Transaction::SetQuicServerInfo( + QuicServerInfo* quic_server_info) {} + bool HttpCache::Transaction::GetLoadTimingInfo( LoadTimingInfo* load_timing_info) const { if (network_trans_) @@ -541,6 +559,18 @@ void HttpCache::Transaction::SetWebSocketHandshakeStreamCreateHelper( network_trans_->SetWebSocketHandshakeStreamCreateHelper(create_helper); } +void HttpCache::Transaction::SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) { + DCHECK(!network_trans_); + before_network_start_callback_ = callback; +} + +int HttpCache::Transaction::ResumeNetworkStart() { + if (network_trans_) + return network_trans_->ResumeNetworkStart(); + return ERR_UNEXPECTED; +} + //----------------------------------------------------------------------------- void HttpCache::Transaction::DoCallback(int rv) { @@ -791,13 +821,11 @@ int HttpCache::Transaction::DoGetBackend() { cache_pending_ = true; next_state_ = STATE_GET_BACKEND_COMPLETE; net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_GET_BACKEND); - ReportCacheActionStart(); return cache_->GetBackendForTransaction(this); } int HttpCache::Transaction::DoGetBackendComplete(int result) { DCHECK(result == OK || result == ERR_FAILED); - ReportCacheActionFinish(); net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_GET_BACKEND, result); cache_pending_ = false; @@ -860,10 +888,11 @@ int HttpCache::Transaction::DoSendRequest() { send_request_since_ = TimeTicks::Now(); // Create a network transaction. - int rv = cache_->network_layer_->CreateTransaction( - priority_, &network_trans_, NULL); + int rv = cache_->network_layer_->CreateTransaction(priority_, + &network_trans_); if (rv != OK) return rv; + network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_); // Old load timing information, if any, is now obsolete. old_network_trans_load_timing_.reset(); @@ -872,15 +901,12 @@ int HttpCache::Transaction::DoSendRequest() { network_trans_->SetWebSocketHandshakeStreamCreateHelper( websocket_handshake_stream_base_create_helper_); - ReportNetworkActionStart(); next_state_ = STATE_SEND_REQUEST_COMPLETE; rv = network_trans_->Start(request_, io_callback_, net_log_); return rv; } int HttpCache::Transaction::DoSendRequestComplete(int result) { - ReportNetworkActionFinish(); - if (!cache_.get()) return ERR_UNEXPECTED; @@ -938,15 +964,37 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) { int HttpCache::Transaction::DoSuccessfulSendRequest() { DCHECK(!new_response_); const HttpResponseInfo* new_response = network_trans_->GetResponseInfo(); + bool authentication_failure = false; if (new_response->headers->response_code() == 401 || new_response->headers->response_code() == 407) { auth_response_ = *new_response; - return OK; + if (!reading_) + return OK; + + // We initiated a second request the caller doesn't know about. We should be + // able to authenticate this request because we should have authenticated + // this URL moments ago. + if (IsReadyToRestartForAuth()) { + DCHECK(!response_.auth_challenge.get()); + next_state_ = STATE_SEND_REQUEST_COMPLETE; + // In theory we should check to see if there are new cookies, but there + // is no way to do that from here. + return network_trans_->RestartWithAuth(AuthCredentials(), io_callback_); + } + + // We have to perform cleanup at this point so that at least the next + // request can succeed. + authentication_failure = true; + if (entry_) + DoomPartialEntry(false); + mode_ = NONE; + partial_.reset(); } new_response_ = new_response; - if (!ValidatePartialResponse() && !auth_response_.headers.get()) { + if (authentication_failure || + (!ValidatePartialResponse() && !auth_response_.headers.get())) { // Something went wrong with this request and we have to restart it. // If we have an authentication response, we are exposed to weird things // hapenning if the user cancels the authentication before we receive @@ -958,18 +1006,13 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { next_state_ = STATE_SEND_REQUEST; return OK; } + if (handling_206_ && mode_ == READ_WRITE && !truncated_ && !is_sparse_) { // We have stored the full entry, but it changed and the server is // sending a range. We have to delete the old entry. UpdateTransactionPattern(PATTERN_NOT_COVERED); DoneWritingToEntry(false); } - if (new_response_->headers->response_code() == 416 && - (request_->method == "GET" || request_->method == "POST")) { - DCHECK_EQ(NONE, mode_); - response_ = *new_response_; - return OK; - } if (mode_ == WRITE && transaction_pattern_ != PATTERN_ENTRY_CANT_CONDITIONALIZE) { @@ -993,6 +1036,14 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { } RecordVaryHeaderHistogram(new_response); + RecordNoStoreHeaderHistogram(request_->load_flags, new_response); + + if (new_response_->headers->response_code() == 416 && + (request_->method == "GET" || request_->method == "POST")) { + // If there is an ective entry it may be destroyed with this transaction. + response_ = *new_response_; + return OK; + } // Are we expecting a response to a conditional query? if (mode_ == READ_WRITE || mode_ == UPDATE) { @@ -1010,7 +1061,6 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { } int HttpCache::Transaction::DoNetworkRead() { - ReportNetworkActionStart(); next_state_ = STATE_NETWORK_READ_COMPLETE; return network_trans_->Read(read_buf_.get(), io_buf_len_, io_callback_); } @@ -1018,8 +1068,6 @@ int HttpCache::Transaction::DoNetworkRead() { int HttpCache::Transaction::DoNetworkReadComplete(int result) { DCHECK(mode_ & WRITE || mode_ == NONE); - ReportNetworkActionFinish(); - if (!cache_.get()) return ERR_UNEXPECTED; @@ -1053,7 +1101,6 @@ int HttpCache::Transaction::DoOpenEntry() { cache_pending_ = true; net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY); first_cache_access_since_ = TimeTicks::Now(); - ReportCacheActionStart(); return cache_->OpenEntry(cache_key_, &new_entry_, this); } @@ -1061,7 +1108,6 @@ int HttpCache::Transaction::DoOpenEntryComplete(int result) { // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is // OK, otherwise the cache will end up with an active entry without any // transaction attached. - ReportCacheActionFinish(); net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY, result); cache_pending_ = false; if (result == OK) { @@ -1105,7 +1151,6 @@ int HttpCache::Transaction::DoCreateEntry() { next_state_ = STATE_CREATE_ENTRY_COMPLETE; cache_pending_ = true; net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY); - ReportCacheActionStart(); return cache_->CreateEntry(cache_key_, &new_entry_, this); } @@ -1113,7 +1158,6 @@ int HttpCache::Transaction::DoCreateEntryComplete(int result) { // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is // OK, otherwise the cache will end up with an active entry without any // transaction attached. - ReportCacheActionFinish(); net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY, result); cache_pending_ = false; @@ -1147,12 +1191,10 @@ int HttpCache::Transaction::DoDoomEntry() { if (first_cache_access_since_.is_null()) first_cache_access_since_ = TimeTicks::Now(); net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY); - ReportCacheActionStart(); return cache_->DoomEntry(cache_key_, this); } int HttpCache::Transaction::DoDoomEntryComplete(int result) { - ReportCacheActionFinish(); net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY, result); next_state_ = STATE_CREATE_ENTRY; cache_pending_ = false; @@ -1337,20 +1379,18 @@ int HttpCache::Transaction::DoTruncateCachedData() { next_state_ = STATE_TRUNCATE_CACHED_DATA_COMPLETE; if (!entry_) return OK; - if (net_log_.IsLoggingAllEvents()) + if (net_log_.IsLogging()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); - ReportCacheActionStart(); // Truncate the stream. return WriteToEntry(kResponseContentIndex, 0, NULL, 0, io_callback_); } int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) { if (entry_) { - ReportCacheActionFinish(); - if (net_log_.IsLoggingAllEvents()) { - net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, - result); - } + if (net_log_.IsLogging()) { + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, + result); + } } next_state_ = STATE_TRUNCATE_CACHED_METADATA; @@ -1362,16 +1402,14 @@ int HttpCache::Transaction::DoTruncateCachedMetadata() { if (!entry_) return OK; - if (net_log_.IsLoggingAllEvents()) + if (net_log_.IsLogging()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); - ReportCacheActionStart(); return WriteToEntry(kMetadataIndex, 0, NULL, 0, io_callback_); } int HttpCache::Transaction::DoTruncateCachedMetadataComplete(int result) { if (entry_) { - ReportCacheActionFinish(); - if (net_log_.IsLoggingAllEvents()) { + if (net_log_.IsLogging()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, result); } @@ -1412,13 +1450,11 @@ int HttpCache::Transaction::DoCacheReadResponse() { read_buf_ = new IOBuffer(io_buf_len_); net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO); - ReportCacheActionStart(); return entry_->disk_entry->ReadData(kResponseInfoIndex, 0, read_buf_.get(), io_buf_len_, io_callback_); } int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { - ReportCacheActionFinish(); net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result); if (result != io_buf_len_ || !HttpCache::ParseResponseInfo(read_buf_->data(), io_buf_len_, @@ -1465,18 +1501,16 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { int HttpCache::Transaction::DoCacheWriteResponse() { if (entry_) { - if (net_log_.IsLoggingAllEvents()) + if (net_log_.IsLogging()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); - ReportCacheActionStart(); } return WriteResponseInfoToEntry(false); } int HttpCache::Transaction::DoCacheWriteTruncatedResponse() { if (entry_) { - if (net_log_.IsLoggingAllEvents()) + if (net_log_.IsLogging()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); - ReportCacheActionStart(); } return WriteResponseInfoToEntry(true); } @@ -1486,8 +1520,7 @@ int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { target_state_ = STATE_NONE; if (!entry_) return OK; - ReportCacheActionFinish(); - if (net_log_.IsLoggingAllEvents()) { + if (net_log_.IsLogging()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_INFO, result); } @@ -1509,7 +1542,6 @@ int HttpCache::Transaction::DoCacheReadMetadata() { new IOBufferWithSize(entry_->disk_entry->GetDataSize(kMetadataIndex)); net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_INFO); - ReportCacheActionStart(); return entry_->disk_entry->ReadData(kMetadataIndex, 0, response_.metadata.get(), response_.metadata->size(), @@ -1517,7 +1549,6 @@ int HttpCache::Transaction::DoCacheReadMetadata() { } int HttpCache::Transaction::DoCacheReadMetadataComplete(int result) { - ReportCacheActionFinish(); net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_INFO, result); if (result != response_.metadata->size()) return OnCacheReadError(result, false); @@ -1547,9 +1578,8 @@ int HttpCache::Transaction::DoCacheReadData() { DCHECK(entry_); next_state_ = STATE_CACHE_READ_DATA_COMPLETE; - if (net_log_.IsLoggingAllEvents()) + if (net_log_.IsLogging()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_READ_DATA); - ReportCacheActionStart(); if (partial_.get()) { return partial_->CacheRead(entry_->disk_entry, read_buf_.get(), io_buf_len_, io_callback_); @@ -1561,8 +1591,7 @@ int HttpCache::Transaction::DoCacheReadData() { } int HttpCache::Transaction::DoCacheReadDataComplete(int result) { - ReportCacheActionFinish(); - if (net_log_.IsLoggingAllEvents()) { + if (net_log_.IsLogging()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_READ_DATA, result); } @@ -1593,9 +1622,8 @@ int HttpCache::Transaction::DoCacheWriteData(int num_bytes) { next_state_ = STATE_CACHE_WRITE_DATA_COMPLETE; write_len_ = num_bytes; if (entry_) { - if (net_log_.IsLoggingAllEvents()) + if (net_log_.IsLogging()) net_log_.BeginEvent(NetLog::TYPE_HTTP_CACHE_WRITE_DATA); - ReportCacheActionStart(); } return AppendResponseDataToEntry(read_buf_.get(), num_bytes, io_callback_); @@ -1603,8 +1631,7 @@ int HttpCache::Transaction::DoCacheWriteData(int num_bytes) { int HttpCache::Transaction::DoCacheWriteDataComplete(int result) { if (entry_) { - ReportCacheActionFinish(); - if (net_log_.IsLoggingAllEvents()) { + if (net_log_.IsLogging()) { net_log_.EndEventWithNetErrorCode(NetLog::TYPE_HTTP_CACHE_WRITE_DATA, result); } @@ -1923,7 +1950,6 @@ int HttpCache::Transaction::RestartNetworkRequest() { DCHECK(network_trans_.get()); DCHECK_EQ(STATE_NONE, next_state_); - ReportNetworkActionStart(); next_state_ = STATE_SEND_REQUEST_COMPLETE; int rv = network_trans_->RestartIgnoringLastError(io_callback_); if (rv != ERR_IO_PENDING) @@ -1937,7 +1963,6 @@ int HttpCache::Transaction::RestartNetworkRequestWithCertificate( DCHECK(network_trans_.get()); DCHECK_EQ(STATE_NONE, next_state_); - ReportNetworkActionStart(); next_state_ = STATE_SEND_REQUEST_COMPLETE; int rv = network_trans_->RestartWithCertificate(client_cert, io_callback_); if (rv != ERR_IO_PENDING) @@ -1951,7 +1976,6 @@ int HttpCache::Transaction::RestartNetworkRequestWithAuth( DCHECK(network_trans_.get()); DCHECK_EQ(STATE_NONE, next_state_); - ReportNetworkActionStart(); next_state_ = STATE_SEND_REQUEST_COMPLETE; int rv = network_trans_->RestartWithAuth(credentials, io_callback_); if (rv != ERR_IO_PENDING) @@ -2265,8 +2289,7 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { response_.headers->HasHeaderValue("cache-control", "no-store")) || net::IsCertStatusError(response_.ssl_info.cert_status)) { DoneWritingToEntry(false); - ReportCacheActionFinish(); - if (net_log_.IsLoggingAllEvents()) + if (net_log_.IsLogging()) net_log_.EndEvent(NetLog::TYPE_HTTP_CACHE_WRITE_INFO); return OK; } @@ -2387,6 +2410,7 @@ void HttpCache::Transaction::ResetNetworkTransaction() { LoadTimingInfo load_timing; if (network_trans_->GetLoadTimingInfo(&load_timing)) old_network_trans_load_timing_.reset(new LoadTimingInfo(load_timing)); + total_received_bytes_ += network_trans_->GetTotalReceivedBytes(); network_trans_.reset(); } @@ -2422,30 +2446,6 @@ bool HttpCache::Transaction::CanResume(bool has_data) { return true; } -void HttpCache::Transaction::OnIOComplete(int result) { - DoLoop(result); -} - -void HttpCache::Transaction::ReportCacheActionStart() { - if (transaction_delegate_) - transaction_delegate_->OnCacheActionStart(); -} - -void HttpCache::Transaction::ReportCacheActionFinish() { - if (transaction_delegate_) - transaction_delegate_->OnCacheActionFinish(); -} - -void HttpCache::Transaction::ReportNetworkActionStart() { - if (transaction_delegate_) - transaction_delegate_->OnNetworkActionStart(); -} - -void HttpCache::Transaction::ReportNetworkActionFinish() { - if (transaction_delegate_) - transaction_delegate_->OnNetworkActionFinish(); -} - void HttpCache::Transaction::UpdateTransactionPattern( TransactionPattern new_transaction_pattern) { if (transaction_pattern_ == PATTERN_NOT_COVERED) @@ -2532,4 +2532,8 @@ void HttpCache::Transaction::RecordHistograms() { } } +void HttpCache::Transaction::OnIOComplete(int result) { + DoLoop(result); +} + } // namespace net diff --git a/chromium/net/http/http_cache_transaction.h b/chromium/net/http/http_cache_transaction.h index 90c4db5a39c..aeef83e9b9c 100644 --- a/chromium/net/http/http_cache_transaction.h +++ b/chromium/net/http/http_cache_transaction.h @@ -23,7 +23,6 @@ namespace net { class PartialData; struct HttpRequestInfo; -class HttpTransactionDelegate; struct LoadTimingInfo; // This is the transaction that is returned by the HttpCache transaction @@ -60,8 +59,7 @@ class HttpCache::Transaction : public HttpTransaction { }; Transaction(RequestPriority priority, - HttpCache* cache, - HttpTransactionDelegate* transaction_delegate); + HttpCache* cache); virtual ~Transaction(); Mode mode() const { return mode_; } @@ -123,15 +121,20 @@ class HttpCache::Transaction : public HttpTransaction { virtual void StopCaching() OVERRIDE; virtual bool GetFullRequestHeaders( HttpRequestHeaders* headers) const OVERRIDE; + virtual int64 GetTotalReceivedBytes() const OVERRIDE; virtual void DoneReading() OVERRIDE; virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; virtual LoadState GetLoadState() const OVERRIDE; virtual UploadProgress GetUploadProgress(void) const OVERRIDE; + virtual void SetQuicServerInfo(QuicServerInfo* quic_server_info) OVERRIDE; virtual bool GetLoadTimingInfo( LoadTimingInfo* load_timing_info) const OVERRIDE; virtual void SetPriority(RequestPriority priority) OVERRIDE; virtual void SetWebSocketHandshakeStreamCreateHelper( net::WebSocketHandshakeStreamBase::CreateHelper* create_helper) OVERRIDE; + virtual void SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) OVERRIDE; + virtual int ResumeNetworkStart() OVERRIDE; private: static const size_t kNumValidationHeaders = 2; @@ -373,16 +376,12 @@ class HttpCache::Transaction : public HttpTransaction { // data is considered for the result. bool CanResume(bool has_data); - // Called to signal completion of asynchronous IO. - void OnIOComplete(int result); - - void ReportCacheActionStart(); - void ReportCacheActionFinish(); - void ReportNetworkActionStart(); - void ReportNetworkActionFinish(); void UpdateTransactionPattern(TransactionPattern new_transaction_pattern); void RecordHistograms(); + // Called to signal completion of asynchronous IO. + void OnIOComplete(int result); + State next_state_; const HttpRequestInfo* request_; RequestPriority priority_; @@ -403,14 +402,14 @@ class HttpCache::Transaction : public HttpTransaction { std::string cache_key_; Mode mode_; State target_state_; - bool reading_; // We are already reading. + bool reading_; // We are already reading. Never reverts to false once set. bool invalid_range_; // We may bypass the cache for this request. bool truncated_; // We don't have all the response data. bool is_sparse_; // The data is stored in sparse byte ranges. bool range_requested_; // The user requested a byte range. bool handling_206_; // We must deal with this 206 response. bool cache_pending_; // We are waiting for the HttpCache. - bool done_reading_; + bool done_reading_; // All available data was read. bool vary_mismatch_; // The request doesn't match the stored vary data. bool couldnt_conditionalize_request_; scoped_refptr<IOBuffer> read_buf_; @@ -429,7 +428,7 @@ class HttpCache::Transaction : public HttpTransaction { base::TimeTicks first_cache_access_since_; base::TimeTicks send_request_since_; - HttpTransactionDelegate* transaction_delegate_; + int64 total_received_bytes_; // Load timing information for the last network request, if any. Set in the // 304 and 206 response cases, as the network transaction may be destroyed @@ -443,6 +442,8 @@ class HttpCache::Transaction : public HttpTransaction { WebSocketHandshakeStreamBase::CreateHelper* websocket_handshake_stream_base_create_helper_; + BeforeNetworkStartCallback before_network_start_callback_; + DISALLOW_COPY_AND_ASSIGN(Transaction); }; diff --git a/chromium/net/http/http_cache_unittest.cc b/chromium/net/http/http_cache_unittest.cc index aa6056a4177..c87fa1b7eec 100644 --- a/chromium/net/http/http_cache_unittest.cc +++ b/chromium/net/http/http_cache_unittest.cc @@ -4,6 +4,8 @@ #include "net/http/http_cache.h" +#include <algorithm> + #include "base/bind.h" #include "base/bind_helpers.h" #include "base/memory/scoped_vector.h" @@ -27,8 +29,7 @@ #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/http/http_transaction.h" -#include "net/http/http_transaction_delegate.h" -#include "net/http/http_transaction_unittest.h" +#include "net/http/http_transaction_test_util.h" #include "net/http/http_util.h" #include "net/http/mock_http_cache.h" #include "net/socket/client_socket_handle.h" @@ -108,60 +109,6 @@ class DeleteCacheCompletionCallback : public net::TestCompletionCallbackBase { //----------------------------------------------------------------------------- // helpers -class TestHttpTransactionDelegate : public net::HttpTransactionDelegate { - public: - TestHttpTransactionDelegate(int num_cache_actions_to_observe, - int num_network_actions_to_observe) - : num_callbacks_observed_(0), - num_remaining_cache_actions_to_observe_(num_cache_actions_to_observe), - num_remaining_network_actions_to_observe_( - num_network_actions_to_observe), - cache_action_in_progress_(false), - network_action_in_progress_(false) { - } - virtual ~TestHttpTransactionDelegate() { - EXPECT_EQ(0, num_remaining_cache_actions_to_observe_); - EXPECT_EQ(0, num_remaining_network_actions_to_observe_); - EXPECT_FALSE(cache_action_in_progress_); - EXPECT_FALSE(network_action_in_progress_); - } - virtual void OnCacheActionStart() OVERRIDE { - num_callbacks_observed_++; - EXPECT_FALSE(cache_action_in_progress_); - EXPECT_FALSE(network_action_in_progress_); - EXPECT_GT(num_remaining_cache_actions_to_observe_, 0); - num_remaining_cache_actions_to_observe_--; - cache_action_in_progress_ = true; - } - virtual void OnCacheActionFinish() OVERRIDE { - num_callbacks_observed_++; - EXPECT_TRUE(cache_action_in_progress_); - cache_action_in_progress_ = false; - } - virtual void OnNetworkActionStart() OVERRIDE { - num_callbacks_observed_++; - EXPECT_FALSE(cache_action_in_progress_); - EXPECT_FALSE(network_action_in_progress_); - EXPECT_GT(num_remaining_network_actions_to_observe_, 0); - num_remaining_network_actions_to_observe_--; - network_action_in_progress_ = true; - } - virtual void OnNetworkActionFinish() OVERRIDE { - num_callbacks_observed_++; - EXPECT_TRUE(network_action_in_progress_); - network_action_in_progress_ = false; - } - - int num_callbacks_observed() { return num_callbacks_observed_; } - - private: - int num_callbacks_observed_; - int num_remaining_cache_actions_to_observe_; - int num_remaining_network_actions_to_observe_; - bool cache_action_in_progress_; - bool network_action_in_progress_; -}; - void ReadAndVerifyTransaction(net::HttpTransaction* trans, const MockTransaction& trans_info) { std::string content; @@ -172,31 +119,19 @@ void ReadAndVerifyTransaction(net::HttpTransaction* trans, EXPECT_EQ(expected, content); } -const int kNoDelegateTransactionCheck = -1; - -void RunTransactionTestWithRequestAndDelegateAndGetTiming( - net::HttpCache* cache, - const MockTransaction& trans_info, - const MockHttpRequest& request, - net::HttpResponseInfo* response_info, - int num_cache_delegate_actions, - int num_network_delegate_actions, - const net::BoundNetLog& net_log, - net::LoadTimingInfo* load_timing_info) { +void RunTransactionTestBase(net::HttpCache* cache, + const MockTransaction& trans_info, + const MockHttpRequest& request, + net::HttpResponseInfo* response_info, + const net::BoundNetLog& net_log, + net::LoadTimingInfo* load_timing_info, + int64* received_bytes) { net::TestCompletionCallback callback; // write to the cache - scoped_ptr<TestHttpTransactionDelegate> delegate; - if (num_cache_delegate_actions != kNoDelegateTransactionCheck && - num_network_delegate_actions != kNoDelegateTransactionCheck) { - delegate.reset( - new TestHttpTransactionDelegate(num_cache_delegate_actions, - num_network_delegate_actions)); - } scoped_ptr<net::HttpTransaction> trans; - int rv = cache->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, delegate.get()); + int rv = cache->CreateTransaction(net::DEFAULT_PRIORITY, &trans); EXPECT_EQ(net::OK, rv); ASSERT_TRUE(trans.get()); @@ -223,47 +158,25 @@ void RunTransactionTestWithRequestAndDelegateAndGetTiming( } ReadAndVerifyTransaction(trans.get(), trans_info); -} -void RunTransactionTestWithRequestAndDelegate( - net::HttpCache* cache, - const MockTransaction& trans_info, - const MockHttpRequest& request, - net::HttpResponseInfo* response_info, - int num_cache_delegate_actions, - int num_network_delegate_actions) { - RunTransactionTestWithRequestAndDelegateAndGetTiming( - cache, trans_info, request, response_info, num_cache_delegate_actions, - num_network_delegate_actions, net::BoundNetLog(), NULL); + if (received_bytes) + *received_bytes = trans->GetTotalReceivedBytes(); } void RunTransactionTestWithRequest(net::HttpCache* cache, const MockTransaction& trans_info, const MockHttpRequest& request, net::HttpResponseInfo* response_info) { - RunTransactionTestWithRequestAndDelegate( - cache, trans_info, request, response_info, kNoDelegateTransactionCheck, - kNoDelegateTransactionCheck); + RunTransactionTestBase(cache, trans_info, request, response_info, + net::BoundNetLog(), NULL, NULL); } -void RunTransactionTestAndGetTiming( - net::HttpCache* cache, - const MockTransaction& trans_info, - const net::BoundNetLog& log, - net::LoadTimingInfo* load_timing_info) { - RunTransactionTestWithRequestAndDelegateAndGetTiming( - cache, trans_info, MockHttpRequest(trans_info), NULL, - kNoDelegateTransactionCheck, kNoDelegateTransactionCheck, log, - load_timing_info); -} - -void RunTransactionTestWithDelegate(net::HttpCache* cache, +void RunTransactionTestAndGetTiming(net::HttpCache* cache, const MockTransaction& trans_info, - int num_cache_delegate_actions, - int num_network_delegate_actions) { - RunTransactionTestWithRequestAndDelegate( - cache, trans_info, MockHttpRequest(trans_info), NULL, - num_cache_delegate_actions, num_network_delegate_actions); + const net::BoundNetLog& log, + net::LoadTimingInfo* load_timing_info) { + RunTransactionTestBase(cache, trans_info, MockHttpRequest(trans_info), + NULL, log, load_timing_info, NULL); } void RunTransactionTest(net::HttpCache* cache, @@ -274,8 +187,8 @@ void RunTransactionTest(net::HttpCache* cache, void RunTransactionTestWithResponseInfo(net::HttpCache* cache, const MockTransaction& trans_info, net::HttpResponseInfo* response) { - RunTransactionTestWithRequest( - cache, trans_info, MockHttpRequest(trans_info), response); + RunTransactionTestWithRequest(cache, trans_info, MockHttpRequest(trans_info), + response); } void RunTransactionTestWithResponseInfoAndGetTiming( @@ -284,10 +197,8 @@ void RunTransactionTestWithResponseInfoAndGetTiming( net::HttpResponseInfo* response, const net::BoundNetLog& log, net::LoadTimingInfo* load_timing_info) { - RunTransactionTestWithRequestAndDelegateAndGetTiming( - cache, trans_info, MockHttpRequest(trans_info), response, - kNoDelegateTransactionCheck, kNoDelegateTransactionCheck, log, - load_timing_info); + RunTransactionTestBase(cache, trans_info, MockHttpRequest(trans_info), + response, log, load_timing_info, NULL); } void RunTransactionTestWithResponse(net::HttpCache* cache, @@ -305,10 +216,8 @@ void RunTransactionTestWithResponseAndGetTiming( const net::BoundNetLog& log, net::LoadTimingInfo* load_timing_info) { net::HttpResponseInfo response; - RunTransactionTestWithRequestAndDelegateAndGetTiming( - cache, trans_info, MockHttpRequest(trans_info), &response, - kNoDelegateTransactionCheck, kNoDelegateTransactionCheck, - log, load_timing_info); + RunTransactionTestBase(cache, trans_info, MockHttpRequest(trans_info), + &response, log, load_timing_info, NULL); response.headers->GetNormalizedHeaders(response_headers); } @@ -418,6 +327,13 @@ void RangeTransactionServer::RangeHandler(const net::HttpRequestInfo* request, // We want to make sure we don't delete extra headers. EXPECT_TRUE(request->extra_headers.HasHeader(kExtraHeaderKey)); + if (request->extra_headers.HasHeader("X-Require-Mock-Auth") && + !request->extra_headers.HasHeader("Authorization")) { + response_status->assign("HTTP/1.1 401 Unauthorized"); + response_data->assign("WWW-Authenticate: Foo\n"); + return; + } + if (not_modified_) { response_status->assign("HTTP/1.1 304 Not Modified"); response_data->clear(); @@ -591,6 +507,31 @@ class FakeWebSocketHandshakeStreamCreateHelper } }; +// Returns true if |entry| is not one of the log types paid attention to in this +// test. Note that TYPE_HTTP_CACHE_WRITE_INFO and TYPE_HTTP_CACHE_*_DATA are +// ignored. +bool ShouldIgnoreLogEntry(const net::CapturingNetLog::CapturedEntry& entry) { + switch (entry.type) { + case net::NetLog::TYPE_HTTP_CACHE_GET_BACKEND: + case net::NetLog::TYPE_HTTP_CACHE_OPEN_ENTRY: + case net::NetLog::TYPE_HTTP_CACHE_CREATE_ENTRY: + case net::NetLog::TYPE_HTTP_CACHE_ADD_TO_ENTRY: + case net::NetLog::TYPE_HTTP_CACHE_DOOM_ENTRY: + case net::NetLog::TYPE_HTTP_CACHE_READ_INFO: + return false; + default: + return true; + } +} + +// Modifies |entries| to only include log entries created by the cache layer and +// asserted on in these tests. +void FilterLogEntries(net::CapturingNetLog::CapturedEntryList* entries) { + entries->erase(std::remove_if(entries->begin(), entries->end(), + &ShouldIgnoreLogEntry), + entries->end()); +} + } // namespace @@ -601,9 +542,7 @@ TEST(HttpCache, CreateThenDestroy) { MockHttpCache cache; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + EXPECT_EQ(net::OK, cache.CreateTransaction(&trans)); ASSERT_TRUE(trans.get()); } @@ -638,7 +577,6 @@ TEST(HttpCache, SimpleGETNoDiskCache) { cache.disk_cache()->set_fail_requests(); net::CapturingBoundNetLog log; - log.SetLogLevel(net::NetLog::LOG_BASIC); net::LoadTimingInfo load_timing_info; // Read from the network, and don't use the cache. @@ -649,6 +587,7 @@ TEST(HttpCache, SimpleGETNoDiskCache) { // (We attempted to both Open and Create entries, but both failed). net::CapturingNetLog::CapturedEntryList entries; log.GetEntries(&entries); + FilterLogEntries(&entries); EXPECT_EQ(6u, entries.size()); EXPECT_TRUE(net::LogContainsBeginEvent( @@ -693,15 +632,13 @@ TEST(HttpCache, ReleaseBuffer) { MockHttpRequest request(kSimpleGET_Transaction); scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - ASSERT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); const int kBufferSize = 10; scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize)); net::ReleaseBufferCompletionCallback cb(buffer.get()); - rv = trans->Start(&request, cb.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, cb.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, cb.GetResult(rv)); rv = trans->Read(buffer.get(), kBufferSize, cb.callback()); @@ -736,9 +673,8 @@ TEST(HttpCache, SimpleGETWithDiskFailures2) { MockHttpRequest request(kSimpleGET_Transaction); scoped_ptr<Context> c(new Context()); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::ERR_IO_PENDING, rv); @@ -782,9 +718,8 @@ TEST(HttpCache, SimpleGETWithDiskFailures3) { // Now fail to read from the cache. scoped_ptr<Context> c(new Context()); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); MockHttpRequest request(kSimpleGET_Transaction); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); @@ -808,10 +743,6 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) { MockHttpCache cache; net::CapturingBoundNetLog log; - - // This prevents a number of write events from being logged. - log.SetLogLevel(net::NetLog::LOG_BASIC); - net::LoadTimingInfo load_timing_info; // Write to the cache. @@ -821,6 +752,7 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) { // Check that the NetLog was filled as expected. net::CapturingNetLog::CapturedEntryList entries; log.GetEntries(&entries); + FilterLogEntries(&entries); EXPECT_EQ(8u, entries.size()); EXPECT_TRUE(net::LogContainsBeginEvent( @@ -853,6 +785,7 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit) { // Check that the NetLog was filled as expected. log.GetEntries(&entries); + FilterLogEntries(&entries); EXPECT_EQ(8u, entries.size()); EXPECT_TRUE(net::LogContainsBeginEvent( @@ -889,12 +822,9 @@ TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Miss) { net::TestCompletionCallback callback; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); - ASSERT_TRUE(trans.get()); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) rv = callback.WaitForResult(); ASSERT_EQ(net::ERR_CACHE_MISS, rv); @@ -1042,11 +972,8 @@ TEST(HttpCache, SimpleGET_CacheOverride_Offline) { MockHttpRequest request(transaction); net::TestCompletionCallback callback; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); - ASSERT_TRUE(trans.get()); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); const net::HttpResponseInfo* response_info = trans->GetResponseInfo(); @@ -1090,6 +1017,42 @@ TEST(HttpCache, SimpleGET_CacheOverride_NonOffline) { RemoveMockTransaction(&transaction); } +// Tests that was_cached was set properly on a failure, even if the cached +// response wasn't returned. +TEST(HttpCache, SimpleGET_CacheSignal_Failure) { + MockHttpCache cache; + + // Prime cache. + MockTransaction transaction(kSimpleGET_Transaction); + transaction.response_headers = "Cache-Control: no-cache\n"; + + AddMockTransaction(&transaction); + RunTransactionTest(cache.http_cache(), transaction); + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_EQ(1, cache.disk_cache()->create_count()); + RemoveMockTransaction(&transaction); + + // Network failure with error; should fail but have was_cached set. + transaction.return_code = net::ERR_FAILED; + AddMockTransaction(&transaction); + + MockHttpRequest request(transaction); + net::TestCompletionCallback callback; + scoped_ptr<net::HttpTransaction> trans; + int rv = cache.http_cache()->CreateTransaction(net::DEFAULT_PRIORITY, &trans); + EXPECT_EQ(net::OK, rv); + ASSERT_TRUE(trans.get()); + rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + EXPECT_EQ(net::ERR_FAILED, callback.GetResult(rv)); + + const net::HttpResponseInfo* response_info = trans->GetResponseInfo(); + ASSERT_TRUE(response_info); + EXPECT_TRUE(response_info->was_cached); + EXPECT_EQ(2, cache.network_layer()->transaction_count()); + + RemoveMockTransaction(&transaction); +} + // Confirm if we have an empty cache, a read is marked as network verified. TEST(HttpCache, SimpleGET_NetworkAccessed_Network) { MockHttpCache cache; @@ -1138,9 +1101,6 @@ TEST(HttpCache, SimpleGET_LoadBypassCache) { transaction.load_flags |= net::LOAD_BYPASS_CACHE; net::CapturingBoundNetLog log; - - // This prevents a number of write events from being logged. - log.SetLogLevel(net::NetLog::LOG_BASIC); net::LoadTimingInfo load_timing_info; // Write to the cache. @@ -1150,6 +1110,7 @@ TEST(HttpCache, SimpleGET_LoadBypassCache) { // Check that the NetLog was filled as expected. net::CapturingNetLog::CapturedEntryList entries; log.GetEntries(&entries); + FilterLogEntries(&entries); EXPECT_EQ(8u, entries.size()); EXPECT_TRUE(net::LogContainsBeginEvent( @@ -1317,9 +1278,8 @@ TEST(HttpCache, SimpleGET_ManyReaders) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); EXPECT_EQ(net::LOAD_STATE_IDLE, c->trans->GetLoadState()); c->result = c->trans->Start( @@ -1386,9 +1346,8 @@ TEST(HttpCache, SimpleGET_RacingReaders) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); MockHttpRequest* this_request = &request; if (i == 1 || i == 2) @@ -1472,9 +1431,8 @@ TEST(HttpCache, SimpleGET_DoomWithPending) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); MockHttpRequest* this_request = &request; if (i == 3) @@ -1521,9 +1479,8 @@ TEST(HttpCache, FastNoStoreGET_DoneWithPending) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->result = c->trans->Start( &request, c->callback.callback(), net::BoundNetLog()); @@ -1569,9 +1526,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelFirst) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->result = c->trans->Start( &request, c->callback.callback(), net::BoundNetLog()); @@ -1630,9 +1586,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_CancelCreate) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->result = c->trans->Start( &request, c->callback.callback(), net::BoundNetLog()); @@ -1682,9 +1637,8 @@ TEST(HttpCache, SimpleGET_CancelCreate) { Context* c = new Context(); - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->result = c->trans->Start( &request, c->callback.callback(), net::BoundNetLog()); @@ -1713,9 +1667,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_BypassCache) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->result = c->trans->Start( &request, c->callback.callback(), net::BoundNetLog()); @@ -1756,10 +1709,8 @@ TEST(HttpCache, SimpleGET_AbandonedCacheRead) { net::TestCompletionCallback callback; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) rv = callback.WaitForResult(); ASSERT_EQ(net::OK, rv); @@ -1792,9 +1743,8 @@ TEST(HttpCache, SimpleGET_ManyWriters_DeleteCache) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache->http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache->CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->result = c->trans->Start( &request, c->callback.callback(), net::BoundNetLog()); @@ -1832,9 +1782,8 @@ TEST(HttpCache, SimpleGET_WaitForBackend) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); } context_list[0]->result = context_list[0]->trans->Start( @@ -1879,9 +1828,8 @@ TEST(HttpCache, SimpleGET_WaitForBackend_CancelCreate) { context_list.push_back(new Context()); Context* c = context_list[i]; - c->result = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); } context_list[0]->result = context_list[0]->trans->Start( @@ -1926,9 +1874,8 @@ TEST(HttpCache, DeleteCacheWaitingForBackend) { MockHttpRequest request(kSimpleGET_Transaction); scoped_ptr<Context> c(new Context()); - c->result = cache->http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache->CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); @@ -1965,9 +1912,8 @@ TEST(HttpCache, DeleteCacheWaitingForBackend2) { MockHttpRequest request(kSimpleGET_Transaction); scoped_ptr<Context> c(new Context()); - c->result = cache->http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, c->result); + c->result = cache->CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, c->result); c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); @@ -2776,12 +2722,10 @@ TEST(HttpCache, SimplePOST_LoadOnlyFromCache_Miss) { net::TestCompletionCallback callback; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); ASSERT_TRUE(trans.get()); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); ASSERT_EQ(net::ERR_CACHE_MISS, callback.GetResult(rv)); trans.reset(); @@ -3383,6 +3327,23 @@ TEST(HttpCache, GET_Crazy206) { RemoveMockTransaction(&transaction); } +// Tests that receiving 416 for a regular request is handled correctly. +TEST(HttpCache, GET_Crazy416) { + MockHttpCache cache; + + // Write to the cache. + MockTransaction transaction(kSimpleGET_Transaction); + AddMockTransaction(&transaction); + transaction.status = "HTTP/1.1 416 Requested Range Not Satisfiable"; + RunTransactionTest(cache.http_cache(), transaction); + + EXPECT_EQ(1, cache.network_layer()->transaction_count()); + EXPECT_EQ(0, cache.disk_cache()->open_count()); + EXPECT_EQ(1, cache.disk_cache()->create_count()); + + RemoveMockTransaction(&transaction); +} + // Tests that we don't cache partial responses that can't be validated. TEST(HttpCache, RangeGET_NoStrongValidators) { MockHttpCache cache; @@ -4327,9 +4288,8 @@ TEST(HttpCache, RangeGET_Cancel) { MockHttpRequest request(kRangeGET_TransactionOK); Context* c = new Context(); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -4367,9 +4327,8 @@ TEST(HttpCache, RangeGET_Cancel2) { request.load_flags |= net::LOAD_VALIDATE_CACHE; Context* c = new Context(); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -4413,9 +4372,8 @@ TEST(HttpCache, RangeGET_Cancel3) { request.load_flags |= net::LOAD_VALIDATE_CACHE; Context* c = new Context(); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::ERR_IO_PENDING, rv); @@ -4441,9 +4399,8 @@ TEST(HttpCache, RangeGET_Cancel3) { // active entry (no open or create). c = new Context(); - rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::ERR_IO_PENDING, rv); @@ -4723,8 +4680,7 @@ TEST(HttpCache, RangeGET_OK_LoadOnlyFromCache) { net::TestCompletionCallback callback; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); + int rv = cache.http_cache()->CreateTransaction(net::DEFAULT_PRIORITY, &trans); EXPECT_EQ(net::OK, rv); ASSERT_TRUE(trans.get()); @@ -4802,9 +4758,8 @@ TEST(HttpCache, DoomOnDestruction) { MockHttpRequest request(kSimpleGET_Transaction); Context* c = new Context(); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -4833,9 +4788,8 @@ TEST(HttpCache, DoomOnDestruction2) { MockHttpRequest request(kSimpleGET_Transaction); Context* c = new Context(); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -4877,9 +4831,8 @@ TEST(HttpCache, DoomOnDestruction3) { MockHttpRequest request(transaction); Context* c = new Context(); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -4921,15 +4874,9 @@ TEST(HttpCache, SetTruncatedFlag) { MockHttpRequest request(transaction); scoped_ptr<Context> c(new Context()); - // We use a test delegate to ensure that after initiating destruction - // of the transaction, no further delegate callbacks happen. - // We initialize the TestHttpTransactionDelegate with the correct number of - // cache actions and network actions to be reported. - scoped_ptr<TestHttpTransactionDelegate> delegate( - new TestHttpTransactionDelegate(7, 3)); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, delegate.get()); - EXPECT_EQ(net::OK, rv); + + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -4952,21 +4899,11 @@ TEST(HttpCache, SetTruncatedFlag) { EXPECT_FALSE(c->callback.have_result()); MockHttpCache::SetTestMode(TEST_MODE_SYNC_ALL); - int num_delegate_callbacks_before_destruction = - delegate->num_callbacks_observed(); // Destroy the transaction. c->trans.reset(); MockHttpCache::SetTestMode(0); - // Ensure the delegate received no callbacks during destruction. - EXPECT_EQ(num_delegate_callbacks_before_destruction, - delegate->num_callbacks_observed()); - - // Since the transaction was aborted in the middle of network I/O, we will - // manually call the delegate so that its pending I/O operation will be - // closed (which is what the test delegate is expecting). - delegate->OnNetworkActionFinish(); // Make sure that we don't invoke the callback. We may have an issue if the // UrlRequestJob is killed directly (without cancelling the UrlRequest) so we @@ -4999,9 +4936,8 @@ TEST(HttpCache, DontSetTruncatedFlag) { MockHttpRequest request(transaction); scoped_ptr<Context> c(new Context()); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, c->callback.GetResult(rv)); @@ -5144,18 +5080,15 @@ TEST(HttpCache, GET_IncompleteResource_Cancel) { MockHttpRequest request(transaction); Context* c = new Context(); - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL); - EXPECT_EQ(net::OK, rv); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); // Queue another request to this transaction. We have to start this request // before the first one gets the response from the server and dooms the entry, // otherwise it will just create a new entry without being queued to the first // request. Context* pending = new Context(); - EXPECT_EQ(net::OK, - cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &pending->trans, NULL)); + ASSERT_EQ(net::OK, cache.CreateTransaction(&pending->trans)); rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::ERR_IO_PENDING, @@ -5239,12 +5172,11 @@ TEST(HttpCache, GET_IncompleteResource3) { "rg: 50-59 rg: 60-69 rg: 70-79 "; scoped_ptr<Context> c(new Context); - EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL)); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); MockHttpRequest request(transaction); - int rv = c->trans->Start( - &request, c->callback.callback(), net::BoundNetLog()); + rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, c->callback.GetResult(rv)); // We should have checked with the server before finishing Start(). @@ -5255,6 +5187,56 @@ TEST(HttpCache, GET_IncompleteResource3) { RemoveMockTransaction(&kRangeGET_TransactionOK); } +// Tests that we handle 401s for truncated resources. +TEST(HttpCache, GET_IncompleteResourceWithAuth) { + MockHttpCache cache; + AddMockTransaction(&kRangeGET_TransactionOK); + + std::string raw_headers("HTTP/1.1 200 OK\n" + "Last-Modified: Sat, 18 Apr 2007 01:10:43 GMT\n" + "ETag: \"foo\"\n" + "Accept-Ranges: bytes\n" + "Content-Length: 80\n"); + CreateTruncatedEntry(raw_headers, &cache); + + // Now make a regular request. + MockTransaction transaction(kRangeGET_TransactionOK); + transaction.request_headers = "X-Require-Mock-Auth: dummy\r\n" + EXTRA_HEADER; + transaction.data = "rg: 00-09 rg: 10-19 rg: 20-29 rg: 30-39 rg: 40-49 " + "rg: 50-59 rg: 60-69 rg: 70-79 "; + RangeTransactionServer handler; + + scoped_ptr<Context> c(new Context); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); + + MockHttpRequest request(transaction); + rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); + EXPECT_EQ(net::OK, c->callback.GetResult(rv)); + + const net::HttpResponseInfo* response = c->trans->GetResponseInfo(); + ASSERT_TRUE(response); + ASSERT_EQ(401, response->headers->response_code()); + rv = c->trans->RestartWithAuth(net::AuthCredentials(), + c->callback.callback()); + EXPECT_EQ(net::OK, c->callback.GetResult(rv)); + response = c->trans->GetResponseInfo(); + ASSERT_TRUE(response); + ASSERT_EQ(200, response->headers->response_code()); + + ReadAndVerifyTransaction(c->trans.get(), transaction); + c.reset(); // The destructor could delete the entry. + EXPECT_EQ(2, cache.network_layer()->transaction_count()); + + // Verify that the entry was not deleted. + disk_cache::Entry* entry; + ASSERT_TRUE(cache.OpenBackendEntry(kRangeGET_TransactionOK.url, &entry)); + entry->Close(); + + RemoveMockTransaction(&kRangeGET_TransactionOK); +} + // Tests that we cache a 200 response to the validation request. TEST(HttpCache, GET_IncompleteResource4) { MockHttpCache cache; @@ -5312,11 +5294,10 @@ TEST(HttpCache, GET_CancelIncompleteResource) { MockHttpRequest request(transaction); Context* c = new Context(); - EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &c->trans, NULL)); + int rv = cache.CreateTransaction(&c->trans); + ASSERT_EQ(net::OK, rv); - int rv = c->trans->Start( - &request, c->callback.callback(), net::BoundNetLog()); + rv = c->trans->Start(&request, c->callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, c->callback.GetResult(rv)); // Read 20 bytes from the cache, and 10 from the net. @@ -5441,12 +5422,9 @@ TEST(HttpCache, CachedRedirect) { // Write to the cache. { scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); - ASSERT_TRUE(trans.get()); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) rv = callback.WaitForResult(); ASSERT_EQ(net::OK, rv); @@ -5478,12 +5456,9 @@ TEST(HttpCache, CachedRedirect) { // Read from the cache. { scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); - ASSERT_TRUE(trans.get()); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) rv = callback.WaitForResult(); ASSERT_EQ(net::OK, rv); @@ -5662,12 +5637,9 @@ TEST(HttpCache, SimpleGET_SSLError) { net::TestCompletionCallback callback; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); - ASSERT_TRUE(trans.get()); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) rv = callback.WaitForResult(); ASSERT_EQ(net::ERR_CACHE_MISS, rv); @@ -5678,9 +5650,7 @@ TEST(HttpCache, OutlivedTransactions) { MockHttpCache* cache = new MockHttpCache; scoped_ptr<net::HttpTransaction> trans; - int rv = cache->http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + EXPECT_EQ(net::OK, cache->CreateTransaction(&trans)); delete cache; trans.reset(); @@ -5927,12 +5897,10 @@ TEST(HttpCache, FilterCompletion) { { scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); MockHttpRequest request(kSimpleGET_Transaction); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); @@ -5965,12 +5933,10 @@ TEST(HttpCache, DoneReading) { transaction.data = ""; scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); MockHttpRequest request(transaction); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); trans->DoneReading(); @@ -5995,11 +5961,9 @@ TEST(HttpCache, StopCachingDeletesEntry) { { scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); @@ -6035,11 +5999,9 @@ TEST(HttpCache, StopCachingThenDoneReadingDeletesEntry) { { scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); @@ -6080,11 +6042,9 @@ TEST(HttpCache, StopCachingWithAuthDeletesEntry) { { scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); trans->StopCaching(); @@ -6114,9 +6074,7 @@ TEST(HttpCache, StopCachingSavesEntry) { { scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); // Force a response that can be resumed. MockTransaction mock_transaction(kSimpleGET_Transaction); @@ -6125,7 +6083,7 @@ TEST(HttpCache, StopCachingSavesEntry) { "Content-Length: 42\n" "Etag: \"foo\"\n"; - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); @@ -6172,11 +6130,9 @@ TEST(HttpCache, StopCachingTruncatedEntry) { { // Now make a regular request. scoped_ptr<net::HttpTransaction> trans; - int rv = cache.http_cache()->CreateTransaction( - net::DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(net::OK, rv); + ASSERT_EQ(net::OK, cache.CreateTransaction(&trans)); - rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); + int rv = trans->Start(&request, callback.callback(), net::BoundNetLog()); EXPECT_EQ(net::OK, callback.GetResult(rv)); scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); @@ -6253,34 +6209,13 @@ TEST(HttpCache, TruncatedByContentLength2) { entry->Close(); } -TEST(HttpCache, SimpleGET_LoadOnlyFromCache_Hit_TransactionDelegate) { - MockHttpCache cache; - - // Write to the cache. - RunTransactionTestWithDelegate(cache.http_cache(), - kSimpleGET_Transaction, - 8, - 3); - - // Force this transaction to read from the cache. - MockTransaction transaction(kSimpleGET_Transaction); - transaction.load_flags |= net::LOAD_ONLY_FROM_CACHE; - - RunTransactionTestWithDelegate(cache.http_cache(), - kSimpleGET_Transaction, - 5, - 0); -} - // Make sure that calling SetPriority on a cache transaction passes on // its priority updates to its underlying network transaction. TEST(HttpCache, SetPriority) { MockHttpCache cache; scoped_ptr<net::HttpTransaction> trans; - EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction( - net::IDLE, &trans, NULL)); - ASSERT_TRUE(trans.get()); + ASSERT_EQ(net::OK, cache.http_cache()->CreateTransaction(net::IDLE, &trans)); // Shouldn't crash, but doesn't do anything either. trans->SetPriority(net::LOW); @@ -6322,9 +6257,7 @@ TEST(HttpCache, SetWebSocketHandshakeStreamCreateHelper) { FakeWebSocketHandshakeStreamCreateHelper create_helper; scoped_ptr<net::HttpTransaction> trans; - EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction( - net::IDLE, &trans, NULL)); - ASSERT_TRUE(trans.get()); + ASSERT_EQ(net::OK, cache.http_cache()->CreateTransaction(net::IDLE, &trans)); EXPECT_FALSE(cache.network_layer()->last_transaction()); @@ -6365,9 +6298,8 @@ TEST(HttpCache, SetPriorityNewTransaction) { "rg: 50-59 rg: 60-69 rg: 70-79 "; scoped_ptr<net::HttpTransaction> trans; - EXPECT_EQ(net::OK, cache.http_cache()->CreateTransaction( - net::MEDIUM, &trans, NULL)); - ASSERT_TRUE(trans.get()); + ASSERT_EQ(net::OK, + cache.http_cache()->CreateTransaction(net::MEDIUM, &trans)); EXPECT_EQ(net::DEFAULT_PRIORITY, cache.network_layer()->last_create_transaction_priority()); @@ -6390,3 +6322,97 @@ TEST(HttpCache, SetPriorityNewTransaction) { RemoveMockTransaction(&kRangeGET_TransactionOK); } + +int64 RunTransactionAndGetReceivedBytes( + MockHttpCache& cache, + const MockTransaction& trans_info) { + int64 received_bytes = -1; + RunTransactionTestBase(cache.http_cache(), trans_info, + MockHttpRequest(trans_info), NULL, net::BoundNetLog(), + NULL, &received_bytes); + return received_bytes; +} + +int64 TransactionSize(const MockTransaction& transaction) { + return strlen(transaction.status) + strlen(transaction.response_headers) + + strlen(transaction.data); +} + +TEST(HttpCache, ReceivedBytesCacheMissAndThenHit) { + MockHttpCache cache; + + MockTransaction transaction(kSimpleGET_Transaction); + int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(TransactionSize(transaction), received_bytes); + + received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(0, received_bytes); +} + +TEST(HttpCache, ReceivedBytesConditionalRequest304) { + MockHttpCache cache; + + ScopedMockTransaction transaction(kETagGET_Transaction); + int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(TransactionSize(transaction), received_bytes); + + transaction.load_flags = net::LOAD_VALIDATE_CACHE; + transaction.handler = ETagGet_ConditionalRequest_Handler; + received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(TransactionSize(transaction), received_bytes); +} + +TEST(HttpCache, ReceivedBytesConditionalRequest200) { + MockHttpCache cache; + + MockTransaction transaction(kTypicalGET_Transaction); + transaction.request_headers = "Foo: bar\r\n"; + transaction.response_headers = + "Date: Wed, 28 Nov 2007 09:40:09 GMT\n" + "Last-Modified: Wed, 28 Nov 2007 00:40:09 GMT\n" + "Etag: \"foopy\"\n" + "Cache-Control: max-age=0\n" + "Vary: Foo\n"; + AddMockTransaction(&transaction); + int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(TransactionSize(transaction), received_bytes); + + RevalidationServer server; + transaction.handler = server.Handler; + transaction.request_headers = "Foo: none\r\n"; + received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(TransactionSize(transaction), received_bytes); + + RemoveMockTransaction(&transaction); +} + +TEST(HttpCache, ReceivedBytesRange) { + MockHttpCache cache; + AddMockTransaction(&kRangeGET_TransactionOK); + MockTransaction transaction(kRangeGET_TransactionOK); + + // Read bytes 40-49 from the network. + int64 received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + int64 range_response_size = TransactionSize(transaction); + EXPECT_EQ(range_response_size, received_bytes); + + // Read bytes 40-49 from the cache. + received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(0, received_bytes); + base::MessageLoop::current()->RunUntilIdle(); + + // Read bytes 30-39 from the network. + transaction.request_headers = "Range: bytes = 30-39\r\n" EXTRA_HEADER; + transaction.data = "rg: 30-39 "; + received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(range_response_size, received_bytes); + base::MessageLoop::current()->RunUntilIdle(); + + // Read bytes 20-29 and 50-59 from the network, bytes 30-49 from the cache. + transaction.request_headers = "Range: bytes = 20-59\r\n" EXTRA_HEADER; + transaction.data = "rg: 20-29 rg: 30-39 rg: 40-49 rg: 50-59 "; + received_bytes = RunTransactionAndGetReceivedBytes(cache, transaction); + EXPECT_EQ(range_response_size * 2, received_bytes); + + RemoveMockTransaction(&kRangeGET_TransactionOK); +} diff --git a/chromium/net/http/http_chunked_decoder.h b/chromium/net/http/http_chunked_decoder.h index 9ee7400ad7d..23076f9b368 100644 --- a/chromium/net/http/http_chunked_decoder.h +++ b/chromium/net/http/http_chunked_decoder.h @@ -122,8 +122,7 @@ class NET_EXPORT_PRIVATE HttpChunkedDecoder { // Set to true when FilterBuf encounters the final CRLF. bool reached_eof_; - // The number of unfiltered bytes after the final CRLF, either extraneous - // data or the first part of the next response in a pipelined stream. + // The number of extraneous unfiltered bytes after the final CRLF. int bytes_after_eof_; }; diff --git a/chromium/net/http/http_content_disposition.cc b/chromium/net/http/http_content_disposition.cc index 3dbf234b943..3a1dedeb788 100644 --- a/chromium/net/http/http_content_disposition.cc +++ b/chromium/net/http/http_content_disposition.cc @@ -5,15 +5,14 @@ #include "net/http/http_content_disposition.h" #include "base/base64.h" -#include "base/i18n/icu_string_conversions.h" #include "base/logging.h" #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "net/base/net_string_util.h" #include "net/base/net_util.h" #include "net/http/http_util.h" -#include "third_party/icu/source/common/unicode/ucnv.h" namespace { @@ -65,32 +64,16 @@ bool DecodeBQEncoding(const std::string& part, std::string* output) { std::string decoded; if (!((enc_type == B_ENCODING) ? - base::Base64Decode(part, &decoded) : DecodeQEncoding(part, &decoded))) + base::Base64Decode(part, &decoded) : DecodeQEncoding(part, &decoded))) { return false; + } if (decoded.empty()) { output->clear(); return true; } - UErrorCode err = U_ZERO_ERROR; - UConverter* converter(ucnv_open(charset.c_str(), &err)); - if (U_FAILURE(err)) - return false; - - // A single byte in a legacy encoding can be expanded to 3 bytes in UTF-8. - // A 'two-byte character' in a legacy encoding can be expanded to 4 bytes - // in UTF-8. Therefore, the expansion ratio is 3 at most. Add one for a - // trailing '\0'. - size_t output_length = decoded.length() * 3 + 1; - char* buf = WriteInto(output, output_length); - output_length = ucnv_toAlgorithmic(UCNV_UTF8, converter, buf, output_length, - decoded.data(), decoded.length(), &err); - ucnv_close(converter); - if (U_FAILURE(err)) - return false; - output->resize(output_length); - return true; + return net::ConvertToUtf8(decoded, charset.c_str(), output); } bool DecodeWord(const std::string& encoded_word, @@ -103,19 +86,18 @@ bool DecodeWord(const std::string& encoded_word, if (encoded_word.empty()) return true; - if (!IsStringASCII(encoded_word)) { + if (!base::IsStringASCII(encoded_word)) { // Try UTF-8, referrer_charset and the native OS default charset in turn. - if (IsStringUTF8(encoded_word)) { + if (base::IsStringUTF8(encoded_word)) { *output = encoded_word; } else { base::string16 utf16_output; if (!referrer_charset.empty() && - base::CodepageToUTF16(encoded_word, referrer_charset.c_str(), - base::OnStringConversionError::FAIL, - &utf16_output)) { - *output = UTF16ToUTF8(utf16_output); + net::ConvertToUTF16(encoded_word, referrer_charset.c_str(), + &utf16_output)) { + *output = base::UTF16ToUTF8(utf16_output); } else { - *output = WideToUTF8(base::SysNativeMBToWide(encoded_word)); + *output = base::WideToUTF8(base::SysNativeMBToWide(encoded_word)); } } @@ -209,7 +191,7 @@ bool DecodeWord(const std::string& encoded_word, if (decoded_word != encoded_word) *parse_result_flags |= net::HttpContentDisposition::HAS_PERCENT_ENCODED_STRINGS; - if (IsStringUTF8(decoded_word)) { + if (base::IsStringUTF8(decoded_word)) { output->swap(decoded_word); return true; // We can try either the OS default charset or 'origin charset' here, @@ -335,7 +317,7 @@ bool DecodeExtValue(const std::string& param_value, std::string* decoded) { return false; // RFC 5987 value should be ASCII-only. - if (!IsStringASCII(value)) { + if (!base::IsStringASCII(value)) { decoded->clear(); return true; } @@ -343,7 +325,7 @@ bool DecodeExtValue(const std::string& param_value, std::string* decoded) { std::string unescaped = net::UnescapeURLComponent( value, net::UnescapeRule::SPACES | net::UnescapeRule::URL_SPECIAL_CHARS); - return base::ConvertToUtf8AndNormalize(unescaped, charset, decoded); + return net::ConvertToUtf8AndNormalize(unescaped, charset.c_str(), decoded); } } // namespace diff --git a/chromium/net/http/http_content_disposition_unittest.cc b/chromium/net/http/http_content_disposition_unittest.cc index 62d95778c0b..43fef9dd0ea 100644 --- a/chromium/net/http/http_content_disposition_unittest.cc +++ b/chromium/net/http/http_content_disposition_unittest.cc @@ -198,7 +198,7 @@ TEST(HttpContentDispositionTest, Filename) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { HttpContentDisposition header(tests[i].header, tests[i].referrer_charset); EXPECT_EQ(tests[i].expected, - UTF8ToWide(header.filename())) + base::UTF8ToWide(header.filename())) << "Failed on input: " << tests[i].header; } } @@ -507,7 +507,7 @@ TEST(HttpContentDispositionTest, tc2231) { HttpContentDisposition header(tests[i].header, std::string()); EXPECT_EQ(tests[i].expected_type, header.type()) << "Failed on input: " << tests[i].header; - EXPECT_EQ(tests[i].expected_filename, UTF8ToWide(header.filename())) + EXPECT_EQ(tests[i].expected_filename, base::UTF8ToWide(header.filename())) << "Failed on input: " << tests[i].header; } } diff --git a/chromium/net/http/http_log_util.cc b/chromium/net/http/http_log_util.cc new file mode 100644 index 00000000000..ab6ebda74ac --- /dev/null +++ b/chromium/net/http/http_log_util.cc @@ -0,0 +1,81 @@ +// Copyright 2014 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/http/http_log_util.h" + +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "net/http/http_auth_challenge_tokenizer.h" + +namespace net { + +namespace { + +bool ShouldRedactChallenge(HttpAuthChallengeTokenizer* challenge) { + // Ignore lines with commas, as they may contain lists of schemes, and + // the information we want to hide is Base64 encoded, so has no commas. + if (challenge->challenge_text().find(',') != std::string::npos) + return false; + + std::string scheme = StringToLowerASCII(challenge->scheme()); + // Invalid input. + if (scheme.empty()) + return false; + + // Ignore Basic and Digest authentication challenges, as they contain + // public information. + if (scheme == "basic" || scheme == "digest") + return false; + + return true; +} + +} // namespace + +std::string ElideHeaderValueForNetLog(NetLog::LogLevel log_level, + const std::string& header, + const std::string& value) { +#if defined(SPDY_PROXY_AUTH_ORIGIN) + if (!base::strcasecmp(header.c_str(), "proxy-authorization") || + !base::strcasecmp(header.c_str(), "proxy-authenticate")) { + return "[elided]"; + } +#endif + + if (log_level < NetLog::LOG_STRIP_PRIVATE_DATA) + return value; + + // Note: this logic should be kept in sync with stripCookiesAndLoginInfo in + // chrome/browser/resources/net_internals/log_view_painter.js. + + std::string::const_iterator redact_begin = value.begin(); + std::string::const_iterator redact_end = value.begin(); + if (!base::strcasecmp(header.c_str(), "set-cookie") || + !base::strcasecmp(header.c_str(), "set-cookie2") || + !base::strcasecmp(header.c_str(), "cookie") || + !base::strcasecmp(header.c_str(), "authorization") || + !base::strcasecmp(header.c_str(), "proxy-authorization")) { + redact_begin = value.begin(); + redact_end = value.end(); + } else if (!base::strcasecmp(header.c_str(), "www-authenticate") || + !base::strcasecmp(header.c_str(), "proxy-authenticate")) { + // Look for authentication information from data received from the server in + // multi-round Negotiate authentication. + HttpAuthChallengeTokenizer challenge(value.begin(), value.end()); + if (ShouldRedactChallenge(&challenge)) { + redact_begin = challenge.params_begin(); + redact_end = challenge.params_end(); + } + } + + if (redact_begin == redact_end) + return value; + + return std::string(value.begin(), redact_begin) + + base::StringPrintf("[%ld bytes were stripped]", + static_cast<long>(redact_end - redact_begin)) + + std::string(redact_end, value.end()); +} + +} // namespace net diff --git a/chromium/net/http/http_log_util.h b/chromium/net/http/http_log_util.h new file mode 100644 index 00000000000..f18c6e737b9 --- /dev/null +++ b/chromium/net/http/http_log_util.h @@ -0,0 +1,24 @@ +// Copyright 2014 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. + +#ifndef NET_HTTP_HTTP_LOG_UTIL_ +#define NET_HTTP_HTTP_LOG_UTIL_ + +#include <string> + +#include "net/base/net_export.h" +#include "net/base/net_log.h" + +namespace net { + +// Given an HTTP header |header| with value |value|, returns the elided version +// of the header value at |log_level|. +NET_EXPORT_PRIVATE std::string ElideHeaderValueForNetLog( + NetLog::LogLevel log_level, + const std::string& header, + const std::string& value); + +} // namespace net + +#endif // NET_HTTP_HTTP_LOG_UTIL_ diff --git a/chromium/net/http/http_log_util_unittest.cc b/chromium/net/http/http_log_util_unittest.cc new file mode 100644 index 00000000000..1b0e9dbbfa2 --- /dev/null +++ b/chromium/net/http/http_log_util_unittest.cc @@ -0,0 +1,76 @@ +// Copyright 2014 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/http/http_log_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +TEST(HttpLogUtilTest, ElideHeaderValueForNetLog) { + // Only elide for appropriate log level. + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Cookie", "name=value")); + EXPECT_EQ("name=value", ElideHeaderValueForNetLog( + net::NetLog::LOG_ALL_BUT_BYTES, "Cookie", "name=value")); + + // Headers are compared case insensitively. + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "cOoKiE", "name=value")); + + // These headers should be completely elided. + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Set-Cookie2", "name=value")); + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Authorization", "Basic 1234")); +#if !defined(SPDY_PROXY_AUTH_ORIGIN) + EXPECT_EQ("[10 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "Proxy-Authorization", "Basic 1234")); +#endif + + // Unknown headers should pass through. + EXPECT_EQ("value", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Boring", "value")); + + // Basic and Digest auth challenges are public. + EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "WWW-Authenticate", "Basic realm=test")); + EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "WWW-Authenticate", "Digest realm=test")); +#if !defined(SPDY_PROXY_AUTH_ORIGIN) + EXPECT_EQ("Basic realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "Proxy-Authenticate", "Basic realm=test")); + EXPECT_EQ("Digest realm=test", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, + "Proxy-Authenticate", "Digest realm=test")); +#endif + + // Multi-round mechanisms partially elided. + EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234")); +#if !defined(SPDY_PROXY_AUTH_ORIGIN) + EXPECT_EQ("NTLM [4 bytes were stripped]", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "Proxy-Authenticate", "NTLM 1234")); +#endif + + // Leave whitespace intact. + EXPECT_EQ("NTLM [4 bytes were stripped] ", ElideHeaderValueForNetLog( + net::NetLog::LOG_STRIP_PRIVATE_DATA, "WWW-Authenticate", "NTLM 1234 ")); + + // Extra elisions for SPDY_PROXY_AUTH_ORIGIN. +#if defined(SPDY_PROXY_AUTH_ORIGIN) + EXPECT_EQ("[elided]", ElideHeaderValueForNetLog( + net::NetLog::LOG_ALL_BUT_BYTES, + "Proxy-Authenticate", "Basic realm=test")); + EXPECT_EQ("[elided]", ElideHeaderValueForNetLog( + net::NetLog::LOG_ALL_BUT_BYTES, "Proxy-Authorization", "Basic 1234")); +#endif +} + +} // namspace net diff --git a/chromium/net/http/http_network_layer.cc b/chromium/net/http/http_network_layer.cc index 7d3f1588006..0704de420de 100644 --- a/chromium/net/http/http_network_layer.cc +++ b/chromium/net/http/http_network_layer.cc @@ -60,8 +60,7 @@ void HttpNetworkLayer::ForceAlternateProtocol() { //----------------------------------------------------------------------------- int HttpNetworkLayer::CreateTransaction(RequestPriority priority, - scoped_ptr<HttpTransaction>* trans, - HttpTransactionDelegate* delegate) { + scoped_ptr<HttpTransaction>* trans) { if (suspended_) return ERR_NETWORK_IO_SUSPENDED; diff --git a/chromium/net/http/http_network_layer.h b/chromium/net/http/http_network_layer.h index c4c41aec43e..fc94d0a7234 100644 --- a/chromium/net/http/http_network_layer.h +++ b/chromium/net/http/http_network_layer.h @@ -44,8 +44,7 @@ class NET_EXPORT HttpNetworkLayer // HttpTransactionFactory methods: virtual int CreateTransaction(RequestPriority priority, - scoped_ptr<HttpTransaction>* trans, - HttpTransactionDelegate* delegate) OVERRIDE; + scoped_ptr<HttpTransaction>* trans) OVERRIDE; virtual HttpCache* GetCache() OVERRIDE; virtual HttpNetworkSession* GetSession() OVERRIDE; diff --git a/chromium/net/http/http_network_layer_unittest.cc b/chromium/net/http/http_network_layer_unittest.cc index 98b8d4eaf44..7225001e752 100644 --- a/chromium/net/http/http_network_layer_unittest.cc +++ b/chromium/net/http/http_network_layer_unittest.cc @@ -11,7 +11,7 @@ #include "net/dns/mock_host_resolver.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties_impl.h" -#include "net/http/http_transaction_unittest.h" +#include "net/http/http_transaction_test_util.h" #include "net/http/transport_security_state.h" #include "net/proxy/proxy_service.h" #include "net/socket/socket_test_util.h" @@ -49,18 +49,6 @@ class HttpNetworkLayerTest : public PlatformTest { factory_.reset(new HttpNetworkLayer(network_session_.get())); } -#if defined(SPDY_PROXY_AUTH_ORIGIN) - std::string GetChromeProxy() { - return HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN)).ToString(); - } -#endif - -#if defined(SPDY_PROXY_AUTH_ORIGIN) && defined(DATA_REDUCTION_FALLBACK_HOST) - std::string GetChromeFallbackProxy() { - return HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST)).ToString(); - } -#endif - void ExecuteRequestExpectingContentAndHeader(const std::string& method, const std::string& content, const std::string& header, @@ -73,7 +61,7 @@ class HttpNetworkLayerTest : public PlatformTest { request_info.load_flags = LOAD_NORMAL; scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(OK, rv); rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); @@ -108,7 +96,7 @@ class HttpNetworkLayerTest : public PlatformTest { // Simulates a request through a proxy which returns a bypass, which is then // retried through a second proxy that doesn't bypass. // Checks that the expected requests were issued, the expected content was - // recieved, and the first proxy |bad_proxy| was marked as bad. + // received, and the first proxy |bad_proxy| was marked as bad. void TestProxyFallback(const std::string& bad_proxy) { MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\n" @@ -278,28 +266,28 @@ class HttpNetworkLayerTest : public PlatformTest { TEST_F(HttpNetworkLayerTest, CreateAndDestroy) { scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(OK, rv); EXPECT_TRUE(trans.get() != NULL); } TEST_F(HttpNetworkLayerTest, Suspend) { scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(OK, rv); trans.reset(); factory_->OnSuspend(); - rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, rv); ASSERT_TRUE(trans == NULL); factory_->OnResume(); - rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(OK, rv); } @@ -329,7 +317,7 @@ TEST_F(HttpNetworkLayerTest, GET) { request_info.load_flags = LOAD_NORMAL; scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(OK, rv); rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); @@ -342,401 +330,6 @@ TEST_F(HttpNetworkLayerTest, GET) { EXPECT_EQ("hello world", contents); } -// Proxy bypass tests. These tests run through various server-induced -// proxy bypass scenarios using both PAC file and fixed proxy params. -// The test scenarios are: -// - bypass with two proxies configured and the first but not the second -// is bypassed. -// - bypass with one proxy configured and an explicit fallback to direct -// connections -// - bypass with two proxies configured and both are bypassed -// - bypass with one proxy configured which is bypassed with no defined -// fallback - -#if defined(SPDY_PROXY_AUTH_ORIGIN) -TEST_F(HttpNetworkLayerTest, ServerTwoProxyBypassPac) { - std::string bad_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + bad_proxy + "; PROXY good:8080")); - TestProxyFallback(bad_proxy); -} - -TEST_F(HttpNetworkLayerTest, ServerTwoProxyBypassFixed) { - std::string bad_proxy = GetChromeProxy(); - ConfigureTestDependencies( - ProxyService::CreateFixed(bad_proxy +", good:8080")); - TestProxyFallback(bad_proxy); -} - -TEST_F(HttpNetworkLayerTest, BypassAndRetryIdempotentMethods) { - std::string bad_proxy = GetChromeProxy(); - const struct { - std::string method; - std::string content; - bool expected_to_retry; - } tests[] = { - { - "GET", - "content", - true, - }, - { - "OPTIONS", - "content", - true, - }, - { - "HEAD", - "", - true, - }, - { - "PUT", - "", - true, - }, - { - "DELETE", - "content", - true, - }, - { - "TRACE", - "content", - true, - }, - { - "POST", - "Bypass message", - false, - }, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - ConfigureTestDependencies( - ProxyService::CreateFixed(bad_proxy +", good:8080")); - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n" - "Chrome-Proxy: bypass=0\r\n\r\n"), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - TestProxyFallbackByMethodWithMockReads(bad_proxy, "", data_reads, - arraysize(data_reads), - tests[i].method, - tests[i].content, - tests[i].expected_to_retry, 1u); - } -} - -TEST_F(HttpNetworkLayerTest, ServerOneProxyWithDirectBypassPac) { - std::string bad_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + bad_proxy + "; DIRECT")); - TestProxyFallbackToDirect(bad_proxy); -} - -TEST_F(HttpNetworkLayerTest, ServerOneProxyWithDirectBypassFixed) { - std::string bad_proxy = GetChromeProxy(); - ConfigureTestDependencies( - ProxyService::CreateFixed(bad_proxy + ", direct://")); - TestProxyFallbackToDirect(bad_proxy); -} - -#if defined(DATA_REDUCTION_FALLBACK_HOST) -TEST_F(HttpNetworkLayerTest, ServerTwoProxyDoubleBypassPac) { - std::string bad_proxy = GetChromeProxy(); - std::string bad_proxy2 = - HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST)).ToString(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + bad_proxy + "; PROXY " + bad_proxy2)); - TestProxyFallbackFail(2u, bad_proxy, bad_proxy2); -} - -TEST_F(HttpNetworkLayerTest, ServerTwoProxyDoubleBypassFixed) { - std::string bad_proxy = GetChromeProxy(); - std::string bad_proxy2 = - HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST)).ToString(); - ConfigureTestDependencies(ProxyService::CreateFixed( - bad_proxy + ", " + bad_proxy2)); - TestProxyFallbackFail(2u, bad_proxy, bad_proxy2); -} -#endif - -TEST_F(HttpNetworkLayerTest, ServerOneProxyNoDirectBypassPac) { - std::string bad_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + bad_proxy)); - TestProxyFallbackFail(1u, bad_proxy, ""); -} - -TEST_F(HttpNetworkLayerTest, ServerOneProxyNoDirectBypassFixed) { - std::string bad_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixed(bad_proxy)); - TestProxyFallbackFail(1u, bad_proxy, ""); -} - -TEST_F(HttpNetworkLayerTest, ServerFallbackOn5xxError) { - // Verify that "500 Internal Server Error", "502 Bad Gateway", and - // "503 Service Unavailable" via the data reduction proxy induce proxy - // fallback to a second proxy, if configured. - - // To configure this test, we need to wire up a custom proxy service to use - // a pair of proxies. We'll induce fallback via the first and return - // the expected data via the second. - std::string data_reduction_proxy( - HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN)).ToString()); - std::string pac_string = base::StringPrintf( - "PROXY %s; PROXY good:8080", data_reduction_proxy.data()); - - std::string headers[] = { - "HTTP/1.1 500 Internal Server Error\r\n\r\n", - "HTTP/1.1 502 Bad Gateway\r\n\r\n", - "HTTP/1.1 503 Service Unavailable\r\n\r\n" - }; - - for (size_t i = 0; i < arraysize(headers); ++i) { - ConfigureTestDependencies( - ProxyService::CreateFixedFromPacResult(pac_string)); - - MockRead data_reads[] = { - MockRead(headers[i].c_str()), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - - MockWrite data_writes[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - }; - - StaticSocketDataProvider data1(data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - mock_socket_factory_.AddSocketDataProvider(&data1); - - // Second data provider returns the expected content. - MockRead data_reads2[] = { - MockRead("HTTP/1.0 200 OK\r\n" - "Server: not-proxy\r\n\r\n"), - MockRead("content"), - MockRead(SYNCHRONOUS, OK), - }; - MockWrite data_writes2[] = { - MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Proxy-Connection: keep-alive\r\n\r\n"), - }; - - StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), - data_writes2, arraysize(data_writes2)); - mock_socket_factory_.AddSocketDataProvider(&data2); - - TestCompletionCallback callback; - - HttpRequestInfo request_info; - request_info.url = GURL("http://www.google.com/"); - request_info.method = "GET"; - request_info.load_flags = LOAD_NORMAL; - - scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(OK, rv); - - rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); - if (rv == ERR_IO_PENDING) - rv = callback.WaitForResult(); - ASSERT_EQ(OK, rv); - - std::string contents; - rv = ReadTransaction(trans.get(), &contents); - EXPECT_EQ(OK, rv); - - // We should obtain content from the second socket provider write - // corresponding to the fallback proxy. - EXPECT_EQ("content", contents); - // We also have a server header here that isn't set by the proxy. - EXPECT_TRUE(trans->GetResponseInfo()->headers->HasHeaderValue( - "server", "not-proxy")); - // We should also observe the data reduction proxy in the retry list. - ASSERT_EQ(1u, proxy_service_->proxy_retry_info().size()); - EXPECT_EQ(data_reduction_proxy, - (*proxy_service_->proxy_retry_info().begin()).first); - } -} -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) - -TEST_F(HttpNetworkLayerTest, ProxyBypassIgnoredOnDirectConnectionPac) { - // Verify that a Chrome-Proxy header is ignored when returned from a directly - // connected origin server. - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult("DIRECT")); - - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n" - "Chrome-Proxy: bypass=0\r\n\r\n"), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - MockWrite data_writes[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - StaticSocketDataProvider data1(data_reads, arraysize(data_reads), - data_writes, arraysize(data_writes)); - mock_socket_factory_.AddSocketDataProvider(&data1); - TestCompletionCallback callback; - - HttpRequestInfo request_info; - request_info.url = GURL("http://www.google.com/"); - request_info.method = "GET"; - request_info.load_flags = LOAD_NORMAL; - - scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); - EXPECT_EQ(OK, rv); - - rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); - if (rv == ERR_IO_PENDING) - rv = callback.WaitForResult(); - ASSERT_EQ(OK, rv); - - // We should have read the original page data. - std::string contents; - rv = ReadTransaction(trans.get(), &contents); - EXPECT_EQ(OK, rv); - EXPECT_EQ("Bypass message", contents); - - // We should have no entries in our bad proxy list. - ASSERT_EQ(0u, proxy_service_->proxy_retry_info().size()); -} - -#if defined(SPDY_PROXY_AUTH_ORIGIN) -TEST_F(HttpNetworkLayerTest, ServerFallbackWithProxyTimedBypass) { - // Verify that a Chrome-Proxy: bypass=<seconds> header induces proxy - // fallback to a second proxy, if configured. - std::string bad_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + bad_proxy + "; PROXY good:8080")); - - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n" - "Connection: keep-alive\r\n" - "Chrome-Proxy: bypass=86400\r\n" - "Via: 1.1 Chrome Compression Proxy\r\n\r\n"), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - - TestProxyFallbackWithMockReads(bad_proxy, "", data_reads, - arraysize(data_reads), 1u); - EXPECT_EQ(base::TimeDelta::FromSeconds(86400), - (*proxy_service_->proxy_retry_info().begin()).second.current_delay); -} - -TEST_F(HttpNetworkLayerTest, ServerFallbackWithWrongViaHeader) { - // Verify that a Via header that lacks the Chrome-Proxy induces proxy fallback - // to a second proxy, if configured. - std::string chrome_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + chrome_proxy + "; PROXY good:8080")); - - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n" - "Connection: keep-alive\r\n" - "Via: 1.0 some-other-proxy\r\n\r\n"), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - - TestProxyFallbackWithMockReads(chrome_proxy, std::string(), data_reads, - arraysize(data_reads), 1u); -} - -TEST_F(HttpNetworkLayerTest, ServerFallbackWithNoViaHeader) { - // Verify that the lack of a Via header induces proxy fallback to a second - // proxy, if configured. - std::string chrome_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + chrome_proxy + "; PROXY good:8080")); - - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n" - "Connection: keep-alive\r\n\r\n"), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - - TestProxyFallbackWithMockReads(chrome_proxy, std::string(), data_reads, - arraysize(data_reads), 1u); -} - -TEST_F(HttpNetworkLayerTest, NoServerFallbackWith304Response) { - // Verify that Chrome will not be induced to bypass the Chrome proxy when - // the Chrome Proxy via header is absent on a 304. - std::string chrome_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + chrome_proxy + "; PROXY good:8080")); - - MockRead data_reads[] = { - MockRead("HTTP/1.1 304 Not Modified\r\n" - "Connection: keep-alive\r\n\r\n"), - MockRead(SYNCHRONOUS, OK), - }; - - TestProxyFallbackByMethodWithMockReads(chrome_proxy, std::string(), - data_reads, arraysize(data_reads), - "GET", std::string(), false, 0); -} - -TEST_F(HttpNetworkLayerTest, NoServerFallbackWithChainedViaHeader) { - // Verify that Chrome will not be induced to bypass the Chrome proxy when - // the Chrome Proxy via header is present, even if that header is chained. - std::string chrome_proxy = GetChromeProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + chrome_proxy + "; PROXY good:8080")); - - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n" - "Connection: keep-alive\r\n" - "Via: 1.1 Chrome Compression Proxy, 1.0 some-other-proxy\r\n\r\n"), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - - TestProxyFallbackByMethodWithMockReads(chrome_proxy, std::string(), - data_reads, arraysize(data_reads), - "GET", "Bypass message", false, 0); -} - -#if defined(DATA_REDUCTION_FALLBACK_HOST) -TEST_F(HttpNetworkLayerTest, ServerFallbackWithProxyTimedBypassAll) { - // Verify that a Chrome-Proxy: block=<seconds> header bypasses a - // a configured Chrome-Proxy and fallback and induces proxy fallback to a - // third proxy, if configured. - std::string bad_proxy = GetChromeProxy(); - std::string fallback_proxy = GetChromeFallbackProxy(); - ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( - "PROXY " + bad_proxy + "; PROXY " + fallback_proxy + - "; PROXY good:8080")); - - MockRead data_reads[] = { - MockRead("HTTP/1.1 200 OK\r\n" - "Connection: keep-alive\r\n" - "Chrome-Proxy: block=86400\r\n" - "Via: 1.1 Chrome Compression Proxy\r\n\r\n"), - MockRead("Bypass message"), - MockRead(SYNCHRONOUS, OK), - }; - - TestProxyFallbackWithMockReads(bad_proxy, fallback_proxy, data_reads, - arraysize(data_reads), 2u); - EXPECT_EQ(base::TimeDelta::FromSeconds(86400), - (*proxy_service_->proxy_retry_info().begin()).second.current_delay); -} -#endif // defined(DATA_REDUCTION_FALLBACK_HOST) -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) - TEST_F(HttpNetworkLayerTest, NetworkVerified) { MockRead data_reads[] = { MockRead("HTTP/1.0 200 OK\r\n\r\n"), @@ -763,7 +356,7 @@ TEST_F(HttpNetworkLayerTest, NetworkVerified) { request_info.load_flags = LOAD_NORMAL; scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(OK, rv); rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); @@ -796,7 +389,7 @@ TEST_F(HttpNetworkLayerTest, NetworkUnVerified) { request_info.load_flags = LOAD_NORMAL; scoped_ptr<HttpTransaction> trans; - int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans, NULL); + int rv = factory_->CreateTransaction(DEFAULT_PRIORITY, &trans); EXPECT_EQ(OK, rv); rv = trans->Start(&request_info, callback.callback(), BoundNetLog()); diff --git a/chromium/net/http/http_network_session.cc b/chromium/net/http/http_network_session.cc index 1be93fe5c3f..b4e373e5de7 100644 --- a/chromium/net/http/http_network_session.cc +++ b/chromium/net/http/http_network_session.cc @@ -24,6 +24,7 @@ #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_pool_manager_impl.h" #include "net/socket/next_proto.h" +#include "net/spdy/hpack_huffman_aggregator.h" #include "net/spdy/spdy_session_pool.h" namespace { @@ -66,13 +67,10 @@ HttpNetworkSession::Params::Params() network_delegate(NULL), net_log(NULL), host_mapping_rules(NULL), - force_http_pipelining(false), ignore_certificate_errors(false), - http_pipelining_enabled(false), testing_fixed_http_port(0), testing_fixed_https_port(0), force_spdy_single_domain(false), - enable_spdy_ip_pooling(true), enable_spdy_compression(true), enable_spdy_ping_based_connection_checking(true), spdy_default_protocol(kProtoUnknown), @@ -80,13 +78,22 @@ HttpNetworkSession::Params::Params() spdy_initial_max_concurrent_streams(0), spdy_max_concurrent_streams_limit(0), time_func(&base::TimeTicks::Now), + force_spdy_over_ssl(true), + force_spdy_always(false), + use_alternate_protocols(false), + enable_websocket_over_spdy(false), enable_quic(false), enable_quic_https(false), + enable_quic_port_selection(true), + enable_quic_pacing(false), + enable_quic_time_based_loss_detection(false), + enable_quic_persist_server_info(true), quic_clock(NULL), quic_random(NULL), quic_max_packet_length(kDefaultMaxPacketSize), enable_user_alternate_protocol_ports(false), quic_crypto_client_stream_factory(NULL) { + quic_supported_versions.push_back(QUIC_VERSION_18); } HttpNetworkSession::Params::~Params() {} @@ -98,7 +105,6 @@ HttpNetworkSession::HttpNetworkSession(const Params& params) http_server_properties_(params.http_server_properties), cert_verifier_(params.cert_verifier), http_auth_handler_factory_(params.http_auth_handler_factory), - force_http_pipelining_(params.force_http_pipelining), proxy_service_(params.proxy_service), ssl_config_service_(params.ssl_config_service), normal_socket_pool_manager_( @@ -110,17 +116,22 @@ HttpNetworkSession::HttpNetworkSession(const Params& params) params.client_socket_factory : net::ClientSocketFactory::GetDefaultFactory(), params.http_server_properties, + params.cert_verifier, params.quic_crypto_client_stream_factory, params.quic_random ? params.quic_random : QuicRandom::GetInstance(), params.quic_clock ? params. quic_clock : new QuicClock(), - params.quic_max_packet_length), + params.quic_max_packet_length, + params.quic_user_agent_id, + params.quic_supported_versions, + params.enable_quic_port_selection, + params.enable_quic_pacing, + params.enable_quic_time_based_loss_detection), spdy_session_pool_(params.host_resolver, params.ssl_config_service, params.http_server_properties, params.force_spdy_single_domain, - params.enable_spdy_ip_pooling, params.enable_spdy_compression, params.enable_spdy_ping_based_connection_checking, params.spdy_default_protocol, @@ -136,6 +147,40 @@ HttpNetworkSession::HttpNetworkSession(const Params& params) DCHECK(proxy_service_); DCHECK(ssl_config_service_.get()); CHECK(http_server_properties_); + + for (int i = ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION; + i <= ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION; ++i) { + enabled_protocols_[i - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] = false; + } + + // TODO(rtenneti): bug 116575 - consider combining the NextProto and + // AlternateProtocol. + for (std::vector<NextProto>::const_iterator it = params_.next_protos.begin(); + it != params_.next_protos.end(); ++it) { + NextProto proto = *it; + + // Add the protocol to the TLS next protocol list, except for QUIC + // since it uses UDP. + if (proto != kProtoQUIC1SPDY3) { + next_protos_.push_back(SSLClientSocket::NextProtoToString(proto)); + } + + // Enable the corresponding alternate protocol, except for HTTP + // which has not corresponding alternative. + if (proto != kProtoHTTP11) { + AlternateProtocol alternate = AlternateProtocolFromNextProto(proto); + if (!IsAlternateProtocolValid(alternate)) { + NOTREACHED() << "Invalid next proto: " << proto; + continue; + } + enabled_protocols_[alternate - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] = + true; + } + } + + if (HpackHuffmanAggregator::UseAggregator()) { + huffman_aggregator_.reset(new HpackHuffmanAggregator()); + } } HttpNetworkSession::~HttpNetworkSession() { @@ -198,6 +243,14 @@ base::Value* HttpNetworkSession::QuicInfoToValue() const { dict->Set("sessions", quic_stream_factory_.QuicStreamFactoryInfoToValue()); dict->SetBoolean("quic_enabled", params_.enable_quic); dict->SetBoolean("quic_enabled_https", params_.enable_quic_https); + dict->SetBoolean("enable_quic_port_selection", + params_.enable_quic_port_selection); + dict->SetBoolean("enable_quic_pacing", + params_.enable_quic_pacing); + dict->SetBoolean("enable_quic_time_based_loss_detection", + params_.enable_quic_time_based_loss_detection); + dict->SetBoolean("enable_quic_persist_server_info", + params_.enable_quic_persist_server_info); dict->SetString("origin_to_force_quic_on", params_.origin_to_force_quic_on.ToString()); return dict; @@ -216,6 +269,27 @@ void HttpNetworkSession::CloseIdleConnections() { spdy_session_pool_.CloseCurrentIdleSessions(); } +bool HttpNetworkSession::IsProtocolEnabled(AlternateProtocol protocol) const { + DCHECK(IsAlternateProtocolValid(protocol)); + return enabled_protocols_[ + protocol - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION]; +} + +void HttpNetworkSession::GetNextProtos( + std::vector<std::string>* next_protos) const { + if (HttpStreamFactory::spdy_enabled()) { + *next_protos = next_protos_; + } else { + next_protos->clear(); + } +} + +bool HttpNetworkSession::HasSpdyExclusion( + HostPortPair host_port_pair) const { + return params_.forced_spdy_exclusions.find(host_port_pair) != + params_.forced_spdy_exclusions.end(); +} + ClientSocketPoolManager* HttpNetworkSession::GetSocketPoolManager( SocketPoolType pool_type) { switch (pool_type) { diff --git a/chromium/net/http/http_network_session.h b/chromium/net/http/http_network_session.h index 471041220ec..b877b08081a 100644 --- a/chromium/net/http/http_network_session.h +++ b/chromium/net/http/http_network_session.h @@ -7,6 +7,7 @@ #include <set> #include <string> +#include <vector> #include "base/basictypes.h" #include "base/memory/ref_counted.h" @@ -18,6 +19,7 @@ #include "net/http/http_auth_cache.h" #include "net/http/http_stream_factory.h" #include "net/quic/quic_stream_factory.h" +#include "net/socket/next_proto.h" #include "net/spdy/spdy_session_pool.h" #include "net/ssl/ssl_client_auth_cache.h" @@ -30,7 +32,9 @@ namespace net { class CertVerifier; class ClientSocketFactory; class ClientSocketPoolManager; +class CTVerifier; class HostResolver; +class HpackHuffmanAggregator; class HttpAuthHandlerFactory; class HttpNetworkSessionPeer; class HttpProxyClientSocketPool; @@ -42,6 +46,7 @@ class ServerBoundCertService; class ProxyService; class QuicClock; class QuicCryptoClientStreamFactory; +class QuicServerInfoFactory; class SOCKSClientSocketPool; class SSLClientSocketPool; class SSLConfigService; @@ -71,29 +76,50 @@ class NET_EXPORT HttpNetworkSession base::WeakPtr<HttpServerProperties> http_server_properties; NetLog* net_log; HostMappingRules* host_mapping_rules; - bool force_http_pipelining; bool ignore_certificate_errors; - bool http_pipelining_enabled; uint16 testing_fixed_http_port; uint16 testing_fixed_https_port; + bool force_spdy_single_domain; - bool enable_spdy_ip_pooling; bool enable_spdy_compression; bool enable_spdy_ping_based_connection_checking; NextProto spdy_default_protocol; + // The protocols supported by NPN (next protocol negotiation) during the + // SSL handshake as well as by HTTP Alternate-Protocol. + // TODO(mmenke): This is currently empty by default, and alternate + // protocols are disabled. We should use some reasonable + // defaults. + NextProtoVector next_protos; size_t spdy_stream_initial_recv_window_size; size_t spdy_initial_max_concurrent_streams; size_t spdy_max_concurrent_streams_limit; SpdySessionPool::TimeFunc time_func; std::string trusted_spdy_proxy; + // Controls whether or not ssl is used when in SPDY mode. + bool force_spdy_over_ssl; + // Controls whether or not SPDY is used without NPN. + bool force_spdy_always; + // URLs to exclude from forced SPDY. + std::set<HostPortPair> forced_spdy_exclusions; + // Noe: Using this in the case of NPN for HTTP only results in the browser + // trying SSL and then falling back to http. + bool use_alternate_protocols; + bool enable_websocket_over_spdy; + bool enable_quic; bool enable_quic_https; + bool enable_quic_port_selection; + bool enable_quic_pacing; + bool enable_quic_time_based_loss_detection; + bool enable_quic_persist_server_info; HostPortPair origin_to_force_quic_on; QuicClock* quic_clock; // Will be owned by QuicStreamFactory. QuicRandom* quic_random; size_t quic_max_packet_length; + std::string quic_user_agent_id; bool enable_user_alternate_protocol_ports; QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory; + QuicVersionVector quic_supported_versions; }; enum SocketPoolType { @@ -148,6 +174,9 @@ class NET_EXPORT HttpNetworkSession NetLog* net_log() { return net_log_; } + HpackHuffmanAggregator* huffman_aggregator() { + return huffman_aggregator_.get(); + } // Creates a Value summary of the state of the socket pools. The caller is // responsible for deleting the returned value. @@ -164,14 +193,16 @@ class NET_EXPORT HttpNetworkSession void CloseAllConnections(); void CloseIdleConnections(); - bool force_http_pipelining() const { return force_http_pipelining_; } - // Returns the original Params used to construct this session. const Params& params() const { return params_; } - void set_http_pipelining_enabled(bool enable) { - params_.http_pipelining_enabled = enable; - } + bool IsProtocolEnabled(AlternateProtocol protocol) const; + + void GetNextProtos(std::vector<std::string>* next_protos) const; + + // Convenience function for searching through |params_| for + // |forced_spdy_exclusions|. + bool HasSpdyExclusion(HostPortPair host_port_pair) const; private: friend class base::RefCounted<HttpNetworkSession>; @@ -186,7 +217,6 @@ class NET_EXPORT HttpNetworkSession const base::WeakPtr<HttpServerProperties> http_server_properties_; CertVerifier* const cert_verifier_; HttpAuthHandlerFactory* const http_auth_handler_factory_; - bool force_http_pipelining_; // Not const since it's modified by HttpNetworkSessionPeer for testing. ProxyService* proxy_service_; @@ -202,6 +232,12 @@ class NET_EXPORT HttpNetworkSession scoped_ptr<HttpStreamFactory> http_stream_factory_for_websocket_; std::set<HttpResponseBodyDrainer*> response_drainers_; + // TODO(jgraettinger): Remove when Huffman collection is complete. + scoped_ptr<HpackHuffmanAggregator> huffman_aggregator_; + + std::vector<std::string> next_protos_; + bool enabled_protocols_[NUM_VALID_ALTERNATE_PROTOCOLS]; + Params params_; }; diff --git a/chromium/net/http/http_network_transaction.cc b/chromium/net/http/http_network_transaction.cc index d7b3f41ff76..32af2dd1844 100644 --- a/chromium/net/http/http_network_transaction.cc +++ b/chromium/net/http/http_network_transaction.cc @@ -54,6 +54,7 @@ #include "net/socket/ssl_client_socket.h" #include "net/socket/ssl_client_socket_pool.h" #include "net/socket/transport_client_socket_pool.h" +#include "net/spdy/hpack_huffman_aggregator.h" #include "net/spdy/spdy_http_stream.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" @@ -75,8 +76,7 @@ namespace net { namespace { void ProcessAlternateProtocol( - HttpStreamFactory* factory, - const base::WeakPtr<HttpServerProperties>& http_server_properties, + HttpNetworkSession* session, const HttpResponseHeaders& headers, const HostPortPair& http_host_port_pair) { std::string alternate_protocol_str; @@ -87,9 +87,11 @@ void ProcessAlternateProtocol( return; } - factory->ProcessAlternateProtocol(http_server_properties, - alternate_protocol_str, - http_host_port_pair); + session->http_stream_factory()->ProcessAlternateProtocol( + session->http_server_properties(), + alternate_protocol_str, + http_host_port_pair, + *session); } // Returns true if |error| is a client certificate authentication error. @@ -119,30 +121,6 @@ base::Value* NetLogSSLVersionFallbackCallback( return dict; } -#if defined(SPDY_PROXY_AUTH_ORIGIN) -// Returns true if |response_headers| contains the data reduction proxy Via -// header value. -bool IsChromeProxyResponse(const net::HttpResponseHeaders* response_headers) { - if (!response_headers) { - return false; - } - const char kDataReductionProxyViaValue[] = "1.1 Chrome Compression Proxy"; - size_t value_len = strlen(kDataReductionProxyViaValue); - void* iter = NULL; - std::string temp; - while (response_headers->EnumerateHeader(&iter, "Via", &temp)) { - std::string::const_iterator it = - std::search(temp.begin(), temp.end(), - kDataReductionProxyViaValue, - kDataReductionProxyViaValue + value_len, - base::CaseInsensitiveCompareASCII<char>()); - if (it != temp.end()) - return true; - } - return false; -} -#endif - } // namespace //----------------------------------------------------------------------------- @@ -160,14 +138,12 @@ HttpNetworkTransaction::HttpNetworkTransaction(RequestPriority priority, fallback_error_code_(ERR_SSL_INAPPROPRIATE_FALLBACK), request_headers_(), read_buf_len_(0), + total_received_bytes_(0), next_state_(STATE_NONE), establishing_tunnel_(false), websocket_handshake_stream_base_create_helper_(NULL) { session->ssl_config_service()->GetSSLConfig(&server_ssl_config_); - if (session->http_stream_factory()->has_next_protos()) { - server_ssl_config_.next_protos = - session->http_stream_factory()->next_protos(); - } + session->GetNextProtos(&server_ssl_config_.next_protos); proxy_ssl_config_ = server_ssl_config_; } @@ -216,13 +192,11 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info, proxy_ssl_config_.rev_checking_enabled = false; } - // Channel ID is enabled unless --disable-tls-channel-id flag is set, - // or if privacy mode is enabled. - bool channel_id_enabled = server_ssl_config_.channel_id_enabled && - (request_->privacy_mode == kPrivacyModeDisabled); - server_ssl_config_.channel_id_enabled = channel_id_enabled; + // Channel ID is disabled if privacy mode is enabled for this request. + if (request_->privacy_mode == PRIVACY_MODE_ENABLED) + server_ssl_config_.channel_id_enabled = false; - next_state_ = STATE_CREATE_STREAM; + next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM; int rv = DoLoop(OK); if (rv == ERR_IO_PENDING) callback_ = callback; @@ -331,6 +305,7 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { DCHECK(!stream_request_.get()); if (stream_.get()) { + total_received_bytes_ += stream_->GetTotalReceivedBytes(); HttpStream* new_stream = NULL; if (keep_alive && stream_->IsConnectionReusable()) { // We should call connection_->set_idle_time(), but this doesn't occur @@ -347,6 +322,8 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { stream_->Close(true); next_state_ = STATE_CREATE_STREAM; } else { + // Renewed streams shouldn't carry over received bytes. + DCHECK_EQ(0, new_stream->GetTotalReceivedBytes()); next_state_ = STATE_INIT_STREAM; } stream_.reset(new_stream); @@ -400,6 +377,8 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len, return rv; } +void HttpNetworkTransaction::StopCaching() {} + bool HttpNetworkTransaction::GetFullRequestHeaders( HttpRequestHeaders* headers) const { // TODO(ttuttle): Make sure we've populated request_headers_. @@ -407,6 +386,15 @@ bool HttpNetworkTransaction::GetFullRequestHeaders( return true; } +int64 HttpNetworkTransaction::GetTotalReceivedBytes() const { + int64 total_received_bytes = total_received_bytes_; + if (stream_) + total_received_bytes += stream_->GetTotalReceivedBytes(); + return total_received_bytes; +} + +void HttpNetworkTransaction::DoneReading() {} + const HttpResponseInfo* HttpNetworkTransaction::GetResponseInfo() const { return ((headers_valid_ && response_.headers.get()) || response_.ssl_info.cert.get() || response_.cert_request_info.get()) @@ -418,6 +406,8 @@ LoadState HttpNetworkTransaction::GetLoadState() const { // TODO(wtc): Define a new LoadState value for the // STATE_INIT_CONNECTION_COMPLETE state, which delays the HTTP request. switch (next_state_) { + case STATE_CREATE_STREAM: + return LOAD_STATE_WAITING_FOR_DELEGATE; case STATE_CREATE_STREAM_COMPLETE: return stream_request_->GetLoadState(); case STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE: @@ -441,6 +431,9 @@ UploadProgress HttpNetworkTransaction::GetUploadProgress() const { return static_cast<HttpStream*>(stream_.get())->GetUploadProgress(); } +void HttpNetworkTransaction::SetQuicServerInfo( + QuicServerInfo* quic_server_info) {} + bool HttpNetworkTransaction::GetLoadTimingInfo( LoadTimingInfo* load_timing_info) const { if (!stream_ || !stream_->GetLoadTimingInfo(load_timing_info)) @@ -467,12 +460,24 @@ void HttpNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper( websocket_handshake_stream_base_create_helper_ = create_helper; } +void HttpNetworkTransaction::SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) { + before_network_start_callback_ = callback; +} + +int HttpNetworkTransaction::ResumeNetworkStart() { + DCHECK_EQ(next_state_, STATE_CREATE_STREAM); + return DoLoop(OK); +} + void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config, const ProxyInfo& used_proxy_info, HttpStreamBase* stream) { DCHECK_EQ(STATE_CREATE_STREAM_COMPLETE, next_state_); DCHECK(stream_request_.get()); + if (stream_) + total_received_bytes_ += stream_->GetTotalReceivedBytes(); stream_.reset(stream); server_ssl_config_ = used_ssl_config; proxy_info_ = used_proxy_info; @@ -481,7 +486,8 @@ void HttpNetworkTransaction::OnStreamReady(const SSLConfig& used_ssl_config, stream_request_->protocol_negotiated()); response_.was_fetched_via_spdy = stream_request_->using_spdy(); response_.was_fetched_via_proxy = !proxy_info_.is_direct(); - + if (response_.was_fetched_via_proxy && !proxy_info_.is_empty()) + response_.proxy_server = proxy_info_.proxy_server().host_port_pair(); OnIOComplete(OK); } @@ -566,6 +572,8 @@ void HttpNetworkTransaction::OnHttpsProxyTunnelResponse( response_ = response_info; server_ssl_config_ = used_ssl_config; proxy_info_ = used_proxy_info; + if (stream_) + total_received_bytes_ += stream_->GetTotalReceivedBytes(); stream_.reset(stream); stream_request_.reset(); // we're done with the stream request OnIOComplete(ERR_HTTPS_PROXY_TUNNEL_RESPONSE); @@ -599,6 +607,10 @@ int HttpNetworkTransaction::DoLoop(int result) { State state = next_state_; next_state_ = STATE_NONE; switch (state) { + case STATE_NOTIFY_BEFORE_CREATE_STREAM: + DCHECK_EQ(OK, rv); + rv = DoNotifyBeforeCreateStream(); + break; case STATE_CREATE_STREAM: DCHECK_EQ(OK, rv); rv = DoCreateStream(); @@ -692,9 +704,18 @@ int HttpNetworkTransaction::DoLoop(int result) { return rv; } +int HttpNetworkTransaction::DoNotifyBeforeCreateStream() { + next_state_ = STATE_CREATE_STREAM; + bool defer = false; + if (!before_network_start_callback_.is_null()) + before_network_start_callback_.Run(&defer); + if (!defer) + return OK; + return ERR_IO_PENDING; +} + int HttpNetworkTransaction::DoCreateStream() { next_state_ = STATE_CREATE_STREAM_COMPLETE; - if (ForWebSocketHandshake()) { stream_request_.reset( session_->http_stream_factory_for_websocket() @@ -755,6 +776,8 @@ int HttpNetworkTransaction::DoInitStreamComplete(int result) { result = HandleIOError(result); // The stream initialization failed, so this stream will never be useful. + if (stream_) + total_received_bytes_ += stream_->GetTotalReceivedBytes(); stream_.reset(); } @@ -918,16 +941,6 @@ int HttpNetworkTransaction::DoReadHeaders() { return stream_->ReadResponseHeaders(io_callback_); } -int HttpNetworkTransaction::HandleConnectionClosedBeforeEndOfHeaders() { - if (!response_.headers.get() && !stream_->IsConnectionReused()) { - // The connection was closed before any data was sent. Likely an error - // rather than empty HTTP/0.9 response. - return ERR_EMPTY_RESPONSE; - } - - return OK; -} - int HttpNetworkTransaction::DoReadHeadersComplete(int result) { // We can get a certificate error or ERR_SSL_CLIENT_AUTH_CERT_NEEDED here // due to SSL renegotiation. @@ -954,108 +967,40 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { return OK; } - if (result < 0 && result != ERR_CONNECTION_CLOSED) - return HandleIOError(result); - - if (result == ERR_CONNECTION_CLOSED && ShouldResendRequest(result)) { - ResetConnectionAndRequestForResend(); - return OK; - } - // After we call RestartWithAuth a new response_time will be recorded, and // we need to be cautious about incorrectly logging the duration across the // authentication activity. if (result == OK) LogTransactionConnectedMetrics(); - if (result == ERR_CONNECTION_CLOSED) { - // For now, if we get at least some data, we do the best we can to make - // sense of it and send it back up the stack. - int rv = HandleConnectionClosedBeforeEndOfHeaders(); - if (rv != OK) - return rv; - } - DCHECK(response_.headers.get()); + // ERR_CONNECTION_CLOSED is treated differently at this point; if partial + // response headers were received, we do the best we can to make sense of it + // and send it back up the stack. + // + // TODO(davidben): Consider moving this to HttpBasicStream, It's a little + // bizarre for SPDY. Assuming this logic is useful at all. + // TODO(davidben): Bubble the error code up so we do not cache? + if (result == ERR_CONNECTION_CLOSED && response_.headers.get()) + result = OK; -#if defined(SPDY_PROXY_AUTH_ORIGIN) - // Server-induced fallback; see: http://crbug.com/143712 - if (response_.was_fetched_via_proxy && response_.headers.get() != NULL) { - ProxyService::DataReductionProxyBypassEventType proxy_bypass_event = - ProxyService::BYPASS_EVENT_TYPE_MAX; - net::HttpResponseHeaders::ChromeProxyInfo chrome_proxy_info; - bool chrome_proxy_used = - proxy_info_.proxy_server().isDataReductionProxy(); - bool chrome_fallback_proxy_used = false; -#if defined(DATA_REDUCTION_FALLBACK_HOST) - if (!chrome_proxy_used) { - chrome_fallback_proxy_used = - proxy_info_.proxy_server().isDataReductionProxyFallback(); - } -#endif + if (result < 0) + return HandleIOError(result); - if (chrome_proxy_used || chrome_fallback_proxy_used) { - // A Via header might not be present in a 304. Since the goal of a 304 - // response is to minimize information transfer, a sender in general - // should not generate representation metadata other than Cache-Control, - // Content-Location, Date, ETag, Expires, and Vary. - if (!IsChromeProxyResponse(response_.headers.get()) && - (response_.headers->response_code() != HTTP_NOT_MODIFIED)) { - proxy_bypass_event = ProxyService::MISSING_VIA_HEADER; - } else if (response_.headers->GetChromeProxyInfo(&chrome_proxy_info)) { - if (chrome_proxy_info.bypass_duration < TimeDelta::FromMinutes(30)) - proxy_bypass_event = ProxyService::SHORT_BYPASS; - else - proxy_bypass_event = ProxyService::LONG_BYPASS; - } else { - // Additionally, fallback if a 500, 502 or 503 is returned via the data - // reduction proxy. This is conservative, as the 500, 502 or 503 might - // have been generated by the origin, and not the proxy. - if (response_.headers->response_code() == HTTP_INTERNAL_SERVER_ERROR || - response_.headers->response_code() == HTTP_BAD_GATEWAY || - response_.headers->response_code() == HTTP_SERVICE_UNAVAILABLE) { - proxy_bypass_event = ProxyService::INTERNAL_SERVER_ERROR_BYPASS; - } - } + DCHECK(response_.headers.get()); - if (proxy_bypass_event < ProxyService::BYPASS_EVENT_TYPE_MAX) { - ProxyService* proxy_service = session_->proxy_service(); - - proxy_service->RecordDataReductionProxyBypassInfo( - chrome_proxy_used, proxy_info_.proxy_server(), proxy_bypass_event); - - ProxyServer proxy_server; -#if defined(DATA_REDUCTION_FALLBACK_HOST) - if (chrome_proxy_used && chrome_proxy_info.bypass_all) { - // TODO(bengr): Rename as DATA_REDUCTION_FALLBACK_ORIGIN. - GURL proxy_url(DATA_REDUCTION_FALLBACK_HOST); - if (proxy_url.SchemeIsHTTPOrHTTPS()) { - proxy_server = ProxyServer(proxy_url.SchemeIs("http") ? - ProxyServer::SCHEME_HTTP : - ProxyServer::SCHEME_HTTPS, - HostPortPair::FromURL(proxy_url)); - } - } -#endif - if (proxy_service->MarkProxiesAsBad(proxy_info_, - chrome_proxy_info.bypass_duration, - proxy_server, - net_log_)) { - // Only retry idempotent methods. We don't want to resubmit a POST - // if the proxy took some action. - if (request_->method == "GET" || - request_->method == "OPTIONS" || - request_->method == "HEAD" || - request_->method == "PUT" || - request_->method == "DELETE" || - request_->method == "TRACE") { - ResetConnectionAndRequestForResend(); - return OK; - } - } - } - } + // On a 408 response from the server ("Request Timeout") on a stale socket, + // retry the request. + // Headers can be NULL because of http://crbug.com/384554. + if (response_.headers.get() && response_.headers->response_code() == 408 && + stream_->IsConnectionReused()) { + net_log_.AddEventWithNetErrorCode( + NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR, + response_.headers->response_code()); + // This will close the socket - it would be weird to try and reuse it, even + // if the server doesn't actually close it. + ResetConnectionAndRequestForResend(); + return OK; } -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) // Like Net.HttpResponseCode, but only for MAIN_FRAME loads. if (request_->load_flags & LOAD_MAIN_FRAME) { @@ -1091,8 +1036,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { HostPortPair endpoint = HostPortPair(request_->url.HostNoBrackets(), request_->url.EffectiveIntPort()); - ProcessAlternateProtocol(session_->http_stream_factory(), - session_->http_server_properties(), + ProcessAlternateProtocol(session_, *response_.headers.get(), endpoint); @@ -1104,6 +1048,14 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { stream_->GetSSLInfo(&response_.ssl_info); headers_valid_ = true; + + if (session_->huffman_aggregator()) { + session_->huffman_aggregator()->AggregateTransactionCharacterCounts( + *request_, + request_headers_, + proxy_info_.proxy_server(), + *response_.headers); + } return OK; } @@ -1280,6 +1232,7 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) { // Since we already have a stream, we're being called as part of SSL // renegotiation. DCHECK(!stream_request_.get()); + total_received_bytes_ += stream_->GetTotalReceivedBytes(); stream_->Close(true); stream_.reset(); } @@ -1328,7 +1281,7 @@ void HttpNetworkTransaction::HandleClientAuthError(int error) { if (server_ssl_config_.send_client_cert && (error == ERR_SSL_PROTOCOL_ERROR || IsClientCertificateError(error))) { session_->ssl_client_auth_cache()->Remove( - GetHostAndPort(request_->url)); + HostPortPair::FromURL(request_->url)); } } @@ -1428,15 +1381,11 @@ int HttpNetworkTransaction::HandleIOError(int error) { // likely happen when trying to retrieve its IP address. // See http://crbug.com/105824 for more details. case ERR_SOCKET_NOT_CONNECTED: - if (ShouldResendRequest(error)) { - net_log_.AddEventWithNetErrorCode( - NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); - ResetConnectionAndRequestForResend(); - error = OK; - } - break; - case ERR_PIPELINE_EVICTION: - if (!session_->force_http_pipelining()) { + // If a socket is closed on its initial request, HttpStreamParser returns + // ERR_EMPTY_RESPONSE. This may still be close/reuse race if the socket was + // preconnected but failed to be used before the server timed it out. + case ERR_EMPTY_RESPONSE: + if (ShouldResendRequest()) { net_log_.AddEventWithNetErrorCode( NetLog::TYPE_HTTP_TRANSACTION_RESTART_AFTER_ERROR, error); ResetConnectionAndRequestForResend(); @@ -1457,6 +1406,8 @@ int HttpNetworkTransaction::HandleIOError(int error) { void HttpNetworkTransaction::ResetStateForRestart() { ResetStateForAuthRestart(); + if (stream_) + total_received_bytes_ += stream_->GetTotalReceivedBytes(); stream_.reset(); } @@ -1477,7 +1428,7 @@ HttpResponseHeaders* HttpNetworkTransaction::GetResponseHeaders() const { return response_.headers.get(); } -bool HttpNetworkTransaction::ShouldResendRequest(int error) const { +bool HttpNetworkTransaction::ShouldResendRequest() const { bool connection_is_proven = stream_->IsConnectionReused(); bool has_received_headers = GetResponseHeaders() != NULL; @@ -1580,6 +1531,7 @@ bool HttpNetworkTransaction::ForWebSocketHandshake() const { std::string HttpNetworkTransaction::DescribeState(State state) { std::string description; switch (state) { + STATE_CASE(STATE_NOTIFY_BEFORE_CREATE_STREAM); STATE_CASE(STATE_CREATE_STREAM); STATE_CASE(STATE_CREATE_STREAM_COMPLETE); STATE_CASE(STATE_INIT_REQUEST_BODY); diff --git a/chromium/net/http/http_network_transaction.h b/chromium/net/http/http_network_transaction.h index 3ae1725d1de..24ab95b8ac2 100644 --- a/chromium/net/http/http_network_transaction.h +++ b/chromium/net/http/http_network_transaction.h @@ -59,18 +59,23 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction virtual int Read(IOBuffer* buf, int buf_len, const CompletionCallback& callback) OVERRIDE; - virtual void StopCaching() OVERRIDE {} + virtual void StopCaching() OVERRIDE; virtual bool GetFullRequestHeaders( HttpRequestHeaders* headers) const OVERRIDE; - virtual void DoneReading() OVERRIDE {} + virtual int64 GetTotalReceivedBytes() const OVERRIDE; + virtual void DoneReading() OVERRIDE; virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; virtual LoadState GetLoadState() const OVERRIDE; virtual UploadProgress GetUploadProgress() const OVERRIDE; + virtual void SetQuicServerInfo(QuicServerInfo* quic_server_info) OVERRIDE; virtual bool GetLoadTimingInfo( LoadTimingInfo* load_timing_info) const OVERRIDE; virtual void SetPriority(RequestPriority priority) OVERRIDE; virtual void SetWebSocketHandshakeStreamCreateHelper( WebSocketHandshakeStreamBase::CreateHelper* create_helper) OVERRIDE; + virtual void SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) OVERRIDE; + virtual int ResumeNetworkStart() OVERRIDE; // HttpStreamRequest::Delegate methods: virtual void OnStreamReady(const SSLConfig& used_ssl_config, @@ -116,6 +121,7 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction FlowControlNegativeSendWindowSize); enum State { + STATE_NOTIFY_BEFORE_CREATE_STREAM, STATE_CREATE_STREAM, STATE_CREATE_STREAM_COMPLETE, STATE_INIT_STREAM, @@ -151,6 +157,7 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction // argument receive the result from the previous state. If a method returns // ERR_IO_PENDING, then the result from OnIOComplete will be passed to the // next state method as the result arg. + int DoNotifyBeforeCreateStream(); int DoCreateStream(); int DoCreateStreamComplete(int result); int DoInitStream(); @@ -203,19 +210,14 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction // Gets the response headers from the HttpStream. HttpResponseHeaders* GetResponseHeaders() const; - // Called when we reached EOF or got an error. Returns true if we should - // resend the request. |error| is OK when we reached EOF. - bool ShouldResendRequest(int error) const; + // Called when the socket is unexpectedly closed. Returns true if the request + // should be resent in case of a socket reuse/close race. + bool ShouldResendRequest() const; // Resets the connection and the request headers for resend. Called when // ShouldResendRequest() is true. void ResetConnectionAndRequestForResend(); - // Decides the policy when the connection is closed before the end of headers - // has been read. This only applies to reading responses, and not writing - // requests. - int HandleConnectionClosedBeforeEndOfHeaders(); - // Sets up the state machine to restart the transaction with auth. void PrepareForAuthRestart(HttpAuth::Target target); @@ -254,6 +256,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction // Debug helper. static std::string DescribeState(State state); + void SetStream(HttpStreamBase* stream); + scoped_refptr<HttpAuthController> auth_controllers_[HttpAuth::AUTH_NUM_TARGETS]; @@ -306,6 +310,9 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction scoped_refptr<IOBuffer> read_buf_; int read_buf_len_; + // Total number of bytes received on streams for this transaction. + int64 total_received_bytes_; + // The time the Start method was called. base::Time start_time_; @@ -326,6 +333,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction WebSocketHandshakeStreamBase::CreateHelper* websocket_handshake_stream_base_create_helper_; + BeforeNetworkStartCallback before_network_start_callback_; + DISALLOW_COPY_AND_ASSIGN(HttpNetworkTransaction); }; diff --git a/chromium/net/http/http_network_transaction_unittest.cc b/chromium/net/http/http_network_transaction_unittest.cc index dd9eefecf37..01756a19654 100644 --- a/chromium/net/http/http_network_transaction_unittest.cc +++ b/chromium/net/http/http_network_transaction_unittest.cc @@ -16,6 +16,7 @@ #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" +#include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/test_file_util.h" @@ -35,6 +36,7 @@ #include "net/cert/mock_cert_verifier.h" #include "net/dns/host_cache.h" #include "net/dns/mock_host_resolver.h" +#include "net/http/http_auth_challenge_tokenizer.h" #include "net/http/http_auth_handler_digest.h" #include "net/http/http_auth_handler_mock.h" #include "net/http/http_auth_handler_ntlm.h" @@ -44,7 +46,7 @@ #include "net/http/http_server_properties_impl.h" #include "net/http/http_stream.h" #include "net/http/http_stream_factory.h" -#include "net/http/http_transaction_unittest.h" +#include "net/http/http_transaction_test_util.h" #include "net/proxy/proxy_config_service_fixed.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_resolver.h" @@ -69,6 +71,8 @@ #include "testing/platform_test.h" #include "url/gurl.h" +using base::ASCIIToUTF16; + //----------------------------------------------------------------------------- namespace { @@ -244,6 +248,7 @@ class HttpNetworkTransactionTest int rv; std::string status_line; std::string response_data; + int64 totalReceivedBytes; LoadTimingInfo load_timing_info; }; @@ -260,8 +265,6 @@ class HttpNetworkTransactionTest PlatformTest::TearDown(); NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); base::MessageLoop::current()->RunUntilIdle(); - HttpStreamFactory::set_use_alternate_protocols(false); - HttpStreamFactory::SetNextProtos(std::vector<NextProto>()); } // This is the expected return from a current server advertising SPDY. @@ -279,6 +282,14 @@ class HttpNetworkTransactionTest void KeepAliveConnectionResendRequestTest(const MockWrite* write_failure, const MockRead* read_failure); + // Either |write_failure| specifies a write failure or |read_failure| + // specifies a read failure when using a reused socket. In either case, the + // failure should cause the network transaction to resend the request, and the + // other argument should be NULL. + void PreconnectErrorResendRequestTest(const MockWrite* write_failure, + const MockRead* read_failure, + bool use_spdy); + SimpleGetHelperResult SimpleGetHelperForData(StaticSocketDataProvider* data[], size_t data_count) { SimpleGetHelperResult out; @@ -300,7 +311,7 @@ class HttpNetworkTransactionTest TestCompletionCallback callback; - EXPECT_TRUE(log.bound().IsLoggingAllEvents()); + EXPECT_TRUE(log.bound().IsLogging()); int rv = trans->Start(&request, callback.callback(), log.bound()); EXPECT_EQ(ERR_IO_PENDING, rv); @@ -356,6 +367,7 @@ class HttpNetworkTransactionTest EXPECT_EQ("['Host: www.google.com','Connection: keep-alive']", response_headers); + out.totalReceivedBytes = trans->GetTotalReceivedBytes(); return out; } @@ -366,6 +378,13 @@ class HttpNetworkTransactionTest return SimpleGetHelperForData(data, 1); } + int64 ReadsSize(MockRead data_reads[], size_t reads_count) { + int64 size = 0; + for (size_t i = 0; i < reads_count; ++i) + size += data_reads[i].data_len; + return size; + } + void ConnectStatusHelperWithExpectedStatus(const MockRead& status, int expected_status); @@ -388,11 +407,32 @@ INSTANTIATE_TEST_CASE_P( NextProto, HttpNetworkTransactionTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, - kProtoHTTP2Draft04)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); namespace { +class BeforeNetworkStartHandler { + public: + explicit BeforeNetworkStartHandler(bool defer) + : defer_on_before_network_start_(defer), + observed_before_network_start_(false) {} + + void OnBeforeNetworkStart(bool* defer) { + *defer = defer_on_before_network_start_; + observed_before_network_start_ = true; + } + + bool observed_before_network_start() const { + return observed_before_network_start_; + } + + private: + const bool defer_on_before_network_start_; + bool observed_before_network_start_; + + DISALLOW_COPY_AND_ASSIGN(BeforeNetworkStartHandler); +}; + // Fill |str| with a long header list that consumes >= |size| bytes. void FillLargeHeadersString(std::string* str, int size) { const char* row = @@ -584,6 +624,8 @@ TEST_P(HttpNetworkTransactionTest, SimpleGET) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.0 200 OK", out.status_line); EXPECT_EQ("hello world", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, out.totalReceivedBytes); } // Response with no status line. @@ -597,6 +639,8 @@ TEST_P(HttpNetworkTransactionTest, SimpleGETNoHeaders) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/0.9 200 OK", out.status_line); EXPECT_EQ("hello world", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, out.totalReceivedBytes); } // Allow up to 4 bytes of junk to precede status line. @@ -610,6 +654,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk3Bytes) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line); EXPECT_EQ("DATA", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, out.totalReceivedBytes); } // Allow up to 4 bytes of junk to precede status line. @@ -623,6 +669,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk4Bytes) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line); EXPECT_EQ("DATA", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, out.totalReceivedBytes); } // Beyond 4 bytes of slop and it should fail to find a status line. @@ -636,6 +684,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk5Bytes) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/0.9 200 OK", out.status_line); EXPECT_EQ("xxxxxHTTP/1.1 404 Not Found\nServer: blah", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, out.totalReceivedBytes); } // Same as StatusLineJunk4Bytes, except the read chunks are smaller. @@ -653,6 +703,8 @@ TEST_P(HttpNetworkTransactionTest, StatusLineJunk4Bytes_Slow) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.0 404 Not Found", out.status_line); EXPECT_EQ("DATA", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, out.totalReceivedBytes); } // Close the connection before enough bytes to have a status line. @@ -666,15 +718,18 @@ TEST_P(HttpNetworkTransactionTest, StatusLinePartial) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/0.9 200 OK", out.status_line); EXPECT_EQ("HTT", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, out.totalReceivedBytes); } // Simulate a 204 response, lacking a Content-Length header, sent over a // persistent connection. The response should still terminate since a 204 // cannot have a response body. TEST_P(HttpNetworkTransactionTest, StopsReading204) { + char junk[] = "junk"; MockRead data_reads[] = { MockRead("HTTP/1.1 204 No Content\r\n\r\n"), - MockRead("junk"), // Should not be read!! + MockRead(junk), // Should not be read!! MockRead(SYNCHRONOUS, OK), }; SimpleGetHelperResult out = SimpleGetHelper(data_reads, @@ -682,18 +737,23 @@ TEST_P(HttpNetworkTransactionTest, StopsReading204) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 204 No Content", out.status_line); EXPECT_EQ("", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + int64 response_size = reads_size - strlen(junk); + EXPECT_EQ(response_size, out.totalReceivedBytes); } // A simple request using chunked encoding with some extra data after. -// (Like might be seen in a pipelined response.) TEST_P(HttpNetworkTransactionTest, ChunkedEncoding) { + std::string final_chunk = "0\r\n\r\n"; + std::string extra_data = "HTTP/1.1 200 OK\r\n"; + std::string last_read = final_chunk + extra_data; MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"), MockRead("5\r\nHello\r\n"), MockRead("1\r\n"), MockRead(" \r\n"), MockRead("5\r\nworld\r\n"), - MockRead("0\r\n\r\nHTTP/1.1 200 OK\r\n"), + MockRead(last_read.data()), MockRead(SYNCHRONOUS, OK), }; SimpleGetHelperResult out = SimpleGetHelper(data_reads, @@ -701,6 +761,9 @@ TEST_P(HttpNetworkTransactionTest, ChunkedEncoding) { EXPECT_EQ(OK, out.rv); EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); EXPECT_EQ("Hello world", out.response_data); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + int64 response_size = reads_size - extra_data.size(); + EXPECT_EQ(response_size, out.totalReceivedBytes); } // Next tests deal with http://crbug.com/56344. @@ -883,6 +946,7 @@ TEST_P(HttpNetworkTransactionTest, TwoIdenticalLocationHeaders) { std::string url; EXPECT_TRUE(response->headers->IsRedirect(&url)); EXPECT_EQ("http://good.com/", url); + EXPECT_TRUE(response->proxy_server.IsEmpty()); } // Checks that two distinct Location headers result in an error. @@ -945,6 +1009,7 @@ TEST_P(HttpNetworkTransactionTest, Head) { EXPECT_TRUE(response->headers.get() != NULL); EXPECT_EQ(1234, response->headers->GetContentLength()); EXPECT_EQ("HTTP/1.1 404 Not Found", response->headers->GetStatusLine()); + EXPECT_TRUE(response->proxy_server.IsEmpty()); std::string server_header; void* iter = NULL; @@ -1000,6 +1065,7 @@ TEST_P(HttpNetworkTransactionTest, ReuseConnection) { EXPECT_TRUE(response->headers.get() != NULL); EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + EXPECT_TRUE(response->proxy_server.IsEmpty()); std::string response_data; rv = ReadTransaction(trans.get(), &response_data); @@ -1181,7 +1247,7 @@ void HttpNetworkTransactionTest::KeepAliveConnectionResendRequestTest( }; if (write_failure) { - ASSERT_TRUE(!read_failure); + ASSERT_FALSE(read_failure); data1_writes[1] = *write_failure; } else { ASSERT_TRUE(read_failure); @@ -1240,6 +1306,126 @@ void HttpNetworkTransactionTest::KeepAliveConnectionResendRequestTest( } } +void HttpNetworkTransactionTest::PreconnectErrorResendRequestTest( + const MockWrite* write_failure, + const MockRead* read_failure, + bool use_spdy) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.foo.com/"); + request.load_flags = 0; + + CapturingNetLog net_log; + session_deps_.net_log = &net_log; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + + SSLSocketDataProvider ssl1(ASYNC, OK); + SSLSocketDataProvider ssl2(ASYNC, OK); + if (use_spdy) { + ssl1.SetNextProto(GetParam()); + ssl2.SetNextProto(GetParam()); + } + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl1); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl2); + + // SPDY versions of the request and response. + scoped_ptr<SpdyFrame> spdy_request(spdy_util_.ConstructSpdyGet( + request.url.spec().c_str(), false, 1, DEFAULT_PRIORITY)); + scoped_ptr<SpdyFrame> spdy_response( + spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<SpdyFrame> spdy_data( + spdy_util_.ConstructSpdyBodyFrame(1, "hello", 5, true)); + + // HTTP/1.1 versions of the request and response. + const char kHttpRequest[] = "GET / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n\r\n"; + const char kHttpResponse[] = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"; + const char kHttpData[] = "hello"; + + std::vector<MockRead> data1_reads; + std::vector<MockWrite> data1_writes; + if (write_failure) { + ASSERT_FALSE(read_failure); + data1_writes.push_back(*write_failure); + data1_reads.push_back(MockRead(ASYNC, OK)); + } else { + ASSERT_TRUE(read_failure); + if (use_spdy) { + data1_writes.push_back(CreateMockWrite(*spdy_request)); + } else { + data1_writes.push_back(MockWrite(kHttpRequest)); + } + data1_reads.push_back(*read_failure); + } + + StaticSocketDataProvider data1(&data1_reads[0], data1_reads.size(), + &data1_writes[0], data1_writes.size()); + session_deps_.socket_factory->AddSocketDataProvider(&data1); + + std::vector<MockRead> data2_reads; + std::vector<MockWrite> data2_writes; + + if (use_spdy) { + data2_writes.push_back(CreateMockWrite(*spdy_request, 0, ASYNC)); + + data2_reads.push_back(CreateMockRead(*spdy_response, 1, ASYNC)); + data2_reads.push_back(CreateMockRead(*spdy_data, 2, ASYNC)); + data2_reads.push_back(MockRead(ASYNC, OK, 3)); + } else { + data2_writes.push_back( + MockWrite(ASYNC, kHttpRequest, strlen(kHttpRequest), 0)); + + data2_reads.push_back( + MockRead(ASYNC, kHttpResponse, strlen(kHttpResponse), 1)); + data2_reads.push_back(MockRead(ASYNC, kHttpData, strlen(kHttpData), 2)); + data2_reads.push_back(MockRead(ASYNC, OK, 3)); + } + OrderedSocketData data2(&data2_reads[0], data2_reads.size(), + &data2_writes[0], data2_writes.size()); + session_deps_.socket_factory->AddSocketDataProvider(&data2); + + // Preconnect a socket. + net::SSLConfig ssl_config; + session->ssl_config_service()->GetSSLConfig(&ssl_config); + session->GetNextProtos(&ssl_config.next_protos); + session->http_stream_factory()->PreconnectStreams( + 1, request, DEFAULT_PRIORITY, ssl_config, ssl_config); + // Wait for the preconnect to complete. + // TODO(davidben): Some way to wait for an idle socket count might be handy. + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get())); + + // Make the request. + TestCompletionCallback callback; + + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + LoadTimingInfo load_timing_info; + EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); + TestLoadTimingNotReused( + load_timing_info, + CONNECT_TIMING_HAS_DNS_TIMES|CONNECT_TIMING_HAS_SSL_TIMES); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ(kHttpData, response_data); +} + TEST_P(HttpNetworkTransactionTest, KeepAliveConnectionNotConnectedOnWrite) { MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED); @@ -1256,6 +1442,71 @@ TEST_P(HttpNetworkTransactionTest, KeepAliveConnectionEOF) { KeepAliveConnectionResendRequestTest(NULL, &read_failure); } +// Make sure that on a 408 response (Request Timeout), the request is retried, +// if the socket was a reused keep alive socket. +TEST_P(HttpNetworkTransactionTest, KeepAlive408) { + MockRead read_failure(SYNCHRONOUS, + "HTTP/1.1 408 Request Timeout\r\n" + "Connection: Keep-Alive\r\n" + "Content-Length: 6\r\n\r\n" + "Pickle"); + KeepAliveConnectionResendRequestTest(NULL, &read_failure); +} + +TEST_P(HttpNetworkTransactionTest, + PreconnectErrorNotConnectedOnWrite) { + MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED); + PreconnectErrorResendRequestTest(&write_failure, NULL, false); +} + +TEST_P(HttpNetworkTransactionTest, PreconnectErrorReset) { + MockRead read_failure(ASYNC, ERR_CONNECTION_RESET); + PreconnectErrorResendRequestTest(NULL, &read_failure, false); +} + +TEST_P(HttpNetworkTransactionTest, PreconnectErrorEOF) { + MockRead read_failure(SYNCHRONOUS, OK); // EOF + PreconnectErrorResendRequestTest(NULL, &read_failure, false); +} + +TEST_P(HttpNetworkTransactionTest, PreconnectErrorAsyncEOF) { + MockRead read_failure(ASYNC, OK); // EOF + PreconnectErrorResendRequestTest(NULL, &read_failure, false); +} + +// Make sure that on a 408 response (Request Timeout), the request is retried, +// if the socket was a preconnected (UNUSED_IDLE) socket. +TEST_P(HttpNetworkTransactionTest, RetryOnIdle408) { + MockRead read_failure(SYNCHRONOUS, + "HTTP/1.1 408 Request Timeout\r\n" + "Connection: Keep-Alive\r\n" + "Content-Length: 6\r\n\r\n" + "Pickle"); + KeepAliveConnectionResendRequestTest(NULL, &read_failure); + PreconnectErrorResendRequestTest(NULL, &read_failure, false); +} + +TEST_P(HttpNetworkTransactionTest, + SpdyPreconnectErrorNotConnectedOnWrite) { + MockWrite write_failure(ASYNC, ERR_SOCKET_NOT_CONNECTED); + PreconnectErrorResendRequestTest(&write_failure, NULL, true); +} + +TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorReset) { + MockRead read_failure(ASYNC, ERR_CONNECTION_RESET); + PreconnectErrorResendRequestTest(NULL, &read_failure, true); +} + +TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorEOF) { + MockRead read_failure(SYNCHRONOUS, OK); // EOF + PreconnectErrorResendRequestTest(NULL, &read_failure, true); +} + +TEST_P(HttpNetworkTransactionTest, SpdyPreconnectErrorAsyncEOF) { + MockRead read_failure(ASYNC, OK); // EOF + PreconnectErrorResendRequestTest(NULL, &read_failure, true); +} + TEST_P(HttpNetworkTransactionTest, NonKeepAliveConnectionReset) { HttpRequestInfo request; request.method = "GET"; @@ -1308,6 +1559,85 @@ TEST_P(HttpNetworkTransactionTest, NonKeepAliveConnectionEOF) { EXPECT_EQ(ERR_EMPTY_RESPONSE, out.rv); } +// Test that network access can be deferred and resumed. +TEST_P(HttpNetworkTransactionTest, ThrottleBeforeNetworkStart) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); + + // Defer on OnBeforeNetworkStart. + BeforeNetworkStartHandler net_start_handler(true); // defer + trans->SetBeforeNetworkStartCallback( + base::Bind(&BeforeNetworkStartHandler::OnBeforeNetworkStart, + base::Unretained(&net_start_handler))); + + MockRead data_reads[] = { + MockRead("HTTP/1.0 200 OK\r\n"), + MockRead("Content-Length: 5\r\n\r\n"), + MockRead("hello"), + MockRead(SYNCHRONOUS, 0), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + base::MessageLoop::current()->RunUntilIdle(); + + // Should have deferred for network start. + EXPECT_TRUE(net_start_handler.observed_before_network_start()); + EXPECT_EQ(LOAD_STATE_WAITING_FOR_DELEGATE, trans->GetLoadState()); + EXPECT_TRUE(trans->GetResponseInfo() == NULL); + + trans->ResumeNetworkStart(); + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + EXPECT_TRUE(trans->GetResponseInfo() != NULL); + + scoped_refptr<IOBufferWithSize> io_buf(new IOBufferWithSize(100)); + rv = trans->Read(io_buf.get(), io_buf->size(), callback.callback()); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_EQ(5, rv); + trans.reset(); +} + +// Test that network use can be deferred and canceled. +TEST_P(HttpNetworkTransactionTest, ThrottleAndCancelBeforeNetworkStart) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); + + // Defer on OnBeforeNetworkStart. + BeforeNetworkStartHandler net_start_handler(true); // defer + trans->SetBeforeNetworkStartCallback( + base::Bind(&BeforeNetworkStartHandler::OnBeforeNetworkStart, + base::Unretained(&net_start_handler))); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + base::MessageLoop::current()->RunUntilIdle(); + + // Should have deferred for network start. + EXPECT_TRUE(net_start_handler.observed_before_network_start()); + EXPECT_EQ(LOAD_STATE_WAITING_FOR_DELEGATE, trans->GetLoadState()); + EXPECT_TRUE(trans->GetResponseInfo() == NULL); +} + // Next 2 cases (KeepAliveEarlyClose and KeepAliveEarlyClose2) are regression // tests. There was a bug causing HttpNetworkTransaction to hang in the // destructor in such situations. @@ -1569,6 +1899,9 @@ TEST_P(HttpNetworkTransactionTest, BasicAuth) { EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info1)); TestLoadTimingNotReused(load_timing_info1, CONNECT_TIMING_HAS_DNS_TIMES); + int64 reads_size1 = ReadsSize(data_reads1, arraysize(data_reads1)); + EXPECT_EQ(reads_size1, trans->GetTotalReceivedBytes()); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(CheckBasicServerAuth(response->auth_challenge.get())); @@ -1591,6 +1924,9 @@ TEST_P(HttpNetworkTransactionTest, BasicAuth) { load_timing_info2.connect_timing.connect_start); EXPECT_NE(load_timing_info1.socket_log_id, load_timing_info2.socket_log_id); + int64 reads_size2 = ReadsSize(data_reads2, arraysize(data_reads2)); + EXPECT_EQ(reads_size1 + reads_size2, trans->GetTotalReceivedBytes()); + response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); @@ -1633,6 +1969,9 @@ TEST_P(HttpNetworkTransactionTest, DoNotSendAuth) { rv = callback.WaitForResult(); EXPECT_EQ(0, rv); + int64 reads_size = ReadsSize(data_reads, arraysize(data_reads)); + EXPECT_EQ(reads_size, trans->GetTotalReceivedBytes()); + const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); @@ -1730,6 +2069,12 @@ TEST_P(HttpNetworkTransactionTest, BasicAuthKeepAlive) { ASSERT_TRUE(response != NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); EXPECT_EQ(5, response->headers->GetContentLength()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + int64 reads_size1 = ReadsSize(data_reads1, arraysize(data_reads1)); + EXPECT_EQ(reads_size1, trans->GetTotalReceivedBytes()); } // Test the request-challenge-retry sequence for basic auth, over a keep-alive @@ -2683,6 +3028,82 @@ TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGet) { EXPECT_EQ(kUploadData, response_data); } +// Verifies that a session which races and wins against the owning transaction +// (completing prior to host resolution), doesn't fail the transaction. +// Regression test for crbug.com/334413. +TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithSessionRace) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + // Configure SPDY proxy server "proxy:70". + session_deps_.proxy_service.reset( + ProxyService::CreateFixed("https://proxy:70")); + CapturingBoundNetLog log; + session_deps_.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + + // Fetch http://www.google.com/ through the SPDY proxy. + scoped_ptr<SpdyFrame> req( + spdy_util_.ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false)); + MockWrite spdy_writes[] = {CreateMockWrite(*req)}; + + scoped_ptr<SpdyFrame> resp(spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 1)); + scoped_ptr<SpdyFrame> data(spdy_util_.ConstructSpdyBodyFrame(1, true)); + MockRead spdy_reads[] = { + CreateMockRead(*resp), CreateMockRead(*data), MockRead(ASYNC, 0, 0), + }; + + DelayedSocketData spdy_data( + 1, // wait for one write to finish before reading. + spdy_reads, + arraysize(spdy_reads), + spdy_writes, + arraysize(spdy_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&spdy_data); + + SSLSocketDataProvider ssl(ASYNC, OK); + ssl.SetNextProto(GetParam()); + session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl); + + TestCompletionCallback callback1; + + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session.get())); + + // Stall the hostname resolution begun by the transaction. + session_deps_.host_resolver->set_synchronous_mode(false); + session_deps_.host_resolver->set_ondemand_mode(true); + + int rv = trans->Start(&request, callback1.callback(), log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + // Race a session to the proxy, which completes first. + session_deps_.host_resolver->set_ondemand_mode(false); + SpdySessionKey key( + HostPortPair("proxy", 70), ProxyServer::Direct(), PRIVACY_MODE_DISABLED); + base::WeakPtr<SpdySession> spdy_session = + CreateSecureSpdySession(session, key, log.bound()); + + // Unstall the resolution begun by the transaction. + session_deps_.host_resolver->set_ondemand_mode(true); + session_deps_.host_resolver->ResolveAllPending(); + + EXPECT_FALSE(callback1.have_result()); + rv = callback1.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + ASSERT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + std::string response_data; + ASSERT_EQ(OK, ReadTransaction(trans.get(), &response_data)); + EXPECT_EQ(kUploadData, response_data); +} + // Test a SPDY get through an HTTPS Proxy. TEST_P(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) { HttpRequestInfo request; @@ -3064,23 +3485,15 @@ TEST_P(HttpNetworkTransactionTest, spdy_util_.ConstructSpdyWindowUpdate(1, wrapped_get_resp1->size())); // CONNECT to news.google.com:443 via SPDY. - const char* const kConnectHeaders2[] = { - spdy_util_.GetMethodKey(), "CONNECT", - spdy_util_.GetPathKey(), "news.google.com:443", - spdy_util_.GetHostKey(), "news.google.com", - spdy_util_.GetVersionKey(), "HTTP/1.1", - }; + SpdySynStreamIR connect2_ir(3); + spdy_util_.SetPriority(LOWEST, &connect2_ir); + connect2_ir.SetHeader(spdy_util_.GetMethodKey(), "CONNECT"); + connect2_ir.SetHeader(spdy_util_.GetPathKey(), "news.google.com:443"); + connect2_ir.SetHeader(spdy_util_.GetHostKey(), "news.google.com"); + spdy_util_.MaybeAddVersionHeader(&connect2_ir); scoped_ptr<SpdyFrame> connect2( - spdy_util_.ConstructSpdyControlFrame(NULL, - 0, - /*compressed*/ false, - 3, - LOWEST, - SYN_STREAM, - CONTROL_FLAG_NONE, - kConnectHeaders2, - arraysize(kConnectHeaders2), - 0)); + spdy_util_.CreateFramer(false)->SerializeFrame(connect2_ir)); + scoped_ptr<SpdyFrame> conn_resp2( spdy_util_.ConstructSpdyGetSynReply(NULL, 0, 3)); @@ -3635,6 +4048,10 @@ TEST_P(HttpNetworkTransactionTest, ConnectStatus307) { ConnectStatusHelper(MockRead("HTTP/1.1 307 Temporary Redirect\r\n")); } +TEST_P(HttpNetworkTransactionTest, ConnectStatus308) { + ConnectStatusHelper(MockRead("HTTP/1.1 308 Permanent Redirect\r\n")); +} + TEST_P(HttpNetworkTransactionTest, ConnectStatus400) { ConnectStatusHelper(MockRead("HTTP/1.1 400 Bad Request\r\n")); } @@ -7160,7 +7577,7 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForDirectConnections) { }, }; - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { session_deps_.proxy_service.reset( @@ -7224,7 +7641,7 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) { }, }; - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { session_deps_.proxy_service.reset( @@ -7295,7 +7712,7 @@ TEST_P(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) { }, }; - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { session_deps_.proxy_service.reset( @@ -7710,7 +8127,7 @@ TEST_P(HttpNetworkTransactionTest, UploadUnreadableFile) { base::FilePath temp_file; ASSERT_TRUE(base::CreateTemporaryFile(&temp_file)); std::string temp_file_content("Unreadable file."); - ASSERT_TRUE(file_util::WriteFile(temp_file, temp_file_content.c_str(), + ASSERT_TRUE(base::WriteFile(temp_file, temp_file_content.c_str(), temp_file_content.length())); ASSERT_TRUE(file_util::MakeFileUnreadable(temp_file)); @@ -7961,8 +8378,8 @@ TEST_P(HttpNetworkTransactionTest, ChangeAuthRealms) { } TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.next_protos = SpdyNextProtos(); + session_deps_.use_alternate_protocols = true; std::string alternate_protocol_http_header = GetAlternateProtocolHttpHeader(); @@ -7993,7 +8410,7 @@ TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { EXPECT_EQ(ERR_IO_PENDING, rv); HostPortPair http_host_port_pair("www.google.com", 80); - const HttpServerProperties& http_server_properties = + HttpServerProperties& http_server_properties = *session->http_server_properties(); EXPECT_FALSE( http_server_properties.HasAlternateProtocol(http_host_port_pair)); @@ -8022,7 +8439,7 @@ TEST_P(HttpNetworkTransactionTest, HonorAlternateProtocolHeader) { TEST_P(HttpNetworkTransactionTest, MarkBrokenAlternateProtocolAndFallback) { - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; HttpRequestInfo request; request.method = "GET"; @@ -8085,7 +8502,7 @@ TEST_P(HttpNetworkTransactionTest, // protocol to an unrestricted (port >= 1024) when the original traffic was // on a restricted port (port < 1024). Ensure that we can redirect in all // other cases. - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; HttpRequestInfo restricted_port_request; restricted_port_request.method = "GET"; @@ -8135,7 +8552,7 @@ TEST_P(HttpNetworkTransactionTest, // on a restricted port (port < 1024) if we set // enable_user_alternate_protocol_ports. - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; session_deps_.enable_user_alternate_protocol_ports = true; HttpRequestInfo restricted_port_request; @@ -8184,7 +8601,7 @@ TEST_P(HttpNetworkTransactionTest, // protocol to an unrestricted (port >= 1024) when the original traffic was // on a restricted port (port < 1024). Ensure that we can redirect in all // other cases. - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; HttpRequestInfo restricted_port_request; restricted_port_request.method = "GET"; @@ -8233,7 +8650,7 @@ TEST_P(HttpNetworkTransactionTest, // protocol to an unrestricted (port >= 1024) when the original traffic was // on a restricted port (port < 1024). Ensure that we can redirect in all // other cases. - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; HttpRequestInfo unrestricted_port_request; unrestricted_port_request.method = "GET"; @@ -8281,7 +8698,7 @@ TEST_P(HttpNetworkTransactionTest, // protocol to an unrestricted (port >= 1024) when the original traffic was // on a restricted port (port < 1024). Ensure that we can redirect in all // other cases. - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; HttpRequestInfo unrestricted_port_request; unrestricted_port_request.method = "GET"; @@ -8323,12 +8740,11 @@ TEST_P(HttpNetworkTransactionTest, EXPECT_EQ(OK, callback.WaitForResult()); } -TEST_P(HttpNetworkTransactionTest, - AlternateProtocolUnsafeBlocked) { +TEST_P(HttpNetworkTransactionTest, AlternateProtocolUnsafeBlocked) { // Ensure that we're not allowed to redirect traffic via an alternate // protocol to an unsafe port, and that we resume the second // HttpStreamFactoryImpl::Job once the alternate protocol request fails. - HttpStreamFactory::set_use_alternate_protocols(true); + session_deps_.use_alternate_protocols = true; HttpRequestInfo request; request.method = "GET"; @@ -8366,7 +8782,7 @@ TEST_P(HttpNetworkTransactionTest, EXPECT_EQ(OK, callback.WaitForResult()); // Disable alternate protocol before the asserts. - HttpStreamFactory::set_use_alternate_protocols(false); + // HttpStreamFactory::set_use_alternate_protocols(false); const HttpResponseInfo* response = trans->GetResponseInfo(); ASSERT_TRUE(response != NULL); @@ -8379,8 +8795,8 @@ TEST_P(HttpNetworkTransactionTest, } TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); HttpRequestInfo request; request.method = "GET"; @@ -8469,8 +8885,8 @@ TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { } TEST_P(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) { - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); HttpRequestInfo request; request.method = "GET"; @@ -8586,8 +9002,8 @@ TEST_P(HttpNetworkTransactionTest, AlternateProtocolWithSpdyLateBinding) { } TEST_P(HttpNetworkTransactionTest, StallAlternateProtocolForNpnSpdy) { - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); HttpRequestInfo request; request.method = "GET"; @@ -8705,8 +9121,8 @@ class CapturingProxyResolver : public ProxyResolver { TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) { - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); ProxyConfig proxy_config; proxy_config.set_auto_detect(true); @@ -8827,8 +9243,8 @@ TEST_P(HttpNetworkTransactionTest, TEST_P(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdyWithExistingSpdySession) { - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); HttpRequestInfo request; request.method = "GET"; @@ -8894,7 +9310,7 @@ TEST_P(HttpNetworkTransactionTest, // Set up an initial SpdySession in the pool to reuse. HostPortPair host_port_pair("www.google.com", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), - kPrivacyModeDisabled); + PRIVACY_MODE_DISABLED); base::WeakPtr<SpdySession> spdy_session = CreateSecureSpdySession(session, key, BoundNetLog()); @@ -9230,8 +9646,8 @@ TEST_P(HttpNetworkTransactionTest, GenerateAuthToken) { HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock()); std::string auth_challenge = "Mock realm=proxy"; GURL origin(test_config.proxy_url); - HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(), - auth_challenge.end()); + HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(), + auth_challenge.end()); auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_PROXY, origin, BoundNetLog()); auth_handler->SetGenerateExpectation( @@ -9244,8 +9660,8 @@ TEST_P(HttpNetworkTransactionTest, GenerateAuthToken) { HttpAuthHandlerMock* auth_handler(new HttpAuthHandlerMock()); std::string auth_challenge = "Mock realm=server"; GURL origin(test_config.server_url); - HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(), - auth_challenge.end()); + HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(), + auth_challenge.end()); auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER, origin, BoundNetLog()); auth_handler->SetGenerateExpectation( @@ -9341,8 +9757,8 @@ TEST_P(HttpNetworkTransactionTest, MultiRoundAuth) { auth_handler->set_connection_based(true); std::string auth_challenge = "Mock realm=server"; GURL origin("http://www.example.com"); - HttpAuth::ChallengeTokenizer tokenizer(auth_challenge.begin(), - auth_challenge.end()); + HttpAuthChallengeTokenizer tokenizer(auth_challenge.begin(), + auth_challenge.end()); auth_handler->InitFromChallenge(&tokenizer, HttpAuth::AUTH_SERVER, origin, BoundNetLog()); auth_factory->AddMockHandler(auth_handler, HttpAuth::AUTH_SERVER); @@ -9518,10 +9934,11 @@ TEST_P(HttpNetworkTransactionTest, MultiRoundAuth) { // This tests the case that a request is issued via http instead of spdy after // npn is negotiated. TEST_P(HttpNetworkTransactionTest, NpnWithHttpOverSSL) { - HttpStreamFactory::set_use_alternate_protocols(true); - std::vector<NextProto> next_protos; + session_deps_.use_alternate_protocols = true; + NextProtoVector next_protos; next_protos.push_back(kProtoHTTP11); - HttpStreamFactory::SetNextProtos(next_protos); + session_deps_.next_protos = next_protos; + HttpRequestInfo request; request.method = "GET"; request.url = GURL("https://www.google.com/"); @@ -9582,8 +9999,8 @@ TEST_P(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) { // Simulate the SSL handshake completing with an NPN negotiation // followed by an immediate server closing of the socket. // Fix crash: http://crbug.com/46369 - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); HttpRequestInfo request; request.method = "GET"; @@ -9645,8 +10062,8 @@ class UrlRecordingHttpAuthHandlerMock : public HttpAuthHandlerMock { TEST_P(HttpNetworkTransactionTest, SpdyAlternateProtocolThroughProxy) { // This test ensures that the URL passed into the proxy is upgraded // to https when doing an Alternate Protocol upgrade. - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); session_deps_.proxy_service.reset( ProxyService::CreateFixedFromPacResult("PROXY myproxy:70")); @@ -9840,6 +10257,56 @@ TEST_P(HttpNetworkTransactionTest, SimpleCancel) { base::MessageLoop::current()->RunUntilIdle(); } +// Test that if a transaction is cancelled after receiving the headers, the +// stream is drained properly and added back to the socket pool. The main +// purpose of this test is to make sure that an HttpStreamParser can be read +// from after the HttpNetworkTransaction and the objects it owns have been +// deleted. +// See http://crbug.com/368418 +TEST_P(HttpNetworkTransactionTest, CancelAfterHeaders) { + MockRead data_reads[] = { + MockRead(ASYNC, "HTTP/1.1 200 OK\r\n"), + MockRead(ASYNC, "Content-Length: 2\r\n"), + MockRead(ASYNC, "Connection: Keep-Alive\r\n\r\n"), + MockRead(ASYNC, "1"), + // 2 async reads are necessary to trigger a ReadResponseBody call after the + // HttpNetworkTransaction has been deleted. + MockRead(ASYNC, "2"), + MockRead(SYNCHRONOUS, ERR_IO_PENDING), // Should never read this. + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + + { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + + HttpNetworkTransaction trans(DEFAULT_PRIORITY, session); + TestCompletionCallback callback; + + int rv = trans.Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + callback.WaitForResult(); + + const HttpResponseInfo* response = trans.GetResponseInfo(); + ASSERT_TRUE(response != NULL); + EXPECT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 200 OK", response->headers->GetStatusLine()); + + // The transaction and HttpRequestInfo are deleted. + } + + // Let the HttpResponseBodyDrainer drain the socket. + base::MessageLoop::current()->RunUntilIdle(); + + // Socket should now be idle, waiting to be reused. + EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session)); +} + // Test a basic GET request through a proxy. TEST_P(HttpNetworkTransactionTest, ProxyGet) { session_deps_.proxy_service.reset( @@ -9887,6 +10354,8 @@ TEST_P(HttpNetworkTransactionTest, ProxyGet) { EXPECT_EQ(200, response->headers->response_code()); EXPECT_EQ(100, response->headers->GetContentLength()); EXPECT_TRUE(response->was_fetched_via_proxy); + EXPECT_TRUE( + response->proxy_server.Equals(HostPortPair::FromString("myproxy:70"))); EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); LoadTimingInfo load_timing_info; @@ -9961,6 +10430,8 @@ TEST_P(HttpNetworkTransactionTest, ProxyTunnelGet) { EXPECT_EQ(100, response->headers->GetContentLength()); EXPECT_TRUE(HttpVersion(1, 1) == response->headers->GetHttpVersion()); EXPECT_TRUE(response->was_fetched_via_proxy); + EXPECT_TRUE( + response->proxy_server.Equals(HostPortPair::FromString("myproxy:70"))); LoadTimingInfo load_timing_info; EXPECT_TRUE(trans->GetLoadTimingInfo(&load_timing_info)); @@ -10053,7 +10524,7 @@ TEST_P(HttpNetworkTransactionTest, PreconnectWithExistingSpdySession) { // Set up an initial SpdySession in the pool to reuse. HostPortPair host_port_pair("www.google.com", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), - kPrivacyModeDisabled); + PRIVACY_MODE_DISABLED); base::WeakPtr<SpdySession> spdy_session = CreateInsecureSpdySession(session, key, BoundNetLog()); @@ -10130,7 +10601,7 @@ TEST_P(HttpNetworkTransactionTest, request_info.load_flags = net::LOAD_NORMAL; scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); - cert_request->host_and_port = "www.example.com:443"; + cert_request->host_and_port = HostPortPair("www.example.com", 443); // [ssl_]data1 contains the data for the first SSL handshake. When a // CertificateRequest is received for the first time, the handshake will @@ -10210,8 +10681,8 @@ TEST_P(HttpNetworkTransactionTest, // Ensure the certificate was added to the client auth cache before // allowing the connection to continue restarting. scoped_refptr<X509Certificate> client_cert; - ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", - &client_cert)); + ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("www.example.com", 443), &client_cert)); ASSERT_EQ(NULL, client_cert.get()); // Restart the handshake. This will consume ssl_data2, which fails, and @@ -10222,8 +10693,8 @@ TEST_P(HttpNetworkTransactionTest, // Ensure that the client certificate is removed from the cache on a // handshake failure. - ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", - &client_cert)); + ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("www.example.com", 443), &client_cert)); } // Ensure that a client certificate is removed from the SSL client auth @@ -10240,7 +10711,7 @@ TEST_P(HttpNetworkTransactionTest, request_info.load_flags = net::LOAD_NORMAL; scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); - cert_request->host_and_port = "www.example.com:443"; + cert_request->host_and_port = HostPortPair("www.example.com", 443); // When TLS False Start is used, SSLClientSocket::Connect() calls will // return successfully after reading up to the peer's Certificate message. @@ -10331,8 +10802,8 @@ TEST_P(HttpNetworkTransactionTest, // Ensure the certificate was added to the client auth cache before // allowing the connection to continue restarting. scoped_refptr<X509Certificate> client_cert; - ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", - &client_cert)); + ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("www.example.com", 443), &client_cert)); ASSERT_EQ(NULL, client_cert.get()); // Restart the handshake. This will consume ssl_data2, which fails, and @@ -10343,8 +10814,8 @@ TEST_P(HttpNetworkTransactionTest, // Ensure that the client certificate is removed from the cache on a // handshake failure. - ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", - &client_cert)); + ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("www.example.com", 443), &client_cert)); } // Ensure that a client certificate is removed from the SSL client auth @@ -10362,7 +10833,7 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) { session_deps_.net_log = log.bound().net_log(); scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); - cert_request->host_and_port = "proxy:70"; + cert_request->host_and_port = HostPortPair("proxy", 70); // See ClientAuthCertCache_Direct_NoFalseStart for the explanation of // [ssl_]data[1-3]. Rather than represending the endpoint @@ -10425,13 +10896,13 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) { // Ensure the certificate was added to the client auth cache before // allowing the connection to continue restarting. scoped_refptr<X509Certificate> client_cert; - ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup("proxy:70", - &client_cert)); + ASSERT_TRUE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("proxy", 70), &client_cert)); ASSERT_EQ(NULL, client_cert.get()); // Ensure the certificate was NOT cached for the endpoint. This only // applies to HTTPS requests, but is fine to check for HTTP requests. - ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", - &client_cert)); + ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("www.example.com", 443), &client_cert)); // Restart the handshake. This will consume ssl_data2, which fails, and // then consume ssl_data3, which should also fail. The result code is @@ -10441,10 +10912,10 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) { // Now that the new handshake has failed, ensure that the client // certificate was removed from the client auth cache. - ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("proxy:70", - &client_cert)); - ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup("www.example.com:443", - &client_cert)); + ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("proxy", 70), &client_cert)); + ASSERT_FALSE(session->ssl_client_auth_cache()->Lookup( + HostPortPair("www.example.com", 443), &client_cert)); } } @@ -10465,8 +10936,8 @@ TEST_P(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) { #define MAYBE_UseIPConnectionPooling UseIPConnectionPooling #endif WRAPPED_TEST_P(HttpNetworkTransactionTest, MAYBE_UseIPConnectionPooling) { - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); // Set up a special HttpNetworkSession with a MockCachingHostResolver. session_deps_.host_resolver.reset(new MockCachingHostResolver()); @@ -10568,8 +11039,8 @@ WRAPPED_TEST_P(HttpNetworkTransactionTest, MAYBE_UseIPConnectionPooling) { #undef MAYBE_UseIPConnectionPooling TEST_P(HttpNetworkTransactionTest, UseIPConnectionPoolingAfterResolution) { - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); // Set up a special HttpNetworkSession with a MockCachingHostResolver. session_deps_.host_resolver.reset(new MockCachingHostResolver()); @@ -10710,10 +11181,9 @@ WRAPPED_TEST_P(HttpNetworkTransactionTest, // prefix doesn't work with parametrized tests). #if defined(OS_WIN) return; -#endif - - HttpStreamFactory::set_use_alternate_protocols(true); - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); +#else + session_deps_.use_alternate_protocols = true; + session_deps_.next_protos = SpdyNextProtos(); // Set up a special HttpNetworkSession with a OneTimeCachingHostResolver. OneTimeCachingHostResolver host_resolver(HostPortPair("www.gmail.com", 443)); @@ -10813,56 +11283,10 @@ WRAPPED_TEST_P(HttpNetworkTransactionTest, EXPECT_TRUE(response->was_npn_negotiated); ASSERT_EQ(OK, ReadTransaction(&trans2, &response_data)); EXPECT_EQ("hello!", response_data); +#endif } #undef MAYBE_UseIPConnectionPoolingWithHostCacheExpiration -TEST_P(HttpNetworkTransactionTest, ReadPipelineEvictionFallback) { - MockRead data_reads1[] = { - MockRead(SYNCHRONOUS, ERR_PIPELINE_EVICTION), - }; - MockRead data_reads2[] = { - MockRead("HTTP/1.0 200 OK\r\n\r\n"), - MockRead("hello world"), - MockRead(SYNCHRONOUS, OK), - }; - StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), NULL, 0); - StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), NULL, 0); - StaticSocketDataProvider* data[] = { &data1, &data2 }; - - SimpleGetHelperResult out = SimpleGetHelperForData(data, arraysize(data)); - - EXPECT_EQ(OK, out.rv); - EXPECT_EQ("HTTP/1.0 200 OK", out.status_line); - EXPECT_EQ("hello world", out.response_data); -} - -TEST_P(HttpNetworkTransactionTest, SendPipelineEvictionFallback) { - MockWrite data_writes1[] = { - MockWrite(SYNCHRONOUS, ERR_PIPELINE_EVICTION), - }; - MockWrite data_writes2[] = { - MockWrite("GET / HTTP/1.1\r\n" - "Host: www.google.com\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead data_reads2[] = { - MockRead("HTTP/1.0 200 OK\r\n\r\n"), - MockRead("hello world"), - MockRead(SYNCHRONOUS, OK), - }; - StaticSocketDataProvider data1(NULL, 0, - data_writes1, arraysize(data_writes1)); - StaticSocketDataProvider data2(data_reads2, arraysize(data_reads2), - data_writes2, arraysize(data_writes2)); - StaticSocketDataProvider* data[] = { &data1, &data2 }; - - SimpleGetHelperResult out = SimpleGetHelperForData(data, arraysize(data)); - - EXPECT_EQ(OK, out.rv); - EXPECT_EQ("HTTP/1.0 200 OK", out.status_line); - EXPECT_EQ("hello world", out.response_data); -} - TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttp) { const std::string https_url = "https://www.google.com/"; const std::string http_url = "http://www.google.com:443/"; @@ -10953,20 +11377,21 @@ TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttpOverTunnel) { LOWEST)); scoped_ptr<SpdyFrame> req1( spdy_util_.ConstructSpdyGet(https_url.c_str(), false, 1, LOWEST)); - - // SPDY GET for HTTP URL (through the proxy, but not the tunnel) scoped_ptr<SpdyFrame> wrapped_req1( spdy_util_.ConstructWrappedSpdyFrame(req1, 1)); - const char* const headers[] = { - spdy_util_.GetMethodKey(), "GET", - spdy_util_.GetPathKey(), spdy_util_.is_spdy2() ? http_url.c_str() : "/", - spdy_util_.GetHostKey(), "www.google.com:443", - spdy_util_.GetSchemeKey(), "http", - spdy_util_.GetVersionKey(), "HTTP/1.1" - }; - scoped_ptr<SpdyFrame> req2(spdy_util_.ConstructSpdyControlFrame( - NULL, 0, false, 3, MEDIUM, SYN_STREAM, CONTROL_FLAG_FIN, - headers, arraysize(headers), 0)); + + // SPDY GET for HTTP URL (through the proxy, but not the tunnel). + SpdySynStreamIR req2_ir(3); + spdy_util_.SetPriority(MEDIUM, &req2_ir); + req2_ir.set_fin(true); + req2_ir.SetHeader(spdy_util_.GetMethodKey(), "GET"); + req2_ir.SetHeader(spdy_util_.GetPathKey(), + spdy_util_.is_spdy2() ? http_url.c_str() : "/"); + req2_ir.SetHeader(spdy_util_.GetHostKey(), "www.google.com:443"); + req2_ir.SetHeader(spdy_util_.GetSchemeKey(), "http"); + spdy_util_.MaybeAddVersionHeader(&req2_ir); + scoped_ptr<SpdyFrame> req2( + spdy_util_.CreateFramer(false)->SerializeFrame(req2_ir)); MockWrite writes1[] = { CreateMockWrite(*connect, 0), @@ -11058,7 +11483,7 @@ TEST_P(HttpNetworkTransactionTest, DoNotUseSpdySessionForHttpOverTunnel) { } TEST_P(HttpNetworkTransactionTest, UseSpdySessionForHttpWhenForced) { - HttpStreamFactory::set_force_spdy_always(true); + session_deps_.force_spdy_always = true; const std::string https_url = "https://www.google.com/"; const std::string http_url = "http://www.google.com:443/"; @@ -11333,7 +11758,7 @@ TEST_P(HttpNetworkTransactionTest, ErrorSocketNotConnected) { } TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) { - HttpStreamFactory::SetNextProtos(SpdyNextProtos()); + session_deps_.next_protos = SpdyNextProtos(); ClientSocketPoolManager::set_max_sockets_per_group( HttpNetworkSession::NORMAL_SOCKET_POOL, 1); ClientSocketPoolManager::set_max_sockets_per_pool( @@ -11411,7 +11836,7 @@ TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) { HostPortPair host_port_pair_a("www.a.com", 443); SpdySessionKey spdy_session_key_a( - host_port_pair_a, ProxyServer::Direct(), kPrivacyModeDisabled); + host_port_pair_a, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); EXPECT_FALSE( HasSpdySession(session->spdy_session_pool(), spdy_session_key_a)); @@ -11443,7 +11868,7 @@ TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) { HostPortPair host_port_pair_b("www.b.com", 443); SpdySessionKey spdy_session_key_b( - host_port_pair_b, ProxyServer::Direct(), kPrivacyModeDisabled); + host_port_pair_b, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); EXPECT_FALSE( HasSpdySession(session->spdy_session_pool(), spdy_session_key_b)); HttpRequestInfo request2; @@ -11471,7 +11896,7 @@ TEST_P(HttpNetworkTransactionTest, CloseIdleSpdySessionToOpenNewOne) { HostPortPair host_port_pair_a1("www.a.com", 80); SpdySessionKey spdy_session_key_a1( - host_port_pair_a1, ProxyServer::Direct(), kPrivacyModeDisabled); + host_port_pair_a1, ProxyServer::Direct(), PRIVACY_MODE_DISABLED); EXPECT_FALSE( HasSpdySession(session->spdy_session_pool(), spdy_session_key_a1)); HttpRequestInfo request3; @@ -11779,11 +12204,6 @@ class FakeStream : public HttpStreamBase, return ERR_UNEXPECTED; } - virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE { - ADD_FAILURE(); - return NULL; - } - virtual int ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) OVERRIDE { ADD_FAILURE(); @@ -11973,11 +12393,6 @@ class FakeStreamFactory : public HttpStreamFactory { ADD_FAILURE(); } - virtual base::Value* PipelineInfoToValue() const OVERRIDE { - ADD_FAILURE(); - return NULL; - } - virtual const HostMappingRules* GetHostMappingRules() const OVERRIDE { ADD_FAILURE(); return NULL; @@ -12289,4 +12704,487 @@ TEST_P(HttpNetworkTransactionTest, CloseSSLSocketOnIdleForHttpRequest2) { EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session)); } +TEST_P(HttpNetworkTransactionTest, PostReadsErrorResponseAfterReset) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + // Send headers successfully, but get an error while sending the body. + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.0 400 Not OK\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("hello world", response_data); +} + +// This test makes sure the retry logic doesn't trigger when reading an error +// response from a server that rejected a POST with a CONNECTION_RESET. +TEST_P(HttpNetworkTransactionTest, + PostReadsErrorResponseAfterResetOnReusedSocket) { + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + MockWrite data_writes[] = { + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n\r\n"), + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 Peachy\r\n" + "Content-Length: 14\r\n\r\n"), + MockRead("first response"), + MockRead("HTTP/1.1 400 Not OK\r\n" + "Content-Length: 15\r\n\r\n"), + MockRead("second response"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + HttpRequestInfo request1; + request1.method = "GET"; + request1.url = GURL("http://www.foo.com/"); + request1.load_flags = 0; + + scoped_ptr<HttpTransaction> trans1( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + int rv = trans1->Start(&request1, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response1 = trans1->GetResponseInfo(); + ASSERT_TRUE(response1 != NULL); + + EXPECT_TRUE(response1->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 200 Peachy", response1->headers->GetStatusLine()); + + std::string response_data1; + rv = ReadTransaction(trans1.get(), &response_data1); + EXPECT_EQ(OK, rv); + EXPECT_EQ("first response", response_data1); + // Delete the transaction to release the socket back into the socket pool. + trans1.reset(); + + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request2; + request2.method = "POST"; + request2.url = GURL("http://www.foo.com/"); + request2.upload_data_stream = &upload_data_stream; + request2.load_flags = 0; + + scoped_ptr<HttpTransaction> trans2( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + rv = trans2->Start(&request2, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response2 = trans2->GetResponseInfo(); + ASSERT_TRUE(response2 != NULL); + + EXPECT_TRUE(response2->headers.get() != NULL); + EXPECT_EQ("HTTP/1.1 400 Not OK", response2->headers->GetStatusLine()); + + std::string response_data2; + rv = ReadTransaction(trans2.get(), &response_data2); + EXPECT_EQ(OK, rv); + EXPECT_EQ("second response", response_data2); +} + +TEST_P(HttpNetworkTransactionTest, + PostReadsErrorResponseAfterResetPartialBodySent) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + // Send headers successfully, but get an error while sending the body. + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n" + "fo"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.0 400 Not OK\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("hello world", response_data); +} + +// This tests the more common case than the previous test, where headers and +// body are not merged into a single request. +TEST_P(HttpNetworkTransactionTest, ChunkedPostReadsErrorResponseAfterReset) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(UploadDataStream::CHUNKED, 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + // Send headers successfully, but get an error while sending the body. + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Transfer-Encoding: chunked\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.0 400 Not OK\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + // Make sure the headers are sent before adding a chunk. This ensures that + // they can't be merged with the body in a single send. Not currently + // necessary since a chunked body is never merged with headers, but this makes + // the test more future proof. + base::RunLoop().RunUntilIdle(); + + upload_data_stream.AppendChunk("last chunk", 10, true); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("hello world", response_data); +} + +TEST_P(HttpNetworkTransactionTest, PostReadsErrorResponseAfterResetAnd100) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.0 100 Continue\r\n\r\n"), + MockRead("HTTP/1.0 400 Not OK\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(OK, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->headers.get() != NULL); + EXPECT_EQ("HTTP/1.0 400 Not OK", response->headers->GetStatusLine()); + + std::string response_data; + rv = ReadTransaction(trans.get(), &response_data); + EXPECT_EQ(OK, rv); + EXPECT_EQ("hello world", response_data); +} + +TEST_P(HttpNetworkTransactionTest, PostIgnoresNonErrorResponseAfterReset) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + // Send headers successfully, but get an error while sending the body. + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.0 200 Just Dandy\r\n\r\n"), + MockRead("hello world"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(ERR_CONNECTION_RESET, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + EXPECT_TRUE(response == NULL); +} + +TEST_P(HttpNetworkTransactionTest, + PostIgnoresNonErrorResponseAfterResetAnd100) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + // Send headers successfully, but get an error while sending the body. + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.0 100 Continue\r\n\r\n"), + MockRead("HTTP/1.0 302 Redirect\r\n"), + MockRead("Location: http://somewhere-else.com/\r\n"), + MockRead("Content-Length: 0\r\n\r\n"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(ERR_CONNECTION_RESET, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + EXPECT_TRUE(response == NULL); +} + +TEST_P(HttpNetworkTransactionTest, PostIgnoresHttp09ResponseAfterReset) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + // Send headers successfully, but get an error while sending the body. + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP 0.9 rocks!"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(ERR_CONNECTION_RESET, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + EXPECT_TRUE(response == NULL); +} + +TEST_P(HttpNetworkTransactionTest, PostIgnoresPartial400HeadersAfterReset) { + ScopedVector<UploadElementReader> element_readers; + element_readers.push_back(new UploadBytesElementReader("foo", 3)); + UploadDataStream upload_data_stream(element_readers.Pass(), 0); + + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.foo.com/"); + request.upload_data_stream = &upload_data_stream; + request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps_)); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(DEFAULT_PRIORITY, session)); + // Send headers successfully, but get an error while sending the body. + MockWrite data_writes[] = { + MockWrite("POST / HTTP/1.1\r\n" + "Host: www.foo.com\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 3\r\n\r\n"), + MockWrite(SYNCHRONOUS, ERR_CONNECTION_RESET), + }; + + MockRead data_reads[] = { + MockRead("HTTP/1.0 400 Not a Full Response\r\n"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, + arraysize(data_writes)); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + TestCompletionCallback callback; + + int rv = trans->Start(&request, callback.callback(), BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback.WaitForResult(); + EXPECT_EQ(ERR_CONNECTION_RESET, rv); + + const HttpResponseInfo* response = trans->GetResponseInfo(); + EXPECT_TRUE(response == NULL); +} + } // namespace net diff --git a/chromium/net/http/http_pipelined_connection.h b/chromium/net/http/http_pipelined_connection.h deleted file mode 100644 index d0d3599e3f1..00000000000 --- a/chromium/net/http/http_pipelined_connection.h +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_CONNECTION_H_ -#define NET_HTTP_HTTP_PIPELINED_CONNECTION_H_ - -#include "net/base/net_export.h" -#include "net/base/net_log.h" -#include "net/socket/ssl_client_socket.h" - -namespace net { - -class BoundNetLog; -class ClientSocketHandle; -class HostPortPair; -class HttpPipelinedStream; -class ProxyInfo; -struct SSLConfig; - -class NET_EXPORT_PRIVATE HttpPipelinedConnection { - public: - enum Feedback { - OK, - PIPELINE_SOCKET_ERROR, - OLD_HTTP_VERSION, - MUST_CLOSE_CONNECTION, - AUTHENTICATION_REQUIRED, - }; - - class Delegate { - public: - // Called when a pipeline has newly available capacity. This may be because - // the first request has been sent and the pipeline is now active. Or, it - // may be because a request successfully completed. - virtual void OnPipelineHasCapacity(HttpPipelinedConnection* pipeline) = 0; - - // Called every time a pipeline receives headers. Lets the delegate know if - // the headers indicate that pipelining can be used. - virtual void OnPipelineFeedback(HttpPipelinedConnection* pipeline, - Feedback feedback) = 0; - }; - - class Factory { - public: - virtual ~Factory() {} - - virtual HttpPipelinedConnection* CreateNewPipeline( - ClientSocketHandle* connection, - Delegate* delegate, - const HostPortPair& origin, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) = 0; - }; - - virtual ~HttpPipelinedConnection() {} - - // Returns a new stream that uses this pipeline. - virtual HttpPipelinedStream* CreateNewStream() = 0; - - // The number of streams currently associated with this pipeline. - virtual int depth() const = 0; - - // True if this pipeline can accept new HTTP requests. False if a fatal error - // has occurred. - virtual bool usable() const = 0; - - // True if this pipeline has bound one request and is ready for additional - // requests. - virtual bool active() const = 0; - - // The SSLConfig used to establish this connection. - virtual const SSLConfig& used_ssl_config() const = 0; - - // The ProxyInfo used to establish this connection. - virtual const ProxyInfo& used_proxy_info() const = 0; - - // The BoundNetLog of this pipelined connection. - virtual const BoundNetLog& net_log() const = 0; - - // True if this connection was NPN negotiated. - virtual bool was_npn_negotiated() const = 0; - - // Protocol negotiated with the server. - virtual NextProto protocol_negotiated() const = 0; -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_CONNECTION_H_ diff --git a/chromium/net/http/http_pipelined_connection_impl.cc b/chromium/net/http/http_pipelined_connection_impl.cc deleted file mode 100644 index 284b25406d8..00000000000 --- a/chromium/net/http/http_pipelined_connection_impl.cc +++ /dev/null @@ -1,845 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_connection_impl.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/message_loop/message_loop.h" -#include "base/stl_util.h" -#include "base/values.h" -#include "net/base/io_buffer.h" -#include "net/http/http_pipelined_stream.h" -#include "net/http/http_request_info.h" -#include "net/http/http_response_body_drainer.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_stream_parser.h" -#include "net/http/http_version.h" -#include "net/socket/client_socket_handle.h" - -namespace net { - -namespace { - -base::Value* NetLogReceivedHeadersCallback(const NetLog::Source& source, - const std::string* feedback, - NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue; - source.AddToEventParameters(dict); - dict->SetString("feedback", *feedback); - return dict; -} - -base::Value* NetLogStreamClosedCallback(const NetLog::Source& source, - bool not_reusable, - NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue; - source.AddToEventParameters(dict); - dict->SetBoolean("not_reusable", not_reusable); - return dict; -} - -base::Value* NetLogHostPortPairCallback(const HostPortPair* host_port_pair, - NetLog::LogLevel /* log_level */) { - base::DictionaryValue* dict = new base::DictionaryValue; - dict->SetString("host_and_port", host_port_pair->ToString()); - return dict; -} - -} // anonymous namespace - -HttpPipelinedConnection* -HttpPipelinedConnectionImpl::Factory::CreateNewPipeline( - ClientSocketHandle* connection, - HttpPipelinedConnection::Delegate* delegate, - const HostPortPair& origin, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) { - return new HttpPipelinedConnectionImpl(connection, delegate, origin, - used_ssl_config, used_proxy_info, - net_log, was_npn_negotiated, - protocol_negotiated); -} - -HttpPipelinedConnectionImpl::HttpPipelinedConnectionImpl( - ClientSocketHandle* connection, - HttpPipelinedConnection::Delegate* delegate, - const HostPortPair& origin, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) - : delegate_(delegate), - connection_(connection), - used_ssl_config_(used_ssl_config), - used_proxy_info_(used_proxy_info), - net_log_(BoundNetLog::Make(net_log.net_log(), - NetLog::SOURCE_HTTP_PIPELINED_CONNECTION)), - was_npn_negotiated_(was_npn_negotiated), - protocol_negotiated_(protocol_negotiated), - read_buf_(new GrowableIOBuffer()), - next_pipeline_id_(1), - active_(false), - usable_(true), - completed_one_request_(false), - weak_factory_(this), - send_next_state_(SEND_STATE_NONE), - send_still_on_call_stack_(false), - read_next_state_(READ_STATE_NONE), - active_read_id_(0), - read_still_on_call_stack_(false) { - CHECK(connection_.get()); - net_log_.BeginEvent( - NetLog::TYPE_HTTP_PIPELINED_CONNECTION, - base::Bind(&NetLogHostPortPairCallback, &origin)); -} - -HttpPipelinedConnectionImpl::~HttpPipelinedConnectionImpl() { - CHECK_EQ(depth(), 0); - CHECK(stream_info_map_.empty()); - CHECK(pending_send_request_queue_.empty()); - CHECK(request_order_.empty()); - CHECK_EQ(send_next_state_, SEND_STATE_NONE); - CHECK_EQ(read_next_state_, READ_STATE_NONE); - CHECK(!active_send_request_.get()); - CHECK(!active_read_id_); - if (!usable_) { - connection_->socket()->Disconnect(); - } - connection_->Reset(); - net_log_.EndEvent(NetLog::TYPE_HTTP_PIPELINED_CONNECTION); -} - -HttpPipelinedStream* HttpPipelinedConnectionImpl::CreateNewStream() { - int pipeline_id = next_pipeline_id_++; - CHECK(pipeline_id); - HttpPipelinedStream* stream = new HttpPipelinedStream(this, pipeline_id); - stream_info_map_.insert(std::make_pair(pipeline_id, StreamInfo())); - return stream; -} - -void HttpPipelinedConnectionImpl::InitializeParser( - int pipeline_id, - const HttpRequestInfo* request, - const BoundNetLog& net_log) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(!stream_info_map_[pipeline_id].parser.get()); - stream_info_map_[pipeline_id].state = STREAM_BOUND; - stream_info_map_[pipeline_id].parser.reset(new HttpStreamParser( - connection_.get(), request, read_buf_.get(), net_log)); - stream_info_map_[pipeline_id].source = net_log.source(); - - // In case our first stream doesn't SendRequest() immediately, we should still - // allow others to use this pipeline. - if (pipeline_id == 1) { - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&HttpPipelinedConnectionImpl::ActivatePipeline, - weak_factory_.GetWeakPtr())); - } -} - -void HttpPipelinedConnectionImpl::ActivatePipeline() { - if (!active_) { - active_ = true; - delegate_->OnPipelineHasCapacity(this); - } -} - -void HttpPipelinedConnectionImpl::OnStreamDeleted(int pipeline_id) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - Close(pipeline_id, false); - - if (stream_info_map_[pipeline_id].state != STREAM_CREATED && - stream_info_map_[pipeline_id].state != STREAM_UNUSED) { - CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_CLOSED); - CHECK(stream_info_map_[pipeline_id].parser.get()); - stream_info_map_[pipeline_id].parser.reset(); - } - CHECK(!stream_info_map_[pipeline_id].parser.get()); - stream_info_map_.erase(pipeline_id); - - delegate_->OnPipelineHasCapacity(this); -} - -int HttpPipelinedConnectionImpl::SendRequest( - int pipeline_id, - const std::string& request_line, - const HttpRequestHeaders& headers, - HttpResponseInfo* response, - const CompletionCallback& callback) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK_EQ(stream_info_map_[pipeline_id].state, STREAM_BOUND); - if (!usable_) { - return ERR_PIPELINE_EVICTION; - } - - PendingSendRequest* send_request = new PendingSendRequest; - send_request->pipeline_id = pipeline_id; - send_request->request_line = request_line; - send_request->headers = headers; - send_request->response = response; - send_request->callback = callback; - pending_send_request_queue_.push(send_request); - - int rv; - if (send_next_state_ == SEND_STATE_NONE) { - send_next_state_ = SEND_STATE_START_IMMEDIATELY; - rv = DoSendRequestLoop(OK); - } else { - rv = ERR_IO_PENDING; - } - ActivatePipeline(); - return rv; -} - -int HttpPipelinedConnectionImpl::DoSendRequestLoop(int result) { - int rv = result; - do { - SendRequestState state = send_next_state_; - send_next_state_ = SEND_STATE_NONE; - switch (state) { - case SEND_STATE_START_IMMEDIATELY: - rv = DoStartRequestImmediately(rv); - break; - case SEND_STATE_START_NEXT_DEFERRED_REQUEST: - rv = DoStartNextDeferredRequest(rv); - break; - case SEND_STATE_SEND_ACTIVE_REQUEST: - rv = DoSendActiveRequest(rv); - break; - case SEND_STATE_COMPLETE: - rv = DoSendComplete(rv); - break; - case SEND_STATE_EVICT_PENDING_REQUESTS: - rv = DoEvictPendingSendRequests(rv); - break; - default: - CHECK(false) << "bad send state: " << state; - rv = ERR_FAILED; - break; - } - } while (rv != ERR_IO_PENDING && send_next_state_ != SEND_STATE_NONE); - send_still_on_call_stack_ = false; - return rv; -} - -void HttpPipelinedConnectionImpl::OnSendIOCallback(int result) { - CHECK(active_send_request_.get()); - DoSendRequestLoop(result); -} - -int HttpPipelinedConnectionImpl::DoStartRequestImmediately(int result) { - CHECK(!active_send_request_.get()); - CHECK_EQ(static_cast<size_t>(1), pending_send_request_queue_.size()); - // If SendRequest() completes synchronously, then we need to return the value - // directly to the caller. |send_still_on_call_stack_| will track this. - // Otherwise, asynchronous completions will notify the caller via callback. - send_still_on_call_stack_ = true; - active_send_request_.reset(pending_send_request_queue_.front()); - pending_send_request_queue_.pop(); - send_next_state_ = SEND_STATE_SEND_ACTIVE_REQUEST; - return OK; -} - -int HttpPipelinedConnectionImpl::DoStartNextDeferredRequest(int result) { - CHECK(!send_still_on_call_stack_); - CHECK(!active_send_request_.get()); - - while (!pending_send_request_queue_.empty()) { - scoped_ptr<PendingSendRequest> next_request( - pending_send_request_queue_.front()); - pending_send_request_queue_.pop(); - CHECK(ContainsKey(stream_info_map_, next_request->pipeline_id)); - if (stream_info_map_[next_request->pipeline_id].state != STREAM_CLOSED) { - active_send_request_.reset(next_request.release()); - send_next_state_ = SEND_STATE_SEND_ACTIVE_REQUEST; - return OK; - } - } - - send_next_state_ = SEND_STATE_NONE; - return OK; -} - -int HttpPipelinedConnectionImpl::DoSendActiveRequest(int result) { - CHECK(stream_info_map_[active_send_request_->pipeline_id].parser.get()); - int rv = stream_info_map_[active_send_request_->pipeline_id].parser-> - SendRequest(active_send_request_->request_line, - active_send_request_->headers, - active_send_request_->response, - base::Bind(&HttpPipelinedConnectionImpl::OnSendIOCallback, - base::Unretained(this))); - stream_info_map_[active_send_request_->pipeline_id].state = STREAM_SENDING; - send_next_state_ = SEND_STATE_COMPLETE; - return rv; -} - -int HttpPipelinedConnectionImpl::DoSendComplete(int result) { - CHECK(active_send_request_.get()); - CHECK_EQ(STREAM_SENDING, - stream_info_map_[active_send_request_->pipeline_id].state); - - request_order_.push(active_send_request_->pipeline_id); - stream_info_map_[active_send_request_->pipeline_id].state = STREAM_SENT; - net_log_.AddEvent( - NetLog::TYPE_HTTP_PIPELINED_CONNECTION_SENT_REQUEST, - stream_info_map_[active_send_request_->pipeline_id].source. - ToEventParametersCallback()); - - if (result == ERR_SOCKET_NOT_CONNECTED && completed_one_request_) { - result = ERR_PIPELINE_EVICTION; - } - if (result < OK) { - usable_ = false; - } - - if (!send_still_on_call_stack_) { - QueueUserCallback(active_send_request_->pipeline_id, - active_send_request_->callback, result, FROM_HERE); - } - - active_send_request_.reset(); - - if (send_still_on_call_stack_) { - // It should be impossible for another request to appear on the queue while - // this send was on the call stack. - CHECK(pending_send_request_queue_.empty()); - send_next_state_ = SEND_STATE_NONE; - } else if (!usable_) { - send_next_state_ = SEND_STATE_EVICT_PENDING_REQUESTS; - } else { - send_next_state_ = SEND_STATE_START_NEXT_DEFERRED_REQUEST; - } - - return result; -} - -int HttpPipelinedConnectionImpl::DoEvictPendingSendRequests(int result) { - while (!pending_send_request_queue_.empty()) { - scoped_ptr<PendingSendRequest> evicted_send( - pending_send_request_queue_.front()); - pending_send_request_queue_.pop(); - if (ContainsKey(stream_info_map_, evicted_send->pipeline_id) && - stream_info_map_[evicted_send->pipeline_id].state != STREAM_CLOSED) { - evicted_send->callback.Run(ERR_PIPELINE_EVICTION); - } - } - send_next_state_ = SEND_STATE_NONE; - return result; -} - -int HttpPipelinedConnectionImpl::ReadResponseHeaders( - int pipeline_id, const CompletionCallback& callback) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK_EQ(STREAM_SENT, stream_info_map_[pipeline_id].state); - CHECK(stream_info_map_[pipeline_id].read_headers_callback.is_null()); - - if (!usable_) - return ERR_PIPELINE_EVICTION; - - stream_info_map_[pipeline_id].state = STREAM_READ_PENDING; - stream_info_map_[pipeline_id].read_headers_callback = callback; - if (read_next_state_ == READ_STATE_NONE && - pipeline_id == request_order_.front()) { - read_next_state_ = READ_STATE_START_IMMEDIATELY; - return DoReadHeadersLoop(OK); - } - return ERR_IO_PENDING; -} - -void HttpPipelinedConnectionImpl::StartNextDeferredRead() { - if (read_next_state_ == READ_STATE_NONE) { - read_next_state_ = READ_STATE_START_NEXT_DEFERRED_READ; - DoReadHeadersLoop(OK); - } -} - -int HttpPipelinedConnectionImpl::DoReadHeadersLoop(int result) { - int rv = result; - do { - ReadHeadersState state = read_next_state_; - read_next_state_ = READ_STATE_NONE; - switch (state) { - case READ_STATE_START_IMMEDIATELY: - rv = DoStartReadImmediately(rv); - break; - case READ_STATE_START_NEXT_DEFERRED_READ: - rv = DoStartNextDeferredRead(rv); - break; - case READ_STATE_READ_HEADERS: - rv = DoReadHeaders(rv); - break; - case READ_STATE_READ_HEADERS_COMPLETE: - rv = DoReadHeadersComplete(rv); - break; - case READ_STATE_WAITING_FOR_CLOSE: - // This is a holding state. We return instead of continuing to run hte - // loop. The state will advance when the stream calls Close(). - rv = DoReadWaitForClose(rv); - read_still_on_call_stack_ = false; - return rv; - case READ_STATE_STREAM_CLOSED: - rv = DoReadStreamClosed(); - break; - case READ_STATE_EVICT_PENDING_READS: - rv = DoEvictPendingReadHeaders(rv); - break; - case READ_STATE_NONE: - break; - default: - CHECK(false) << "bad read state"; - rv = ERR_FAILED; - break; - } - } while (rv != ERR_IO_PENDING && read_next_state_ != READ_STATE_NONE); - read_still_on_call_stack_ = false; - return rv; -} - -void HttpPipelinedConnectionImpl::OnReadIOCallback(int result) { - DoReadHeadersLoop(result); -} - -int HttpPipelinedConnectionImpl::DoStartReadImmediately(int result) { - CHECK(!active_read_id_); - CHECK(!read_still_on_call_stack_); - CHECK(!request_order_.empty()); - // If ReadResponseHeaders() completes synchronously, then we need to return - // the value directly to the caller. |read_still_on_call_stack_| will track - // this. Otherwise, asynchronous completions will notify the caller via - // callback. - read_still_on_call_stack_ = true; - read_next_state_ = READ_STATE_READ_HEADERS; - active_read_id_ = request_order_.front(); - request_order_.pop(); - return OK; -} - -int HttpPipelinedConnectionImpl::DoStartNextDeferredRead(int result) { - CHECK(!active_read_id_); - CHECK(!read_still_on_call_stack_); - - if (request_order_.empty()) { - read_next_state_ = READ_STATE_NONE; - return OK; - } - - int next_id = request_order_.front(); - CHECK(ContainsKey(stream_info_map_, next_id)); - switch (stream_info_map_[next_id].state) { - case STREAM_READ_PENDING: - read_next_state_ = READ_STATE_READ_HEADERS; - active_read_id_ = next_id; - request_order_.pop(); - break; - - case STREAM_CLOSED: - // Since nobody will read whatever data is on the pipeline associated with - // this closed request, we must shut down the rest of the pipeline. - read_next_state_ = READ_STATE_EVICT_PENDING_READS; - break; - - case STREAM_SENT: - read_next_state_ = READ_STATE_NONE; - break; - - default: - CHECK(false) << "Unexpected read state: " - << stream_info_map_[next_id].state; - } - - return OK; -} - -int HttpPipelinedConnectionImpl::DoReadHeaders(int result) { - CHECK(active_read_id_); - CHECK(ContainsKey(stream_info_map_, active_read_id_)); - CHECK_EQ(STREAM_READ_PENDING, stream_info_map_[active_read_id_].state); - stream_info_map_[active_read_id_].state = STREAM_ACTIVE; - int rv = stream_info_map_[active_read_id_].parser->ReadResponseHeaders( - base::Bind(&HttpPipelinedConnectionImpl::OnReadIOCallback, - base::Unretained(this))); - read_next_state_ = READ_STATE_READ_HEADERS_COMPLETE; - return rv; -} - -int HttpPipelinedConnectionImpl::DoReadHeadersComplete(int result) { - CHECK(active_read_id_); - CHECK(ContainsKey(stream_info_map_, active_read_id_)); - CHECK_EQ(STREAM_ACTIVE, stream_info_map_[active_read_id_].state); - - read_next_state_ = READ_STATE_WAITING_FOR_CLOSE; - if (result < OK) { - if (completed_one_request_ && - (result == ERR_CONNECTION_CLOSED || - result == ERR_EMPTY_RESPONSE || - result == ERR_SOCKET_NOT_CONNECTED)) { - // These usually indicate that pipelining failed on the server side. In - // that case, we should retry without pipelining. - result = ERR_PIPELINE_EVICTION; - } - usable_ = false; - } - - CheckHeadersForPipelineCompatibility(active_read_id_, result); - - if (!read_still_on_call_stack_) { - QueueUserCallback(active_read_id_, - stream_info_map_[active_read_id_].read_headers_callback, - result, FROM_HERE); - } - - return result; -} - -int HttpPipelinedConnectionImpl::DoReadWaitForClose(int result) { - read_next_state_ = READ_STATE_WAITING_FOR_CLOSE; - return result; -} - -int HttpPipelinedConnectionImpl::DoReadStreamClosed() { - CHECK(active_read_id_); - CHECK(ContainsKey(stream_info_map_, active_read_id_)); - CHECK_EQ(stream_info_map_[active_read_id_].state, STREAM_CLOSED); - active_read_id_ = 0; - if (!usable_) { - // TODO(simonjam): Don't wait this long to evict. - read_next_state_ = READ_STATE_EVICT_PENDING_READS; - return OK; - } - completed_one_request_ = true; - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&HttpPipelinedConnectionImpl::StartNextDeferredRead, - weak_factory_.GetWeakPtr())); - read_next_state_ = READ_STATE_NONE; - return OK; -} - -int HttpPipelinedConnectionImpl::DoEvictPendingReadHeaders(int result) { - while (!request_order_.empty()) { - int evicted_id = request_order_.front(); - request_order_.pop(); - if (!ContainsKey(stream_info_map_, evicted_id)) { - continue; - } - if (stream_info_map_[evicted_id].state == STREAM_READ_PENDING) { - stream_info_map_[evicted_id].state = STREAM_READ_EVICTED; - stream_info_map_[evicted_id].read_headers_callback.Run( - ERR_PIPELINE_EVICTION); - } - } - read_next_state_ = READ_STATE_NONE; - return result; -} - -void HttpPipelinedConnectionImpl::Close(int pipeline_id, - bool not_reusable) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - net_log_.AddEvent( - NetLog::TYPE_HTTP_PIPELINED_CONNECTION_STREAM_CLOSED, - base::Bind(&NetLogStreamClosedCallback, - stream_info_map_[pipeline_id].source, not_reusable)); - switch (stream_info_map_[pipeline_id].state) { - case STREAM_CREATED: - stream_info_map_[pipeline_id].state = STREAM_UNUSED; - break; - - case STREAM_BOUND: - stream_info_map_[pipeline_id].state = STREAM_CLOSED; - break; - - case STREAM_SENDING: - usable_ = false; - stream_info_map_[pipeline_id].state = STREAM_CLOSED; - active_send_request_.reset(); - send_next_state_ = SEND_STATE_EVICT_PENDING_REQUESTS; - DoSendRequestLoop(OK); - break; - - case STREAM_SENT: - case STREAM_READ_PENDING: - usable_ = false; - stream_info_map_[pipeline_id].state = STREAM_CLOSED; - if (!request_order_.empty() && - pipeline_id == request_order_.front() && - read_next_state_ == READ_STATE_NONE) { - read_next_state_ = READ_STATE_EVICT_PENDING_READS; - DoReadHeadersLoop(OK); - } - break; - - case STREAM_ACTIVE: - stream_info_map_[pipeline_id].state = STREAM_CLOSED; - if (not_reusable) { - usable_ = false; - } - read_next_state_ = READ_STATE_STREAM_CLOSED; - DoReadHeadersLoop(OK); - break; - - case STREAM_READ_EVICTED: - stream_info_map_[pipeline_id].state = STREAM_CLOSED; - break; - - case STREAM_CLOSED: - case STREAM_UNUSED: - // TODO(simonjam): Why is Close() sometimes called twice? - break; - - default: - CHECK(false); - break; - } -} - -int HttpPipelinedConnectionImpl::ReadResponseBody( - int pipeline_id, IOBuffer* buf, int buf_len, - const CompletionCallback& callback) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK_EQ(active_read_id_, pipeline_id); - CHECK(stream_info_map_[pipeline_id].parser.get()); - return stream_info_map_[pipeline_id].parser->ReadResponseBody( - buf, buf_len, callback); -} - -UploadProgress HttpPipelinedConnectionImpl::GetUploadProgress( - int pipeline_id) const { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); - return stream_info_map_.find(pipeline_id)->second.parser->GetUploadProgress(); -} - -HttpResponseInfo* HttpPipelinedConnectionImpl::GetResponseInfo( - int pipeline_id) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); - return stream_info_map_.find(pipeline_id)->second.parser->GetResponseInfo(); -} - -bool HttpPipelinedConnectionImpl::IsResponseBodyComplete( - int pipeline_id) const { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); - return stream_info_map_.find(pipeline_id)->second.parser-> - IsResponseBodyComplete(); -} - -bool HttpPipelinedConnectionImpl::CanFindEndOfResponse(int pipeline_id) const { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); - return stream_info_map_.find(pipeline_id)->second.parser-> - CanFindEndOfResponse(); -} - -bool HttpPipelinedConnectionImpl::IsConnectionReused(int pipeline_id) const { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - if (pipeline_id > 1) { - return true; - } - ClientSocketHandle::SocketReuseType reuse_type = connection_->reuse_type(); - return connection_->is_reused() || - reuse_type == ClientSocketHandle::UNUSED_IDLE; -} - -void HttpPipelinedConnectionImpl::SetConnectionReused(int pipeline_id) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - connection_->set_is_reused(true); -} - -int64 HttpPipelinedConnectionImpl::GetTotalReceivedBytes( - int pipeline_id) const { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(stream_info_map_.find(pipeline_id)->second.parser.get()); - return stream_info_map_.find(pipeline_id)->second.parser->received_bytes(); -} - -bool HttpPipelinedConnectionImpl::GetLoadTimingInfo( - int pipeline_id, LoadTimingInfo* load_timing_info) const { - return connection_->GetLoadTimingInfo(IsConnectionReused(pipeline_id), - load_timing_info); -} - -void HttpPipelinedConnectionImpl::GetSSLInfo(int pipeline_id, - SSLInfo* ssl_info) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(stream_info_map_[pipeline_id].parser.get()); - stream_info_map_[pipeline_id].parser->GetSSLInfo(ssl_info); -} - -void HttpPipelinedConnectionImpl::GetSSLCertRequestInfo( - int pipeline_id, - SSLCertRequestInfo* cert_request_info) { - CHECK(ContainsKey(stream_info_map_, pipeline_id)); - CHECK(stream_info_map_[pipeline_id].parser.get()); - stream_info_map_[pipeline_id].parser->GetSSLCertRequestInfo( - cert_request_info); -} - -void HttpPipelinedConnectionImpl::Drain(HttpPipelinedStream* stream, - HttpNetworkSession* session) { - HttpResponseHeaders* headers = stream->GetResponseInfo()->headers.get(); - if (!stream->CanFindEndOfResponse() || headers->IsChunkEncoded() || - !usable_) { - // TODO(simonjam): Drain chunk-encoded responses if they're relatively - // common. - stream->Close(true); - delete stream; - return; - } - HttpResponseBodyDrainer* drainer = new HttpResponseBodyDrainer(stream); - drainer->StartWithSize(session, headers->GetContentLength()); - // |drainer| will delete itself when done. -} - -void HttpPipelinedConnectionImpl::CheckHeadersForPipelineCompatibility( - int pipeline_id, - int result) { - if (result < OK) { - switch (result) { - // TODO(simonjam): Ignoring specific errors like this may not work. - // Collect metrics to see if this code is useful. - case ERR_ABORTED: - case ERR_INTERNET_DISCONNECTED: - case ERR_NETWORK_CHANGED: - // These errors are no fault of the server. - break; - - default: - ReportPipelineFeedback(pipeline_id, PIPELINE_SOCKET_ERROR); - break; - } - return; - } - HttpResponseInfo* info = GetResponseInfo(pipeline_id); - const HttpVersion required_version(1, 1); - if (info->headers->GetParsedHttpVersion() < required_version) { - ReportPipelineFeedback(pipeline_id, OLD_HTTP_VERSION); - return; - } - if (!info->headers->IsKeepAlive() || !CanFindEndOfResponse(pipeline_id)) { - usable_ = false; - ReportPipelineFeedback(pipeline_id, MUST_CLOSE_CONNECTION); - return; - } - if (info->headers->HasHeader( - HttpAuth::GetChallengeHeaderName(HttpAuth::AUTH_SERVER))) { - ReportPipelineFeedback(pipeline_id, AUTHENTICATION_REQUIRED); - return; - } - ReportPipelineFeedback(pipeline_id, OK); -} - -void HttpPipelinedConnectionImpl::ReportPipelineFeedback(int pipeline_id, - Feedback feedback) { - std::string feedback_str; - switch (feedback) { - case OK: - feedback_str = "OK"; - break; - - case PIPELINE_SOCKET_ERROR: - feedback_str = "PIPELINE_SOCKET_ERROR"; - break; - - case OLD_HTTP_VERSION: - feedback_str = "OLD_HTTP_VERSION"; - break; - - case MUST_CLOSE_CONNECTION: - feedback_str = "MUST_CLOSE_CONNECTION"; - break; - - case AUTHENTICATION_REQUIRED: - feedback_str = "AUTHENTICATION_REQUIRED"; - break; - - default: - NOTREACHED(); - feedback_str = "UNKNOWN"; - break; - } - net_log_.AddEvent( - NetLog::TYPE_HTTP_PIPELINED_CONNECTION_RECEIVED_HEADERS, - base::Bind(&NetLogReceivedHeadersCallback, - stream_info_map_[pipeline_id].source, &feedback_str)); - delegate_->OnPipelineFeedback(this, feedback); -} - -void HttpPipelinedConnectionImpl::QueueUserCallback( - int pipeline_id, const CompletionCallback& callback, int rv, - const tracked_objects::Location& from_here) { - CHECK(stream_info_map_[pipeline_id].pending_user_callback.is_null()); - stream_info_map_[pipeline_id].pending_user_callback = callback; - base::MessageLoop::current()->PostTask( - from_here, - base::Bind(&HttpPipelinedConnectionImpl::FireUserCallback, - weak_factory_.GetWeakPtr(), pipeline_id, rv)); -} - -void HttpPipelinedConnectionImpl::FireUserCallback(int pipeline_id, - int result) { - if (ContainsKey(stream_info_map_, pipeline_id)) { - CHECK(!stream_info_map_[pipeline_id].pending_user_callback.is_null()); - CompletionCallback callback = - stream_info_map_[pipeline_id].pending_user_callback; - stream_info_map_[pipeline_id].pending_user_callback.Reset(); - callback.Run(result); - } -} - -int HttpPipelinedConnectionImpl::depth() const { - return stream_info_map_.size(); -} - -bool HttpPipelinedConnectionImpl::usable() const { - return usable_; -} - -bool HttpPipelinedConnectionImpl::active() const { - return active_; -} - -const SSLConfig& HttpPipelinedConnectionImpl::used_ssl_config() const { - return used_ssl_config_; -} - -const ProxyInfo& HttpPipelinedConnectionImpl::used_proxy_info() const { - return used_proxy_info_; -} - -const BoundNetLog& HttpPipelinedConnectionImpl::net_log() const { - return net_log_; -} - -bool HttpPipelinedConnectionImpl::was_npn_negotiated() const { - return was_npn_negotiated_; -} - -NextProto HttpPipelinedConnectionImpl::protocol_negotiated() - const { - return protocol_negotiated_; -} - -HttpPipelinedConnectionImpl::PendingSendRequest::PendingSendRequest() - : pipeline_id(0), - response(NULL) { -} - -HttpPipelinedConnectionImpl::PendingSendRequest::~PendingSendRequest() { -} - -HttpPipelinedConnectionImpl::StreamInfo::StreamInfo() - : state(STREAM_CREATED) { -} - -HttpPipelinedConnectionImpl::StreamInfo::~StreamInfo() { -} - -} // namespace net diff --git a/chromium/net/http/http_pipelined_connection_impl.h b/chromium/net/http/http_pipelined_connection_impl.h deleted file mode 100644 index d558e47eb0b..00000000000 --- a/chromium/net/http/http_pipelined_connection_impl.h +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_CONNECTION_IMPL_H_ -#define NET_HTTP_HTTP_PIPELINED_CONNECTION_IMPL_H_ - -#include <map> -#include <queue> -#include <string> - -#include "base/basictypes.h" -#include "base/location.h" -#include "base/memory/linked_ptr.h" -#include "base/memory/weak_ptr.h" -#include "net/base/completion_callback.h" -#include "net/base/net_export.h" -#include "net/base/net_log.h" -#include "net/http/http_pipelined_connection.h" -#include "net/http/http_request_info.h" -#include "net/http/http_stream_parser.h" -#include "net/proxy/proxy_info.h" -#include "net/ssl/ssl_config_service.h" - -namespace net { - -class ClientSocketHandle; -class GrowableIOBuffer; -class HostPortPair; -class HttpNetworkSession; -class HttpRequestHeaders; -class HttpResponseInfo; -class IOBuffer; -struct LoadTimingInfo; -class SSLCertRequestInfo; -class SSLInfo; - -// This class manages all of the state for a single pipelined connection. It -// tracks the order that HTTP requests are sent and enforces that the -// subsequent reads occur in the appropriate order. -// -// If an error occurs related to pipelining, ERR_PIPELINE_EVICTION will be -// returned to the client. This indicates the client should retry the request -// without pipelining. -class NET_EXPORT_PRIVATE HttpPipelinedConnectionImpl - : public HttpPipelinedConnection { - public: - class Factory : public HttpPipelinedConnection::Factory { - public: - virtual HttpPipelinedConnection* CreateNewPipeline( - ClientSocketHandle* connection, - HttpPipelinedConnection::Delegate* delegate, - const HostPortPair& origin, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) OVERRIDE; - }; - - HttpPipelinedConnectionImpl(ClientSocketHandle* connection, - Delegate* delegate, - const HostPortPair& origin, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated); - virtual ~HttpPipelinedConnectionImpl(); - - // HttpPipelinedConnection interface. - - // Used by HttpStreamFactoryImpl and friends. - virtual HttpPipelinedStream* CreateNewStream() OVERRIDE; - - // Used by HttpPipelinedHost. - virtual int depth() const OVERRIDE; - virtual bool usable() const OVERRIDE; - virtual bool active() const OVERRIDE; - - // Used by HttpStreamFactoryImpl. - virtual const SSLConfig& used_ssl_config() const OVERRIDE; - virtual const ProxyInfo& used_proxy_info() const OVERRIDE; - virtual const BoundNetLog& net_log() const OVERRIDE; - virtual bool was_npn_negotiated() const OVERRIDE; - virtual NextProto protocol_negotiated() const OVERRIDE; - - // Used by HttpPipelinedStream. - - // Notifies this pipeline that a stream is no longer using it. - void OnStreamDeleted(int pipeline_id); - - // Effective implementation of HttpStream. Note that we don't directly - // implement that interface. Instead, these functions will be called by the - // pass-through methods in HttpPipelinedStream. - void InitializeParser(int pipeline_id, - const HttpRequestInfo* request, - const BoundNetLog& net_log); - - int SendRequest(int pipeline_id, - const std::string& request_line, - const HttpRequestHeaders& headers, - HttpResponseInfo* response, - const CompletionCallback& callback); - - int ReadResponseHeaders(int pipeline_id, - const CompletionCallback& callback); - - int ReadResponseBody(int pipeline_id, - IOBuffer* buf, int buf_len, - const CompletionCallback& callback); - - void Close(int pipeline_id, - bool not_reusable); - - UploadProgress GetUploadProgress(int pipeline_id) const; - - HttpResponseInfo* GetResponseInfo(int pipeline_id); - - bool IsResponseBodyComplete(int pipeline_id) const; - - bool CanFindEndOfResponse(int pipeline_id) const; - - bool IsConnectionReused(int pipeline_id) const; - - void SetConnectionReused(int pipeline_id); - - int64 GetTotalReceivedBytes(int pipeline_id) const; - - bool GetLoadTimingInfo(int pipeline_id, - LoadTimingInfo* load_timing_info) const; - - void GetSSLInfo(int pipeline_id, SSLInfo* ssl_info); - - void GetSSLCertRequestInfo(int pipeline_id, - SSLCertRequestInfo* cert_request_info); - - // Attempts to drain the response body for |stream| so that the pipeline may - // be reused. - void Drain(HttpPipelinedStream* stream, HttpNetworkSession* session); - - private: - enum StreamState { - STREAM_CREATED, - STREAM_BOUND, - STREAM_SENDING, - STREAM_SENT, - STREAM_READ_PENDING, - STREAM_ACTIVE, - STREAM_CLOSED, - STREAM_READ_EVICTED, - STREAM_UNUSED, - }; - enum SendRequestState { - SEND_STATE_START_IMMEDIATELY, - SEND_STATE_START_NEXT_DEFERRED_REQUEST, - SEND_STATE_SEND_ACTIVE_REQUEST, - SEND_STATE_COMPLETE, - SEND_STATE_EVICT_PENDING_REQUESTS, - SEND_STATE_NONE, - }; - enum ReadHeadersState { - READ_STATE_START_IMMEDIATELY, - READ_STATE_START_NEXT_DEFERRED_READ, - READ_STATE_READ_HEADERS, - READ_STATE_READ_HEADERS_COMPLETE, - READ_STATE_WAITING_FOR_CLOSE, - READ_STATE_STREAM_CLOSED, - READ_STATE_NONE, - READ_STATE_EVICT_PENDING_READS, - }; - - struct PendingSendRequest { - PendingSendRequest(); - ~PendingSendRequest(); - - int pipeline_id; - std::string request_line; - HttpRequestHeaders headers; - HttpResponseInfo* response; - CompletionCallback callback; - }; - - struct StreamInfo { - StreamInfo(); - ~StreamInfo(); - - linked_ptr<HttpStreamParser> parser; - CompletionCallback read_headers_callback; - CompletionCallback pending_user_callback; - StreamState state; - NetLog::Source source; - }; - - typedef std::map<int, StreamInfo> StreamInfoMap; - - // Called after the first request is sent or in a task sometime after the - // first stream is added to this pipeline. This gives the first request - // priority to send, but doesn't hold up other requests if it doesn't. - // When called the first time, notifies the |delegate_| that we can accept new - // requests. - void ActivatePipeline(); - - // Responsible for sending one request at a time and waiting until each - // comepletes. - int DoSendRequestLoop(int result); - - // Called when an asynchronous Send() completes. - void OnSendIOCallback(int result); - - // Activates the only request in |pending_send_request_queue_|. This should - // only be called via SendRequest() when the send loop is idle. - int DoStartRequestImmediately(int result); - - // Activates the first request in |pending_send_request_queue_| that hasn't - // been closed, if any. This is called via DoSendComplete() after a prior - // request complets. - int DoStartNextDeferredRequest(int result); - - // Sends the active request. - int DoSendActiveRequest(int result); - - // Notifies the user that the send has completed. This may be called directly - // after SendRequest() for a synchronous request, or it may be called in - // response to OnSendIOCallback for an asynchronous request. - int DoSendComplete(int result); - - // Evicts all unsent deferred requests. This is called if there is a Send() - // error or one of our streams informs us the connection is no longer - // reusable. - int DoEvictPendingSendRequests(int result); - - // Ensures that only the active request's HttpPipelinedSocket can read from - // the underlying socket until it completes. A HttpPipelinedSocket informs us - // that it's done by calling Close(). - int DoReadHeadersLoop(int result); - - // Called when the pending asynchronous ReadResponseHeaders() completes. - void OnReadIOCallback(int result); - - // Invokes DoStartNextDeferredRead() if the read loop is idle. This is called - // via a task queued when the previous |active_read_id_| closes its stream - // after a succesful response. - void StartNextDeferredRead(); - - // Activates the next read request immediately. This is called via - // ReadResponseHeaders() if that stream is at the front of |request_order_| - // and the read loop is idle. - int DoStartReadImmediately(int result); - - // Activates the next read request in |request_order_| if it's ready to go. - // This is called via StartNextDeferredRead(). - int DoStartNextDeferredRead(int result); - - // Calls ReadResponseHeaders() on the active request's parser. - int DoReadHeaders(int result); - - // Notifies the user that reading the headers has completed. This may happen - // directly after DoReadNextHeaders() if the response is already available. - // Otherwise, it is called in response to OnReadIOCallback(). - int DoReadHeadersComplete(int result); - - // Halts the read loop until Close() is called by the active stream. - int DoReadWaitForClose(int result); - - // Cleans up the state associated with the active request. Invokes - // DoReadNextHeaders() in a new task to start the next response. This is - // called after the active request's HttpPipelinedSocket calls Close(). - int DoReadStreamClosed(); - - // Removes all pending ReadResponseHeaders() requests from the queue. This may - // happen if there is an error with the pipeline or one of our - // HttpPipelinedSockets indicates the connection was suddenly closed. - int DoEvictPendingReadHeaders(int result); - - // Determines if the response headers indicate pipelining will work. This is - // called every time we receive headers. - void CheckHeadersForPipelineCompatibility(int pipeline_id, int result); - - // Reports back to |delegate_| whether pipelining will work. - void ReportPipelineFeedback(int pipeline_id, Feedback feedback); - - // Posts a task to fire the user's callback in response to SendRequest() or - // ReadResponseHeaders() completing on an underlying parser. This might be - // invoked in response to our own IO callbacks, or it may be invoked if the - // underlying parser completes SendRequest() or ReadResponseHeaders() - // synchronously, but we've already returned ERR_IO_PENDING to the user's - // SendRequest() or ReadResponseHeaders() call into us. - void QueueUserCallback(int pipeline_id, - const CompletionCallback& callback, - int rv, - const tracked_objects::Location& from_here); - - // Invokes the callback queued in QueueUserCallback(). - void FireUserCallback(int pipeline_id, int result); - - Delegate* delegate_; - scoped_ptr<ClientSocketHandle> connection_; - SSLConfig used_ssl_config_; - ProxyInfo used_proxy_info_; - BoundNetLog net_log_; - bool was_npn_negotiated_; - // Protocol negotiated with the server. - NextProto protocol_negotiated_; - scoped_refptr<GrowableIOBuffer> read_buf_; - int next_pipeline_id_; - bool active_; - bool usable_; - bool completed_one_request_; - base::WeakPtrFactory<HttpPipelinedConnectionImpl> weak_factory_; - - StreamInfoMap stream_info_map_; - - std::queue<int> request_order_; - - std::queue<PendingSendRequest*> pending_send_request_queue_; - scoped_ptr<PendingSendRequest> active_send_request_; - SendRequestState send_next_state_; - bool send_still_on_call_stack_; - - ReadHeadersState read_next_state_; - int active_read_id_; - bool read_still_on_call_stack_; - - DISALLOW_COPY_AND_ASSIGN(HttpPipelinedConnectionImpl); -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_CONNECTION_IMPL_H_ diff --git a/chromium/net/http/http_pipelined_connection_impl_unittest.cc b/chromium/net/http/http_pipelined_connection_impl_unittest.cc deleted file mode 100644 index 296194ecd63..00000000000 --- a/chromium/net/http/http_pipelined_connection_impl_unittest.cc +++ /dev/null @@ -1,1597 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_connection_impl.h" - -#include <string> - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "net/base/capturing_net_log.h" -#include "net/base/io_buffer.h" -#include "net/base/load_timing_info.h" -#include "net/base/load_timing_info_test_util.h" -#include "net/base/net_errors.h" -#include "net/base/request_priority.h" -#include "net/http/http_pipelined_stream.h" -#include "net/socket/client_socket_handle.h" -#include "net/socket/client_socket_pool_histograms.h" -#include "net/socket/socket_test_util.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using testing::NiceMock; -using testing::StrEq; - -namespace net { - -namespace { - -// Tests the load timing of a stream that's connected and is not the first -// request sent on a connection. -void TestLoadTimingReused(const HttpStream& stream) { - LoadTimingInfo load_timing_info; - EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); - - EXPECT_TRUE(load_timing_info.socket_reused); - EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); - - ExpectConnectTimingHasNoTimes(load_timing_info.connect_timing); - ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); -} - -// Tests the load timing of a stream that's connected and using a fresh -// connection. -void TestLoadTimingNotReused(const HttpStream& stream) { - LoadTimingInfo load_timing_info; - EXPECT_TRUE(stream.GetLoadTimingInfo(&load_timing_info)); - - EXPECT_FALSE(load_timing_info.socket_reused); - EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); - - ExpectConnectTimingHasTimes(load_timing_info.connect_timing, - CONNECT_TIMING_HAS_DNS_TIMES); - ExpectLoadTimingHasOnlyConnectionTimes(load_timing_info); -} - -class MockPipelineDelegate : public HttpPipelinedConnection::Delegate { - public: - MOCK_METHOD1(OnPipelineHasCapacity, void(HttpPipelinedConnection* pipeline)); - MOCK_METHOD2(OnPipelineFeedback, void( - HttpPipelinedConnection* pipeline, - HttpPipelinedConnection::Feedback feedback)); -}; - -class SuddenCloseObserver : public base::MessageLoop::TaskObserver { - public: - SuddenCloseObserver(HttpStream* stream, int close_before_task) - : stream_(stream), - close_before_task_(close_before_task), - current_task_(0) { } - - virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE { - ++current_task_; - if (current_task_ == close_before_task_) { - stream_->Close(false); - base::MessageLoop::current()->RemoveTaskObserver(this); - } - } - - virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {} - - private: - HttpStream* stream_; - int close_before_task_; - int current_task_; -}; - -class HttpPipelinedConnectionImplTest : public testing::Test { - public: - HttpPipelinedConnectionImplTest() - : histograms_("a"), - pool_(1, 1, &histograms_, &factory_), - origin_("host", 123) { - } - - void TearDown() { - base::MessageLoop::current()->RunUntilIdle(); - } - - void Initialize(MockRead* reads, size_t reads_count, - MockWrite* writes, size_t writes_count) { - data_.reset(new DeterministicSocketData(reads, reads_count, - writes, writes_count)); - data_->set_connect_data(MockConnect(SYNCHRONOUS, OK)); - if (reads_count || writes_count) { - data_->StopAfter(reads_count + writes_count); - } - factory_.AddSocketDataProvider(data_.get()); - scoped_refptr<MockTransportSocketParams> params; - ClientSocketHandle* connection = new ClientSocketHandle; - // Only give the connection a real NetLog to make sure that LoadTiming uses - // the connection's ID, rather than the pipeline's. Since pipelines are - // destroyed when they've responded to all requests, but the connection - // lives on, this is an important behavior. - connection->Init("a", params, MEDIUM, CompletionCallback(), &pool_, - net_log_.bound()); - pipeline_.reset(new HttpPipelinedConnectionImpl( - connection, &delegate_, origin_, ssl_config_, proxy_info_, - BoundNetLog(), false, kProtoUnknown)); - } - - HttpRequestInfo* GetRequestInfo(const std::string& filename) { - HttpRequestInfo* request_info = new HttpRequestInfo; - request_info->url = GURL("http://localhost/" + filename); - request_info->method = "GET"; - request_info_vector_.push_back(request_info); - return request_info; - } - - HttpStream* NewTestStream(const std::string& filename) { - HttpStream* stream = pipeline_->CreateNewStream(); - HttpRequestInfo* request_info = GetRequestInfo(filename); - int rv = stream->InitializeStream( - request_info, DEFAULT_PRIORITY, BoundNetLog(), CompletionCallback()); - DCHECK_EQ(OK, rv); - return stream; - } - - void ExpectResponse(const std::string& expected, - scoped_ptr<HttpStream>& stream, bool async) { - scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size())); - - if (async) { - EXPECT_EQ(ERR_IO_PENDING, - stream->ReadResponseBody(buffer.get(), expected.size(), - callback_.callback())); - data_->RunFor(1); - EXPECT_EQ(static_cast<int>(expected.size()), callback_.WaitForResult()); - } else { - EXPECT_EQ(static_cast<int>(expected.size()), - stream->ReadResponseBody(buffer.get(), expected.size(), - callback_.callback())); - } - std::string actual(buffer->data(), expected.size()); - EXPECT_THAT(actual, StrEq(expected)); - } - - void TestSyncRequest(scoped_ptr<HttpStream>& stream, - const std::string& filename) { - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, stream->SendRequest(headers, &response, - callback_.callback())); - EXPECT_EQ(OK, stream->ReadResponseHeaders(callback_.callback())); - ExpectResponse(filename, stream, false); - - stream->Close(false); - } - - CapturingBoundNetLog net_log_; - DeterministicMockClientSocketFactory factory_; - ClientSocketPoolHistograms histograms_; - MockTransportClientSocketPool pool_; - scoped_ptr<DeterministicSocketData> data_; - - HostPortPair origin_; - SSLConfig ssl_config_; - ProxyInfo proxy_info_; - NiceMock<MockPipelineDelegate> delegate_; - TestCompletionCallback callback_; - scoped_ptr<HttpPipelinedConnectionImpl> pipeline_; - ScopedVector<HttpRequestInfo> request_info_vector_; -}; - -TEST_F(HttpPipelinedConnectionImplTest, PipelineNotUsed) { - Initialize(NULL, 0, NULL, 0); -} - -TEST_F(HttpPipelinedConnectionImplTest, StreamNotUsed) { - Initialize(NULL, 0, NULL, 0); - - scoped_ptr<HttpStream> stream(pipeline_->CreateNewStream()); - - stream->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, StreamBoundButNotUsed) { - Initialize(NULL, 0, NULL, 0); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - - TestLoadTimingNotReused(*stream); - stream->Close(false); - TestLoadTimingNotReused(*stream); -} - -TEST_F(HttpPipelinedConnectionImplTest, SyncSingleRequest) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - TestLoadTimingNotReused(*stream); - TestSyncRequest(stream, "ok.html"); - TestLoadTimingNotReused(*stream); -} - -TEST_F(HttpPipelinedConnectionImplTest, AsyncSingleRequest) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"), - MockRead(ASYNC, 3, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response, - callback_.callback())); - data_->RunFor(1); - EXPECT_LE(OK, callback_.WaitForResult()); - TestLoadTimingNotReused(*stream); - - EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); - data_->RunFor(2); - EXPECT_LE(OK, callback_.WaitForResult()); - TestLoadTimingNotReused(*stream); - - ExpectResponse("ok.html", stream, true); - TestLoadTimingNotReused(*stream); - - stream->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, LockStepAsyncRequests) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(ASYNC, 1, "GET /ko.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(ASYNC, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"), - MockRead(ASYNC, 4, "ok.html"), - MockRead(ASYNC, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 6, "Content-Length: 7\r\n\r\n"), - MockRead(ASYNC, 7, "ko.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("ok.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ko.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, &response1, - callback_.callback())); - TestLoadTimingNotReused(*stream1); - - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2, - callback_.callback())); - TestLoadTimingReused(*stream2); - - data_->RunFor(1); - EXPECT_LE(OK, callback_.WaitForResult()); - data_->RunFor(1); - EXPECT_LE(OK, callback_.WaitForResult()); - - EXPECT_EQ(ERR_IO_PENDING, stream1->ReadResponseHeaders(callback_.callback())); - EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback_.callback())); - - data_->RunFor(2); - EXPECT_LE(OK, callback_.WaitForResult()); - - ExpectResponse("ok.html", stream1, true); - - TestLoadTimingNotReused(*stream1); - LoadTimingInfo load_timing_info1; - EXPECT_TRUE(stream1->GetLoadTimingInfo(&load_timing_info1)); - stream1->Close(false); - - data_->RunFor(2); - EXPECT_LE(OK, callback_.WaitForResult()); - - ExpectResponse("ko.html", stream2, true); - - TestLoadTimingReused(*stream2); - LoadTimingInfo load_timing_info2; - EXPECT_TRUE(stream2->GetLoadTimingInfo(&load_timing_info2)); - EXPECT_EQ(load_timing_info1.socket_log_id, - load_timing_info2.socket_log_id); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, TwoResponsesInOnePacket) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /ko.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, - "HTTP/1.1 200 OK\r\n" - "Content-Length: 7\r\n\r\n" - "ok.html" - "HTTP/1.1 200 OK\r\n" - "Content-Length: 7\r\n\r\n" - "ko.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("ok.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ko.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(OK, stream2->SendRequest(headers2, - &response2, callback_.callback())); - - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", stream1, false); - stream1->Close(false); - - EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ko.html", stream2, false); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, SendOrderSwapped) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ko.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 4, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "ko.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("ok.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ko.html")); - - TestSyncRequest(stream2, "ko.html"); - TestSyncRequest(stream1, "ok.html"); - TestLoadTimingNotReused(*stream1); - TestLoadTimingReused(*stream2); -} - -TEST_F(HttpPipelinedConnectionImplTest, ReadOrderSwapped) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /ko.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "ko.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("ok.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ko.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(OK, stream2->SendRequest(headers2, - &response2, callback_.callback())); - - EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback_.callback())); - - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", stream1, false); - - stream1->Close(false); - - EXPECT_LE(OK, callback_.WaitForResult()); - ExpectResponse("ko.html", stream2, false); - - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, SendWhileReading) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 3, "GET /ko.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "ko.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("ok.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ko.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(OK, stream2->SendRequest(headers2, - &response2, callback_.callback())); - - ExpectResponse("ok.html", stream1, false); - stream1->Close(false); - - EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ko.html", stream2, false); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, AsyncSendWhileAsyncReadBlocked) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(ASYNC, 3, "GET /ko.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"), - MockRead(ASYNC, 4, "ok.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "ko.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("ok.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ko.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - TestCompletionCallback callback1; - std::string expected = "ok.html"; - scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size())); - EXPECT_EQ(ERR_IO_PENDING, - stream1->ReadResponseBody(buffer.get(), expected.size(), - callback1.callback())); - - HttpRequestHeaders headers2; - HttpResponseInfo response2; - TestCompletionCallback callback2; - EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2, - callback2.callback())); - - data_->RunFor(1); - EXPECT_LE(OK, callback2.WaitForResult()); - EXPECT_EQ(ERR_IO_PENDING, stream2->ReadResponseHeaders(callback2.callback())); - - data_->RunFor(1); - EXPECT_EQ(static_cast<int>(expected.size()), callback1.WaitForResult()); - std::string actual(buffer->data(), expected.size()); - EXPECT_THAT(actual, StrEq(expected)); - stream1->Close(false); - - data_->StopAfter(8); - EXPECT_LE(OK, callback2.WaitForResult()); - ExpectResponse("ko.html", stream2, false); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, UnusedStreamAllowsLaterUse) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> unused_stream(NewTestStream("unused.html")); - unused_stream->Close(false); - - scoped_ptr<HttpStream> later_stream(NewTestStream("ok.html")); - TestSyncRequest(later_stream, "ok.html"); -} - -TEST_F(HttpPipelinedConnectionImplTest, UnsentStreamAllowsLaterUse) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 4, "GET /ko.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"), - MockRead(ASYNC, 3, "ok.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "ko.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response, - callback_.callback())); - - scoped_ptr<HttpStream> unsent_stream(NewTestStream("unsent.html")); - HttpRequestHeaders unsent_headers; - HttpResponseInfo unsent_response; - EXPECT_EQ(ERR_IO_PENDING, unsent_stream->SendRequest(unsent_headers, - &unsent_response, - callback_.callback())); - unsent_stream->Close(false); - - data_->RunFor(1); - EXPECT_LE(OK, callback_.WaitForResult()); - - EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); - data_->RunFor(2); - EXPECT_LE(OK, callback_.WaitForResult()); - - ExpectResponse("ok.html", stream, true); - - stream->Close(false); - - data_->StopAfter(8); - scoped_ptr<HttpStream> later_stream(NewTestStream("ko.html")); - TestSyncRequest(later_stream, "ko.html"); -} - -TEST_F(HttpPipelinedConnectionImplTest, FailedSend) { - MockWrite writes[] = { - MockWrite(ASYNC, ERR_FAILED), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - scoped_ptr<HttpStream> failed_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - scoped_ptr<HttpStream> closed_stream(NewTestStream("closed.html")); - scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - TestCompletionCallback failed_callback; - EXPECT_EQ(ERR_IO_PENDING, - failed_stream->SendRequest(headers, &response, - failed_callback.callback())); - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->SendRequest(headers, &response, - evicted_callback.callback())); - EXPECT_EQ(ERR_IO_PENDING, closed_stream->SendRequest(headers, &response, - callback_.callback())); - closed_stream->Close(false); - - data_->RunFor(1); - EXPECT_EQ(ERR_FAILED, failed_callback.WaitForResult()); - EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult()); - EXPECT_EQ(ERR_PIPELINE_EVICTION, - rejected_stream->SendRequest(headers, &response, - callback_.callback())); - - failed_stream->Close(true); - evicted_stream->Close(true); - rejected_stream->Close(true); -} - -TEST_F(HttpPipelinedConnectionImplTest, ConnectionSuddenlyClosedAfterResponse) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /read_evicted.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 2, "GET /read_rejected.html HTTP/1.1\r\n\r\n"), - MockWrite(ASYNC, ERR_SOCKET_NOT_CONNECTED, 5), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(ASYNC, OK, 6), // Connection closed message. Not read before the - // ERR_SOCKET_NOT_CONNECTED. - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> closed_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> read_evicted_stream( - NewTestStream("read_evicted.html")); - scoped_ptr<HttpStream> read_rejected_stream( - NewTestStream("read_rejected.html")); - scoped_ptr<HttpStream> send_closed_stream( - NewTestStream("send_closed.html")); - scoped_ptr<HttpStream> send_evicted_stream( - NewTestStream("send_evicted.html")); - scoped_ptr<HttpStream> send_rejected_stream( - NewTestStream("send_rejected.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, closed_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, read_evicted_stream->SendRequest(headers, &response, - callback_.callback())); - EXPECT_EQ(OK, read_rejected_stream->SendRequest(headers, &response, - callback_.callback())); - TestCompletionCallback send_closed_callback; - EXPECT_EQ(ERR_IO_PENDING, - send_closed_stream->SendRequest(headers, &response, - send_closed_callback.callback())); - TestCompletionCallback send_evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - send_evicted_stream->SendRequest(headers, &response, - send_evicted_callback.callback())); - - TestCompletionCallback read_evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - read_evicted_stream->ReadResponseHeaders( - read_evicted_callback.callback())); - - EXPECT_EQ(OK, closed_stream->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", closed_stream, false); - closed_stream->Close(true); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, read_evicted_callback.WaitForResult()); - read_evicted_stream->Close(true); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - read_rejected_stream->ReadResponseHeaders(callback_.callback())); - read_rejected_stream->Close(true); - - data_->RunFor(1); - EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, send_closed_callback.WaitForResult()); - send_closed_stream->Close(true); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, send_evicted_callback.WaitForResult()); - send_evicted_stream->Close(true); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - send_rejected_stream->SendRequest(headers, &response, - callback_.callback())); - send_rejected_stream->Close(true); -} - -TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSending) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /aborts.html HTTP/1.1\r\n\r\n"), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - TestCompletionCallback aborted_callback; - EXPECT_EQ(ERR_IO_PENDING, - aborted_stream->SendRequest(headers, &response, - aborted_callback.callback())); - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->SendRequest(headers, &response, - evicted_callback.callback())); - - aborted_stream->Close(true); - EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult()); - evicted_stream->Close(true); - EXPECT_FALSE(aborted_callback.have_result()); -} - -TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSendingSecondRequest) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(ASYNC, 1, "GET /aborts.html HTTP/1.1\r\n\r\n"), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - TestCompletionCallback ok_callback; - EXPECT_EQ(ERR_IO_PENDING, ok_stream->SendRequest(headers, &response, - ok_callback.callback())); - TestCompletionCallback aborted_callback; - EXPECT_EQ(ERR_IO_PENDING, - aborted_stream->SendRequest(headers, &response, - aborted_callback.callback())); - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->SendRequest(headers, &response, - evicted_callback.callback())); - - data_->RunFor(1); - EXPECT_LE(OK, ok_callback.WaitForResult()); - base::MessageLoop::current()->RunUntilIdle(); - aborted_stream->Close(true); - EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult()); - evicted_stream->Close(true); - EXPECT_FALSE(aborted_callback.have_result()); - ok_stream->Close(true); -} - -TEST_F(HttpPipelinedConnectionImplTest, AbortWhileReadingHeaders) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /aborts.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(ASYNC, ERR_FAILED, 2), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> aborted_stream(NewTestStream("aborts.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, - aborted_stream->SendRequest(headers, &response, - callback_.callback())); - EXPECT_EQ(OK, - evicted_stream->SendRequest(headers, &response, - callback_.callback())); - - EXPECT_EQ(ERR_IO_PENDING, - aborted_stream->ReadResponseHeaders(callback_.callback())); - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->ReadResponseHeaders(evicted_callback.callback())); - - aborted_stream->Close(true); - EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult()); - evicted_stream->Close(true); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - rejected_stream->SendRequest(headers, &response, - callback_.callback())); - rejected_stream->Close(true); -} - -TEST_F(HttpPipelinedConnectionImplTest, PendingResponseAbandoned) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /abandoned.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 2, "GET /evicted.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 4, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 5, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> abandoned_stream(NewTestStream("abandoned.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, ok_stream->SendRequest(headers, &response, - callback_.callback())); - EXPECT_EQ(OK, abandoned_stream->SendRequest(headers, &response, - callback_.callback())); - EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response, - callback_.callback())); - - EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback())); - TestCompletionCallback abandoned_callback; - EXPECT_EQ(ERR_IO_PENDING, abandoned_stream->ReadResponseHeaders( - abandoned_callback.callback())); - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->ReadResponseHeaders(evicted_callback.callback())); - - abandoned_stream->Close(false); - - ExpectResponse("ok.html", ok_stream, false); - ok_stream->Close(false); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult()); - evicted_stream->Close(true); - EXPECT_FALSE(evicted_stream->IsConnectionReusable()); -} - -TEST_F(HttpPipelinedConnectionImplTest, DisconnectedAfterOneRequestRecovery) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /rejected.html HTTP/1.1\r\n\r\n"), - MockWrite(ASYNC, ERR_SOCKET_NOT_CONNECTED, 5), - MockWrite(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 7), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 6), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> rejected_read_stream(NewTestStream("rejected.html")); - scoped_ptr<HttpStream> evicted_send_stream(NewTestStream("evicted.html")); - scoped_ptr<HttpStream> rejected_send_stream(NewTestStream("rejected.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, ok_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, rejected_read_stream->SendRequest(headers, &response, - callback_.callback())); - - EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", ok_stream, false); - ok_stream->Close(false); - - TestCompletionCallback read_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_send_stream->SendRequest(headers, &response, - read_callback.callback())); - data_->RunFor(1); - EXPECT_EQ(ERR_PIPELINE_EVICTION, read_callback.WaitForResult()); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - rejected_read_stream->ReadResponseHeaders(callback_.callback())); - EXPECT_EQ(ERR_PIPELINE_EVICTION, - rejected_send_stream->SendRequest(headers, &response, - callback_.callback())); - - rejected_read_stream->Close(true); - rejected_send_stream->Close(true); -} - -TEST_F(HttpPipelinedConnectionImplTest, DisconnectedPendingReadRecovery) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, ok_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response, - callback_.callback())); - - EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", ok_stream, false); - - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->ReadResponseHeaders(evicted_callback.callback())); - - ok_stream->Close(false); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult()); - evicted_stream->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, CloseCalledBeforeNextReadLoop) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, ok_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response, - callback_.callback())); - - EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", ok_stream, false); - - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->ReadResponseHeaders(evicted_callback.callback())); - - ok_stream->Close(false); - evicted_stream->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, CloseCalledBeforeReadCallback) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, ok_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response, - callback_.callback())); - - EXPECT_EQ(OK, ok_stream->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", ok_stream, false); - - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->ReadResponseHeaders(evicted_callback.callback())); - - ok_stream->Close(false); - - // The posted tasks should be: - // 1. DoReadHeadersLoop, which will post: - // 2. InvokeUserCallback - SuddenCloseObserver observer(evicted_stream.get(), 2); - base::MessageLoop::current()->AddTaskObserver(&observer); - base::MessageLoop::current()->RunUntilIdle(); - EXPECT_FALSE(evicted_callback.have_result()); -} - -class StreamDeleter { - public: - StreamDeleter(HttpStream* stream) - : stream_(stream), - callback_(base::Bind(&StreamDeleter::OnIOComplete, - base::Unretained(this))) { - } - - ~StreamDeleter() { - EXPECT_FALSE(stream_); - } - - const CompletionCallback& callback() { return callback_; } - - private: - void OnIOComplete(int result) { - stream_->Close(true); - stream_.reset(); - } - - scoped_ptr<HttpStream> stream_; - CompletionCallback callback_; -}; - -TEST_F(HttpPipelinedConnectionImplTest, CloseCalledDuringSendCallback) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - HttpStream* stream(NewTestStream("ok.html")); - - StreamDeleter deleter(stream); - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(ERR_IO_PENDING, stream->SendRequest(headers, &response, - deleter.callback())); - data_->RunFor(1); -} - -TEST_F(HttpPipelinedConnectionImplTest, CloseCalledDuringReadCallback) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - HttpStream* stream(NewTestStream("ok.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, stream->SendRequest(headers, - &response, callback_.callback())); - - StreamDeleter deleter(stream); - EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(deleter.callback())); - data_->RunFor(1); -} - -TEST_F(HttpPipelinedConnectionImplTest, - CloseCalledDuringReadCallbackWithPendingRead) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /failed.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - HttpStream* failed_stream(NewTestStream("failed.html")); - HttpStream* evicted_stream(NewTestStream("evicted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, failed_stream->SendRequest(headers, &response, - callback_.callback())); - EXPECT_EQ(OK, evicted_stream->SendRequest(headers, &response, - callback_.callback())); - - StreamDeleter failed_deleter(failed_stream); - EXPECT_EQ(ERR_IO_PENDING, - failed_stream->ReadResponseHeaders(failed_deleter.callback())); - StreamDeleter evicted_deleter(evicted_stream); - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->ReadResponseHeaders(evicted_deleter.callback())); - data_->RunFor(1); -} - -TEST_F(HttpPipelinedConnectionImplTest, CloseOtherDuringReadCallback) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /deleter.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /deleted.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> deleter_stream(NewTestStream("deleter.html")); - HttpStream* deleted_stream(NewTestStream("deleted.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, deleter_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, deleted_stream->SendRequest(headers, - &response, callback_.callback())); - - StreamDeleter deleter(deleted_stream); - EXPECT_EQ(ERR_IO_PENDING, - deleter_stream->ReadResponseHeaders(deleter.callback())); - EXPECT_EQ(ERR_IO_PENDING, - deleted_stream->ReadResponseHeaders(callback_.callback())); - data_->RunFor(1); -} - -TEST_F(HttpPipelinedConnectionImplTest, CloseBeforeSendCallbackRuns) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /close.html HTTP/1.1\r\n\r\n"), - MockWrite(ASYNC, 1, "GET /dummy.html HTTP/1.1\r\n\r\n"), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - scoped_ptr<HttpStream> close_stream(NewTestStream("close.html")); - scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html")); - - scoped_ptr<TestCompletionCallback> close_callback( - new TestCompletionCallback); - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(ERR_IO_PENDING, - close_stream->SendRequest(headers, - &response, close_callback->callback())); - - data_->RunFor(1); - EXPECT_FALSE(close_callback->have_result()); - - close_stream->Close(false); - close_stream.reset(); - close_callback.reset(); - - base::MessageLoop::current()->RunUntilIdle(); -} - -TEST_F(HttpPipelinedConnectionImplTest, CloseBeforeReadCallbackRuns) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /close.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 3, "GET /dummy.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 2, "Content-Length: 7\r\n\r\n"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> close_stream(NewTestStream("close.html")); - scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, close_stream->SendRequest(headers, - &response, callback_.callback())); - - scoped_ptr<TestCompletionCallback> close_callback( - new TestCompletionCallback); - EXPECT_EQ(ERR_IO_PENDING, - close_stream->ReadResponseHeaders(close_callback->callback())); - - data_->RunFor(1); - EXPECT_FALSE(close_callback->have_result()); - - close_stream->Close(false); - close_stream.reset(); - close_callback.reset(); - - base::MessageLoop::current()->RunUntilIdle(); -} - -TEST_F(HttpPipelinedConnectionImplTest, AbortWhileSendQueued) { - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(ASYNC, 1, "GET /ko.html HTTP/1.1\r\n\r\n"), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("ok.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ko.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - TestCompletionCallback callback1; - EXPECT_EQ(ERR_IO_PENDING, stream1->SendRequest(headers1, &response1, - callback1.callback())); - - HttpRequestHeaders headers2; - HttpResponseInfo response2; - TestCompletionCallback callback2; - EXPECT_EQ(ERR_IO_PENDING, stream2->SendRequest(headers2, &response2, - callback2.callback())); - - stream2.reset(); - stream1->Close(true); - - EXPECT_FALSE(callback2.have_result()); -} - -TEST_F(HttpPipelinedConnectionImplTest, NoGapBetweenCloseAndEviction) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /close.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 2, "GET /dummy.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 3, "Content-Length: 7\r\n\r\n"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> close_stream(NewTestStream("close.html")); - scoped_ptr<HttpStream> dummy_stream(NewTestStream("dummy.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, close_stream->SendRequest(headers, &response, - callback_.callback())); - - TestCompletionCallback close_callback; - EXPECT_EQ(ERR_IO_PENDING, - close_stream->ReadResponseHeaders(close_callback.callback())); - - EXPECT_EQ(OK, dummy_stream->SendRequest(headers, &response, - callback_.callback())); - - TestCompletionCallback dummy_callback; - EXPECT_EQ(ERR_IO_PENDING, - dummy_stream->ReadResponseHeaders(dummy_callback.callback())); - - close_stream->Close(true); - close_stream.reset(); - - EXPECT_TRUE(dummy_callback.have_result()); - EXPECT_EQ(ERR_PIPELINE_EVICTION, dummy_callback.WaitForResult()); - dummy_stream->Close(true); - dummy_stream.reset(); - pipeline_.reset(); -} - -TEST_F(HttpPipelinedConnectionImplTest, RecoverFromDrainOnRedirect) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, - "HTTP/1.1 302 OK\r\n" - "Content-Length: 8\r\n\r\n" - "redirect"), - MockRead(SYNCHRONOUS, 3, - "HTTP/1.1 200 OK\r\n" - "Content-Length: 7\r\n\r\n" - "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ok.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(OK, stream2->SendRequest(headers2, - &response2, callback_.callback())); - - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - stream1.release()->Drain(NULL); - - EXPECT_EQ(OK, stream2->ReadResponseHeaders(callback_.callback())); - ExpectResponse("ok.html", stream2, false); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, EvictAfterDrainOfUnknownSize) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, - "HTTP/1.1 302 OK\r\n\r\n" - "redirect"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ok.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(OK, stream2->SendRequest(headers2, - &response2, callback_.callback())); - - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - stream1.release()->Drain(NULL); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - stream2->ReadResponseHeaders(callback_.callback())); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, EvictAfterFailedDrain) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, - "HTTP/1.1 302 OK\r\n" - "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 3), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ok.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(OK, stream2->SendRequest(headers2, - &response2, callback_.callback())); - - - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - stream1.release()->Drain(NULL); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - stream2->ReadResponseHeaders(callback_.callback())); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, EvictIfDrainingChunkedEncoding) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 2, - "HTTP/1.1 302 OK\r\n" - "Transfer-Encoding: chunked\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, - "jibberish"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> stream1(NewTestStream("redirect.html")); - scoped_ptr<HttpStream> stream2(NewTestStream("ok.html")); - - HttpRequestHeaders headers1; - HttpResponseInfo response1; - EXPECT_EQ(OK, stream1->SendRequest(headers1, - &response1, callback_.callback())); - HttpRequestHeaders headers2; - HttpResponseInfo response2; - EXPECT_EQ(OK, stream2->SendRequest(headers2, - &response2, callback_.callback())); - - - EXPECT_EQ(OK, stream1->ReadResponseHeaders(callback_.callback())); - stream1.release()->Drain(NULL); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - stream2->ReadResponseHeaders(callback_.callback())); - stream2->Close(false); -} - -TEST_F(HttpPipelinedConnectionImplTest, EvictionDueToMissingContentLength) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 1, "GET /evicted.html HTTP/1.1\r\n\r\n"), - MockWrite(SYNCHRONOUS, 2, "GET /rejected.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(ASYNC, 3, "HTTP/1.1 200 OK\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - MockRead(SYNCHRONOUS, OK, 5), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpStream> ok_stream(NewTestStream("ok.html")); - scoped_ptr<HttpStream> evicted_stream(NewTestStream("evicted.html")); - scoped_ptr<HttpStream> rejected_stream(NewTestStream("rejected.html")); - - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, ok_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, evicted_stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(OK, rejected_stream->SendRequest(headers, - &response, callback_.callback())); - - TestCompletionCallback ok_callback; - EXPECT_EQ(ERR_IO_PENDING, - ok_stream->ReadResponseHeaders(ok_callback.callback())); - - TestCompletionCallback evicted_callback; - EXPECT_EQ(ERR_IO_PENDING, - evicted_stream->ReadResponseHeaders(evicted_callback.callback())); - - data_->RunFor(1); - EXPECT_LE(OK, ok_callback.WaitForResult()); - data_->StopAfter(10); - - ExpectResponse("ok.html", ok_stream, false); - ok_stream->Close(false); - - EXPECT_EQ(ERR_PIPELINE_EVICTION, - rejected_stream->ReadResponseHeaders(callback_.callback())); - rejected_stream->Close(true); - EXPECT_EQ(ERR_PIPELINE_EVICTION, evicted_callback.WaitForResult()); - evicted_stream->Close(true); -} - -TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnSocketError) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, ERR_FAILED, 1), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - EXPECT_CALL(delegate_, - OnPipelineFeedback( - pipeline_.get(), - HttpPipelinedConnection::PIPELINE_SOCKET_ERROR)) - .Times(1); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(ERR_FAILED, stream->ReadResponseHeaders(callback_.callback())); -} - -TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnNoInternetConnection) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, ERR_INTERNET_DISCONNECTED, 1), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - EXPECT_CALL(delegate_, OnPipelineFeedback(_, _)) - .Times(0); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, stream->SendRequest(headers, - &response, callback_.callback())); - EXPECT_EQ(ERR_INTERNET_DISCONNECTED, - stream->ReadResponseHeaders(callback_.callback())); -} - -TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnHttp10) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.0 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n"), - MockRead(SYNCHRONOUS, 3, "Connection: keep-alive\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - EXPECT_CALL(delegate_, - OnPipelineFeedback(pipeline_.get(), - HttpPipelinedConnection::OLD_HTTP_VERSION)) - .Times(1); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - TestSyncRequest(stream, "ok.html"); -} - -TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnMustClose) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 7\r\n"), - MockRead(SYNCHRONOUS, 3, "Connection: close\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - EXPECT_CALL(delegate_, - OnPipelineFeedback( - pipeline_.get(), - HttpPipelinedConnection::MUST_CLOSE_CONNECTION)) - .Times(1); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - TestSyncRequest(stream, "ok.html"); -} - -TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnNoContentLength) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"), - MockRead(SYNCHRONOUS, 2, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - EXPECT_CALL(delegate_, - OnPipelineFeedback( - pipeline_.get(), - HttpPipelinedConnection::MUST_CLOSE_CONNECTION)) - .Times(1); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - TestSyncRequest(stream, "ok.html"); -} - -TEST_F(HttpPipelinedConnectionImplTest, FeedbackOnAuthenticationRequired) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 401 Unauthorized\r\n"), - MockRead(SYNCHRONOUS, 2, "WWW-Authenticate: NTLM\r\n"), - MockRead(SYNCHRONOUS, 3, "Content-Length: 7\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "ok.html"), - }; - Initialize(reads, arraysize(reads), writes, arraysize(writes)); - - EXPECT_CALL(delegate_, - OnPipelineFeedback( - pipeline_.get(), - HttpPipelinedConnection::AUTHENTICATION_REQUIRED)) - .Times(1); - - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - TestSyncRequest(stream, "ok.html"); -} - -TEST_F(HttpPipelinedConnectionImplTest, OnPipelineHasCapacity) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0); - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - - EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1); - HttpRequestHeaders headers; - HttpResponseInfo response; - EXPECT_EQ(OK, stream->SendRequest(headers, - &response, callback_.callback())); - - EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0); - base::MessageLoop::current()->RunUntilIdle(); - - stream->Close(false); - EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1); - stream.reset(NULL); -} - -TEST_F(HttpPipelinedConnectionImplTest, OnPipelineHasCapacityWithoutSend) { - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /ok.html HTTP/1.1\r\n\r\n"), - }; - Initialize(NULL, 0, writes, arraysize(writes)); - - EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(0); - scoped_ptr<HttpStream> stream(NewTestStream("ok.html")); - - EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1); - base::MessageLoop::current()->RunUntilIdle(); - - stream->Close(false); - EXPECT_CALL(delegate_, OnPipelineHasCapacity(pipeline_.get())).Times(1); - stream.reset(NULL); -} - -} // anonymous namespace - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host.cc b/chromium/net/http/http_pipelined_host.cc deleted file mode 100644 index ca477808e9b..00000000000 --- a/chromium/net/http/http_pipelined_host.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host.h" - -namespace net { - -HttpPipelinedHost::Key::Key(const HostPortPair& origin) - : origin_(origin) { -} - -bool HttpPipelinedHost::Key::operator<(const Key& rhs) const { - return origin_ < rhs.origin_; -} - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host.h b/chromium/net/http/http_pipelined_host.h deleted file mode 100644 index b7732e60a8b..00000000000 --- a/chromium/net/http/http_pipelined_host.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_HOST_H_ -#define NET_HTTP_HTTP_PIPELINED_HOST_H_ - -#include "net/base/host_port_pair.h" -#include "net/base/net_export.h" -#include "net/http/http_pipelined_connection.h" -#include "net/http/http_pipelined_host_capability.h" - -namespace base { -class Value; -} - -namespace net { - -class BoundNetLog; -class ClientSocketHandle; -class HostPortPair; -class HttpPipelinedStream; -class ProxyInfo; -struct SSLConfig; - -// Manages all of the pipelining state for specific host with active pipelined -// HTTP requests. Manages connection jobs, constructs pipelined streams, and -// assigns requests to the least loaded pipelined connection. -class NET_EXPORT_PRIVATE HttpPipelinedHost { - public: - class NET_EXPORT_PRIVATE Key { - public: - Key(const HostPortPair& origin); - - // The host and port associated with this key. - const HostPortPair& origin() const { return origin_; } - - bool operator<(const Key& rhs) const; - - private: - const HostPortPair origin_; - }; - - class Delegate { - public: - // Called when a pipelined host has no outstanding requests on any of its - // pipelined connections. - virtual void OnHostIdle(HttpPipelinedHost* host) = 0; - - // Called when a pipelined host has newly available pipeline capacity, like - // when a request completes. - virtual void OnHostHasAdditionalCapacity(HttpPipelinedHost* host) = 0; - - // Called when a host determines if pipelining can be used. - virtual void OnHostDeterminedCapability( - HttpPipelinedHost* host, - HttpPipelinedHostCapability capability) = 0; - }; - - class Factory { - public: - virtual ~Factory() {} - - // Returns a new HttpPipelinedHost. - virtual HttpPipelinedHost* CreateNewHost( - Delegate* delegate, const Key& key, - HttpPipelinedConnection::Factory* factory, - HttpPipelinedHostCapability capability, - bool force_pipelining) = 0; - }; - - virtual ~HttpPipelinedHost() {} - - // Constructs a new pipeline on |connection| and returns a new - // HttpPipelinedStream that uses it. - virtual HttpPipelinedStream* CreateStreamOnNewPipeline( - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) = 0; - - // Tries to find an existing pipeline with capacity for a new request. If - // successful, returns a new stream on that pipeline. Otherwise, returns NULL. - virtual HttpPipelinedStream* CreateStreamOnExistingPipeline() = 0; - - // Returns true if we have a pipelined connection that can accept new - // requests. - virtual bool IsExistingPipelineAvailable() const = 0; - - // Returns a Key that uniquely identifies this host. - virtual const Key& GetKey() const = 0; - - // Creates a Value summary of this host's pipelines. Caller assumes - // ownership of the returned Value. - virtual base::Value* PipelineInfoToValue() const = 0; -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_HOST_H_ diff --git a/chromium/net/http/http_pipelined_host_capability.h b/chromium/net/http/http_pipelined_host_capability.h deleted file mode 100644 index d03a20893ea..00000000000 --- a/chromium/net/http/http_pipelined_host_capability.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2011 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_HOST_CAPABILITY_H_ -#define NET_HTTP_HTTP_PIPELINED_HOST_CAPABILITY_H_ - -namespace net { - -// These values are serialized in Preferences. Do not change these values and -// only add new ones at the end. -enum HttpPipelinedHostCapability { - PIPELINE_UNKNOWN = 0, - PIPELINE_INCAPABLE = 1, - PIPELINE_CAPABLE = 2, - PIPELINE_PROBABLY_CAPABLE = 3, // We are using pipelining, but haven't - // processed enough requests to record this - // host as known to be capable. -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_HOST_CAPABILITY_H_ diff --git a/chromium/net/http/http_pipelined_host_forced.cc b/chromium/net/http/http_pipelined_host_forced.cc deleted file mode 100644 index 8059d848d73..00000000000 --- a/chromium/net/http/http_pipelined_host_forced.cc +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host_forced.h" - -#include "base/values.h" -#include "net/http/http_pipelined_connection_impl.h" -#include "net/http/http_pipelined_stream.h" -#include "net/socket/buffered_write_stream_socket.h" -#include "net/socket/client_socket_handle.h" - -namespace net { - -HttpPipelinedHostForced::HttpPipelinedHostForced( - HttpPipelinedHost::Delegate* delegate, - const Key& key, - HttpPipelinedConnection::Factory* factory) - : delegate_(delegate), - key_(key), - factory_(factory) { - if (!factory) { - factory_.reset(new HttpPipelinedConnectionImpl::Factory()); - } -} - -HttpPipelinedHostForced::~HttpPipelinedHostForced() { - CHECK(!pipeline_.get()); -} - -HttpPipelinedStream* HttpPipelinedHostForced::CreateStreamOnNewPipeline( - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) { - CHECK(!pipeline_.get()); - scoped_ptr<BufferedWriteStreamSocket> buffered_socket( - new BufferedWriteStreamSocket(connection->PassSocket())); - connection->SetSocket(buffered_socket.PassAs<StreamSocket>()); - pipeline_.reset(factory_->CreateNewPipeline( - connection, this, key_.origin(), used_ssl_config, used_proxy_info, - net_log, was_npn_negotiated, protocol_negotiated)); - return pipeline_->CreateNewStream(); -} - -HttpPipelinedStream* HttpPipelinedHostForced::CreateStreamOnExistingPipeline() { - if (!pipeline_.get()) { - return NULL; - } - return pipeline_->CreateNewStream(); -} - -bool HttpPipelinedHostForced::IsExistingPipelineAvailable() const { - return pipeline_.get() != NULL; -} - -const HttpPipelinedHost::Key& HttpPipelinedHostForced::GetKey() const { - return key_; -} - -void HttpPipelinedHostForced::OnPipelineEmpty( - HttpPipelinedConnection* pipeline) { - CHECK_EQ(pipeline_.get(), pipeline); - pipeline_.reset(); - delegate_->OnHostIdle(this); - // WARNING: We'll probably be deleted here. -} - -void HttpPipelinedHostForced::OnPipelineHasCapacity( - HttpPipelinedConnection* pipeline) { - CHECK_EQ(pipeline_.get(), pipeline); - delegate_->OnHostHasAdditionalCapacity(this); - if (!pipeline->depth()) { - OnPipelineEmpty(pipeline); - // WARNING: We might be deleted here. - } -} - -void HttpPipelinedHostForced::OnPipelineFeedback( - HttpPipelinedConnection* pipeline, - HttpPipelinedConnection::Feedback feedback) { - // We don't care. We always pipeline. -} - -base::Value* HttpPipelinedHostForced::PipelineInfoToValue() const { - base::ListValue* list_value = new base::ListValue(); - if (pipeline_.get()) { - base::DictionaryValue* pipeline_dict = new base::DictionaryValue; - pipeline_dict->SetString("host", key_.origin().ToString()); - pipeline_dict->SetBoolean("forced", true); - pipeline_dict->SetInteger("depth", pipeline_->depth()); - pipeline_dict->SetInteger("capacity", 1000); - pipeline_dict->SetBoolean("usable", pipeline_->usable()); - pipeline_dict->SetBoolean("active", pipeline_->active()); - pipeline_dict->SetInteger("source_id", pipeline_->net_log().source().id); - list_value->Append(pipeline_dict); - } - return list_value; -} - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host_forced.h b/chromium/net/http/http_pipelined_host_forced.h deleted file mode 100644 index 2c3c9159342..00000000000 --- a/chromium/net/http/http_pipelined_host_forced.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_HOST_FORCED_H_ -#define NET_HTTP_HTTP_PIPELINED_HOST_FORCED_H_ - -#include <string> - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/host_port_pair.h" -#include "net/base/net_export.h" -#include "net/http/http_pipelined_connection.h" -#include "net/http/http_pipelined_host.h" -#include "net/http/http_pipelined_host_capability.h" - -namespace base { -class Value; -} - -namespace net { - -class BoundNetLog; -class ClientSocketHandle; -class HttpPipelinedStream; -class ProxyInfo; -struct SSLConfig; - -// Manages a single pipelined connection for requests to a host that are forced -// to use pipelining. Note that this is normally not used. It is intended to -// test the user's connection for pipelining compatibility. -class NET_EXPORT_PRIVATE HttpPipelinedHostForced - : public HttpPipelinedHost, - public HttpPipelinedConnection::Delegate { - public: - HttpPipelinedHostForced(HttpPipelinedHost::Delegate* delegate, - const Key& key, - HttpPipelinedConnection::Factory* factory); - virtual ~HttpPipelinedHostForced(); - - // HttpPipelinedHost interface - virtual HttpPipelinedStream* CreateStreamOnNewPipeline( - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) OVERRIDE; - - virtual HttpPipelinedStream* CreateStreamOnExistingPipeline() OVERRIDE; - - virtual bool IsExistingPipelineAvailable() const OVERRIDE; - - virtual const Key& GetKey() const OVERRIDE; - - virtual base::Value* PipelineInfoToValue() const OVERRIDE; - - // HttpPipelinedConnection::Delegate interface - - virtual void OnPipelineHasCapacity( - HttpPipelinedConnection* pipeline) OVERRIDE; - - virtual void OnPipelineFeedback( - HttpPipelinedConnection* pipeline, - HttpPipelinedConnection::Feedback feedback) OVERRIDE; - - private: - // Called when a pipeline is empty and there are no pending requests. Closes - // the connection. - void OnPipelineEmpty(HttpPipelinedConnection* pipeline); - - HttpPipelinedHost::Delegate* delegate_; - const Key key_; - scoped_ptr<HttpPipelinedConnection> pipeline_; - scoped_ptr<HttpPipelinedConnection::Factory> factory_; - - DISALLOW_COPY_AND_ASSIGN(HttpPipelinedHostForced); -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_HOST_FORCED_H_ diff --git a/chromium/net/http/http_pipelined_host_forced_unittest.cc b/chromium/net/http/http_pipelined_host_forced_unittest.cc deleted file mode 100644 index b86dd96d50a..00000000000 --- a/chromium/net/http/http_pipelined_host_forced_unittest.cc +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host_forced.h" - -#include "base/memory/scoped_ptr.h" -#include "net/http/http_pipelined_host_test_util.h" -#include "net/proxy/proxy_info.h" -#include "net/socket/client_socket_handle.h" -#include "net/ssl/ssl_config_service.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::NiceMock; -using testing::Ref; -using testing::Return; - -namespace net { - -namespace { - -HttpPipelinedStream* kDummyStream = - reinterpret_cast<HttpPipelinedStream*>(24); - -class HttpPipelinedHostForcedTest : public testing::Test { - public: - HttpPipelinedHostForcedTest() - : key_(HostPortPair("host", 123)), - factory_(new MockPipelineFactory), // Owned by |host_|. - host_(new HttpPipelinedHostForced(&delegate_, key_, factory_)) { - } - - MockPipeline* AddTestPipeline() { - MockPipeline* pipeline = new MockPipeline(0, true, true); - EXPECT_CALL(*factory_, CreateNewPipeline(&connection_, host_.get(), - MatchesOrigin(key_.origin()), - Ref(ssl_config_), Ref(proxy_info_), - Ref(net_log_), true, - kProtoSPDY3)) - .Times(1) - .WillOnce(Return(pipeline)); - EXPECT_CALL(*pipeline, CreateNewStream()) - .Times(1) - .WillOnce(Return(kDummyStream)); - EXPECT_EQ(kDummyStream, host_->CreateStreamOnNewPipeline( - &connection_, ssl_config_, proxy_info_, net_log_, true, - kProtoSPDY3)); - return pipeline; - } - - ClientSocketHandle connection_; - NiceMock<MockHostDelegate> delegate_; - HttpPipelinedHost::Key key_; - MockPipelineFactory* factory_; - scoped_ptr<HttpPipelinedHostForced> host_; - - SSLConfig ssl_config_; - ProxyInfo proxy_info_; - BoundNetLog net_log_; -}; - -TEST_F(HttpPipelinedHostForcedTest, Delegate) { - EXPECT_TRUE(key_.origin().Equals(host_->GetKey().origin())); -} - -TEST_F(HttpPipelinedHostForcedTest, SingleUser) { - EXPECT_FALSE(host_->IsExistingPipelineAvailable()); - - MockPipeline* pipeline = AddTestPipeline(); - EXPECT_TRUE(host_->IsExistingPipelineAvailable()); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - EXPECT_CALL(delegate_, OnHostIdle(host_.get())) - .Times(1); - host_->OnPipelineHasCapacity(pipeline); -} - -TEST_F(HttpPipelinedHostForcedTest, ReuseExisting) { - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - - MockPipeline* pipeline = AddTestPipeline(); - EXPECT_CALL(*pipeline, CreateNewStream()) - .Times(1) - .WillOnce(Return(kDummyStream)); - EXPECT_EQ(kDummyStream, host_->CreateStreamOnExistingPipeline()); - - pipeline->SetState(1, true, true); - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - EXPECT_CALL(delegate_, OnHostIdle(host_.get())) - .Times(0); - host_->OnPipelineHasCapacity(pipeline); - - pipeline->SetState(0, true, true); - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - EXPECT_CALL(delegate_, OnHostIdle(host_.get())) - .Times(1); - host_->OnPipelineHasCapacity(pipeline); -} - -} // anonymous namespace - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host_impl.cc b/chromium/net/http/http_pipelined_host_impl.cc deleted file mode 100644 index 2a41ca41a85..00000000000 --- a/chromium/net/http/http_pipelined_host_impl.cc +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host_impl.h" - -#include "base/stl_util.h" -#include "base/values.h" -#include "net/http/http_pipelined_connection_impl.h" -#include "net/http/http_pipelined_stream.h" - -namespace net { - -// TODO(simonjam): Run experiments to see what value minimizes evictions without -// costing too much performance. Until then, this is just a bad guess. -static const int kNumKnownSuccessesThreshold = 3; - -HttpPipelinedHostImpl::HttpPipelinedHostImpl( - HttpPipelinedHost::Delegate* delegate, - const HttpPipelinedHost::Key& key, - HttpPipelinedConnection::Factory* factory, - HttpPipelinedHostCapability capability) - : delegate_(delegate), - key_(key), - factory_(factory), - capability_(capability) { - if (!factory) { - factory_.reset(new HttpPipelinedConnectionImpl::Factory()); - } -} - -HttpPipelinedHostImpl::~HttpPipelinedHostImpl() { - CHECK(pipelines_.empty()); -} - -HttpPipelinedStream* HttpPipelinedHostImpl::CreateStreamOnNewPipeline( - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) { - if (capability_ == PIPELINE_INCAPABLE) { - return NULL; - } - HttpPipelinedConnection* pipeline = factory_->CreateNewPipeline( - connection, this, key_.origin(), used_ssl_config, used_proxy_info, - net_log, was_npn_negotiated, protocol_negotiated); - PipelineInfo info; - pipelines_.insert(std::make_pair(pipeline, info)); - return pipeline->CreateNewStream(); -} - -HttpPipelinedStream* HttpPipelinedHostImpl::CreateStreamOnExistingPipeline() { - HttpPipelinedConnection* available_pipeline = NULL; - for (PipelineInfoMap::iterator it = pipelines_.begin(); - it != pipelines_.end(); ++it) { - if (CanPipelineAcceptRequests(it->first) && - (!available_pipeline || - it->first->depth() < available_pipeline->depth())) { - available_pipeline = it->first; - } - } - if (!available_pipeline) { - return NULL; - } - return available_pipeline->CreateNewStream(); -} - -bool HttpPipelinedHostImpl::IsExistingPipelineAvailable() const { - for (PipelineInfoMap::const_iterator it = pipelines_.begin(); - it != pipelines_.end(); ++it) { - if (CanPipelineAcceptRequests(it->first)) { - return true; - } - } - return false; -} - -const HttpPipelinedHost::Key& HttpPipelinedHostImpl::GetKey() const { - return key_; -} - -void HttpPipelinedHostImpl::OnPipelineEmpty(HttpPipelinedConnection* pipeline) { - CHECK(ContainsKey(pipelines_, pipeline)); - pipelines_.erase(pipeline); - delete pipeline; - if (pipelines_.empty()) { - delegate_->OnHostIdle(this); - // WARNING: We'll probably be deleted here. - } -} - -void HttpPipelinedHostImpl::OnPipelineHasCapacity( - HttpPipelinedConnection* pipeline) { - CHECK(ContainsKey(pipelines_, pipeline)); - if (CanPipelineAcceptRequests(pipeline)) { - delegate_->OnHostHasAdditionalCapacity(this); - } - if (!pipeline->depth()) { - OnPipelineEmpty(pipeline); - // WARNING: We might be deleted here. - } -} - -void HttpPipelinedHostImpl::OnPipelineFeedback( - HttpPipelinedConnection* pipeline, - HttpPipelinedConnection::Feedback feedback) { - CHECK(ContainsKey(pipelines_, pipeline)); - switch (feedback) { - case HttpPipelinedConnection::OK: - ++pipelines_[pipeline].num_successes; - if (capability_ == PIPELINE_UNKNOWN) { - capability_ = PIPELINE_PROBABLY_CAPABLE; - NotifyAllPipelinesHaveCapacity(); - } else if (capability_ == PIPELINE_PROBABLY_CAPABLE && - pipelines_[pipeline].num_successes >= - kNumKnownSuccessesThreshold) { - capability_ = PIPELINE_CAPABLE; - delegate_->OnHostDeterminedCapability(this, PIPELINE_CAPABLE); - } - break; - - case HttpPipelinedConnection::PIPELINE_SOCKET_ERROR: - // Socket errors on the initial request - when no other requests are - // pipelined - can't be due to pipelining. - if (pipelines_[pipeline].num_successes > 0 || pipeline->depth() > 1) { - // TODO(simonjam): This may be needlessly harsh. For example, pogo.com - // only returns a socket error once after the root document, but is - // otherwise able to pipeline just fine. Consider being more persistent - // and only give up on pipelining if we get a couple of failures. - capability_ = PIPELINE_INCAPABLE; - delegate_->OnHostDeterminedCapability(this, PIPELINE_INCAPABLE); - } - break; - - case HttpPipelinedConnection::OLD_HTTP_VERSION: - case HttpPipelinedConnection::AUTHENTICATION_REQUIRED: - capability_ = PIPELINE_INCAPABLE; - delegate_->OnHostDeterminedCapability(this, PIPELINE_INCAPABLE); - break; - - case HttpPipelinedConnection::MUST_CLOSE_CONNECTION: - break; - } -} - -int HttpPipelinedHostImpl::GetPipelineCapacity() const { - int capacity = 0; - switch (capability_) { - case PIPELINE_CAPABLE: - case PIPELINE_PROBABLY_CAPABLE: - capacity = max_pipeline_depth(); - break; - - case PIPELINE_INCAPABLE: - CHECK(false); - - case PIPELINE_UNKNOWN: - capacity = 1; - break; - - default: - CHECK(false) << "Unkown pipeline capability: " << capability_; - } - return capacity; -} - -bool HttpPipelinedHostImpl::CanPipelineAcceptRequests( - HttpPipelinedConnection* pipeline) const { - return capability_ != PIPELINE_INCAPABLE && - pipeline->usable() && - pipeline->active() && - pipeline->depth() < GetPipelineCapacity(); -} - -void HttpPipelinedHostImpl::NotifyAllPipelinesHaveCapacity() { - // Calling OnPipelineHasCapacity() can have side effects that include - // deleting and removing entries from |pipelines_|. - PipelineInfoMap pipelines_to_notify = pipelines_; - for (PipelineInfoMap::iterator it = pipelines_to_notify.begin(); - it != pipelines_to_notify.end(); ++it) { - if (pipelines_.find(it->first) != pipelines_.end()) { - OnPipelineHasCapacity(it->first); - } - } -} - -base::Value* HttpPipelinedHostImpl::PipelineInfoToValue() const { - base::ListValue* list_value = new base::ListValue(); - for (PipelineInfoMap::const_iterator it = pipelines_.begin(); - it != pipelines_.end(); ++it) { - base::DictionaryValue* pipeline_dict = new base::DictionaryValue; - pipeline_dict->SetString("host", key_.origin().ToString()); - pipeline_dict->SetBoolean("forced", false); - pipeline_dict->SetInteger("depth", it->first->depth()); - pipeline_dict->SetInteger("capacity", GetPipelineCapacity()); - pipeline_dict->SetBoolean("usable", it->first->usable()); - pipeline_dict->SetBoolean("active", it->first->active()); - pipeline_dict->SetInteger("source_id", it->first->net_log().source().id); - list_value->Append(pipeline_dict); - } - return list_value; -} - -HttpPipelinedHostImpl::PipelineInfo::PipelineInfo() - : num_successes(0) { -} - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host_impl.h b/chromium/net/http/http_pipelined_host_impl.h deleted file mode 100644 index e2e53c93c37..00000000000 --- a/chromium/net/http/http_pipelined_host_impl.h +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_HOST_IMPL_H_ -#define NET_HTTP_HTTP_PIPELINED_HOST_IMPL_H_ - -#include <map> -#include <string> - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/host_port_pair.h" -#include "net/base/net_export.h" -#include "net/http/http_pipelined_connection.h" -#include "net/http/http_pipelined_host.h" -#include "net/http/http_pipelined_host_capability.h" - -namespace base { -class Value; -} - -namespace net { - -class BoundNetLog; -class ClientSocketHandle; -class HttpPipelinedStream; -class ProxyInfo; -struct SSLConfig; - -// Manages all of the pipelining state for specific host with active pipelined -// HTTP requests. Manages connection jobs, constructs pipelined streams, and -// assigns requests to the least loaded pipelined connection. -class NET_EXPORT_PRIVATE HttpPipelinedHostImpl - : public HttpPipelinedHost, - public HttpPipelinedConnection::Delegate { - public: - HttpPipelinedHostImpl(HttpPipelinedHost::Delegate* delegate, - const HttpPipelinedHost::Key& key, - HttpPipelinedConnection::Factory* factory, - HttpPipelinedHostCapability capability); - virtual ~HttpPipelinedHostImpl(); - - // HttpPipelinedHost interface - virtual HttpPipelinedStream* CreateStreamOnNewPipeline( - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) OVERRIDE; - - virtual HttpPipelinedStream* CreateStreamOnExistingPipeline() OVERRIDE; - - virtual bool IsExistingPipelineAvailable() const OVERRIDE; - - // HttpPipelinedConnection::Delegate interface - - // Called when a pipelined connection completes a request. Adds a pending - // request to the pipeline if the pipeline is still usable. - virtual void OnPipelineHasCapacity( - HttpPipelinedConnection* pipeline) OVERRIDE; - - virtual void OnPipelineFeedback( - HttpPipelinedConnection* pipeline, - HttpPipelinedConnection::Feedback feedback) OVERRIDE; - - virtual const Key& GetKey() const OVERRIDE; - - // Creates a Value summary of this host's |pipelines_|. Caller assumes - // ownership of the returned Value. - virtual base::Value* PipelineInfoToValue() const OVERRIDE; - - // Returns the maximum number of in-flight pipelined requests we'll allow on a - // single connection. - static int max_pipeline_depth() { return 3; } - - private: - struct PipelineInfo { - PipelineInfo(); - - int num_successes; - }; - typedef std::map<HttpPipelinedConnection*, PipelineInfo> PipelineInfoMap; - - // Called when a pipeline is empty and there are no pending requests. Closes - // the connection. - void OnPipelineEmpty(HttpPipelinedConnection* pipeline); - - // Adds the next pending request to the pipeline if it's still usuable. - void AddRequestToPipeline(HttpPipelinedConnection* pipeline); - - // Returns the current pipeline capacity based on |capability_|. This should - // not be called if |capability_| is INCAPABLE. - int GetPipelineCapacity() const; - - // Returns true if |pipeline| can handle a new request. This is true if the - // |pipeline| is active, usable, has capacity, and |capability_| is - // sufficient. - bool CanPipelineAcceptRequests(HttpPipelinedConnection* pipeline) const; - - // Called when |this| moves from UNKNOWN |capability_| to PROBABLY_CAPABLE. - // Causes all pipelines to increase capacity to start pipelining. - void NotifyAllPipelinesHaveCapacity(); - - HttpPipelinedHost::Delegate* delegate_; - const Key key_; - PipelineInfoMap pipelines_; - scoped_ptr<HttpPipelinedConnection::Factory> factory_; - HttpPipelinedHostCapability capability_; - - DISALLOW_COPY_AND_ASSIGN(HttpPipelinedHostImpl); -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_HOST_IMPL_H_ diff --git a/chromium/net/http/http_pipelined_host_impl_unittest.cc b/chromium/net/http/http_pipelined_host_impl_unittest.cc deleted file mode 100644 index 2658472138d..00000000000 --- a/chromium/net/http/http_pipelined_host_impl_unittest.cc +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host_impl.h" - -#include "base/memory/scoped_ptr.h" -#include "net/http/http_pipelined_connection.h" -#include "net/http/http_pipelined_host_test_util.h" -#include "net/proxy/proxy_info.h" -#include "net/ssl/ssl_config_service.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using testing::NiceMock; -using testing::Ref; -using testing::Return; -using testing::ReturnNull; - -namespace net { - -namespace { - -ClientSocketHandle* kDummyConnection = - reinterpret_cast<ClientSocketHandle*>(84); -HttpPipelinedStream* kDummyStream = - reinterpret_cast<HttpPipelinedStream*>(42); - -class HttpPipelinedHostImplTest : public testing::Test { - public: - HttpPipelinedHostImplTest() - : key_(HostPortPair("host", 123)), - factory_(new MockPipelineFactory), // Owned by host_. - host_(new HttpPipelinedHostImpl(&delegate_, key_, factory_, - PIPELINE_CAPABLE)) { - } - - void SetCapability(HttpPipelinedHostCapability capability) { - factory_ = new MockPipelineFactory; - host_.reset(new HttpPipelinedHostImpl( - &delegate_, key_, factory_, capability)); - } - - MockPipeline* AddTestPipeline(int depth, bool usable, bool active) { - MockPipeline* pipeline = new MockPipeline(depth, usable, active); - EXPECT_CALL(*factory_, CreateNewPipeline(kDummyConnection, host_.get(), - MatchesOrigin(key_.origin()), - Ref(ssl_config_), Ref(proxy_info_), - Ref(net_log_), true, - kProtoSPDY3)) - .Times(1) - .WillOnce(Return(pipeline)); - EXPECT_CALL(*pipeline, CreateNewStream()) - .Times(1) - .WillOnce(Return(kDummyStream)); - EXPECT_EQ(kDummyStream, host_->CreateStreamOnNewPipeline( - kDummyConnection, ssl_config_, proxy_info_, net_log_, true, - kProtoSPDY3)); - return pipeline; - } - - void ClearTestPipeline(MockPipeline* pipeline) { - pipeline->SetState(0, true, true); - host_->OnPipelineHasCapacity(pipeline); - } - - NiceMock<MockHostDelegate> delegate_; - HttpPipelinedHost::Key key_; - MockPipelineFactory* factory_; - scoped_ptr<HttpPipelinedHostImpl> host_; - - SSLConfig ssl_config_; - ProxyInfo proxy_info_; - BoundNetLog net_log_; -}; - -TEST_F(HttpPipelinedHostImplTest, Delegate) { - EXPECT_TRUE(key_.origin().Equals(host_->GetKey().origin())); -} - -TEST_F(HttpPipelinedHostImplTest, OnUnusablePipelineHasCapacity) { - MockPipeline* pipeline = AddTestPipeline(0, false, true); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(0); - EXPECT_CALL(delegate_, OnHostIdle(host_.get())) - .Times(1); - host_->OnPipelineHasCapacity(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, OnUsablePipelineHasCapacity) { - MockPipeline* pipeline = AddTestPipeline(1, true, true); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - EXPECT_CALL(delegate_, OnHostIdle(host_.get())) - .Times(0); - - host_->OnPipelineHasCapacity(pipeline); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - EXPECT_CALL(delegate_, OnHostIdle(host_.get())) - .Times(1); - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, IgnoresUnusablePipeline) { - MockPipeline* pipeline = AddTestPipeline(1, false, true); - - EXPECT_FALSE(host_->IsExistingPipelineAvailable()); - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, IgnoresInactivePipeline) { - MockPipeline* pipeline = AddTestPipeline(1, true, false); - - EXPECT_FALSE(host_->IsExistingPipelineAvailable()); - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, IgnoresFullPipeline) { - MockPipeline* pipeline = AddTestPipeline( - HttpPipelinedHostImpl::max_pipeline_depth(), true, true); - - EXPECT_FALSE(host_->IsExistingPipelineAvailable()); - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, PicksLeastLoadedPipeline) { - MockPipeline* full_pipeline = AddTestPipeline( - HttpPipelinedHostImpl::max_pipeline_depth(), true, true); - MockPipeline* usable_pipeline = AddTestPipeline( - HttpPipelinedHostImpl::max_pipeline_depth() - 1, true, true); - MockPipeline* empty_pipeline = AddTestPipeline(0, true, true); - - EXPECT_TRUE(host_->IsExistingPipelineAvailable()); - EXPECT_CALL(*empty_pipeline, CreateNewStream()) - .Times(1) - .WillOnce(ReturnNull()); - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - - ClearTestPipeline(full_pipeline); - ClearTestPipeline(usable_pipeline); - ClearTestPipeline(empty_pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, OpensUpOnPipelineSuccess) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline = AddTestPipeline(1, true, true); - - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK); - - EXPECT_CALL(*pipeline, CreateNewStream()) - .Times(1) - .WillOnce(Return(kDummyStream)); - EXPECT_EQ(kDummyStream, host_->CreateStreamOnExistingPipeline()); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, OpensAllPipelinesOnPipelineSuccess) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline1 = AddTestPipeline(1, false, true); - MockPipeline* pipeline2 = AddTestPipeline(1, true, true); - - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - host_->OnPipelineFeedback(pipeline1, HttpPipelinedConnection::OK); - - EXPECT_CALL(*pipeline2, CreateNewStream()) - .Times(1) - .WillOnce(Return(kDummyStream)); - EXPECT_EQ(kDummyStream, host_->CreateStreamOnExistingPipeline()); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(2); - ClearTestPipeline(pipeline1); - ClearTestPipeline(pipeline2); -} - -TEST_F(HttpPipelinedHostImplTest, ShutsDownOnOldVersion) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline = AddTestPipeline(1, true, true); - - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(0); - EXPECT_CALL(delegate_, - OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE)) - .Times(1); - host_->OnPipelineFeedback(pipeline, - HttpPipelinedConnection::OLD_HTTP_VERSION); - - ClearTestPipeline(pipeline); - EXPECT_EQ(NULL, host_->CreateStreamOnNewPipeline( - kDummyConnection, ssl_config_, proxy_info_, net_log_, true, - kProtoSPDY3)); -} - -TEST_F(HttpPipelinedHostImplTest, ShutsDownOnAuthenticationRequired) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline = AddTestPipeline(1, true, true); - - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(0); - EXPECT_CALL(delegate_, - OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE)) - .Times(1); - host_->OnPipelineFeedback(pipeline, - HttpPipelinedConnection::AUTHENTICATION_REQUIRED); - - ClearTestPipeline(pipeline); - EXPECT_EQ(NULL, host_->CreateStreamOnNewPipeline( - kDummyConnection, ssl_config_, proxy_info_, net_log_, true, - kProtoSPDY3)); -} - -TEST_F(HttpPipelinedHostImplTest, ConnectionCloseHasNoEffect) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline = AddTestPipeline(1, true, true); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(0); - EXPECT_CALL(delegate_, OnHostDeterminedCapability(host_.get(), _)) - .Times(0); - host_->OnPipelineFeedback(pipeline, - HttpPipelinedConnection::MUST_CLOSE_CONNECTION); - EXPECT_EQ(NULL, host_->CreateStreamOnExistingPipeline()); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, SuccessesLeadToCapable) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline = AddTestPipeline(1, true, true); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - EXPECT_CALL(delegate_, - OnHostDeterminedCapability(host_.get(), PIPELINE_CAPABLE)) - .Times(1); - host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK); - - pipeline->SetState(3, true, true); - host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK); - host_->OnPipelineFeedback(pipeline, HttpPipelinedConnection::OK); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, IgnoresSocketErrorOnFirstRequest) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline = AddTestPipeline(1, true, true); - - EXPECT_CALL(delegate_, OnHostDeterminedCapability(host_.get(), _)) - .Times(0); - host_->OnPipelineFeedback(pipeline, - HttpPipelinedConnection::PIPELINE_SOCKET_ERROR); - - EXPECT_CALL(delegate_, OnHostHasAdditionalCapacity(host_.get())) - .Times(1); - host_->OnPipelineFeedback(pipeline, - HttpPipelinedConnection::OK); - - EXPECT_CALL(delegate_, - OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE)) - .Times(1); - host_->OnPipelineFeedback(pipeline, - HttpPipelinedConnection::PIPELINE_SOCKET_ERROR); - - ClearTestPipeline(pipeline); -} - -TEST_F(HttpPipelinedHostImplTest, HeedsSocketErrorOnFirstRequestWithPipeline) { - SetCapability(PIPELINE_UNKNOWN); - MockPipeline* pipeline = AddTestPipeline(2, true, true); - - EXPECT_CALL(delegate_, - OnHostDeterminedCapability(host_.get(), PIPELINE_INCAPABLE)) - .Times(1); - host_->OnPipelineFeedback(pipeline, - HttpPipelinedConnection::PIPELINE_SOCKET_ERROR); - - ClearTestPipeline(pipeline); -} - -} // anonymous namespace - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host_pool.cc b/chromium/net/http/http_pipelined_host_pool.cc deleted file mode 100644 index ee37e74c29d..00000000000 --- a/chromium/net/http/http_pipelined_host_pool.cc +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host_pool.h" - -#include "base/logging.h" -#include "base/stl_util.h" -#include "base/values.h" -#include "net/http/http_pipelined_host_capability.h" -#include "net/http/http_pipelined_host_forced.h" -#include "net/http/http_pipelined_host_impl.h" -#include "net/http/http_server_properties.h" - -namespace net { - -class HttpPipelinedHostImplFactory : public HttpPipelinedHost::Factory { - public: - virtual HttpPipelinedHost* CreateNewHost( - HttpPipelinedHost::Delegate* delegate, - const HttpPipelinedHost::Key& key, - HttpPipelinedConnection::Factory* factory, - HttpPipelinedHostCapability capability, - bool force_pipelining) OVERRIDE { - if (force_pipelining) { - return new HttpPipelinedHostForced(delegate, key, factory); - } else { - return new HttpPipelinedHostImpl(delegate, key, factory, capability); - } - } -}; - -HttpPipelinedHostPool::HttpPipelinedHostPool( - Delegate* delegate, - HttpPipelinedHost::Factory* factory, - const base::WeakPtr<HttpServerProperties>& http_server_properties, - bool force_pipelining) - : delegate_(delegate), - factory_(factory), - http_server_properties_(http_server_properties), - force_pipelining_(force_pipelining) { - if (!factory) { - factory_.reset(new HttpPipelinedHostImplFactory); - } -} - -HttpPipelinedHostPool::~HttpPipelinedHostPool() { - CHECK(host_map_.empty()); -} - -bool HttpPipelinedHostPool::IsKeyEligibleForPipelining( - const HttpPipelinedHost::Key& key) { - HttpPipelinedHostCapability capability = - http_server_properties_->GetPipelineCapability(key.origin()); - return capability != PIPELINE_INCAPABLE; -} - -HttpPipelinedStream* HttpPipelinedHostPool::CreateStreamOnNewPipeline( - const HttpPipelinedHost::Key& key, - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated) { - HttpPipelinedHost* host = GetPipelinedHost(key, true); - if (!host) { - return NULL; - } - return host->CreateStreamOnNewPipeline(connection, used_ssl_config, - used_proxy_info, net_log, - was_npn_negotiated, - protocol_negotiated); -} - -HttpPipelinedStream* HttpPipelinedHostPool::CreateStreamOnExistingPipeline( - const HttpPipelinedHost::Key& key) { - HttpPipelinedHost* host = GetPipelinedHost(key, false); - if (!host) { - return NULL; - } - return host->CreateStreamOnExistingPipeline(); -} - -bool HttpPipelinedHostPool::IsExistingPipelineAvailableForKey( - const HttpPipelinedHost::Key& key) { - HttpPipelinedHost* host = GetPipelinedHost(key, false); - if (!host) { - return false; - } - return host->IsExistingPipelineAvailable(); -} - -HttpPipelinedHost* HttpPipelinedHostPool::GetPipelinedHost( - const HttpPipelinedHost::Key& key, bool create_if_not_found) { - HostMap::iterator host_it = host_map_.find(key); - if (host_it != host_map_.end()) { - CHECK(host_it->second); - return host_it->second; - } else if (!create_if_not_found) { - return NULL; - } - - HttpPipelinedHostCapability capability = - http_server_properties_->GetPipelineCapability(key.origin()); - if (capability == PIPELINE_INCAPABLE) { - return NULL; - } - - HttpPipelinedHost* host = factory_->CreateNewHost( - this, key, NULL, capability, force_pipelining_); - host_map_[key] = host; - return host; -} - -void HttpPipelinedHostPool::OnHostIdle(HttpPipelinedHost* host) { - const HttpPipelinedHost::Key& key = host->GetKey(); - CHECK(ContainsKey(host_map_, key)); - host_map_.erase(key); - delete host; -} - -void HttpPipelinedHostPool::OnHostHasAdditionalCapacity( - HttpPipelinedHost* host) { - delegate_->OnHttpPipelinedHostHasAdditionalCapacity(host); -} - -void HttpPipelinedHostPool::OnHostDeterminedCapability( - HttpPipelinedHost* host, - HttpPipelinedHostCapability capability) { - http_server_properties_->SetPipelineCapability(host->GetKey().origin(), - capability); -} - -base::Value* HttpPipelinedHostPool::PipelineInfoToValue() const { - base::ListValue* list = new base::ListValue(); - for (HostMap::const_iterator it = host_map_.begin(); - it != host_map_.end(); ++it) { - base::Value* value = it->second->PipelineInfoToValue(); - list->Append(value); - } - return list; -} - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host_pool.h b/chromium/net/http/http_pipelined_host_pool.h deleted file mode 100644 index 45cb38cedf6..00000000000 --- a/chromium/net/http/http_pipelined_host_pool.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_HOST_POOL_H_ -#define NET_HTTP_HTTP_PIPELINED_HOST_POOL_H_ - -#include <map> - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "net/http/http_pipelined_host.h" -#include "net/http/http_pipelined_host_capability.h" - -namespace base { -class Value; -} - -namespace net { - -class HostPortPair; -class HttpPipelinedStream; -class HttpServerProperties; - -// Manages all of the pipelining state for specific host with active pipelined -// HTTP requests. Manages connection jobs, constructs pipelined streams, and -// assigns requests to the least loaded pipelined connection. -class NET_EXPORT_PRIVATE HttpPipelinedHostPool - : public HttpPipelinedHost::Delegate { - public: - class Delegate { - public: - // Called when a HttpPipelinedHost has new capacity. Attempts to allocate - // any pending pipeline-capable requests to pipelines. - virtual void OnHttpPipelinedHostHasAdditionalCapacity( - HttpPipelinedHost* host) = 0; - }; - - HttpPipelinedHostPool( - Delegate* delegate, - HttpPipelinedHost::Factory* factory, - const base::WeakPtr<HttpServerProperties>& http_server_properties, - bool force_pipelining); - virtual ~HttpPipelinedHostPool(); - - // Returns true if pipelining might work for |key|. Generally, this returns - // true, unless |key| is known to have failed pipelining recently. - bool IsKeyEligibleForPipelining(const HttpPipelinedHost::Key& key); - - // Constructs a new pipeline on |connection| and returns a new - // HttpPipelinedStream that uses it. - HttpPipelinedStream* CreateStreamOnNewPipeline( - const HttpPipelinedHost::Key& key, - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated); - - // Tries to find an existing pipeline with capacity for a new request. If - // successful, returns a new stream on that pipeline. Otherwise, returns NULL. - HttpPipelinedStream* CreateStreamOnExistingPipeline( - const HttpPipelinedHost::Key& key); - - // Returns true if a pipelined connection already exists for |key| and - // can accept new requests. - bool IsExistingPipelineAvailableForKey(const HttpPipelinedHost::Key& key); - - // Callbacks for HttpPipelinedHost. - virtual void OnHostIdle(HttpPipelinedHost* host) OVERRIDE; - - virtual void OnHostHasAdditionalCapacity(HttpPipelinedHost* host) OVERRIDE; - - virtual void OnHostDeterminedCapability( - HttpPipelinedHost* host, - HttpPipelinedHostCapability capability) OVERRIDE; - - // Creates a Value summary of this pool's |host_map_|. Caller assumes - // ownership of the returned Value. - base::Value* PipelineInfoToValue() const; - - private: - typedef std::map<HttpPipelinedHost::Key, HttpPipelinedHost*> HostMap; - - HttpPipelinedHost* GetPipelinedHost(const HttpPipelinedHost::Key& key, - bool create_if_not_found); - - Delegate* delegate_; - scoped_ptr<HttpPipelinedHost::Factory> factory_; - HostMap host_map_; - const base::WeakPtr<HttpServerProperties> http_server_properties_; - bool force_pipelining_; - - DISALLOW_COPY_AND_ASSIGN(HttpPipelinedHostPool); -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_HOST_POOL_H_ diff --git a/chromium/net/http/http_pipelined_host_pool_unittest.cc b/chromium/net/http/http_pipelined_host_pool_unittest.cc deleted file mode 100644 index fa8d93facb1..00000000000 --- a/chromium/net/http/http_pipelined_host_pool_unittest.cc +++ /dev/null @@ -1,262 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host_pool.h" - -#include "base/memory/scoped_ptr.h" -#include "base/rand_util.h" -#include "net/http/http_pipelined_host.h" -#include "net/http/http_pipelined_host_capability.h" -#include "net/http/http_server_properties_impl.h" -#include "net/proxy/proxy_info.h" -#include "net/ssl/ssl_config_service.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::_; -using testing::Ref; -using testing::Return; -using testing::ReturnNull; - -namespace net { - -namespace { - -ClientSocketHandle* kDummyConnection = - reinterpret_cast<ClientSocketHandle*>(188); -HttpPipelinedStream* kDummyStream = - reinterpret_cast<HttpPipelinedStream*>(99); - -class MockPoolDelegate : public HttpPipelinedHostPool::Delegate { - public: - MOCK_METHOD1(OnHttpPipelinedHostHasAdditionalCapacity, - void(HttpPipelinedHost* host)); -}; - -class MockHostFactory : public HttpPipelinedHost::Factory { - public: - MOCK_METHOD5(CreateNewHost, HttpPipelinedHost*( - HttpPipelinedHost::Delegate* delegate, - const HttpPipelinedHost::Key& key, - HttpPipelinedConnection::Factory* factory, - HttpPipelinedHostCapability capability, - bool force_pipelining)); -}; - -class MockHost : public HttpPipelinedHost { - public: - MockHost(const Key& key) - : key_(key) { - } - - MOCK_METHOD6(CreateStreamOnNewPipeline, HttpPipelinedStream*( - ClientSocketHandle* connection, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated)); - MOCK_METHOD0(CreateStreamOnExistingPipeline, HttpPipelinedStream*()); - MOCK_CONST_METHOD0(IsExistingPipelineAvailable, bool()); - MOCK_CONST_METHOD0(PipelineInfoToValue, base::Value*()); - - virtual const Key& GetKey() const OVERRIDE { return key_; } - - private: - Key key_; -}; - -class HttpPipelinedHostPoolTest : public testing::Test { - public: - HttpPipelinedHostPoolTest() - : key_(HostPortPair("host", 123)), - factory_(new MockHostFactory), // Owned by pool_. - host_(new MockHost(key_)), // Owned by pool_. - http_server_properties_(new HttpServerPropertiesImpl()), - pool_(new HttpPipelinedHostPool( - &delegate_, factory_, - http_server_properties_->GetWeakPtr(), false)), - was_npn_negotiated_(false), - protocol_negotiated_(kProtoUnknown) { - } - - void CreateDummyStream(const HttpPipelinedHost::Key& key, - ClientSocketHandle* connection, - HttpPipelinedStream* stream, - MockHost* host) { - EXPECT_CALL(*host, CreateStreamOnNewPipeline(connection, - Ref(ssl_config_), - Ref(proxy_info_), - Ref(net_log_), - was_npn_negotiated_, - protocol_negotiated_)) - .Times(1) - .WillOnce(Return(stream)); - EXPECT_EQ(stream, - pool_->CreateStreamOnNewPipeline(key, connection, - ssl_config_, proxy_info_, - net_log_, was_npn_negotiated_, - protocol_negotiated_)); - } - - MockHost* CreateDummyHost(const HttpPipelinedHost::Key& key) { - MockHost* mock_host = new MockHost(key); - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key), _, - PIPELINE_UNKNOWN, false)) - .Times(1) - .WillOnce(Return(mock_host)); - ClientSocketHandle* dummy_connection = - reinterpret_cast<ClientSocketHandle*>(base::RandUint64()); - HttpPipelinedStream* dummy_stream = - reinterpret_cast<HttpPipelinedStream*>(base::RandUint64()); - CreateDummyStream(key, dummy_connection, dummy_stream, mock_host); - return mock_host; - } - - HttpPipelinedHost::Key key_; - MockPoolDelegate delegate_; - MockHostFactory* factory_; - MockHost* host_; - scoped_ptr<HttpServerPropertiesImpl> http_server_properties_; - scoped_ptr<HttpPipelinedHostPool> pool_; - - const SSLConfig ssl_config_; - const ProxyInfo proxy_info_; - const BoundNetLog net_log_; - bool was_npn_negotiated_; - NextProto protocol_negotiated_; -}; - -TEST_F(HttpPipelinedHostPoolTest, DefaultUnknown) { - EXPECT_TRUE(pool_->IsKeyEligibleForPipelining(key_)); - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_UNKNOWN, false)) - .Times(1) - .WillOnce(Return(host_)); - - CreateDummyStream(key_, kDummyConnection, kDummyStream, host_); - pool_->OnHostIdle(host_); -} - -TEST_F(HttpPipelinedHostPoolTest, RemembersIncapable) { - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_UNKNOWN, false)) - .Times(1) - .WillOnce(Return(host_)); - - CreateDummyStream(key_, kDummyConnection, kDummyStream, host_); - pool_->OnHostDeterminedCapability(host_, PIPELINE_INCAPABLE); - pool_->OnHostIdle(host_); - EXPECT_FALSE(pool_->IsKeyEligibleForPipelining(key_)); - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_INCAPABLE, false)) - .Times(0); - EXPECT_EQ(NULL, - pool_->CreateStreamOnNewPipeline(key_, kDummyConnection, - ssl_config_, proxy_info_, net_log_, - was_npn_negotiated_, - protocol_negotiated_)); -} - -TEST_F(HttpPipelinedHostPoolTest, RemembersCapable) { - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_UNKNOWN, false)) - .Times(1) - .WillOnce(Return(host_)); - - CreateDummyStream(key_, kDummyConnection, kDummyStream, host_); - pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE); - pool_->OnHostIdle(host_); - EXPECT_TRUE(pool_->IsKeyEligibleForPipelining(key_)); - - host_ = new MockHost(key_); - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_CAPABLE, false)) - .Times(1) - .WillOnce(Return(host_)); - CreateDummyStream(key_, kDummyConnection, kDummyStream, host_); - pool_->OnHostIdle(host_); -} - -TEST_F(HttpPipelinedHostPoolTest, IncapableIsSticky) { - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_UNKNOWN, false)) - .Times(1) - .WillOnce(Return(host_)); - - CreateDummyStream(key_, kDummyConnection, kDummyStream, host_); - pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE); - pool_->OnHostDeterminedCapability(host_, PIPELINE_INCAPABLE); - pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE); - pool_->OnHostIdle(host_); - EXPECT_FALSE(pool_->IsKeyEligibleForPipelining(key_)); -} - -TEST_F(HttpPipelinedHostPoolTest, RemainsUnknownWithoutFeedback) { - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_UNKNOWN, false)) - .Times(1) - .WillOnce(Return(host_)); - - CreateDummyStream(key_, kDummyConnection, kDummyStream, host_); - pool_->OnHostIdle(host_); - EXPECT_TRUE(pool_->IsKeyEligibleForPipelining(key_)); - - host_ = new MockHost(key_); - EXPECT_CALL(*factory_, CreateNewHost(pool_.get(), Ref(key_), _, - PIPELINE_UNKNOWN, false)) - .Times(1) - .WillOnce(Return(host_)); - - CreateDummyStream(key_, kDummyConnection, kDummyStream, host_); - pool_->OnHostIdle(host_); -} - -TEST_F(HttpPipelinedHostPoolTest, PopulatesServerProperties) { - EXPECT_EQ(PIPELINE_UNKNOWN, - http_server_properties_->GetPipelineCapability( - host_->GetKey().origin())); - pool_->OnHostDeterminedCapability(host_, PIPELINE_CAPABLE); - EXPECT_EQ(PIPELINE_CAPABLE, - http_server_properties_->GetPipelineCapability( - host_->GetKey().origin())); - delete host_; // Must manually delete, because it's never added to |pool_|. -} - -TEST_F(HttpPipelinedHostPoolTest, MultipleKeys) { - HttpPipelinedHost::Key key1(HostPortPair("host", 123)); - HttpPipelinedHost::Key key2(HostPortPair("host", 456)); - HttpPipelinedHost::Key key3(HostPortPair("other", 456)); - HttpPipelinedHost::Key key4(HostPortPair("other", 789)); - MockHost* host1 = CreateDummyHost(key1); - MockHost* host2 = CreateDummyHost(key2); - MockHost* host3 = CreateDummyHost(key3); - - EXPECT_CALL(*host1, IsExistingPipelineAvailable()) - .Times(1) - .WillOnce(Return(true)); - EXPECT_TRUE(pool_->IsExistingPipelineAvailableForKey(key1)); - - EXPECT_CALL(*host2, IsExistingPipelineAvailable()) - .Times(1) - .WillOnce(Return(false)); - EXPECT_FALSE(pool_->IsExistingPipelineAvailableForKey(key2)); - - EXPECT_CALL(*host3, IsExistingPipelineAvailable()) - .Times(1) - .WillOnce(Return(true)); - EXPECT_TRUE(pool_->IsExistingPipelineAvailableForKey(key3)); - - EXPECT_FALSE(pool_->IsExistingPipelineAvailableForKey(key4)); - - pool_->OnHostIdle(host1); - pool_->OnHostIdle(host2); - pool_->OnHostIdle(host3); - - delete host_; // Must manually delete, because it's never added to |pool_|. -} - -} // anonymous namespace - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host_test_util.cc b/chromium/net/http/http_pipelined_host_test_util.cc deleted file mode 100644 index 5162e983708..00000000000 --- a/chromium/net/http/http_pipelined_host_test_util.cc +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_host_test_util.h" - -#include "net/proxy/proxy_info.h" -#include "net/ssl/ssl_config_service.h" - -namespace net { - -MockHostDelegate::MockHostDelegate() { -} - -MockHostDelegate::~MockHostDelegate() { -} - -MockPipelineFactory::MockPipelineFactory() { -} - -MockPipelineFactory::~MockPipelineFactory() { -} - -MockPipeline::MockPipeline(int depth, bool usable, bool active) - : depth_(depth), - usable_(usable), - active_(active) { -} - -MockPipeline::~MockPipeline() { -} - -} // namespace net diff --git a/chromium/net/http/http_pipelined_host_test_util.h b/chromium/net/http/http_pipelined_host_test_util.h deleted file mode 100644 index 245ff02c4d5..00000000000 --- a/chromium/net/http/http_pipelined_host_test_util.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_connection.h" -#include "net/http/http_pipelined_host.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace net { - -class MockHostDelegate : public HttpPipelinedHost::Delegate { - public: - MockHostDelegate(); - virtual ~MockHostDelegate(); - - MOCK_METHOD1(OnHostIdle, void(HttpPipelinedHost* host)); - MOCK_METHOD1(OnHostHasAdditionalCapacity, void(HttpPipelinedHost* host)); - MOCK_METHOD2(OnHostDeterminedCapability, - void(HttpPipelinedHost* host, - HttpPipelinedHostCapability capability)); -}; - -class MockPipelineFactory : public HttpPipelinedConnection::Factory { - public: - MockPipelineFactory(); - virtual ~MockPipelineFactory(); - - MOCK_METHOD8(CreateNewPipeline, HttpPipelinedConnection*( - ClientSocketHandle* connection, - HttpPipelinedConnection::Delegate* delegate, - const HostPortPair& origin, - const SSLConfig& used_ssl_config, - const ProxyInfo& used_proxy_info, - const BoundNetLog& net_log, - bool was_npn_negotiated, - NextProto protocol_negotiated)); -}; - -class MockPipeline : public HttpPipelinedConnection { - public: - MockPipeline(int depth, bool usable, bool active); - virtual ~MockPipeline(); - - void SetState(int depth, bool usable, bool active) { - depth_ = depth; - usable_ = usable; - active_ = active; - } - - virtual int depth() const OVERRIDE { return depth_; } - virtual bool usable() const OVERRIDE { return usable_; } - virtual bool active() const OVERRIDE { return active_; } - - MOCK_METHOD0(CreateNewStream, HttpPipelinedStream*()); - MOCK_METHOD1(OnStreamDeleted, void(int pipeline_id)); - MOCK_CONST_METHOD0(used_ssl_config, const SSLConfig&()); - MOCK_CONST_METHOD0(used_proxy_info, const ProxyInfo&()); - MOCK_CONST_METHOD0(net_log, const BoundNetLog&()); - MOCK_CONST_METHOD0(was_npn_negotiated, bool()); - MOCK_CONST_METHOD0(protocol_negotiated, NextProto()); - - private: - int depth_; - bool usable_; - bool active_; -}; - -MATCHER_P(MatchesOrigin, expected, "") { return expected.Equals(arg); } - -} // namespace net diff --git a/chromium/net/http/http_pipelined_network_transaction_unittest.cc b/chromium/net/http/http_pipelined_network_transaction_unittest.cc deleted file mode 100644 index 80b74fd554b..00000000000 --- a/chromium/net/http/http_pipelined_network_transaction_unittest.cc +++ /dev/null @@ -1,1035 +0,0 @@ -// Copyright (c) 2012 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 <string> - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "base/strings/stringprintf.h" -#include "base/strings/utf_string_conversions.h" -#include "net/base/address_list.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/net_util.h" -#include "net/base/request_priority.h" -#include "net/dns/host_cache.h" -#include "net/dns/mock_host_resolver.h" -#include "net/http/http_auth_handler_mock.h" -#include "net/http/http_network_session.h" -#include "net/http/http_network_transaction.h" -#include "net/http/http_request_info.h" -#include "net/http/http_server_properties_impl.h" -#include "net/proxy/proxy_config_service.h" -#include "net/proxy/proxy_service.h" -#include "net/socket/client_socket_handle.h" -#include "net/socket/client_socket_pool_histograms.h" -#include "net/socket/client_socket_pool_manager.h" -#include "net/socket/socket_test_util.h" -#include "net/ssl/ssl_config_service_defaults.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::StrEq; - -namespace net { - -namespace { - -class SimpleProxyConfigService : public ProxyConfigService { - public: - virtual void AddObserver(Observer* observer) OVERRIDE { - observer_ = observer; - } - - virtual void RemoveObserver(Observer* observer) OVERRIDE { - if (observer_ == observer) { - observer_ = NULL; - } - } - - virtual ConfigAvailability GetLatestProxyConfig( - ProxyConfig* config) OVERRIDE { - *config = config_; - return CONFIG_VALID; - } - - void IncrementConfigId() { - config_.set_id(config_.id() + 1); - observer_->OnProxyConfigChanged(config_, ProxyConfigService::CONFIG_VALID); - } - - private: - ProxyConfig config_; - Observer* observer_; -}; - -class HttpPipelinedNetworkTransactionTest : public testing::Test { - public: - HttpPipelinedNetworkTransactionTest() - : histograms_("a"), - pool_(1, 1, &histograms_, &factory_) { - } - - void Initialize(bool force_http_pipelining) { - // Normally, this code could just go in SetUp(). For a few of these tests, - // we change the default number of sockets per group. That needs to be done - // before we construct the HttpNetworkSession. - proxy_config_service_ = new SimpleProxyConfigService(); - proxy_service_.reset(new ProxyService(proxy_config_service_, NULL, NULL)); - ssl_config_ = new SSLConfigServiceDefaults; - auth_handler_factory_.reset(new HttpAuthHandlerMock::Factory()); - - HttpNetworkSession::Params session_params; - session_params.client_socket_factory = &factory_; - session_params.proxy_service = proxy_service_.get(); - session_params.host_resolver = &mock_resolver_; - session_params.ssl_config_service = ssl_config_.get(); - session_params.http_auth_handler_factory = auth_handler_factory_.get(); - session_params.http_server_properties = - http_server_properties_.GetWeakPtr(); - session_params.force_http_pipelining = force_http_pipelining; - session_params.http_pipelining_enabled = true; - session_ = new HttpNetworkSession(session_params); - } - - void AddExpectedConnection(MockRead* reads, size_t reads_count, - MockWrite* writes, size_t writes_count) { - DeterministicSocketData* data = new DeterministicSocketData( - reads, reads_count, writes, writes_count); - data->set_connect_data(MockConnect(SYNCHRONOUS, OK)); - if (reads_count || writes_count) { - data->StopAfter(reads_count + writes_count); - } - factory_.AddSocketDataProvider(data); - data_vector_.push_back(data); - } - - enum RequestInfoOptions { - REQUEST_DEFAULT, - REQUEST_MAIN_RESOURCE, - }; - - HttpRequestInfo* GetRequestInfo( - const char* filename, RequestInfoOptions options = REQUEST_DEFAULT) { - std::string url = base::StringPrintf("http://localhost/%s", filename); - HttpRequestInfo* request_info = new HttpRequestInfo; - request_info->url = GURL(url); - request_info->method = "GET"; - if (options == REQUEST_MAIN_RESOURCE) { - request_info->load_flags = LOAD_MAIN_FRAME; - } - request_info_vector_.push_back(request_info); - return request_info; - } - - void ExpectResponse(const std::string& expected, - HttpNetworkTransaction& transaction, - IoMode io_mode) { - scoped_refptr<IOBuffer> buffer(new IOBuffer(expected.size())); - if (io_mode == ASYNC) { - EXPECT_EQ(ERR_IO_PENDING, transaction.Read(buffer.get(), expected.size(), - callback_.callback())); - data_vector_[0]->RunFor(1); - EXPECT_EQ(static_cast<int>(expected.length()), callback_.WaitForResult()); - } else { - EXPECT_EQ(static_cast<int>(expected.size()), - transaction.Read(buffer.get(), expected.size(), - callback_.callback())); - } - std::string actual(buffer->data(), expected.size()); - EXPECT_THAT(actual, StrEq(expected)); - EXPECT_EQ(OK, transaction.Read(buffer.get(), expected.size(), - callback_.callback())); - } - - void CompleteTwoRequests(int data_index, int stop_at_step) { - scoped_ptr<HttpNetworkTransaction> one_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - EXPECT_EQ(OK, one_callback.WaitForResult()); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - TestCompletionCallback one_read_callback; - scoped_refptr<IOBuffer> buffer(new IOBuffer(8)); - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Read(buffer.get(), 8, - one_read_callback.callback())); - - data_vector_[data_index]->SetStop(stop_at_step); - data_vector_[data_index]->Run(); - EXPECT_EQ(8, one_read_callback.WaitForResult()); - data_vector_[data_index]->SetStop(10); - std::string actual(buffer->data(), 8); - EXPECT_THAT(actual, StrEq("one.html")); - EXPECT_EQ(OK, one_transaction->Read(buffer.get(), 8, - one_read_callback.callback())); - - EXPECT_EQ(OK, two_callback.WaitForResult()); - ExpectResponse("two.html", two_transaction, SYNCHRONOUS); - } - - void CompleteFourRequests(RequestInfoOptions options) { - scoped_ptr<HttpNetworkTransaction> one_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Start(GetRequestInfo("one.html", options), - one_callback.callback(), BoundNetLog())); - EXPECT_EQ(OK, one_callback.WaitForResult()); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html", options), - two_callback.callback(), BoundNetLog())); - - HttpNetworkTransaction three_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback three_callback; - EXPECT_EQ(ERR_IO_PENDING, - three_transaction.Start(GetRequestInfo("three.html", options), - three_callback.callback(), - BoundNetLog())); - - HttpNetworkTransaction four_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback four_callback; - EXPECT_EQ(ERR_IO_PENDING, - four_transaction.Start(GetRequestInfo("four.html", options), - four_callback.callback(), BoundNetLog())); - - ExpectResponse("one.html", *one_transaction.get(), SYNCHRONOUS); - EXPECT_EQ(OK, two_callback.WaitForResult()); - ExpectResponse("two.html", two_transaction, SYNCHRONOUS); - EXPECT_EQ(OK, three_callback.WaitForResult()); - ExpectResponse("three.html", three_transaction, SYNCHRONOUS); - - one_transaction.reset(); - EXPECT_EQ(OK, four_callback.WaitForResult()); - ExpectResponse("four.html", four_transaction, SYNCHRONOUS); - } - - DeterministicMockClientSocketFactory factory_; - ClientSocketPoolHistograms histograms_; - MockTransportClientSocketPool pool_; - ScopedVector<DeterministicSocketData> data_vector_; - TestCompletionCallback callback_; - ScopedVector<HttpRequestInfo> request_info_vector_; - - SimpleProxyConfigService* proxy_config_service_; - scoped_ptr<ProxyService> proxy_service_; - MockHostResolver mock_resolver_; - scoped_refptr<SSLConfigService> ssl_config_; - scoped_ptr<HttpAuthHandlerMock::Factory> auth_handler_factory_; - HttpServerPropertiesImpl http_server_properties_; - scoped_refptr<HttpNetworkSession> session_; -}; - -TEST_F(HttpPipelinedNetworkTransactionTest, OneRequest) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /test.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 9\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "test.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get()); - EXPECT_EQ(ERR_IO_PENDING, - transaction.Start(GetRequestInfo("test.html"), callback_.callback(), - BoundNetLog())); - EXPECT_EQ(OK, callback_.WaitForResult()); - ExpectResponse("test.html", transaction, SYNCHRONOUS); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ReusePipeline) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(ASYNC, 4, "one.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "two.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - CompleteTwoRequests(0, 5); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ReusesOnSpaceAvailable) { - int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL); - ClientSocketPoolManager::set_max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 7, "GET /three.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "one.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 8, "two.html"), - MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"), - MockRead(SYNCHRONOUS, 11, "three.html"), - MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"), - MockRead(SYNCHRONOUS, 15, "four.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - CompleteFourRequests(REQUEST_DEFAULT); - - ClientSocketPoolManager::set_max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, WontPipelineMainResource) { - int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL); - ClientSocketPoolManager::set_max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 8, "GET /three.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "one.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "two.html"), - MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"), - MockRead(SYNCHRONOUS, 11, "three.html"), - MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"), - MockRead(SYNCHRONOUS, 15, "four.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - CompleteFourRequests(REQUEST_MAIN_RESOURCE); - - ClientSocketPoolManager::set_max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, UnknownSizeEvictsToNewPipeline) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"), - MockRead(ASYNC, 2, "one.html"), - MockRead(SYNCHRONOUS, OK, 3), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - MockWrite writes2[] = { - MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads2[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "two.html"), - }; - AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2)); - - CompleteTwoRequests(0, 3); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ConnectionCloseEvictToNewPipeline) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(ASYNC, 4, "one.html"), - MockRead(SYNCHRONOUS, ERR_SOCKET_NOT_CONNECTED, 5), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - MockWrite writes2[] = { - MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads2[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "two.html"), - }; - AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2)); - - CompleteTwoRequests(0, 5); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ErrorEvictsToNewPipeline) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n\r\n"), - MockRead(SYNCHRONOUS, ERR_FAILED, 2), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - MockWrite writes2[] = { - MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads2[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "two.html"), - }; - AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2)); - - HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction.Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - EXPECT_EQ(OK, one_callback.WaitForResult()); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - scoped_refptr<IOBuffer> buffer(new IOBuffer(1)); - EXPECT_EQ(ERR_FAILED, - one_transaction.Read(buffer.get(), 1, callback_.callback())); - EXPECT_EQ(OK, two_callback.WaitForResult()); - ExpectResponse("two.html", two_transaction, SYNCHRONOUS); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, SendErrorEvictsToNewPipeline) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(ASYNC, ERR_FAILED, 0), - }; - AddExpectedConnection(NULL, 0, writes, arraysize(writes)); - - MockWrite writes2[] = { - MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads2[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "two.html"), - }; - AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2)); - - HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction.Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - data_vector_[0]->RunFor(1); - EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult()); - - EXPECT_EQ(OK, two_callback.WaitForResult()); - ExpectResponse("two.html", two_transaction, SYNCHRONOUS); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, RedirectDrained) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /redirect.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 3, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 302 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(ASYNC, 4, "redirect"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "two.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpNetworkTransaction> one_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Start(GetRequestInfo("redirect.html"), - one_callback.callback(), BoundNetLog())); - EXPECT_EQ(OK, one_callback.WaitForResult()); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - one_transaction.reset(); - data_vector_[0]->RunFor(2); - data_vector_[0]->SetStop(10); - - EXPECT_EQ(OK, two_callback.WaitForResult()); - ExpectResponse("two.html", two_transaction, SYNCHRONOUS); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, BasicHttpAuthentication) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 5, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n" - "Authorization: auth_token\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 401 Authentication Required\r\n"), - MockRead(SYNCHRONOUS, 2, - "WWW-Authenticate: Basic realm=\"Secure Area\"\r\n"), - MockRead(SYNCHRONOUS, 3, "Content-Length: 20\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "needs authentication"), - MockRead(SYNCHRONOUS, 6, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 7, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 8, "one.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - HttpAuthHandlerMock* mock_auth = new HttpAuthHandlerMock; - std::string challenge_text = "Basic"; - HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(), - challenge_text.end()); - GURL origin("localhost"); - EXPECT_TRUE(mock_auth->InitFromChallenge(&challenge, - HttpAuth::AUTH_SERVER, - origin, - BoundNetLog())); - auth_handler_factory_->AddMockHandler(mock_auth, HttpAuth::AUTH_SERVER); - - HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get()); - EXPECT_EQ(ERR_IO_PENDING, - transaction.Start(GetRequestInfo("one.html"), - callback_.callback(), - BoundNetLog())); - EXPECT_EQ(OK, callback_.WaitForResult()); - - AuthCredentials credentials(ASCIIToUTF16("user"), ASCIIToUTF16("pass")); - EXPECT_EQ(OK, transaction.RestartWithAuth(credentials, callback_.callback())); - - ExpectResponse("one.html", transaction, SYNCHRONOUS); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, OldVersionDisablesPipelining) { - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /pipelined.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.0 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 14\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "pipelined.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - MockWrite writes2[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads2[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(ASYNC, 3, "one.html"), - MockRead(SYNCHRONOUS, OK, 4), - }; - AddExpectedConnection(reads2, arraysize(reads2), writes2, arraysize(writes2)); - - MockWrite writes3[] = { - MockWrite(SYNCHRONOUS, 0, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads3[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "two.html"), - MockRead(SYNCHRONOUS, OK, 4), - }; - AddExpectedConnection(reads3, arraysize(reads3), writes3, arraysize(writes3)); - - HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction.Start(GetRequestInfo("pipelined.html"), - one_callback.callback(), BoundNetLog())); - EXPECT_EQ(OK, one_callback.WaitForResult()); - ExpectResponse("pipelined.html", one_transaction, SYNCHRONOUS); - - CompleteTwoRequests(1, 4); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, PipelinesImmediatelyIfKnownGood) { - // The first request gets us an HTTP/1.1. The next 3 test pipelining. When the - // 3rd request completes, we know pipelining is safe. After the first 4 - // complete, the 5th and 6th should then be immediately sent pipelined on a - // new HttpPipelinedConnection. - int old_max_sockets = ClientSocketPoolManager::max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL); - ClientSocketPoolManager::set_max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL, 1); - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 4, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 7, "GET /three.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 12, "GET /four.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 16, "GET /second-pipeline-one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(SYNCHRONOUS, 17, "GET /second-pipeline-two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 3, "one.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 8, "two.html"), - MockRead(SYNCHRONOUS, 9, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 10, "Content-Length: 10\r\n\r\n"), - MockRead(SYNCHRONOUS, 11, "three.html"), - MockRead(SYNCHRONOUS, 13, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 14, "Content-Length: 9\r\n\r\n"), - MockRead(SYNCHRONOUS, 15, "four.html"), - MockRead(ASYNC, 18, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 19, "Content-Length: 24\r\n\r\n"), - MockRead(SYNCHRONOUS, 20, "second-pipeline-one.html"), - MockRead(SYNCHRONOUS, 21, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 22, "Content-Length: 24\r\n\r\n"), - MockRead(SYNCHRONOUS, 23, "second-pipeline-two.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - CompleteFourRequests(REQUEST_DEFAULT); - - HttpNetworkTransaction second_one_transaction( - DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback second_one_callback; - EXPECT_EQ(ERR_IO_PENDING, - second_one_transaction.Start( - GetRequestInfo("second-pipeline-one.html"), - second_one_callback.callback(), BoundNetLog())); - base::MessageLoop::current()->RunUntilIdle(); - - HttpNetworkTransaction second_two_transaction( - DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback second_two_callback; - EXPECT_EQ(ERR_IO_PENDING, - second_two_transaction.Start( - GetRequestInfo("second-pipeline-two.html"), - second_two_callback.callback(), BoundNetLog())); - - data_vector_[0]->RunFor(3); - EXPECT_EQ(OK, second_one_callback.WaitForResult()); - data_vector_[0]->StopAfter(100); - ExpectResponse("second-pipeline-one.html", second_one_transaction, - SYNCHRONOUS); - EXPECT_EQ(OK, second_two_callback.WaitForResult()); - ExpectResponse("second-pipeline-two.html", second_two_transaction, - SYNCHRONOUS); - - ClientSocketPoolManager::set_max_sockets_per_group( - HttpNetworkSession::NORMAL_SOCKET_POOL, old_max_sockets); -} - -class DataRunnerObserver : public base::MessageLoop::TaskObserver { - public: - DataRunnerObserver(DeterministicSocketData* data, int run_before_task) - : data_(data), - run_before_task_(run_before_task), - current_task_(0) { } - - virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE { - ++current_task_; - if (current_task_ == run_before_task_) { - data_->Run(); - base::MessageLoop::current()->RemoveTaskObserver(this); - } - } - - virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {} - - private: - DeterministicSocketData* data_; - int run_before_task_; - int current_task_; -}; - -TEST_F(HttpPipelinedNetworkTransactionTest, OpenPipelinesWhileBinding) { - // There was a racy crash in the pipelining code. This test recreates that - // race. The steps are: - // 1. The first request starts a pipeline and requests headers. - // 2. HttpStreamFactoryImpl::Job tries to bind a pending request to a new - // pipeline and queues a task to do so. - // 3. Before that task runs, the first request receives its headers and - // determines this host is probably capable of pipelining. - // 4. All of the hosts' pipelines are notified they have capacity in a loop. - // 5. On the first iteration, the first pipeline is opened up to accept new - // requests and steals the request from step #2. - // 6. The pipeline from #2 is deleted because it has no streams. - // 7. On the second iteration, the host tries to notify the pipeline from step - // #2 that it has capacity. This is a use-after-free. - Initialize(false); - - MockWrite writes[] = { - MockWrite(SYNCHRONOUS, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - MockWrite(ASYNC, 3, "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(SYNCHRONOUS, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 2, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 4, "one.html"), - MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), - MockRead(SYNCHRONOUS, 6, "Content-Length: 8\r\n\r\n"), - MockRead(SYNCHRONOUS, 7, "two.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - AddExpectedConnection(NULL, 0, NULL, 0); - - HttpNetworkTransaction one_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction.Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - - data_vector_[0]->SetStop(2); - data_vector_[0]->Run(); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - // Posted tasks should be: - // 1. MockHostResolverBase::ResolveNow - // 2. HttpStreamFactoryImpl::Job::OnStreamReadyCallback for job 1 - // 3. HttpStreamFactoryImpl::Job::OnStreamReadyCallback for job 2 - // - // We need to make sure that the response that triggers OnPipelineFeedback(OK) - // is called in between when task #3 is scheduled and when it runs. The - // DataRunnerObserver does that. - DataRunnerObserver observer(data_vector_[0], 3); - base::MessageLoop::current()->AddTaskObserver(&observer); - data_vector_[0]->SetStop(4); - base::MessageLoop::current()->RunUntilIdle(); - data_vector_[0]->SetStop(10); - - EXPECT_EQ(OK, one_callback.WaitForResult()); - ExpectResponse("one.html", one_transaction, SYNCHRONOUS); - EXPECT_EQ(OK, two_callback.WaitForResult()); - ExpectResponse("two.html", two_transaction, SYNCHRONOUS); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ProxyChangesWhileConnecting) { - Initialize(false); - - DeterministicSocketData data(NULL, 0, NULL, 0); - data.set_connect_data(MockConnect(ASYNC, ERR_CONNECTION_REFUSED)); - factory_.AddSocketDataProvider(&data); - - DeterministicSocketData data2(NULL, 0, NULL, 0); - data2.set_connect_data(MockConnect(ASYNC, ERR_FAILED)); - factory_.AddSocketDataProvider(&data2); - - HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session_.get()); - EXPECT_EQ(ERR_IO_PENDING, - transaction.Start(GetRequestInfo("test.html"), callback_.callback(), - BoundNetLog())); - - proxy_config_service_->IncrementConfigId(); - - EXPECT_EQ(ERR_FAILED, callback_.WaitForResult()); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineSharesConnection) { - Initialize(true); - - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n" - "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(ASYNC, 1, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 2, "Content-Length: 8\r\n\r\n"), - MockRead(ASYNC, 3, "one.html"), - MockRead(ASYNC, 4, "HTTP/1.1 200 OK\r\n"), - MockRead(ASYNC, 5, "Content-Length: 8\r\n\r\n"), - MockRead(ASYNC, 6, "two.html"), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpNetworkTransaction> one_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - data_vector_[0]->RunFor(3); // Send + 2 lines of headers. - EXPECT_EQ(OK, one_callback.WaitForResult()); - ExpectResponse("one.html", *one_transaction.get(), ASYNC); - one_transaction.reset(); - - data_vector_[0]->RunFor(2); // 2 lines of headers. - EXPECT_EQ(OK, two_callback.WaitForResult()); - ExpectResponse("two.html", two_transaction, ASYNC); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, - ForcedPipelineConnectionErrorFailsBoth) { - Initialize(true); - - DeterministicSocketData data(NULL, 0, NULL, 0); - data.set_connect_data(MockConnect(ASYNC, ERR_FAILED)); - factory_.AddSocketDataProvider(&data); - - scoped_ptr<HttpNetworkTransaction> one_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - data.Run(); - EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult()); - EXPECT_EQ(ERR_FAILED, two_callback.WaitForResult()); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineEvictionIsFatal) { - Initialize(true); - - MockWrite writes[] = { - MockWrite(ASYNC, 0, "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n" - "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n"), - }; - MockRead reads[] = { - MockRead(ASYNC, ERR_FAILED, 1), - }; - AddExpectedConnection(reads, arraysize(reads), writes, arraysize(writes)); - - scoped_ptr<HttpNetworkTransaction> one_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - - HttpNetworkTransaction two_transaction(DEFAULT_PRIORITY, session_.get()); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction.Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - data_vector_[0]->RunFor(2); - EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult()); - one_transaction.reset(); - EXPECT_EQ(ERR_PIPELINE_EVICTION, two_callback.WaitForResult()); -} - -TEST_F(HttpPipelinedNetworkTransactionTest, ForcedPipelineOrder) { - Initialize(true); - - MockWrite writes[] = { - MockWrite(ASYNC, 0, - "GET /one.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n" - "GET /two.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n" - "GET /three.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n" - "GET /four.html HTTP/1.1\r\n" - "Host: localhost\r\n" - "Connection: keep-alive\r\n\r\n" - ), - }; - MockRead reads[] = { - MockRead(ASYNC, ERR_FAILED, 1), - }; - DeterministicSocketData data( - reads, arraysize(reads), writes, arraysize(writes)); - data.set_connect_data(MockConnect(ASYNC, OK)); - factory_.AddSocketDataProvider(&data); - - scoped_ptr<HttpNetworkTransaction> one_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback one_callback; - EXPECT_EQ(ERR_IO_PENDING, - one_transaction->Start(GetRequestInfo("one.html"), - one_callback.callback(), BoundNetLog())); - - scoped_ptr<HttpNetworkTransaction> two_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback two_callback; - EXPECT_EQ(ERR_IO_PENDING, - two_transaction->Start(GetRequestInfo("two.html"), - two_callback.callback(), BoundNetLog())); - - scoped_ptr<HttpNetworkTransaction> three_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback three_callback; - EXPECT_EQ(ERR_IO_PENDING, - three_transaction->Start(GetRequestInfo("three.html"), - three_callback.callback(), BoundNetLog())); - - scoped_ptr<HttpNetworkTransaction> four_transaction( - new HttpNetworkTransaction(DEFAULT_PRIORITY, session_.get())); - TestCompletionCallback four_callback; - EXPECT_EQ(ERR_IO_PENDING, - four_transaction->Start(GetRequestInfo("four.html"), - four_callback.callback(), BoundNetLog())); - - data.RunFor(3); - EXPECT_EQ(ERR_FAILED, one_callback.WaitForResult()); - one_transaction.reset(); - EXPECT_EQ(ERR_PIPELINE_EVICTION, two_callback.WaitForResult()); - two_transaction.reset(); - EXPECT_EQ(ERR_PIPELINE_EVICTION, three_callback.WaitForResult()); - three_transaction.reset(); - EXPECT_EQ(ERR_PIPELINE_EVICTION, four_callback.WaitForResult()); -} - -} // anonymous namespace - -} // namespace net diff --git a/chromium/net/http/http_pipelined_stream.cc b/chromium/net/http/http_pipelined_stream.cc deleted file mode 100644 index cc267e2510e..00000000000 --- a/chromium/net/http/http_pipelined_stream.cc +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (c) 2012 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/http/http_pipelined_stream.h" - -#include "base/logging.h" -#include "base/strings/stringprintf.h" -#include "net/base/net_errors.h" -#include "net/http/http_pipelined_connection_impl.h" -#include "net/http/http_request_headers.h" -#include "net/http/http_request_info.h" -#include "net/http/http_util.h" - -namespace net { - -HttpPipelinedStream::HttpPipelinedStream(HttpPipelinedConnectionImpl* pipeline, - int pipeline_id) - : pipeline_(pipeline), - pipeline_id_(pipeline_id), - request_info_(NULL) { -} - -HttpPipelinedStream::~HttpPipelinedStream() { - pipeline_->OnStreamDeleted(pipeline_id_); -} - -int HttpPipelinedStream::InitializeStream( - const HttpRequestInfo* request_info, - RequestPriority priority, - const BoundNetLog& net_log, - const CompletionCallback& callback) { - request_info_ = request_info; - pipeline_->InitializeParser(pipeline_id_, request_info, net_log); - return OK; -} - - -int HttpPipelinedStream::SendRequest(const HttpRequestHeaders& headers, - HttpResponseInfo* response, - const CompletionCallback& callback) { - CHECK(pipeline_id_); - CHECK(request_info_); - // TODO(simonjam): Proxy support will be needed here. - const std::string path = HttpUtil::PathForRequest(request_info_->url); - std::string request_line_ = base::StringPrintf("%s %s HTTP/1.1\r\n", - request_info_->method.c_str(), - path.c_str()); - return pipeline_->SendRequest(pipeline_id_, request_line_, headers, response, - callback); -} - -UploadProgress HttpPipelinedStream::GetUploadProgress() const { - return pipeline_->GetUploadProgress(pipeline_id_); -} - -int HttpPipelinedStream::ReadResponseHeaders( - const CompletionCallback& callback) { - return pipeline_->ReadResponseHeaders(pipeline_id_, callback); -} - -const HttpResponseInfo* HttpPipelinedStream::GetResponseInfo() const { - return pipeline_->GetResponseInfo(pipeline_id_); -} - -int HttpPipelinedStream::ReadResponseBody(IOBuffer* buf, int buf_len, - const CompletionCallback& callback) { - return pipeline_->ReadResponseBody(pipeline_id_, buf, buf_len, callback); -} - -void HttpPipelinedStream::Close(bool not_reusable) { - pipeline_->Close(pipeline_id_, not_reusable); -} - -HttpStream* HttpPipelinedStream::RenewStreamForAuth() { - if (pipeline_->usable()) { - return pipeline_->CreateNewStream(); - } - return NULL; -} - -bool HttpPipelinedStream::IsResponseBodyComplete() const { - return pipeline_->IsResponseBodyComplete(pipeline_id_); -} - -bool HttpPipelinedStream::CanFindEndOfResponse() const { - return pipeline_->CanFindEndOfResponse(pipeline_id_); -} - -bool HttpPipelinedStream::IsConnectionReused() const { - return pipeline_->IsConnectionReused(pipeline_id_); -} - -void HttpPipelinedStream::SetConnectionReused() { - pipeline_->SetConnectionReused(pipeline_id_); -} - -bool HttpPipelinedStream::IsConnectionReusable() const { - return pipeline_->usable(); -} - -int64 HttpPipelinedStream::GetTotalReceivedBytes() const { - return pipeline_->GetTotalReceivedBytes(pipeline_id_); -} - -bool HttpPipelinedStream::GetLoadTimingInfo( - LoadTimingInfo* load_timing_info) const { - return pipeline_->GetLoadTimingInfo(pipeline_id_, load_timing_info); -} - -void HttpPipelinedStream::GetSSLInfo(SSLInfo* ssl_info) { - pipeline_->GetSSLInfo(pipeline_id_, ssl_info); -} - -void HttpPipelinedStream::GetSSLCertRequestInfo( - SSLCertRequestInfo* cert_request_info) { - pipeline_->GetSSLCertRequestInfo(pipeline_id_, cert_request_info); -} - -bool HttpPipelinedStream::IsSpdyHttpStream() const { - return false; -} - -void HttpPipelinedStream::Drain(HttpNetworkSession* session) { - pipeline_->Drain(this, session); -} - -void HttpPipelinedStream::SetPriority(RequestPriority priority) { - // TODO(akalin): Plumb this through to |pipeline_| and its - // underlying ClientSocketHandle. -} - -const SSLConfig& HttpPipelinedStream::used_ssl_config() const { - return pipeline_->used_ssl_config(); -} - -const ProxyInfo& HttpPipelinedStream::used_proxy_info() const { - return pipeline_->used_proxy_info(); -} - -const BoundNetLog& HttpPipelinedStream::net_log() const { - return pipeline_->net_log(); -} - -bool HttpPipelinedStream::was_npn_negotiated() const { - return pipeline_->was_npn_negotiated(); -} - -NextProto HttpPipelinedStream::protocol_negotiated() const { - return pipeline_->protocol_negotiated(); -} - -} // namespace net diff --git a/chromium/net/http/http_pipelined_stream.h b/chromium/net/http/http_pipelined_stream.h deleted file mode 100644 index 7a853abc1a7..00000000000 --- a/chromium/net/http/http_pipelined_stream.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_PIPELINED_STREAM_H_ -#define NET_HTTP_HTTP_PIPELINED_STREAM_H_ - -#include <string> - -#include "base/basictypes.h" -#include "net/base/net_log.h" -#include "net/http/http_stream.h" -#include "net/socket/ssl_client_socket.h" - -namespace net { - -class BoundNetLog; -class HttpPipelinedConnectionImpl; -class HttpResponseInfo; -class HttpRequestHeaders; -struct HttpRequestInfo; -class IOBuffer; -class ProxyInfo; -struct SSLConfig; - -// HttpPipelinedStream is the pipelined implementation of HttpStream. It has -// very little code in it. Instead, it serves as the client's interface to the -// pipelined connection, where all the work happens. -// -// In the case of pipelining failures, these functions may return -// ERR_PIPELINE_EVICTION. In that case, the client should retry the HTTP -// request without pipelining. -class HttpPipelinedStream : public HttpStream { - public: - HttpPipelinedStream(HttpPipelinedConnectionImpl* pipeline, - int pipeline_id); - virtual ~HttpPipelinedStream(); - - // HttpStream methods: - virtual int InitializeStream(const HttpRequestInfo* request_info, - RequestPriority priority, - const BoundNetLog& net_log, - const CompletionCallback& callback) OVERRIDE; - - virtual int SendRequest(const HttpRequestHeaders& headers, - HttpResponseInfo* response, - const CompletionCallback& callback) OVERRIDE; - - virtual UploadProgress GetUploadProgress() const OVERRIDE; - - virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE; - - virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; - - virtual int ReadResponseBody(IOBuffer* buf, int buf_len, - const CompletionCallback& callback) OVERRIDE; - - virtual void Close(bool not_reusable) OVERRIDE; - - virtual HttpStream* RenewStreamForAuth() OVERRIDE; - - virtual bool IsResponseBodyComplete() const OVERRIDE; - - virtual bool CanFindEndOfResponse() const OVERRIDE; - - virtual bool IsConnectionReused() const OVERRIDE; - - virtual void SetConnectionReused() OVERRIDE; - - virtual bool IsConnectionReusable() const OVERRIDE; - - virtual int64 GetTotalReceivedBytes() const OVERRIDE; - - virtual bool GetLoadTimingInfo( - LoadTimingInfo* load_timing_info) const OVERRIDE; - - virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE; - - virtual void GetSSLCertRequestInfo( - SSLCertRequestInfo* cert_request_info) OVERRIDE; - - virtual bool IsSpdyHttpStream() const OVERRIDE; - - virtual void Drain(HttpNetworkSession* session) OVERRIDE; - - virtual void SetPriority(RequestPriority priority) OVERRIDE; - - // The SSLConfig used to establish this stream's pipeline. - const SSLConfig& used_ssl_config() const; - - // The ProxyInfo used to establish this this stream's pipeline. - const ProxyInfo& used_proxy_info() const; - - // The BoundNetLog of this stream's pipelined connection. - const BoundNetLog& net_log() const; - - // True if this stream's pipeline was NPN negotiated. - bool was_npn_negotiated() const; - - // Protocol negotiated with the server. - NextProto protocol_negotiated() const; - - private: - HttpPipelinedConnectionImpl* pipeline_; - - int pipeline_id_; - - const HttpRequestInfo* request_info_; - - DISALLOW_COPY_AND_ASSIGN(HttpPipelinedStream); -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_PIPELINED_STREAM_H_ diff --git a/chromium/net/http/http_proxy_client_socket.cc b/chromium/net/http/http_proxy_client_socket.cc index 7037616ae5a..3d9eadd4835 100644 --- a/chromium/net/http/http_proxy_client_socket.cc +++ b/chromium/net/http/http_proxy_client_socket.cc @@ -237,11 +237,11 @@ int HttpProxyClientSocket::Write(IOBuffer* buf, int buf_len, return transport_->socket()->Write(buf, buf_len, callback); } -bool HttpProxyClientSocket::SetReceiveBufferSize(int32 size) { +int HttpProxyClientSocket::SetReceiveBufferSize(int32 size) { return transport_->socket()->SetReceiveBufferSize(size); } -bool HttpProxyClientSocket::SetSendBufferSize(int32 size) { +int HttpProxyClientSocket::SetSendBufferSize(int32 size) { return transport_->socket()->SetSendBufferSize(size); } @@ -276,7 +276,7 @@ int HttpProxyClientSocket::PrepareForAuthRestart() { int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { if (keep_alive && transport_->socket()->IsConnectedAndIdle()) { next_state_ = STATE_GENERATE_AUTH_TOKEN; - transport_->set_is_reused(true); + transport_->set_reuse_type(ClientSocketHandle::REUSED_IDLE); } else { // This assumes that the underlying transport socket is a TCP socket, // since only TCP sockets are restartable. diff --git a/chromium/net/http/http_proxy_client_socket.h b/chromium/net/http/http_proxy_client_socket.h index fca054f9c44..a2682c505b1 100644 --- a/chromium/net/http/http_proxy_client_socket.h +++ b/chromium/net/http/http_proxy_client_socket.h @@ -82,8 +82,8 @@ class HttpProxyClientSocket : public ProxyClientSocket { virtual int Write(IOBuffer* buf, int buf_len, const CompletionCallback& callback) OVERRIDE; - virtual bool SetReceiveBufferSize(int32 size) OVERRIDE; - virtual bool SetSendBufferSize(int32 size) OVERRIDE; + virtual int SetReceiveBufferSize(int32 size) OVERRIDE; + virtual int SetSendBufferSize(int32 size) OVERRIDE; virtual int GetPeerAddress(IPEndPoint* address) const OVERRIDE; virtual int GetLocalAddress(IPEndPoint* address) const OVERRIDE; diff --git a/chromium/net/http/http_proxy_client_socket_pool.cc b/chromium/net/http/http_proxy_client_socket_pool.cc index 8d691b7dd80..829a5e78314 100644 --- a/chromium/net/http/http_proxy_client_socket_pool.cc +++ b/chromium/net/http/http_proxy_client_socket_pool.cc @@ -206,7 +206,7 @@ int HttpProxyConnectJob::DoSSLConnect() { if (params_->tunnel()) { SpdySessionKey key(params_->destination().host_port_pair(), ProxyServer::Direct(), - kPrivacyModeDisabled); + PRIVACY_MODE_DISABLED); if (params_->spdy_session_pool()->FindAvailableSession(key, net_log())) { using_spdy_ = true; next_state_ = STATE_SPDY_PROXY_CREATE_STREAM; @@ -237,6 +237,13 @@ int HttpProxyConnectJob::DoSSLConnectComplete(int result) { return ERR_PROXY_CERTIFICATE_INVALID; } } + // A SPDY session to the proxy completed prior to resolving the proxy + // hostname. Surface this error, and allow the delegate to retry. + // See crbug.com/334413. + if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) { + DCHECK(!transport_socket_handle_->socket()); + return ERR_SPDY_SESSION_ALREADY_EXISTS; + } if (result < 0) { if (transport_socket_handle_->socket()) transport_socket_handle_->socket()->Disconnect(); @@ -302,7 +309,7 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStream() { DCHECK(params_->tunnel()); SpdySessionKey key(params_->destination().host_port_pair(), ProxyServer::Direct(), - kPrivacyModeDisabled); + PRIVACY_MODE_DISABLED); SpdySessionPool* spdy_pool = params_->spdy_session_pool(); base::WeakPtr<SpdySession> spdy_session = spdy_pool->FindAvailableSession(key, net_log()); @@ -315,11 +322,11 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStream() { } } else { // Create a session direct to the proxy itself - int rv = spdy_pool->CreateAvailableSessionFromSocket( - key, transport_socket_handle_.Pass(), - net_log(), OK, &spdy_session, /*using_ssl_*/ true); - if (rv < 0) - return rv; + spdy_session = + spdy_pool->CreateAvailableSessionFromSocket( + key, transport_socket_handle_.Pass(), + net_log(), OK, /*using_ssl_*/ true); + DCHECK(spdy_session); } next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE; diff --git a/chromium/net/http/http_proxy_client_socket_pool_unittest.cc b/chromium/net/http/http_proxy_client_socket_pool_unittest.cc index a70fe6ab067..d7955d2c058 100644 --- a/chromium/net/http/http_proxy_client_socket_pool_unittest.cc +++ b/chromium/net/http/http_proxy_client_socket_pool_unittest.cc @@ -100,8 +100,8 @@ class HttpProxyClientSocketPoolTest } void AddAuthToCache() { - const base::string16 kFoo(ASCIIToUTF16("foo")); - const base::string16 kBar(ASCIIToUTF16("bar")); + const base::string16 kFoo(base::ASCIIToUTF16("foo")); + const base::string16 kBar(base::ASCIIToUTF16("bar")); GURL proxy_url(GetParam().proxy_type == HTTP ? (std::string("http://") + kHttpProxyHost) : (std::string("https://") + kHttpsProxyHost)); @@ -135,7 +135,7 @@ class HttpProxyClientSocketPoolTest NULL, HostPortPair(kHttpsProxyHost, 443), SSLConfig(), - kPrivacyModeDisabled, + PRIVACY_MODE_DISABLED, 0, false, false); @@ -248,12 +248,9 @@ INSTANTIATE_TEST_CASE_P( HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY31), HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY31), HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY31), - HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4a2), - HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4a2), - HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4a2), - HttpProxyClientSocketPoolTestParams(HTTP, kProtoHTTP2Draft04), - HttpProxyClientSocketPoolTestParams(HTTPS, kProtoHTTP2Draft04), - HttpProxyClientSocketPoolTestParams(SPDY, kProtoHTTP2Draft04))); + HttpProxyClientSocketPoolTestParams(HTTP, kProtoSPDY4), + HttpProxyClientSocketPoolTestParams(HTTPS, kProtoSPDY4), + HttpProxyClientSocketPoolTestParams(SPDY, kProtoSPDY4))); TEST_P(HttpProxyClientSocketPoolTest, NoTunnel) { Initialize(NULL, 0, NULL, 0, NULL, 0, NULL, 0); diff --git a/chromium/net/http/http_request_headers.cc b/chromium/net/http/http_request_headers.cc index 8c9c4289336..9348e3e2778 100644 --- a/chromium/net/http/http_request_headers.cc +++ b/chromium/net/http/http_request_headers.cc @@ -9,20 +9,9 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" +#include "net/http/http_log_util.h" #include "net/http/http_util.h" -namespace { - -bool ShouldShowHttpHeaderValue(const std::string& header_name) { -#if defined(SPDY_PROXY_AUTH_ORIGIN) - if (header_name == "Proxy-Authorization") - return false; -#endif - return true; -} - -} // namespace - namespace net { const char HttpRequestHeaders::kGetMethod[] = "GET"; @@ -197,17 +186,17 @@ std::string HttpRequestHeaders::ToString() const { base::Value* HttpRequestHeaders::NetLogCallback( const std::string* request_line, - NetLog::LogLevel /* log_level */) const { + NetLog::LogLevel log_level) const { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetString("line", *request_line); base::ListValue* headers = new base::ListValue(); for (HeaderVector::const_iterator it = headers_.begin(); it != headers_.end(); ++it) { + std::string log_value = ElideHeaderValueForNetLog( + log_level, it->key, it->value); headers->Append(new base::StringValue( base::StringPrintf("%s: %s", - it->key.c_str(), - (ShouldShowHttpHeaderValue(it->key) ? - it->value.c_str() : "[elided]")))); + it->key.c_str(), log_value.c_str()))); } dict->Set("headers", headers); return dict; diff --git a/chromium/net/http/http_request_info.cc b/chromium/net/http/http_request_info.cc index bffc96ccfae..794aa1c3a7c 100644 --- a/chromium/net/http/http_request_info.cc +++ b/chromium/net/http/http_request_info.cc @@ -10,7 +10,7 @@ HttpRequestInfo::HttpRequestInfo() : upload_data_stream(NULL), load_flags(0), motivation(NORMAL_MOTIVATION), - privacy_mode(kPrivacyModeDisabled) { + privacy_mode(PRIVACY_MODE_DISABLED) { } HttpRequestInfo::~HttpRequestInfo() {} diff --git a/chromium/net/http/http_response_body_drainer.cc b/chromium/net/http/http_response_body_drainer.cc index a1ba35ad31e..91dbbf70984 100644 --- a/chromium/net/http/http_response_body_drainer.cc +++ b/chromium/net/http/http_response_body_drainer.cc @@ -14,8 +14,7 @@ namespace net { HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStreamBase* stream) - : read_size_(0), - stream_(stream), + : stream_(stream), next_state_(STATE_NONE), total_read_(0), session_(NULL) {} @@ -23,25 +22,7 @@ HttpResponseBodyDrainer::HttpResponseBodyDrainer(HttpStreamBase* stream) HttpResponseBodyDrainer::~HttpResponseBodyDrainer() {} void HttpResponseBodyDrainer::Start(HttpNetworkSession* session) { - StartWithSize(session, kDrainBodyBufferSize); -} - -void HttpResponseBodyDrainer::StartWithSize(HttpNetworkSession* session, - int num_bytes_to_drain) { - DCHECK_LE(0, num_bytes_to_drain); - // TODO(simonjam): Consider raising this limit if we're pipelining. If we have - // a bunch of responses in the pipeline, we should be less willing to give up - // while draining. - if (num_bytes_to_drain > kDrainBodyBufferSize) { - Finish(ERR_RESPONSE_BODY_TOO_BIG_TO_DRAIN); - return; - } else if (num_bytes_to_drain == 0) { - Finish(OK); - return; - } - - read_size_ = num_bytes_to_drain; - read_buf_ = new IOBuffer(read_size_); + read_buf_ = new IOBuffer(kDrainBodyBufferSize); next_state_ = STATE_DRAIN_RESPONSE_BODY; int rv = DoLoop(OK); @@ -88,7 +69,7 @@ int HttpResponseBodyDrainer::DoDrainResponseBody() { return stream_->ReadResponseBody( read_buf_.get(), - read_size_ - total_read_, + kDrainBodyBufferSize - total_read_, base::Bind(&HttpResponseBodyDrainer::OnIOComplete, base::Unretained(this))); } diff --git a/chromium/net/http/http_response_body_drainer.h b/chromium/net/http/http_response_body_drainer.h index 284d08a99cd..5c7713e254b 100644 --- a/chromium/net/http/http_response_body_drainer.h +++ b/chromium/net/http/http_response_body_drainer.h @@ -35,9 +35,6 @@ class NET_EXPORT_PRIVATE HttpResponseBodyDrainer { // doesn't complete immediately, it will add itself to |session|. void Start(HttpNetworkSession* session); - // As above, but stop reading once |num_bytes_to_drain| has been reached. - void StartWithSize(HttpNetworkSession* session, int num_bytes_to_drain); - private: enum State { STATE_DRAIN_RESPONSE_BODY, @@ -54,7 +51,6 @@ class NET_EXPORT_PRIVATE HttpResponseBodyDrainer { void OnTimerFired(); void Finish(int result); - int read_size_; scoped_refptr<IOBuffer> read_buf_; const scoped_ptr<HttpStreamBase> stream_; State next_state_; diff --git a/chromium/net/http/http_response_body_drainer_unittest.cc b/chromium/net/http/http_response_body_drainer_unittest.cc index f587b908529..42efa57f919 100644 --- a/chromium/net/http/http_response_body_drainer_unittest.cc +++ b/chromium/net/http/http_response_body_drainer_unittest.cc @@ -93,9 +93,6 @@ class MockHttpStream : public HttpStream { virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE { return ERR_UNEXPECTED; } - virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE { - return NULL; - } virtual bool CanFindEndOfResponse() const OVERRIDE { return true; } virtual bool IsConnectionReused() const OVERRIDE { return false; } @@ -305,22 +302,6 @@ TEST_F(HttpResponseBodyDrainerTest, DrainBodyTooLarge) { EXPECT_TRUE(result_waiter_.WaitForResult()); } -TEST_F(HttpResponseBodyDrainerTest, StartBodyTooLarge) { - int too_many_chunks = - HttpResponseBodyDrainer::kDrainBodyBufferSize / kMagicChunkSize; - too_many_chunks += 1; // Now it's too large. - - mock_stream_->set_num_chunks(0); - drainer_->StartWithSize(session_.get(), too_many_chunks * kMagicChunkSize); - EXPECT_TRUE(result_waiter_.WaitForResult()); -} - -TEST_F(HttpResponseBodyDrainerTest, StartWithNothingToDo) { - mock_stream_->set_num_chunks(0); - drainer_->StartWithSize(session_.get(), 0); - EXPECT_FALSE(result_waiter_.WaitForResult()); -} - } // namespace } // namespace net diff --git a/chromium/net/http/http_response_headers.cc b/chromium/net/http/http_response_headers.cc index 289facd4f9e..b7ef98a5980 100644 --- a/chromium/net/http/http_response_headers.cc +++ b/chromium/net/http/http_response_headers.cc @@ -11,6 +11,7 @@ #include <algorithm> +#include "base/format_macros.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/pickle.h" @@ -21,6 +22,8 @@ #include "base/time/time.h" #include "base/values.h" #include "net/base/escape.h" +#include "net/http/http_byte_range.h" +#include "net/http/http_log_util.h" #include "net/http/http_util.h" using base::StringPiece; @@ -113,16 +116,10 @@ void CheckDoesNotHaveEmbededNulls(const std::string& str) { CHECK(str.find('\0') == std::string::npos); } -bool ShouldShowHttpHeaderValue(const std::string& header_name) { -#if defined(SPDY_PROXY_AUTH_ORIGIN) - if (header_name == "Proxy-Authenticate") - return false; -#endif - return true; -} - } // namespace +const char HttpResponseHeaders::kContentRange[] = "Content-Range"; + struct HttpResponseHeaders::ParsedHeader { // A header "continuation" contains only a subsequent value for the // preceding header. (Header values are comma separated.) @@ -372,6 +369,32 @@ void HttpResponseHeaders::ReplaceStatusLine(const std::string& new_status) { MergeWithHeaders(new_raw_headers, empty_to_remove); } +void HttpResponseHeaders::UpdateWithNewRange( + const HttpByteRange& byte_range, + int64 resource_size, + bool replace_status_line) { + DCHECK(byte_range.IsValid()); + DCHECK(byte_range.HasFirstBytePosition()); + DCHECK(byte_range.HasLastBytePosition()); + + const char kLengthHeader[] = "Content-Length"; + const char kRangeHeader[] = "Content-Range"; + + RemoveHeader(kLengthHeader); + RemoveHeader(kRangeHeader); + + int64 start = byte_range.first_byte_position(); + int64 end = byte_range.last_byte_position(); + int64 range_len = end - start + 1; + + if (replace_status_line) + ReplaceStatusLine("HTTP/1.1 206 Partial Content"); + + AddHeader(base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64, + kRangeHeader, start, end, resource_size)); + AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, range_len)); +} + void HttpResponseHeaders::Parse(const std::string& raw_input) { raw_headers_.reserve(raw_input.size()); @@ -827,7 +850,7 @@ void HttpResponseHeaders::AddChallengeHeaders(HeaderSet* result) { } void HttpResponseHeaders::AddHopContentRangeHeaders(HeaderSet* result) { - result->insert("content-range"); + result->insert(kContentRange); } void HttpResponseHeaders::AddSecurityStateHeaders(HeaderSet* result) { @@ -894,7 +917,8 @@ bool HttpResponseHeaders::IsRedirectResponseCode(int response_code) { return (response_code == 301 || response_code == 302 || response_code == 303 || - response_code == 307); + response_code == 307 || + response_code == 308); } // From RFC 2616 section 13.2.4: @@ -993,6 +1017,9 @@ TimeDelta HttpResponseHeaders::GetFreshnessLifetime( // time, if, based solely on the origin server's Expires or max-age value, // the cached response is stale.) // + // https://datatracker.ietf.org/doc/draft-reschke-http-status-308/ is an + // experimental RFC that adds 308 permanent redirect as well, for which "any + // future references ... SHOULD use one of the returned URIs." if ((response_code_ == 200 || response_code_ == 203 || response_code_ == 206) && !HasHeaderValue("cache-control", "must-revalidate")) { @@ -1006,8 +1033,10 @@ TimeDelta HttpResponseHeaders::GetFreshnessLifetime( } // These responses are implicitly fresh (unless otherwise overruled): - if (response_code_ == 300 || response_code_ == 301 || response_code_ == 410) - return TimeDelta::FromMicroseconds(kint64max); + if (response_code_ == 300 || response_code_ == 301 || response_code_ == 308 || + response_code_ == 410) { + return TimeDelta::Max(); + } return TimeDelta(); // not fresh } @@ -1207,7 +1236,7 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position, void* iter = NULL; std::string content_range_spec; *first_byte_position = *last_byte_position = *instance_length = -1; - if (!EnumerateHeader(&iter, "content-range", &content_range_spec)) + if (!EnumerateHeader(&iter, kContentRange, &content_range_spec)) return false; // If the header value is empty, we have an invalid header. @@ -1308,7 +1337,7 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position, } base::Value* HttpResponseHeaders::NetLogCallback( - NetLog::LogLevel /* log_level */) const { + NetLog::LogLevel log_level) const { base::DictionaryValue* dict = new base::DictionaryValue(); base::ListValue* headers = new base::ListValue(); headers->Append(new base::StringValue(GetStatusLine())); @@ -1316,12 +1345,10 @@ base::Value* HttpResponseHeaders::NetLogCallback( std::string name; std::string value; while (EnumerateHeaderLines(&iterator, &name, &value)) { + std::string log_value = ElideHeaderValueForNetLog(log_level, name, value); headers->Append( new base::StringValue( - base::StringPrintf("%s: %s", - name.c_str(), - (ShouldShowHttpHeaderValue(name) ? - value.c_str() : "[elided]")))); + base::StringPrintf("%s: %s", name.c_str(), log_value.c_str()))); } dict->Set("headers", headers); return dict; @@ -1364,59 +1391,4 @@ bool HttpResponseHeaders::IsChunkEncoded() const { HasHeaderValue("Transfer-Encoding", "chunked"); } -#if defined(SPDY_PROXY_AUTH_ORIGIN) -bool HttpResponseHeaders::GetChromeProxyBypassDuration( - const std::string& action_prefix, - base::TimeDelta* duration) const { - void* iter = NULL; - std::string value; - std::string name = "chrome-proxy"; - - while (EnumerateHeader(&iter, name, &value)) { - if (value.size() > action_prefix.size()) { - if (LowerCaseEqualsASCII(value.begin(), - value.begin() + action_prefix.size(), - action_prefix.c_str())) { - int64 seconds; - if (!base::StringToInt64( - StringPiece(value.begin() + action_prefix.size(), value.end()), - &seconds) || seconds < 0) { - continue; // In case there is a well formed instruction. - } - *duration = TimeDelta::FromSeconds(seconds); - return true; - } - } - } - return false; -} - -bool HttpResponseHeaders::GetChromeProxyInfo( - ChromeProxyInfo* proxy_info) const { - DCHECK(proxy_info); - proxy_info->bypass_all = false; - proxy_info->bypass_duration = base::TimeDelta(); - - // Support header of the form Chrome-Proxy: bypass|block=<duration>, where - // <duration> is the number of seconds to wait before retrying - // the proxy. If the duration is 0, then the default proxy retry delay - // (specified in |ProxyList::UpdateRetryInfoOnFallback|) will be used. - // 'bypass' instructs Chrome to bypass the currently connected Chrome proxy, - // whereas 'block' instructs Chrome to bypass all available Chrome proxies. - - // 'block' takes precedence over 'bypass', so look for it first. - // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop. - if (GetChromeProxyBypassDuration("block=", &proxy_info->bypass_duration)) { - proxy_info->bypass_all = true; - return true; - } - - // Next, look for 'bypass'. - if (GetChromeProxyBypassDuration("bypass=", &proxy_info->bypass_duration)) - return true; - - return false; -} -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) - } // namespace net diff --git a/chromium/net/http/http_response_headers.h b/chromium/net/http/http_response_headers.h index e54ac5df669..2d306c95ca0 100644 --- a/chromium/net/http/http_response_headers.h +++ b/chromium/net/http/http_response_headers.h @@ -26,6 +26,8 @@ class TimeDelta; namespace net { +class HttpByteRange; + // HttpResponseHeaders: parses and holds HTTP response headers. class NET_EXPORT HttpResponseHeaders : public base::RefCountedThreadSafe<HttpResponseHeaders> { @@ -41,6 +43,8 @@ class NET_EXPORT HttpResponseHeaders static const PersistOptions PERSIST_SANS_RANGES = 1 << 4; static const PersistOptions PERSIST_SANS_SECURITY_STATE = 1 << 5; + static const char kContentRange[]; + // Parses the given raw_headers. raw_headers should be formatted thus: // includes the http status response line, each line is \0-terminated, and // it's terminated by an empty line (ie, 2 \0s in a row). @@ -81,6 +85,15 @@ class NET_EXPORT HttpResponseHeaders // not have any EOL). void ReplaceStatusLine(const std::string& new_status); + // Updates headers (Content-Length and Content-Range) in the |headers| to + // include the right content length and range for |byte_range|. This also + // updates HTTP status line if |replace_status_line| is true. + // |byte_range| must have a valid, bounded range (i.e. coming from a valid + // response or should be usable for a response). + void UpdateWithNewRange(const HttpByteRange& byte_range, + int64 resource_size, + bool replace_status_line); + // Creates a normalized header string. The output will be formatted exactly // like so: // HTTP/<version> <status_code> <status_text>\n @@ -250,28 +263,6 @@ class NET_EXPORT HttpResponseHeaders // Returns true if the response is chunk-encoded. bool IsChunkEncoded() const; -#if defined (SPDY_PROXY_AUTH_ORIGIN) - // Contains instructions contained in the Chrome-Proxy header. - struct ChromeProxyInfo { - ChromeProxyInfo() : bypass_all(false) {} - - // True if Chrome should bypass all available Chrome proxies. False if only - // the currently connected Chrome proxy should be bypassed. - bool bypass_all; - - // Amount of time to bypass the Chrome proxy or proxies. - base::TimeDelta bypass_duration; - }; - - // Returns true if the Chrome-Proxy header is present and contains a bypass - // delay. Sets |proxy_info->bypass_duration| to the specified delay if greater - // than 0, and to 0 otherwise to indicate that the default proxy delay - // (as specified in |ProxyList::UpdateRetryInfoOnFallback|) should be used. - // If all available Chrome proxies should by bypassed, |bypass_all| is set to - // true. |proxy_info| must be non-NULL. - bool GetChromeProxyInfo(ChromeProxyInfo* proxy_info) const; -#endif - // Creates a Value for use with the NetLog containing the response headers. base::Value* NetLogCallback(NetLog::LogLevel log_level) const; @@ -370,13 +361,6 @@ class NET_EXPORT HttpResponseHeaders // Adds the set of transport security state headers. static void AddSecurityStateHeaders(HeaderSet* header_names); -#if defined(SPDY_PROXY_AUTH_ORIGIN) - // Searches for the specified Chrome-Proxy action, and if present interprets - // its value as a duration in seconds. - bool GetChromeProxyBypassDuration(const std::string& action_prefix, - base::TimeDelta* duration) const; -#endif - // We keep a list of ParsedHeader objects. These tell us where to locate the // header-value pairs within raw_headers_. HeaderList parsed_; diff --git a/chromium/net/http/http_response_headers_unittest.cc b/chromium/net/http/http_response_headers_unittest.cc index 4be74783b74..cc236d183e8 100644 --- a/chromium/net/http/http_response_headers_unittest.cc +++ b/chromium/net/http/http_response_headers_unittest.cc @@ -9,6 +9,7 @@ #include "base/pickle.h" #include "base/time/time.h" #include "base/values.h" +#include "net/http/http_byte_range.h" #include "net/http/http_response_headers.h" #include "testing/gtest/include/gtest/gtest.h" @@ -807,6 +808,11 @@ TEST(HttpResponseHeadersTest, RequiresValidation) { "\n", false }, + // another cached permanent redirect + { "HTTP/1.1 308 Permanent Redirect\n" + "\n", + false + }, // cached redirect: not reusable even though by default it would be { "HTTP/1.1 300 Multiple Choices\n" "Cache-Control: no-cache\n" @@ -1851,6 +1857,58 @@ TEST(HttpResponseHeadersTest, ReplaceStatus) { } } +TEST(HttpResponseHeadersTest, UpdateWithNewRange) { + const struct { + const char* orig_headers; + const char* expected_headers; + const char* expected_headers_with_replaced_status; + } tests[] = { + { "HTTP/1.1 200 OK\n" + "Content-Length: 450\n", + + "HTTP/1.1 200 OK\n" + "Content-Range: bytes 3-5/450\n" + "Content-Length: 3\n", + + "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 3-5/450\n" + "Content-Length: 3\n", + }, + { "HTTP/1.1 200 OK\n" + "Content-Length: 5\n", + + "HTTP/1.1 200 OK\n" + "Content-Range: bytes 3-5/5\n" + "Content-Length: 3\n", + + "HTTP/1.1 206 Partial Content\n" + "Content-Range: bytes 3-5/5\n" + "Content-Length: 3\n", + }, + }; + const net::HttpByteRange range = net::HttpByteRange::Bounded(3, 5); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string orig_headers(tests[i].orig_headers); + std::replace(orig_headers.begin(), orig_headers.end(), '\n', '\0'); + scoped_refptr<net::HttpResponseHeaders> parsed( + new net::HttpResponseHeaders(orig_headers + '\0')); + int64 content_size = parsed->GetContentLength(); + std::string resulting_headers; + + // Update headers without replacing status line. + parsed->UpdateWithNewRange(range, content_size, false); + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(tests[i].expected_headers), resulting_headers); + + // Replace status line too. + parsed->UpdateWithNewRange(range, content_size, true); + parsed->GetNormalizedHeaders(&resulting_headers); + EXPECT_EQ(std::string(tests[i].expected_headers_with_replaced_status), + resulting_headers); + } +} + TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) { std::string headers("HTTP/1.1 404\n" "Content-Length: 450\n" @@ -1877,156 +1935,3 @@ TEST(HttpResponseHeadersTest, ToNetLogParamAndBackAgain) { parsed->GetNormalizedHeaders(&normalized_recreated); EXPECT_EQ(normalized_parsed, normalized_recreated); } - -#if defined(SPDY_PROXY_AUTH_ORIGIN) -TEST(HttpResponseHeadersTest, GetProxyBypassInfo) { - const struct { - const char* headers; - bool expected_result; - int64 expected_retry_delay; - bool expected_bypass_all; - } tests[] = { - { "HTTP/1.1 200 OK\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=0\n" - "Content-Length: 999\n", - true, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=-1\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=xyz\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass\n" - "Content-Length: 999\n", - false, - 0, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: foo=abc, bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=86400, bar=abc\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=3600\n" - "Chrome-Proxy: bypass=86400\n" - "Content-Length: 999\n", - true, - 3600, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=3600, bypass=86400\n" - "Content-Length: 999\n", - true, - 3600, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=, bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass\n" - "Chrome-Proxy: bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: block=, block=3600\n" - "Content-Length: 999\n", - true, - 3600, - true, - }, - { "HTTP/1.1 200 OK\n" - "connection: keep-alive\n" - "Chrome-Proxy: bypass=86400, block=3600\n" - "Content-Length: 999\n", - true, - 3600, - true, - }, - { "HTTP/1.1 200 OK\n" - "connection: proxy-bypass\n" - "Chrome-Proxy: block=, bypass=86400\n" - "Content-Length: 999\n", - true, - 86400, - false, - }, - }; - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - std::string headers(tests[i].headers); - HeadersToRaw(&headers); - scoped_refptr<net::HttpResponseHeaders> parsed( - new net::HttpResponseHeaders(headers)); - - net::HttpResponseHeaders::ChromeProxyInfo chrome_proxy_info; - EXPECT_EQ(tests[i].expected_result, - parsed->GetChromeProxyInfo(&chrome_proxy_info)); - EXPECT_EQ(tests[i].expected_retry_delay, - chrome_proxy_info.bypass_duration.InSeconds()); - EXPECT_EQ(tests[i].expected_bypass_all, - chrome_proxy_info.bypass_all); - } -} -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) diff --git a/chromium/net/http/http_response_info.cc b/chromium/net/http/http_response_info.cc index 4f9e014cfb2..83896736a12 100644 --- a/chromium/net/http/http_response_info.cc +++ b/chromium/net/http/http_response_info.cc @@ -113,6 +113,7 @@ HttpResponseInfo::HttpResponseInfo(const HttpResponseInfo& rhs) was_fetched_via_spdy(rhs.was_fetched_via_spdy), was_npn_negotiated(rhs.was_npn_negotiated), was_fetched_via_proxy(rhs.was_fetched_via_proxy), + proxy_server(rhs.proxy_server), did_use_http_auth(rhs.did_use_http_auth), socket_address(rhs.socket_address), npn_negotiated_protocol(rhs.npn_negotiated_protocol), @@ -135,6 +136,7 @@ HttpResponseInfo& HttpResponseInfo::operator=(const HttpResponseInfo& rhs) { server_data_unavailable = rhs.server_data_unavailable; network_accessed = rhs.network_accessed; was_fetched_via_spdy = rhs.was_fetched_via_spdy; + proxy_server = rhs.proxy_server; was_npn_negotiated = rhs.was_npn_negotiated; was_fetched_via_proxy = rhs.was_fetched_via_proxy; did_use_http_auth = rhs.did_use_http_auth; @@ -340,8 +342,8 @@ void HttpResponseInfo::Persist(Pickle* pickle, for (SignedCertificateTimestampAndStatusList::const_iterator it = ssl_info.signed_certificate_timestamps.begin(); it != ssl_info.signed_certificate_timestamps.end(); ++it) { - it->sct_->Persist(pickle); - pickle->WriteUInt16(it->status_); + it->sct->Persist(pickle); + pickle->WriteUInt16(it->status); } } } @@ -367,10 +369,8 @@ HttpResponseInfo::ConnectionInfo HttpResponseInfo::ConnectionInfoFromNextProto( case kProtoSPDY3: case kProtoSPDY31: return CONNECTION_INFO_SPDY3; - case kProtoSPDY4a2: - return CONNECTION_INFO_SPDY4A2; - case kProtoHTTP2Draft04: - return CONNECTION_INFO_HTTP2_DRAFT_04; + case kProtoSPDY4: + return CONNECTION_INFO_SPDY4; case kProtoQUIC1SPDY3: return CONNECTION_INFO_QUIC1_SPDY3; @@ -395,10 +395,10 @@ std::string HttpResponseInfo::ConnectionInfoToString( return "spdy/2"; case CONNECTION_INFO_SPDY3: return "spdy/3"; - case CONNECTION_INFO_SPDY4A2: - return "spdy/4a2"; - case CONNECTION_INFO_HTTP2_DRAFT_04: - return "HTTP-draft-04/2.0"; + case CONNECTION_INFO_SPDY4: + // This is the HTTP/2 draft 12 identifier. For internal + // consistency, HTTP/2 is named SPDY4 within Chromium. + return "h2-12"; case CONNECTION_INFO_QUIC1_SPDY3: return "quic/1+spdy/3"; case NUM_OF_CONNECTION_INFOS: diff --git a/chromium/net/http/http_response_info.h b/chromium/net/http/http_response_info.h index f0908b0ab46..efd76fe7b51 100644 --- a/chromium/net/http/http_response_info.h +++ b/chromium/net/http/http_response_info.h @@ -37,9 +37,8 @@ class NET_EXPORT HttpResponseInfo { CONNECTION_INFO_HTTP1 = 1, CONNECTION_INFO_DEPRECATED_SPDY2 = 2, CONNECTION_INFO_SPDY3 = 3, - CONNECTION_INFO_SPDY4A2 = 4, + CONNECTION_INFO_SPDY4 = 4, CONNECTION_INFO_QUIC1_SPDY3 = 5, - CONNECTION_INFO_HTTP2_DRAFT_04 = 6, NUM_OF_CONNECTION_INFOS, }; @@ -59,12 +58,14 @@ class NET_EXPORT HttpResponseInfo { bool response_truncated) const; // The following is only defined if the request_time member is set. - // If this response was resurrected from cache, then this bool is set, and + // If this resource was found in the cache, then this bool is set, and // request_time may corresponds to a time "far" in the past. Note that // stale content (perhaps un-cacheable) may be fetched from cache subject to // the load flags specified on the request info. For example, this is done // when a user presses the back button to re-render pages, or at startup, // when reloading previously visited pages (without going over the network). + // Note also that under normal circumstances, was_cached is set to the correct + // value even if the request fails. bool was_cached; // True if the request was fetched from cache rather than the network @@ -84,8 +85,10 @@ class NET_EXPORT HttpResponseInfo { // True if the request was fetched via an explicit proxy. The proxy could // be any type of proxy, HTTP or SOCKS. Note, we do not know if a - // transparent proxy may have been involved. + // transparent proxy may have been involved. If true, |proxy_server| contains + // the name of the proxy server that was used. bool was_fetched_via_proxy; + HostPortPair proxy_server; // Whether the request use http proxy or server authentication. bool did_use_http_auth; diff --git a/chromium/net/http/http_security_headers.cc b/chromium/net/http/http_security_headers.cc index 0c3305f6e42..8d0c1465307 100644 --- a/chromium/net/http/http_security_headers.cc +++ b/chromium/net/http/http_security_headers.cc @@ -253,6 +253,7 @@ bool ParseHSTSHeader(const std::string& value, } switch (state) { + case DIRECTIVE_END: case AFTER_MAX_AGE: case AFTER_INCLUDE_SUBDOMAINS: case AFTER_UNKNOWN_LABEL: @@ -260,7 +261,6 @@ bool ParseHSTSHeader(const std::string& value, *include_subdomains = include_subdomains_candidate; return true; case START: - case DIRECTIVE_END: case AFTER_MAX_AGE_LABEL: case AFTER_MAX_AGE_EQUALS: return false; diff --git a/chromium/net/http/http_security_headers_unittest.cc b/chromium/net/http/http_security_headers_unittest.cc index 42a5ee98960..ce919ff81f3 100644 --- a/chromium/net/http/http_security_headers_unittest.cc +++ b/chromium/net/http/http_security_headers_unittest.cc @@ -90,8 +90,6 @@ TEST_F(HttpSecurityHeadersTest, BogusHeaders) { &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=-3488923", &max_age, &include_subdomains)); - EXPECT_FALSE(ParseHSTSHeader("max-age=3488923;", &max_age, - &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=3488923 e", &max_age, &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=3488923 includesubdomain", @@ -114,6 +112,16 @@ TEST_F(HttpSecurityHeadersTest, BogusHeaders) { &max_age, &include_subdomains)); EXPECT_FALSE(ParseHSTSHeader("max-age=34889 includesubdomains", &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(";;;; ;;;", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(";;;; includeSubDomains;;;", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(" includeSubDomains; ", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader(";", + &max_age, &include_subdomains)); + EXPECT_FALSE(ParseHSTSHeader("max-age; ;", + &max_age, &include_subdomains)); // Check the out args were not updated by checking the default // values for its predictable fields. @@ -223,6 +231,9 @@ TEST_F(HttpSecurityHeadersTest, ValidSTSHeaders) { EXPECT_EQ(expect_max_age, max_age); EXPECT_FALSE(include_subdomains); + EXPECT_TRUE(ParseHSTSHeader("max-age=3488923;", &max_age, + &include_subdomains)); + EXPECT_TRUE(ParseHSTSHeader(" Max-agE = 567", &max_age, &include_subdomains)); expect_max_age = base::TimeDelta::FromSeconds(567); @@ -311,6 +322,46 @@ TEST_F(HttpSecurityHeadersTest, ValidSTSHeaders) { EXPECT_TRUE(include_subdomains); EXPECT_TRUE(ParseHSTSHeader( + "max-age=394082038 ; incLudesUbdOmains;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + ";; max-age=394082038 ; incLudesUbdOmains; ;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + ";; max-age=394082038 ;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_FALSE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + ";; ; ; max-age=394082038;;; includeSubdomains ;; ;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( + "incLudesUbdOmains ; max-age=394082038 ;;", &max_age, + &include_subdomains)); + expect_max_age = base::TimeDelta::FromSeconds( + std::min(kMaxHSTSAgeSecs, static_cast<int64>(GG_INT64_C(394082038)))); + EXPECT_EQ(expect_max_age, max_age); + EXPECT_TRUE(include_subdomains); + + EXPECT_TRUE(ParseHSTSHeader( " max-age=0 ; incLudesUbdOmains ", &max_age, &include_subdomains)); expect_max_age = base::TimeDelta::FromSeconds(0); @@ -450,13 +501,15 @@ TEST_F(HttpSecurityHeadersTest, ValidPKPHeadersSHA256) { TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { TransportSecurityState state; - TransportSecurityState::DomainState domain_state; + TransportSecurityState::DomainState static_domain_state; // docs.google.com has preloaded pins. + const bool sni_enabled = true; std::string domain = "docs.google.com"; - EXPECT_TRUE(state.GetDomainState(domain, true, &domain_state)); - EXPECT_GT(domain_state.static_spki_hashes.size(), 1UL); - HashValueVector saved_hashes = domain_state.static_spki_hashes; + EXPECT_TRUE( + state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); + EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); + HashValueVector saved_hashes = static_domain_state.pkp.spki_hashes; // Add a header, which should only update the dynamic state. HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); @@ -471,49 +524,185 @@ TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { ssl_info.public_key_hashes.push_back(saved_hashes[0]); EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); - // Expect the preloaded state to remain unchanged. - std::string canonicalized_host = TransportSecurityState::CanonicalizeHost( - domain); - TransportSecurityState::DomainState static_domain_state; - EXPECT_TRUE(state.GetStaticDomainState(canonicalized_host, - true, - &static_domain_state)); + // Expect the static state to remain unchanged. + TransportSecurityState::DomainState new_static_domain_state; + EXPECT_TRUE(state.GetStaticDomainState( + domain, sni_enabled, &new_static_domain_state)); for (size_t i = 0; i < saved_hashes.size(); ++i) { - EXPECT_TRUE(HashValuesEqual( - saved_hashes[i])(static_domain_state.static_spki_hashes[i])); + EXPECT_TRUE(HashValuesEqual(saved_hashes[i])( + new_static_domain_state.pkp.spki_hashes[i])); } // Expect the dynamic state to reflect the header. TransportSecurityState::DomainState dynamic_domain_state; EXPECT_TRUE(state.GetDynamicDomainState(domain, &dynamic_domain_state)); - EXPECT_EQ(2UL, dynamic_domain_state.dynamic_spki_hashes.size()); + EXPECT_EQ(2UL, dynamic_domain_state.pkp.spki_hashes.size()); - HashValueVector::const_iterator hash = std::find_if( - dynamic_domain_state.dynamic_spki_hashes.begin(), - dynamic_domain_state.dynamic_spki_hashes.end(), - HashValuesEqual(good_hash)); - EXPECT_NE(dynamic_domain_state.dynamic_spki_hashes.end(), hash); + HashValueVector::const_iterator hash = + std::find_if(dynamic_domain_state.pkp.spki_hashes.begin(), + dynamic_domain_state.pkp.spki_hashes.end(), + HashValuesEqual(good_hash)); + EXPECT_NE(dynamic_domain_state.pkp.spki_hashes.end(), hash); - hash = std::find_if( - dynamic_domain_state.dynamic_spki_hashes.begin(), - dynamic_domain_state.dynamic_spki_hashes.end(), - HashValuesEqual(backup_hash)); - EXPECT_NE(dynamic_domain_state.dynamic_spki_hashes.end(), hash); + hash = std::find_if(dynamic_domain_state.pkp.spki_hashes.begin(), + dynamic_domain_state.pkp.spki_hashes.end(), + HashValuesEqual(backup_hash)); + EXPECT_NE(dynamic_domain_state.pkp.spki_hashes.end(), hash); // Expect the overall state to reflect the header, too. - EXPECT_TRUE(state.GetDomainState(domain, true, &domain_state)); - EXPECT_EQ(2UL, domain_state.dynamic_spki_hashes.size()); + EXPECT_TRUE(state.HasPublicKeyPins(domain, sni_enabled)); + HashValueVector hashes; + hashes.push_back(good_hash); + std::string failure_log; + EXPECT_TRUE( + state.CheckPublicKeyPins(domain, sni_enabled, hashes, &failure_log)); - hash = std::find_if(domain_state.dynamic_spki_hashes.begin(), - domain_state.dynamic_spki_hashes.end(), + TransportSecurityState::DomainState new_dynamic_domain_state; + EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state)); + EXPECT_EQ(2UL, new_dynamic_domain_state.pkp.spki_hashes.size()); + + hash = std::find_if(new_dynamic_domain_state.pkp.spki_hashes.begin(), + new_dynamic_domain_state.pkp.spki_hashes.end(), HashValuesEqual(good_hash)); - EXPECT_NE(domain_state.dynamic_spki_hashes.end(), hash); + EXPECT_NE(new_dynamic_domain_state.pkp.spki_hashes.end(), hash); + + hash = std::find_if(new_dynamic_domain_state.pkp.spki_hashes.begin(), + new_dynamic_domain_state.pkp.spki_hashes.end(), + HashValuesEqual(backup_hash)); + EXPECT_NE(new_dynamic_domain_state.pkp.spki_hashes.end(), hash); +} + +// Failing on win_chromium_rel. crbug.com/375538 +#if defined(OS_WIN) +#define MAYBE_UpdateDynamicPKPMaxAge0 DISABLED_UpdateDynamicPKPMaxAge0 +#else +#define MAYBE_UpdateDynamicPKPMaxAge0 UpdateDynamicPKPMaxAge0 +#endif +TEST_F(HttpSecurityHeadersTest, MAYBE_UpdateDynamicPKPMaxAge0) { + TransportSecurityState state; + TransportSecurityState::DomainState static_domain_state; + + // docs.google.com has preloaded pins. + const bool sni_enabled = true; + std::string domain = "docs.google.com"; + ASSERT_TRUE( + state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); + EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); + HashValueVector saved_hashes = static_domain_state.pkp.spki_hashes; + + // Add a header, which should only update the dynamic state. + HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); + std::string good_pin = GetTestPin(1, HASH_VALUE_SHA1); + std::string backup_pin = GetTestPin(2, HASH_VALUE_SHA1); + std::string header = "max-age = 10000; " + good_pin + "; " + backup_pin; + + // Construct a fake SSLInfo that will pass AddHPKPHeader's checks. + SSLInfo ssl_info; + ssl_info.public_key_hashes.push_back(good_hash); + ssl_info.public_key_hashes.push_back(saved_hashes[0]); + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + + // Expect the static state to remain unchanged. + TransportSecurityState::DomainState new_static_domain_state; + EXPECT_TRUE(state.GetStaticDomainState( + domain, sni_enabled, &new_static_domain_state)); + EXPECT_EQ(saved_hashes.size(), + new_static_domain_state.pkp.spki_hashes.size()); + for (size_t i = 0; i < saved_hashes.size(); ++i) { + EXPECT_TRUE(HashValuesEqual(saved_hashes[i])( + new_static_domain_state.pkp.spki_hashes[i])); + } - hash = std::find_if( - domain_state.dynamic_spki_hashes.begin(), - domain_state.dynamic_spki_hashes.end(), - HashValuesEqual(backup_hash)); - EXPECT_NE(domain_state.dynamic_spki_hashes.end(), hash); + // Expect the dynamic state to have pins. + TransportSecurityState::DomainState new_dynamic_domain_state; + EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state)); + EXPECT_EQ(2UL, new_dynamic_domain_state.pkp.spki_hashes.size()); + EXPECT_TRUE(new_dynamic_domain_state.HasPublicKeyPins()); + + // Now set another header with max-age=0, and check that the pins are + // cleared in the dynamic state only. + header = "max-age = 0; " + good_pin + "; " + backup_pin; + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + + // Expect the static state to remain unchanged. + TransportSecurityState::DomainState new_static_domain_state2; + EXPECT_TRUE(state.GetStaticDomainState( + domain, sni_enabled, &new_static_domain_state2)); + EXPECT_EQ(saved_hashes.size(), + new_static_domain_state2.pkp.spki_hashes.size()); + for (size_t i = 0; i < saved_hashes.size(); ++i) { + EXPECT_TRUE(HashValuesEqual(saved_hashes[i])( + new_static_domain_state2.pkp.spki_hashes[i])); + } + + // Expect the dynamic pins to be gone. + TransportSecurityState::DomainState new_dynamic_domain_state2; + EXPECT_FALSE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state2)); + + // Expect the exact-matching static policy to continue to apply, even + // though dynamic policy has been removed. (This policy may change in the + // future, in which case this test must be updated.) + EXPECT_TRUE(state.HasPublicKeyPins(domain, true)); + EXPECT_TRUE(state.ShouldSSLErrorsBeFatal(domain, true)); + std::string failure_log; + // Damage the hashes to cause a pin validation failure. + new_static_domain_state2.pkp.spki_hashes[0].data()[0] ^= 0x80; + new_static_domain_state2.pkp.spki_hashes[1].data()[0] ^= 0x80; + EXPECT_FALSE(state.CheckPublicKeyPins( + domain, true, new_static_domain_state2.pkp.spki_hashes, &failure_log)); + EXPECT_NE(0UL, failure_log.length()); +} +#undef MAYBE_UpdateDynamicPKPMaxAge0 + +// Tests that when a static HSTS and a static HPKP entry are present, adding a +// dynamic HSTS header does not clobber the static HPKP entry. Further, adding a +// dynamic HPKP entry could not affect the HSTS entry for the site. +TEST_F(HttpSecurityHeadersTest, NoClobberPins) { + TransportSecurityState state; + TransportSecurityState::DomainState domain_state; + + // accounts.google.com has preloaded pins. + std::string domain = "accounts.google.com"; + + // Retrieve the DomainState as it is by default, including its known good + // pins. + const bool sni_enabled = true; + EXPECT_TRUE(state.GetStaticDomainState(domain, sni_enabled, &domain_state)); + HashValueVector saved_hashes = domain_state.pkp.spki_hashes; + EXPECT_TRUE(domain_state.ShouldUpgradeToSSL()); + EXPECT_TRUE(domain_state.HasPublicKeyPins()); + EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); + EXPECT_TRUE(state.HasPublicKeyPins(domain, sni_enabled)); + + // Add a dynamic HSTS header. CheckPublicKeyPins should still pass when given + // the original |saved_hashes|, indicating that the static PKP data is still + // configured for the domain. + EXPECT_TRUE(state.AddHSTSHeader(domain, "includesubdomains; max-age=10000")); + EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); + std::string failure_log; + EXPECT_TRUE(state.CheckPublicKeyPins( + domain, sni_enabled, saved_hashes, &failure_log)); + + // Add an HPKP header, which should only update the dynamic state. + HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); + std::string good_pin = GetTestPin(1, HASH_VALUE_SHA1); + std::string backup_pin = GetTestPin(2, HASH_VALUE_SHA1); + std::string header = "max-age = 10000; " + good_pin + "; " + backup_pin; + + // Construct a fake SSLInfo that will pass AddHPKPHeader's checks. + SSLInfo ssl_info; + ssl_info.public_key_hashes.push_back(good_hash); + ssl_info.public_key_hashes.push_back(saved_hashes[0]); + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + + EXPECT_TRUE(state.AddHPKPHeader(domain, header, ssl_info)); + // HSTS should still be configured for this domain. + EXPECT_TRUE(domain_state.ShouldUpgradeToSSL()); + EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); + // The dynamic pins, which do not match |saved_hashes|, should take + // precedence over the static pins and cause the check to fail. + EXPECT_FALSE(state.CheckPublicKeyPins( + domain, sni_enabled, saved_hashes, &failure_log)); } }; // namespace net diff --git a/chromium/net/http/http_server_properties.cc b/chromium/net/http/http_server_properties.cc index a10d5060c06..19b334c1fb3 100644 --- a/chromium/net/http/http_server_properties.cc +++ b/chromium/net/http/http_server_properties.cc @@ -5,6 +5,7 @@ #include "net/http/http_server_properties.h" #include "base/logging.h" +#include "base/metrics/histogram.h" #include "base/strings/stringprintf.h" namespace net { @@ -19,8 +20,7 @@ const char* const kAlternateProtocolStrings[] = { "npn-spdy/2", "npn-spdy/3", "npn-spdy/3.1", - "npn-spdy/4a2", - "npn-HTTP-draft-04/2.0", + "npn-h2-12", // HTTP/2 draft 12. Called SPDY4 internally. "quic" }; const char kBrokenAlternateProtocol[] = "Broken"; @@ -31,6 +31,28 @@ COMPILE_ASSERT( } // namespace +void HistogramAlternateProtocolUsage( + AlternateProtocolUsage usage, + AlternateProtocolExperiment alternate_protocol_experiment) { + UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage", usage, + ALTERNATE_PROTOCOL_USAGE_MAX); + if (alternate_protocol_experiment == + ALTERNATE_PROTOCOL_TRUNCATED_200_SERVERS) { + UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage.200Truncated", usage, + ALTERNATE_PROTOCOL_USAGE_MAX); + } else if (alternate_protocol_experiment == + ALTERNATE_PROTOCOL_TRUNCATED_1000_SERVERS) { + UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage.1000Truncated", usage, + ALTERNATE_PROTOCOL_USAGE_MAX); + } +} + +void HistogramBrokenAlternateProtocolLocation( + BrokenAlternateProtocolLocation location){ + UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolBrokenLocation", location, + BROKEN_ALTERNATE_PROTOCOL_LOCATION_MAX); +} + bool IsAlternateProtocolValid(AlternateProtocol protocol) { return protocol >= ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION && protocol <= ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION; @@ -41,8 +63,7 @@ const char* AlternateProtocolToString(AlternateProtocol protocol) { case DEPRECATED_NPN_SPDY_2: case NPN_SPDY_3: case NPN_SPDY_3_1: - case NPN_SPDY_4A2: - case NPN_HTTP2_DRAFT_04: + case NPN_SPDY_4: case QUIC: DCHECK(IsAlternateProtocolValid(protocol)); return kAlternateProtocolStrings[ @@ -76,10 +97,8 @@ AlternateProtocol AlternateProtocolFromNextProto(NextProto next_proto) { return NPN_SPDY_3; case kProtoSPDY31: return NPN_SPDY_3_1; - case kProtoSPDY4a2: - return NPN_SPDY_4A2; - case kProtoHTTP2Draft04: - return NPN_HTTP2_DRAFT_04; + case kProtoSPDY4: + return NPN_SPDY_4; case kProtoQUIC1SPDY3: return QUIC; diff --git a/chromium/net/http/http_server_properties.h b/chromium/net/http/http_server_properties.h index 72fda4355b1..88d1f3cefba 100644 --- a/chromium/net/http/http_server_properties.h +++ b/chromium/net/http/http_server_properties.h @@ -8,25 +8,67 @@ #include <map> #include <string> #include "base/basictypes.h" +#include "base/containers/mru_cache.h" #include "base/memory/weak_ptr.h" +#include "base/time/time.h" #include "net/base/host_port_pair.h" #include "net/base/net_export.h" -#include "net/http/http_pipelined_host_capability.h" #include "net/socket/next_proto.h" #include "net/spdy/spdy_framer.h" // TODO(willchan): Reconsider this. namespace net { +enum AlternateProtocolExperiment { + // 200 alternate_protocol servers are loaded (persisted 200 MRU servers). + ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT = 0, + // 200 alternate_protocol servers are loaded (persisted 1000 MRU servers). + ALTERNATE_PROTOCOL_TRUNCATED_200_SERVERS, + // 1000 alternate_protocol servers are loaded (persisted 1000 MRU servers). + ALTERNATE_PROTOCOL_TRUNCATED_1000_SERVERS, +}; + +enum AlternateProtocolUsage { + // Alternate Protocol was used without racing a normal connection. + ALTERNATE_PROTOCOL_USAGE_NO_RACE = 0, + // Alternate Protocol was used by winning a race with a normal connection. + ALTERNATE_PROTOCOL_USAGE_WON_RACE = 1, + // Alternate Protocol was not used by losing a race with a normal connection. + ALTERNATE_PROTOCOL_USAGE_LOST_RACE = 2, + // Alternate Protocol was not used because no Alternate-Protocol information + // was available when the request was issued, but an Alternate-Protocol header + // was present in the response. + ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING = 3, + // Alternate Protocol was not used because it was marked broken. + ALTERNATE_PROTOCOL_USAGE_BROKEN = 4, + // Maximum value for the enum. + ALTERNATE_PROTOCOL_USAGE_MAX, +}; + +// Log a histogram to reflect |usage| and |alternate_protocol_experiment|. +NET_EXPORT void HistogramAlternateProtocolUsage( + AlternateProtocolUsage usage, + AlternateProtocolExperiment alternate_protocol_experiment); + +enum BrokenAlternateProtocolLocation { + BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB = 0, + BROKEN_ALTERNATE_PROTOCOL_LOCATION_QUIC_STREAM_FACTORY = 1, + BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT = 2, + BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN = 3, + BROKEN_ALTERNATE_PROTOCOL_LOCATION_MAX, +}; + +// Log a histogram to reflect |location|. +NET_EXPORT void HistogramBrokenAlternateProtocolLocation( + BrokenAlternateProtocolLocation location); + enum AlternateProtocol { DEPRECATED_NPN_SPDY_2 = 0, ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION = DEPRECATED_NPN_SPDY_2, NPN_SPDY_MINIMUM_VERSION = DEPRECATED_NPN_SPDY_2, NPN_SPDY_3, NPN_SPDY_3_1, - NPN_SPDY_4A2, - // We lump in HTTP/2 with the SPDY protocols for now. - NPN_HTTP2_DRAFT_04, - NPN_SPDY_MAXIMUM_VERSION = NPN_HTTP2_DRAFT_04, + NPN_SPDY_4, // SPDY4 is HTTP/2. + NPN_SPDY_MAXIMUM_VERSION = NPN_SPDY_4, QUIC, ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION = QUIC, ALTERNATE_PROTOCOL_BROKEN, // The alternate protocol is known to be broken. @@ -61,10 +103,9 @@ struct NET_EXPORT PortAlternateProtocolPair { AlternateProtocol protocol; }; -typedef std::map<HostPortPair, PortAlternateProtocolPair> AlternateProtocolMap; -typedef std::map<HostPortPair, SettingsMap> SpdySettingsMap; -typedef std::map<HostPortPair, - HttpPipelinedHostCapability> PipelineCapabilityMap; +typedef base::MRUCache< + HostPortPair, PortAlternateProtocolPair> AlternateProtocolMap; +typedef base::MRUCache<HostPortPair, SettingsMap> SpdySettingsMap; extern const char kAlternateProtocolHeader[]; @@ -75,6 +116,11 @@ extern const char kAlternateProtocolHeader[]; // * Spdy Settings (like CWND ID field) class NET_EXPORT HttpServerProperties { public: + struct NetworkStats { + base::TimeDelta srtt; + uint64 bandwidth_estimate; + }; + HttpServerProperties() {} virtual ~HttpServerProperties() {} @@ -85,7 +131,7 @@ class NET_EXPORT HttpServerProperties { virtual void Clear() = 0; // Returns true if |server| supports SPDY. - virtual bool SupportsSpdy(const HostPortPair& server) const = 0; + virtual bool SupportsSpdy(const HostPortPair& server) = 0; // Add |server| into the persistent store. Should only be called from IO // thread. @@ -93,12 +139,12 @@ class NET_EXPORT HttpServerProperties { bool support_spdy) = 0; // Returns true if |server| has an Alternate-Protocol header. - virtual bool HasAlternateProtocol(const HostPortPair& server) const = 0; + virtual bool HasAlternateProtocol(const HostPortPair& server) = 0; // Returns the Alternate-Protocol and port for |server|. // HasAlternateProtocol(server) must be true. virtual PortAlternateProtocolPair GetAlternateProtocol( - const HostPortPair& server) const = 0; + const HostPortPair& server) = 0; // Sets the Alternate-Protocol for |server|. virtual void SetAlternateProtocol(const HostPortPair& server, @@ -108,13 +154,29 @@ class NET_EXPORT HttpServerProperties { // Sets the Alternate-Protocol for |server| to be BROKEN. virtual void SetBrokenAlternateProtocol(const HostPortPair& server) = 0; + // Returns true if Alternate-Protocol for |server| was recently BROKEN. + virtual bool WasAlternateProtocolRecentlyBroken( + const HostPortPair& server) = 0; + + // Confirms that Alternate-Protocol for |server| is working. + virtual void ConfirmAlternateProtocol(const HostPortPair& server) = 0; + + // Clears the Alternate-Protocol for |server|. + virtual void ClearAlternateProtocol(const HostPortPair& server) = 0; + // Returns all Alternate-Protocol mappings. virtual const AlternateProtocolMap& alternate_protocol_map() const = 0; + virtual void SetAlternateProtocolExperiment( + AlternateProtocolExperiment experiment) = 0; + + virtual AlternateProtocolExperiment GetAlternateProtocolExperiment() + const = 0; + // Gets a reference to the SettingsMap stored for a host. // If no settings are stored, returns an empty SettingsMap. virtual const SettingsMap& GetSpdySettings( - const HostPortPair& host_port_pair) const = 0; + const HostPortPair& host_port_pair) = 0; // Saves an individual SPDY setting for a host. Returns true if SPDY setting // is to be persisted. @@ -132,16 +194,11 @@ class NET_EXPORT HttpServerProperties { // Returns all persistent SPDY settings. virtual const SpdySettingsMap& spdy_settings_map() const = 0; - virtual HttpPipelinedHostCapability GetPipelineCapability( - const HostPortPair& origin) = 0; - - virtual void SetPipelineCapability( - const HostPortPair& origin, - HttpPipelinedHostCapability capability) = 0; + virtual void SetServerNetworkStats(const HostPortPair& host_port_pair, + NetworkStats stats) = 0; - virtual void ClearPipelineCapabilities() = 0; - - virtual PipelineCapabilityMap GetPipelineCapabilityMap() const = 0; + virtual const NetworkStats* GetServerNetworkStats( + const HostPortPair& host_port_pair) const = 0; private: DISALLOW_COPY_AND_ASSIGN(HttpServerProperties); diff --git a/chromium/net/http/http_server_properties_impl.cc b/chromium/net/http/http_server_properties_impl.cc index a4ac6dc3804..1def87f71a0 100644 --- a/chromium/net/http/http_server_properties_impl.cc +++ b/chromium/net/http/http_server_properties_impl.cc @@ -4,23 +4,32 @@ #include "net/http/http_server_properties_impl.h" +#include "base/bind.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" #include "base/stl_util.h" +#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" -#include "net/http/http_pipelined_host_capability.h" namespace net { -// TODO(simonjam): Run experiments with different values of this to see what -// value is good at avoiding evictions without eating too much memory. Until -// then, this is just a bad guess. -static const int kDefaultNumHostsToRemember = 200; +namespace { + +const uint64 kBrokenAlternateProtocolDelaySecs = 300; + +} // namespace HttpServerPropertiesImpl::HttpServerPropertiesImpl() - : pipeline_capability_map_( - new CachedPipelineCapabilityMap(kDefaultNumHostsToRemember)), + : spdy_servers_map_(SpdyServerHostPortMap::NO_AUTO_EVICT), + alternate_protocol_map_(AlternateProtocolMap::NO_AUTO_EVICT), + alternate_protocol_experiment_( + ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT), + spdy_settings_map_(SpdySettingsMap::NO_AUTO_EVICT), weak_ptr_factory_(this) { + canoncial_suffixes_.push_back(".c.youtube.com"); + canoncial_suffixes_.push_back(".googlevideo.com"); + canoncial_suffixes_.push_back(".googleusercontent.com"); } HttpServerPropertiesImpl::~HttpServerPropertiesImpl() { @@ -30,59 +39,84 @@ void HttpServerPropertiesImpl::InitializeSpdyServers( std::vector<std::string>* spdy_servers, bool support_spdy) { DCHECK(CalledOnValidThread()); - spdy_servers_table_.clear(); if (!spdy_servers) return; - for (std::vector<std::string>::iterator it = spdy_servers->begin(); - it != spdy_servers->end(); ++it) { - spdy_servers_table_[*it] = support_spdy; + // Add the entries from persisted data. + for (std::vector<std::string>::reverse_iterator it = spdy_servers->rbegin(); + it != spdy_servers->rend(); ++it) { + spdy_servers_map_.Put(*it, support_spdy); } } void HttpServerPropertiesImpl::InitializeAlternateProtocolServers( AlternateProtocolMap* alternate_protocol_map) { - // First swap, and then add back all the ALTERNATE_PROTOCOL_BROKEN ones since - // those don't get persisted. - alternate_protocol_map_.swap(*alternate_protocol_map); - for (AlternateProtocolMap::const_iterator it = - alternate_protocol_map->begin(); - it != alternate_protocol_map->end(); ++it) { - if (it->second.protocol == ALTERNATE_PROTOCOL_BROKEN) - alternate_protocol_map_[it->first] = it->second; + // Keep all the ALTERNATE_PROTOCOL_BROKEN ones since those don't + // get persisted. + for (AlternateProtocolMap::iterator it = alternate_protocol_map_.begin(); + it != alternate_protocol_map_.end();) { + AlternateProtocolMap::iterator old_it = it; + ++it; + if (old_it->second.protocol != ALTERNATE_PROTOCOL_BROKEN) { + alternate_protocol_map_.Erase(old_it); + } } -} -void HttpServerPropertiesImpl::InitializeSpdySettingsServers( - SpdySettingsMap* spdy_settings_map) { - spdy_settings_map_.swap(*spdy_settings_map); -} + // Add the entries from persisted data. + for (AlternateProtocolMap::reverse_iterator it = + alternate_protocol_map->rbegin(); + it != alternate_protocol_map->rend(); ++it) { + alternate_protocol_map_.Put(it->first, it->second); + } -void HttpServerPropertiesImpl::InitializePipelineCapabilities( - const PipelineCapabilityMap* pipeline_capability_map) { - PipelineCapabilityMap::const_iterator it; - pipeline_capability_map_->Clear(); - for (it = pipeline_capability_map->begin(); - it != pipeline_capability_map->end(); ++it) { - pipeline_capability_map_->Put(it->first, it->second); + // Attempt to find canonical servers. + int canonical_ports[] = { 80, 443 }; + for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { + std::string canonical_suffix = canoncial_suffixes_[i]; + for (size_t j = 0; j < arraysize(canonical_ports); ++j) { + HostPortPair canonical_host(canonical_suffix, canonical_ports[j]); + // If we already have a valid canonical server, we're done. + if (ContainsKey(canonical_host_to_origin_map_, canonical_host) && + (alternate_protocol_map_.Peek(canonical_host_to_origin_map_[ + canonical_host]) != alternate_protocol_map_.end())) { + continue; + } + // Now attempt to find a server which matches this origin and set it as + // canonical . + for (AlternateProtocolMap::const_iterator it = + alternate_protocol_map_.begin(); + it != alternate_protocol_map_.end(); ++it) { + if (EndsWith(it->first.host(), canoncial_suffixes_[i], false)) { + canonical_host_to_origin_map_[canonical_host] = it->first; + break; + } + } + } } } -void HttpServerPropertiesImpl::SetNumPipelinedHostsToRemember(int max_size) { - DCHECK(pipeline_capability_map_->empty()); - pipeline_capability_map_.reset(new CachedPipelineCapabilityMap(max_size)); +void HttpServerPropertiesImpl::InitializeSpdySettingsServers( + SpdySettingsMap* spdy_settings_map) { + for (SpdySettingsMap::reverse_iterator it = spdy_settings_map->rbegin(); + it != spdy_settings_map->rend(); ++it) { + spdy_settings_map_.Put(it->first, it->second); + } } void HttpServerPropertiesImpl::GetSpdyServerList( - base::ListValue* spdy_server_list) const { + base::ListValue* spdy_server_list, + size_t max_size) const { DCHECK(CalledOnValidThread()); DCHECK(spdy_server_list); spdy_server_list->Clear(); + size_t count = 0; // Get the list of servers (host/port) that support SPDY. - for (SpdyServerHostPortTable::const_iterator it = spdy_servers_table_.begin(); - it != spdy_servers_table_.end(); ++it) { + for (SpdyServerHostPortMap::const_iterator it = spdy_servers_map_.begin(); + it != spdy_servers_map_.end() && count < max_size; ++it) { const std::string spdy_server_host_port = it->first; - if (it->second) + if (it->second) { spdy_server_list->Append(new base::StringValue(spdy_server_host_port)); + ++count; + } } } @@ -119,22 +153,21 @@ base::WeakPtr<HttpServerProperties> HttpServerPropertiesImpl::GetWeakPtr() { void HttpServerPropertiesImpl::Clear() { DCHECK(CalledOnValidThread()); - spdy_servers_table_.clear(); - alternate_protocol_map_.clear(); - spdy_settings_map_.clear(); - pipeline_capability_map_->Clear(); + spdy_servers_map_.Clear(); + alternate_protocol_map_.Clear(); + spdy_settings_map_.Clear(); } bool HttpServerPropertiesImpl::SupportsSpdy( - const net::HostPortPair& host_port_pair) const { + const net::HostPortPair& host_port_pair) { DCHECK(CalledOnValidThread()); if (host_port_pair.host().empty()) return false; std::string spdy_server = GetFlattenedSpdyServer(host_port_pair); - SpdyServerHostPortTable::const_iterator spdy_host_port = - spdy_servers_table_.find(spdy_server); - if (spdy_host_port != spdy_servers_table_.end()) + SpdyServerHostPortMap::iterator spdy_host_port = + spdy_servers_map_.Get(spdy_server); + if (spdy_host_port != spdy_servers_map_.end()) return spdy_host_port->second; return false; } @@ -147,33 +180,53 @@ void HttpServerPropertiesImpl::SetSupportsSpdy( return; std::string spdy_server = GetFlattenedSpdyServer(host_port_pair); - SpdyServerHostPortTable::iterator spdy_host_port = - spdy_servers_table_.find(spdy_server); - if ((spdy_host_port != spdy_servers_table_.end()) && + SpdyServerHostPortMap::iterator spdy_host_port = + spdy_servers_map_.Get(spdy_server); + if ((spdy_host_port != spdy_servers_map_.end()) && (spdy_host_port->second == support_spdy)) { return; } // Cache the data. - spdy_servers_table_[spdy_server] = support_spdy; + spdy_servers_map_.Put(spdy_server, support_spdy); } bool HttpServerPropertiesImpl::HasAlternateProtocol( - const HostPortPair& server) const { - return ContainsKey(alternate_protocol_map_, server) || - g_forced_alternate_protocol; + const HostPortPair& server) { + if (alternate_protocol_map_.Get(server) != alternate_protocol_map_.end() || + g_forced_alternate_protocol) + return true; + + return GetCanonicalHost(server) != canonical_host_to_origin_map_.end(); +} + +std::string HttpServerPropertiesImpl::GetCanonicalSuffix( + const HostPortPair& server) { + // If this host ends with a canonical suffix, then return the canonical + // suffix. + for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { + std::string canonical_suffix = canoncial_suffixes_[i]; + if (EndsWith(server.host(), canoncial_suffixes_[i], false)) { + return canonical_suffix; + } + } + return std::string(); } PortAlternateProtocolPair HttpServerPropertiesImpl::GetAlternateProtocol( - const HostPortPair& server) const { + const HostPortPair& server) { DCHECK(HasAlternateProtocol(server)); // First check the map. - AlternateProtocolMap::const_iterator it = - alternate_protocol_map_.find(server); + AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server); if (it != alternate_protocol_map_.end()) return it->second; + // Next check the canonical host. + CanonicalHostMap::const_iterator canonical_host = GetCanonicalHost(server); + if (canonical_host != canonical_host_to_origin_map_.end()) + return alternate_protocol_map_.Get(canonical_host->second)->second; + // We must be forcing an alternate. DCHECK(g_forced_alternate_protocol); return *g_forced_alternate_protocol; @@ -210,14 +263,68 @@ void HttpServerPropertiesImpl::SetAlternateProtocol( << ", Protocol: " << alternate_protocol << "]."; } + } else { + // TODO(rch): Consider the case where multiple requests are started + // before the first completes. In this case, only one of the jobs + // would reach this code, whereas all of them should should have. + HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_MAPPING_MISSING, + alternate_protocol_experiment_); } - alternate_protocol_map_[server] = alternate; + alternate_protocol_map_.Put(server, alternate); + + // If this host ends with a canonical suffix, then set it as the + // canonical host. + for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { + std::string canonical_suffix = canoncial_suffixes_[i]; + if (EndsWith(server.host(), canoncial_suffixes_[i], false)) { + HostPortPair canonical_host(canonical_suffix, server.port()); + canonical_host_to_origin_map_[canonical_host] = server; + break; + } + } } void HttpServerPropertiesImpl::SetBrokenAlternateProtocol( const HostPortPair& server) { - alternate_protocol_map_[server].protocol = ALTERNATE_PROTOCOL_BROKEN; + AlternateProtocolMap::iterator it = alternate_protocol_map_.Get(server); + if (it != alternate_protocol_map_.end()) { + it->second.protocol = ALTERNATE_PROTOCOL_BROKEN; + } else { + PortAlternateProtocolPair alternate; + alternate.protocol = ALTERNATE_PROTOCOL_BROKEN; + alternate_protocol_map_.Put(server, alternate); + } + int count = ++broken_alternate_protocol_map_[server]; + base::TimeDelta delay = + base::TimeDelta::FromSeconds(kBrokenAlternateProtocolDelaySecs); + BrokenAlternateProtocolEntry entry; + entry.server = server; + entry.when = base::TimeTicks::Now() + delay * (1 << (count - 1)); + broken_alternate_protocol_list_.push_back(entry); + // If this is the only entry in the list, schedule an expiration task. + // Otherwse it will be rescheduled automatically when the pending + // task runs. + if (broken_alternate_protocol_list_.size() == 1) { + ScheduleBrokenAlternateProtocolMappingsExpiration(); + } +} + +bool HttpServerPropertiesImpl::WasAlternateProtocolRecentlyBroken( + const HostPortPair& server) { + return ContainsKey(broken_alternate_protocol_map_, server); +} + +void HttpServerPropertiesImpl::ConfirmAlternateProtocol( + const HostPortPair& server) { + broken_alternate_protocol_map_.erase(server); +} + +void HttpServerPropertiesImpl::ClearAlternateProtocol( + const HostPortPair& server) { + AlternateProtocolMap::iterator it = alternate_protocol_map_.Peek(server); + if (it != alternate_protocol_map_.end()) + alternate_protocol_map_.Erase(it); } const AlternateProtocolMap& @@ -225,9 +332,19 @@ HttpServerPropertiesImpl::alternate_protocol_map() const { return alternate_protocol_map_; } +void HttpServerPropertiesImpl::SetAlternateProtocolExperiment( + AlternateProtocolExperiment experiment) { + alternate_protocol_experiment_ = experiment; +} + +AlternateProtocolExperiment +HttpServerPropertiesImpl::GetAlternateProtocolExperiment() const { + return alternate_protocol_experiment_; +} + const SettingsMap& HttpServerPropertiesImpl::GetSpdySettings( - const HostPortPair& host_port_pair) const { - SpdySettingsMap::const_iterator it = spdy_settings_map_.find(host_port_pair); + const HostPortPair& host_port_pair) { + SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair); if (it == spdy_settings_map_.end()) { CR_DEFINE_STATIC_LOCAL(SettingsMap, kEmptySettingsMap, ()); return kEmptySettingsMap; @@ -243,19 +360,28 @@ bool HttpServerPropertiesImpl::SetSpdySetting( if (!(flags & SETTINGS_FLAG_PLEASE_PERSIST)) return false; - SettingsMap& settings_map = spdy_settings_map_[host_port_pair]; SettingsFlagsAndValue flags_and_value(SETTINGS_FLAG_PERSISTED, value); - settings_map[id] = flags_and_value; + SpdySettingsMap::iterator it = spdy_settings_map_.Get(host_port_pair); + if (it == spdy_settings_map_.end()) { + SettingsMap settings_map; + settings_map[id] = flags_and_value; + spdy_settings_map_.Put(host_port_pair, settings_map); + } else { + SettingsMap& settings_map = it->second; + settings_map[id] = flags_and_value; + } return true; } void HttpServerPropertiesImpl::ClearSpdySettings( const HostPortPair& host_port_pair) { - spdy_settings_map_.erase(host_port_pair); + SpdySettingsMap::iterator it = spdy_settings_map_.Peek(host_port_pair); + if (it != spdy_settings_map_.end()) + spdy_settings_map_.Erase(it); } void HttpServerPropertiesImpl::ClearAllSpdySettings() { - spdy_settings_map_.clear(); + spdy_settings_map_.Clear(); } const SpdySettingsMap& @@ -263,41 +389,65 @@ HttpServerPropertiesImpl::spdy_settings_map() const { return spdy_settings_map_; } -HttpPipelinedHostCapability HttpServerPropertiesImpl::GetPipelineCapability( - const HostPortPair& origin) { - HttpPipelinedHostCapability capability = PIPELINE_UNKNOWN; - CachedPipelineCapabilityMap::const_iterator it = - pipeline_capability_map_->Get(origin); - if (it != pipeline_capability_map_->end()) { - capability = it->second; +void HttpServerPropertiesImpl::SetServerNetworkStats( + const HostPortPair& host_port_pair, + NetworkStats stats) { + server_network_stats_map_[host_port_pair] = stats; +} + +const HttpServerProperties::NetworkStats* +HttpServerPropertiesImpl::GetServerNetworkStats( + const HostPortPair& host_port_pair) const { + ServerNetworkStatsMap::const_iterator it = + server_network_stats_map_.find(host_port_pair); + if (it == server_network_stats_map_.end()) { + return NULL; } - return capability; + return &it->second; } -void HttpServerPropertiesImpl::SetPipelineCapability( - const HostPortPair& origin, - HttpPipelinedHostCapability capability) { - CachedPipelineCapabilityMap::iterator it = - pipeline_capability_map_->Peek(origin); - if (it == pipeline_capability_map_->end() || - it->second != PIPELINE_INCAPABLE) { - pipeline_capability_map_->Put(origin, capability); +HttpServerPropertiesImpl::CanonicalHostMap::const_iterator +HttpServerPropertiesImpl::GetCanonicalHost(HostPortPair server) const { + for (size_t i = 0; i < canoncial_suffixes_.size(); ++i) { + std::string canonical_suffix = canoncial_suffixes_[i]; + if (EndsWith(server.host(), canoncial_suffixes_[i], false)) { + HostPortPair canonical_host(canonical_suffix, server.port()); + return canonical_host_to_origin_map_.find(canonical_host); + } } + + return canonical_host_to_origin_map_.end(); } -void HttpServerPropertiesImpl::ClearPipelineCapabilities() { - pipeline_capability_map_->Clear(); +void HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings() { + base::TimeTicks now = base::TimeTicks::Now(); + while (!broken_alternate_protocol_list_.empty()) { + BrokenAlternateProtocolEntry entry = + broken_alternate_protocol_list_.front(); + if (now < entry.when) { + break; + } + + ClearAlternateProtocol(entry.server); + broken_alternate_protocol_list_.pop_front(); + } + ScheduleBrokenAlternateProtocolMappingsExpiration(); } -PipelineCapabilityMap -HttpServerPropertiesImpl::GetPipelineCapabilityMap() const { - PipelineCapabilityMap result; - CachedPipelineCapabilityMap::const_iterator it; - for (it = pipeline_capability_map_->begin(); - it != pipeline_capability_map_->end(); ++it) { - result[it->first] = it->second; +void +HttpServerPropertiesImpl::ScheduleBrokenAlternateProtocolMappingsExpiration() { + if (broken_alternate_protocol_list_.empty()) { + return; } - return result; + base::TimeTicks now = base::TimeTicks::Now(); + base::TimeTicks when = broken_alternate_protocol_list_.front().when; + base::TimeDelta delay = when > now ? when - now : base::TimeDelta(); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + base::Bind( + &HttpServerPropertiesImpl::ExpireBrokenAlternateProtocolMappings, + weak_ptr_factory_.GetWeakPtr()), + delay); } } // namespace net diff --git a/chromium/net/http/http_server_properties_impl.h b/chromium/net/http/http_server_properties_impl.h index cf96b7d4f32..389c961f491 100644 --- a/chromium/net/http/http_server_properties_impl.h +++ b/chromium/net/http/http_server_properties_impl.h @@ -11,13 +11,11 @@ #include "base/basictypes.h" #include "base/containers/hash_tables.h" -#include "base/containers/mru_cache.h" #include "base/gtest_prod_util.h" #include "base/threading/non_thread_safe.h" #include "base/values.h" #include "net/base/host_port_pair.h" #include "net/base/net_export.h" -#include "net/http/http_pipelined_host_capability.h" #include "net/http/http_server_properties.h" namespace base { @@ -34,7 +32,7 @@ class NET_EXPORT HttpServerPropertiesImpl HttpServerPropertiesImpl(); virtual ~HttpServerPropertiesImpl(); - // Initializes |spdy_servers_table_| with the servers (host/port) from + // Initializes |spdy_servers_map_| with the servers (host/port) from // |spdy_servers| that either support SPDY or not. void InitializeSpdyServers(std::vector<std::string>* spdy_servers, bool support_spdy); @@ -44,13 +42,10 @@ class NET_EXPORT HttpServerPropertiesImpl void InitializeSpdySettingsServers(SpdySettingsMap* spdy_settings_map); - // Initializes |pipeline_capability_map_| with the servers (host/port) from - // |pipeline_capability_map| that either support HTTP pipelining or not. - void InitializePipelineCapabilities( - const PipelineCapabilityMap* pipeline_capability_map); - - // Get the list of servers (host/port) that support SPDY. - void GetSpdyServerList(base::ListValue* spdy_server_list) const; + // Get the list of servers (host/port) that support SPDY. The max_size is the + // number of MRU servers that support SPDY that are to be returned. + void GetSpdyServerList(base::ListValue* spdy_server_list, + size_t max_size) const; // Returns flattened string representation of the |host_port_pair|. Used by // unittests. @@ -63,11 +58,9 @@ class NET_EXPORT HttpServerPropertiesImpl static void ForceAlternateProtocol(const PortAlternateProtocolPair& pair); static void DisableForcedAlternateProtocol(); - // Changes the number of host/port pairs we remember pipelining capability - // for. A larger number means we're more likely to be able to pipeline - // immediately if a host is known good, but uses more memory. This function - // can only be called if |pipeline_capability_map_| is empty. - void SetNumPipelinedHostsToRemember(int max_size); + // Returns the canonical host suffix for |server|, or std::string() if none + // exists. + std::string GetCanonicalSuffix(const net::HostPortPair& server); // ----------------------------- // HttpServerProperties methods: @@ -80,19 +73,19 @@ class NET_EXPORT HttpServerPropertiesImpl virtual void Clear() OVERRIDE; // Returns true if |server| supports SPDY. - virtual bool SupportsSpdy(const HostPortPair& server) const OVERRIDE; + virtual bool SupportsSpdy(const HostPortPair& server) OVERRIDE; // Add |server| into the persistent store. virtual void SetSupportsSpdy(const HostPortPair& server, bool support_spdy) OVERRIDE; // Returns true if |server| has an Alternate-Protocol header. - virtual bool HasAlternateProtocol(const HostPortPair& server) const OVERRIDE; + virtual bool HasAlternateProtocol(const HostPortPair& server) OVERRIDE; // Returns the Alternate-Protocol and port for |server|. // HasAlternateProtocol(server) must be true. virtual PortAlternateProtocolPair GetAlternateProtocol( - const HostPortPair& server) const OVERRIDE; + const HostPortPair& server) OVERRIDE; // Sets the Alternate-Protocol for |server|. virtual void SetAlternateProtocol( @@ -103,13 +96,29 @@ class NET_EXPORT HttpServerPropertiesImpl // Sets the Alternate-Protocol for |server| to be BROKEN. virtual void SetBrokenAlternateProtocol(const HostPortPair& server) OVERRIDE; + // Returns true if Alternate-Protocol for |server| was recently BROKEN. + virtual bool WasAlternateProtocolRecentlyBroken( + const HostPortPair& server) OVERRIDE; + + // Confirms that Alternate-Protocol for |server| is working. + virtual void ConfirmAlternateProtocol(const HostPortPair& server) OVERRIDE; + + // Clears the Alternate-Protocol for |server|. + virtual void ClearAlternateProtocol(const HostPortPair& server) OVERRIDE; + // Returns all Alternate-Protocol mappings. virtual const AlternateProtocolMap& alternate_protocol_map() const OVERRIDE; + virtual void SetAlternateProtocolExperiment( + AlternateProtocolExperiment experiment) OVERRIDE; + + virtual AlternateProtocolExperiment GetAlternateProtocolExperiment() + const OVERRIDE; + // Gets a reference to the SettingsMap stored for a host. // If no settings are stored, returns an empty SettingsMap. virtual const SettingsMap& GetSpdySettings( - const HostPortPair& host_port_pair) const OVERRIDE; + const HostPortPair& host_port_pair) OVERRIDE; // Saves an individual SPDY setting for a host. Returns true if SPDY setting // is to be persisted. @@ -127,29 +136,52 @@ class NET_EXPORT HttpServerPropertiesImpl // Returns all persistent SPDY settings. virtual const SpdySettingsMap& spdy_settings_map() const OVERRIDE; - virtual HttpPipelinedHostCapability GetPipelineCapability( - const HostPortPair& origin) OVERRIDE; - - virtual void SetPipelineCapability( - const HostPortPair& origin, - HttpPipelinedHostCapability capability) OVERRIDE; - - virtual void ClearPipelineCapabilities() OVERRIDE; + virtual void SetServerNetworkStats(const HostPortPair& host_port_pair, + NetworkStats stats) OVERRIDE; - virtual PipelineCapabilityMap GetPipelineCapabilityMap() const OVERRIDE; + virtual const NetworkStats* GetServerNetworkStats( + const HostPortPair& host_port_pair) const OVERRIDE; private: - typedef base::MRUCache< - HostPortPair, HttpPipelinedHostCapability> CachedPipelineCapabilityMap; - // |spdy_servers_table_| has flattened representation of servers (host/port - // pair) that either support or not support SPDY protocol. - typedef base::hash_map<std::string, bool> SpdyServerHostPortTable; - - SpdyServerHostPortTable spdy_servers_table_; + // |spdy_servers_map_| has flattened representation of servers (host, port) + // that either support or not support SPDY protocol. + typedef base::MRUCache<std::string, bool> SpdyServerHostPortMap; + typedef std::map<HostPortPair, NetworkStats> ServerNetworkStatsMap; + typedef std::map<HostPortPair, HostPortPair> CanonicalHostMap; + typedef std::vector<std::string> CanonicalSufficList; + // List of broken host:ports and the times when they can be expired. + struct BrokenAlternateProtocolEntry { + HostPortPair server; + base::TimeTicks when; + }; + typedef std::list<BrokenAlternateProtocolEntry> + BrokenAlternateProtocolList; + // Map from host:port to the number of times alternate protocol has + // been marked broken. + typedef std::map<HostPortPair, int> BrokenAlternateProtocolMap; + + // Return the canonical host for |server|, or end if none exists. + CanonicalHostMap::const_iterator GetCanonicalHost(HostPortPair server) const; + + void ExpireBrokenAlternateProtocolMappings(); + void ScheduleBrokenAlternateProtocolMappingsExpiration(); + + SpdyServerHostPortMap spdy_servers_map_; AlternateProtocolMap alternate_protocol_map_; + BrokenAlternateProtocolList broken_alternate_protocol_list_; + BrokenAlternateProtocolMap broken_alternate_protocol_map_; + AlternateProtocolExperiment alternate_protocol_experiment_; + SpdySettingsMap spdy_settings_map_; - scoped_ptr<CachedPipelineCapabilityMap> pipeline_capability_map_; + ServerNetworkStatsMap server_network_stats_map_; + // Contains a map of servers which could share the same alternate protocol. + // Map from a Canonical host/port (host is some postfix of host names) to an + // actual origin, which has a plausible alternate protocol mapping. + CanonicalHostMap canonical_host_to_origin_map_; + // Contains list of suffixes (for exmaple ".c.youtube.com", + // ".googlevideo.com", ".googleusercontent.com") of canoncial hostnames. + CanonicalSufficList canoncial_suffixes_; base::WeakPtrFactory<HttpServerPropertiesImpl> weak_ptr_factory_; diff --git a/chromium/net/http/http_server_properties_impl_unittest.cc b/chromium/net/http/http_server_properties_impl_unittest.cc index cf3a4643f9d..c8014741d7a 100644 --- a/chromium/net/http/http_server_properties_impl_unittest.cc +++ b/chromium/net/http/http_server_properties_impl_unittest.cc @@ -21,6 +21,8 @@ class ListValue; namespace net { +const int kMaxSupportsSpdyServerHosts = 500; + namespace { class HttpServerPropertiesImplTest : public testing::Test { @@ -60,6 +62,17 @@ TEST_F(SpdyServerPropertiesTest, Initialize) { spdy_servers2.push_back(spdy_server_g); spdy_servers2.push_back(spdy_server_d); impl_.InitializeSpdyServers(&spdy_servers2, true); + + // Verify spdy_server_g and spdy_server_d are in the list in the same order. + base::ListValue spdy_server_list; + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); + EXPECT_EQ(2U, spdy_server_list.GetSize()); + std::string string_value_g; + ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g)); + ASSERT_EQ(spdy_server_g, string_value_g); + std::string string_value_d; + ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_d)); + ASSERT_EQ(spdy_server_d, string_value_d); EXPECT_TRUE(impl_.SupportsSpdy(spdy_server_google)); EXPECT_TRUE(impl_.SupportsSpdy(spdy_server_docs)); } @@ -130,13 +143,13 @@ TEST_F(SpdyServerPropertiesTest, GetSpdyServerList) { base::ListValue spdy_server_list; // Check there are no spdy_servers. - impl_.GetSpdyServerList(&spdy_server_list); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); EXPECT_EQ(0U, spdy_server_list.GetSize()); // Check empty server is not added. HostPortPair spdy_server_empty(std::string(), 443); impl_.SetSupportsSpdy(spdy_server_empty, true); - impl_.GetSpdyServerList(&spdy_server_list); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); EXPECT_EQ(0U, spdy_server_list.GetSize()); std::string string_value_g; @@ -150,38 +163,79 @@ TEST_F(SpdyServerPropertiesTest, GetSpdyServerList) { // Add www.google.com:443 as not supporting SPDY. impl_.SetSupportsSpdy(spdy_server_google, false); - impl_.GetSpdyServerList(&spdy_server_list); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); EXPECT_EQ(0U, spdy_server_list.GetSize()); // Add www.google.com:443 as supporting SPDY. impl_.SetSupportsSpdy(spdy_server_google, true); - impl_.GetSpdyServerList(&spdy_server_list); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); ASSERT_EQ(1U, spdy_server_list.GetSize()); ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g)); ASSERT_EQ(spdy_server_g, string_value_g); // Add mail.google.com:443 as not supporting SPDY. impl_.SetSupportsSpdy(spdy_server_mail, false); - impl_.GetSpdyServerList(&spdy_server_list); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); ASSERT_EQ(1U, spdy_server_list.GetSize()); ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g)); ASSERT_EQ(spdy_server_g, string_value_g); // Add mail.google.com:443 as supporting SPDY. impl_.SetSupportsSpdy(spdy_server_mail, true); - impl_.GetSpdyServerList(&spdy_server_list); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); ASSERT_EQ(2U, spdy_server_list.GetSize()); // Verify www.google.com:443 and mail.google.com:443 are in the list. + ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_m)); + ASSERT_EQ(spdy_server_m, string_value_m); + ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_g)); + ASSERT_EQ(spdy_server_g, string_value_g); + + // Request for only one server and verify that we get only one server. + impl_.GetSpdyServerList(&spdy_server_list, 1); + ASSERT_EQ(1U, spdy_server_list.GetSize()); + ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_m)); + ASSERT_EQ(spdy_server_m, string_value_m); +} + +TEST_F(SpdyServerPropertiesTest, MRUOfGetSpdyServerList) { + base::ListValue spdy_server_list; + + std::string string_value_g; + std::string string_value_m; + HostPortPair spdy_server_google("www.google.com", 443); + std::string spdy_server_g = + HttpServerPropertiesImpl::GetFlattenedSpdyServer(spdy_server_google); + HostPortPair spdy_server_mail("mail.google.com", 443); + std::string spdy_server_m = + HttpServerPropertiesImpl::GetFlattenedSpdyServer(spdy_server_mail); + + // Add www.google.com:443 as supporting SPDY. + impl_.SetSupportsSpdy(spdy_server_google, true); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); + ASSERT_EQ(1U, spdy_server_list.GetSize()); + ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g)); + ASSERT_EQ(spdy_server_g, string_value_g); + + // Add mail.google.com:443 as supporting SPDY. Verify mail.google.com:443 and + // www.google.com:443 are in the list. + impl_.SetSupportsSpdy(spdy_server_mail, true); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); + ASSERT_EQ(2U, spdy_server_list.GetSize()); + ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_m)); + ASSERT_EQ(spdy_server_m, string_value_m); + ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_g)); + ASSERT_EQ(spdy_server_g, string_value_g); + + // Get www.google.com:443 should reorder SpdyServerHostPortMap. Verify that it + // is www.google.com:443 is the MRU server. + EXPECT_TRUE(impl_.SupportsSpdy(spdy_server_google)); + impl_.GetSpdyServerList(&spdy_server_list, kMaxSupportsSpdyServerHosts); + ASSERT_EQ(2U, spdy_server_list.GetSize()); ASSERT_TRUE(spdy_server_list.GetString(0, &string_value_g)); + ASSERT_EQ(spdy_server_g, string_value_g); ASSERT_TRUE(spdy_server_list.GetString(1, &string_value_m)); - if (string_value_g.compare(spdy_server_g) == 0) { - ASSERT_EQ(spdy_server_g, string_value_g); - ASSERT_EQ(spdy_server_m, string_value_m); - } else { - ASSERT_EQ(spdy_server_g, string_value_m); - ASSERT_EQ(spdy_server_m, string_value_g); - } + ASSERT_EQ(spdy_server_m, string_value_m); } typedef HttpServerPropertiesImplTest AlternateProtocolServerPropertiesTest; @@ -206,13 +260,27 @@ TEST_F(AlternateProtocolServerPropertiesTest, Initialize) { HostPortPair test_host_port_pair2("foo2", 80); impl_.SetAlternateProtocol(test_host_port_pair2, 443, NPN_SPDY_3); - AlternateProtocolMap alternate_protocol_map; + AlternateProtocolMap alternate_protocol_map( + AlternateProtocolMap::NO_AUTO_EVICT); PortAlternateProtocolPair port_alternate_protocol_pair; port_alternate_protocol_pair.port = 123; port_alternate_protocol_pair.protocol = NPN_SPDY_3; - alternate_protocol_map[test_host_port_pair2] = port_alternate_protocol_pair; + alternate_protocol_map.Put(test_host_port_pair2, + port_alternate_protocol_pair); + HostPortPair test_host_port_pair3("foo3", 80); + port_alternate_protocol_pair.port = 1234; + alternate_protocol_map.Put(test_host_port_pair3, + port_alternate_protocol_pair); impl_.InitializeAlternateProtocolServers(&alternate_protocol_map); + // Verify test_host_port_pair3 is the MRU server. + const net::AlternateProtocolMap& map = impl_.alternate_protocol_map(); + net::AlternateProtocolMap::const_iterator it = map.begin(); + it = map.begin(); + EXPECT_TRUE(it->first.Equals(test_host_port_pair3)); + EXPECT_EQ(1234, it->second.port); + EXPECT_EQ(NPN_SPDY_3, it->second.protocol); + ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair1)); ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair2)); port_alternate_protocol_pair = @@ -224,6 +292,49 @@ TEST_F(AlternateProtocolServerPropertiesTest, Initialize) { EXPECT_EQ(NPN_SPDY_3, port_alternate_protocol_pair.protocol); } +TEST_F(AlternateProtocolServerPropertiesTest, MRUOfHasAlternateProtocol) { + HostPortPair test_host_port_pair1("foo1", 80); + impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3); + HostPortPair test_host_port_pair2("foo2", 80); + impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3); + + const net::AlternateProtocolMap& map = impl_.alternate_protocol_map(); + net::AlternateProtocolMap::const_iterator it = map.begin(); + EXPECT_TRUE(it->first.Equals(test_host_port_pair2)); + EXPECT_EQ(1234, it->second.port); + EXPECT_EQ(NPN_SPDY_3, it->second.protocol); + + // HasAlternateProtocol should reoder the AlternateProtocol map. + ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair1)); + it = map.begin(); + EXPECT_TRUE(it->first.Equals(test_host_port_pair1)); + EXPECT_EQ(443, it->second.port); + EXPECT_EQ(NPN_SPDY_3, it->second.protocol); +} + +TEST_F(AlternateProtocolServerPropertiesTest, MRUOfGetAlternateProtocol) { + HostPortPair test_host_port_pair1("foo1", 80); + impl_.SetAlternateProtocol(test_host_port_pair1, 443, NPN_SPDY_3); + HostPortPair test_host_port_pair2("foo2", 80); + impl_.SetAlternateProtocol(test_host_port_pair2, 1234, NPN_SPDY_3); + + const net::AlternateProtocolMap& map = impl_.alternate_protocol_map(); + net::AlternateProtocolMap::const_iterator it = map.begin(); + EXPECT_TRUE(it->first.Equals(test_host_port_pair2)); + EXPECT_EQ(1234, it->second.port); + EXPECT_EQ(NPN_SPDY_3, it->second.protocol); + + // GetAlternateProtocol should reoder the AlternateProtocol map. + PortAlternateProtocolPair alternate = + impl_.GetAlternateProtocol(test_host_port_pair1); + EXPECT_EQ(443, alternate.port); + EXPECT_EQ(NPN_SPDY_3, alternate.protocol); + it = map.begin(); + EXPECT_TRUE(it->first.Equals(test_host_port_pair1)); + EXPECT_EQ(443, it->second.port); + EXPECT_EQ(NPN_SPDY_3, it->second.protocol); +} + TEST_F(AlternateProtocolServerPropertiesTest, SetBroken) { HostPortPair test_host_port_pair("foo", 80); impl_.SetBrokenAlternateProtocol(test_host_port_pair); @@ -241,6 +352,17 @@ TEST_F(AlternateProtocolServerPropertiesTest, SetBroken) { << "Second attempt should be ignored."; } +TEST_F(AlternateProtocolServerPropertiesTest, ClearBroken) { + HostPortPair test_host_port_pair("foo", 80); + impl_.SetBrokenAlternateProtocol(test_host_port_pair); + ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair)); + PortAlternateProtocolPair alternate = + impl_.GetAlternateProtocol(test_host_port_pair); + EXPECT_EQ(ALTERNATE_PROTOCOL_BROKEN, alternate.protocol); + impl_.ClearAlternateProtocol(test_host_port_pair); + EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair)); +} + TEST_F(AlternateProtocolServerPropertiesTest, Forced) { // Test forced alternate protocols. @@ -273,13 +395,39 @@ TEST_F(AlternateProtocolServerPropertiesTest, Forced) { EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair2)); } +TEST_F(AlternateProtocolServerPropertiesTest, Canonical) { + HostPortPair test_host_port_pair("foo.c.youtube.com", 80); + EXPECT_FALSE(impl_.HasAlternateProtocol(test_host_port_pair)); + + HostPortPair canonical_port_pair("bar.c.youtube.com", 80); + EXPECT_FALSE(impl_.HasAlternateProtocol(canonical_port_pair)); + + PortAlternateProtocolPair canonical_protocol; + canonical_protocol.port = 1234; + canonical_protocol.protocol = QUIC; + + impl_.SetAlternateProtocol(canonical_port_pair, + canonical_protocol.port, + canonical_protocol.protocol); + // Verify the forced protocol. + ASSERT_TRUE(impl_.HasAlternateProtocol(test_host_port_pair)); + PortAlternateProtocolPair alternate = + impl_.GetAlternateProtocol(test_host_port_pair); + EXPECT_EQ(canonical_protocol.port, alternate.port); + EXPECT_EQ(canonical_protocol.protocol, alternate.protocol); + + // Verify the canonical suffix. + EXPECT_EQ(".c.youtube.com", impl_.GetCanonicalSuffix(test_host_port_pair)); + EXPECT_EQ(".c.youtube.com", impl_.GetCanonicalSuffix(canonical_port_pair)); +} + typedef HttpServerPropertiesImplTest SpdySettingsServerPropertiesTest; TEST_F(SpdySettingsServerPropertiesTest, Initialize) { HostPortPair spdy_server_google("www.google.com", 443); // Check by initializing empty spdy settings. - SpdySettingsMap spdy_settings_map; + SpdySettingsMap spdy_settings_map(SpdySettingsMap::NO_AUTO_EVICT); impl_.InitializeSpdySettingsServers(&spdy_settings_map); EXPECT_TRUE(impl_.GetSpdySettings(spdy_server_google).empty()); @@ -290,7 +438,7 @@ TEST_F(SpdySettingsServerPropertiesTest, Initialize) { const uint32 value = 31337; SettingsFlagsAndValue flags_and_value(flags, value); settings_map[id] = flags_and_value; - spdy_settings_map[spdy_server_google] = settings_map; + spdy_settings_map.Put(spdy_server_google, settings_map); impl_.InitializeSpdySettingsServers(&spdy_settings_map); const SettingsMap& settings_map2 = impl_.GetSpdySettings(spdy_server_google); @@ -411,6 +559,55 @@ TEST_F(SpdySettingsServerPropertiesTest, Clear) { EXPECT_EQ(0U, impl_.GetSpdySettings(spdy_server_docs).size()); } +TEST_F(SpdySettingsServerPropertiesTest, MRUOfGetSpdySettings) { + // Add www.google.com:443 as persisting. + HostPortPair spdy_server_google("www.google.com", 443); + const SpdySettingsIds id1 = SETTINGS_UPLOAD_BANDWIDTH; + const SpdySettingsFlags flags1 = SETTINGS_FLAG_PLEASE_PERSIST; + const uint32 value1 = 31337; + EXPECT_TRUE(impl_.SetSpdySetting(spdy_server_google, id1, flags1, value1)); + + // Add docs.google.com:443 as persisting + HostPortPair spdy_server_docs("docs.google.com", 443); + const SpdySettingsIds id2 = SETTINGS_ROUND_TRIP_TIME; + const SpdySettingsFlags flags2 = SETTINGS_FLAG_PLEASE_PERSIST; + const uint32 value2 = 93997; + EXPECT_TRUE(impl_.SetSpdySetting(spdy_server_docs, id2, flags2, value2)); + + // Verify the first element is docs.google.com:443. + const net::SpdySettingsMap& map = impl_.spdy_settings_map(); + net::SpdySettingsMap::const_iterator it = map.begin(); + EXPECT_TRUE(it->first.Equals(spdy_server_docs)); + const SettingsMap& settings_map2_ret = it->second; + ASSERT_EQ(1U, settings_map2_ret.size()); + SettingsMap::const_iterator it2_ret = settings_map2_ret.find(id2); + EXPECT_TRUE(it2_ret != settings_map2_ret.end()); + SettingsFlagsAndValue flags_and_value2_ret = it2_ret->second; + EXPECT_EQ(SETTINGS_FLAG_PERSISTED, flags_and_value2_ret.first); + EXPECT_EQ(value2, flags_and_value2_ret.second); + + // GetSpdySettings should reoder the SpdySettingsMap. + const SettingsMap& settings_map1_ret = + impl_.GetSpdySettings(spdy_server_google); + ASSERT_EQ(1U, settings_map1_ret.size()); + SettingsMap::const_iterator it1_ret = settings_map1_ret.find(id1); + EXPECT_TRUE(it1_ret != settings_map1_ret.end()); + SettingsFlagsAndValue flags_and_value1_ret = it1_ret->second; + EXPECT_EQ(SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first); + EXPECT_EQ(value1, flags_and_value1_ret.second); + + // Check the first entry is spdy_server_google by accessing it via iterator. + it = map.begin(); + EXPECT_TRUE(it->first.Equals(spdy_server_google)); + const SettingsMap& settings_map1_it_ret = it->second; + ASSERT_EQ(1U, settings_map1_it_ret.size()); + it1_ret = settings_map1_it_ret.find(id1); + EXPECT_TRUE(it1_ret != settings_map1_it_ret.end()); + flags_and_value1_ret = it1_ret->second; + EXPECT_EQ(SETTINGS_FLAG_PERSISTED, flags_and_value1_ret.first); + EXPECT_EQ(value1, flags_and_value1_ret.second); +} + } // namespace } // namespace net diff --git a/chromium/net/http/http_stream.h b/chromium/net/http/http_stream.h index 3680db3f862..22362e28991 100644 --- a/chromium/net/http/http_stream.h +++ b/chromium/net/http/http_stream.h @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // -// HttpStream provides an abstraction for a basic http streams, http pipelining -// implementations, and SPDY. The HttpStream subtype is expected to manage the -// underlying transport appropriately. For example, a non-pipelined HttpStream -// would return the transport socket to the pool for reuse. SPDY streams on the -// other hand leave the transport socket management to the SpdySession. +// HttpStream provides an abstraction for a basic http streams, SPDY, and QUIC. +// The HttpStream subtype is expected to manage the underlying transport +// appropriately. For example, a basic http stream will return the transport +// socket to the pool for reuse. SPDY streams on the other hand leave the +// transport socket management to the SpdySession. #ifndef NET_HTTP_HTTP_STREAM_H_ #define NET_HTTP_HTTP_STREAM_H_ diff --git a/chromium/net/http/http_stream_base.h b/chromium/net/http/http_stream_base.h index f5dcc29409e..76e57ef8ff8 100644 --- a/chromium/net/http/http_stream_base.h +++ b/chromium/net/http/http_stream_base.h @@ -48,21 +48,30 @@ class NET_EXPORT_PRIVATE HttpStreamBase { // ERR_IO_PENDING is returned if the operation could not be completed // synchronously, in which case the result will be passed to the callback // when available. Returns OK on success. - // |response| must outlive the HttpStreamBase. + // + // The callback will only be invoked once the first full set of headers have + // been received, at which point |response| will have been populated with that + // set of headers, and is safe to read, until/unless ReadResponseHeaders is + // called. + // + // |response| must remain valid until all sets of headers has been read, or + // the HttpStreamBase is destroyed. There's typically only one set of + // headers, except in the case of 1xx responses (See ReadResponseHeaders). virtual int SendRequest(const HttpRequestHeaders& request_headers, HttpResponseInfo* response, const CompletionCallback& callback) = 0; - // Reads from the underlying socket until the response headers have been - // completely received. ERR_IO_PENDING is returned if the operation could - // not be completed synchronously, in which case the result will be passed - // to the callback when available. Returns OK on success. The response - // headers are available in the HttpResponseInfo returned by GetResponseInfo + // Reads from the underlying socket until the next set of response headers + // have been completely received. This may only be called on 1xx responses + // after SendRequest has completed successfully, to read the next set of + // headers. + // + // ERR_IO_PENDING is returned if the operation could not be completed + // synchronously, in which case the result will be passed to the callback when + // available. Returns OK on success. The response headers are available in + // the HttpResponseInfo passed in to original call to SendRequest. virtual int ReadResponseHeaders(const CompletionCallback& callback) = 0; - // Provides access to HttpResponseInfo (owned by HttpStream). - virtual const HttpResponseInfo* GetResponseInfo() const = 0; - // Reads response body data, up to |buf_len| bytes. |buf_len| should be a // reasonable size (<2MB). The number of bytes read is returned, or an // error is returned upon failure. 0 indicates that the request has been diff --git a/chromium/net/http/http_stream_factory.cc b/chromium/net/http/http_stream_factory.cc index e88046fba3c..70d1101d07b 100644 --- a/chromium/net/http/http_stream_factory.cc +++ b/chromium/net/http/http_stream_factory.cc @@ -1,261 +1,99 @@ -// Copyright (c) 2012 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/http/http_stream_factory.h" - -#include "base/logging.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_split.h" -#include "net/base/host_mapping_rules.h" -#include "net/base/host_port_pair.h" -#include "url/gurl.h" - -namespace net { - -// WARNING: If you modify or add any static flags, you must keep them in sync -// with |ResetStaticSettingsToInit|. This is critical for unit test isolation. - -// static -std::vector<std::string>* HttpStreamFactory::next_protos_ = NULL; -// static -bool HttpStreamFactory::enabled_protocols_[NUM_VALID_ALTERNATE_PROTOCOLS]; -// static -bool HttpStreamFactory::spdy_enabled_ = true; -// static -bool HttpStreamFactory::use_alternate_protocols_ = false; -// static -bool HttpStreamFactory::force_spdy_over_ssl_ = true; -// static -bool HttpStreamFactory::force_spdy_always_ = false; -// static -std::list<HostPortPair>* HttpStreamFactory::forced_spdy_exclusions_ = NULL; - -HttpStreamFactory::~HttpStreamFactory() {} - -// static -bool HttpStreamFactory::IsProtocolEnabled(AlternateProtocol protocol) { - DCHECK(IsAlternateProtocolValid(protocol)); - return enabled_protocols_[ - protocol - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION]; -} - -// static -void HttpStreamFactory::SetProtocolEnabled(AlternateProtocol protocol) { - DCHECK(IsAlternateProtocolValid(protocol)); - enabled_protocols_[ - protocol - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] = true; -} - -// static -void HttpStreamFactory::ResetEnabledProtocols() { - for (int i = ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION; - i <= ALTERNATE_PROTOCOL_MAXIMUM_VALID_VERSION; ++i) { - enabled_protocols_[i - ALTERNATE_PROTOCOL_MINIMUM_VALID_VERSION] = false; - } -} - -// static -void HttpStreamFactory::ResetStaticSettingsToInit() { - // WARNING: These must match the initializers above. - delete next_protos_; - delete forced_spdy_exclusions_; - next_protos_ = NULL; - spdy_enabled_ = true; - use_alternate_protocols_ = false; - force_spdy_over_ssl_ = true; - force_spdy_always_ = false; - forced_spdy_exclusions_ = NULL; - ResetEnabledProtocols(); -} - -void HttpStreamFactory::ProcessAlternateProtocol( - const base::WeakPtr<HttpServerProperties>& http_server_properties, - const std::string& alternate_protocol_str, - const HostPortPair& http_host_port_pair) { - std::vector<std::string> port_protocol_vector; - base::SplitString(alternate_protocol_str, ':', &port_protocol_vector); - if (port_protocol_vector.size() != 2) { - DVLOG(1) << kAlternateProtocolHeader - << " header has too many tokens: " - << alternate_protocol_str; - return; - } - - int port; - if (!base::StringToInt(port_protocol_vector[0], &port) || - port <= 0 || port >= 1 << 16) { - DVLOG(1) << kAlternateProtocolHeader - << " header has unrecognizable port: " - << port_protocol_vector[0]; - return; - } - - AlternateProtocol protocol = - AlternateProtocolFromString(port_protocol_vector[1]); - if (IsAlternateProtocolValid(protocol) && !IsProtocolEnabled(protocol)) { - protocol = ALTERNATE_PROTOCOL_BROKEN; - } - - if (protocol == ALTERNATE_PROTOCOL_BROKEN) { - DVLOG(1) << kAlternateProtocolHeader - << " header has unrecognized protocol: " - << port_protocol_vector[1]; - return; - } - - HostPortPair host_port(http_host_port_pair); - const HostMappingRules* mapping_rules = GetHostMappingRules(); - if (mapping_rules) - mapping_rules->RewriteHost(&host_port); - - if (http_server_properties->HasAlternateProtocol(host_port)) { - const PortAlternateProtocolPair existing_alternate = - http_server_properties->GetAlternateProtocol(host_port); - // If we think the alternate protocol is broken, don't change it. - if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) - return; - } - - http_server_properties->SetAlternateProtocol(host_port, port, protocol); -} - -GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url, - HostPortPair* endpoint) { - const HostMappingRules* mapping_rules = GetHostMappingRules(); - if (mapping_rules && mapping_rules->RewriteHost(endpoint)) { - url_canon::Replacements<char> replacements; - const std::string port_str = base::IntToString(endpoint->port()); - replacements.SetPort(port_str.c_str(), - url_parse::Component(0, port_str.size())); - replacements.SetHost(endpoint->host().c_str(), - url_parse::Component(0, endpoint->host().size())); - return url.ReplaceComponents(replacements); - } - return url; -} - -// static -void HttpStreamFactory::add_forced_spdy_exclusion(const std::string& value) { - HostPortPair pair = HostPortPair::FromURL(GURL(value)); - if (!forced_spdy_exclusions_) - forced_spdy_exclusions_ = new std::list<HostPortPair>(); - forced_spdy_exclusions_->push_back(pair); -} - -// static -bool HttpStreamFactory::HasSpdyExclusion(const HostPortPair& endpoint) { - std::list<HostPortPair>* exclusions = forced_spdy_exclusions_; - if (!exclusions) - return false; - - std::list<HostPortPair>::const_iterator it; - for (it = exclusions->begin(); it != exclusions->end(); ++it) - if (it->Equals(endpoint)) - return true; - return false; -} - -// static -void HttpStreamFactory::EnableNpnHttpOnly() { - // Avoid alternate protocol in this case. Otherwise, browser will try SSL - // and then fallback to http. This introduces extra load. - set_use_alternate_protocols(false); - std::vector<NextProto> next_protos; - next_protos.push_back(kProtoHTTP11); - SetNextProtos(next_protos); -} - -// static -void HttpStreamFactory::EnableNpnSpdy3() { - set_use_alternate_protocols(true); - std::vector<NextProto> next_protos; - next_protos.push_back(kProtoHTTP11); - next_protos.push_back(kProtoQUIC1SPDY3); - next_protos.push_back(kProtoSPDY3); - SetNextProtos(next_protos); -} - -// static -void HttpStreamFactory::EnableNpnSpdy31() { - set_use_alternate_protocols(true); - std::vector<NextProto> next_protos; - next_protos.push_back(kProtoHTTP11); - next_protos.push_back(kProtoQUIC1SPDY3); - next_protos.push_back(kProtoSPDY3); - next_protos.push_back(kProtoSPDY31); - SetNextProtos(next_protos); -} - -// static -void HttpStreamFactory::EnableNpnSpdy31WithSpdy2() { - set_use_alternate_protocols(true); - std::vector<NextProto> next_protos; - next_protos.push_back(kProtoHTTP11); - next_protos.push_back(kProtoQUIC1SPDY3); - next_protos.push_back(kProtoDeprecatedSPDY2); - next_protos.push_back(kProtoSPDY3); - next_protos.push_back(kProtoSPDY31); - SetNextProtos(next_protos); -} - -// static -void HttpStreamFactory::EnableNpnSpdy4a2() { - set_use_alternate_protocols(true); - std::vector<NextProto> next_protos; - next_protos.push_back(kProtoHTTP11); - next_protos.push_back(kProtoQUIC1SPDY3); - next_protos.push_back(kProtoSPDY3); - next_protos.push_back(kProtoSPDY31); - next_protos.push_back(kProtoSPDY4a2); - SetNextProtos(next_protos); -} - -// static -void HttpStreamFactory::EnableNpnHttp2Draft04() { - set_use_alternate_protocols(true); - std::vector<NextProto> next_protos; - next_protos.push_back(kProtoHTTP11); - next_protos.push_back(kProtoQUIC1SPDY3); - next_protos.push_back(kProtoSPDY3); - next_protos.push_back(kProtoSPDY31); - next_protos.push_back(kProtoSPDY4a2); - next_protos.push_back(kProtoHTTP2Draft04); - SetNextProtos(next_protos); -} - -// static -void HttpStreamFactory::SetNextProtos(const std::vector<NextProto>& value) { - if (!next_protos_) - next_protos_ = new std::vector<std::string>; - - next_protos_->clear(); - - ResetEnabledProtocols(); - - // TODO(rtenneti): bug 116575 - consider combining the NextProto and - // AlternateProtocol. - for (uint32 i = 0; i < value.size(); ++i) { - NextProto proto = value[i]; - // Add the protocol to the TLS next protocol list, except for QUIC - // since it uses UDP. - if (proto != kProtoQUIC1SPDY3) { - next_protos_->push_back(SSLClientSocket::NextProtoToString(proto)); - } - - // Enable the corresponding alternate protocol, except for HTTP - // which has not corresponding alternative. - if (proto != kProtoHTTP11) { - AlternateProtocol alternate = AlternateProtocolFromNextProto(proto); - if (!IsAlternateProtocolValid(alternate)) { - NOTREACHED() << "Invalid next proto: " << proto; - continue; - } - SetProtocolEnabled(alternate); - } - } -} - -HttpStreamFactory::HttpStreamFactory() {} - -} // namespace net +// Copyright (c) 2012 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/http/http_stream_factory.h"
+
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "net/base/host_mapping_rules.h"
+#include "net/base/host_port_pair.h"
+#include "net/http/http_network_session.h"
+#include "url/gurl.h"
+
+namespace net {
+
+// WARNING: If you modify or add any static flags, you must keep them in sync
+// with |ResetStaticSettingsToInit|. This is critical for unit test isolation.
+
+// static
+bool HttpStreamFactory::spdy_enabled_ = true;
+
+HttpStreamFactory::~HttpStreamFactory() {}
+
+// static
+void HttpStreamFactory::ResetStaticSettingsToInit() {
+ spdy_enabled_ = true;
+}
+
+void HttpStreamFactory::ProcessAlternateProtocol(
+ const base::WeakPtr<HttpServerProperties>& http_server_properties,
+ const std::string& alternate_protocol_str,
+ const HostPortPair& http_host_port_pair,
+ const HttpNetworkSession& session) {
+ std::vector<std::string> port_protocol_vector;
+ base::SplitString(alternate_protocol_str, ':', &port_protocol_vector);
+ if (port_protocol_vector.size() != 2) {
+ DVLOG(1) << kAlternateProtocolHeader
+ << " header has too many tokens: "
+ << alternate_protocol_str;
+ return;
+ }
+
+ int port;
+ if (!base::StringToInt(port_protocol_vector[0], &port) ||
+ port <= 0 || port >= 1 << 16) {
+ DVLOG(1) << kAlternateProtocolHeader
+ << " header has unrecognizable port: "
+ << port_protocol_vector[0];
+ return;
+ }
+
+ AlternateProtocol protocol =
+ AlternateProtocolFromString(port_protocol_vector[1]);
+ if (IsAlternateProtocolValid(protocol) &&
+ !session.IsProtocolEnabled(protocol)) {
+ protocol = ALTERNATE_PROTOCOL_BROKEN;
+ }
+
+ if (protocol == ALTERNATE_PROTOCOL_BROKEN) {
+ DVLOG(1) << kAlternateProtocolHeader
+ << " header has unrecognized protocol: "
+ << port_protocol_vector[1];
+ return;
+ }
+
+ HostPortPair host_port(http_host_port_pair);
+ const HostMappingRules* mapping_rules = GetHostMappingRules();
+ if (mapping_rules)
+ mapping_rules->RewriteHost(&host_port);
+
+ if (http_server_properties->HasAlternateProtocol(host_port)) {
+ const PortAlternateProtocolPair existing_alternate =
+ http_server_properties->GetAlternateProtocol(host_port);
+ // If we think the alternate protocol is broken, don't change it.
+ if (existing_alternate.protocol == ALTERNATE_PROTOCOL_BROKEN)
+ return;
+ }
+
+ http_server_properties->SetAlternateProtocol(host_port, port, protocol);
+}
+
+GURL HttpStreamFactory::ApplyHostMappingRules(const GURL& url,
+ HostPortPair* endpoint) {
+ const HostMappingRules* mapping_rules = GetHostMappingRules();
+ if (mapping_rules && mapping_rules->RewriteHost(endpoint)) {
+ url::Replacements<char> replacements;
+ const std::string port_str = base::IntToString(endpoint->port());
+ replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size()));
+ replacements.SetHost(endpoint->host().c_str(),
+ url::Component(0, endpoint->host().size()));
+ return url.ReplaceComponents(replacements);
+ }
+ return url;
+}
+
+HttpStreamFactory::HttpStreamFactory() {}
+
+} // namespace net
diff --git a/chromium/net/http/http_stream_factory.h b/chromium/net/http/http_stream_factory.h index 0d854a5dd9d..f3b0203ccfb 100644 --- a/chromium/net/http/http_stream_factory.h +++ b/chromium/net/http/http_stream_factory.h @@ -7,7 +7,6 @@ #include <list> #include <string> -#include <vector> #include "base/basictypes.h" #include "base/memory/ref_counted.h" @@ -17,7 +16,6 @@ #include "net/base/net_export.h" #include "net/base/request_priority.h" #include "net/http/http_server_properties.h" -#include "net/socket/ssl_client_socket.h" // This file can be included from net/http even though // it is in net/websockets because it doesn't // introduce any link dependency to net/websockets. @@ -36,6 +34,7 @@ class BoundNetLog; class HostMappingRules; class HostPortPair; class HttpAuthController; +class HttpNetworkSession; class HttpResponseInfo; class HttpServerProperties; class HttpStreamBase; @@ -181,7 +180,8 @@ class NET_EXPORT HttpStreamFactory { void ProcessAlternateProtocol( const base::WeakPtr<HttpServerProperties>& http_server_properties, const std::string& alternate_protocol_str, - const HostPortPair& http_host_port_pair); + const HostPortPair& http_host_port_pair, + const HttpNetworkSession& session); GURL ApplyHostMappingRules(const GURL& url, HostPortPair* endpoint); @@ -216,11 +216,6 @@ class NET_EXPORT HttpStreamFactory { const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config) = 0; - // If pipelining is supported, creates a Value summary of the currently active - // pipelines. Caller assumes ownership of the returned value. Otherwise, - // returns an empty Value. - virtual base::Value* PipelineInfoToValue() const = 0; - virtual const HostMappingRules* GetHostMappingRules() const = 0; // Static settings @@ -229,87 +224,18 @@ class NET_EXPORT HttpStreamFactory { static void ResetStaticSettingsToInit(); // Turns spdy on or off. + // TODO(mmenke): Figure out if this can be made a property of the + // HttpNetworkSession. static void set_spdy_enabled(bool value) { spdy_enabled_ = value; - if (!spdy_enabled_) { - delete next_protos_; - next_protos_ = NULL; - } } static bool spdy_enabled() { return spdy_enabled_; } - // Controls whether or not we use the Alternate-Protocol header. - static void set_use_alternate_protocols(bool value) { - use_alternate_protocols_ = value; - } - static bool use_alternate_protocols() { return use_alternate_protocols_; } - - // Controls whether or not we use ssl when in spdy mode. - static void set_force_spdy_over_ssl(bool value) { - force_spdy_over_ssl_ = value; - } - static bool force_spdy_over_ssl() { - return force_spdy_over_ssl_; - } - - // Controls whether or not we use spdy without npn. - static void set_force_spdy_always(bool value) { - force_spdy_always_ = value; - } - static bool force_spdy_always() { return force_spdy_always_; } - - // Add a URL to exclude from forced SPDY. - static void add_forced_spdy_exclusion(const std::string& value); - // Check if a HostPortPair is excluded from using spdy. - static bool HasSpdyExclusion(const HostPortPair& endpoint); - - // Sets http/1.1 as the only protocol supported via NPN or Alternate-Protocol. - static void EnableNpnHttpOnly(); - - // Sets http/1.1, quic, and spdy/3 as the protocols supported via - // NPN or Alternate-Protocol. - static void EnableNpnSpdy3(); - - // Sets http/1.1, quic, spdy/3, and spdy/3.1 as the protocols - // supported via NPN or Alternate-Protocol. - static void EnableNpnSpdy31(); - - // Sets http/1.1, quic, spdy/2, spdy/3, and spdy/3.1 as the - // protocols supported via NPN or Alternate-Protocol. - static void EnableNpnSpdy31WithSpdy2(); - - // Sets http/1.1, quic, spdy/3, spdy/3.1, and spdy/4a2 as the - // protocols supported via NPN or Alternate-Protocol. - static void EnableNpnSpdy4a2(); - - // Sets http/1.1, quic, spdy/3, spdy/3.1, spdy/4a2, and http/2 draft - // 04 as the protocols supported via NPN or Alternate-Protocol. - static void EnableNpnHttp2Draft04(); - - // Sets the protocols supported by NPN (next protocol negotiation) during the - // SSL handshake as well as by HTTP Alternate-Protocol. - static void SetNextProtos(const std::vector<NextProto>& value); - static bool has_next_protos() { return next_protos_ != NULL; } - static const std::vector<std::string>& next_protos() { - return *next_protos_; - } - protected: HttpStreamFactory(); private: - // |protocol| must be a valid protocol value. - static bool IsProtocolEnabled(AlternateProtocol protocol); - static void SetProtocolEnabled(AlternateProtocol protocol); - static void ResetEnabledProtocols(); - - static std::vector<std::string>* next_protos_; - static bool enabled_protocols_[NUM_VALID_ALTERNATE_PROTOCOLS]; static bool spdy_enabled_; - static bool use_alternate_protocols_; - static bool force_spdy_over_ssl_; - static bool force_spdy_always_; - static std::list<HostPortPair>* forced_spdy_exclusions_; DISALLOW_COPY_AND_ASSIGN(HttpStreamFactory); }; diff --git a/chromium/net/http/http_stream_factory_impl.cc b/chromium/net/http/http_stream_factory_impl.cc index 056a0f6b879..4340e62fb6a 100644 --- a/chromium/net/http/http_stream_factory_impl.cc +++ b/chromium/net/http/http_stream_factory_impl.cc @@ -6,14 +6,12 @@ #include <string> +#include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "net/base/net_log.h" #include "net/base/net_util.h" #include "net/http/http_network_session.h" -#include "net/http/http_pipelined_connection.h" -#include "net/http/http_pipelined_host.h" -#include "net/http/http_pipelined_stream.h" #include "net/http/http_server_properties.h" #include "net/http/http_stream_factory_impl_job.h" #include "net/http/http_stream_factory_impl_request.h" @@ -44,15 +42,11 @@ GURL UpgradeUrlToHttps(const GURL& original_url, int port) { HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session, bool for_websockets) : session_(session), - http_pipelined_host_pool_(this, NULL, - session_->http_server_properties(), - session_->force_http_pipelining()), for_websockets_(for_websockets) {} HttpStreamFactoryImpl::~HttpStreamFactoryImpl() { DCHECK(request_map_.empty()); DCHECK(spdy_session_request_map_.empty()); - DCHECK(http_pipelining_request_map_.empty()); std::set<const Job*> tmp_job_set; tmp_job_set.swap(orphaned_job_set_); @@ -178,18 +172,14 @@ void HttpStreamFactoryImpl::PreconnectStreams( job->Preconnect(num_streams); } -base::Value* HttpStreamFactoryImpl::PipelineInfoToValue() const { - return http_pipelined_host_pool_.PipelineInfoToValue(); -} - const HostMappingRules* HttpStreamFactoryImpl::GetHostMappingRules() const { return session_->params().host_mapping_rules; } PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor( const GURL& original_url, - GURL* alternate_url) const { - if (!use_alternate_protocols()) + GURL* alternate_url) { + if (!session_->params().use_alternate_protocols) return kNoAlternateProtocol; if (original_url.SchemeIs("ftp")) @@ -198,15 +188,19 @@ PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor( HostPortPair origin = HostPortPair(original_url.HostNoBrackets(), original_url.EffectiveIntPort()); - const HttpServerProperties& http_server_properties = + HttpServerProperties& http_server_properties = *session_->http_server_properties(); if (!http_server_properties.HasAlternateProtocol(origin)) return kNoAlternateProtocol; PortAlternateProtocolPair alternate = http_server_properties.GetAlternateProtocol(origin); - if (alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) + if (alternate.protocol == ALTERNATE_PROTOCOL_BROKEN) { + HistogramAlternateProtocolUsage( + ALTERNATE_PROTOCOL_USAGE_BROKEN, + http_server_properties.GetAlternateProtocolExperiment()); return kNoAlternateProtocol; + } if (!IsAlternateProtocolValid(alternate.protocol)) { NOTREACHED(); @@ -228,10 +222,10 @@ PortAlternateProtocolPair HttpStreamFactoryImpl::GetAlternateProtocolRequestFor( origin.set_port(alternate.port); if (alternate.protocol >= NPN_SPDY_MINIMUM_VERSION && alternate.protocol <= NPN_SPDY_MAXIMUM_VERSION) { - if (!spdy_enabled()) + if (!HttpStreamFactory::spdy_enabled()) return kNoAlternateProtocol; - if (HttpStreamFactory::HasSpdyExclusion(origin)) + if (session_->HasSpdyExclusion(origin)) return kNoAlternateProtocol; *alternate_url = UpgradeUrlToHttps(original_url, alternate.port); @@ -290,15 +284,9 @@ void HttpStreamFactoryImpl::OnNewSpdySessionReady( using_spdy, net_log); if (for_websockets_) { - WebSocketHandshakeStreamBase::CreateHelper* create_helper = - request->websocket_handshake_stream_create_helper(); - DCHECK(create_helper); - bool use_relative_url = direct || request->url().SchemeIs("wss"); - request->OnWebSocketHandshakeStreamReady( - NULL, - used_ssl_config, - used_proxy_info, - create_helper->CreateSpdyStream(spdy_session, use_relative_url)); + // TODO(ricea): Restore this code path when WebSocket over SPDY + // implementation is ready. + NOTREACHED(); } else { bool use_relative_url = direct || request->url().SchemeIs("https"); request->OnStreamReady( @@ -322,40 +310,4 @@ void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) { OnPreconnectsCompleteInternal(); } -void HttpStreamFactoryImpl::OnHttpPipelinedHostHasAdditionalCapacity( - HttpPipelinedHost* host) { - while (ContainsKey(http_pipelining_request_map_, host->GetKey())) { - HttpPipelinedStream* stream = - http_pipelined_host_pool_.CreateStreamOnExistingPipeline( - host->GetKey()); - if (!stream) { - break; - } - - Request* request = *http_pipelining_request_map_[host->GetKey()].begin(); - request->Complete(stream->was_npn_negotiated(), - stream->protocol_negotiated(), - false, // not using_spdy - stream->net_log()); - request->OnStreamReady(NULL, - stream->used_ssl_config(), - stream->used_proxy_info(), - stream); - } -} - -void HttpStreamFactoryImpl::AbortPipelinedRequestsWithKey( - const Job* job, const HttpPipelinedHost::Key& key, int status, - const SSLConfig& used_ssl_config) { - RequestVector requests_to_fail = http_pipelining_request_map_[key]; - for (RequestVector::const_iterator it = requests_to_fail.begin(); - it != requests_to_fail.end(); ++it) { - Request* request = *it; - if (request == request_map_[job]) { - continue; - } - request->OnStreamFailed(NULL, status, used_ssl_config); - } -} - } // namespace net diff --git a/chromium/net/http/http_stream_factory_impl.h b/chromium/net/http/http_stream_factory_impl.h index 4824dece14f..8a6fdaf4fc3 100644 --- a/chromium/net/http/http_stream_factory_impl.h +++ b/chromium/net/http/http_stream_factory_impl.h @@ -13,7 +13,6 @@ #include "base/memory/ref_counted.h" #include "net/base/host_port_pair.h" #include "net/base/net_log.h" -#include "net/http/http_pipelined_host_pool.h" #include "net/http/http_stream_factory.h" #include "net/proxy/proxy_server.h" #include "net/socket/ssl_client_socket.h" @@ -22,12 +21,9 @@ namespace net { class HttpNetworkSession; -class HttpPipelinedHost; class SpdySession; -class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : - public HttpStreamFactory, - public HttpPipelinedHostPool::Delegate { +class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : public HttpStreamFactory { public: // RequestStream may only be called if |for_websockets| is false. // RequestWebSocketHandshakeStream may only be called if |for_websockets| @@ -58,13 +54,8 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : RequestPriority priority, const SSLConfig& server_ssl_config, const SSLConfig& proxy_ssl_config) OVERRIDE; - virtual base::Value* PipelineInfoToValue() const OVERRIDE; virtual const HostMappingRules* GetHostMappingRules() const OVERRIDE; - // HttpPipelinedHostPool::Delegate interface - virtual void OnHttpPipelinedHostHasAdditionalCapacity( - HttpPipelinedHost* host) OVERRIDE; - size_t num_orphaned_jobs() const { return orphaned_job_set_.size(); } private: @@ -74,10 +65,7 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : class NET_EXPORT_PRIVATE Job; typedef std::set<Request*> RequestSet; - typedef std::vector<Request*> RequestVector; typedef std::map<SpdySessionKey, RequestSet> SpdySessionRequestMap; - typedef std::map<HttpPipelinedHost::Key, - RequestVector> HttpPipeliningRequestMap; HttpStreamRequest* RequestStreamInternal( const HttpRequestInfo& info, @@ -90,7 +78,7 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : PortAlternateProtocolPair GetAlternateProtocolRequestFor( const GURL& original_url, - GURL* alternate_url) const; + GURL* alternate_url); // Detaches |job| from |request|. void OrphanJob(Job* job, const Request* request); @@ -121,11 +109,6 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : // Called when the Preconnect completes. Used for testing. virtual void OnPreconnectsCompleteInternal() {} - void AbortPipelinedRequestsWithKey(const Job* job, - const HttpPipelinedHost::Key& key, - int status, - const SSLConfig& used_ssl_config); - HttpNetworkSession* const session_; // All Requests are handed out to clients. By the time HttpStreamFactoryImpl @@ -134,9 +117,6 @@ class NET_EXPORT_PRIVATE HttpStreamFactoryImpl : std::map<const Job*, Request*> request_map_; SpdySessionRequestMap spdy_session_request_map_; - HttpPipeliningRequestMap http_pipelining_request_map_; - - HttpPipelinedHostPool http_pipelined_host_pool_; // These jobs correspond to jobs orphaned by Requests and now owned by // HttpStreamFactoryImpl. Since they are no longer tied to Requests, they will diff --git a/chromium/net/http/http_stream_factory_impl_job.cc b/chromium/net/http/http_stream_factory_impl_job.cc index c86bee7495b..79a95361d43 100644 --- a/chromium/net/http/http_stream_factory_impl_job.cc +++ b/chromium/net/http/http_stream_factory_impl_job.cc @@ -20,10 +20,6 @@ #include "net/base/net_util.h" #include "net/http/http_basic_stream.h" #include "net/http/http_network_session.h" -#include "net/http/http_pipelined_connection.h" -#include "net/http/http_pipelined_host.h" -#include "net/http/http_pipelined_host_pool.h" -#include "net/http/http_pipelined_stream.h" #include "net/http/http_proxy_client_socket.h" #include "net/http/http_proxy_client_socket_pool.h" #include "net/http/http_request_info.h" @@ -98,15 +94,15 @@ HttpStreamFactoryImpl::Job::Job(HttpStreamFactoryImpl* stream_factory, using_spdy_(false), using_quic_(false), quic_request_(session_->quic_stream_factory()), - force_spdy_always_(HttpStreamFactory::force_spdy_always()), - force_spdy_over_ssl_(HttpStreamFactory::force_spdy_over_ssl()), + using_existing_quic_session_(false), spdy_certificate_error_(OK), establishing_tunnel_(false), was_npn_negotiated_(false), protocol_negotiated_(kProtoUnknown), num_streams_(0), spdy_session_direct_(false), - existing_available_pipeline_(false), + job_status_(STATUS_RUNNING), + other_job_status_(STATUS_RUNNING), ptr_factory_(this) { DCHECK(stream_factory); DCHECK(session); @@ -217,15 +213,17 @@ void HttpStreamFactoryImpl::Job::Orphan(const Request* request) { blocking_job_->waiting_job_ = NULL; blocking_job_ = NULL; if (stream_factory_->for_websockets_ && - connection_ && connection_->socket()) + connection_ && connection_->socket()) { connection_->socket()->Disconnect(); + } stream_factory_->OnOrphanedJobComplete(this); } else if (stream_factory_->for_websockets_) { // We cancel this job because a WebSocketHandshakeStream can't be created // without a WebSocketHandshakeStreamBase::CreateHelper which is stored in // the Request class and isn't accessible from this job. - if (connection_ && connection_->socket()) + if (connection_ && connection_->socket()) { connection_->socket()->Disconnect(); + } stream_factory_->OnOrphanedJobComplete(this); } } @@ -291,10 +289,11 @@ bool HttpStreamFactoryImpl::Job::CanUseExistingSpdySession() const { // The only time we can use an existing session is if the request URL is // https (the normal case) or if we're connection to a SPDY proxy, or // if we're running with force_spdy_always_. crbug.com/133176 + // TODO(ricea): Add "wss" back to this list when SPDY WebSocket support is + // working. return request_info_.url.SchemeIs("https") || - request_info_.url.SchemeIs("wss") || proxy_info_.proxy_server().is_https() || - force_spdy_always_; + session_->params().force_spdy_always; } void HttpStreamFactoryImpl::Job::OnStreamReadyCallback() { @@ -333,20 +332,26 @@ void HttpStreamFactoryImpl::Job::OnWebSocketHandshakeStreamReadyCallback() { } void HttpStreamFactoryImpl::Job::OnNewSpdySessionReadyCallback() { - DCHECK(!stream_.get()); + DCHECK(stream_.get()); DCHECK(!IsPreconnecting()); DCHECK(using_spdy()); - if (!new_spdy_session_) - return; + // Note: an event loop iteration has passed, so |new_spdy_session_| may be + // NULL at this point if the SpdySession closed immediately after creation. base::WeakPtr<SpdySession> spdy_session = new_spdy_session_; new_spdy_session_.reset(); + + // TODO(jgraettinger): Notify the factory, and let that notify |request_|, + // rather than notifying |request_| directly. if (IsOrphaned()) { - stream_factory_->OnNewSpdySessionReady( - spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_, - was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_); + if (spdy_session) { + stream_factory_->OnNewSpdySessionReady( + spdy_session, spdy_session_direct_, server_ssl_config_, proxy_info_, + was_npn_negotiated(), protocol_negotiated(), using_spdy(), net_log_); + } stream_factory_->OnOrphanedJobComplete(this); } else { - request_->OnNewSpdySessionReady(this, spdy_session, spdy_session_direct_); + request_->OnNewSpdySessionReady( + this, stream_.Pass(), spdy_session, spdy_session_direct_); } // |this| may be deleted after this call. } @@ -451,9 +456,8 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { if (IsPreconnecting()) { base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind( - &HttpStreamFactoryImpl::Job::OnPreconnectsComplete, - ptr_factory_.GetWeakPtr())); + base::Bind(&HttpStreamFactoryImpl::Job::OnPreconnectsComplete, + ptr_factory_.GetWeakPtr())); return ERR_IO_PENDING; } @@ -464,64 +468,57 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { next_state_ = STATE_WAITING_USER_ACTION; base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind( - &HttpStreamFactoryImpl::Job::OnCertificateErrorCallback, - ptr_factory_.GetWeakPtr(), - result, ssl_info_)); + base::Bind(&HttpStreamFactoryImpl::Job::OnCertificateErrorCallback, + ptr_factory_.GetWeakPtr(), result, ssl_info_)); return ERR_IO_PENDING; } switch (result) { - case ERR_PROXY_AUTH_REQUESTED: - { - DCHECK(connection_.get()); - DCHECK(connection_->socket()); - DCHECK(establishing_tunnel_); - - ProxyClientSocket* proxy_socket = - static_cast<ProxyClientSocket*>(connection_->socket()); - const HttpResponseInfo* tunnel_auth_response = - proxy_socket->GetConnectResponseInfo(); - - next_state_ = STATE_WAITING_USER_ACTION; - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind( - &Job::OnNeedsProxyAuthCallback, - ptr_factory_.GetWeakPtr(), - *tunnel_auth_response, - proxy_socket->GetAuthController())); - } + case ERR_PROXY_AUTH_REQUESTED: { + UMA_HISTOGRAM_BOOLEAN("Net.ProxyAuthRequested.HasConnection", + connection_.get() != NULL); + if (!connection_.get()) + return ERR_PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION; + CHECK(connection_->socket()); + CHECK(establishing_tunnel_); + + next_state_ = STATE_WAITING_USER_ACTION; + ProxyClientSocket* proxy_socket = + static_cast<ProxyClientSocket*>(connection_->socket()); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&Job::OnNeedsProxyAuthCallback, ptr_factory_.GetWeakPtr(), + *proxy_socket->GetConnectResponseInfo(), + proxy_socket->GetAuthController())); return ERR_IO_PENDING; + } case ERR_SSL_CLIENT_AUTH_CERT_NEEDED: base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind( - &Job::OnNeedsClientAuthCallback, - ptr_factory_.GetWeakPtr(), - connection_->ssl_error_response_info().cert_request_info)); + base::Bind(&Job::OnNeedsClientAuthCallback, ptr_factory_.GetWeakPtr(), + connection_->ssl_error_response_info().cert_request_info)); return ERR_IO_PENDING; - case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: - { - DCHECK(connection_.get()); - DCHECK(connection_->socket()); - DCHECK(establishing_tunnel_); + case ERR_HTTPS_PROXY_TUNNEL_RESPONSE: { + DCHECK(connection_.get()); + DCHECK(connection_->socket()); + DCHECK(establishing_tunnel_); - ProxyClientSocket* proxy_socket = - static_cast<ProxyClientSocket*>(connection_->socket()); - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind( - &Job::OnHttpsProxyTunnelResponseCallback, - ptr_factory_.GetWeakPtr(), - *proxy_socket->GetConnectResponseInfo(), - proxy_socket->CreateConnectResponseStream())); - return ERR_IO_PENDING; - } + ProxyClientSocket* proxy_socket = + static_cast<ProxyClientSocket*>(connection_->socket()); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&Job::OnHttpsProxyTunnelResponseCallback, + ptr_factory_.GetWeakPtr(), + *proxy_socket->GetConnectResponseInfo(), + proxy_socket->CreateConnectResponseStream())); + return ERR_IO_PENDING; + } case OK: + job_status_ = STATUS_SUCCEEDED; + MaybeMarkAlternateProtocolBroken(); next_state_ = STATE_DONE; if (new_spdy_session_.get()) { base::MessageLoop::current()->PostTask( @@ -538,22 +535,22 @@ int HttpStreamFactoryImpl::Job::RunLoop(int result) { DCHECK(stream_.get()); base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind( - &Job::OnStreamReadyCallback, - ptr_factory_.GetWeakPtr())); + base::Bind(&Job::OnStreamReadyCallback, ptr_factory_.GetWeakPtr())); } return ERR_IO_PENDING; default: + if (job_status_ != STATUS_BROKEN) { + DCHECK_EQ(STATUS_RUNNING, job_status_); + job_status_ = STATUS_FAILED; + MaybeMarkAlternateProtocolBroken(); + } base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind( - &Job::OnStreamFailedCallback, - ptr_factory_.GetWeakPtr(), - result)); + base::Bind(&Job::OnStreamFailedCallback, ptr_factory_.GetWeakPtr(), + result)); return ERR_IO_PENDING; } - return result; } int HttpStreamFactoryImpl::Job::DoLoop(int result) { @@ -627,7 +624,6 @@ int HttpStreamFactoryImpl::Job::DoStart() { origin_ = HostPortPair(request_info_.url.HostNoBrackets(), port); origin_url_ = stream_factory_->ApplyHostMappingRules( request_info_.url, &origin_); - http_pipelining_key_.reset(new HttpPipelinedHost::Key(origin_)); net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_JOB, base::Bind(&NetLogHttpStreamJobCallback, @@ -674,7 +670,7 @@ int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) { if (result == OK) { // Remove unsupported proxies from the list. proxy_info_.RemoveProxiesWithoutScheme( - ProxyServer::SCHEME_DIRECT | + ProxyServer::SCHEME_DIRECT | ProxyServer::SCHEME_QUIC | ProxyServer::SCHEME_HTTP | ProxyServer::SCHEME_HTTPS | ProxyServer::SCHEME_SOCKS4 | ProxyServer::SCHEME_SOCKS5); @@ -682,6 +678,11 @@ int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) { // No proxies/direct to choose from. This happens when we don't support // any of the proxies in the returned list. result = ERR_NO_SUPPORTED_PROXIES; + } else if (using_quic_ && + (!proxy_info_.is_quic() && !proxy_info_.is_direct())) { + // QUIC can not be spoken to non-QUIC proxies. This error should not be + // user visible, because the non-alternate job should be resumed. + result = ERR_NO_SUPPORTED_PROXIES; } } @@ -701,13 +702,15 @@ int HttpStreamFactoryImpl::Job::DoResolveProxyComplete(int result) { } bool HttpStreamFactoryImpl::Job::ShouldForceSpdySSL() const { - bool rv = force_spdy_always_ && force_spdy_over_ssl_; - return rv && !HttpStreamFactory::HasSpdyExclusion(origin_); + bool rv = session_->params().force_spdy_always && + session_->params().force_spdy_over_ssl; + return rv && !session_->HasSpdyExclusion(origin_); } bool HttpStreamFactoryImpl::Job::ShouldForceSpdyWithoutSSL() const { - bool rv = force_spdy_always_ && !force_spdy_over_ssl_; - return rv && !HttpStreamFactory::HasSpdyExclusion(origin_); + bool rv = session_->params().force_spdy_always && + !session_->params().force_spdy_over_ssl; + return rv && !session_->HasSpdyExclusion(origin_); } bool HttpStreamFactoryImpl::Job::ShouldForceQuic() const { @@ -742,19 +745,26 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { if (ShouldForceQuic()) using_quic_ = true; + if (proxy_info_.is_quic()) + using_quic_ = true; + if (using_quic_) { DCHECK(session_->params().enable_quic); - if (!proxy_info_.is_direct()) { + if (proxy_info_.is_quic() && !request_info_.url.SchemeIs("http")) { NOTREACHED(); - // TODO(rch): support QUIC proxies. + // TODO(rch): support QUIC proxies for HTTPS urls. return ERR_NOT_IMPLEMENTED; } + HostPortPair destination = proxy_info_.is_quic() ? + proxy_info_.proxy_server().host_port_pair() : origin_; next_state_ = STATE_INIT_CONNECTION_COMPLETE; - const ProxyServer& proxy_server = proxy_info_.proxy_server(); - int rv = quic_request_.Request(HostPortProxyPair(origin_, proxy_server), - using_ssl_, session_->cert_verifier(), - net_log_, io_callback_); - if (rv != OK) { + bool secure_quic = using_ssl_ || proxy_info_.is_quic(); + int rv = quic_request_.Request( + destination, secure_quic, request_info_.privacy_mode, + request_info_.method, net_log_, io_callback_); + if (rv == OK) { + using_existing_quic_session_ = true; + } else { // OK, there's no available QUIC session. Let |waiting_job_| resume // if it's paused. if (waiting_job_) { @@ -780,29 +790,10 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { next_state_ = STATE_CREATE_STREAM; existing_spdy_session_ = spdy_session; return OK; - } else if (request_ && (using_ssl_ || ShouldForceSpdyWithoutSSL())) { + } else if (request_ && !request_->HasSpdySessionKey() && + (using_ssl_ || ShouldForceSpdyWithoutSSL())) { // Update the spdy session key for the request that launched this job. request_->SetSpdySessionKey(spdy_session_key); - } else if (IsRequestEligibleForPipelining()) { - // TODO(simonjam): With pipelining, we might be better off using fewer - // connections and thus should make fewer preconnections. Explore - // preconnecting fewer than the requested num_connections. - // - // Separate note: A forced pipeline is always available if one exists for - // this key. This is different than normal pipelines, which may be - // unavailable or unusable. So, there is no need to worry about a race - // between when a pipeline becomes available and when this job blocks. - existing_available_pipeline_ = stream_factory_->http_pipelined_host_pool_. - IsExistingPipelineAvailableForKey(*http_pipelining_key_.get()); - if (existing_available_pipeline_) { - return OK; - } else { - bool was_new_key = request_->SetHttpPipeliningKey( - *http_pipelining_key_.get()); - if (!was_new_key && session_->force_http_pipelining()) { - return ERR_IO_PENDING; - } - } } // OK, there's no available SPDY session. Let |waiting_job_| resume if it's @@ -847,28 +838,32 @@ int HttpStreamFactoryImpl::Job::DoInitConnection() { request_info_.privacy_mode, net_log_, num_streams_); - } else { - // If we can't use a SPDY session, don't both checking for one after - // the hostname is resolved. - OnHostResolutionCallback resolution_callback = CanUseExistingSpdySession() ? - base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(), - GetSpdySessionKey()) : - OnHostResolutionCallback(); - if (stream_factory_->for_websockets_) { - return InitSocketHandleForWebSocketRequest( - origin_url_, request_info_.extra_headers, request_info_.load_flags, - priority_, session_, proxy_info_, ShouldForceSpdySSL(), - want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_, - request_info_.privacy_mode, net_log_, - connection_.get(), resolution_callback, io_callback_); - } - return InitSocketHandleForHttpRequest( + } + + // If we can't use a SPDY session, don't both checking for one after + // the hostname is resolved. + OnHostResolutionCallback resolution_callback = CanUseExistingSpdySession() ? + base::Bind(&Job::OnHostResolution, session_->spdy_session_pool(), + GetSpdySessionKey()) : + OnHostResolutionCallback(); + if (stream_factory_->for_websockets_) { + // TODO(ricea): Re-enable NPN when WebSockets over SPDY is supported. + SSLConfig websocket_server_ssl_config = server_ssl_config_; + websocket_server_ssl_config.next_protos.clear(); + return InitSocketHandleForWebSocketRequest( origin_url_, request_info_.extra_headers, request_info_.load_flags, priority_, session_, proxy_info_, ShouldForceSpdySSL(), - want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_, + want_spdy_over_npn, websocket_server_ssl_config, proxy_ssl_config_, request_info_.privacy_mode, net_log_, connection_.get(), resolution_callback, io_callback_); } + + return InitSocketHandleForHttpRequest( + origin_url_, request_info_.extra_headers, request_info_.load_flags, + priority_, session_, proxy_info_, ShouldForceSpdySSL(), + want_spdy_over_npn, server_ssl_config_, proxy_ssl_config_, + request_info_.privacy_mode, net_log_, + connection_.get(), resolution_callback, io_callback_); } int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { @@ -903,11 +898,6 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { waiting_job_ = NULL; } - if (result < 0 && session_->force_http_pipelining()) { - stream_factory_->AbortPipelinedRequestsWithKey( - this, *http_pipelining_key_.get(), result, server_ssl_config_); - } - // |result| may be the result of any of the stacked pools. The following // logic is used when determining how to interpret an error. // If |result| < 0: @@ -976,15 +966,17 @@ int HttpStreamFactoryImpl::Job::DoInitConnectionComplete(int result) { } if (!ssl_started && result < 0 && original_url_.get()) { - // Mark the alternate protocol as broken and fallback. - session_->http_server_properties()->SetBrokenAlternateProtocol( - HostPortPair::FromURL(*original_url_)); + job_status_ = STATUS_BROKEN; + MaybeMarkAlternateProtocolBroken(); return result; } if (using_quic_) { - if (result < 0) + if (result < 0) { + job_status_ = STATUS_BROKEN; + MaybeMarkAlternateProtocolBroken(); return result; + } stream_ = quic_request_.ReleaseStream(); next_state_ = STATE_NONE; return OK; @@ -1039,8 +1031,7 @@ int HttpStreamFactoryImpl::Job::DoWaitingUserAction(int result) { } int HttpStreamFactoryImpl::Job::DoCreateStream() { - DCHECK(connection_->socket() || existing_spdy_session_.get() || - existing_available_pipeline_ || using_quic_); + DCHECK(connection_->socket() || existing_spdy_session_.get() || using_quic_); next_state_ = STATE_CREATE_STREAM_COMPLETE; @@ -1055,31 +1046,12 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { bool using_proxy = (proxy_info_.is_http() || proxy_info_.is_https()) && (request_info_.url.SchemeIs("http") || request_info_.url.SchemeIs("ftp")); - if (stream_factory_->http_pipelined_host_pool_. - IsExistingPipelineAvailableForKey(*http_pipelining_key_.get())) { - DCHECK(!stream_factory_->for_websockets_); - stream_.reset(stream_factory_->http_pipelined_host_pool_. - CreateStreamOnExistingPipeline( - *http_pipelining_key_.get())); - CHECK(stream_.get()); - } else if (stream_factory_->for_websockets_) { + if (stream_factory_->for_websockets_) { DCHECK(request_); DCHECK(request_->websocket_handshake_stream_create_helper()); websocket_stream_.reset( request_->websocket_handshake_stream_create_helper() ->CreateBasicStream(connection_.Pass(), using_proxy)); - } else if (!using_proxy && IsRequestEligibleForPipelining()) { - // TODO(simonjam): Support proxies. - stream_.reset( - stream_factory_->http_pipelined_host_pool_.CreateStreamOnNewPipeline( - *http_pipelining_key_.get(), - connection_.release(), - server_ssl_config_, - proxy_info_, - net_log_, - was_npn_negotiated_, - protocol_negotiated_)); - CHECK(stream_.get()); } else { stream_.reset(new HttpBasicStream(connection_.release(), using_proxy)); } @@ -1098,7 +1070,7 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { // We never use privacy mode for connection to proxy server. spdy_session_key = SpdySessionKey(proxy_server.host_port_pair(), ProxyServer::Direct(), - kPrivacyModeDisabled); + PRIVACY_MODE_DISABLED); direct = false; } @@ -1113,21 +1085,34 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { SpdySessionPool* spdy_pool = session_->spdy_session_pool(); spdy_session = spdy_pool->FindAvailableSession(spdy_session_key, net_log_); if (!spdy_session) { - int error = + base::WeakPtr<SpdySession> new_spdy_session = spdy_pool->CreateAvailableSessionFromSocket(spdy_session_key, connection_.Pass(), net_log_, spdy_certificate_error_, - &new_spdy_session_, using_ssl_); - if (error != OK) - return error; + if (!new_spdy_session->HasAcceptableTransportSecurity()) { + new_spdy_session->CloseSessionOnError( + ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY, ""); + return ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY; + } + + new_spdy_session_ = new_spdy_session; + spdy_session_direct_ = direct; const HostPortPair& host_port_pair = spdy_session_key.host_port_pair(); base::WeakPtr<HttpServerProperties> http_server_properties = session_->http_server_properties(); if (http_server_properties) http_server_properties->SetSupportsSpdy(host_port_pair, true); - spdy_session_direct_ = direct; + + // Create a SpdyHttpStream attached to the session; + // OnNewSpdySessionReadyCallback is not called until an event loop + // iteration later, so if the SpdySession is closed between then, allow + // reuse state from the underlying socket, sampled by SpdyHttpStream, + // bubble up to the request. + bool use_relative_url = direct || request_info_.url.SchemeIs("https"); + stream_.reset(new SpdyHttpStream(new_spdy_session_, use_relative_url)); + return OK; } } @@ -1140,12 +1125,8 @@ int HttpStreamFactoryImpl::Job::DoCreateStream() { // will know when SpdySessions become available. if (stream_factory_->for_websockets_) { - DCHECK(request_); - DCHECK(request_->websocket_handshake_stream_create_helper()); - bool use_relative_url = direct || request_info_.url.SchemeIs("wss"); - websocket_stream_.reset( - request_->websocket_handshake_stream_create_helper()->CreateSpdyStream( - spdy_session, use_relative_url)); + // TODO(ricea): Restore this code when WebSockets over SPDY is implemented. + NOTREACHED(); } else { bool use_relative_url = direct || request_info_.url.SchemeIs("https"); stream_.reset(new SpdyHttpStream(spdy_session, use_relative_url)); @@ -1194,10 +1175,8 @@ void HttpStreamFactoryImpl::Job::ReturnToStateInitConnection( connection_->socket()->Disconnect(); connection_->Reset(); - if (request_) { + if (request_) request_->RemoveRequestFromSpdySessionRequestMap(); - request_->RemoveRequestFromHttpPipeliningRequestMap(); - } next_state_ = STATE_INIT_CONNECTION; } @@ -1282,7 +1261,7 @@ void HttpStreamFactoryImpl::Job::InitSSLConfig( ssl_config->verify_ev_cert = true; // Disable Channel ID if privacy mode is enabled. - if (request_info_.privacy_mode == kPrivacyModeEnabled) + if (request_info_.privacy_mode == PRIVACY_MODE_ENABLED) ssl_config->channel_id_enabled = false; } @@ -1338,21 +1317,20 @@ int HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError(int error) { if (proxy_info_.is_https() && proxy_ssl_config_.send_client_cert) { session_->ssl_client_auth_cache()->Remove( - proxy_info_.proxy_server().host_port_pair().ToString()); + proxy_info_.proxy_server().host_port_pair()); } int rv = session_->proxy_service()->ReconsiderProxyAfterError( - request_info_.url, &proxy_info_, io_callback_, &pac_request_, net_log_); + request_info_.url, error, &proxy_info_, io_callback_, &pac_request_, + net_log_); if (rv == OK || rv == ERR_IO_PENDING) { // If the error was during connection setup, there is no socket to // disconnect. if (connection_->socket()) connection_->socket()->Disconnect(); connection_->Reset(); - if (request_) { + if (request_) request_->RemoveRequestFromSpdySessionRequestMap(); - request_->RemoveRequestFromHttpPipeliningRequestMap(); - } next_state_ = STATE_RESOLVE_PROXY_COMPLETE; } else { // If ReconsiderProxyAfterError() failed synchronously, it means @@ -1446,33 +1424,59 @@ bool HttpStreamFactoryImpl::Job::IsOrphaned() const { return !IsPreconnecting() && !request_; } -bool HttpStreamFactoryImpl::Job::IsRequestEligibleForPipelining() { - if (IsPreconnecting() || !request_) { - return false; - } - if (stream_factory_->for_websockets_) { - return false; - } - if (session_->force_http_pipelining()) { - return true; - } - if (!session_->params().http_pipelining_enabled) { - return false; +void HttpStreamFactoryImpl::Job::ReportJobSuccededForRequest() { + net::AlternateProtocolExperiment alternate_protocol_experiment = + ALTERNATE_PROTOCOL_NOT_PART_OF_EXPERIMENT; + base::WeakPtr<HttpServerProperties> http_server_properties = + session_->http_server_properties(); + if (http_server_properties) { + alternate_protocol_experiment = + http_server_properties->GetAlternateProtocolExperiment(); } - if (using_ssl_) { - return false; + if (using_existing_quic_session_) { + // If an existing session was used, then no TCP connection was + // started. + HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_NO_RACE, + alternate_protocol_experiment); + } else if (original_url_) { + // This job was the alternate protocol job, and hence won the race. + HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_WON_RACE, + alternate_protocol_experiment); + } else { + // This job was the normal job, and hence the alternate protocol job lost + // the race. + HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_LOST_RACE, + alternate_protocol_experiment); } - if (request_info_.method != "GET" && request_info_.method != "HEAD") { - return false; +} + +void HttpStreamFactoryImpl::Job::MarkOtherJobComplete(const Job& job) { + DCHECK_EQ(STATUS_RUNNING, other_job_status_); + other_job_status_ = job.job_status_; + MaybeMarkAlternateProtocolBroken(); +} + +void HttpStreamFactoryImpl::Job::MaybeMarkAlternateProtocolBroken() { + if (job_status_ == STATUS_RUNNING || other_job_status_ == STATUS_RUNNING) + return; + + bool is_alternate_protocol_job = original_url_.get() != NULL; + if (is_alternate_protocol_job) { + if (job_status_ == STATUS_BROKEN && other_job_status_ == STATUS_SUCCEEDED) { + HistogramBrokenAlternateProtocolLocation( + BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_ALT); + session_->http_server_properties()->SetBrokenAlternateProtocol( + HostPortPair::FromURL(*original_url_)); + } + return; } - if (request_info_.load_flags & - (net::LOAD_MAIN_FRAME | net::LOAD_SUB_FRAME | net::LOAD_PREFETCH | - net::LOAD_IS_DOWNLOAD)) { - // Avoid pipelining resources that may be streamed for a long time. - return false; + + if (job_status_ == STATUS_SUCCEEDED && other_job_status_ == STATUS_BROKEN) { + HistogramBrokenAlternateProtocolLocation( + BROKEN_ALTERNATE_PROTOCOL_LOCATION_HTTP_STREAM_FACTORY_IMPL_JOB_MAIN); + session_->http_server_properties()->SetBrokenAlternateProtocol( + HostPortPair::FromURL(request_info_.url)); } - return stream_factory_->http_pipelined_host_pool_.IsKeyEligibleForPipelining( - *http_pipelining_key_.get()); } } // namespace net diff --git a/chromium/net/http/http_stream_factory_impl_job.h b/chromium/net/http/http_stream_factory_impl_job.h index 1970b5be08b..9659d45060a 100644 --- a/chromium/net/http/http_stream_factory_impl_job.h +++ b/chromium/net/http/http_stream_factory_impl_job.h @@ -13,7 +13,6 @@ #include "net/base/request_priority.h" #include "net/http/http_auth.h" #include "net/http/http_auth_controller.h" -#include "net/http/http_pipelined_host.h" #include "net/http/http_request_info.h" #include "net/http/http_stream_factory_impl.h" #include "net/proxy/proxy_service.h" @@ -92,6 +91,13 @@ class HttpStreamFactoryImpl::Job { // Indicates whether or not this Job has been orphaned by a Request. bool IsOrphaned() const; + // Called to indicate that this job succeeded, and some other jobs + // will be orphaned. + void ReportJobSuccededForRequest(); + + // Marks that the other |job| has completed. + void MarkOtherJobComplete(const Job& job); + private: enum State { STATE_START, @@ -129,6 +135,13 @@ class HttpStreamFactoryImpl::Job { STATE_NONE }; + enum JobStatus { + STATUS_RUNNING, + STATUS_FAILED, + STATUS_BROKEN, + STATUS_SUCCEEDED + }; + void OnStreamReadyCallback(); void OnWebSocketHandshakeStreamReadyCallback(); // This callback function is called when a new SPDY session is created. @@ -216,7 +229,7 @@ class HttpStreamFactoryImpl::Job { // Should we force QUIC for this stream request. bool ShouldForceQuic() const; - bool IsRequestEligibleForPipelining(); + void MaybeMarkAlternateProtocolBroken(); // Record histograms of latency until Connect() completes. static void LogHttpConnectedMetrics(const ClientSocketHandle& handle); @@ -277,11 +290,8 @@ class HttpStreamFactoryImpl::Job { bool using_quic_; QuicStreamRequest quic_request_; - // Force spdy for all connections. - bool force_spdy_always_; - - // Force spdy only for SSL connections. - bool force_spdy_over_ssl_; + // True if this job used an existing QUIC session. + bool using_existing_quic_session_; // Force quic for a specific port. int force_quic_port_; @@ -318,11 +328,8 @@ class HttpStreamFactoryImpl::Job { // Only used if |new_spdy_session_| is non-NULL. bool spdy_session_direct_; - // Key used to identify the HttpPipelinedHost for |request_|. - scoped_ptr<HttpPipelinedHost::Key> http_pipelining_key_; - - // True if an existing pipeline can handle this job's request. - bool existing_available_pipeline_; + JobStatus job_status_; + JobStatus other_job_status_; base::WeakPtrFactory<Job> ptr_factory_; diff --git a/chromium/net/http/http_stream_factory_impl_request.cc b/chromium/net/http/http_stream_factory_impl_request.cc index d731d414b71..2cc497f2112 100644 --- a/chromium/net/http/http_stream_factory_impl_request.cc +++ b/chromium/net/http/http_stream_factory_impl_request.cc @@ -48,14 +48,13 @@ HttpStreamFactoryImpl::Request::~Request() { factory_->request_map_.erase(*it); RemoveRequestFromSpdySessionRequestMap(); - RemoveRequestFromHttpPipeliningRequestMap(); STLDeleteElements(&jobs_); } void HttpStreamFactoryImpl::Request::SetSpdySessionKey( const SpdySessionKey& spdy_session_key) { - DCHECK(!spdy_session_key_.get()); + CHECK(!spdy_session_key_.get()); spdy_session_key_.reset(new SpdySessionKey(spdy_session_key)); RequestSet& request_set = factory_->spdy_session_request_map_[spdy_session_key]; @@ -63,18 +62,6 @@ void HttpStreamFactoryImpl::Request::SetSpdySessionKey( request_set.insert(this); } -bool HttpStreamFactoryImpl::Request::SetHttpPipeliningKey( - const HttpPipelinedHost::Key& http_pipelining_key) { - CHECK(!http_pipelining_key_.get()); - http_pipelining_key_.reset(new HttpPipelinedHost::Key(http_pipelining_key)); - bool was_new_key = !ContainsKey(factory_->http_pipelining_request_map_, - http_pipelining_key); - RequestVector& request_vector = - factory_->http_pipelining_request_map_[http_pipelining_key]; - request_vector.push_back(this); - return was_new_key; -} - void HttpStreamFactoryImpl::Request::AttachJob(Job* job) { DCHECK(job); jobs_.insert(job); @@ -131,21 +118,16 @@ void HttpStreamFactoryImpl::Request::OnStreamFailed( int status, const SSLConfig& used_ssl_config) { DCHECK_NE(OK, status); - // |job| should only be NULL if we're being canceled by a late bound - // HttpPipelinedConnection (one that was not created by a job in our |jobs_| - // set). - if (!job) { - DCHECK(!bound_job_.get()); - DCHECK(!jobs_.empty()); - // NOTE(willchan): We do *NOT* call OrphanJobs() here. The reason is because - // we *WANT* to cancel the unnecessary Jobs from other requests if another - // Job completes first. - } else if (!bound_job_.get()) { + DCHECK(job); + if (!bound_job_.get()) { // Hey, we've got other jobs! Maybe one of them will succeed, let's just // ignore this failure. if (jobs_.size() > 1) { jobs_.erase(job); factory_->request_map_.erase(job); + // Notify all the other jobs that this one failed. + for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) + (*it)->MarkOtherJobComplete(*job); delete job; return; } else { @@ -268,34 +250,29 @@ HttpStreamFactoryImpl::Request::RemoveRequestFromSpdySessionRequestMap() { } } -void -HttpStreamFactoryImpl::Request::RemoveRequestFromHttpPipeliningRequestMap() { - if (http_pipelining_key_.get()) { - HttpPipeliningRequestMap& http_pipelining_request_map = - factory_->http_pipelining_request_map_; - DCHECK(ContainsKey(http_pipelining_request_map, *http_pipelining_key_)); - RequestVector& request_vector = - http_pipelining_request_map[*http_pipelining_key_]; - for (RequestVector::iterator it = request_vector.begin(); - it != request_vector.end(); ++it) { - if (*it == this) { - request_vector.erase(it); - break; - } - } - if (request_vector.empty()) - http_pipelining_request_map.erase(*http_pipelining_key_); - http_pipelining_key_.reset(); - } +bool HttpStreamFactoryImpl::Request::HasSpdySessionKey() const { + return spdy_session_key_.get() != NULL; } +// TODO(jgraettinger): Currently, HttpStreamFactoryImpl::Job notifies a +// Request that the session is ready, which in turn notifies it's delegate, +// and then it notifies HttpStreamFactoryImpl so that /other/ requests may +// be woken, but only if the spdy_session is still okay. This is tough to grok. +// Instead, see if Job can notify HttpStreamFactoryImpl only, and have one +// path for notifying any requests waiting for the session (including the +// request which spawned it). void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady( Job* job, + scoped_ptr<HttpStream> stream, const base::WeakPtr<SpdySession>& spdy_session, bool direct) { DCHECK(job); DCHECK(job->using_spdy()); + // Note: |spdy_session| may be NULL. In that case, |delegate_| should still + // receive |stream| so the error propogates up correctly, however there is no + // point in broadcasting |spdy_session| to other requests. + // The first case is the usual case. if (!bound_job_.get()) { OrphanJobsExcept(job); @@ -318,29 +295,24 @@ void HttpStreamFactoryImpl::Request::OnNewSpdySessionReady( // Cache this so we can still use it if the request is deleted. HttpStreamFactoryImpl* factory = factory_; if (factory->for_websockets_) { - DCHECK(websocket_handshake_stream_create_helper_); - bool use_relative_url = direct || url().SchemeIs("wss"); - delegate_->OnWebSocketHandshakeStreamReady( - job->server_ssl_config(), - job->proxy_info(), - websocket_handshake_stream_create_helper_->CreateSpdyStream( - spdy_session, use_relative_url)); + // TODO(ricea): Re-instate this code when WebSockets over SPDY is + // implemented. + NOTREACHED(); } else { - bool use_relative_url = direct || url().SchemeIs("https"); - delegate_->OnStreamReady( - job->server_ssl_config(), - job->proxy_info(), - new SpdyHttpStream(spdy_session, use_relative_url)); + delegate_->OnStreamReady(job->server_ssl_config(), job->proxy_info(), + stream.release()); } // |this| may be deleted after this point. - factory->OnNewSpdySessionReady(spdy_session, - direct, - used_ssl_config, - used_proxy_info, - was_npn_negotiated, - protocol_negotiated, - using_spdy, - net_log); + if (spdy_session && spdy_session->IsAvailable()) { + factory->OnNewSpdySessionReady(spdy_session, + direct, + used_ssl_config, + used_proxy_info, + was_npn_negotiated, + protocol_negotiated, + using_spdy, + net_log); + } } void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) { @@ -356,7 +328,6 @@ void HttpStreamFactoryImpl::Request::OrphanJobsExcept(Job* job) { void HttpStreamFactoryImpl::Request::OrphanJobs() { RemoveRequestFromSpdySessionRequestMap(); - RemoveRequestFromHttpPipeliningRequestMap(); std::set<Job*> tmp; tmp.swap(jobs_); @@ -367,8 +338,7 @@ void HttpStreamFactoryImpl::Request::OrphanJobs() { void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) { // |job| should only be NULL if we're being serviced by a late bound - // SpdySession or HttpPipelinedConnection (one that was not created by a job - // in our |jobs_| set). + // SpdySession (one that was not created by a job in our |jobs_| set). if (!job) { DCHECK(!bound_job_.get()); DCHECK(!jobs_.empty()); @@ -380,13 +350,23 @@ void HttpStreamFactoryImpl::Request::OnJobSucceeded(Job* job) { // they complete? Or do we want to prevent connecting a new SpdySession if // we've already got one available for a different hostname where the ip // address matches up? - } else if (!bound_job_.get()) { + return; + } + if (!bound_job_.get()) { + if (jobs_.size() > 1) + job->ReportJobSuccededForRequest(); + // Notify all the other jobs that this one succeeded. + for (std::set<Job*>::iterator it = jobs_.begin(); it != jobs_.end(); ++it) { + if (*it != job) { + (*it)->MarkOtherJobComplete(*job); + } + } // We may have other jobs in |jobs_|. For example, if we start multiple jobs // for Alternate-Protocol. OrphanJobsExcept(job); - } else { - DCHECK(jobs_.empty()); + return; } + DCHECK(jobs_.empty()); } } // namespace net diff --git a/chromium/net/http/http_stream_factory_impl_request.h b/chromium/net/http/http_stream_factory_impl_request.h index 3d3b2c8bd63..eafc138925f 100644 --- a/chromium/net/http/http_stream_factory_impl_request.h +++ b/chromium/net/http/http_stream_factory_impl_request.h @@ -16,6 +16,7 @@ namespace net { class ClientSocketHandle; +class HttpStream; class SpdySession; class HttpStreamFactoryImpl::Request : public HttpStreamRequest { @@ -36,12 +37,7 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { // for this SpdySessionKey, since we may need to wait for NPN to complete // before knowing if SPDY is available. void SetSpdySessionKey(const SpdySessionKey& spdy_session_key); - - // Called when the Job determines the appropriate |http_pipelining_key| for - // the Request. Registers this Request with the factory, so that if an - // existing pipeline becomes available, this Request can be late bound to it. - // Returns true if this is this key was new to the factory. - bool SetHttpPipeliningKey(const HttpPipelinedHost::Key& http_pipelining_key); + bool HasSpdySessionKey() const; // Attaches |job| to this request. Does not mean that Request will use |job|, // but Request will own |job|. @@ -58,12 +54,9 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { // SpdySessionRequestMap. void RemoveRequestFromSpdySessionRequestMap(); - // If this Request has a |http_pipelining_key_|, remove this session from the - // HttpPipeliningRequestMap. - void RemoveRequestFromHttpPipeliningRequestMap(); - // Called by an attached Job if it sets up a SpdySession. void OnNewSpdySessionReady(Job* job, + scoped_ptr<HttpStream> stream, const base::WeakPtr<SpdySession>& spdy_session, bool direct); @@ -135,7 +128,6 @@ class HttpStreamFactoryImpl::Request : public HttpStreamRequest { scoped_ptr<Job> bound_job_; std::set<HttpStreamFactoryImpl::Job*> jobs_; scoped_ptr<const SpdySessionKey> spdy_session_key_; - scoped_ptr<const HttpPipelinedHost::Key> http_pipelining_key_; bool completed_; bool was_npn_negotiated_; diff --git a/chromium/net/http/http_stream_factory_impl_request_unittest.cc b/chromium/net/http/http_stream_factory_impl_request_unittest.cc index b3b12bfa5a3..14c06ef7107 100644 --- a/chromium/net/http/http_stream_factory_impl_request_unittest.cc +++ b/chromium/net/http/http_stream_factory_impl_request_unittest.cc @@ -20,8 +20,7 @@ INSTANTIATE_TEST_CASE_P( NextProto, HttpStreamFactoryImplRequestTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, - kProtoHTTP2Draft04)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); namespace { diff --git a/chromium/net/http/http_stream_factory_impl_unittest.cc b/chromium/net/http/http_stream_factory_impl_unittest.cc index e3169b65993..e98edbad576 100644 --- a/chromium/net/http/http_stream_factory_impl_unittest.cc +++ b/chromium/net/http/http_stream_factory_impl_unittest.cc @@ -43,20 +43,6 @@ namespace net { namespace { -class UseAlternateProtocolsScopedSetter { - public: - explicit UseAlternateProtocolsScopedSetter(bool use_alternate_protocols) - : use_alternate_protocols_(HttpStreamFactory::use_alternate_protocols()) { - HttpStreamFactory::set_use_alternate_protocols(use_alternate_protocols); - } - ~UseAlternateProtocolsScopedSetter() { - HttpStreamFactory::set_use_alternate_protocols(use_alternate_protocols_); - } - - private: - bool use_alternate_protocols_; -}; - class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase { public: enum StreamType { @@ -87,9 +73,6 @@ class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase { virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE { return ERR_IO_PENDING; } - virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE { - return NULL; - } virtual int ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) OVERRIDE { @@ -420,19 +403,19 @@ CapturePreconnectsSSLSocketPool::CapturePreconnectsSocketPool( CertVerifier* cert_verifier) : SSLClientSocketPool(0, 0, - NULL, + NULL, // ssl_histograms host_resolver, cert_verifier, + NULL, // server_bound_cert_store + NULL, // transport_security_state + NULL, // cert_transparency_verifier + std::string(), // ssl_session_cache_shard + NULL, // deterministic_socket_factory + NULL, // transport_socket_pool NULL, NULL, - NULL, - std::string(), - NULL, - NULL, - NULL, - NULL, - NULL, - NULL), + NULL, // ssl_config_service + NULL), // net_log last_num_streams_(-1) {} class HttpStreamFactoryTest : public ::testing::Test, @@ -443,8 +426,7 @@ INSTANTIATE_TEST_CASE_P( NextProto, HttpStreamFactoryTest, testing::Values(kProtoDeprecatedSPDY2, - kProtoSPDY3, kProtoSPDY31, kProtoSPDY4a2, - kProtoHTTP2Draft04)); + kProtoSPDY3, kProtoSPDY31, kProtoSPDY4)); TEST_P(HttpStreamFactoryTest, PreconnectDirect) { for (size_t i = 0; i < arraysize(kTests); ++i) { @@ -546,7 +528,7 @@ TEST_P(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) { // Put a SpdySession in the pool. HostPortPair host_port_pair("www.google.com", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), - kPrivacyModeDisabled); + PRIVACY_MODE_DISABLED); ignore_result(CreateFakeSpdySession(session->spdy_session_pool(), key)); CapturePreconnectsTransportSocketPool* transport_conn_pool = @@ -657,13 +639,13 @@ TEST_P(HttpStreamFactoryTest, PrivacyModeDisablesChannelId) { // Set an existing SpdySession in the pool. HostPortPair host_port_pair("www.google.com", 443); SpdySessionKey key(host_port_pair, ProxyServer::Direct(), - kPrivacyModeEnabled); + PRIVACY_MODE_ENABLED); HttpRequestInfo request_info; request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; - request_info.privacy_mode = kPrivacyModeDisabled; + request_info.privacy_mode = PRIVACY_MODE_DISABLED; SSLConfig ssl_config; StreamRequestWaiter waiter; @@ -716,7 +698,7 @@ TEST_P(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) { request_info.method = "GET"; request_info.url = GURL("https://www.google.com"); request_info.load_flags = 0; - request_info.privacy_mode = kPrivacyModeDisabled; + request_info.privacy_mode = PRIVACY_MODE_DISABLED; SSLConfig ssl_config; StreamRequestWaiter waiter; @@ -737,7 +719,7 @@ TEST_P(HttpStreamFactoryTest, PrivacyModeUsesDifferentSocketPoolGroup) { EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 1); - request_info.privacy_mode = kPrivacyModeEnabled; + request_info.privacy_mode = PRIVACY_MODE_ENABLED; scoped_ptr<HttpStreamRequest> request3( session->http_stream_factory()->RequestStream( request_info, DEFAULT_PRIORITY, ssl_config, ssl_config, @@ -1127,7 +1109,64 @@ TEST_P(HttpStreamFactoryTest, RequestSpdyHttpStream) { EXPECT_TRUE(waiter.used_proxy_info().is_direct()); } -TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStream) { +// TODO(ricea): This test can be removed once the new WebSocket stack supports +// SPDY. Currently, even if we connect to a SPDY-supporting server, we need to +// use plain SSL. +TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStreamButGetSSL) { + SpdySessionDependencies session_deps(GetParam(), + ProxyService::CreateDirect()); + + MockRead mock_read(SYNCHRONOUS, ERR_IO_PENDING); + StaticSocketDataProvider socket_data(&mock_read, 1, NULL, 0); + socket_data.set_connect_data(MockConnect(ASYNC, OK)); + session_deps.socket_factory->AddSocketDataProvider(&socket_data); + + SSLSocketDataProvider ssl_socket_data(ASYNC, OK); + session_deps.socket_factory->AddSSLSocketDataProvider(&ssl_socket_data); + + HostPortPair host_port_pair("www.google.com", 80); + scoped_refptr<HttpNetworkSession> + session(SpdySessionDependencies::SpdyCreateSession(&session_deps)); + + // Now request a stream. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("wss://www.google.com"); + request_info.load_flags = 0; + + SSLConfig ssl_config; + StreamRequestWaiter waiter1; + WebSocketStreamCreateHelper create_helper; + scoped_ptr<HttpStreamRequest> request1( + session->http_stream_factory_for_websocket() + ->RequestWebSocketHandshakeStream(request_info, + DEFAULT_PRIORITY, + ssl_config, + ssl_config, + &waiter1, + &create_helper, + BoundNetLog())); + waiter1.WaitForStream(); + EXPECT_TRUE(waiter1.stream_done()); + ASSERT_TRUE(NULL != waiter1.websocket_stream()); + EXPECT_EQ(MockWebSocketHandshakeStream::kStreamTypeBasic, + waiter1.websocket_stream()->type()); + EXPECT_TRUE(NULL == waiter1.stream()); + + EXPECT_EQ(0, GetSocketPoolGroupCount( + session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(0, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetTransportSocketPool( + HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); + EXPECT_EQ(1, GetSocketPoolGroupCount( + session->GetSSLSocketPool(HttpNetworkSession::WEBSOCKET_SOCKET_POOL))); + EXPECT_TRUE(waiter1.used_proxy_info().is_direct()); +} + +// TODO(ricea): Re-enable once WebSocket-over-SPDY is implemented. +TEST_P(HttpStreamFactoryTest, DISABLED_RequestWebSocketSpdyHandshakeStream) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); @@ -1203,10 +1242,11 @@ TEST_P(HttpStreamFactoryTest, RequestWebSocketSpdyHandshakeStream) { EXPECT_TRUE(waiter1.used_proxy_info().is_direct()); } -TEST_P(HttpStreamFactoryTest, OrphanedWebSocketStream) { - UseAlternateProtocolsScopedSetter use_alternate_protocols(true); +// TODO(ricea): Re-enable once WebSocket over SPDY is implemented. +TEST_P(HttpStreamFactoryTest, DISABLED_OrphanedWebSocketStream) { SpdySessionDependencies session_deps(GetParam(), ProxyService::CreateDirect()); + session_deps.use_alternate_protocols = true; MockRead mock_read(ASYNC, OK); DeterministicSocketData socket_data(&mock_read, 1, NULL, 0); diff --git a/chromium/net/http/http_stream_parser.cc b/chromium/net/http/http_stream_parser.cc index 0821c848652..86b188b6a1f 100644 --- a/chromium/net/http/http_stream_parser.cc +++ b/chromium/net/http/http_stream_parser.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/compiler_specific.h" +#include "base/logging.h" #include "base/strings/string_util.h" #include "base/values.h" #include "net/base/io_buffer.h" @@ -19,12 +20,14 @@ #include "net/socket/client_socket_handle.h" #include "net/socket/ssl_client_socket.h" +namespace net { + namespace { const size_t kMaxMergedHeaderAndBodySize = 1400; const size_t kRequestBodyBufferSize = 1 << 14; // 16KB -std::string GetResponseHeaderLines(const net::HttpResponseHeaders& headers) { +std::string GetResponseHeaderLines(const HttpResponseHeaders& headers) { std::string raw_headers = headers.raw_headers(); const char* null_separated_headers = raw_headers.c_str(); const char* header_line = null_separated_headers; @@ -39,9 +42,8 @@ std::string GetResponseHeaderLines(const net::HttpResponseHeaders& headers) { // Return true if |headers| contain multiple |field_name| fields with different // values. -bool HeadersContainMultipleCopiesOfField( - const net::HttpResponseHeaders& headers, - const std::string& field_name) { +bool HeadersContainMultipleCopiesOfField(const HttpResponseHeaders& headers, + const std::string& field_name) { void* it = NULL; std::string field_value; if (!headers.EnumerateHeader(&it, field_name, &field_value)) @@ -56,11 +58,10 @@ bool HeadersContainMultipleCopiesOfField( return false; } -base::Value* NetLogSendRequestBodyCallback( - int length, - bool is_chunked, - bool did_merge, - net::NetLog::LogLevel /* log_level */) { +base::Value* NetLogSendRequestBodyCallback(int length, + bool is_chunked, + bool did_merge, + NetLog::LogLevel /* log_level */) { base::DictionaryValue* dict = new base::DictionaryValue(); dict->SetInteger("length", length); dict->SetBoolean("is_chunked", is_chunked); @@ -68,9 +69,14 @@ base::Value* NetLogSendRequestBodyCallback( return dict; } -} // namespace +// Returns true if |error_code| is an error for which we give the server a +// chance to send a body containing error information, if the error was received +// while trying to upload a request body. +bool ShouldTryReadingOnUploadError(int error_code) { + return (error_code == ERR_CONNECTION_RESET); +} -namespace net { +} // namespace // Similar to DrainableIOBuffer(), but this version comes with its own // storage. The motivation is to avoid repeated allocations of @@ -101,7 +107,7 @@ namespace net { // // size() == BytesRemaining() == BytesConsumed() == 0. // // data() points to the beginning of the buffer. // -class HttpStreamParser::SeekableIOBuffer : public net::IOBuffer { +class HttpStreamParser::SeekableIOBuffer : public IOBuffer { public: explicit SeekableIOBuffer(int capacity) : IOBuffer(capacity), @@ -176,6 +182,7 @@ HttpStreamParser::HttpStreamParser(ClientSocketHandle* connection, : io_state_(STATE_NONE), request_(request), request_headers_(NULL), + request_headers_length_(0), read_buf_(read_buffer), read_buf_unused_offset_(0), response_header_start_offset_(-1), @@ -187,6 +194,7 @@ HttpStreamParser::HttpStreamParser(ClientSocketHandle* connection, connection_(connection), net_log_(net_log), sent_last_chunk_(false), + upload_error_(OK), weak_ptr_factory_(this) { io_callback_ = base::Bind(&HttpStreamParser::OnIOComplete, weak_ptr_factory_.GetWeakPtr()); @@ -223,6 +231,7 @@ int HttpStreamParser::SendRequest(const std::string& request_line, response_->socket_address = HostPortPair::FromIPEndPoint(ip_endpoint); std::string request = request_line + headers.ToString(); + request_headers_length_ = request.size(); if (request_->upload_data_stream != NULL) { request_body_send_buf_ = new SeekableIOBuffer(kRequestBodyBufferSize); @@ -237,13 +246,14 @@ int HttpStreamParser::SendRequest(const std::string& request_line, } } - io_state_ = STATE_SENDING_HEADERS; + io_state_ = STATE_SEND_HEADERS; // If we have a small request body, then we'll merge with the headers into a // single write. bool did_merge = false; if (ShouldMergeRequestHeadersAndBody(request, request_->upload_data_stream)) { - size_t merged_size = request.size() + request_->upload_data_stream->size(); + size_t merged_size = + request_headers_length_ + request_->upload_data_stream->size(); scoped_refptr<IOBuffer> merged_request_headers_and_body( new IOBuffer(merged_size)); // We'll repurpose |request_headers_| to store the merged headers and @@ -251,8 +261,8 @@ int HttpStreamParser::SendRequest(const std::string& request_line, request_headers_ = new DrainableIOBuffer( merged_request_headers_and_body.get(), merged_size); - memcpy(request_headers_->data(), request.data(), request.size()); - request_headers_->DidConsume(request.size()); + memcpy(request_headers_->data(), request.data(), request_headers_length_); + request_headers_->DidConsume(request_headers_length_); size_t todo = request_->upload_data_stream->size(); while (todo) { @@ -291,7 +301,7 @@ int HttpStreamParser::SendRequest(const std::string& request_line, } int HttpStreamParser::ReadResponseHeaders(const CompletionCallback& callback) { - DCHECK(io_state_ == STATE_REQUEST_SENT || io_state_ == STATE_DONE); + DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE); DCHECK(callback_.is_null()); DCHECK(!callback.is_null()); DCHECK_EQ(0, read_buf_unused_offset_); @@ -327,7 +337,7 @@ void HttpStreamParser::Close(bool not_reusable) { int HttpStreamParser::ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) { - DCHECK(io_state_ == STATE_BODY_PENDING || io_state_ == STATE_DONE); + DCHECK(io_state_ == STATE_NONE || io_state_ == STATE_DONE); DCHECK(callback_.is_null()); DCHECK(!callback.is_null()); DCHECK_LE(buf_len, kMaxBufSize); @@ -359,30 +369,33 @@ void HttpStreamParser::OnIOComplete(int result) { } int HttpStreamParser::DoLoop(int result) { - bool can_do_more = true; do { - switch (io_state_) { - case STATE_SENDING_HEADERS: - if (result < 0) - can_do_more = false; - else - result = DoSendHeaders(result); + DCHECK_NE(ERR_IO_PENDING, result); + DCHECK_NE(STATE_DONE, io_state_); + DCHECK_NE(STATE_NONE, io_state_); + State state = io_state_; + io_state_ = STATE_NONE; + switch (state) { + case STATE_SEND_HEADERS: + DCHECK_EQ(OK, result); + result = DoSendHeaders(); break; - case STATE_SENDING_BODY: - if (result < 0) - can_do_more = false; - else - result = DoSendBody(result); + case STATE_SEND_HEADERS_COMPLETE: + result = DoSendHeadersComplete(result); break; - case STATE_SEND_REQUEST_READING_BODY: - result = DoSendRequestReadingBody(result); + case STATE_SEND_BODY: + DCHECK_EQ(OK, result); + result = DoSendBody(); break; - case STATE_REQUEST_SENT: - DCHECK(result != ERR_IO_PENDING); - can_do_more = false; + case STATE_SEND_BODY_COMPLETE: + result = DoSendBodyComplete(result); + break; + case STATE_SEND_REQUEST_READ_BODY_COMPLETE: + result = DoSendRequestReadBodyComplete(result); break; case STATE_READ_HEADERS: net_log_.BeginEvent(NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS); + DCHECK_GE(result, 0); result = DoReadHeaders(); break; case STATE_READ_HEADERS_COMPLETE: @@ -390,68 +403,79 @@ int HttpStreamParser::DoLoop(int result) { net_log_.EndEventWithNetErrorCode( NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS, result); break; - case STATE_BODY_PENDING: - DCHECK(result != ERR_IO_PENDING); - can_do_more = false; - break; case STATE_READ_BODY: + DCHECK_GE(result, 0); result = DoReadBody(); - // DoReadBodyComplete handles error conditions. break; case STATE_READ_BODY_COMPLETE: result = DoReadBodyComplete(result); break; - case STATE_DONE: - DCHECK(result != ERR_IO_PENDING); - can_do_more = false; - break; default: NOTREACHED(); - can_do_more = false; break; } - } while (result != ERR_IO_PENDING && can_do_more); + } while (result != ERR_IO_PENDING && + (io_state_ != STATE_DONE && io_state_ != STATE_NONE)); return result; } -int HttpStreamParser::DoSendHeaders(int result) { - request_headers_->DidConsume(result); +int HttpStreamParser::DoSendHeaders() { int bytes_remaining = request_headers_->BytesRemaining(); - if (bytes_remaining > 0) { - // Record our best estimate of the 'request time' as the time when we send - // out the first bytes of the request headers. - if (bytes_remaining == request_headers_->size()) { - response_->request_time = base::Time::Now(); + DCHECK_GT(bytes_remaining, 0); + + // Record our best estimate of the 'request time' as the time when we send + // out the first bytes of the request headers. + if (bytes_remaining == request_headers_->size()) + response_->request_time = base::Time::Now(); + + io_state_ = STATE_SEND_HEADERS_COMPLETE; + return connection_->socket() + ->Write(request_headers_.get(), bytes_remaining, io_callback_); +} + +int HttpStreamParser::DoSendHeadersComplete(int result) { + if (result < 0) { + // In the unlikely case that the headers and body were merged, all the + // the headers were sent, but not all of the body way, and |result| is + // an error that this should try reading after, stash the error for now and + // act like the request was successfully sent. + if (request_headers_->BytesConsumed() >= request_headers_length_ && + ShouldTryReadingOnUploadError(result)) { + upload_error_ = result; + return OK; } - result = connection_->socket() - ->Write(request_headers_.get(), bytes_remaining, io_callback_); - } else if (request_->upload_data_stream != NULL && - (request_->upload_data_stream->is_chunked() || - // !IsEOF() indicates that the body wasn't merged. - (request_->upload_data_stream->size() > 0 && - !request_->upload_data_stream->IsEOF()))) { + return result; + } + + request_headers_->DidConsume(result); + if (request_headers_->BytesRemaining() > 0) { + io_state_ = STATE_SEND_HEADERS; + return OK; + } + + if (request_->upload_data_stream != NULL && + (request_->upload_data_stream->is_chunked() || + // !IsEOF() indicates that the body wasn't merged. + (request_->upload_data_stream->size() > 0 && + !request_->upload_data_stream->IsEOF()))) { net_log_.AddEvent( NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_BODY, base::Bind(&NetLogSendRequestBodyCallback, request_->upload_data_stream->size(), request_->upload_data_stream->is_chunked(), false /* not merged */)); - io_state_ = STATE_SENDING_BODY; - result = OK; - } else { - io_state_ = STATE_REQUEST_SENT; + io_state_ = STATE_SEND_BODY; + return OK; } - return result; -} -int HttpStreamParser::DoSendBody(int result) { - // |result| is the number of bytes sent from the last call to - // DoSendBody(), or 0 (i.e. OK). + // Finished sending the request. + return OK; +} - // Send the remaining data in the request body buffer. - request_body_send_buf_->DidConsume(result); +int HttpStreamParser::DoSendBody() { if (request_body_send_buf_->BytesRemaining() > 0) { + io_state_ = STATE_SEND_BODY_COMPLETE; return connection_->socket() ->Write(request_body_send_buf_.get(), request_body_send_buf_->BytesRemaining(), @@ -459,18 +483,35 @@ int HttpStreamParser::DoSendBody(int result) { } if (request_->upload_data_stream->is_chunked() && sent_last_chunk_) { - io_state_ = STATE_REQUEST_SENT; + // Finished sending the request. return OK; } request_body_read_buf_->Clear(); - io_state_ = STATE_SEND_REQUEST_READING_BODY; + io_state_ = STATE_SEND_REQUEST_READ_BODY_COMPLETE; return request_->upload_data_stream->Read(request_body_read_buf_.get(), request_body_read_buf_->capacity(), io_callback_); } -int HttpStreamParser::DoSendRequestReadingBody(int result) { +int HttpStreamParser::DoSendBodyComplete(int result) { + if (result < 0) { + // If |result| is an error that this should try reading after, stash the + // error for now and act like the request was successfully sent. + if (ShouldTryReadingOnUploadError(result)) { + upload_error_ = result; + return OK; + } + return result; + } + + request_body_send_buf_->DidConsume(result); + + io_state_ = STATE_SEND_BODY; + return OK; +} + +int HttpStreamParser::DoSendRequestReadBodyComplete(int result) { // |result| is the result of read from the request body from the last call to // DoSendBody(). DCHECK_GE(result, 0); // There won't be errors. @@ -494,11 +535,11 @@ int HttpStreamParser::DoSendRequestReadingBody(int result) { // chunked. (i.e. No need to send the terminal chunk.) DCHECK(request_->upload_data_stream->IsEOF()); DCHECK(!request_->upload_data_stream->is_chunked()); - io_state_ = STATE_REQUEST_SENT; + // Finished sending the request. } else if (result > 0) { request_body_send_buf_->DidAppend(result); result = 0; - io_state_ = STATE_SENDING_BODY; + io_state_ = STATE_SEND_BODY; } return result; } @@ -519,110 +560,50 @@ int HttpStreamParser::DoReadHeaders() { } int HttpStreamParser::DoReadHeadersComplete(int result) { - DCHECK_EQ(0, read_buf_unused_offset_); + result = HandleReadHeaderResult(result); - if (result == 0) - result = ERR_CONNECTION_CLOSED; + // TODO(mmenke): The code below is ugly and hacky. A much better and more + // flexible long term solution would be to separate out the read and write + // loops, though this would involve significant changes, both here and + // elsewhere (WebSockets, for instance). - if (result < 0 && result != ERR_CONNECTION_CLOSED) { - io_state_ = STATE_DONE; + // If still reading the headers, or there was no error uploading the request + // body, just return the result. + if (io_state_ == STATE_READ_HEADERS || upload_error_ == OK) return result; - } - // If we've used the connection before, then we know it is not a HTTP/0.9 - // response and return ERR_CONNECTION_CLOSED. - if (result == ERR_CONNECTION_CLOSED && read_buf_->offset() == 0 && - connection_->is_reused()) { + + // If the result is ERR_IO_PENDING, |io_state_| should be STATE_READ_HEADERS. + DCHECK_NE(ERR_IO_PENDING, result); + + // On errors, use the original error received when sending the request. + // The main cases where these are different is when there's a header-related + // error code, or when there's an ERR_CONNECTION_CLOSED, which can result in + // special handling of partial responses and HTTP/0.9 responses. + if (result < 0) { + // Nothing else to do. In the HTTP/0.9 or only partial headers received + // cases, can normally go to other states after an error reading headers. io_state_ = STATE_DONE; - return result; + // Don't let caller see the headers. + response_->headers = NULL; + return upload_error_; } - // Record our best estimate of the 'response time' as the time when we read - // the first bytes of the response headers. - if (read_buf_->offset() == 0 && result != ERR_CONNECTION_CLOSED) - response_->response_time = base::Time::Now(); - - if (result == ERR_CONNECTION_CLOSED) { - // The connection closed before we detected the end of the headers. - if (read_buf_->offset() == 0) { - // The connection was closed before any data was sent. Likely an error - // rather than empty HTTP/0.9 response. - io_state_ = STATE_DONE; - return ERR_EMPTY_RESPONSE; - } else if (request_->url.SchemeIsSecure()) { - // The connection was closed in the middle of the headers. For HTTPS we - // don't parse partial headers. Return a different error code so that we - // know that we shouldn't attempt to retry the request. - io_state_ = STATE_DONE; - return ERR_RESPONSE_HEADERS_TRUNCATED; - } - // Parse things as well as we can and let the caller decide what to do. - int end_offset; - if (response_header_start_offset_ >= 0) { - io_state_ = STATE_READ_BODY_COMPLETE; - end_offset = read_buf_->offset(); - } else { - io_state_ = STATE_BODY_PENDING; - end_offset = 0; - } - int rv = DoParseResponseHeaders(end_offset); - if (rv < 0) - return rv; + // Skip over 1xx responses as usual, and allow 4xx/5xx error responses to + // override the error received while uploading the body. + int response_code_class = response_->headers->response_code() / 100; + if (response_code_class == 1 || response_code_class == 4 || + response_code_class == 5) { return result; } - read_buf_->set_offset(read_buf_->offset() + result); - DCHECK_LE(read_buf_->offset(), read_buf_->capacity()); - DCHECK_GE(result, 0); - - int end_of_header_offset = ParseResponseHeaders(); - - // Note: -1 is special, it indicates we haven't found the end of headers. - // Anything less than -1 is a net::Error, so we bail out. - if (end_of_header_offset < -1) - return end_of_header_offset; - - if (end_of_header_offset == -1) { - io_state_ = STATE_READ_HEADERS; - // Prevent growing the headers buffer indefinitely. - if (read_buf_->offset() >= kMaxHeaderBufSize) { - io_state_ = STATE_DONE; - return ERR_RESPONSE_HEADERS_TOO_BIG; - } - } else { - CalculateResponseBodySize(); - // If the body is zero length, the caller may not call ReadResponseBody, - // which is where any extra data is copied to read_buf_, so we move the - // data here. - if (response_body_length_ == 0) { - int extra_bytes = read_buf_->offset() - end_of_header_offset; - if (extra_bytes) { - CHECK_GT(extra_bytes, 0); - memmove(read_buf_->StartOfBuffer(), - read_buf_->StartOfBuffer() + end_of_header_offset, - extra_bytes); - } - read_buf_->SetCapacity(extra_bytes); - if (response_->headers->response_code() / 100 == 1) { - // After processing a 1xx response, the caller will ask for the next - // header, so reset state to support that. We don't completely ignore a - // 1xx response because it cannot be returned in reply to a CONNECT - // request so we return OK here, which lets the caller inspect the - // response and reject it in the event that we're setting up a CONNECT - // tunnel. - response_header_start_offset_ = -1; - response_body_length_ = -1; - io_state_ = STATE_REQUEST_SENT; - } else { - io_state_ = STATE_DONE; - } - return OK; - } + // All other status codes are not allowed after an error during upload, to + // make sure the consumer has some indication there was an error. - // Note where the headers stop. - read_buf_unused_offset_ = end_of_header_offset; - io_state_ = STATE_BODY_PENDING; - } - return result; + // Nothing else to do. + io_state_ = STATE_DONE; + // Don't let caller see the headers. + response_->headers = NULL; + return upload_error_; } int HttpStreamParser::DoReadBody() { @@ -751,7 +732,7 @@ int HttpStreamParser::DoReadBodyComplete(int result) { } read_buf_unused_offset_ = 0; } else { - io_state_ = STATE_BODY_PENDING; + // Now waiting for more of the body to be read. user_read_buf_ = NULL; user_read_buf_len_ = 0; } @@ -759,6 +740,113 @@ int HttpStreamParser::DoReadBodyComplete(int result) { return result; } +int HttpStreamParser::HandleReadHeaderResult(int result) { + DCHECK_EQ(0, read_buf_unused_offset_); + + if (result == 0) + result = ERR_CONNECTION_CLOSED; + + if (result < 0 && result != ERR_CONNECTION_CLOSED) { + io_state_ = STATE_DONE; + return result; + } + // If we've used the connection before, then we know it is not a HTTP/0.9 + // response and return ERR_CONNECTION_CLOSED. + if (result == ERR_CONNECTION_CLOSED && read_buf_->offset() == 0 && + connection_->is_reused()) { + io_state_ = STATE_DONE; + return result; + } + + // Record our best estimate of the 'response time' as the time when we read + // the first bytes of the response headers. + if (read_buf_->offset() == 0 && result != ERR_CONNECTION_CLOSED) + response_->response_time = base::Time::Now(); + + if (result == ERR_CONNECTION_CLOSED) { + // The connection closed before we detected the end of the headers. + if (read_buf_->offset() == 0) { + // The connection was closed before any data was sent. Likely an error + // rather than empty HTTP/0.9 response. + io_state_ = STATE_DONE; + return ERR_EMPTY_RESPONSE; + } else if (request_->url.SchemeIsSecure()) { + // The connection was closed in the middle of the headers. For HTTPS we + // don't parse partial headers. Return a different error code so that we + // know that we shouldn't attempt to retry the request. + io_state_ = STATE_DONE; + return ERR_RESPONSE_HEADERS_TRUNCATED; + } + // Parse things as well as we can and let the caller decide what to do. + int end_offset; + if (response_header_start_offset_ >= 0) { + io_state_ = STATE_READ_BODY_COMPLETE; + end_offset = read_buf_->offset(); + } else { + // Now waiting for the body to be read. + end_offset = 0; + } + int rv = DoParseResponseHeaders(end_offset); + if (rv < 0) + return rv; + return result; + } + + read_buf_->set_offset(read_buf_->offset() + result); + DCHECK_LE(read_buf_->offset(), read_buf_->capacity()); + DCHECK_GE(result, 0); + + int end_of_header_offset = ParseResponseHeaders(); + + // Note: -1 is special, it indicates we haven't found the end of headers. + // Anything less than -1 is a net::Error, so we bail out. + if (end_of_header_offset < -1) + return end_of_header_offset; + + if (end_of_header_offset == -1) { + io_state_ = STATE_READ_HEADERS; + // Prevent growing the headers buffer indefinitely. + if (read_buf_->offset() >= kMaxHeaderBufSize) { + io_state_ = STATE_DONE; + return ERR_RESPONSE_HEADERS_TOO_BIG; + } + } else { + CalculateResponseBodySize(); + // If the body is zero length, the caller may not call ReadResponseBody, + // which is where any extra data is copied to read_buf_, so we move the + // data here. + if (response_body_length_ == 0) { + int extra_bytes = read_buf_->offset() - end_of_header_offset; + if (extra_bytes) { + CHECK_GT(extra_bytes, 0); + memmove(read_buf_->StartOfBuffer(), + read_buf_->StartOfBuffer() + end_of_header_offset, + extra_bytes); + } + read_buf_->SetCapacity(extra_bytes); + if (response_->headers->response_code() / 100 == 1) { + // After processing a 1xx response, the caller will ask for the next + // header, so reset state to support that. We don't completely ignore a + // 1xx response because it cannot be returned in reply to a CONNECT + // request so we return OK here, which lets the caller inspect the + // response and reject it in the event that we're setting up a CONNECT + // tunnel. + response_header_start_offset_ = -1; + response_body_length_ = -1; + // Now waiting for the second set of headers to be read. + } else { + io_state_ = STATE_DONE; + } + return OK; + } + + // Note where the headers stop. + read_buf_unused_offset_ = end_of_header_offset; + // Now waiting for the body to be read. + } + return result; +} + int HttpStreamParser::ParseResponseHeaders() { int end_offset = -1; DCHECK_EQ(0, read_buf_unused_offset_); @@ -876,10 +964,6 @@ UploadProgress HttpStreamParser::GetUploadProgress() const { request_->upload_data_stream->size()); } -HttpResponseInfo* HttpStreamParser::GetResponseInfo() { - return response_; -} - bool HttpStreamParser::IsResponseBodyComplete() const { if (chunked_decoder_.get()) return chunked_decoder_->reached_eof(); @@ -904,7 +988,7 @@ bool HttpStreamParser::IsConnectionReused() const { } void HttpStreamParser::SetConnectionReused() { - connection_->set_is_reused(true); + connection_->set_reuse_type(ClientSocketHandle::REUSED_IDLE); } bool HttpStreamParser::IsConnectionReusable() const { diff --git a/chromium/net/http/http_stream_parser.h b/chromium/net/http/http_stream_parser.h index e3690322bbd..a8d0447a086 100644 --- a/chromium/net/http/http_stream_parser.h +++ b/chromium/net/http/http_stream_parser.h @@ -63,8 +63,6 @@ class NET_EXPORT_PRIVATE HttpStreamParser { // zero, but position will not be. UploadProgress GetUploadProgress() const; - HttpResponseInfo* GetResponseInfo(); - bool IsResponseBodyComplete() const; bool CanFindEndOfResponse() const; @@ -110,17 +108,16 @@ class NET_EXPORT_PRIVATE HttpStreamParser { // FOO_COMPLETE states implement the second half of potentially asynchronous // operations and don't necessarily mean that FOO is complete. enum State { + // STATE_NONE indicates that this is waiting on an external call before + // continuing. STATE_NONE, - STATE_SENDING_HEADERS, - // If the request comes with a body, either of the following two - // states will be executed, depending on whether the body is chunked - // or not. - STATE_SENDING_BODY, - STATE_SEND_REQUEST_READING_BODY, - STATE_REQUEST_SENT, + STATE_SEND_HEADERS, + STATE_SEND_HEADERS_COMPLETE, + STATE_SEND_BODY, + STATE_SEND_BODY_COMPLETE, + STATE_SEND_REQUEST_READ_BODY_COMPLETE, STATE_READ_HEADERS, STATE_READ_HEADERS_COMPLETE, - STATE_BODY_PENDING, STATE_READ_BODY, STATE_READ_BODY_COMPLETE, STATE_DONE @@ -146,14 +143,19 @@ class NET_EXPORT_PRIVATE HttpStreamParser { int DoLoop(int result); // The implementations of each state of the state machine. - int DoSendHeaders(int result); - int DoSendBody(int result); - int DoSendRequestReadingBody(int result); + int DoSendHeaders(); + int DoSendHeadersComplete(int result); + int DoSendBody(); + int DoSendBodyComplete(int result); + int DoSendRequestReadBodyComplete(int result); int DoReadHeaders(); int DoReadHeadersComplete(int result); int DoReadBody(); int DoReadBodyComplete(int result); + // This handles most of the logic for DoReadHeadersComplete. + int HandleReadHeaderResult(int result); + // Examines |read_buf_| to find the start and end of the headers. If they are // found, parse them with DoParseResponseHeaders(). Return the offset for // the end of the headers, or -1 if the complete headers were not found, or @@ -167,15 +169,19 @@ class NET_EXPORT_PRIVATE HttpStreamParser { // Examine the parsed headers to try to determine the response body size. void CalculateResponseBodySize(); - // Current state of the request. + // Next state of the request, when the current one completes. State io_state_; // The request to send. const HttpRequestInfo* request_; - // The request header data. + // The request header data. May include a merged request body. scoped_refptr<DrainableIOBuffer> request_headers_; + // Size of just the request headers. May be less than the length of + // |request_headers_| if the body was merged with the headers. + int request_headers_length_; + // Temporary buffer for reading. scoped_refptr<GrowableIOBuffer> read_buf_; @@ -192,7 +198,10 @@ class NET_EXPORT_PRIVATE HttpStreamParser { // value may be bigger than final. int64 received_bytes_; - // The parsed response headers. Owned by the caller. + // The parsed response headers. Owned by the caller of SendRequest. This + // cannot be safely accessed after reading the final set of headers, as the + // caller of SendRequest may have been destroyed - this happens in the case an + // HttpResponseBodyDrainer is used. HttpResponseInfo* response_; // Indicates the content length. If this value is less than zero @@ -235,6 +244,9 @@ class NET_EXPORT_PRIVATE HttpStreamParser { scoped_refptr<SeekableIOBuffer> request_body_send_buf_; bool sent_last_chunk_; + // Error received when uploading the body, if any. + int upload_error_; + base::WeakPtrFactory<HttpStreamParser> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(HttpStreamParser); diff --git a/chromium/net/http/http_stream_parser_unittest.cc b/chromium/net/http/http_stream_parser_unittest.cc index dcaf1f3e9c3..33910395cbc 100644 --- a/chromium/net/http/http_stream_parser_unittest.cc +++ b/chromium/net/http/http_stream_parser_unittest.cc @@ -804,6 +804,63 @@ TEST(HttpStreamParser, ReceivedBytesIncludesContinueHeader) { EXPECT_EQ(response_size, get_runner.parser()->received_bytes()); } +// Test that an HttpStreamParser can be read from after it's received headers +// and data structures owned by its owner have been deleted. This happens +// when a ResponseBodyDrainer is used. +TEST(HttpStreamParser, ReadAfterUnownedObjectsDestroyed) { + MockWrite writes[] = { + MockWrite(SYNCHRONOUS, 0, + "GET /foo.html HTTP/1.1\r\n\r\n"), + MockWrite(SYNCHRONOUS, 1, "1"), + }; + + const int kBodySize = 1; + MockRead reads[] = { + MockRead(SYNCHRONOUS, 5, "HTTP/1.1 200 OK\r\n"), + MockRead(SYNCHRONOUS, 6, "Content-Length: 1\r\n\r\n"), + MockRead(SYNCHRONOUS, 6, "Connection: Keep-Alive\r\n\r\n"), + MockRead(SYNCHRONOUS, 7, "1"), + MockRead(SYNCHRONOUS, 0, 8), // EOF + }; + + StaticSocketDataProvider data(reads, arraysize(reads), writes, + arraysize(writes)); + data.set_connect_data(MockConnect(SYNCHRONOUS, OK)); + + scoped_ptr<MockTCPClientSocket> transport( + new MockTCPClientSocket(AddressList(), NULL, &data)); + + TestCompletionCallback callback; + ASSERT_EQ(OK, transport->Connect(callback.callback())); + + scoped_ptr<ClientSocketHandle> socket_handle(new ClientSocketHandle); + socket_handle->SetSocket(transport.PassAs<StreamSocket>()); + + scoped_ptr<HttpRequestInfo> request_info(new HttpRequestInfo()); + request_info->method = "GET"; + request_info->url = GURL("http://somewhere/foo.html"); + + scoped_refptr<GrowableIOBuffer> read_buffer(new GrowableIOBuffer); + HttpStreamParser parser(socket_handle.get(), request_info.get(), + read_buffer.get(), BoundNetLog()); + + scoped_ptr<HttpRequestHeaders> request_headers(new HttpRequestHeaders()); + scoped_ptr<HttpResponseInfo> response_info(new HttpResponseInfo()); + ASSERT_EQ(OK, parser.SendRequest("GET /foo.html HTTP/1.1\r\n", + *request_headers, response_info.get(), callback.callback())); + ASSERT_EQ(OK, parser.ReadResponseHeaders(callback.callback())); + + // If the object that owns the HttpStreamParser is deleted, it takes the + // objects passed to the HttpStreamParser with it. + request_info.reset(); + request_headers.reset(); + response_info.reset(); + + scoped_refptr<IOBuffer> body_buffer(new IOBuffer(kBodySize)); + ASSERT_EQ(kBodySize, parser.ReadResponseBody( + body_buffer.get(), kBodySize, callback.callback())); +} + } // namespace } // namespace net diff --git a/chromium/net/http/http_transaction.h b/chromium/net/http/http_transaction.h index 849d03af50c..ed1b20dac2f 100644 --- a/chromium/net/http/http_transaction.h +++ b/chromium/net/http/http_transaction.h @@ -21,6 +21,7 @@ struct HttpRequestInfo; class HttpResponseInfo; class IOBuffer; struct LoadTimingInfo; +class QuicServerInfo; class X509Certificate; // Represents a single HTTP transaction (i.e., a single request/response pair). @@ -28,6 +29,10 @@ class X509Certificate; // answered. Cookies are assumed to be managed by the caller. class NET_EXPORT_PRIVATE HttpTransaction { public: + // If |*defer| is set to true, the transaction will wait until + // ResumeNetworkStart is called before establishing a connection. + typedef base::Callback<void(bool* defer)> BeforeNetworkStartCallback; + // Stops any pending IO and destroys the transaction object. virtual ~HttpTransaction() {} @@ -106,6 +111,9 @@ class NET_EXPORT_PRIVATE HttpTransaction { // otherwise, returns false and does not modify headers. virtual bool GetFullRequestHeaders(HttpRequestHeaders* headers) const = 0; + // Get the number of bytes received from network. + virtual int64 GetTotalReceivedBytes() const = 0; + // Called to tell the transaction that we have successfully reached the end // of the stream. This is equivalent to performing an extra Read() at the end // that should return 0 bytes. This method should not be called if the @@ -126,6 +134,10 @@ class NET_EXPORT_PRIVATE HttpTransaction { // zero will be returned. This does not include the request headers. virtual UploadProgress GetUploadProgress() const = 0; + // SetQuicServerInfo sets a object which reads and writes public information + // about a QUIC server. + virtual void SetQuicServerInfo(QuicServerInfo* quic_server_info) = 0; + // Populates all of load timing, except for request start times and receive // headers time. // |load_timing_info| must have all null times when called. Returns false and @@ -141,6 +153,13 @@ class NET_EXPORT_PRIVATE HttpTransaction { // Start(). Ownership of |create_helper| remains with the caller. virtual void SetWebSocketHandshakeStreamCreateHelper( WebSocketHandshakeStreamBase::CreateHelper* create_helper) = 0; + + // Set the callback to receive notification just before network use. + virtual void SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) = 0; + + // Resumes the transaction after being deferred. + virtual int ResumeNetworkStart() = 0; }; } // namespace net diff --git a/chromium/net/http/http_transaction_delegate.h b/chromium/net/http/http_transaction_delegate.h deleted file mode 100644 index 18493733bff..00000000000 --- a/chromium/net/http/http_transaction_delegate.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2012 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. - -#ifndef NET_HTTP_HTTP_TRANSACTION_DELEGATE_H_ -#define NET_HTTP_HTTP_TRANSACTION_DELEGATE_H_ - -namespace net { - -// Delegate class receiving notifications when cache or network actions start -// and finish, i.e. when the object starts and finishes waiting on an -// underlying cache or the network. The owner of a HttpTransaction can use -// this to register a delegate to receive notifications when these events -// happen. -class HttpTransactionDelegate { - public: - virtual ~HttpTransactionDelegate() {} - virtual void OnCacheActionStart() = 0; - virtual void OnCacheActionFinish() = 0; - virtual void OnNetworkActionStart() = 0; - virtual void OnNetworkActionFinish() = 0; -}; - -} // namespace net - -#endif // NET_HTTP_HTTP_TRANSACTION_DELEGATE_H_ diff --git a/chromium/net/http/http_transaction_factory.h b/chromium/net/http/http_transaction_factory.h index d9870be914b..673a6f921e3 100644 --- a/chromium/net/http/http_transaction_factory.h +++ b/chromium/net/http/http_transaction_factory.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef NET_HTTP_HTTP_TRANSACTION_FACTORY_H__ -#define NET_HTTP_HTTP_TRANSACTION_FACTORY_H__ +#ifndef NET_HTTP_HTTP_TRANSACTION_FACTORY_H_ +#define NET_HTTP_HTTP_TRANSACTION_FACTORY_H_ #include "base/memory/scoped_ptr.h" #include "net/base/net_export.h" @@ -14,7 +14,6 @@ namespace net { class HttpCache; class HttpNetworkSession; class HttpTransaction; -class HttpTransactionDelegate; // An interface to a class that can create HttpTransaction objects. class NET_EXPORT HttpTransactionFactory { @@ -24,8 +23,7 @@ class NET_EXPORT HttpTransactionFactory { // Creates a HttpTransaction object. On success, saves the new // transaction to |*trans| and returns OK. virtual int CreateTransaction(RequestPriority priority, - scoped_ptr<HttpTransaction>* trans, - HttpTransactionDelegate* delegate) = 0; + scoped_ptr<HttpTransaction>* trans) = 0; // Returns the associated cache if any (may be NULL). virtual HttpCache* GetCache() = 0; @@ -36,4 +34,4 @@ class NET_EXPORT HttpTransactionFactory { } // namespace net -#endif // NET_HTTP_HTTP_TRANSACTION_FACTORY_H__ +#endif // NET_HTTP_HTTP_TRANSACTION_FACTORY_H_ diff --git a/chromium/net/http/http_transaction_unittest.cc b/chromium/net/http/http_transaction_test_util.cc index e161ecac506..f3d086c5133 100644 --- a/chromium/net/http/http_transaction_unittest.cc +++ b/chromium/net/http/http_transaction_test_util.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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/http/http_transaction_unittest.h" +#include "net/http/http_transaction_test_util.h" #include <algorithm> @@ -157,7 +157,7 @@ TestTransactionConsumer::TestTransactionConsumer( net::HttpTransactionFactory* factory) : state_(IDLE), error_(net::OK) { // Disregard the error code. - factory->CreateTransaction(priority, &trans_, NULL); + factory->CreateTransaction(priority, &trans_); ++quit_counter_; } @@ -226,10 +226,12 @@ MockNetworkTransaction::MockNetworkTransaction( net::RequestPriority priority, MockNetworkLayer* factory) : weak_factory_(this), + request_(NULL), data_cursor_(0), priority_(priority), websocket_handshake_stream_create_helper_(NULL), transaction_factory_(factory->AsWeakPtr()), + received_bytes_(0), socket_log_id_(net::NetLog::Source::kInvalidId) { } @@ -238,54 +240,11 @@ MockNetworkTransaction::~MockNetworkTransaction() {} int MockNetworkTransaction::Start(const net::HttpRequestInfo* request, const net::CompletionCallback& callback, const net::BoundNetLog& net_log) { - const MockTransaction* t = FindMockTransaction(request->url); - if (!t) + if (request_) return net::ERR_FAILED; - test_mode_ = t->test_mode; - - // Return immediately if we're returning an error. - if (net::OK != t->return_code) { - if (test_mode_ & TEST_MODE_SYNC_NET_START) - return t->return_code; - CallbackLater(callback, t->return_code); - return net::ERR_IO_PENDING; - } - - std::string resp_status = t->status; - std::string resp_headers = t->response_headers; - std::string resp_data = t->data; - if (t->handler) - (t->handler)(request, &resp_status, &resp_headers, &resp_data); - - std::string header_data = base::StringPrintf( - "%s\n%s\n", resp_status.c_str(), resp_headers.c_str()); - std::replace(header_data.begin(), header_data.end(), '\n', '\0'); - - response_.request_time = base::Time::Now(); - if (!t->request_time.is_null()) - response_.request_time = t->request_time; - - response_.was_cached = false; - response_.network_accessed = true; - - response_.response_time = base::Time::Now(); - if (!t->response_time.is_null()) - response_.response_time = t->response_time; - - response_.headers = new net::HttpResponseHeaders(header_data); - response_.vary_data.Init(*request, *response_.headers.get()); - response_.ssl_info.cert_status = t->cert_status; - data_ = resp_data; - - if (net_log.net_log()) - socket_log_id_ = net_log.net_log()->NextID(); - - if (test_mode_ & TEST_MODE_SYNC_NET_START) - return net::OK; - - CallbackLater(callback, net::OK); - return net::ERR_IO_PENDING; + request_ = request; + return StartInternal(request, callback, net_log); } int MockNetworkTransaction::RestartIgnoringLastError( @@ -302,17 +261,32 @@ int MockNetworkTransaction::RestartWithCertificate( int MockNetworkTransaction::RestartWithAuth( const net::AuthCredentials& credentials, const net::CompletionCallback& callback) { - return net::ERR_FAILED; + if (!IsReadyToRestartForAuth()) + return net::ERR_FAILED; + + net::HttpRequestInfo auth_request_info = *request_; + auth_request_info.extra_headers.AddHeaderFromString("Authorization: Bar"); + + // Let the MockTransactionHandler worry about this: the only way for this + // test to succeed is by using an explicit handler for the transaction so + // that server behavior can be simulated. + return StartInternal(&auth_request_info, callback, net::BoundNetLog()); } bool MockNetworkTransaction::IsReadyToRestartForAuth() { - return false; + if (!request_) + return false; + + // Only mock auth when the test asks for it. + return request_->extra_headers.HasHeader("X-Require-Mock-Auth"); } int MockNetworkTransaction::Read(net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) { int data_len = static_cast<int>(data_.size()); int num = std::min(buf_len, data_len - data_cursor_); + if (test_mode_ & TEST_MODE_SLOW_READ) + num = std::min(num, 1); if (num) { memcpy(buf->data(), data_.data() + data_cursor_, num); data_cursor_ += num; @@ -324,13 +298,20 @@ int MockNetworkTransaction::Read(net::IOBuffer* buf, int buf_len, return net::ERR_IO_PENDING; } -void MockNetworkTransaction::StopCaching() {} +void MockNetworkTransaction::StopCaching() { + if (transaction_factory_.get()) + transaction_factory_->TransactionStopCaching(); +} bool MockNetworkTransaction::GetFullRequestHeaders( net::HttpRequestHeaders* headers) const { return false; } +int64 MockNetworkTransaction::GetTotalReceivedBytes() const { + return received_bytes_; +} + void MockNetworkTransaction::DoneReading() { if (transaction_factory_.get()) transaction_factory_->TransactionDoneReading(); @@ -350,6 +331,9 @@ net::UploadProgress MockNetworkTransaction::GetUploadProgress() const { return net::UploadProgress(); } +void MockNetworkTransaction::SetQuicServerInfo( + net::QuicServerInfo* quic_server_info) {} + bool MockNetworkTransaction::GetLoadTimingInfo( net::LoadTimingInfo* load_timing_info) const { if (socket_log_id_ != net::NetLog::Source::kInvalidId) { @@ -381,6 +365,70 @@ void MockNetworkTransaction::SetWebSocketHandshakeStreamCreateHelper( websocket_handshake_stream_create_helper_ = create_helper; } +int MockNetworkTransaction::StartInternal( + const net::HttpRequestInfo* request, + const net::CompletionCallback& callback, + const net::BoundNetLog& net_log) { + const MockTransaction* t = FindMockTransaction(request->url); + if (!t) + return net::ERR_FAILED; + + test_mode_ = t->test_mode; + + // Return immediately if we're returning an error. + if (net::OK != t->return_code) { + if (test_mode_ & TEST_MODE_SYNC_NET_START) + return t->return_code; + CallbackLater(callback, t->return_code); + return net::ERR_IO_PENDING; + } + + std::string resp_status = t->status; + std::string resp_headers = t->response_headers; + std::string resp_data = t->data; + received_bytes_ = resp_status.size() + resp_headers.size() + resp_data.size(); + if (t->handler) + (t->handler)(request, &resp_status, &resp_headers, &resp_data); + + std::string header_data = base::StringPrintf( + "%s\n%s\n", resp_status.c_str(), resp_headers.c_str()); + std::replace(header_data.begin(), header_data.end(), '\n', '\0'); + + response_.request_time = base::Time::Now(); + if (!t->request_time.is_null()) + response_.request_time = t->request_time; + + response_.was_cached = false; + response_.network_accessed = true; + + response_.response_time = base::Time::Now(); + if (!t->response_time.is_null()) + response_.response_time = t->response_time; + + response_.headers = new net::HttpResponseHeaders(header_data); + response_.vary_data.Init(*request, *response_.headers.get()); + response_.ssl_info.cert_status = t->cert_status; + data_ = resp_data; + + if (net_log.net_log()) + socket_log_id_ = net_log.net_log()->NextID(); + + if (test_mode_ & TEST_MODE_SYNC_NET_START) + return net::OK; + + CallbackLater(callback, net::OK); + return net::ERR_IO_PENDING; +} + +void MockNetworkTransaction::SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) { +} + +int MockNetworkTransaction::ResumeNetworkStart() { + // Should not get here. + return net::ERR_FAILED; +} + void MockNetworkTransaction::CallbackLater( const net::CompletionCallback& callback, int result) { base::MessageLoop::current()->PostTask( @@ -396,6 +444,7 @@ void MockNetworkTransaction::RunCallback( MockNetworkLayer::MockNetworkLayer() : transaction_count_(0), done_reading_called_(false), + stop_caching_called_(false), last_create_transaction_priority_(net::DEFAULT_PRIORITY) {} MockNetworkLayer::~MockNetworkLayer() {} @@ -404,10 +453,13 @@ void MockNetworkLayer::TransactionDoneReading() { done_reading_called_ = true; } +void MockNetworkLayer::TransactionStopCaching() { + stop_caching_called_ = true; +} + int MockNetworkLayer::CreateTransaction( net::RequestPriority priority, - scoped_ptr<net::HttpTransaction>* trans, - net::HttpTransactionDelegate* delegate) { + scoped_ptr<net::HttpTransaction>* trans) { transaction_count_++; last_create_transaction_priority_ = priority; scoped_ptr<MockNetworkTransaction> mock_transaction( diff --git a/chromium/net/http/http_transaction_unittest.h b/chromium/net/http/http_transaction_test_util.h index 3d6bb0287fb..f123794529b 100644 --- a/chromium/net/http/http_transaction_unittest.h +++ b/chromium/net/http/http_transaction_test_util.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2014 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. @@ -43,7 +43,8 @@ enum { TEST_MODE_SYNC_CACHE_WRITE = 1 << 4, TEST_MODE_SYNC_ALL = (TEST_MODE_SYNC_NET_START | TEST_MODE_SYNC_NET_READ | TEST_MODE_SYNC_CACHE_START | TEST_MODE_SYNC_CACHE_READ | - TEST_MODE_SYNC_CACHE_WRITE) + TEST_MODE_SYNC_CACHE_WRITE), + TEST_MODE_SLOW_READ = 1 << 5 }; typedef void (*MockTransactionHandler)(const net::HttpRequestInfo* request, @@ -193,6 +194,8 @@ class MockNetworkTransaction virtual bool GetFullRequestHeaders( net::HttpRequestHeaders* headers) const OVERRIDE; + virtual int64 GetTotalReceivedBytes() const OVERRIDE; + virtual void DoneReading() OVERRIDE; virtual const net::HttpResponseInfo* GetResponseInfo() const OVERRIDE; @@ -201,6 +204,9 @@ class MockNetworkTransaction virtual net::UploadProgress GetUploadProgress() const OVERRIDE; + virtual void SetQuicServerInfo( + net::QuicServerInfo* quic_server_info) OVERRIDE; + virtual bool GetLoadTimingInfo( net::LoadTimingInfo* load_timing_info) const OVERRIDE; @@ -209,16 +215,25 @@ class MockNetworkTransaction virtual void SetWebSocketHandshakeStreamCreateHelper( CreateHelper* create_helper) OVERRIDE; + virtual void SetBeforeNetworkStartCallback( + const BeforeNetworkStartCallback& callback) OVERRIDE; + + virtual int ResumeNetworkStart() OVERRIDE; + CreateHelper* websocket_handshake_stream_create_helper() { return websocket_handshake_stream_create_helper_; } net::RequestPriority priority() const { return priority_; } private: + int StartInternal(const net::HttpRequestInfo* request, + const net::CompletionCallback& callback, + const net::BoundNetLog& net_log); void CallbackLater(const net::CompletionCallback& callback, int result); void RunCallback(const net::CompletionCallback& callback, int result); base::WeakPtrFactory<MockNetworkTransaction> weak_factory_; + const net::HttpRequestInfo* request_; net::HttpResponseInfo response_; std::string data_; int data_cursor_; @@ -226,6 +241,7 @@ class MockNetworkTransaction net::RequestPriority priority_; CreateHelper* websocket_handshake_stream_create_helper_; base::WeakPtr<MockNetworkLayer> transaction_factory_; + int64 received_bytes_; // NetLog ID of the fake / non-existent underlying socket used by the // connection. Requires Start() be passed a BoundNetLog with a real NetLog to @@ -241,7 +257,9 @@ class MockNetworkLayer : public net::HttpTransactionFactory, int transaction_count() const { return transaction_count_; } bool done_reading_called() const { return done_reading_called_; } + bool stop_caching_called() const { return stop_caching_called_; } void TransactionDoneReading(); + void TransactionStopCaching(); // Returns the last priority passed to CreateTransaction, or // DEFAULT_PRIORITY if it hasn't been called yet. @@ -267,14 +285,14 @@ class MockNetworkLayer : public net::HttpTransactionFactory, // net::HttpTransactionFactory: virtual int CreateTransaction( net::RequestPriority priority, - scoped_ptr<net::HttpTransaction>* trans, - net::HttpTransactionDelegate* delegate) OVERRIDE; + scoped_ptr<net::HttpTransaction>* trans) OVERRIDE; virtual net::HttpCache* GetCache() OVERRIDE; virtual net::HttpNetworkSession* GetSession() OVERRIDE; private: int transaction_count_; bool done_reading_called_; + bool stop_caching_called_; net::RequestPriority last_create_transaction_priority_; base::WeakPtr<MockNetworkTransaction> last_transaction_; }; diff --git a/chromium/net/http/http_util.cc b/chromium/net/http/http_util.cc index 77f498133bc..f4f994af605 100644 --- a/chromium/net/http/http_util.cc +++ b/chromium/net/http/http_util.cc @@ -18,54 +18,40 @@ #include "base/strings/stringprintf.h" #include "base/time/time.h" -using std::string; namespace net { -//----------------------------------------------------------------------------- +// Helpers -------------------------------------------------------------------- -// Return the index of the closing quote of the string, if any. -static size_t FindStringEnd(const string& line, size_t start, char delim) { - DCHECK(start < line.length() && line[start] == delim && - (delim == '"' || delim == '\'')); +// Returns the index of the closing quote of the string, if any. |start| points +// at the opening quote. +static size_t FindStringEnd(const std::string& line, size_t start, char delim) { + DCHECK_LT(start, line.length()); + DCHECK_EQ(line[start], delim); + DCHECK((delim == '"') || (delim == '\'')); const char set[] = { delim, '\\', '\0' }; - for (;;) { - // start points to either the start quote or the last - // escaped char (the char following a '\\') - - size_t end = line.find_first_of(set, start + 1); - if (end == string::npos) - return line.length(); - - if (line[end] == '\\') { - // Hit a backslash-escaped char. Need to skip over it. - start = end + 1; - if (start == line.length()) - return start; - - // Go back to looking for the next escape or the string end - continue; - } - - return end; + for (size_t end = line.find_first_of(set, start + 1); + end != std::string::npos; end = line.find_first_of(set, end + 2)) { + if (line[end] != '\\') + return end; } - - NOTREACHED(); return line.length(); } -//----------------------------------------------------------------------------- + +// HttpUtil ------------------------------------------------------------------- // static -size_t HttpUtil::FindDelimiter(const string& line, size_t search_start, +size_t HttpUtil::FindDelimiter(const std::string& line, + size_t search_start, char delimiter) { do { // search_start points to the spot from which we should start looking // for the delimiter. const char delim_str[] = { delimiter, '"', '\'', '\0' }; size_t cur_delim_pos = line.find_first_of(delim_str, search_start); - if (cur_delim_pos == string::npos) + if (cur_delim_pos == std::string::npos) return line.length(); char ch = line[cur_delim_pos]; @@ -91,12 +77,12 @@ size_t HttpUtil::FindDelimiter(const string& line, size_t search_start, } // static -void HttpUtil::ParseContentType(const string& content_type_str, - string* mime_type, - string* charset, +void HttpUtil::ParseContentType(const std::string& content_type_str, + std::string* mime_type, + std::string* charset, bool* had_charset, - string* boundary) { - const string::const_iterator begin = content_type_str.begin(); + std::string* boundary) { + const std::string::const_iterator begin = content_type_str.begin(); // Trim leading and trailing whitespace from type. We include '(' in // the trailing trim set to catch media-type comments, which are not at all @@ -104,7 +90,7 @@ void HttpUtil::ParseContentType(const string& content_type_str, size_t type_val = content_type_str.find_first_not_of(HTTP_LWS); type_val = std::min(type_val, content_type_str.length()); size_t type_end = content_type_str.find_first_of(HTTP_LWS ";(", type_val); - if (string::npos == type_end) + if (type_end == std::string::npos) type_end = content_type_str.length(); size_t charset_val = 0; @@ -113,22 +99,22 @@ void HttpUtil::ParseContentType(const string& content_type_str, // Iterate over parameters size_t param_start = content_type_str.find_first_of(';', type_end); - if (param_start != string::npos) { + if (param_start != std::string::npos) { base::StringTokenizer tokenizer(begin + param_start, content_type_str.end(), ";"); tokenizer.set_quote_chars("\""); while (tokenizer.GetNext()) { - string::const_iterator equals_sign = + std::string::const_iterator equals_sign = std::find(tokenizer.token_begin(), tokenizer.token_end(), '='); if (equals_sign == tokenizer.token_end()) continue; - string::const_iterator param_name_begin = tokenizer.token_begin(); - string::const_iterator param_name_end = equals_sign; + std::string::const_iterator param_name_begin = tokenizer.token_begin(); + std::string::const_iterator param_name_end = equals_sign; TrimLWS(¶m_name_begin, ¶m_name_end); - string::const_iterator param_value_begin = equals_sign + 1; - string::const_iterator param_value_end = tokenizer.token_end(); + std::string::const_iterator param_value_begin = equals_sign + 1; + std::string::const_iterator param_value_end = tokenizer.token_end(); DCHECK(param_value_begin <= tokenizer.token_end()); TrimLWS(¶m_value_begin, ¶m_value_end); @@ -172,7 +158,7 @@ void HttpUtil::ParseContentType(const string& content_type_str, // include a comma, so this check makes us a bit more tolerant. if (content_type_str.length() != 0 && content_type_str != "*/*" && - content_type_str.find_first_of('/') != string::npos) { + content_type_str.find_first_of('/') != std::string::npos) { // Common case here is that mime_type is empty bool eq = !mime_type->empty() && LowerCaseEqualsASCII(begin + type_val, begin + type_end, @@ -291,7 +277,7 @@ bool HttpUtil::ParseRangeHeader(const std::string& ranges_specifier, // static bool HttpUtil::HasHeader(const std::string& headers, const char* name) { size_t name_len = strlen(name); - string::const_iterator it = + std::string::const_iterator it = std::search(headers.begin(), headers.end(), name, @@ -379,8 +365,8 @@ std::string HttpUtil::StripHeaders(const std::string& headers, } // static -bool HttpUtil::IsNonCoalescingHeader(string::const_iterator name_begin, - string::const_iterator name_end) { +bool HttpUtil::IsNonCoalescingHeader(std::string::const_iterator name_begin, + std::string::const_iterator name_end) { // NOTE: "set-cookie2" headers do not support expires attributes, so we don't // have to list them here. const char* kNonCoalescingHeaders[] = { @@ -409,8 +395,8 @@ bool HttpUtil::IsLWS(char c) { return strchr(HTTP_LWS, c) != NULL; } -void HttpUtil::TrimLWS(string::const_iterator* begin, - string::const_iterator* end) { +void HttpUtil::TrimLWS(std::string::const_iterator* begin, + std::string::const_iterator* end) { // leading whitespace while (*begin < *end && IsLWS((*begin)[0])) ++(*begin); @@ -427,8 +413,8 @@ bool HttpUtil::IsQuote(char c) { } // See RFC 2616 Sec 2.2 for the definition of |token|. -bool HttpUtil::IsToken(string::const_iterator begin, - string::const_iterator end) { +bool HttpUtil::IsToken(std::string::const_iterator begin, + std::string::const_iterator end) { if (begin == end) return false; for (std::string::const_iterator iter = begin; iter != end; ++iter) { @@ -764,9 +750,10 @@ int HttpUtil::MapStatusCodeForHistogram(int code) { // of token, separators, and quoted-string> // -HttpUtil::HeadersIterator::HeadersIterator(string::const_iterator headers_begin, - string::const_iterator headers_end, - const std::string& line_delimiter) +HttpUtil::HeadersIterator::HeadersIterator( + std::string::const_iterator headers_begin, + std::string::const_iterator headers_end, + const std::string& line_delimiter) : lines_(headers_begin, headers_end, line_delimiter) { } @@ -778,7 +765,7 @@ bool HttpUtil::HeadersIterator::GetNext() { name_begin_ = lines_.token_begin(); values_end_ = lines_.token_end(); - string::const_iterator colon = std::find(name_begin_, values_end_, ':'); + std::string::const_iterator colon(std::find(name_begin_, values_end_, ':')); if (colon == values_end_) continue; // skip malformed header @@ -818,10 +805,10 @@ bool HttpUtil::HeadersIterator::AdvanceTo(const char* name) { } HttpUtil::ValuesIterator::ValuesIterator( - string::const_iterator values_begin, - string::const_iterator values_end, + std::string::const_iterator values_begin, + std::string::const_iterator values_end, char delimiter) - : values_(values_begin, values_end, string(1, delimiter)) { + : values_(values_begin, values_end, std::string(1, delimiter)) { values_.set_quote_chars("\'\""); } @@ -842,8 +829,8 @@ bool HttpUtil::ValuesIterator::GetNext() { } HttpUtil::NameValuePairsIterator::NameValuePairsIterator( - string::const_iterator begin, - string::const_iterator end, + std::string::const_iterator begin, + std::string::const_iterator end, char delimiter) : props_(begin, end, delimiter), valid_(true), diff --git a/chromium/net/http/http_util.h b/chromium/net/http/http_util.h index ae65146e6e4..14117c00289 100644 --- a/chromium/net/http/http_util.h +++ b/chromium/net/http/http_util.h @@ -56,8 +56,8 @@ class NET_EXPORT HttpUtil { // if "Range" exists and the first one of it is well formatted then returns // true, |ranges| will contain a list of valid ranges. If return // value is false then values in |ranges| should not be used. The format of - // "Range" header is defined in RFC 2616 Section 14.35.1. - // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.1 + // "Range" header is defined in RFC 7233 Section 2.1. + // https://tools.ietf.org/html/rfc7233#section-2.1 static bool ParseRanges(const std::string& headers, std::vector<HttpByteRange>* ranges); diff --git a/chromium/net/http/mock_http_cache.cc b/chromium/net/http/mock_http_cache.cc index a3d55b17e6b..5dbd486df0f 100644 --- a/chromium/net/http/mock_http_cache.cc +++ b/chromium/net/http/mock_http_cache.cc @@ -512,6 +512,10 @@ MockDiskCache* MockHttpCache::disk_cache() { return (rv == net::OK) ? static_cast<MockDiskCache*>(backend) : NULL; } +int MockHttpCache::CreateTransaction(scoped_ptr<net::HttpTransaction>* trans) { + return http_cache_.CreateTransaction(net::DEFAULT_PRIORITY, trans); +} + bool MockHttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry, net::HttpResponseInfo* response_info, bool* response_truncated) { diff --git a/chromium/net/http/mock_http_cache.h b/chromium/net/http/mock_http_cache.h index 90399ec4ff0..0fd192e597e 100644 --- a/chromium/net/http/mock_http_cache.h +++ b/chromium/net/http/mock_http_cache.h @@ -13,7 +13,7 @@ #include "base/containers/hash_tables.h" #include "net/disk_cache/disk_cache.h" #include "net/http/http_cache.h" -#include "net/http/http_transaction_unittest.h" +#include "net/http/http_transaction_test_util.h" //----------------------------------------------------------------------------- // Mock disk cache (a very basic memory cache implementation). @@ -174,6 +174,9 @@ class MockHttpCache { } MockDiskCache* disk_cache(); + // Wrapper around http_cache()->CreateTransaction(net::DEFAULT_PRIORITY...) + int CreateTransaction(scoped_ptr<net::HttpTransaction>* trans); + // Helper function for reading response info from the disk cache. static bool ReadResponseInfo(disk_cache::Entry* disk_entry, net::HttpResponseInfo* response_info, diff --git a/chromium/net/http/partial_data.cc b/chromium/net/http/partial_data.cc index ee3678b5ea0..a05c2189754 100644 --- a/chromium/net/http/partial_data.cc +++ b/chromium/net/http/partial_data.cc @@ -382,40 +382,26 @@ void PartialData::FixResponseHeaders(HttpResponseHeaders* headers, if (truncated_) return; + if (byte_range_.IsValid() && success) { + headers->UpdateWithNewRange(byte_range_, resource_size_, !sparse_entry_); + return; + } + headers->RemoveHeader(kLengthHeader); headers->RemoveHeader(kRangeHeader); - int64 range_len, start, end; if (byte_range_.IsValid()) { - if (success) { - if (!sparse_entry_) - headers->ReplaceStatusLine("HTTP/1.1 206 Partial Content"); - - DCHECK(byte_range_.HasFirstBytePosition()); - DCHECK(byte_range_.HasLastBytePosition()); - start = byte_range_.first_byte_position(); - end = byte_range_.last_byte_position(); - range_len = end - start + 1; - } else { - headers->ReplaceStatusLine( - "HTTP/1.1 416 Requested Range Not Satisfiable"); - start = 0; - end = 0; - range_len = 0; - } - - headers->AddHeader( - base::StringPrintf("%s: bytes %" PRId64 "-%" PRId64 "/%" PRId64, - kRangeHeader, start, end, resource_size_)); + headers->ReplaceStatusLine("HTTP/1.1 416 Requested Range Not Satisfiable"); + headers->AddHeader(base::StringPrintf("%s: bytes 0-0/%" PRId64, + kRangeHeader, resource_size_)); + headers->AddHeader(base::StringPrintf("%s: 0", kLengthHeader)); } else { // TODO(rvargas): Is it safe to change the protocol version? headers->ReplaceStatusLine("HTTP/1.1 200 OK"); DCHECK_NE(resource_size_, 0); - range_len = resource_size_; + headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, + resource_size_)); } - - headers->AddHeader(base::StringPrintf("%s: %" PRId64, kLengthHeader, - range_len)); } void PartialData::FixContentLength(HttpResponseHeaders* headers) { diff --git a/chromium/net/http/proxy_connect_redirect_http_stream.cc b/chromium/net/http/proxy_connect_redirect_http_stream.cc index 0857a7451ca..b859d37de63 100644 --- a/chromium/net/http/proxy_connect_redirect_http_stream.cc +++ b/chromium/net/http/proxy_connect_redirect_http_stream.cc @@ -43,12 +43,6 @@ int ProxyConnectRedirectHttpStream::ReadResponseHeaders( return OK; } -const HttpResponseInfo* -ProxyConnectRedirectHttpStream::GetResponseInfo() const { - NOTREACHED(); - return NULL; -} - int ProxyConnectRedirectHttpStream::ReadResponseBody( IOBuffer* buf, int buf_len, diff --git a/chromium/net/http/proxy_connect_redirect_http_stream.h b/chromium/net/http/proxy_connect_redirect_http_stream.h index 0547654ab92..f848c64fcd1 100644 --- a/chromium/net/http/proxy_connect_redirect_http_stream.h +++ b/chromium/net/http/proxy_connect_redirect_http_stream.h @@ -33,7 +33,6 @@ class ProxyConnectRedirectHttpStream : public HttpStream { HttpResponseInfo* response, const CompletionCallback& callback) OVERRIDE; virtual int ReadResponseHeaders(const CompletionCallback& callback) OVERRIDE; - virtual const HttpResponseInfo* GetResponseInfo() const OVERRIDE; virtual int ReadResponseBody(IOBuffer* buf, int buf_len, const CompletionCallback& callback) OVERRIDE; diff --git a/chromium/net/http/transport_security_persister.cc b/chromium/net/http/transport_security_persister.cc index 771d07dc1bc..7310b590f7e 100644 --- a/chromium/net/http/transport_security_persister.cc +++ b/chromium/net/http/transport_security_persister.cc @@ -26,14 +26,15 @@ using net::TransportSecurityState; namespace { -ListValue* SPKIHashesToListValue(const HashValueVector& hashes) { - ListValue* pins = new ListValue; +base::ListValue* SPKIHashesToListValue(const HashValueVector& hashes) { + base::ListValue* pins = new base::ListValue; for (size_t i = 0; i != hashes.size(); i++) - pins->Append(new StringValue(hashes[i].ToString())); + pins->Append(new base::StringValue(hashes[i].ToString())); return pins; } -void SPKIHashesFromListValue(const ListValue& pins, HashValueVector* hashes) { +void SPKIHashesFromListValue(const base::ListValue& pins, + HashValueVector* hashes) { size_t num_pins = pins.GetSize(); for (size_t i = 0; i < num_pins; ++i) { std::string type_and_base64; @@ -71,14 +72,14 @@ const char kPkpIncludeSubdomains[] = "pkp_include_subdomains"; const char kMode[] = "mode"; const char kExpiry[] = "expiry"; const char kDynamicSPKIHashesExpiry[] = "dynamic_spki_hashes_expiry"; -const char kStaticSPKIHashes[] = "static_spki_hashes"; -const char kPreloadedSPKIHashes[] = "preloaded_spki_hashes"; const char kDynamicSPKIHashes[] = "dynamic_spki_hashes"; const char kForceHTTPS[] = "force-https"; const char kStrict[] = "strict"; const char kDefault[] = "default"; const char kPinningOnly[] = "pinning-only"; const char kCreated[] = "created"; +const char kStsObserved[] = "sts_observed"; +const char kPkpObserved[] = "pkp_observed"; std::string LoadState(const base::FilePath& path) { std::string result; @@ -135,7 +136,7 @@ void TransportSecurityPersister::StateIsDirty( bool TransportSecurityPersister::SerializeData(std::string* output) { DCHECK(foreground_runner_->RunsTasksOnCurrentThread()); - DictionaryValue toplevel; + base::DictionaryValue toplevel; base::Time now = base::Time::Now(); TransportSecurityState::Iterator state(*transport_security_state_); for (; state.HasNext(); state.Advance()) { @@ -143,17 +144,20 @@ bool TransportSecurityPersister::SerializeData(std::string* output) { const TransportSecurityState::DomainState& domain_state = state.domain_state(); - DictionaryValue* serialized = new DictionaryValue; + base::DictionaryValue* serialized = new base::DictionaryValue; serialized->SetBoolean(kStsIncludeSubdomains, - domain_state.sts_include_subdomains); + domain_state.sts.include_subdomains); serialized->SetBoolean(kPkpIncludeSubdomains, - domain_state.pkp_include_subdomains); - serialized->SetDouble(kCreated, domain_state.created.ToDoubleT()); - serialized->SetDouble(kExpiry, domain_state.upgrade_expiry.ToDoubleT()); + domain_state.pkp.include_subdomains); + serialized->SetDouble(kStsObserved, + domain_state.sts.last_observed.ToDoubleT()); + serialized->SetDouble(kPkpObserved, + domain_state.pkp.last_observed.ToDoubleT()); + serialized->SetDouble(kExpiry, domain_state.sts.expiry.ToDoubleT()); serialized->SetDouble(kDynamicSPKIHashesExpiry, - domain_state.dynamic_spki_hashes_expiry.ToDoubleT()); + domain_state.pkp.expiry.ToDoubleT()); - switch (domain_state.upgrade_mode) { + switch (domain_state.sts.upgrade_mode) { case TransportSecurityState::DomainState::MODE_FORCE_HTTPS: serialized->SetString(kMode, kForceHTTPS); break; @@ -166,12 +170,9 @@ bool TransportSecurityPersister::SerializeData(std::string* output) { continue; } - serialized->Set(kStaticSPKIHashes, - SPKIHashesToListValue(domain_state.static_spki_hashes)); - - if (now < domain_state.dynamic_spki_hashes_expiry) { + if (now < domain_state.pkp.expiry) { serialized->Set(kDynamicSPKIHashes, - SPKIHashesToListValue(domain_state.dynamic_spki_hashes)); + SPKIHashesToListValue(domain_state.pkp.spki_hashes)); } toplevel.Set(HashedDomainToExternalString(hostname), serialized); @@ -195,23 +196,23 @@ bool TransportSecurityPersister::LoadEntries(const std::string& serialized, bool TransportSecurityPersister::Deserialize(const std::string& serialized, bool* dirty, TransportSecurityState* state) { - scoped_ptr<Value> value(base::JSONReader::Read(serialized)); - DictionaryValue* dict_value = NULL; + scoped_ptr<base::Value> value(base::JSONReader::Read(serialized)); + base::DictionaryValue* dict_value = NULL; if (!value.get() || !value->GetAsDictionary(&dict_value)) return false; const base::Time current_time(base::Time::Now()); bool dirtied = false; - for (DictionaryValue::Iterator i(*dict_value); !i.IsAtEnd(); i.Advance()) { - const DictionaryValue* parsed = NULL; + for (base::DictionaryValue::Iterator i(*dict_value); + !i.IsAtEnd(); i.Advance()) { + const base::DictionaryValue* parsed = NULL; if (!i.value().GetAsDictionary(&parsed)) { LOG(WARNING) << "Could not parse entry " << i.key() << "; skipping entry"; continue; } std::string mode_string; - double created; double expiry; double dynamic_spki_hashes_expiry = 0.0; TransportSecurityState::DomainState domain_state; @@ -222,14 +223,14 @@ bool TransportSecurityPersister::Deserialize(const std::string& serialized, bool include_subdomains = false; bool parsed_include_subdomains = parsed->GetBoolean(kIncludeSubdomains, &include_subdomains); - domain_state.sts_include_subdomains = include_subdomains; - domain_state.pkp_include_subdomains = include_subdomains; + domain_state.sts.include_subdomains = include_subdomains; + domain_state.pkp.include_subdomains = include_subdomains; if (parsed->GetBoolean(kStsIncludeSubdomains, &include_subdomains)) { - domain_state.sts_include_subdomains = include_subdomains; + domain_state.sts.include_subdomains = include_subdomains; parsed_include_subdomains = true; } if (parsed->GetBoolean(kPkpIncludeSubdomains, &include_subdomains)) { - domain_state.pkp_include_subdomains = include_subdomains; + domain_state.pkp.include_subdomains = include_subdomains; parsed_include_subdomains = true; } @@ -245,21 +246,16 @@ bool TransportSecurityPersister::Deserialize(const std::string& serialized, parsed->GetDouble(kDynamicSPKIHashesExpiry, &dynamic_spki_hashes_expiry); - const ListValue* pins_list = NULL; - // preloaded_spki_hashes is a legacy synonym for static_spki_hashes. - if (parsed->GetList(kStaticSPKIHashes, &pins_list)) - SPKIHashesFromListValue(*pins_list, &domain_state.static_spki_hashes); - else if (parsed->GetList(kPreloadedSPKIHashes, &pins_list)) - SPKIHashesFromListValue(*pins_list, &domain_state.static_spki_hashes); - - if (parsed->GetList(kDynamicSPKIHashes, &pins_list)) - SPKIHashesFromListValue(*pins_list, &domain_state.dynamic_spki_hashes); + const base::ListValue* pins_list = NULL; + if (parsed->GetList(kDynamicSPKIHashes, &pins_list)) { + SPKIHashesFromListValue(*pins_list, &domain_state.pkp.spki_hashes); + } if (mode_string == kForceHTTPS || mode_string == kStrict) { - domain_state.upgrade_mode = + domain_state.sts.upgrade_mode = TransportSecurityState::DomainState::MODE_FORCE_HTTPS; } else if (mode_string == kDefault || mode_string == kPinningOnly) { - domain_state.upgrade_mode = + domain_state.sts.upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; } else { LOG(WARNING) << "Unknown TransportSecurityState mode string " @@ -268,20 +264,34 @@ bool TransportSecurityPersister::Deserialize(const std::string& serialized, continue; } - domain_state.upgrade_expiry = base::Time::FromDoubleT(expiry); - domain_state.dynamic_spki_hashes_expiry = + domain_state.sts.expiry = base::Time::FromDoubleT(expiry); + domain_state.pkp.expiry = base::Time::FromDoubleT(dynamic_spki_hashes_expiry); - if (parsed->GetDouble(kCreated, &created)) { - domain_state.created = base::Time::FromDoubleT(created); + + double sts_observed; + double pkp_observed; + if (parsed->GetDouble(kStsObserved, &sts_observed)) { + domain_state.sts.last_observed = base::Time::FromDoubleT(sts_observed); + } else if (parsed->GetDouble(kCreated, &sts_observed)) { + // kCreated is a legacy synonym for both kStsObserved and kPkpObserved. + domain_state.sts.last_observed = base::Time::FromDoubleT(sts_observed); } else { - // We're migrating an old entry with no creation date. Make sure we + // We're migrating an old entry with no observation date. Make sure we // write the new date back in a reasonable time frame. dirtied = true; - domain_state.created = base::Time::Now(); + domain_state.sts.last_observed = base::Time::Now(); + } + if (parsed->GetDouble(kPkpObserved, &pkp_observed)) { + domain_state.pkp.last_observed = base::Time::FromDoubleT(pkp_observed); + } else if (parsed->GetDouble(kCreated, &pkp_observed)) { + domain_state.pkp.last_observed = base::Time::FromDoubleT(pkp_observed); + } else { + dirtied = true; + domain_state.pkp.last_observed = base::Time::Now(); } - if (domain_state.upgrade_expiry <= current_time && - domain_state.dynamic_spki_hashes_expiry <= current_time) { + if (domain_state.sts.expiry <= current_time && + domain_state.pkp.expiry <= current_time) { // Make sure we dirty the state if we drop an entry. dirtied = true; continue; diff --git a/chromium/net/http/transport_security_persister_unittest.cc b/chromium/net/http/transport_security_persister_unittest.cc index 8c41f9e81da..a30edae94f7 100644 --- a/chromium/net/http/transport_security_persister_unittest.cc +++ b/chromium/net/http/transport_security_persister_unittest.cc @@ -57,7 +57,8 @@ TEST_F(TransportSecurityPersisterTest, SerializeData2) { const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); static const char kYahooDomain[] = "yahoo.com"; - EXPECT_FALSE(state_.GetDomainState(kYahooDomain, true, &domain_state)); + EXPECT_FALSE(state_.GetStaticDomainState(kYahooDomain, true, &domain_state)); + EXPECT_FALSE(state_.GetDynamicDomainState(kYahooDomain, &domain_state)); bool include_subdomains = true; state_.AddHSTS(kYahooDomain, expiry, include_subdomains); @@ -67,20 +68,20 @@ TEST_F(TransportSecurityPersisterTest, SerializeData2) { EXPECT_TRUE(persister_->SerializeData(&output)); EXPECT_TRUE(persister_->LoadEntries(output, &dirty)); - EXPECT_TRUE(state_.GetDomainState(kYahooDomain, true, &domain_state)); - EXPECT_EQ(domain_state.upgrade_mode, + EXPECT_TRUE(state_.GetDynamicDomainState(kYahooDomain, &domain_state)); + EXPECT_EQ(domain_state.sts.upgrade_mode, TransportSecurityState::DomainState::MODE_FORCE_HTTPS); - EXPECT_TRUE(state_.GetDomainState("foo.yahoo.com", true, &domain_state)); - EXPECT_EQ(domain_state.upgrade_mode, + EXPECT_TRUE(state_.GetDynamicDomainState("foo.yahoo.com", &domain_state)); + EXPECT_EQ(domain_state.sts.upgrade_mode, TransportSecurityState::DomainState::MODE_FORCE_HTTPS); - EXPECT_TRUE(state_.GetDomainState("foo.bar.yahoo.com", true, &domain_state)); - EXPECT_EQ(domain_state.upgrade_mode, + EXPECT_TRUE(state_.GetDynamicDomainState("foo.bar.yahoo.com", &domain_state)); + EXPECT_EQ(domain_state.sts.upgrade_mode, TransportSecurityState::DomainState::MODE_FORCE_HTTPS); - EXPECT_TRUE(state_.GetDomainState("foo.bar.baz.yahoo.com", true, - &domain_state)); - EXPECT_EQ(domain_state.upgrade_mode, + EXPECT_TRUE( + state_.GetDynamicDomainState("foo.bar.baz.yahoo.com", &domain_state)); + EXPECT_EQ(domain_state.sts.upgrade_mode, TransportSecurityState::DomainState::MODE_FORCE_HTTPS); - EXPECT_FALSE(state_.GetDomainState("com", true, &domain_state)); + EXPECT_FALSE(state_.GetStaticDomainState("com", true, &domain_state)); } TEST_F(TransportSecurityPersisterTest, SerializeData3) { @@ -127,8 +128,7 @@ TEST_F(TransportSecurityPersisterTest, SerializeData3) { // than block.) Use a different basename just for cleanliness. base::FilePath path = temp_dir_.path().AppendASCII("TransportSecurityPersisterTest"); - EXPECT_TRUE(file_util::WriteFile(path, serialized.c_str(), - serialized.size())); + EXPECT_TRUE(base::WriteFile(path, serialized.c_str(), serialized.size())); // Read the data back. std::string persisted; @@ -167,35 +167,40 @@ TEST_F(TransportSecurityPersisterTest, SerializeDataOld) { TEST_F(TransportSecurityPersisterTest, PublicKeyHashes) { TransportSecurityState::DomainState domain_state; static const char kTestDomain[] = "example.com"; - EXPECT_FALSE(state_.GetDomainState(kTestDomain, false, &domain_state)); + EXPECT_FALSE(state_.GetDynamicDomainState(kTestDomain, &domain_state)); net::HashValueVector hashes; - EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes)); + std::string failure_log; + EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log)); net::HashValue sha1(net::HASH_VALUE_SHA1); memset(sha1.data(), '1', sha1.size()); - domain_state.dynamic_spki_hashes.push_back(sha1); + domain_state.pkp.spki_hashes.push_back(sha1); - EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes)); + EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log)); hashes.push_back(sha1); - EXPECT_TRUE(domain_state.CheckPublicKeyPins(hashes)); + EXPECT_TRUE(domain_state.CheckPublicKeyPins(hashes, &failure_log)); hashes[0].data()[0] = '2'; - EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes)); + EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log)); const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); bool include_subdomains = false; state_.AddHSTS(kTestDomain, expiry, include_subdomains); - state_.AddHPKP(kTestDomain, expiry, include_subdomains, - domain_state.dynamic_spki_hashes); - std::string ser; - EXPECT_TRUE(persister_->SerializeData(&ser)); + state_.AddHPKP( + kTestDomain, expiry, include_subdomains, domain_state.pkp.spki_hashes); + std::string serialized; + EXPECT_TRUE(persister_->SerializeData(&serialized)); bool dirty; - EXPECT_TRUE(persister_->LoadEntries(ser, &dirty)); - EXPECT_TRUE(state_.GetDomainState(kTestDomain, false, &domain_state)); - EXPECT_EQ(1u, domain_state.dynamic_spki_hashes.size()); - EXPECT_EQ(sha1.tag, domain_state.dynamic_spki_hashes[0].tag); - EXPECT_EQ(0, memcmp(domain_state.dynamic_spki_hashes[0].data(), sha1.data(), - sha1.size())); + EXPECT_TRUE(persister_->LoadEntries(serialized, &dirty)); + + TransportSecurityState::DomainState new_domain_state; + EXPECT_TRUE(state_.GetDynamicDomainState(kTestDomain, &new_domain_state)); + EXPECT_EQ(1u, new_domain_state.pkp.spki_hashes.size()); + EXPECT_EQ(sha1.tag, new_domain_state.pkp.spki_hashes[0].tag); + EXPECT_EQ(0, + memcmp(new_domain_state.pkp.spki_hashes[0].data(), + sha1.data(), + sha1.size())); } diff --git a/chromium/net/http/transport_security_state.cc b/chromium/net/http/transport_security_state.cc index f9ba807ff79..f50b26483d1 100644 --- a/chromium/net/http/transport_security_state.cc +++ b/chromium/net/http/transport_security_state.cc @@ -95,6 +95,61 @@ TransportSecurityState::Iterator::Iterator(const TransportSecurityState& state) TransportSecurityState::Iterator::~Iterator() {} +bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host, + bool sni_enabled) { + DomainState state; + if (GetStaticDomainState(host, sni_enabled, &state)) + return true; + return GetDynamicDomainState(host, &state); +} + +bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host, + bool sni_enabled) { + DomainState dynamic_state; + if (GetDynamicDomainState(host, &dynamic_state)) + return dynamic_state.ShouldUpgradeToSSL(); + + DomainState static_state; + if (GetStaticDomainState(host, sni_enabled, &static_state) && + static_state.ShouldUpgradeToSSL()) { + return true; + } + + return false; +} + +bool TransportSecurityState::CheckPublicKeyPins(const std::string& host, + bool sni_enabled, + const HashValueVector& hashes, + std::string* failure_log) { + DomainState dynamic_state; + if (GetDynamicDomainState(host, &dynamic_state)) + return dynamic_state.CheckPublicKeyPins(hashes, failure_log); + + DomainState static_state; + if (GetStaticDomainState(host, sni_enabled, &static_state) && + static_state.CheckPublicKeyPins(hashes, failure_log)) { + return true; + } + + return false; +} + +bool TransportSecurityState::HasPublicKeyPins(const std::string& host, + bool sni_enabled) { + DomainState dynamic_state; + if (GetDynamicDomainState(host, &dynamic_state)) + return dynamic_state.HasPublicKeyPins(); + + DomainState static_state; + if (GetStaticDomainState(host, sni_enabled, &static_state)) { + if (static_state.HasPublicKeyPins()) + return true; + } + + return false; +} + void TransportSecurityState::SetDelegate( TransportSecurityState::Delegate* delegate) { DCHECK(CalledOnValidThread()); @@ -135,61 +190,6 @@ bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { return false; } -bool TransportSecurityState::GetDomainState(const std::string& host, - bool sni_enabled, - DomainState* result) { - DCHECK(CalledOnValidThread()); - - DomainState state; - const std::string canonicalized_host = CanonicalizeHost(host); - if (canonicalized_host.empty()) - return false; - - bool has_preload = GetStaticDomainState(canonicalized_host, sni_enabled, - &state); - std::string canonicalized_preload = CanonicalizeHost(state.domain); - GetDynamicDomainState(host, &state); - - base::Time current_time(base::Time::Now()); - - for (size_t i = 0; canonicalized_host[i]; i += canonicalized_host[i] + 1) { - std::string host_sub_chunk(&canonicalized_host[i], - canonicalized_host.size() - i); - // Exact match of a preload always wins. - if (has_preload && host_sub_chunk == canonicalized_preload) { - *result = state; - return true; - } - - DomainStateMap::iterator j = - enabled_hosts_.find(HashHost(host_sub_chunk)); - if (j == enabled_hosts_.end()) - continue; - - if (current_time > j->second.upgrade_expiry && - current_time > j->second.dynamic_spki_hashes_expiry) { - enabled_hosts_.erase(j); - DirtyNotify(); - continue; - } - - state = j->second; - state.domain = DNSDomainToString(host_sub_chunk); - - // Succeed if we matched the domain exactly or if subdomain matches are - // allowed. - if (i == 0 || j->second.sts_include_subdomains || - j->second.pkp_include_subdomains) { - *result = state; - return true; - } - - return false; - } - - return false; -} - void TransportSecurityState::ClearDynamicData() { DCHECK(CalledOnValidThread()); enabled_hosts_.clear(); @@ -199,15 +199,24 @@ void TransportSecurityState::DeleteAllDynamicDataSince(const base::Time& time) { DCHECK(CalledOnValidThread()); bool dirtied = false; - DomainStateMap::iterator i = enabled_hosts_.begin(); while (i != enabled_hosts_.end()) { - if (i->second.created >= time) { + if (i->second.sts.last_observed >= time && + i->second.pkp.last_observed >= time) { dirtied = true; enabled_hosts_.erase(i++); - } else { - i++; + continue; + } + + if (i->second.sts.last_observed >= time) { + dirtied = true; + i->second.sts.upgrade_mode = DomainState::MODE_DEFAULT; + } else if (i->second.pkp.last_observed >= time) { + dirtied = true; + i->second.pkp.spki_hashes.clear(); + i->second.pkp.expiry = base::Time(); } + ++i; } if (dirtied) @@ -515,6 +524,7 @@ enum SecondLevelDomainName { DOMAIN_LAVABIT_COM, DOMAIN_GOOGLETAGMANAGER_COM, + DOMAIN_GOOGLETAGSERVICES_COM, // Boundary value for UMA_HISTOGRAM_ENUMERATION: DOMAIN_NUM_EVENTS @@ -547,22 +557,27 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries, if (!entries[j].include_subdomains && i != 0) { *ret = false; } else { - out->sts_include_subdomains = entries[j].include_subdomains; - out->pkp_include_subdomains = entries[j].include_subdomains; + out->sts.include_subdomains = entries[j].include_subdomains; + out->sts.last_observed = base::GetBuildTime(); + out->pkp.include_subdomains = entries[j].include_subdomains; + out->pkp.last_observed = base::GetBuildTime(); *ret = true; + out->sts.upgrade_mode = + TransportSecurityState::DomainState::MODE_FORCE_HTTPS; if (!entries[j].https_required) - out->upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; + out->sts.upgrade_mode = + TransportSecurityState::DomainState::MODE_DEFAULT; if (entries[j].pins.required_hashes) { const char* const* sha1_hash = entries[j].pins.required_hashes; while (*sha1_hash) { - AddHash(*sha1_hash, &out->static_spki_hashes); + AddHash(*sha1_hash, &out->pkp.spki_hashes); sha1_hash++; } } if (entries[j].pins.excluded_hashes) { const char* const* sha1_hash = entries[j].pins.excluded_hashes; while (*sha1_hash) { - AddHash(*sha1_hash, &out->bad_static_spki_hashes); + AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); sha1_hash++; } } @@ -610,14 +625,14 @@ bool TransportSecurityState::AddHSTSHeader(const std::string& host, base::TimeDelta max_age; TransportSecurityState::DomainState domain_state; GetDynamicDomainState(host, &domain_state); - if (ParseHSTSHeader(value, &max_age, &domain_state.sts_include_subdomains)) { - // Handle max-age == 0 + if (ParseHSTSHeader(value, &max_age, &domain_state.sts.include_subdomains)) { + // Handle max-age == 0. if (max_age.InSeconds() == 0) - domain_state.upgrade_mode = DomainState::MODE_DEFAULT; + domain_state.sts.upgrade_mode = DomainState::MODE_DEFAULT; else - domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; - domain_state.created = now; - domain_state.upgrade_expiry = now + max_age; + domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; + domain_state.sts.last_observed = now; + domain_state.sts.expiry = now + max_age; EnableHost(host, domain_state); return true; } @@ -633,12 +648,16 @@ bool TransportSecurityState::AddHPKPHeader(const std::string& host, base::TimeDelta max_age; TransportSecurityState::DomainState domain_state; GetDynamicDomainState(host, &domain_state); - if (ParseHPKPHeader(value, ssl_info.public_key_hashes, - &max_age, &domain_state.pkp_include_subdomains, - &domain_state.dynamic_spki_hashes)) { - // TODO(palmer): http://crbug.com/243865 handle max-age == 0. - domain_state.created = now; - domain_state.dynamic_spki_hashes_expiry = now + max_age; + if (ParseHPKPHeader(value, + ssl_info.public_key_hashes, + &max_age, + &domain_state.pkp.include_subdomains, + &domain_state.pkp.spki_hashes)) { + // Handle max-age == 0. + if (max_age.InSeconds() == 0) + domain_state.pkp.spki_hashes.clear(); + domain_state.pkp.last_observed = now; + domain_state.pkp.expiry = now + max_age; EnableHost(host, domain_state); return true; } @@ -659,10 +678,10 @@ bool TransportSecurityState::AddHSTS(const std::string& host, if (i != enabled_hosts_.end()) domain_state = i->second; - domain_state.created = base::Time::Now(); - domain_state.sts_include_subdomains = include_subdomains; - domain_state.upgrade_expiry = expiry; - domain_state.upgrade_mode = DomainState::MODE_FORCE_HTTPS; + domain_state.sts.last_observed = base::Time::Now(); + domain_state.sts.include_subdomains = include_subdomains; + domain_state.sts.expiry = expiry; + domain_state.sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; EnableHost(host, domain_state); return true; } @@ -682,10 +701,10 @@ bool TransportSecurityState::AddHPKP(const std::string& host, if (i != enabled_hosts_.end()) domain_state = i->second; - domain_state.created = base::Time::Now(); - domain_state.pkp_include_subdomains = include_subdomains; - domain_state.dynamic_spki_hashes_expiry = expiry; - domain_state.dynamic_spki_hashes = hashes; + domain_state.pkp.last_observed = base::Time::Now(); + domain_state.pkp.include_subdomains = include_subdomains; + domain_state.pkp.expiry = expiry; + domain_state.pkp.spki_hashes = hashes; EnableHost(host, domain_state); return true; } @@ -742,15 +761,16 @@ bool TransportSecurityState::IsBuildTimely() { return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; } -bool TransportSecurityState::GetStaticDomainState( - const std::string& canonicalized_host, - bool sni_enabled, - DomainState* out) { +bool TransportSecurityState::GetStaticDomainState(const std::string& host, + bool sni_enabled, + DomainState* out) const { DCHECK(CalledOnValidThread()); - out->upgrade_mode = DomainState::MODE_FORCE_HTTPS; - out->sts_include_subdomains = false; - out->pkp_include_subdomains = false; + const std::string canonicalized_host = CanonicalizeHost(host); + + out->sts.upgrade_mode = DomainState::MODE_FORCE_HTTPS; + out->sts.include_subdomains = false; + out->pkp.include_subdomains = false; const bool is_build_timely = IsBuildTimely(); @@ -794,8 +814,8 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host, if (j == enabled_hosts_.end()) continue; - if (current_time > j->second.upgrade_expiry && - current_time > j->second.dynamic_spki_hashes_expiry) { + if (current_time > j->second.sts.expiry && + current_time > j->second.pkp.expiry) { enabled_hosts_.erase(j); DirtyNotify(); continue; @@ -806,8 +826,8 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host, // Succeed if we matched the domain exactly or if subdomain matches are // allowed. - if (i == 0 || j->second.sts_include_subdomains || - j->second.pkp_include_subdomains) { + if (i == 0 || j->second.sts.include_subdomains || + j->second.pkp.include_subdomains) { *result = state; return true; } @@ -818,60 +838,57 @@ bool TransportSecurityState::GetDynamicDomainState(const std::string& host, return false; } - void TransportSecurityState::AddOrUpdateEnabledHosts( const std::string& hashed_host, const DomainState& state) { DCHECK(CalledOnValidThread()); enabled_hosts_[hashed_host] = state; } -TransportSecurityState::DomainState::DomainState() - : upgrade_mode(MODE_DEFAULT), - created(base::Time::Now()), - sts_include_subdomains(false), - pkp_include_subdomains(false) { +TransportSecurityState::DomainState::DomainState() { + sts.upgrade_mode = MODE_DEFAULT; + sts.include_subdomains = false; + pkp.include_subdomains = false; } TransportSecurityState::DomainState::~DomainState() { } bool TransportSecurityState::DomainState::CheckPublicKeyPins( - const HashValueVector& hashes) const { + const HashValueVector& hashes, std::string* failure_log) const { // Validate that hashes is not empty. By the time this code is called (in // production), that should never happen, but it's good to be defensive. // And, hashes *can* be empty in some test scenarios. if (hashes.empty()) { - LOG(ERROR) << "Rejecting empty public key chain for public-key-pinned " - "domain " << domain; + failure_log->append( + "Rejecting empty public key chain for public-key-pinned domains: " + + domain); return false; } - if (HashesIntersect(bad_static_spki_hashes, hashes)) { - LOG(ERROR) << "Rejecting public key chain for domain " << domain - << ". Validated chain: " << HashesToBase64String(hashes) - << ", matches one or more bad hashes: " - << HashesToBase64String(bad_static_spki_hashes); + if (HashesIntersect(pkp.bad_spki_hashes, hashes)) { + failure_log->append("Rejecting public key chain for domain " + domain + + ". Validated chain: " + HashesToBase64String(hashes) + + ", matches one or more bad hashes: " + + HashesToBase64String(pkp.bad_spki_hashes)); return false; } // If there are no pins, then any valid chain is acceptable. - if (dynamic_spki_hashes.empty() && static_spki_hashes.empty()) + if (pkp.spki_hashes.empty()) return true; - if (HashesIntersect(dynamic_spki_hashes, hashes) || - HashesIntersect(static_spki_hashes, hashes)) { + if (HashesIntersect(pkp.spki_hashes, hashes)) { return true; } - LOG(ERROR) << "Rejecting public key chain for domain " << domain - << ". Validated chain: " << HashesToBase64String(hashes) - << ", expected: " << HashesToBase64String(dynamic_spki_hashes) - << " or: " << HashesToBase64String(static_spki_hashes); + failure_log->append("Rejecting public key chain for domain " + domain + + ". Validated chain: " + HashesToBase64String(hashes) + + ", expected: " + HashesToBase64String(pkp.spki_hashes)); return false; } bool TransportSecurityState::DomainState::ShouldUpgradeToSSL() const { - return upgrade_mode == MODE_FORCE_HTTPS; + return sts.upgrade_mode == MODE_FORCE_HTTPS; } bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { @@ -879,9 +896,13 @@ bool TransportSecurityState::DomainState::ShouldSSLErrorsBeFatal() const { } bool TransportSecurityState::DomainState::HasPublicKeyPins() const { - return static_spki_hashes.size() > 0 || - bad_static_spki_hashes.size() > 0 || - dynamic_spki_hashes.size() > 0; + return pkp.spki_hashes.size() > 0 || pkp.bad_spki_hashes.size() > 0; +} + +TransportSecurityState::DomainState::PKPState::PKPState() { +} + +TransportSecurityState::DomainState::PKPState::~PKPState() { } } // namespace diff --git a/chromium/net/http/transport_security_state.h b/chromium/net/http/transport_security_state.h index 97b4d7c1fc8..36459379145 100644 --- a/chromium/net/http/transport_security_state.h +++ b/chromium/net/http/transport_security_state.h @@ -61,6 +61,44 @@ class NET_EXPORT TransportSecurityState DomainState(); ~DomainState(); + struct STSState { + // The absolute time (UTC) when the |upgrade_mode| (and other state) was + // observed. + base::Time last_observed; + + // The absolute time (UTC) when the |upgrade_mode|, if set to + // MODE_FORCE_HTTPS, downgrades to MODE_DEFAULT. + base::Time expiry; + + UpgradeMode upgrade_mode; + + // Are subdomains subject to this policy state? + bool include_subdomains; + }; + + struct PKPState { + PKPState(); + ~PKPState(); + + // The absolute time (UTC) when the |spki_hashes| (and other state) were + // observed. + base::Time last_observed; + + // The absolute time (UTC) when the |spki_hashes| expire. + base::Time expiry; + + // Optional; hashes of pinned SubjectPublicKeyInfos. + HashValueVector spki_hashes; + + // Optional; hashes of static known-bad SubjectPublicKeyInfos which MUST + // NOT intersect with the set of SPKIs in the TLS server's certificate + // chain. + HashValueVector bad_spki_hashes; + + // Are subdomains subject to this policy state? + bool include_subdomains; + }; + // Takes a set of SubjectPublicKeyInfo |hashes| and returns true if: // 1) |bad_static_spki_hashes| does not intersect |hashes|; AND // 2) Both |static_spki_hashes| and |dynamic_spki_hashes| are empty @@ -77,67 +115,29 @@ class NET_EXPORT TransportSecurityState // // |bad_static_spki_hashes| contains public keys that we don't want to // trust. - bool CheckPublicKeyPins(const HashValueVector& hashes) const; + bool CheckPublicKeyPins(const HashValueVector& hashes, + std::string* failure_log) const; // Returns true if any of the HashValueVectors |static_spki_hashes|, // |bad_static_spki_hashes|, or |dynamic_spki_hashes| contains any // items. bool HasPublicKeyPins() const; - // ShouldUpgradeToSSL returns true iff, given the |mode| of this - // DomainState, HTTP requests should be internally redirected to HTTPS - // (also if the "ws" WebSocket request should be upgraded to "wss") + // ShouldUpgradeToSSL returns true iff HTTP requests should be internally + // redirected to HTTPS (also if WS should be upgraded to WSS). bool ShouldUpgradeToSSL() const; // ShouldSSLErrorsBeFatal returns true iff HTTPS errors should cause - // hard-fail behavior (e.g. if HSTS is set for the domain) + // hard-fail behavior (e.g. if HSTS is set for the domain). bool ShouldSSLErrorsBeFatal() const; - UpgradeMode upgrade_mode; - - // The absolute time (UTC) when this DomainState was first created. - // - // Static entries do not have a created time. - base::Time created; - - // The absolute time (UTC) when the |upgrade_mode|, if set to - // UPGRADE_ALWAYS, downgrades to UPGRADE_NEVER. - base::Time upgrade_expiry; - - // Are subdomains subject to this DomainState, for the purposes of - // upgrading to HTTPS? - bool sts_include_subdomains; - - // Are subdomains subject to this DomainState, for the purposes of - // Pin Validation? - bool pkp_include_subdomains; - - // Optional; hashes of static pinned SubjectPublicKeyInfos. Unless both - // are empty, at least one of |static_spki_hashes| and - // |dynamic_spki_hashes| MUST intersect with the set of SPKIs in the TLS - // server's certificate chain. - // - // |dynamic_spki_hashes| take precedence over |static_spki_hashes|. - // That is, |IsChainOfPublicKeysPermitted| first checks dynamic pins and - // then checks static pins. - HashValueVector static_spki_hashes; - - // Optional; hashes of dynamically pinned SubjectPublicKeyInfos. - HashValueVector dynamic_spki_hashes; - - // The absolute time (UTC) when the |dynamic_spki_hashes| expire. - base::Time dynamic_spki_hashes_expiry; - - // Optional; hashes of static known-bad SubjectPublicKeyInfos which - // MUST NOT intersect with the set of SPKIs in the TLS server's - // certificate chain. - HashValueVector bad_static_spki_hashes; + STSState sts; + PKPState pkp; // The following members are not valid when stored in |enabled_hosts_|: // The domain which matched during a search for this DomainState entry. - // Updated by |GetDomainState|, |GetDynamicDomainState|, and - // |GetStaticDomainState|. + // Updated by |GetDynamicDomainState| and |GetStaticDomainState|. std::string domain; }; @@ -156,6 +156,17 @@ class NET_EXPORT TransportSecurityState std::map<std::string, DomainState>::const_iterator end_; }; + // These functions search for static and dynamic DomainStates, and invoke the + // functions of the same name on them. These functions are the primary public + // interface; direct access to DomainStates is best left to tests. + bool ShouldSSLErrorsBeFatal(const std::string& host, bool sni_enabled); + bool ShouldUpgradeToSSL(const std::string& host, bool sni_enabled); + bool CheckPublicKeyPins(const std::string& host, + bool sni_enabled, + const HashValueVector& hashes, + std::string* failure_log); + bool HasPublicKeyPins(const std::string& host, bool sni_enabled); + // Assign a |Delegate| for persisting the transport security state. If // |NULL|, state will not be persisted. The caller retains // ownership of |delegate|. @@ -195,20 +206,31 @@ class NET_EXPORT TransportSecurityState // the Delegate (if any). bool DeleteDynamicDataForHost(const std::string& host); - // Returns true and updates |*result| iff there is a DomainState for - // |host|. + // Returns true and updates |*result| iff there is a static (built-in) + // DomainState for |host|. // - // If |sni_enabled| is true, searches the static pins defined for - // SNI-using hosts as well as the rest of the pins. + // If |sni_enabled| is true, searches the static pins defined for SNI-using + // hosts as well as the rest of the pins. // - // If |host| matches both an exact entry and is a subdomain of another - // entry, the exact match determines the return value. + // If |host| matches both an exact entry and is a subdomain of another entry, + // the exact match determines the return value. // // Note that this method is not const because it opportunistically removes // entries that have expired. - bool GetDomainState(const std::string& host, - bool sni_enabled, - DomainState* result); + bool GetStaticDomainState(const std::string& host, + bool sni_enabled, + DomainState* result) const; + + // Returns true and updates |*result| iff there is a dynamic DomainState + // (learned from HSTS or HPKP headers, or set by the user, or other means) for + // |host|. + // + // If |host| matches both an exact entry and is a subdomain of another entry, + // the exact match determines the return value. + // + // Note that this method is not const because it opportunistically removes + // entries that have expired. + bool GetDynamicDomainState(const std::string& host, DomainState* result); // Processes an HSTS header value from the host, adding entries to // dynamic state if necessary. @@ -282,39 +304,6 @@ class NET_EXPORT TransportSecurityState // the result. static std::string CanonicalizeHost(const std::string& hostname); - // Returns true and updates |*result| iff there is a static DomainState for - // |host|. - // - // |GetStaticDomainState| is identical to |GetDomainState| except that it - // searches only the statically-defined transport security state, ignoring - // all dynamically-added DomainStates. - // - // If |sni_enabled| is true, searches the static pins defined for - // SNI-using hosts as well as the rest of the pins. - // - // If |host| matches both an exact entry and is a subdomain of another - // entry, the exact match determines the return value. - // - // Note that this method is not const because it opportunistically removes - // entries that have expired. - bool GetStaticDomainState(const std::string& host, - bool sni_enabled, - DomainState* result); - - // Returns true and updates |*result| iff there is a dynamic DomainState for - // |host|. - // - // |GetDynamicDomainState| is identical to |GetDomainState| except that it - // searches only the dynamically-added transport security state, ignoring - // all statically-defined DomainStates. - // - // If |host| matches both an exact entry and is a subdomain of another - // entry, the exact match determines the return value. - // - // Note that this method is not const because it opportunistically removes - // entries that have expired. - bool GetDynamicDomainState(const std::string& host, DomainState* result); - // The set of hosts that have enabled TransportSecurity. DomainStateMap enabled_hosts_; diff --git a/chromium/net/http/transport_security_state_static.h b/chromium/net/http/transport_security_state_static.h index 6a28ca4562b..3723deafb3d 100644 --- a/chromium/net/http/transport_security_state_static.h +++ b/chromium/net/http/transport_security_state_static.h @@ -389,7 +389,7 @@ static const struct HSTSPreload kPreloadedSTS[] = { {17, true, "\004docs\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {18, true, "\005sites\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {25, true, "\014spreadsheets\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {22, false, "\011appengine\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {22, true, "\011appengine\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {22, true, "\011encrypted\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {21, true, "\010accounts\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {21, true, "\010profiles\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, @@ -405,6 +405,7 @@ static const struct HSTSPreload kPreloadedSTS[] = { {17, true, "\004goto\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {18, true, "\005cloud\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {18, true, "\005glass\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {18, true, "\005admin\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {17, false, "\004play\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {20, true, "\006market\007android\003com", true, kGooglePins, DOMAIN_ANDROID_COM }, {26, true, "\003ssl\020google-analytics\003com", true, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM }, @@ -420,6 +421,21 @@ static const struct HSTSPreload kPreloadedSTS[] = { {16, true, "\012googlecode\003com", false, kGooglePins, DOMAIN_GOOGLECODE_COM }, {15, true, "\002dl\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, {26, true, "\011translate\012googleapis\003com", true, kGooglePins, DOMAIN_GOOGLEAPIS_COM }, + {24, true, "\012webfilings\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {35, true, "\025webfilings-mirror-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {27, true, "\015webfilings-eu\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {34, true, "\024webfilings-eu-mirror\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {24, true, "\012wf-demo-eu\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {25, true, "\013wf-demo-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {24, true, "\012wf-pentest\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {26, true, "\014wf-trial-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {25, true, "\013xbrlsuccess\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {25, true, "\013w-spotlight\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {29, true, "\017wf-training-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {30, true, "\020wf-bigsky-master\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {27, true, "\015wf-staging-hr\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {32, true, "\022wf-training-master\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {28, true, "\016wf-dogfood-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, {23, true, "\005chart\004apis\006google\003com", false, kGooglePins, DOMAIN_GOOGLE_COM }, {11, true, "\005ytimg\003com", false, kGooglePins, DOMAIN_YTIMG_COM }, {23, true, "\021googleusercontent\003com", false, kGooglePins, DOMAIN_GOOGLEUSERCONTENT_COM }, @@ -437,6 +453,7 @@ static const struct HSTSPreload kPreloadedSTS[] = { {8, true, "\003goo\002gl", false, kGooglePins, DOMAIN_GOO_GL }, {6, true, "\001g\002co", false, kGooglePins, DOMAIN_G_CO }, {22, true, "\020googletagmanager\003com", false, kGooglePins, DOMAIN_GOOGLETAGMANAGER_COM }, + {23, true, "\021googletagservices\003com", false, kGooglePins, DOMAIN_GOOGLETAGSERVICES_COM }, {11, true, "\006google\002ac", false, kGooglePins, DOMAIN_GOOGLE_AC }, {11, true, "\006google\002ad", false, kGooglePins, DOMAIN_GOOGLE_AD }, {11, true, "\006google\002ae", false, kGooglePins, DOMAIN_GOOGLE_AE }, @@ -647,7 +664,6 @@ static const struct HSTSPreload kPreloadedSTS[] = { {11, true, "\006google\002tm", false, kGooglePins, DOMAIN_GOOGLE_TM }, {11, true, "\006google\002tn", false, kGooglePins, DOMAIN_GOOGLE_TN }, {11, true, "\006google\002to", false, kGooglePins, DOMAIN_GOOGLE_TO }, - {11, true, "\006google\002tp", false, kGooglePins, DOMAIN_GOOGLE_TP }, {11, true, "\006google\002tt", false, kGooglePins, DOMAIN_GOOGLE_TT }, {11, true, "\006google\002us", false, kGooglePins, DOMAIN_GOOGLE_US }, {11, true, "\006google\002uz", false, kGooglePins, DOMAIN_GOOGLE_UZ }, @@ -760,7 +776,7 @@ static const struct HSTSPreload kPreloadedSTS[] = { {17, false, "\003www\010intercom\002io", true, kNoPins, DOMAIN_NOT_PINNED }, {17, true, "\010fatzebra\003com\002au", true, kNoPins, DOMAIN_NOT_PINNED }, {18, true, "\007csawctf\004poly\003edu", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED }, {22, false, "\003www\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED }, {16, true, "\003iop\006intuit\003com", true, kNoPins, DOMAIN_NOT_PINNED }, {14, false, "\010surfeasy\003com", true, kNoPins, DOMAIN_NOT_PINNED }, @@ -776,6 +792,12 @@ static const struct HSTSPreload kPreloadedSTS[] = { {17, true, "\003faq\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, {22, true, "\010platform\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, {19, true, "\005email\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003app\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003api\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\011keymaster\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\011discovery\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014mobilethreat\003net", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\023mobilethreatnetwork\003net", true, kNoPins, DOMAIN_NOT_PINNED }, {15, true, "\011itriskltd\003com", true, kNoPins, DOMAIN_NOT_PINNED }, {15, true, "\012stocktrade\002de", true, kNoPins, DOMAIN_NOT_PINNED }, {22, true, "\011openshift\006redhat\003com", true, kNoPins, DOMAIN_NOT_PINNED }, @@ -884,6 +906,91 @@ static const struct HSTSPreload kPreloadedSTS[] = { {14, true, "\003api\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED }, {9, true, "\003eff\003org", true, kNoPins, DOMAIN_NOT_PINNED }, {9, true, "\004mail\002de", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010passport\006yandex\002ru", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, false, "\010passport\006yandex\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010passport\006yandex\002ua", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010passport\006yandex\002by", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010passport\006yandex\002kz", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\010passport\006yandex\003com\002tr", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006mnsure\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\010getcloak\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003www\010getcloak\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\020matteomarescotti\004name", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\003www\011heliosnet\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007opsmate\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003www\007opsmate\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007f-droid\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003www\010evernote\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003app\010yinxiang\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011neilwynne\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\016calyxinstitute\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\003www\016calyxinstitute\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011blacklane\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012boxcryptor\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, false, "\004aclu\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\003www\004aclu\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007prodpad\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007mailbox\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006roddis\003net", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\003www\006roddis\003net", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005fiken\002no", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010fairbill\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005nexth\003net", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005nexth\002us", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005nexth\002de", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006souyar\003net", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006souyar\002de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006souyar\002us", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, false, "\003www\007banking\002co\002at", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, false, "\003mbp\007banking\002co\002at", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007feedbin\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004heha\002co", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013passwordbox\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004pypi\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\003www\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004docs\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013encircleapp\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\010onedrive\004live\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010onedrive\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016keepersecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011keeperapp\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006donmez\002ws", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\010activiti\010alfresco\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011cloudcert\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010seifried\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, false, "\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\003www\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\006static\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\005stage\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011vmoagents\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007adsfund\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, false, "\004pult\002co", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014dillonkorman\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006edmodo\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\003www\013eternalgoth\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003app\007manilla\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012harvestapp\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007anycoin\002me", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010noexpect\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006airbnb\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\003www\006airbnb\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, false, "\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\003www\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\006mobile\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007subrosa\002io", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011detectify\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005crbug\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016manageprojects\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, false, "\017tinfoilsecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, false, "\003www\017tinfoilsecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, false, "\006imouto\002my", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010vocaloid\002my", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\006sakaki\005anime\002my", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\007reviews\005anime\002my", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004miku\007hatsune\002my", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\012webcollect\003org\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\003www\016capitainetrain\003com", true, kNoPins, DOMAIN_NOT_PINNED }, }; static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS); @@ -894,6 +1001,8 @@ static const struct HSTSPreload kPreloadedSNISTS[] = { {20, false, "\003www\012googlemail\003com", true, kGooglePins, DOMAIN_GOOGLEMAIL_COM }, {22, true, "\020google-analytics\003com", false, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM }, {18, true, "\014googlegroups\003com", false, kGooglePins, DOMAIN_GOOGLEGROUPS_COM }, + {13, true, "\007mykolab\003com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013semenkovich\003com", true, kNoPins, DOMAIN_NOT_PINNED }, }; static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS); diff --git a/chromium/net/http/transport_security_state_static.json b/chromium/net/http/transport_security_state_static.json index 500e1782110..35c14248a68 100644 --- a/chromium/net/http/transport_security_state_static.json +++ b/chromium/net/http/transport_security_state_static.json @@ -168,7 +168,7 @@ { "name": "docs.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "sites.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "spreadsheets.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, - { "name": "appengine.google.com", "mode": "force-https", "pins": "google" }, + { "name": "appengine.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "encrypted.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "accounts.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "profiles.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, @@ -184,6 +184,7 @@ { "name": "goto.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "cloud.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "glass.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "admin.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, // play.google.com doesn't have include_subdomains because of crbug.com/327834. { "name": "play.google.com", "mode": "force-https", "pins": "google" }, @@ -203,6 +204,22 @@ { "name": "dl.google.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, { "name": "translate.googleapis.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "webfilings.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "webfilings-mirror-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "webfilings-eu.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "webfilings-eu-mirror.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-demo-eu.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-demo-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-pentest.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-trial-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "xbrlsuccess.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "w-spotlight.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-training-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-bigsky-master.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-staging-hr.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-training-master.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + { "name": "wf-dogfood-hrd.appspot.com", "include_subdomains": true, "mode": "force-https", "pins": "google" }, + // chart.apis.google.com is *not* HSTS because the certificate doesn't match // and there are lots of links out there that still use the name. The correct // hostname for this is chart.googleapis.com. @@ -226,6 +243,7 @@ { "name": "goo.gl", "include_subdomains": true, "pins": "google" }, { "name": "g.co", "include_subdomains": true, "pins": "google" }, { "name": "googletagmanager.com", "include_subdomains": true, "pins": "google" }, + { "name": "googletagservices.com", "include_subdomains": true, "pins": "google" }, { "name": "google.ac", "include_subdomains": true, "pins": "google" }, { "name": "google.ad", "include_subdomains": true, "pins": "google" }, { "name": "google.ae", "include_subdomains": true, "pins": "google" }, @@ -436,7 +454,6 @@ { "name": "google.tm", "include_subdomains": true, "pins": "google" }, { "name": "google.tn", "include_subdomains": true, "pins": "google" }, { "name": "google.to", "include_subdomains": true, "pins": "google" }, - { "name": "google.tp", "include_subdomains": true, "pins": "google" }, { "name": "google.tt", "include_subdomains": true, "pins": "google" }, { "name": "google.us", "include_subdomains": true, "pins": "google" }, { "name": "google.uz", "include_subdomains": true, "pins": "google" }, @@ -553,7 +570,7 @@ { "name": "www.intercom.io", "mode": "force-https" }, { "name": "fatzebra.com.au", "include_subdomains": true, "mode": "force-https" }, { "name": "csawctf.poly.edu", "include_subdomains": true, "mode": "force-https" }, - { "name": "makeyourlaws.org", "mode": "force-https" }, + { "name": "makeyourlaws.org", "include_subdomains": true, "mode": "force-https" }, { "name": "www.makeyourlaws.org", "mode": "force-https" }, { "name": "iop.intuit.com", "include_subdomains": true, "mode": "force-https" }, { "name": "surfeasy.com", "mode": "force-https" }, @@ -569,6 +586,12 @@ { "name": "faq.lookout.com", "include_subdomains": true, "mode": "force-https" }, { "name": "platform.lookout.com", "include_subdomains": true, "mode": "force-https" }, { "name": "email.lookout.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "app.lookout.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "api.lookout.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "keymaster.lookout.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "discovery.lookout.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "mobilethreat.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "mobilethreatnetwork.net", "include_subdomains": true, "mode": "force-https" }, { "name": "itriskltd.com", "include_subdomains": true, "mode": "force-https" }, { "name": "stocktrade.de", "include_subdomains": true, "mode": "force-https" }, { "name": "openshift.redhat.com", "include_subdomains": true, "mode": "force-https" }, @@ -677,6 +700,91 @@ { "name": "api.xero.com", "include_subdomains": true, "mode": "force-https" }, { "name": "eff.org", "include_subdomains": true, "mode": "force-https" }, { "name": "mail.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "passport.yandex.ru", "mode": "force-https" }, + { "name": "passport.yandex.com", "mode": "force-https" }, + { "name": "passport.yandex.ua", "mode": "force-https" }, + { "name": "passport.yandex.by", "mode": "force-https" }, + { "name": "passport.yandex.kz", "mode": "force-https" }, + { "name": "passport.yandex.com.tr", "mode": "force-https" }, + { "name": "mnsure.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "getcloak.com", "mode": "force-https" }, + { "name": "www.getcloak.com", "mode": "force-https" }, + { "name": "matteomarescotti.name", "include_subdomains": true, "mode": "force-https" }, + { "name": "www.heliosnet.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "opsmate.com", "mode": "force-https" }, + { "name": "www.opsmate.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "f-droid.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "www.evernote.com", "mode": "force-https" }, + { "name": "app.yinxiang.com", "mode": "force-https" }, + { "name": "neilwynne.com", "mode": "force-https" }, + { "name": "calyxinstitute.org", "mode": "force-https" }, + { "name": "www.calyxinstitute.org", "mode": "force-https" }, + { "name": "blacklane.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "boxcryptor.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "aclu.org", "mode": "force-https" }, + { "name": "www.aclu.org", "mode": "force-https" }, + { "name": "prodpad.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "mailbox.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "roddis.net", "mode": "force-https" }, + { "name": "www.roddis.net", "mode": "force-https" }, + { "name": "fiken.no", "include_subdomains": true, "mode": "force-https" }, + { "name": "fairbill.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "nexth.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "nexth.us", "include_subdomains": true, "mode": "force-https" }, + { "name": "nexth.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "souyar.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "souyar.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "souyar.us", "include_subdomains": true, "mode": "force-https" }, + { "name": "www.banking.co.at", "mode": "force-https" }, + { "name": "mbp.banking.co.at", "mode": "force-https" }, + { "name": "feedbin.com", "mode": "force-https" }, + { "name": "heha.co", "include_subdomains": true, "mode": "force-https" }, + { "name": "passwordbox.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "python.org", "mode": "force-https" }, + { "name": "pypi.python.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "www.python.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "docs.python.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "encircleapp.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "onedrive.live.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "onedrive.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "keepersecurity.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "keeperapp.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "donmez.ws", "include_subdomains": true, "mode": "force-https" }, + { "name": "activiti.alfresco.com", "mode": "force-https" }, + { "name": "cloudcert.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "seifried.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "wepay.com", "mode": "force-https" }, + { "name": "www.wepay.com", "mode": "force-https" }, + { "name": "static.wepay.com", "mode": "force-https" }, + { "name": "stage.wepay.com", "mode": "force-https" }, + { "name": "vmoagents.com", "mode": "force-https" }, + { "name": "adsfund.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "pult.co", "mode": "force-https" }, + { "name": "dillonkorman.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "edmodo.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "www.eternalgoth.co.uk", "mode": "force-https" }, + { "name": "app.manilla.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "harvestapp.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "anycoin.me", "include_subdomains": true, "mode": "force-https" }, + { "name": "noexpect.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "airbnb.com", "mode": "force-https" }, + { "name": "www.airbnb.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "usaa.com", "mode": "force-https" }, + { "name": "www.usaa.com", "mode": "force-https" }, + { "name": "mobile.usaa.com", "mode": "force-https" }, + { "name": "subrosa.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "detectify.com", "mode": "force-https" }, + { "name": "crbug.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "manageprojects.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "tinfoilsecurity.com", "mode": "force-https" }, + { "name": "www.tinfoilsecurity.com", "mode": "force-https" }, + { "name": "imouto.my", "mode": "force-https" }, + { "name": "vocaloid.my", "include_subdomains": true, "mode": "force-https" }, + { "name": "sakaki.anime.my", "include_subdomains": true, "mode": "force-https" }, + { "name": "reviews.anime.my", "include_subdomains": true, "mode": "force-https" }, + { "name": "miku.hatsune.my", "include_subdomains": true, "mode": "force-https" }, + { "name": "webcollect.org.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "www.capitainetrain.com", "mode": "force-https" }, // Entries that are only valid if the client supports SNI. { "name": "gmail.com", "mode": "force-https", "pins": "google", "snionly": true }, @@ -684,6 +792,8 @@ { "name": "www.gmail.com", "mode": "force-https", "pins": "google", "snionly": true }, { "name": "www.googlemail.com", "mode": "force-https", "pins": "google", "snionly": true }, { "name": "google-analytics.com", "include_subdomains": true, "pins": "google", "snionly": true }, - { "name": "googlegroups.com", "include_subdomains": true, "pins": "google", "snionly": true } + { "name": "googlegroups.com", "include_subdomains": true, "pins": "google", "snionly": true }, + { "name": "mykolab.com", "include_subdomains": true, "mode": "force-https", "snionly": true }, + { "name": "semenkovich.com", "include_subdomains": true, "mode": "force-https", "snionly": true } ] } diff --git a/chromium/net/http/transport_security_state_unittest.cc b/chromium/net/http/transport_security_state_unittest.cc index c3d15da29f9..476ae94bd68 100644 --- a/chromium/net/http/transport_security_state_unittest.cc +++ b/chromium/net/http/transport_security_state_unittest.cc @@ -46,10 +46,6 @@ class TransportSecurityStateTest : public testing::Test { } protected: - std::string CanonicalizeHost(const std::string& host) { - return TransportSecurityState::CanonicalizeHost(host); - } - bool GetStaticDomainState(TransportSecurityState* state, const std::string& host, bool sni_enabled, @@ -70,10 +66,10 @@ TEST_F(TransportSecurityStateTest, SimpleMatches) { const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); - EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); bool include_subdomains = false; state.AddHSTS("yahoo.com", expiry, include_subdomains); - EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state)); } TEST_F(TransportSecurityStateTest, MatchesCase1) { @@ -82,10 +78,10 @@ TEST_F(TransportSecurityStateTest, MatchesCase1) { const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); - EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); bool include_subdomains = false; state.AddHSTS("YAhoo.coM", expiry, include_subdomains); - EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state)); } TEST_F(TransportSecurityStateTest, MatchesCase2) { @@ -94,10 +90,10 @@ TEST_F(TransportSecurityStateTest, MatchesCase2) { const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); - EXPECT_FALSE(state.GetDomainState("YAhoo.coM", true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("YAhoo.coM", &domain_state)); bool include_subdomains = false; state.AddHSTS("yahoo.com", expiry, include_subdomains); - EXPECT_TRUE(state.GetDomainState("YAhoo.coM", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("YAhoo.coM", &domain_state)); } TEST_F(TransportSecurityStateTest, SubdomainMatches) { @@ -106,15 +102,15 @@ TEST_F(TransportSecurityStateTest, SubdomainMatches) { const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); - EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); bool include_subdomains = true; state.AddHSTS("yahoo.com", expiry, include_subdomains); - EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state)); - EXPECT_TRUE(state.GetDomainState("foo.yahoo.com", true, &domain_state)); - EXPECT_TRUE(state.GetDomainState("foo.bar.yahoo.com", true, &domain_state)); - EXPECT_TRUE(state.GetDomainState("foo.bar.baz.yahoo.com", true, - &domain_state)); - EXPECT_FALSE(state.GetDomainState("com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("foo.yahoo.com", &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("foo.bar.yahoo.com", &domain_state)); + EXPECT_TRUE( + state.GetDynamicDomainState("foo.bar.baz.yahoo.com", &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("com", &domain_state)); } TEST_F(TransportSecurityStateTest, InvalidDomains) { @@ -123,11 +119,12 @@ TEST_F(TransportSecurityStateTest, InvalidDomains) { const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); - EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); bool include_subdomains = true; state.AddHSTS("yahoo.com", expiry, include_subdomains); - EXPECT_TRUE(state.GetDomainState("www-.foo.yahoo.com", true, &domain_state)); - EXPECT_TRUE(state.GetDomainState("2\x01.foo.yahoo.com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("www-.foo.yahoo.com", &domain_state)); + EXPECT_TRUE( + state.GetDynamicDomainState("2\x01.foo.yahoo.com", &domain_state)); } TEST_F(TransportSecurityStateTest, DeleteAllDynamicDataSince) { @@ -137,14 +134,18 @@ TEST_F(TransportSecurityStateTest, DeleteAllDynamicDataSince) { const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); const base::Time older = current_time - base::TimeDelta::FromSeconds(1000); - EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); bool include_subdomains = false; state.AddHSTS("yahoo.com", expiry, include_subdomains); state.DeleteAllDynamicDataSince(expiry); - EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state)); + EXPECT_EQ(TransportSecurityState::DomainState::MODE_FORCE_HTTPS, + domain_state.sts.upgrade_mode); state.DeleteAllDynamicDataSince(older); - EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state)); + EXPECT_EQ(TransportSecurityState::DomainState::MODE_DEFAULT, + domain_state.sts.upgrade_mode); } TEST_F(TransportSecurityStateTest, DeleteDynamicDataForHost) { @@ -155,28 +156,28 @@ TEST_F(TransportSecurityStateTest, DeleteDynamicDataForHost) { bool include_subdomains = false; state.AddHSTS("yahoo.com", expiry, include_subdomains); - EXPECT_TRUE(state.GetDomainState("yahoo.com", true, &domain_state)); - EXPECT_FALSE(state.GetDomainState("example.com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("yahoo.com", &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("example.com", &domain_state)); EXPECT_TRUE(state.DeleteDynamicDataForHost("yahoo.com")); - EXPECT_FALSE(state.GetDomainState("yahoo.com", true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); } TEST_F(TransportSecurityStateTest, IsPreloaded) { - const std::string paypal = CanonicalizeHost("paypal.com"); - const std::string www_paypal = CanonicalizeHost("www.paypal.com"); - const std::string foo_paypal = CanonicalizeHost("foo.paypal.com"); - const std::string a_www_paypal = CanonicalizeHost("a.www.paypal.com"); - const std::string abc_paypal = CanonicalizeHost("a.b.c.paypal.com"); - const std::string example = CanonicalizeHost("example.com"); - const std::string aypal = CanonicalizeHost("aypal.com"); + const std::string paypal = "paypal.com"; + const std::string www_paypal = "www.paypal.com"; + const std::string foo_paypal = "foo.paypal.com"; + const std::string a_www_paypal = "a.www.paypal.com"; + const std::string abc_paypal = "a.b.c.paypal.com"; + const std::string example = "example.com"; + const std::string aypal = "aypal.com"; TransportSecurityState state; TransportSecurityState::DomainState domain_state; EXPECT_TRUE(GetStaticDomainState(&state, paypal, true, &domain_state)); EXPECT_TRUE(GetStaticDomainState(&state, www_paypal, true, &domain_state)); - EXPECT_FALSE(domain_state.sts_include_subdomains); - EXPECT_FALSE(domain_state.pkp_include_subdomains); + EXPECT_FALSE(domain_state.sts.include_subdomains); + EXPECT_FALSE(domain_state.pkp.include_subdomains); EXPECT_FALSE(GetStaticDomainState(&state, a_www_paypal, true, &domain_state)); EXPECT_FALSE(GetStaticDomainState(&state, abc_paypal, true, &domain_state)); EXPECT_FALSE(GetStaticDomainState(&state, example, true, &domain_state)); @@ -189,48 +190,49 @@ TEST_F(TransportSecurityStateTest, PreloadedDomainSet) { // The domain wasn't being set, leading to a blank string in the // chrome://net-internals/#hsts UI. So test that. - EXPECT_TRUE(state.GetDomainState("market.android.com", true, &domain_state)); + EXPECT_TRUE( + state.GetStaticDomainState("market.android.com", true, &domain_state)); EXPECT_EQ(domain_state.domain, "market.android.com"); - EXPECT_TRUE(state.GetDomainState("sub.market.android.com", true, - &domain_state)); + EXPECT_TRUE(state.GetStaticDomainState( + "sub.market.android.com", true, &domain_state)); EXPECT_EQ(domain_state.domain, "market.android.com"); } -static bool ShouldRedirect(const char* hostname) { +static bool StaticShouldRedirect(const char* hostname) { TransportSecurityState state; TransportSecurityState::DomainState domain_state; - return state.GetDomainState(hostname, true /* SNI ok */, &domain_state) && + return state.GetStaticDomainState( + hostname, true /* SNI ok */, &domain_state) && domain_state.ShouldUpgradeToSSL(); } -static bool HasState(const char* hostname) { +static bool HasStaticState(const char* hostname) { TransportSecurityState state; TransportSecurityState::DomainState domain_state; - return state.GetDomainState(hostname, true /* SNI ok */, &domain_state); + return state.GetStaticDomainState(hostname, true /* SNI ok */, &domain_state); } -static bool HasPublicKeyPins(const char* hostname, bool sni_enabled) { +static bool HasStaticPublicKeyPins(const char* hostname, bool sni_enabled) { TransportSecurityState state; TransportSecurityState::DomainState domain_state; - if (!state.GetDomainState(hostname, sni_enabled, &domain_state)) + if (!state.GetStaticDomainState(hostname, sni_enabled, &domain_state)) return false; return domain_state.HasPublicKeyPins(); } -static bool HasPublicKeyPins(const char* hostname) { - return HasPublicKeyPins(hostname, true); +static bool HasStaticPublicKeyPins(const char* hostname) { + return HasStaticPublicKeyPins(hostname, true); } -static bool OnlyPinning(const char *hostname) { +static bool OnlyPinningInStaticState(const char* hostname) { TransportSecurityState state; TransportSecurityState::DomainState domain_state; - if (!state.GetDomainState(hostname, true /* SNI ok */, &domain_state)) + if (!state.GetStaticDomainState(hostname, true /* SNI ok */, &domain_state)) return false; - return (domain_state.static_spki_hashes.size() > 0 || - domain_state.bad_static_spki_hashes.size() > 0 || - domain_state.dynamic_spki_hashes.size() > 0) && + return (domain_state.pkp.spki_hashes.size() > 0 || + domain_state.pkp.bad_spki_hashes.size() > 0) && !domain_state.ShouldUpgradeToSSL(); } @@ -239,240 +241,245 @@ TEST_F(TransportSecurityStateTest, Preloaded) { TransportSecurityState::DomainState domain_state; // We do more extensive checks for the first domain. - EXPECT_TRUE(state.GetDomainState("www.paypal.com", true, &domain_state)); - EXPECT_EQ(domain_state.upgrade_mode, + EXPECT_TRUE( + state.GetStaticDomainState("www.paypal.com", true, &domain_state)); + EXPECT_EQ(domain_state.sts.upgrade_mode, TransportSecurityState::DomainState::MODE_FORCE_HTTPS); - EXPECT_FALSE(domain_state.sts_include_subdomains); - EXPECT_FALSE(domain_state.pkp_include_subdomains); + EXPECT_FALSE(domain_state.sts.include_subdomains); + EXPECT_FALSE(domain_state.pkp.include_subdomains); - EXPECT_TRUE(HasState("paypal.com")); - EXPECT_FALSE(HasState("www2.paypal.com")); - EXPECT_FALSE(HasState("www2.paypal.com")); + EXPECT_TRUE(HasStaticState("paypal.com")); + EXPECT_FALSE(HasStaticState("www2.paypal.com")); + EXPECT_FALSE(HasStaticState("www2.paypal.com")); // Google hosts: - EXPECT_TRUE(ShouldRedirect("chrome.google.com")); - EXPECT_TRUE(ShouldRedirect("checkout.google.com")); - EXPECT_TRUE(ShouldRedirect("wallet.google.com")); - EXPECT_TRUE(ShouldRedirect("docs.google.com")); - EXPECT_TRUE(ShouldRedirect("sites.google.com")); - EXPECT_TRUE(ShouldRedirect("drive.google.com")); - EXPECT_TRUE(ShouldRedirect("spreadsheets.google.com")); - EXPECT_TRUE(ShouldRedirect("appengine.google.com")); - EXPECT_TRUE(ShouldRedirect("market.android.com")); - EXPECT_TRUE(ShouldRedirect("encrypted.google.com")); - EXPECT_TRUE(ShouldRedirect("accounts.google.com")); - EXPECT_TRUE(ShouldRedirect("profiles.google.com")); - EXPECT_TRUE(ShouldRedirect("mail.google.com")); - EXPECT_TRUE(ShouldRedirect("chatenabled.mail.google.com")); - EXPECT_TRUE(ShouldRedirect("talkgadget.google.com")); - EXPECT_TRUE(ShouldRedirect("hostedtalkgadget.google.com")); - EXPECT_TRUE(ShouldRedirect("talk.google.com")); - EXPECT_TRUE(ShouldRedirect("plus.google.com")); - EXPECT_TRUE(ShouldRedirect("groups.google.com")); - EXPECT_TRUE(ShouldRedirect("apis.google.com")); - EXPECT_FALSE(ShouldRedirect("chart.apis.google.com")); - EXPECT_TRUE(ShouldRedirect("ssl.google-analytics.com")); - EXPECT_TRUE(ShouldRedirect("gmail.com")); - EXPECT_TRUE(ShouldRedirect("www.gmail.com")); - EXPECT_TRUE(ShouldRedirect("googlemail.com")); - EXPECT_TRUE(ShouldRedirect("www.googlemail.com")); - EXPECT_TRUE(ShouldRedirect("googleplex.com")); - EXPECT_TRUE(ShouldRedirect("www.googleplex.com")); - EXPECT_FALSE(HasState("m.gmail.com")); - EXPECT_FALSE(HasState("m.googlemail.com")); - - EXPECT_TRUE(OnlyPinning("www.google.com")); - EXPECT_TRUE(OnlyPinning("foo.google.com")); - EXPECT_TRUE(OnlyPinning("google.com")); - EXPECT_TRUE(OnlyPinning("www.youtube.com")); - EXPECT_TRUE(OnlyPinning("youtube.com")); - EXPECT_TRUE(OnlyPinning("i.ytimg.com")); - EXPECT_TRUE(OnlyPinning("ytimg.com")); - EXPECT_TRUE(OnlyPinning("googleusercontent.com")); - EXPECT_TRUE(OnlyPinning("www.googleusercontent.com")); - EXPECT_TRUE(OnlyPinning("www.google-analytics.com")); - EXPECT_TRUE(OnlyPinning("googleapis.com")); - EXPECT_TRUE(OnlyPinning("googleadservices.com")); - EXPECT_TRUE(OnlyPinning("googlecode.com")); - EXPECT_TRUE(OnlyPinning("appspot.com")); - EXPECT_TRUE(OnlyPinning("googlesyndication.com")); - EXPECT_TRUE(OnlyPinning("doubleclick.net")); - EXPECT_TRUE(OnlyPinning("googlegroups.com")); + EXPECT_TRUE(StaticShouldRedirect("chrome.google.com")); + EXPECT_TRUE(StaticShouldRedirect("checkout.google.com")); + EXPECT_TRUE(StaticShouldRedirect("wallet.google.com")); + EXPECT_TRUE(StaticShouldRedirect("docs.google.com")); + EXPECT_TRUE(StaticShouldRedirect("sites.google.com")); + EXPECT_TRUE(StaticShouldRedirect("drive.google.com")); + EXPECT_TRUE(StaticShouldRedirect("spreadsheets.google.com")); + EXPECT_TRUE(StaticShouldRedirect("appengine.google.com")); + EXPECT_TRUE(StaticShouldRedirect("market.android.com")); + EXPECT_TRUE(StaticShouldRedirect("encrypted.google.com")); + EXPECT_TRUE(StaticShouldRedirect("accounts.google.com")); + EXPECT_TRUE(StaticShouldRedirect("profiles.google.com")); + EXPECT_TRUE(StaticShouldRedirect("mail.google.com")); + EXPECT_TRUE(StaticShouldRedirect("chatenabled.mail.google.com")); + EXPECT_TRUE(StaticShouldRedirect("talkgadget.google.com")); + EXPECT_TRUE(StaticShouldRedirect("hostedtalkgadget.google.com")); + EXPECT_TRUE(StaticShouldRedirect("talk.google.com")); + EXPECT_TRUE(StaticShouldRedirect("plus.google.com")); + EXPECT_TRUE(StaticShouldRedirect("groups.google.com")); + EXPECT_TRUE(StaticShouldRedirect("apis.google.com")); + EXPECT_FALSE(StaticShouldRedirect("chart.apis.google.com")); + EXPECT_TRUE(StaticShouldRedirect("ssl.google-analytics.com")); + EXPECT_TRUE(StaticShouldRedirect("gmail.com")); + EXPECT_TRUE(StaticShouldRedirect("www.gmail.com")); + EXPECT_TRUE(StaticShouldRedirect("googlemail.com")); + EXPECT_TRUE(StaticShouldRedirect("www.googlemail.com")); + EXPECT_TRUE(StaticShouldRedirect("googleplex.com")); + EXPECT_TRUE(StaticShouldRedirect("www.googleplex.com")); + EXPECT_FALSE(HasStaticState("m.gmail.com")); + EXPECT_FALSE(HasStaticState("m.googlemail.com")); + + EXPECT_TRUE(OnlyPinningInStaticState("www.google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("foo.google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.youtube.com")); + EXPECT_TRUE(OnlyPinningInStaticState("youtube.com")); + EXPECT_TRUE(OnlyPinningInStaticState("i.ytimg.com")); + EXPECT_TRUE(OnlyPinningInStaticState("ytimg.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleusercontent.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.googleusercontent.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.google-analytics.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleapis.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleadservices.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googlecode.com")); + EXPECT_TRUE(OnlyPinningInStaticState("appspot.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googlesyndication.com")); + EXPECT_TRUE(OnlyPinningInStaticState("doubleclick.net")); + EXPECT_TRUE(OnlyPinningInStaticState("googlegroups.com")); // Tests for domains that don't work without SNI. - EXPECT_FALSE(state.GetDomainState("gmail.com", false, &domain_state)); - EXPECT_FALSE(state.GetDomainState("www.gmail.com", false, &domain_state)); - EXPECT_FALSE(state.GetDomainState("m.gmail.com", false, &domain_state)); - EXPECT_FALSE(state.GetDomainState("googlemail.com", false, &domain_state)); - EXPECT_FALSE(state.GetDomainState("www.googlemail.com", false, - &domain_state)); - EXPECT_FALSE(state.GetDomainState("m.googlemail.com", false, &domain_state)); + EXPECT_FALSE(state.GetStaticDomainState("gmail.com", false, &domain_state)); + EXPECT_FALSE( + state.GetStaticDomainState("www.gmail.com", false, &domain_state)); + EXPECT_FALSE(state.GetStaticDomainState("m.gmail.com", false, &domain_state)); + EXPECT_FALSE( + state.GetStaticDomainState("googlemail.com", false, &domain_state)); + EXPECT_FALSE( + state.GetStaticDomainState("www.googlemail.com", false, &domain_state)); + EXPECT_FALSE( + state.GetStaticDomainState("m.googlemail.com", false, &domain_state)); // Other hosts: - EXPECT_TRUE(ShouldRedirect("aladdinschools.appspot.com")); - - EXPECT_TRUE(ShouldRedirect("ottospora.nl")); - EXPECT_TRUE(ShouldRedirect("www.ottospora.nl")); + EXPECT_TRUE(StaticShouldRedirect("aladdinschools.appspot.com")); - EXPECT_TRUE(ShouldRedirect("www.paycheckrecords.com")); + EXPECT_TRUE(StaticShouldRedirect("ottospora.nl")); + EXPECT_TRUE(StaticShouldRedirect("www.ottospora.nl")); - EXPECT_TRUE(ShouldRedirect("lastpass.com")); - EXPECT_TRUE(ShouldRedirect("www.lastpass.com")); - EXPECT_FALSE(HasState("blog.lastpass.com")); + EXPECT_TRUE(StaticShouldRedirect("www.paycheckrecords.com")); - EXPECT_TRUE(ShouldRedirect("keyerror.com")); - EXPECT_TRUE(ShouldRedirect("www.keyerror.com")); + EXPECT_TRUE(StaticShouldRedirect("lastpass.com")); + EXPECT_TRUE(StaticShouldRedirect("www.lastpass.com")); + EXPECT_FALSE(HasStaticState("blog.lastpass.com")); - EXPECT_TRUE(ShouldRedirect("entropia.de")); - EXPECT_TRUE(ShouldRedirect("www.entropia.de")); - EXPECT_FALSE(HasState("foo.entropia.de")); + EXPECT_TRUE(StaticShouldRedirect("keyerror.com")); + EXPECT_TRUE(StaticShouldRedirect("www.keyerror.com")); - EXPECT_TRUE(ShouldRedirect("www.elanex.biz")); - EXPECT_FALSE(HasState("elanex.biz")); - EXPECT_FALSE(HasState("foo.elanex.biz")); + EXPECT_TRUE(StaticShouldRedirect("entropia.de")); + EXPECT_TRUE(StaticShouldRedirect("www.entropia.de")); + EXPECT_FALSE(HasStaticState("foo.entropia.de")); - EXPECT_TRUE(ShouldRedirect("sunshinepress.org")); - EXPECT_TRUE(ShouldRedirect("www.sunshinepress.org")); - EXPECT_TRUE(ShouldRedirect("a.b.sunshinepress.org")); + EXPECT_TRUE(StaticShouldRedirect("www.elanex.biz")); + EXPECT_FALSE(HasStaticState("elanex.biz")); + EXPECT_FALSE(HasStaticState("foo.elanex.biz")); - EXPECT_TRUE(ShouldRedirect("www.noisebridge.net")); - EXPECT_FALSE(HasState("noisebridge.net")); - EXPECT_FALSE(HasState("foo.noisebridge.net")); + EXPECT_TRUE(StaticShouldRedirect("sunshinepress.org")); + EXPECT_TRUE(StaticShouldRedirect("www.sunshinepress.org")); + EXPECT_TRUE(StaticShouldRedirect("a.b.sunshinepress.org")); - EXPECT_TRUE(ShouldRedirect("neg9.org")); - EXPECT_FALSE(HasState("www.neg9.org")); + EXPECT_TRUE(StaticShouldRedirect("www.noisebridge.net")); + EXPECT_FALSE(HasStaticState("noisebridge.net")); + EXPECT_FALSE(HasStaticState("foo.noisebridge.net")); - EXPECT_TRUE(ShouldRedirect("riseup.net")); - EXPECT_TRUE(ShouldRedirect("foo.riseup.net")); + EXPECT_TRUE(StaticShouldRedirect("neg9.org")); + EXPECT_FALSE(HasStaticState("www.neg9.org")); - EXPECT_TRUE(ShouldRedirect("factor.cc")); - EXPECT_FALSE(HasState("www.factor.cc")); + EXPECT_TRUE(StaticShouldRedirect("riseup.net")); + EXPECT_TRUE(StaticShouldRedirect("foo.riseup.net")); - EXPECT_TRUE(ShouldRedirect("members.mayfirst.org")); - EXPECT_TRUE(ShouldRedirect("support.mayfirst.org")); - EXPECT_TRUE(ShouldRedirect("id.mayfirst.org")); - EXPECT_TRUE(ShouldRedirect("lists.mayfirst.org")); - EXPECT_FALSE(HasState("www.mayfirst.org")); + EXPECT_TRUE(StaticShouldRedirect("factor.cc")); + EXPECT_FALSE(HasStaticState("www.factor.cc")); - EXPECT_TRUE(ShouldRedirect("romab.com")); - EXPECT_TRUE(ShouldRedirect("www.romab.com")); - EXPECT_TRUE(ShouldRedirect("foo.romab.com")); + EXPECT_TRUE(StaticShouldRedirect("members.mayfirst.org")); + EXPECT_TRUE(StaticShouldRedirect("support.mayfirst.org")); + EXPECT_TRUE(StaticShouldRedirect("id.mayfirst.org")); + EXPECT_TRUE(StaticShouldRedirect("lists.mayfirst.org")); + EXPECT_FALSE(HasStaticState("www.mayfirst.org")); - EXPECT_TRUE(ShouldRedirect("logentries.com")); - EXPECT_TRUE(ShouldRedirect("www.logentries.com")); - EXPECT_FALSE(HasState("foo.logentries.com")); + EXPECT_TRUE(StaticShouldRedirect("romab.com")); + EXPECT_TRUE(StaticShouldRedirect("www.romab.com")); + EXPECT_TRUE(StaticShouldRedirect("foo.romab.com")); - EXPECT_TRUE(ShouldRedirect("stripe.com")); - EXPECT_TRUE(ShouldRedirect("foo.stripe.com")); + EXPECT_TRUE(StaticShouldRedirect("logentries.com")); + EXPECT_TRUE(StaticShouldRedirect("www.logentries.com")); + EXPECT_FALSE(HasStaticState("foo.logentries.com")); - EXPECT_TRUE(ShouldRedirect("cloudsecurityalliance.org")); - EXPECT_TRUE(ShouldRedirect("foo.cloudsecurityalliance.org")); + EXPECT_TRUE(StaticShouldRedirect("stripe.com")); + EXPECT_TRUE(StaticShouldRedirect("foo.stripe.com")); - EXPECT_TRUE(ShouldRedirect("login.sapo.pt")); - EXPECT_TRUE(ShouldRedirect("foo.login.sapo.pt")); + EXPECT_TRUE(StaticShouldRedirect("cloudsecurityalliance.org")); + EXPECT_TRUE(StaticShouldRedirect("foo.cloudsecurityalliance.org")); - EXPECT_TRUE(ShouldRedirect("mattmccutchen.net")); - EXPECT_TRUE(ShouldRedirect("foo.mattmccutchen.net")); + EXPECT_TRUE(StaticShouldRedirect("login.sapo.pt")); + EXPECT_TRUE(StaticShouldRedirect("foo.login.sapo.pt")); - EXPECT_TRUE(ShouldRedirect("betnet.fr")); - EXPECT_TRUE(ShouldRedirect("foo.betnet.fr")); + EXPECT_TRUE(StaticShouldRedirect("mattmccutchen.net")); + EXPECT_TRUE(StaticShouldRedirect("foo.mattmccutchen.net")); - EXPECT_TRUE(ShouldRedirect("uprotect.it")); - EXPECT_TRUE(ShouldRedirect("foo.uprotect.it")); + EXPECT_TRUE(StaticShouldRedirect("betnet.fr")); + EXPECT_TRUE(StaticShouldRedirect("foo.betnet.fr")); - EXPECT_TRUE(ShouldRedirect("squareup.com")); - EXPECT_FALSE(HasState("foo.squareup.com")); + EXPECT_TRUE(StaticShouldRedirect("uprotect.it")); + EXPECT_TRUE(StaticShouldRedirect("foo.uprotect.it")); - EXPECT_TRUE(ShouldRedirect("cert.se")); - EXPECT_TRUE(ShouldRedirect("foo.cert.se")); + EXPECT_TRUE(StaticShouldRedirect("squareup.com")); + EXPECT_FALSE(HasStaticState("foo.squareup.com")); - EXPECT_TRUE(ShouldRedirect("crypto.is")); - EXPECT_TRUE(ShouldRedirect("foo.crypto.is")); + EXPECT_TRUE(StaticShouldRedirect("cert.se")); + EXPECT_TRUE(StaticShouldRedirect("foo.cert.se")); - EXPECT_TRUE(ShouldRedirect("simon.butcher.name")); - EXPECT_TRUE(ShouldRedirect("foo.simon.butcher.name")); + EXPECT_TRUE(StaticShouldRedirect("crypto.is")); + EXPECT_TRUE(StaticShouldRedirect("foo.crypto.is")); - EXPECT_TRUE(ShouldRedirect("linx.net")); - EXPECT_TRUE(ShouldRedirect("foo.linx.net")); + EXPECT_TRUE(StaticShouldRedirect("simon.butcher.name")); + EXPECT_TRUE(StaticShouldRedirect("foo.simon.butcher.name")); - EXPECT_TRUE(ShouldRedirect("dropcam.com")); - EXPECT_TRUE(ShouldRedirect("www.dropcam.com")); - EXPECT_FALSE(HasState("foo.dropcam.com")); + EXPECT_TRUE(StaticShouldRedirect("linx.net")); + EXPECT_TRUE(StaticShouldRedirect("foo.linx.net")); - EXPECT_TRUE(state.GetDomainState("torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.static_spki_hashes.empty()); - EXPECT_TRUE(state.GetDomainState("www.torproject.org", false, - &domain_state)); - EXPECT_FALSE(domain_state.static_spki_hashes.empty()); - EXPECT_TRUE(state.GetDomainState("check.torproject.org", false, - &domain_state)); - EXPECT_FALSE(domain_state.static_spki_hashes.empty()); - EXPECT_TRUE(state.GetDomainState("blog.torproject.org", false, - &domain_state)); - EXPECT_FALSE(domain_state.static_spki_hashes.empty()); - EXPECT_TRUE(ShouldRedirect("ebanking.indovinabank.com.vn")); - EXPECT_TRUE(ShouldRedirect("foo.ebanking.indovinabank.com.vn")); + EXPECT_TRUE(StaticShouldRedirect("dropcam.com")); + EXPECT_TRUE(StaticShouldRedirect("www.dropcam.com")); + EXPECT_FALSE(HasStaticState("foo.dropcam.com")); - EXPECT_TRUE(ShouldRedirect("epoxate.com")); - EXPECT_FALSE(HasState("foo.epoxate.com")); + EXPECT_TRUE( + state.GetStaticDomainState("torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("www.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("check.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("blog.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE(StaticShouldRedirect("ebanking.indovinabank.com.vn")); + EXPECT_TRUE(StaticShouldRedirect("foo.ebanking.indovinabank.com.vn")); - EXPECT_TRUE(HasPublicKeyPins("torproject.org")); - EXPECT_TRUE(HasPublicKeyPins("www.torproject.org")); - EXPECT_TRUE(HasPublicKeyPins("check.torproject.org")); - EXPECT_TRUE(HasPublicKeyPins("blog.torproject.org")); - EXPECT_FALSE(HasState("foo.torproject.org")); + EXPECT_TRUE(StaticShouldRedirect("epoxate.com")); + EXPECT_FALSE(HasStaticState("foo.epoxate.com")); - EXPECT_TRUE(ShouldRedirect("www.moneybookers.com")); - EXPECT_FALSE(HasState("moneybookers.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("check.torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("blog.torproject.org")); + EXPECT_FALSE(HasStaticState("foo.torproject.org")); - EXPECT_TRUE(ShouldRedirect("ledgerscope.net")); - EXPECT_TRUE(ShouldRedirect("www.ledgerscope.net")); - EXPECT_FALSE(HasState("status.ledgerscope.net")); + EXPECT_TRUE(StaticShouldRedirect("www.moneybookers.com")); + EXPECT_FALSE(HasStaticState("moneybookers.com")); - EXPECT_TRUE(ShouldRedirect("foo.app.recurly.com")); - EXPECT_TRUE(ShouldRedirect("foo.api.recurly.com")); + EXPECT_TRUE(StaticShouldRedirect("ledgerscope.net")); + EXPECT_TRUE(StaticShouldRedirect("www.ledgerscope.net")); + EXPECT_FALSE(HasStaticState("status.ledgerscope.net")); - EXPECT_TRUE(ShouldRedirect("greplin.com")); - EXPECT_TRUE(ShouldRedirect("www.greplin.com")); - EXPECT_FALSE(HasState("foo.greplin.com")); + EXPECT_TRUE(StaticShouldRedirect("foo.app.recurly.com")); + EXPECT_TRUE(StaticShouldRedirect("foo.api.recurly.com")); - EXPECT_TRUE(ShouldRedirect("luneta.nearbuysystems.com")); - EXPECT_TRUE(ShouldRedirect("foo.luneta.nearbuysystems.com")); + EXPECT_TRUE(StaticShouldRedirect("greplin.com")); + EXPECT_TRUE(StaticShouldRedirect("www.greplin.com")); + EXPECT_FALSE(HasStaticState("foo.greplin.com")); - EXPECT_TRUE(ShouldRedirect("ubertt.org")); - EXPECT_TRUE(ShouldRedirect("foo.ubertt.org")); + EXPECT_TRUE(StaticShouldRedirect("luneta.nearbuysystems.com")); + EXPECT_TRUE(StaticShouldRedirect("foo.luneta.nearbuysystems.com")); - EXPECT_TRUE(ShouldRedirect("pixi.me")); - EXPECT_TRUE(ShouldRedirect("www.pixi.me")); + EXPECT_TRUE(StaticShouldRedirect("ubertt.org")); + EXPECT_TRUE(StaticShouldRedirect("foo.ubertt.org")); - EXPECT_TRUE(ShouldRedirect("grepular.com")); - EXPECT_TRUE(ShouldRedirect("www.grepular.com")); + EXPECT_TRUE(StaticShouldRedirect("pixi.me")); + EXPECT_TRUE(StaticShouldRedirect("www.pixi.me")); - EXPECT_TRUE(ShouldRedirect("mydigipass.com")); - EXPECT_FALSE(ShouldRedirect("foo.mydigipass.com")); - EXPECT_TRUE(ShouldRedirect("www.mydigipass.com")); - EXPECT_FALSE(ShouldRedirect("foo.www.mydigipass.com")); - EXPECT_TRUE(ShouldRedirect("developer.mydigipass.com")); - EXPECT_FALSE(ShouldRedirect("foo.developer.mydigipass.com")); - EXPECT_TRUE(ShouldRedirect("www.developer.mydigipass.com")); - EXPECT_FALSE(ShouldRedirect("foo.www.developer.mydigipass.com")); - EXPECT_TRUE(ShouldRedirect("sandbox.mydigipass.com")); - EXPECT_FALSE(ShouldRedirect("foo.sandbox.mydigipass.com")); - EXPECT_TRUE(ShouldRedirect("www.sandbox.mydigipass.com")); - EXPECT_FALSE(ShouldRedirect("foo.www.sandbox.mydigipass.com")); + EXPECT_TRUE(StaticShouldRedirect("grepular.com")); + EXPECT_TRUE(StaticShouldRedirect("www.grepular.com")); + + EXPECT_TRUE(StaticShouldRedirect("mydigipass.com")); + EXPECT_FALSE(StaticShouldRedirect("foo.mydigipass.com")); + EXPECT_TRUE(StaticShouldRedirect("www.mydigipass.com")); + EXPECT_FALSE(StaticShouldRedirect("foo.www.mydigipass.com")); + EXPECT_TRUE(StaticShouldRedirect("developer.mydigipass.com")); + EXPECT_FALSE(StaticShouldRedirect("foo.developer.mydigipass.com")); + EXPECT_TRUE(StaticShouldRedirect("www.developer.mydigipass.com")); + EXPECT_FALSE(StaticShouldRedirect("foo.www.developer.mydigipass.com")); + EXPECT_TRUE(StaticShouldRedirect("sandbox.mydigipass.com")); + EXPECT_FALSE(StaticShouldRedirect("foo.sandbox.mydigipass.com")); + EXPECT_TRUE(StaticShouldRedirect("www.sandbox.mydigipass.com")); + EXPECT_FALSE(StaticShouldRedirect("foo.www.sandbox.mydigipass.com")); - EXPECT_TRUE(ShouldRedirect("crypto.cat")); - EXPECT_FALSE(ShouldRedirect("foo.crypto.cat")); + EXPECT_TRUE(StaticShouldRedirect("crypto.cat")); + EXPECT_FALSE(StaticShouldRedirect("foo.crypto.cat")); - EXPECT_TRUE(ShouldRedirect("bigshinylock.minazo.net")); - EXPECT_TRUE(ShouldRedirect("foo.bigshinylock.minazo.net")); + EXPECT_TRUE(StaticShouldRedirect("bigshinylock.minazo.net")); + EXPECT_TRUE(StaticShouldRedirect("foo.bigshinylock.minazo.net")); - EXPECT_TRUE(ShouldRedirect("crate.io")); - EXPECT_TRUE(ShouldRedirect("foo.crate.io")); + EXPECT_TRUE(StaticShouldRedirect("crate.io")); + EXPECT_TRUE(StaticShouldRedirect("foo.crate.io")); - EXPECT_TRUE(HasPublicKeyPins("www.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); } TEST_F(TransportSecurityStateTest, LongNames) { @@ -482,59 +489,62 @@ TEST_F(TransportSecurityStateTest, LongNames) { "WaveletIdDomainAndBlipBlipid"; TransportSecurityState::DomainState domain_state; // Just checks that we don't hit a NOTREACHED. - EXPECT_FALSE(state.GetDomainState(kLongName, true, &domain_state)); + EXPECT_FALSE(state.GetStaticDomainState(kLongName, true, &domain_state)); + EXPECT_FALSE(state.GetDynamicDomainState(kLongName, &domain_state)); } TEST_F(TransportSecurityStateTest, BuiltinCertPins) { TransportSecurityState state; TransportSecurityState::DomainState domain_state; - EXPECT_TRUE(state.GetDomainState("chrome.google.com", true, &domain_state)); - EXPECT_TRUE(HasPublicKeyPins("chrome.google.com")); + EXPECT_TRUE( + state.GetStaticDomainState("chrome.google.com", true, &domain_state)); + EXPECT_TRUE(HasStaticPublicKeyPins("chrome.google.com")); HashValueVector hashes; + std::string failure_log; // Checks that a built-in list does exist. - EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes)); - EXPECT_FALSE(HasPublicKeyPins("www.paypal.com")); - - EXPECT_TRUE(HasPublicKeyPins("docs.google.com")); - EXPECT_TRUE(HasPublicKeyPins("1.docs.google.com")); - EXPECT_TRUE(HasPublicKeyPins("sites.google.com")); - EXPECT_TRUE(HasPublicKeyPins("drive.google.com")); - EXPECT_TRUE(HasPublicKeyPins("spreadsheets.google.com")); - EXPECT_TRUE(HasPublicKeyPins("wallet.google.com")); - EXPECT_TRUE(HasPublicKeyPins("checkout.google.com")); - EXPECT_TRUE(HasPublicKeyPins("appengine.google.com")); - EXPECT_TRUE(HasPublicKeyPins("market.android.com")); - EXPECT_TRUE(HasPublicKeyPins("encrypted.google.com")); - EXPECT_TRUE(HasPublicKeyPins("accounts.google.com")); - EXPECT_TRUE(HasPublicKeyPins("profiles.google.com")); - EXPECT_TRUE(HasPublicKeyPins("mail.google.com")); - EXPECT_TRUE(HasPublicKeyPins("chatenabled.mail.google.com")); - EXPECT_TRUE(HasPublicKeyPins("talkgadget.google.com")); - EXPECT_TRUE(HasPublicKeyPins("hostedtalkgadget.google.com")); - EXPECT_TRUE(HasPublicKeyPins("talk.google.com")); - EXPECT_TRUE(HasPublicKeyPins("plus.google.com")); - EXPECT_TRUE(HasPublicKeyPins("groups.google.com")); - EXPECT_TRUE(HasPublicKeyPins("apis.google.com")); - - EXPECT_TRUE(HasPublicKeyPins("ssl.gstatic.com")); - EXPECT_TRUE(HasPublicKeyPins("gstatic.com")); - EXPECT_TRUE(HasPublicKeyPins("www.gstatic.com")); - EXPECT_TRUE(HasPublicKeyPins("ssl.google-analytics.com")); - EXPECT_TRUE(HasPublicKeyPins("www.googleplex.com")); + EXPECT_FALSE(domain_state.CheckPublicKeyPins(hashes, &failure_log)); + EXPECT_FALSE(HasStaticPublicKeyPins("www.paypal.com")); + + EXPECT_TRUE(HasStaticPublicKeyPins("docs.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("1.docs.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("sites.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("drive.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("spreadsheets.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("wallet.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("checkout.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("appengine.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("market.android.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("encrypted.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("accounts.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("profiles.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("mail.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("chatenabled.mail.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("talkgadget.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("hostedtalkgadget.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("talk.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("plus.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("groups.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("apis.google.com")); + + EXPECT_TRUE(HasStaticPublicKeyPins("ssl.gstatic.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("gstatic.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.gstatic.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("ssl.google-analytics.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.googleplex.com")); // Disabled in order to help track down pinning failures --agl - EXPECT_TRUE(HasPublicKeyPins("twitter.com")); - EXPECT_FALSE(HasPublicKeyPins("foo.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("www.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("api.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("oauth.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("mobile.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("dev.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("business.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("platform.twitter.com")); - EXPECT_TRUE(HasPublicKeyPins("si0.twimg.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("twitter.com")); + EXPECT_FALSE(HasStaticPublicKeyPins("foo.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("api.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("oauth.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("mobile.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("dev.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("business.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("platform.twitter.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("si0.twimg.com")); } static bool AddHash(const std::string& type_and_base64, @@ -576,54 +586,56 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { TransportSecurityState state; TransportSecurityState::DomainState domain_state; - EXPECT_TRUE(state.GetDomainState("blog.torproject.org", true, &domain_state)); + EXPECT_TRUE( + state.GetStaticDomainState("blog.torproject.org", true, &domain_state)); EXPECT_TRUE(domain_state.HasPublicKeyPins()); - EXPECT_TRUE(domain_state.CheckPublicKeyPins(good_hashes)); - EXPECT_FALSE(domain_state.CheckPublicKeyPins(bad_hashes)); + std::string failure_log; + EXPECT_TRUE(domain_state.CheckPublicKeyPins(good_hashes, &failure_log)); + EXPECT_FALSE(domain_state.CheckPublicKeyPins(bad_hashes, &failure_log)); } TEST_F(TransportSecurityStateTest, OptionalHSTSCertPins) { TransportSecurityState state; TransportSecurityState::DomainState domain_state; - EXPECT_FALSE(ShouldRedirect("www.google-analytics.com")); - - EXPECT_FALSE(HasPublicKeyPins("www.google-analytics.com", false)); - EXPECT_TRUE(HasPublicKeyPins("www.google-analytics.com")); - EXPECT_TRUE(HasPublicKeyPins("google.com")); - EXPECT_TRUE(HasPublicKeyPins("www.google.com")); - EXPECT_TRUE(HasPublicKeyPins("mail-attachment.googleusercontent.com")); - EXPECT_TRUE(HasPublicKeyPins("www.youtube.com")); - EXPECT_TRUE(HasPublicKeyPins("i.ytimg.com")); - EXPECT_TRUE(HasPublicKeyPins("googleapis.com")); - EXPECT_TRUE(HasPublicKeyPins("ajax.googleapis.com")); - EXPECT_TRUE(HasPublicKeyPins("googleadservices.com")); - EXPECT_TRUE(HasPublicKeyPins("pagead2.googleadservices.com")); - EXPECT_TRUE(HasPublicKeyPins("googlecode.com")); - EXPECT_TRUE(HasPublicKeyPins("kibbles.googlecode.com")); - EXPECT_TRUE(HasPublicKeyPins("appspot.com")); - EXPECT_TRUE(HasPublicKeyPins("googlesyndication.com")); - EXPECT_TRUE(HasPublicKeyPins("doubleclick.net")); - EXPECT_TRUE(HasPublicKeyPins("ad.doubleclick.net")); - EXPECT_FALSE(HasPublicKeyPins("learn.doubleclick.net")); - EXPECT_TRUE(HasPublicKeyPins("a.googlegroups.com")); - EXPECT_FALSE(HasPublicKeyPins("a.googlegroups.com", false)); + EXPECT_FALSE(StaticShouldRedirect("www.google-analytics.com")); + + EXPECT_FALSE(HasStaticPublicKeyPins("www.google-analytics.com", false)); + EXPECT_TRUE(HasStaticPublicKeyPins("www.google-analytics.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("mail-attachment.googleusercontent.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.youtube.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("i.ytimg.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("googleapis.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("ajax.googleapis.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("googleadservices.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("pagead2.googleadservices.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("googlecode.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("kibbles.googlecode.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("appspot.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("googlesyndication.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("doubleclick.net")); + EXPECT_TRUE(HasStaticPublicKeyPins("ad.doubleclick.net")); + EXPECT_FALSE(HasStaticPublicKeyPins("learn.doubleclick.net")); + EXPECT_TRUE(HasStaticPublicKeyPins("a.googlegroups.com")); + EXPECT_FALSE(HasStaticPublicKeyPins("a.googlegroups.com", false)); } TEST_F(TransportSecurityStateTest, OverrideBuiltins) { - EXPECT_TRUE(HasPublicKeyPins("google.com")); - EXPECT_FALSE(ShouldRedirect("google.com")); - EXPECT_FALSE(ShouldRedirect("www.google.com")); + EXPECT_TRUE(HasStaticPublicKeyPins("google.com")); + EXPECT_FALSE(StaticShouldRedirect("google.com")); + EXPECT_FALSE(StaticShouldRedirect("www.google.com")); TransportSecurityState state; TransportSecurityState::DomainState domain_state; const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); - domain_state.upgrade_expiry = expiry; + domain_state.sts.expiry = expiry; EnableHost(&state, "www.google.com", domain_state); - EXPECT_TRUE(state.GetDomainState("www.google.com", true, &domain_state)); + EXPECT_TRUE(state.GetDynamicDomainState("www.google.com", &domain_state)); } TEST_F(TransportSecurityStateTest, GooglePinnedProperties) { diff --git a/chromium/net/http/url_security_manager_win.cc b/chromium/net/http/url_security_manager_win.cc index cb3c66ef0f0..4e2d938963e 100644 --- a/chromium/net/http/url_security_manager_win.cc +++ b/chromium/net/http/url_security_manager_win.cc @@ -53,7 +53,7 @@ bool URLSecurityManagerWin::CanUseDefaultCredentials( if (!const_cast<URLSecurityManagerWin*>(this)->EnsureSystemSecurityManager()) return false; - std::wstring url_w = ASCIIToWide(auth_origin.spec()); + std::wstring url_w = base::ASCIIToWide(auth_origin.spec()); DWORD policy = 0; HRESULT hr; hr = security_manager_->ProcessUrlAction(url_w.c_str(), |