diff options
author | Kristian Monsen <kristianm@google.com> | 2011-05-31 20:30:28 +0100 |
---|---|---|
committer | Kristian Monsen <kristianm@google.com> | 2011-06-14 20:31:41 -0700 |
commit | 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801 (patch) | |
tree | 382278a54ce7a744d62fa510a9a80688cc12434b /net/http | |
parent | c4becdd46e31d261b930e4b5a539cbc1d45c23a6 (diff) | |
download | external_chromium-72a454cd3513ac24fbdd0e0cb9ad70b86a99b801.zip external_chromium-72a454cd3513ac24fbdd0e0cb9ad70b86a99b801.tar.gz external_chromium-72a454cd3513ac24fbdd0e0cb9ad70b86a99b801.tar.bz2 |
Merge Chromium.org at r11.0.672.0: Initial merge by git.
Change-Id: I8b4aaf611a2a405fe3fe10e8a94ea7658645c192
Diffstat (limited to 'net/http')
72 files changed, 2907 insertions, 2360 deletions
diff --git a/net/http/disk_cache_based_ssl_host_info.cc b/net/http/disk_cache_based_ssl_host_info.cc index 1b1dfaf..7021cfb 100644 --- a/net/http/disk_cache_based_ssl_host_info.cc +++ b/net/http/disk_cache_based_ssl_host_info.cc @@ -13,12 +13,32 @@ namespace net { +DiskCacheBasedSSLHostInfo::CallbackImpl::CallbackImpl( + const base::WeakPtr<DiskCacheBasedSSLHostInfo>& obj, + void (DiskCacheBasedSSLHostInfo::*meth)(int)) + : obj_(obj), + meth_(meth), + backend_(NULL), + entry_(NULL) { +} + +DiskCacheBasedSSLHostInfo::CallbackImpl::~CallbackImpl() {} + +void DiskCacheBasedSSLHostInfo::CallbackImpl::RunWithParams( + const Tuple1<int>& params) { + if (!obj_) { + delete this; + } else { + DispatchToMethod(obj_.get(), meth_, params); + } +} + DiskCacheBasedSSLHostInfo::DiskCacheBasedSSLHostInfo( const std::string& hostname, const SSLConfig& ssl_config, + CertVerifier* cert_verifier, HttpCache* http_cache) - : SSLHostInfo(hostname, ssl_config, - http_cache->network_layer()->GetSession()->cert_verifier()), + : SSLHostInfo(hostname, ssl_config, cert_verifier), weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), callback_(new CallbackImpl(weak_ptr_factory_.GetWeakPtr(), &DiskCacheBasedSSLHostInfo::DoLoop)), @@ -37,6 +57,35 @@ void DiskCacheBasedSSLHostInfo::Start() { DoLoop(OK); } +int DiskCacheBasedSSLHostInfo::WaitForDataReady(CompletionCallback* callback) { + DCHECK(CalledOnValidThread()); + DCHECK(state_ != GET_BACKEND); + + if (ready_) + return OK; + if (callback) { + DCHECK(!user_callback_); + user_callback_ = callback; + } + return ERR_IO_PENDING; +} + +void DiskCacheBasedSSLHostInfo::Persist() { + DCHECK(CalledOnValidThread()); + DCHECK(state_ != GET_BACKEND); + + DCHECK(new_data_.empty()); + CHECK(ready_); + DCHECK(user_callback_ == NULL); + new_data_ = Serialize(); + + if (!backend_) + return; + + state_ = CREATE; + DoLoop(OK); +} + DiskCacheBasedSSLHostInfo::~DiskCacheBasedSSLHostInfo() { DCHECK(!user_callback_); if (entry_) @@ -95,24 +144,6 @@ void DiskCacheBasedSSLHostInfo::DoLoop(int rv) { } while (rv != ERR_IO_PENDING && state_ != NONE); } -bool DiskCacheBasedSSLHostInfo::IsCallbackPending() const { - switch (state_) { - case GET_BACKEND_COMPLETE: - case OPEN_COMPLETE: - case READ_COMPLETE: - case CREATE_COMPLETE: - case WRITE_COMPLETE: - return true; - default: - return false; - } -} - -int DiskCacheBasedSSLHostInfo::DoGetBackend() { - state_ = GET_BACKEND_COMPLETE; - return http_cache_->GetBackend(callback_->backend_pointer(), callback_); -} - int DiskCacheBasedSSLHostInfo::DoGetBackendComplete(int rv) { if (rv == OK) { backend_ = callback_->backend(); @@ -123,11 +154,6 @@ int DiskCacheBasedSSLHostInfo::DoGetBackendComplete(int rv) { return OK; } -int DiskCacheBasedSSLHostInfo::DoOpen() { - state_ = OPEN_COMPLETE; - return backend_->OpenEntry(key(), callback_->entry_pointer(), callback_); -} - int DiskCacheBasedSSLHostInfo::DoOpenComplete(int rv) { if (rv == OK) { entry_ = callback_->entry(); @@ -139,6 +165,39 @@ int DiskCacheBasedSSLHostInfo::DoOpenComplete(int rv) { return OK; } +int DiskCacheBasedSSLHostInfo::DoReadComplete(int rv) { + if (rv > 0) + data_ = std::string(read_buffer_->data(), rv); + + state_ = WAIT_FOR_DATA_READY_DONE; + return OK; +} + +int DiskCacheBasedSSLHostInfo::DoWriteComplete(int rv) { + state_ = SET_DONE; + return OK; +} + +int DiskCacheBasedSSLHostInfo::DoCreateComplete(int rv) { + if (rv != OK) { + state_ = SET_DONE; + } else { + entry_ = callback_->entry(); + state_ = WRITE; + } + return OK; +} + +int DiskCacheBasedSSLHostInfo::DoGetBackend() { + state_ = GET_BACKEND_COMPLETE; + return http_cache_->GetBackend(callback_->backend_pointer(), callback_); +} + +int DiskCacheBasedSSLHostInfo::DoOpen() { + state_ = OPEN_COMPLETE; + return backend_->OpenEntry(key(), callback_->entry_pointer(), callback_); +} + int DiskCacheBasedSSLHostInfo::DoRead() { const int32 size = entry_->GetDataSize(0 /* index */); if (!size) { @@ -152,12 +211,19 @@ int DiskCacheBasedSSLHostInfo::DoRead() { size, callback_); } -int DiskCacheBasedSSLHostInfo::DoReadComplete(int rv) { - if (rv > 0) - data_ = std::string(read_buffer_->data(), rv); +int DiskCacheBasedSSLHostInfo::DoWrite() { + write_buffer_ = new IOBuffer(new_data_.size()); + memcpy(write_buffer_->data(), new_data_.data(), new_data_.size()); + state_ = WRITE_COMPLETE; - state_ = WAIT_FOR_DATA_READY_DONE; - return OK; + return entry_->WriteData(0 /* index */, 0 /* offset */, write_buffer_, + new_data_.size(), callback_, true /* truncate */); +} + +int DiskCacheBasedSSLHostInfo::DoCreate() { + DCHECK(entry_ == NULL); + state_ = CREATE_COMPLETE; + return backend_->CreateEntry(key(), callback_->entry_pointer(), callback_); } int DiskCacheBasedSSLHostInfo::WaitForDataReadyDone() { @@ -181,65 +247,6 @@ int DiskCacheBasedSSLHostInfo::WaitForDataReadyDone() { return OK; } -int DiskCacheBasedSSLHostInfo::WaitForDataReady(CompletionCallback* callback) { - DCHECK(CalledOnValidThread()); - DCHECK(state_ != GET_BACKEND); - - if (ready_) - return OK; - if (callback) { - DCHECK(!user_callback_); - user_callback_ = callback; - } - return ERR_IO_PENDING; -} - -void DiskCacheBasedSSLHostInfo::Persist() { - DCHECK(CalledOnValidThread()); - DCHECK(state_ != GET_BACKEND); - - DCHECK(new_data_.empty()); - CHECK(ready_); - DCHECK(user_callback_ == NULL); - new_data_ = Serialize(); - - if (!backend_) - return; - - state_ = CREATE; - DoLoop(OK); -} - -int DiskCacheBasedSSLHostInfo::DoCreate() { - DCHECK(entry_ == NULL); - state_ = CREATE_COMPLETE; - return backend_->CreateEntry(key(), callback_->entry_pointer(), callback_); -} - -int DiskCacheBasedSSLHostInfo::DoCreateComplete(int rv) { - if (rv != OK) { - state_ = SET_DONE; - } else { - entry_ = callback_->entry(); - state_ = WRITE; - } - return OK; -} - -int DiskCacheBasedSSLHostInfo::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(), callback_, true /* truncate */); -} - -int DiskCacheBasedSSLHostInfo::DoWriteComplete(int rv) { - state_ = SET_DONE; - return OK; -} - int DiskCacheBasedSSLHostInfo::SetDone() { if (entry_) entry_->Close(); @@ -248,4 +255,17 @@ int DiskCacheBasedSSLHostInfo::SetDone() { return OK; } -} // namespace net +bool DiskCacheBasedSSLHostInfo::IsCallbackPending() const { + switch (state_) { + case GET_BACKEND_COMPLETE: + case OPEN_COMPLETE: + case READ_COMPLETE: + case CREATE_COMPLETE: + case WRITE_COMPLETE: + return true; + default: + return false; + } +} + +} // namespace net diff --git a/net/http/disk_cache_based_ssl_host_info.h b/net/http/disk_cache_based_ssl_host_info.h index c094163..905a3a9 100644 --- a/net/http/disk_cache_based_ssl_host_info.h +++ b/net/http/disk_cache_based_ssl_host_info.h @@ -2,12 +2,11 @@ // 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_SSL_HOST_INFO_H -#define NET_HTTP_DISK_CACHE_BASED_SSL_HOST_INFO_H +#ifndef NET_HTTP_DISK_CACHE_BASED_SSL_HOST_INFO_H_ +#define NET_HTTP_DISK_CACHE_BASED_SSL_HOST_INFO_H_ #include <string> -#include "base/lock.h" #include "base/scoped_ptr.h" #include "base/threading/non_thread_safe.h" #include "base/weak_ptr.h" @@ -29,6 +28,7 @@ class DiskCacheBasedSSLHostInfo : public SSLHostInfo, public: DiskCacheBasedSSLHostInfo(const std::string& hostname, const SSLConfig& ssl_config, + CertVerifier* cert_verifier, HttpCache* http_cache); // Implementation of SSLHostInfo @@ -53,37 +53,30 @@ class DiskCacheBasedSSLHostInfo : public SSLHostInfo, NONE, }; - ~DiskCacheBasedSSLHostInfo(); - class CallbackImpl : public CallbackRunner<Tuple1<int> > { public: CallbackImpl(const base::WeakPtr<DiskCacheBasedSSLHostInfo>& obj, - void (DiskCacheBasedSSLHostInfo::*meth) (int)) - : obj_(obj), - meth_(meth) { - } - - virtual void RunWithParams(const Tuple1<int>& params) { - if (!obj_) { - delete this; - } else { - DispatchToMethod(obj_.get(), meth_, params); - } - } + void (DiskCacheBasedSSLHostInfo::*meth)(int)); + virtual ~CallbackImpl(); disk_cache::Backend** backend_pointer() { return &backend_; } disk_cache::Entry** entry_pointer() { return &entry_; } disk_cache::Backend* backend() const { return backend_; } disk_cache::Entry* entry() const { return entry_; } + // CallbackRunner<Tuple1<int> >: + virtual void RunWithParams(const Tuple1<int>& params); + private: base::WeakPtr<DiskCacheBasedSSLHostInfo> obj_; - void (DiskCacheBasedSSLHostInfo::*meth_) (int); + void (DiskCacheBasedSSLHostInfo::*meth_)(int); disk_cache::Backend* backend_; disk_cache::Entry* entry_; }; + virtual ~DiskCacheBasedSSLHostInfo(); + std::string key() const; void DoLoop(int rv); @@ -97,11 +90,12 @@ class DiskCacheBasedSSLHostInfo : public SSLHostInfo, int DoGetBackend(); int DoOpen(); int DoRead(); - int DoCreate(); int DoWrite(); + int DoCreate(); // WaitForDataReadyDone is the terminal state of the read operation. int WaitForDataReadyDone(); + // SetDone is the terminal state of the write operation. int SetDone(); @@ -125,4 +119,4 @@ class DiskCacheBasedSSLHostInfo : public SSLHostInfo, } // namespace net -#endif // NET_HTTP_DISK_CACHE_BASED_SSL_HOST_INFO_H +#endif // NET_HTTP_DISK_CACHE_BASED_SSL_HOST_INFO_H_ diff --git a/net/http/http_alternate_protocols.cc b/net/http/http_alternate_protocols.cc index 3e6b488..28c52dc 100644 --- a/net/http/http_alternate_protocols.cc +++ b/net/http/http_alternate_protocols.cc @@ -5,6 +5,7 @@ #include "net/http/http_alternate_protocols.h" #include "base/logging.h" +#include "base/stringprintf.h" #include "base/stl_util-inl.h" namespace net { @@ -15,6 +16,28 @@ const char* const HttpAlternateProtocols::kProtocolStrings[] = { "npn-spdy/2", }; +const char* HttpAlternateProtocols::ProtocolToString( + HttpAlternateProtocols::Protocol protocol) { + switch (protocol) { + case HttpAlternateProtocols::NPN_SPDY_1: + case HttpAlternateProtocols::NPN_SPDY_2: + return HttpAlternateProtocols::kProtocolStrings[protocol]; + case HttpAlternateProtocols::BROKEN: + return "Broken"; + case HttpAlternateProtocols::UNINITIALIZED: + return "Uninitialized"; + default: + NOTREACHED(); + return ""; + } +} + + +std::string HttpAlternateProtocols::PortProtocolPair::ToString() const { + return base::StringPrintf("%d:%s", port, + HttpAlternateProtocols::ProtocolToString(protocol)); +} + // static HttpAlternateProtocols::PortProtocolPair* HttpAlternateProtocols::forced_alternate_protocol_ = NULL; diff --git a/net/http/http_alternate_protocols.h b/net/http/http_alternate_protocols.h index d5f9d77..10b1a06 100644 --- a/net/http/http_alternate_protocols.h +++ b/net/http/http_alternate_protocols.h @@ -34,10 +34,14 @@ class HttpAlternateProtocols { return port == other.port && protocol == other.protocol; } + std::string ToString() const; + uint16 port; Protocol protocol; }; + typedef std::map<HostPortPair, PortProtocolPair> ProtocolMap; + static const char kHeader[]; static const char* const kProtocolStrings[NUM_ALTERNATE_PROTOCOLS]; @@ -64,6 +68,8 @@ class HttpAlternateProtocols { // attempts to set the alternate protocol for |http_host_port_pair| will fail. void MarkBrokenAlternateProtocolFor(const HostPortPair& http_host_port_pair); + const ProtocolMap& protocol_map() const { return protocol_map_; } + // Debugging to simulate presence of an AlternateProtocol. // If we don't have an alternate protocol in the map for any given host/port // pair, force this ProtocolPortPair. @@ -71,10 +77,10 @@ class HttpAlternateProtocols { static void DisableForcedAlternateProtocol(); private: - typedef std::map<HostPortPair, PortProtocolPair> ProtocolMap; - ProtocolMap protocol_map_; + static const char* ProtocolToString(Protocol protocol); + // The forced alternate protocol. If not-null, there is a protocol being // forced. static PortProtocolPair* forced_alternate_protocol_; diff --git a/net/http/http_auth.cc b/net/http/http_auth.cc index e7002cf..d5d6e0c 100644 --- a/net/http/http_auth.cc +++ b/net/http/http_auth.cc @@ -26,7 +26,7 @@ void HttpAuth::ChooseBestChallenge( const HttpResponseHeaders* headers, Target target, const GURL& origin, - const std::set<std::string>& disabled_schemes, + const std::set<Scheme>& disabled_schemes, const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler) { DCHECK(http_auth_handler_factory); @@ -47,7 +47,7 @@ void HttpAuth::ChooseBestChallenge( continue; } if (cur.get() && (!best.get() || best->score() < cur->score()) && - (disabled_schemes.find(cur->scheme()) == disabled_schemes.end())) + (disabled_schemes.find(cur->auth_scheme()) == disabled_schemes.end())) best.swap(cur); } handler->swap(best); @@ -58,15 +58,16 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse( HttpAuthHandler* handler, const HttpResponseHeaders* headers, Target target, - const std::set<std::string>& disabled_schemes, + const std::set<Scheme>& disabled_schemes, std::string* challenge_used) { DCHECK(handler); DCHECK(headers); DCHECK(challenge_used); challenge_used->clear(); - const std::string& current_scheme = handler->scheme(); + HttpAuth::Scheme current_scheme = handler->auth_scheme(); if (disabled_schemes.find(current_scheme) != disabled_schemes.end()) return HttpAuth::AUTHORIZATION_RESULT_REJECT; + std::string current_scheme_name = SchemeToString(current_scheme); const std::string header_name = GetChallengeHeaderName(target); void* iter = NULL; std::string challenge; @@ -74,7 +75,7 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse( HttpAuth::AUTHORIZATION_RESULT_INVALID; while (headers->EnumerateHeader(&iter, header_name, &challenge)) { HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end()); - if (!LowerCaseEqualsASCII(props.scheme(), current_scheme.c_str())) + if (!LowerCaseEqualsASCII(props.scheme(), current_scheme_name.c_str())) continue; authorization_result = handler->HandleAnotherChallenge(&props); if (authorization_result != HttpAuth::AUTHORIZATION_RESULT_INVALID) { @@ -86,6 +87,24 @@ HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse( return HttpAuth::AUTHORIZATION_RESULT_REJECT; } +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. @@ -106,24 +125,6 @@ void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin, HttpUtil::TrimLWS(¶ms_begin_, ¶ms_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); -} - // static std::string HttpAuth::GetChallengeHeaderName(Target target) { switch (target) { @@ -151,9 +152,34 @@ std::string HttpAuth::GetAuthorizationHeaderName(Target target) { } // static -std::string HttpAuth::GetAuthTargetString( - HttpAuth::Target target) { - return target == HttpAuth::AUTH_PROXY ? "proxy" : "server"; +std::string HttpAuth::GetAuthTargetString(Target target) { + switch (target) { + case AUTH_PROXY: + return "proxy"; + case AUTH_SERVER: + return "server"; + default: + NOTREACHED(); + return ""; + } +} + +// static +const char* HttpAuth::SchemeToString(Scheme scheme) { + static const char* const kSchemeNames[] = { + "basic", + "digest", + "ntlm", + "negotiate", + "mock", + }; + COMPILE_ASSERT(arraysize(kSchemeNames) == AUTH_SCHEME_MAX, + http_auth_scheme_names_incorrect_size); + if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) { + NOTREACHED(); + return "invalid_scheme"; + } + return kSchemeNames[scheme]; } } // namespace net diff --git a/net/http/http_auth.h b/net/http/http_auth.h index 0034b1f..ef779a1 100644 --- a/net/http/http_auth.h +++ b/net/http/http_auth.h @@ -80,6 +80,15 @@ class HttpAuth { IDENT_SRC_DEFAULT_CREDENTIALS, }; + enum Scheme { + AUTH_SCHEME_BASIC = 0, + AUTH_SCHEME_DIGEST, + AUTH_SCHEME_NTLM, + AUTH_SCHEME_NEGOTIATE, + AUTH_SCHEME_MOCK, + AUTH_SCHEME_MAX, + }; + // Helper structure used by HttpNetworkTransaction to track // the current identity being used for authorization. struct Identity { @@ -103,6 +112,9 @@ class HttpAuth { // messages. static std::string GetAuthTargetString(Target target); + // Returns a string representation of an authentication Scheme. + static const char* SchemeToString(Scheme scheme); + // Iterate through the challenge headers, and pick the best one that // we support. Obtains the implementation class for handling the challenge, // and passes it back in |*handler|. If no supported challenge was found, @@ -117,7 +129,7 @@ class HttpAuth { const HttpResponseHeaders* headers, Target target, const GURL& origin, - const std::set<std::string>& disabled_schemes, + const std::set<Scheme>& disabled_schemes, const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler); @@ -146,7 +158,7 @@ class HttpAuth { HttpAuthHandler* handler, const HttpResponseHeaders* headers, Target target, - const std::set<std::string>& disabled_schemes, + const std::set<Scheme>& disabled_schemes, std::string* challenge_used); // Breaks up a challenge string into the the auth scheme and parameter list, diff --git a/net/http/http_auth_cache.cc b/net/http/http_auth_cache.cc index cc80861..95d0d69 100644 --- a/net/http/http_auth_cache.cc +++ b/net/http/http_auth_cache.cc @@ -68,7 +68,7 @@ HttpAuthCache::~HttpAuthCache() { // Performance: O(n), where n is the number of realm entries. HttpAuthCache::Entry* HttpAuthCache::Lookup(const GURL& origin, const std::string& realm, - const std::string& scheme) { + HttpAuth::Scheme scheme) { CheckOriginIsValid(origin); // Linear scan through the realm entries. @@ -104,7 +104,7 @@ HttpAuthCache::Entry* HttpAuthCache::LookupByPath(const GURL& origin, HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin, const std::string& realm, - const std::string& scheme, + HttpAuth::Scheme scheme, const std::string& auth_challenge, const string16& username, const string16& password, @@ -143,8 +143,15 @@ HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin, HttpAuthCache::Entry::~Entry() { } +void HttpAuthCache::Entry::UpdateStaleChallenge( + const std::string& auth_challenge) { + auth_challenge_ = auth_challenge; + nonce_count_ = 1; +} + HttpAuthCache::Entry::Entry() - : nonce_count_(0) { + : scheme_(HttpAuth::AUTH_SCHEME_MAX), + nonce_count_(0) { } void HttpAuthCache::Entry::AddPath(const std::string& path) { @@ -175,15 +182,9 @@ bool HttpAuthCache::Entry::HasEnclosingPath(const std::string& dir) { return false; } -void HttpAuthCache::Entry::UpdateStaleChallenge( - const std::string& auth_challenge) { - auth_challenge_ = auth_challenge; - nonce_count_ = 1; -} - bool HttpAuthCache::Remove(const GURL& origin, const std::string& realm, - const std::string& scheme, + HttpAuth::Scheme scheme, const string16& username, const string16& password) { for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) { @@ -201,7 +202,7 @@ bool HttpAuthCache::Remove(const GURL& origin, bool HttpAuthCache::UpdateStaleChallenge(const GURL& origin, const std::string& realm, - const std::string& scheme, + HttpAuth::Scheme scheme, const std::string& auth_challenge) { HttpAuthCache::Entry* entry = Lookup(origin, realm, scheme); if (!entry) diff --git a/net/http/http_auth_cache.h b/net/http/http_auth_cache.h index b6c382c..a130956 100644 --- a/net/http/http_auth_cache.h +++ b/net/http/http_auth_cache.h @@ -13,6 +13,7 @@ #include "base/ref_counted.h" #include "base/string16.h" #include "googleurl/src/gurl.h" +#include "net/http/http_auth.h" namespace net { @@ -42,10 +43,11 @@ class HttpAuthCache { // scheme |scheme|. // |origin| - the {scheme, host, port} of the server. // |realm| - case sensitive realm string. - // |scheme| - case sensitive authentication scheme, should be lower-case. + // |scheme| - the authentication scheme (i.e. basic, negotiate). // returns - the matched entry or NULL. - Entry* Lookup(const GURL& origin, const std::string& realm, - const std::string& scheme); + Entry* Lookup(const GURL& origin, + const std::string& realm, + HttpAuth::Scheme scheme); // Find the entry on server |origin| whose protection space includes // |path|. This uses the assumption in RFC 2617 section 2 that deeper @@ -62,7 +64,7 @@ class HttpAuthCache { // paths list. // |origin| - the {scheme, host, port} of the server. // |realm| - the auth realm for the challenge. - // |scheme| - the authentication scheme for the challenge. + // |scheme| - the authentication scheme (i.e. basic, negotiate). // |username| - login information for the realm. // |password| - login information for the realm. // |path| - absolute path for a resource contained in the protection @@ -70,7 +72,7 @@ class HttpAuthCache { // returns - the entry that was just added/updated. Entry* Add(const GURL& origin, const std::string& realm, - const std::string& scheme, + HttpAuth::Scheme scheme, const std::string& auth_challenge, const string16& username, const string16& password, @@ -80,13 +82,13 @@ class HttpAuthCache { // if one exists AND if the cached identity matches (|username|, |password|). // |origin| - the {scheme, host, port} of the server. // |realm| - case sensitive realm string. - // |scheme| - authentication scheme + // |scheme| - the authentication scheme (i.e. basic, negotiate). // |username| - condition to match. // |password| - condition to match. // returns - true if an entry was removed. bool Remove(const GURL& origin, const std::string& realm, - const std::string& scheme, + HttpAuth::Scheme scheme, const string16& username, const string16& password); @@ -97,7 +99,7 @@ class HttpAuthCache { // cache, false otherwise. bool UpdateStaleChallenge(const GURL& origin, const std::string& realm, - const std::string& scheme, + HttpAuth::Scheme scheme, const std::string& auth_challenge); private: @@ -119,8 +121,8 @@ class HttpAuthCache::Entry { return realm_; } - // The authentication scheme string of the challenge - const std::string scheme() const { + // The authentication scheme of the challenge. + HttpAuth::Scheme scheme() const { return scheme_; } @@ -161,10 +163,10 @@ class HttpAuthCache::Entry { // Returns true if |dir| is contained within the realm's protection space. bool HasEnclosingPath(const std::string& dir); - // |origin_| contains the {scheme, host, port} of the server. + // |origin_| contains the {protocol, host, port} of the server. GURL origin_; std::string realm_; - std::string scheme_; + HttpAuth::Scheme scheme_; // Identity. std::string auth_challenge_; diff --git a/net/http/http_auth_cache_unittest.cc b/net/http/http_auth_cache_unittest.cc index 2c28969..e940527 100644 --- a/net/http/http_auth_cache_unittest.cc +++ b/net/http/http_auth_cache_unittest.cc @@ -19,10 +19,11 @@ namespace { class MockAuthHandler : public HttpAuthHandler { public: - MockAuthHandler(const char* scheme, const std::string& realm, + MockAuthHandler(HttpAuth::Scheme scheme, + const std::string& realm, HttpAuth::Target target) { // Can't use initializer list since these are members of the base class. - scheme_ = scheme; + auth_scheme_ = scheme; realm_ = realm; score_ = 1; target_ = target; @@ -53,8 +54,6 @@ class MockAuthHandler : public HttpAuthHandler { ~MockAuthHandler() {} }; -const char* kBasic = "basic"; -const char* kDigest = "digest"; const char* kRealm1 = "Realm1"; const char* kRealm2 = "Realm2"; const char* kRealm3 = "Realm3"; @@ -80,48 +79,58 @@ TEST(HttpAuthCacheTest, Basic) { // Add cache entries for 3 realms: "Realm1", "Realm2", "Realm3" scoped_ptr<HttpAuthHandler> realm1_handler( - new MockAuthHandler(kBasic, kRealm1, HttpAuth::AUTH_SERVER)); - cache.Add(origin, realm1_handler->realm(), realm1_handler->scheme(), + new MockAuthHandler(HttpAuth::AUTH_SCHEME_BASIC, + kRealm1, + HttpAuth::AUTH_SERVER)); + cache.Add(origin, realm1_handler->realm(), realm1_handler->auth_scheme(), "Basic realm=Realm1", ASCIIToUTF16("realm1-user"), ASCIIToUTF16("realm1-password"), "/foo/bar/index.html"); scoped_ptr<HttpAuthHandler> realm2_handler( - new MockAuthHandler(kBasic, kRealm2, HttpAuth::AUTH_SERVER)); - cache.Add(origin, realm2_handler->realm(), realm2_handler->scheme(), + new MockAuthHandler(HttpAuth::AUTH_SCHEME_BASIC, + kRealm2, + HttpAuth::AUTH_SERVER)); + cache.Add(origin, realm2_handler->realm(), realm2_handler->auth_scheme(), "Basic realm=Realm2", ASCIIToUTF16("realm2-user"), ASCIIToUTF16("realm2-password"), "/foo2/index.html"); scoped_ptr<HttpAuthHandler> realm3_basic_handler( - new MockAuthHandler(kBasic, kRealm3, HttpAuth::AUTH_PROXY)); + new MockAuthHandler(HttpAuth::AUTH_SCHEME_BASIC, + kRealm3, + HttpAuth::AUTH_PROXY)); cache.Add(origin, realm3_basic_handler->realm(), - realm3_basic_handler->scheme(), "Basic realm=Realm3", + realm3_basic_handler->auth_scheme(), "Basic realm=Realm3", ASCIIToUTF16("realm3-basic-user"), ASCIIToUTF16("realm3-basic-password"), ""); scoped_ptr<HttpAuthHandler> realm3_digest_handler( - new MockAuthHandler(kDigest, kRealm3, HttpAuth::AUTH_PROXY)); + new MockAuthHandler(HttpAuth::AUTH_SCHEME_DIGEST, + kRealm3, + HttpAuth::AUTH_PROXY)); cache.Add(origin, realm3_digest_handler->realm(), - realm3_digest_handler->scheme(), "Digest realm=Realm3", + realm3_digest_handler->auth_scheme(), "Digest realm=Realm3", ASCIIToUTF16("realm3-digest-user"), ASCIIToUTF16("realm3-digest-password"), "/baz/index.html"); // There is no Realm4 - entry = cache.Lookup(origin, kRealm4, kBasic); + entry = cache.Lookup(origin, kRealm4, HttpAuth::AUTH_SCHEME_BASIC); EXPECT_TRUE(NULL == entry); // While Realm3 does exist, the origin scheme is wrong. entry = cache.Lookup(GURL("https://www.google.com"), kRealm3, - kBasic); + HttpAuth::AUTH_SCHEME_BASIC); EXPECT_TRUE(NULL == entry); // Realm, origin scheme ok, authentication scheme wrong - entry = cache.Lookup(GURL("http://www.google.com"), kRealm1, kDigest); + entry = cache.Lookup + (GURL("http://www.google.com"), kRealm1, HttpAuth::AUTH_SCHEME_DIGEST); EXPECT_TRUE(NULL == entry); // Valid lookup by origin, realm, scheme. - entry = cache.Lookup(GURL("http://www.google.com:80"), kRealm3, kBasic); + entry = cache.Lookup( + GURL("http://www.google.com:80"), kRealm3, HttpAuth::AUTH_SCHEME_BASIC); ASSERT_FALSE(NULL == entry); - EXPECT_EQ(kBasic, entry->scheme()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme()); EXPECT_EQ(kRealm3, entry->realm()); EXPECT_EQ("Basic realm=Realm3", entry->auth_challenge()); EXPECT_EQ(ASCIIToUTF16("realm3-basic-user"), entry->username()); @@ -129,25 +138,27 @@ TEST(HttpAuthCacheTest, Basic) { // Valid lookup by origin, realm, scheme when there's a duplicate // origin, realm in the cache - entry = cache.Lookup(GURL("http://www.google.com:80"), kRealm3, kDigest); + entry = cache.Lookup( + GURL("http://www.google.com:80"), kRealm3, HttpAuth::AUTH_SCHEME_DIGEST); ASSERT_FALSE(NULL == entry); - EXPECT_EQ(kDigest, entry->scheme()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, entry->scheme()); EXPECT_EQ(kRealm3, entry->realm()); EXPECT_EQ("Digest realm=Realm3", entry->auth_challenge()); EXPECT_EQ(ASCIIToUTF16("realm3-digest-user"), entry->username()); EXPECT_EQ(ASCIIToUTF16("realm3-digest-password"), entry->password()); // Valid lookup by realm. - entry = cache.Lookup(origin, kRealm2, kBasic); + entry = cache.Lookup(origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC); ASSERT_FALSE(NULL == entry); - EXPECT_EQ(kBasic, entry->scheme()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme()); EXPECT_EQ(kRealm2, entry->realm()); EXPECT_EQ("Basic realm=Realm2", entry->auth_challenge()); EXPECT_EQ(ASCIIToUTF16("realm2-user"), entry->username()); EXPECT_EQ(ASCIIToUTF16("realm2-password"), entry->password()); // Check that subpaths are recognized. - HttpAuthCache::Entry* realm2_entry = cache.Lookup(origin, kRealm2, kBasic); + HttpAuthCache::Entry* realm2_entry = cache.Lookup( + origin, kRealm2, HttpAuth::AUTH_SCHEME_BASIC); EXPECT_FALSE(NULL == realm2_entry); // Positive tests: entry = cache.LookupByPath(origin, "/foo2/index.html"); @@ -171,7 +182,7 @@ TEST(HttpAuthCacheTest, Basic) { // Confirm we find the same realm, different auth scheme by path lookup HttpAuthCache::Entry* realm3_digest_entry = - cache.Lookup(origin, kRealm3, kDigest); + cache.Lookup(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST); EXPECT_FALSE(NULL == realm3_digest_entry); entry = cache.LookupByPath(origin, "/baz/index.html"); EXPECT_TRUE(realm3_digest_entry == entry); @@ -182,7 +193,7 @@ TEST(HttpAuthCacheTest, Basic) { // Confirm we find the same realm, different auth scheme by path lookup HttpAuthCache::Entry* realm3DigestEntry = - cache.Lookup(origin, kRealm3, kDigest); + cache.Lookup(origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST); EXPECT_FALSE(NULL == realm3DigestEntry); entry = cache.LookupByPath(origin, "/baz/index.html"); EXPECT_TRUE(realm3DigestEntry == entry); @@ -194,7 +205,7 @@ TEST(HttpAuthCacheTest, Basic) { // Lookup using empty path (may be used for proxy). entry = cache.LookupByPath(origin, ""); EXPECT_FALSE(NULL == entry); - EXPECT_EQ(kBasic, entry->scheme()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, entry->scheme()); EXPECT_EQ(kRealm3, entry->realm()); } @@ -238,17 +249,18 @@ TEST(HttpAuthCacheTest, AddToExistingEntry) { const std::string auth_challenge = "Basic realm=MyRealm"; scoped_ptr<HttpAuthHandler> handler( - new MockAuthHandler(kBasic, "MyRealm", HttpAuth::AUTH_SERVER)); - + new MockAuthHandler( + HttpAuth::AUTH_SCHEME_BASIC, "MyRealm", HttpAuth::AUTH_SERVER)); HttpAuthCache::Entry* orig_entry = cache.Add( - origin, handler->realm(), handler->scheme(), auth_challenge, + origin, handler->realm(), handler->auth_scheme(), auth_challenge, ASCIIToUTF16("user1"), ASCIIToUTF16("password1"), "/x/y/z/"); - cache.Add(origin, handler->realm(), handler->scheme(), auth_challenge, + cache.Add(origin, handler->realm(), handler->auth_scheme(), auth_challenge, ASCIIToUTF16("user2"), ASCIIToUTF16("password2"), "/z/y/x/"); - cache.Add(origin, handler->realm(), handler->scheme(), auth_challenge, + cache.Add(origin, handler->realm(), handler->auth_scheme(), auth_challenge, ASCIIToUTF16("user3"), ASCIIToUTF16("password3"), "/z/y"); - HttpAuthCache::Entry* entry = cache.Lookup(origin, "MyRealm", kBasic); + HttpAuthCache::Entry* entry = cache.Lookup( + origin, "MyRealm", HttpAuth::AUTH_SCHEME_BASIC); EXPECT_TRUE(entry == orig_entry); EXPECT_EQ(ASCIIToUTF16("user3"), entry->username()); @@ -263,64 +275,80 @@ TEST(HttpAuthCacheTest, Remove) { GURL origin("http://foobar2.com"); scoped_ptr<HttpAuthHandler> realm1_handler( - new MockAuthHandler(kBasic, kRealm1, HttpAuth::AUTH_SERVER)); + new MockAuthHandler( + HttpAuth::AUTH_SCHEME_BASIC, kRealm1, HttpAuth::AUTH_SERVER)); scoped_ptr<HttpAuthHandler> realm2_handler( - new MockAuthHandler(kBasic, kRealm2, HttpAuth::AUTH_SERVER)); + new MockAuthHandler( + HttpAuth::AUTH_SCHEME_BASIC, kRealm2, HttpAuth::AUTH_SERVER)); scoped_ptr<HttpAuthHandler> realm3_basic_handler( - new MockAuthHandler(kBasic, kRealm3, HttpAuth::AUTH_SERVER)); + new MockAuthHandler( + HttpAuth::AUTH_SCHEME_BASIC, kRealm3, HttpAuth::AUTH_SERVER)); scoped_ptr<HttpAuthHandler> realm3_digest_handler( - new MockAuthHandler(kDigest, kRealm3, HttpAuth::AUTH_SERVER)); + new MockAuthHandler( + HttpAuth::AUTH_SCHEME_DIGEST, kRealm3, HttpAuth::AUTH_SERVER)); HttpAuthCache cache; - cache.Add(origin, realm1_handler->realm(), realm1_handler->scheme(), + cache.Add(origin, realm1_handler->realm(), realm1_handler->auth_scheme(), "basic realm=Realm1", kAlice, k123, "/"); - cache.Add(origin, realm2_handler->realm(), realm2_handler->scheme(), + cache.Add(origin, realm2_handler->realm(), realm2_handler->auth_scheme(), "basic realm=Realm2", ASCIIToUTF16("bob"), ASCIIToUTF16("princess"), "/"); cache.Add(origin, realm3_basic_handler->realm(), - realm3_basic_handler->scheme(), "basic realm=Realm3", + realm3_basic_handler->auth_scheme(), "basic realm=Realm3", kAdmin, kPassword, "/"); cache.Add(origin, realm3_digest_handler->realm(), - realm3_digest_handler->scheme(), "digest realm=Realm3", + realm3_digest_handler->auth_scheme(), "digest realm=Realm3", kRoot, kWileCoyote, "/"); // Fails, because there is no realm "Realm4". - EXPECT_FALSE(cache.Remove(origin, kRealm4, kBasic, kAlice, k123)); + EXPECT_FALSE(cache.Remove( + origin, kRealm4, HttpAuth::AUTH_SCHEME_BASIC, kAlice, k123)); // Fails because the origin is wrong. - EXPECT_FALSE(cache.Remove( - GURL("http://foobar2.com:100"), kRealm1, kBasic, kAlice, k123)); + EXPECT_FALSE(cache.Remove(GURL("http://foobar2.com:100"), + kRealm1, + HttpAuth::AUTH_SCHEME_BASIC, + kAlice, + k123)); // Fails because the username is wrong. - EXPECT_FALSE(cache.Remove(origin, kRealm1, kBasic, kAlice2, k123)); + EXPECT_FALSE(cache.Remove( + origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kAlice2, k123)); // Fails because the password is wrong. - EXPECT_FALSE(cache.Remove(origin, kRealm1, kBasic, kAlice, k1234)); + EXPECT_FALSE(cache.Remove( + origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kAlice, k1234)); // Fails because the authentication type is wrong. - EXPECT_FALSE(cache.Remove(origin, kRealm1, kDigest, kAlice, k123)); + EXPECT_FALSE(cache.Remove( + origin, kRealm1, HttpAuth::AUTH_SCHEME_DIGEST, kAlice, k123)); // Succeeds. - EXPECT_TRUE(cache.Remove(origin, kRealm1, kBasic, kAlice, k123)); + EXPECT_TRUE(cache.Remove( + origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kAlice, k123)); // Fails because we just deleted the entry! - EXPECT_FALSE(cache.Remove(origin, kRealm1, kBasic, kAlice, k123)); + EXPECT_FALSE(cache.Remove( + origin, kRealm1, HttpAuth::AUTH_SCHEME_BASIC, kAlice, k123)); // Succeed when there are two authentication types for the same origin,realm. - EXPECT_TRUE(cache.Remove(origin, kRealm3, kDigest, kRoot, kWileCoyote)); + EXPECT_TRUE(cache.Remove( + origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST, kRoot, kWileCoyote)); // Succeed as above, but when entries were added in opposite order cache.Add(origin, realm3_digest_handler->realm(), - realm3_digest_handler->scheme(), "digest realm=Realm3", + realm3_digest_handler->auth_scheme(), "digest realm=Realm3", kRoot, kWileCoyote, "/"); - EXPECT_TRUE(cache.Remove(origin, kRealm3, kBasic, kAdmin, kPassword)); + EXPECT_TRUE(cache.Remove( + origin, kRealm3, HttpAuth::AUTH_SCHEME_BASIC, kAdmin, kPassword)); // Make sure that removing one entry still leaves the other available for // lookup. - HttpAuthCache::Entry* entry = cache.Lookup(origin, kRealm3, kDigest); + HttpAuthCache::Entry* entry = cache.Lookup( + origin, kRealm3, HttpAuth::AUTH_SCHEME_DIGEST); EXPECT_FALSE(NULL == entry); } @@ -328,11 +356,12 @@ TEST(HttpAuthCacheTest, UpdateStaleChallenge) { HttpAuthCache cache; GURL origin("http://foobar2.com"); scoped_ptr<HttpAuthHandler> digest_handler( - new MockAuthHandler(kDigest, kRealm1, HttpAuth::AUTH_PROXY)); + new MockAuthHandler( + HttpAuth::AUTH_SCHEME_DIGEST, kRealm1, HttpAuth::AUTH_PROXY)); HttpAuthCache::Entry* entry_pre = cache.Add( origin, digest_handler->realm(), - digest_handler->scheme(), + digest_handler->auth_scheme(), "Digest realm=Realm1," "nonce=\"s3MzvFhaBAA=4c520af5acd9d8d7ae26947529d18c8eae1e98f4\"", ASCIIToUTF16("realm-digest-user"), @@ -347,7 +376,7 @@ TEST(HttpAuthCacheTest, UpdateStaleChallenge) { bool update_success = cache.UpdateStaleChallenge( origin, digest_handler->realm(), - digest_handler->scheme(), + digest_handler->auth_scheme(), "Digest realm=Realm1," "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\"," "stale=\"true\""); @@ -358,7 +387,7 @@ TEST(HttpAuthCacheTest, UpdateStaleChallenge) { HttpAuthCache::Entry* entry_post = cache.Lookup( origin, digest_handler->realm(), - digest_handler->scheme()); + digest_handler->auth_scheme()); ASSERT_TRUE(entry_post != NULL); EXPECT_EQ(2, entry_post->IncrementNonceCount()); @@ -366,7 +395,7 @@ TEST(HttpAuthCacheTest, UpdateStaleChallenge) { bool update_failure = cache.UpdateStaleChallenge( origin, kRealm2, - digest_handler->scheme(), + digest_handler->auth_scheme(), "Digest realm=Realm2," "nonce=\"claGgoRXBAA=7583377687842fdb7b56ba0555d175baa0b800e3\"," "stale=\"true\""); @@ -392,13 +421,14 @@ class HttpAuthCacheEvictionTest : public testing::Test { } void AddPathToRealm(int realm_i, int path_i) { - cache_.Add(origin_, GenerateRealm(realm_i), kBasic, "", + cache_.Add(origin_, GenerateRealm(realm_i), HttpAuth::AUTH_SCHEME_BASIC, "", kUsername, kPassword, GeneratePath(realm_i, path_i)); } void CheckRealmExistence(int realm_i, bool exists) { const HttpAuthCache::Entry* entry = - cache_.Lookup(origin_, GenerateRealm(realm_i), kBasic); + cache_.Lookup( + origin_, GenerateRealm(realm_i), HttpAuth::AUTH_SCHEME_BASIC); if (exists) { EXPECT_FALSE(entry == NULL); EXPECT_EQ(GenerateRealm(realm_i), entry->realm()); diff --git a/net/http/http_auth_controller.cc b/net/http/http_auth_controller.cc index ddd0bdb..f438ea4 100644 --- a/net/http/http_auth_controller.cc +++ b/net/http/http_auth_controller.cc @@ -96,8 +96,8 @@ void HistogramAuthEvent(HttpAuthHandler* handler, AuthEvent auth_event) { DCHECK_EQ(first_thread, base::PlatformThread::CurrentId()); #endif - HttpAuthHandler::AuthScheme auth_scheme = handler->auth_scheme(); - DCHECK(auth_scheme >= 0 && auth_scheme < HttpAuthHandler::AUTH_SCHEME_MAX); + HttpAuth::Scheme auth_scheme = handler->auth_scheme(); + DCHECK(auth_scheme >= 0 && auth_scheme < HttpAuth::AUTH_SCHEME_MAX); // Record start and rejection events for authentication. // @@ -111,7 +111,7 @@ void HistogramAuthEvent(HttpAuthHandler* handler, AuthEvent auth_event) { // Negotiate Start: 6 // Negotiate Reject: 7 static const int kEventBucketsEnd = - HttpAuthHandler::AUTH_SCHEME_MAX * AUTH_EVENT_MAX; + HttpAuth::AUTH_SCHEME_MAX * AUTH_EVENT_MAX; int event_bucket = auth_scheme * AUTH_EVENT_MAX + auth_event; DCHECK(event_bucket >= 0 && event_bucket < kEventBucketsEnd); UMA_HISTOGRAM_ENUMERATION("Net.HttpAuthCount", event_bucket, @@ -139,7 +139,7 @@ void HistogramAuthEvent(HttpAuthHandler* handler, AuthEvent auth_event) { if (auth_event != AUTH_EVENT_START) return; static const int kTargetBucketsEnd = - HttpAuthHandler::AUTH_SCHEME_MAX * AUTH_TARGET_MAX; + HttpAuth::AUTH_SCHEME_MAX * AUTH_TARGET_MAX; AuthTarget auth_target = DetermineAuthTarget(handler); int target_bucket = auth_scheme * AUTH_TARGET_MAX + auth_target; DCHECK(target_bucket >= 0 && target_bucket < kTargetBucketsEnd); @@ -282,7 +282,7 @@ int HttpAuthController::HandleAuthChallenge( case HttpAuth::AUTHORIZATION_RESULT_STALE: if (http_auth_cache_->UpdateStaleChallenge(auth_origin_, handler_->realm(), - handler_->scheme(), + handler_->auth_scheme(), challenge_used)) { handler_.reset(); identity_ = HttpAuth::Identity(); @@ -388,7 +388,7 @@ void HttpAuthController::ResetAuth(const string16& username, break; default: http_auth_cache_->Add(auth_origin_, handler_->realm(), - handler_->scheme(), handler_->challenge(), + handler_->auth_scheme(), handler_->challenge(), identity_.username, identity_.password, auth_path_); break; @@ -426,7 +426,7 @@ void HttpAuthController::InvalidateRejectedAuthFromCache() { // Note: we require the username/password to match before invalidating // since the entry in the cache may be newer than what we used last time. http_auth_cache_->Remove(auth_origin_, handler_->realm(), - handler_->scheme(), identity_.username, + handler_->auth_scheme(), identity_.username, identity_.password); } @@ -453,7 +453,7 @@ bool HttpAuthController::SelectNextAuthIdentityToTry() { // Check the auth cache for a realm entry. HttpAuthCache::Entry* entry = http_auth_cache_->Lookup(auth_origin_, handler_->realm(), - handler_->scheme()); + handler_->auth_scheme()); if (entry) { identity_.source = HttpAuth::IDENT_SRC_REALM_LOOKUP; @@ -487,7 +487,8 @@ void HttpAuthController::PopulateAuthChallenge() { auth_info_ = new AuthChallengeInfo; auth_info_->is_proxy = target_ == HttpAuth::AUTH_PROXY; auth_info_->host_and_port = ASCIIToWide(GetHostAndPort(auth_origin_)); - auth_info_->scheme = ASCIIToWide(handler_->scheme()); + auth_info_->scheme = ASCIIToWide( + HttpAuth::SchemeToString(handler_->auth_scheme())); // TODO(eroman): decode realm according to RFC 2047. auth_info_->realm = ASCIIToWide(handler_->realm()); } @@ -497,7 +498,7 @@ void HttpAuthController::OnIOComplete(int result) { // This error occurs with GSSAPI, if the user has not already logged in. // In that case, disable the current scheme as it cannot succeed. if (result == ERR_MISSING_AUTH_CREDENTIALS) { - DisableAuthScheme(handler_->scheme()); + DisableAuthScheme(handler_->auth_scheme()); auth_token_.clear(); result = OK; } @@ -513,12 +514,12 @@ scoped_refptr<AuthChallengeInfo> HttpAuthController::auth_info() { return auth_info_; } -bool HttpAuthController::IsAuthSchemeDisabled(const std::string& scheme) const { +bool HttpAuthController::IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const { DCHECK(CalledOnValidThread()); return disabled_schemes_.find(scheme) != disabled_schemes_.end(); } -void HttpAuthController::DisableAuthScheme(const std::string& scheme) { +void HttpAuthController::DisableAuthScheme(HttpAuth::Scheme scheme) { DCHECK(CalledOnValidThread()); disabled_schemes_.insert(scheme); } diff --git a/net/http/http_auth_controller.h b/net/http/http_auth_controller.h index 85d9fa1..0b7f430 100644 --- a/net/http/http_auth_controller.h +++ b/net/http/http_auth_controller.h @@ -69,8 +69,8 @@ class HttpAuthController : public base::RefCounted<HttpAuthController>, virtual scoped_refptr<AuthChallengeInfo> auth_info(); - virtual bool IsAuthSchemeDisabled(const std::string& scheme) const; - virtual void DisableAuthScheme(const std::string& scheme); + virtual bool IsAuthSchemeDisabled(HttpAuth::Scheme scheme) const; + virtual void DisableAuthScheme(HttpAuth::Scheme scheme); private: // So that we can mock this object. @@ -146,7 +146,7 @@ class HttpAuthController : public base::RefCounted<HttpAuthController>, HttpAuthCache* const http_auth_cache_; HttpAuthHandlerFactory* const http_auth_handler_factory_; - std::set<std::string> disabled_schemes_; + std::set<HttpAuth::Scheme> disabled_schemes_; CompletionCallbackImpl<HttpAuthController> io_callback_; CompletionCallback* user_callback_; diff --git a/net/http/http_auth_filter.cc b/net/http/http_auth_filter.cc index a61e7f7..2109e4d 100644 --- a/net/http/http_auth_filter.cc +++ b/net/http/http_auth_filter.cc @@ -24,21 +24,6 @@ HttpAuthFilterWhitelist::HttpAuthFilterWhitelist( HttpAuthFilterWhitelist::~HttpAuthFilterWhitelist() { } -void HttpAuthFilterWhitelist::SetWhitelist( - const std::string& server_whitelist) { - rules_.ParseFromString(server_whitelist); -} - -bool HttpAuthFilterWhitelist::IsValid(const GURL& url, - HttpAuth::Target target) const { - if ((target != HttpAuth::AUTH_SERVER) && (target != HttpAuth::AUTH_PROXY)) - return false; - // All proxies pass - if (target == HttpAuth::AUTH_PROXY) - return true; - return rules_.Matches(url); -} - // Add a new domain |filter| to the whitelist, if it's not already there bool HttpAuthFilterWhitelist::AddFilter(const std::string& filter, HttpAuth::Target target) { @@ -55,4 +40,19 @@ void HttpAuthFilterWhitelist::AddRuleToBypassLocal() { rules_.AddRuleToBypassLocal(); } +bool HttpAuthFilterWhitelist::IsValid(const GURL& url, + HttpAuth::Target target) const { + if ((target != HttpAuth::AUTH_SERVER) && (target != HttpAuth::AUTH_PROXY)) + return false; + // All proxies pass + if (target == HttpAuth::AUTH_PROXY) + return true; + return rules_.Matches(url); +} + +void HttpAuthFilterWhitelist::SetWhitelist( + const std::string& server_whitelist) { + rules_.ParseFromString(server_whitelist); +} + } // namespace net diff --git a/net/http/http_auth_filter.h b/net/http/http_auth_filter.h index 334bc91..81d414c 100644 --- a/net/http/http_auth_filter.h +++ b/net/http/http_auth_filter.h @@ -37,9 +37,6 @@ class HttpAuthFilterWhitelist : public HttpAuthFilter { explicit HttpAuthFilterWhitelist(const std::string& server_whitelist); virtual ~HttpAuthFilterWhitelist(); - // HttpAuthFilter methods: - virtual bool IsValid(const GURL& url, HttpAuth::Target target) const; - // Adds an individual URL |filter| to the list, of the specified |target|. bool AddFilter(const std::string& filter, HttpAuth::Target target); @@ -48,6 +45,9 @@ class HttpAuthFilterWhitelist : public HttpAuthFilter { const ProxyBypassRules& rules() const { return rules_; } + // HttpAuthFilter methods: + virtual bool IsValid(const GURL& url, HttpAuth::Target target) const; + private: // Installs the whitelist. // |server_whitelist| is parsed by ProxyBypassRules. diff --git a/net/http/http_auth_gssapi_posix_unittest.cc b/net/http/http_auth_gssapi_posix_unittest.cc index f83acb4..43a3285 100644 --- a/net/http/http_auth_gssapi_posix_unittest.cc +++ b/net/http/http_auth_gssapi_posix_unittest.cc @@ -109,23 +109,20 @@ TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) { 1, // Locally initiated 1); // Open test::MockGSSAPILibrary::SecurityContextQuery queries[] = { - { "Negotiate", // Package name - GSS_S_CONTINUE_NEEDED, // Major response code - 0, // Minor response code - context1, // Context - { 0, NULL }, // Expected input token - { arraysize(kAuthResponse), - const_cast<char*>(kAuthResponse) } // Output token - }, - { "Negotiate", // Package name - GSS_S_COMPLETE, // Major response code - 0, // Minor response code - context2, // Context - { arraysize(kAuthResponse), - const_cast<char*>(kAuthResponse) }, // Expected input token - { arraysize(kAuthResponse), - const_cast<char*>(kAuthResponse) } // Output token - }, + test::MockGSSAPILibrary::SecurityContextQuery( + "Negotiate", // Package name + GSS_S_CONTINUE_NEEDED, // Major response code + 0, // Minor response code + context1, // Context + NULL, // Expected input token + kAuthResponse), // Output token + test::MockGSSAPILibrary::SecurityContextQuery( + "Negotiate", // Package name + GSS_S_COMPLETE, // Major response code + 0, // Minor response code + context2, // Context + kAuthResponse, // Expected input token + kAuthResponse) // Output token }; for (size_t i = 0; i < arraysize(queries); ++i) { diff --git a/net/http/http_auth_handler.cc b/net/http/http_auth_handler.cc index 130123b..51bd6aa 100644 --- a/net/http/http_auth_handler.cc +++ b/net/http/http_auth_handler.cc @@ -5,14 +5,12 @@ #include "net/http/http_auth_handler.h" #include "base/logging.h" -#include "base/string_util.h" -#include "base/stringprintf.h" #include "net/base/net_errors.h" namespace net { HttpAuthHandler::HttpAuthHandler() - : auth_scheme_(AUTH_SCHEME_MAX), + : auth_scheme_(HttpAuth::AUTH_SCHEME_MAX), score_(-1), target_(HttpAuth::AUTH_NONE), properties_(-1), @@ -41,10 +39,9 @@ bool HttpAuthHandler::InitFromChallenge( // Init() is expected to set the scheme, realm, score, and properties. The // realm may be empty. - DCHECK(!ok || !scheme().empty()); DCHECK(!ok || score_ != -1); DCHECK(!ok || properties_ != -1); - DCHECK(!ok || auth_scheme_ != AUTH_SCHEME_MAX); + DCHECK(!ok || auth_scheme_ != HttpAuth::AUTH_SCHEME_MAX); return ok; } diff --git a/net/http/http_auth_handler.h b/net/http/http_auth_handler.h index 65340df..0a574e5 100644 --- a/net/http/http_auth_handler.h +++ b/net/http/http_auth_handler.h @@ -22,14 +22,6 @@ struct HttpRequestInfo; // HttpAuthHandler objects are typically created by an HttpAuthHandlerFactory. class HttpAuthHandler { public: - enum AuthScheme { - AUTH_SCHEME_BASIC = 0, - AUTH_SCHEME_DIGEST, - AUTH_SCHEME_NTLM, - AUTH_SCHEME_NEGOTIATE, - AUTH_SCHEME_MAX, - }; - HttpAuthHandler(); virtual ~HttpAuthHandler(); @@ -84,15 +76,10 @@ class HttpAuthHandler { std::string* auth_token); // The authentication scheme as an enumerated value. - AuthScheme auth_scheme() const { + HttpAuth::Scheme auth_scheme() const { return auth_scheme_; } - // Lowercase name of the auth scheme - const std::string& scheme() const { - return scheme_; - } - // The realm value that was parsed during Init(). const std::string& realm() const { return realm_; @@ -171,10 +158,7 @@ class HttpAuthHandler { std::string* auth_token) = 0; // The auth-scheme as an enumerated value. - AuthScheme auth_scheme_; - - // The lowercase auth-scheme {"basic", "digest", "ntlm", "negotiate"} - std::string scheme_; + HttpAuth::Scheme auth_scheme_; // The realm. Used by "basic" and "digest". std::string realm_; diff --git a/net/http/http_auth_handler_basic.cc b/net/http/http_auth_handler_basic.cc index 8ee775d..e48aa67 100644 --- a/net/http/http_auth_handler_basic.cc +++ b/net/http/http_auth_handler_basic.cc @@ -23,8 +23,7 @@ namespace net { // We allow it to be compatibility with certain embedded webservers that don't // include a realm (see http://crbug.com/20984.) bool HttpAuthHandlerBasic::Init(HttpAuth::ChallengeTokenizer* challenge) { - auth_scheme_ = AUTH_SCHEME_BASIC; - scheme_ = "basic"; + auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC; score_ = 1; properties_ = 0; return ParseChallenge(challenge); diff --git a/net/http/http_auth_handler_digest.cc b/net/http/http_auth_handler_digest.cc index 2b103f4..e8cb819 100644 --- a/net/http/http_auth_handler_digest.cc +++ b/net/http/http_auth_handler_digest.cc @@ -74,46 +74,60 @@ std::string HttpAuthHandlerDigest::FixedNonceGenerator::GenerateNonce() const { return nonce_; } -// static -std::string HttpAuthHandlerDigest::QopToString(QualityOfProtection qop) { - switch (qop) { - case QOP_UNSPECIFIED: - return ""; - case QOP_AUTH: - return "auth"; - default: - NOTREACHED(); - return ""; - } +HttpAuthHandlerDigest::Factory::Factory() + : nonce_generator_(new DynamicNonceGenerator()) { } -// static -std::string HttpAuthHandlerDigest::AlgorithmToString( - DigestAlgorithm algorithm) { - switch (algorithm) { - case ALGORITHM_UNSPECIFIED: - return ""; - case ALGORITHM_MD5: - return "MD5"; - case ALGORITHM_MD5_SESS: - return "MD5-sess"; - default: - NOTREACHED(); - return ""; - } +HttpAuthHandlerDigest::Factory::~Factory() { } -HttpAuthHandlerDigest::HttpAuthHandlerDigest( - int nonce_count, const NonceGenerator* nonce_generator) - : stale_(false), - algorithm_(ALGORITHM_UNSPECIFIED), - qop_(QOP_UNSPECIFIED), - nonce_count_(nonce_count), - nonce_generator_(nonce_generator) { - DCHECK(nonce_generator_); +void HttpAuthHandlerDigest::Factory::set_nonce_generator( + const NonceGenerator* nonce_generator) { + nonce_generator_.reset(nonce_generator); } -HttpAuthHandlerDigest::~HttpAuthHandlerDigest() { +int HttpAuthHandlerDigest::Factory::CreateAuthHandler( + HttpAuth::ChallengeTokenizer* challenge, + HttpAuth::Target target, + const GURL& origin, + CreateReason reason, + int digest_nonce_count, + const BoundNetLog& net_log, + scoped_ptr<HttpAuthHandler>* handler) { + // TODO(cbentzel): Move towards model of parsing in the factory + // method and only constructing when valid. + scoped_ptr<HttpAuthHandler> tmp_handler( + new HttpAuthHandlerDigest(digest_nonce_count, nonce_generator_.get())); + if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) + return ERR_INVALID_RESPONSE; + handler->swap(tmp_handler); + return OK; +} + +HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge( + HttpAuth::ChallengeTokenizer* 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 + // there is a rejection the realm hasn't changed. + if (!LowerCaseEqualsASCII(challenge->scheme(), "digest")) + return HttpAuth::AUTHORIZATION_RESULT_INVALID; + + HttpUtil::NameValuePairsIterator parameters = challenge->param_pairs(); + + // Try to find the "stale" value. + while (parameters.GetNext()) { + if (!LowerCaseEqualsASCII(parameters.name(), "stale")) + continue; + if (LowerCaseEqualsASCII(parameters.value(), "true")) + return HttpAuth::AUTHORIZATION_RESULT_STALE; + } + + return HttpAuth::AUTHORIZATION_RESULT_REJECT; +} + +bool HttpAuthHandlerDigest::Init(HttpAuth::ChallengeTokenizer* challenge) { + return ParseChallenge(challenge); } int HttpAuthHandlerDigest::GenerateAuthTokenImpl( @@ -138,112 +152,17 @@ int HttpAuthHandlerDigest::GenerateAuthTokenImpl( return OK; } -void HttpAuthHandlerDigest::GetRequestMethodAndPath( - const HttpRequestInfo* request, - std::string* method, - std::string* path) const { - DCHECK(request); - - const GURL& url = request->url; - - if (target_ == HttpAuth::AUTH_PROXY && url.SchemeIs("https")) { - *method = "CONNECT"; - *path = GetHostAndPort(url); - } else { - *method = request->method; - *path = HttpUtil::PathForRequest(url); - } -} - -std::string HttpAuthHandlerDigest::AssembleResponseDigest( - const std::string& method, - const std::string& path, - const string16& username, - const string16& password, - const std::string& cnonce, - const std::string& nc) const { - // ha1 = MD5(A1) - // TODO(eroman): is this the right encoding? - std::string ha1 = MD5String(UTF16ToUTF8(username) + ":" + realm_ + ":" + - UTF16ToUTF8(password)); - if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS) - ha1 = MD5String(ha1 + ":" + nonce_ + ":" + cnonce); - - // ha2 = MD5(A2) - // TODO(eroman): need to add MD5(req-entity-body) for qop=auth-int. - std::string ha2 = MD5String(method + ":" + path); - - std::string nc_part; - if (qop_ != HttpAuthHandlerDigest::QOP_UNSPECIFIED) { - nc_part = nc + ":" + cnonce + ":" + QopToString(qop_) + ":"; - } - - return MD5String(ha1 + ":" + nonce_ + ":" + nc_part + ha2); -} - -std::string HttpAuthHandlerDigest::AssembleCredentials( - const std::string& method, - const std::string& path, - const string16& username, - const string16& password, - const std::string& cnonce, - int nonce_count) const { - // the nonce-count is an 8 digit hex string. - std::string nc = base::StringPrintf("%08x", nonce_count); - - // TODO(eroman): is this the right encoding? - std::string authorization = (std::string("Digest username=") + - HttpUtil::Quote(UTF16ToUTF8(username))); - authorization += ", realm=" + HttpUtil::Quote(realm_); - authorization += ", nonce=" + HttpUtil::Quote(nonce_); - authorization += ", uri=" + HttpUtil::Quote(path); - - if (algorithm_ != ALGORITHM_UNSPECIFIED) { - authorization += ", algorithm=" + AlgorithmToString(algorithm_); - } - std::string response = AssembleResponseDigest(method, path, username, - password, cnonce, nc); - // No need to call HttpUtil::Quote() as the response digest cannot contain - // any characters needing to be escaped. - authorization += ", response=\"" + response + "\""; - - if (!opaque_.empty()) { - authorization += ", opaque=" + HttpUtil::Quote(opaque_); - } - if (qop_ != QOP_UNSPECIFIED) { - // TODO(eroman): Supposedly IIS server requires quotes surrounding qop. - authorization += ", qop=" + QopToString(qop_); - authorization += ", nc=" + nc; - authorization += ", cnonce=" + HttpUtil::Quote(cnonce); - } - - return authorization; -} - -bool HttpAuthHandlerDigest::Init(HttpAuth::ChallengeTokenizer* challenge) { - return ParseChallenge(challenge); +HttpAuthHandlerDigest::HttpAuthHandlerDigest( + int nonce_count, const NonceGenerator* nonce_generator) + : stale_(false), + algorithm_(ALGORITHM_UNSPECIFIED), + qop_(QOP_UNSPECIFIED), + nonce_count_(nonce_count), + nonce_generator_(nonce_generator) { + DCHECK(nonce_generator_); } -HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* 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 - // there is a rejection the realm hasn't changed. - if (!LowerCaseEqualsASCII(challenge->scheme(), "digest")) - return HttpAuth::AUTHORIZATION_RESULT_INVALID; - - HttpUtil::NameValuePairsIterator parameters = challenge->param_pairs(); - - // Try to find the "stale" value. - while (parameters.GetNext()) { - if (!LowerCaseEqualsASCII(parameters.name(), "stale")) - continue; - if (LowerCaseEqualsASCII(parameters.value(), "true")) - return HttpAuth::AUTHORIZATION_RESULT_STALE; - } - - return HttpAuth::AUTHORIZATION_RESULT_REJECT; +HttpAuthHandlerDigest::~HttpAuthHandlerDigest() { } // The digest challenge header looks like: @@ -266,8 +185,7 @@ HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge( // webserver was not sending the realm with a BASIC challenge). bool HttpAuthHandlerDigest::ParseChallenge( HttpAuth::ChallengeTokenizer* challenge) { - auth_scheme_ = AUTH_SCHEME_DIGEST; - scheme_ = "digest"; + auth_scheme_ = HttpAuth::AUTH_SCHEME_DIGEST; score_ = 2; properties_ = ENCRYPTS_IDENTITY; @@ -343,34 +261,115 @@ bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name, return true; } -HttpAuthHandlerDigest::Factory::Factory() - : nonce_generator_(new DynamicNonceGenerator()) { +// static +std::string HttpAuthHandlerDigest::QopToString(QualityOfProtection qop) { + switch (qop) { + case QOP_UNSPECIFIED: + return ""; + case QOP_AUTH: + return "auth"; + default: + NOTREACHED(); + return ""; + } } -HttpAuthHandlerDigest::Factory::~Factory() { +// static +std::string HttpAuthHandlerDigest::AlgorithmToString( + DigestAlgorithm algorithm) { + switch (algorithm) { + case ALGORITHM_UNSPECIFIED: + return ""; + case ALGORITHM_MD5: + return "MD5"; + case ALGORITHM_MD5_SESS: + return "MD5-sess"; + default: + NOTREACHED(); + return ""; + } } -void HttpAuthHandlerDigest::Factory::set_nonce_generator( - const NonceGenerator* nonce_generator) { - nonce_generator_.reset(nonce_generator); +void HttpAuthHandlerDigest::GetRequestMethodAndPath( + const HttpRequestInfo* request, + std::string* method, + std::string* path) const { + DCHECK(request); + + const GURL& url = request->url; + + if (target_ == HttpAuth::AUTH_PROXY && url.SchemeIs("https")) { + *method = "CONNECT"; + *path = GetHostAndPort(url); + } else { + *method = request->method; + *path = HttpUtil::PathForRequest(url); + } } -int HttpAuthHandlerDigest::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, - HttpAuth::Target target, - const GURL& origin, - CreateReason reason, - int digest_nonce_count, - const BoundNetLog& net_log, - scoped_ptr<HttpAuthHandler>* handler) { - // TODO(cbentzel): Move towards model of parsing in the factory - // method and only constructing when valid. - scoped_ptr<HttpAuthHandler> tmp_handler( - new HttpAuthHandlerDigest(digest_nonce_count, nonce_generator_.get())); - if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) - return ERR_INVALID_RESPONSE; - handler->swap(tmp_handler); - return OK; +std::string HttpAuthHandlerDigest::AssembleResponseDigest( + const std::string& method, + const std::string& path, + const string16& username, + const string16& password, + const std::string& cnonce, + const std::string& nc) const { + // ha1 = MD5(A1) + // TODO(eroman): is this the right encoding? + std::string ha1 = MD5String(UTF16ToUTF8(username) + ":" + realm_ + ":" + + UTF16ToUTF8(password)); + if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS) + ha1 = MD5String(ha1 + ":" + nonce_ + ":" + cnonce); + + // ha2 = MD5(A2) + // TODO(eroman): need to add MD5(req-entity-body) for qop=auth-int. + std::string ha2 = MD5String(method + ":" + path); + + std::string nc_part; + if (qop_ != HttpAuthHandlerDigest::QOP_UNSPECIFIED) { + nc_part = nc + ":" + cnonce + ":" + QopToString(qop_) + ":"; + } + + return MD5String(ha1 + ":" + nonce_ + ":" + nc_part + ha2); +} + +std::string HttpAuthHandlerDigest::AssembleCredentials( + const std::string& method, + const std::string& path, + const string16& username, + const string16& password, + const std::string& cnonce, + int nonce_count) const { + // the nonce-count is an 8 digit hex string. + std::string nc = base::StringPrintf("%08x", nonce_count); + + // TODO(eroman): is this the right encoding? + std::string authorization = (std::string("Digest username=") + + HttpUtil::Quote(UTF16ToUTF8(username))); + authorization += ", realm=" + HttpUtil::Quote(realm_); + authorization += ", nonce=" + HttpUtil::Quote(nonce_); + authorization += ", uri=" + HttpUtil::Quote(path); + + if (algorithm_ != ALGORITHM_UNSPECIFIED) { + authorization += ", algorithm=" + AlgorithmToString(algorithm_); + } + std::string response = AssembleResponseDigest(method, path, username, + password, cnonce, nc); + // No need to call HttpUtil::Quote() as the response digest cannot contain + // any characters needing to be escaped. + authorization += ", response=\"" + response + "\""; + + if (!opaque_.empty()) { + authorization += ", opaque=" + HttpUtil::Quote(opaque_); + } + if (qop_ != QOP_UNSPECIFIED) { + // TODO(eroman): Supposedly IIS server requires quotes surrounding qop. + authorization += ", qop=" + QopToString(qop_); + authorization += ", nc=" + nc; + authorization += ", cnonce=" + HttpUtil::Quote(cnonce); + } + + return authorization; } } // namespace net diff --git a/net/http/http_auth_handler_digest.h b/net/http/http_auth_handler_digest.h index c319f5d..fca77e4 100644 --- a/net/http/http_auth_handler_digest.h +++ b/net/http/http_auth_handler_digest.h @@ -62,6 +62,9 @@ class HttpAuthHandlerDigest : public HttpAuthHandler { Factory(); virtual ~Factory(); + // This factory owns the passed in |nonce_generator|. + void set_nonce_generator(const NonceGenerator* nonce_generator); + virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, @@ -70,9 +73,6 @@ class HttpAuthHandlerDigest : public HttpAuthHandler { const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler); - // This factory owns the passed in |nonce_generator|. - void set_nonce_generator(const NonceGenerator* nonce_generator); - private: scoped_ptr<const NonceGenerator> nonce_generator_; }; diff --git a/net/http/http_auth_handler_factory.cc b/net/http/http_auth_handler_factory.cc index 7d34cff..8cf7d3e 100644 --- a/net/http/http_auth_handler_factory.cc +++ b/net/http/http_auth_handler_factory.cc @@ -79,6 +79,46 @@ bool IsSupportedScheme(const std::vector<std::string>& supported_schemes, } // namespace +HttpAuthHandlerRegistryFactory::HttpAuthHandlerRegistryFactory() { +} + +HttpAuthHandlerRegistryFactory::~HttpAuthHandlerRegistryFactory() { + STLDeleteContainerPairSecondPointers(factory_map_.begin(), + factory_map_.end()); +} + +void HttpAuthHandlerRegistryFactory::SetURLSecurityManager( + const std::string& scheme, + URLSecurityManager* security_manager) { + HttpAuthHandlerFactory* factory = GetSchemeFactory(scheme); + if (factory) + factory->set_url_security_manager(security_manager); +} + +void HttpAuthHandlerRegistryFactory::RegisterSchemeFactory( + const std::string& scheme, + HttpAuthHandlerFactory* factory) { + std::string lower_scheme = StringToLowerASCII(scheme); + FactoryMap::iterator it = factory_map_.find(lower_scheme); + if (it != factory_map_.end()) { + delete it->second; + } + if (factory) + factory_map_[lower_scheme] = factory; + else + factory_map_.erase(it); +} + +HttpAuthHandlerFactory* HttpAuthHandlerRegistryFactory::GetSchemeFactory( + const std::string& scheme) const { + std::string lower_scheme = StringToLowerASCII(scheme); + FactoryMap::const_iterator it = factory_map_.find(lower_scheme); + if (it == factory_map_.end()) { + return NULL; // |scheme| is not registered. + } + return it->second; +} + // static HttpAuthHandlerRegistryFactory* HttpAuthHandlerRegistryFactory::Create( const std::vector<std::string>& supported_schemes, @@ -124,36 +164,6 @@ HttpAuthHandlerRegistryFactory* HttpAuthHandlerRegistryFactory::Create( return registry_factory; } -HttpAuthHandlerRegistryFactory::HttpAuthHandlerRegistryFactory() { -} - -HttpAuthHandlerRegistryFactory::~HttpAuthHandlerRegistryFactory() { - STLDeleteContainerPairSecondPointers(factory_map_.begin(), - factory_map_.end()); -} - -void HttpAuthHandlerRegistryFactory::SetURLSecurityManager( - const std::string& scheme, - URLSecurityManager* security_manager) { - HttpAuthHandlerFactory* factory = GetSchemeFactory(scheme); - if (factory) - factory->set_url_security_manager(security_manager); -} - -void HttpAuthHandlerRegistryFactory::RegisterSchemeFactory( - const std::string& scheme, - HttpAuthHandlerFactory* factory) { - std::string lower_scheme = StringToLowerASCII(scheme); - FactoryMap::iterator it = factory_map_.find(lower_scheme); - if (it != factory_map_.end()) { - delete it->second; - } - if (factory) - factory_map_[lower_scheme] = factory; - else - factory_map_.erase(it); -} - int HttpAuthHandlerRegistryFactory::CreateAuthHandler( HttpAuth::ChallengeTokenizer* challenge, HttpAuth::Target target, @@ -178,14 +188,4 @@ int HttpAuthHandlerRegistryFactory::CreateAuthHandler( digest_nonce_count, net_log, handler); } -HttpAuthHandlerFactory* HttpAuthHandlerRegistryFactory::GetSchemeFactory( - const std::string& scheme) const { - std::string lower_scheme = StringToLowerASCII(scheme); - FactoryMap::const_iterator it = factory_map_.find(lower_scheme); - if (it == factory_map_.end()) { - return NULL; // |scheme| is not registered. - } - return it->second; -} - } // namespace net diff --git a/net/http/http_auth_handler_factory.h b/net/http/http_auth_handler_factory.h index 2879aed..1e4134f 100644 --- a/net/http/http_auth_handler_factory.h +++ b/net/http/http_auth_handler_factory.h @@ -28,6 +28,11 @@ class HttpAuthHandlerRegistryFactory; // objects that it creates. class HttpAuthHandlerFactory { public: + enum CreateReason { + CREATE_CHALLENGE, // Create a handler in response to a challenge. + CREATE_PREEMPTIVE, // Create a handler preemptively. + }; + HttpAuthHandlerFactory() : url_security_manager_(NULL) {} virtual ~HttpAuthHandlerFactory() {} @@ -42,11 +47,6 @@ class HttpAuthHandlerFactory { return url_security_manager_; } - enum CreateReason { - CREATE_CHALLENGE, // Create a handler in response to a challenge. - CREATE_PREEMPTIVE, // Create a handler preemptively. - }; - // Creates an HttpAuthHandler object based on the authentication // challenge specified by |*challenge|. |challenge| must point to a valid // non-NULL tokenizer. @@ -152,16 +152,6 @@ class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory { // registry factory is destroyed. HttpAuthHandlerFactory* GetSchemeFactory(const std::string& scheme) const; - // Creates an auth handler by dispatching out to the registered factories - // based on the first token in |challenge|. - virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, - HttpAuth::Target target, - const GURL& origin, - CreateReason reason, - int digest_nonce_count, - const BoundNetLog& net_log, - scoped_ptr<HttpAuthHandler>* handler); - // Creates an HttpAuthHandlerRegistryFactory. // // |supported_schemes| is a list of authentication schemes. Valid values @@ -189,6 +179,16 @@ class HttpAuthHandlerRegistryFactory : public HttpAuthHandlerFactory { bool negotiate_disable_cname_lookup, bool negotiate_enable_port); + // Creates an auth handler by dispatching out to the registered factories + // based on the first token in |challenge|. + virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, + HttpAuth::Target target, + const GURL& origin, + CreateReason reason, + int digest_nonce_count, + const BoundNetLog& net_log, + scoped_ptr<HttpAuthHandler>* handler); + private: typedef std::map<std::string, HttpAuthHandlerFactory*> FactoryMap; diff --git a/net/http/http_auth_handler_factory_unittest.cc b/net/http/http_auth_handler_factory_unittest.cc index 0b90f19..3145c4b 100644 --- a/net/http/http_auth_handler_factory_unittest.cc +++ b/net/http/http_auth_handler_factory_unittest.cc @@ -7,6 +7,7 @@ #include "net/base/net_errors.h" #include "net/http/http_auth_handler.h" #include "net/http/http_auth_handler_factory.h" +#include "net/http/mock_allow_url_security_manager.h" #include "net/http/url_security_manager.h" #include "testing/gtest/include/gtest/gtest.h" @@ -97,7 +98,7 @@ TEST(HttpAuthHandlerFactoryTest, RegistryFactory) { TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { scoped_ptr<HostResolver> host_resolver(new MockHostResolver()); - URLSecurityManagerAllow url_security_manager; + MockAllowURLSecurityManager url_security_manager; scoped_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory( HttpAuthHandlerFactory::CreateDefault(host_resolver.get())); http_auth_handler_factory->SetURLSecurityManager( @@ -114,7 +115,7 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { &handler); EXPECT_EQ(OK, rv); ASSERT_FALSE(handler.get() == NULL); - EXPECT_STREQ("basic", handler->scheme().c_str()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_BASIC, handler->auth_scheme()); EXPECT_STREQ("FooBar", handler->realm().c_str()); EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target()); EXPECT_FALSE(handler->encrypts_identity()); @@ -141,7 +142,7 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { &handler); EXPECT_EQ(OK, rv); ASSERT_FALSE(handler.get() == NULL); - EXPECT_STREQ("digest", handler->scheme().c_str()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_DIGEST, handler->auth_scheme()); EXPECT_STREQ("FooBar", handler->realm().c_str()); EXPECT_EQ(HttpAuth::AUTH_PROXY, handler->target()); EXPECT_TRUE(handler->encrypts_identity()); @@ -157,7 +158,7 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { &handler); EXPECT_EQ(OK, rv); ASSERT_FALSE(handler.get() == NULL); - EXPECT_STREQ("ntlm", handler->scheme().c_str()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_NTLM, handler->auth_scheme()); EXPECT_STREQ("", handler->realm().c_str()); EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target()); EXPECT_TRUE(handler->encrypts_identity()); @@ -173,7 +174,7 @@ TEST(HttpAuthHandlerFactoryTest, DefaultFactory) { &handler); EXPECT_EQ(OK, rv); ASSERT_FALSE(handler.get() == NULL); - EXPECT_STREQ("negotiate", handler->scheme().c_str()); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_NEGOTIATE, handler->auth_scheme()); EXPECT_STREQ("", handler->realm().c_str()); EXPECT_EQ(HttpAuth::AUTH_SERVER, handler->target()); EXPECT_TRUE(handler->encrypts_identity()); diff --git a/net/http/http_auth_handler_mock.cc b/net/http/http_auth_handler_mock.cc index 0a49169..aad1bd1 100644 --- a/net/http/http_auth_handler_mock.cc +++ b/net/http/http_auth_handler_mock.cc @@ -71,14 +71,6 @@ void HttpAuthHandlerMock::SetGenerateExpectation(bool async, int rv) { generate_rv_ = rv; } -bool HttpAuthHandlerMock::Init(HttpAuth::ChallengeTokenizer* challenge) { - auth_scheme_ = AUTH_SCHEME_BASIC; - scheme_ = "mock"; - score_ = 1; - properties_ = connection_based_ ? IS_CONNECTION_BASED : 0; - return true; -} - HttpAuth::AuthorizationResult HttpAuthHandlerMock::HandleAnotherChallenge( HttpAuth::ChallengeTokenizer* challenge) { if (!is_connection_based()) @@ -88,6 +80,17 @@ HttpAuth::AuthorizationResult HttpAuthHandlerMock::HandleAnotherChallenge( return HttpAuth::AUTHORIZATION_RESULT_ACCEPT; } +bool HttpAuthHandlerMock::NeedsIdentity() { + return first_round_; +} + +bool HttpAuthHandlerMock::Init(HttpAuth::ChallengeTokenizer* challenge) { + auth_scheme_ = HttpAuth::AUTH_SCHEME_MOCK; + score_ = 1; + properties_ = connection_based_ ? IS_CONNECTION_BASED : 0; + return true; +} + int HttpAuthHandlerMock::GenerateAuthTokenImpl(const string16* username, const string16* password, const HttpRequestInfo* request, diff --git a/net/http/http_auth_handler_mock.h b/net/http/http_auth_handler_mock.h index bef8b2b..473ca2e 100644 --- a/net/http/http_auth_handler_mock.h +++ b/net/http/http_auth_handler_mock.h @@ -29,32 +29,6 @@ class HttpAuthHandlerMock : public HttpAuthHandler { RESOLVE_TESTED, }; - HttpAuthHandlerMock(); - - virtual ~HttpAuthHandlerMock(); - - void SetResolveExpectation(Resolve resolve); - - virtual bool NeedsCanonicalName(); - - virtual int ResolveCanonicalName(HostResolver* host_resolver, - CompletionCallback* callback); - - virtual bool NeedsIdentity() { return first_round_; } - - void SetGenerateExpectation(bool async, int rv); - - void set_connection_based(bool connection_based) { - connection_based_ = connection_based; - } - - const GURL& request_url() const { - return request_url_; - } - - HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge); - // The Factory class simply returns the same handler each time // CreateAuthHandler is called. class Factory : public HttpAuthHandlerFactory { @@ -68,6 +42,7 @@ class HttpAuthHandlerMock : public HttpAuthHandler { do_init_from_challenge_ = do_init_from_challenge; } + // HttpAuthHandlerFactory: virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, @@ -81,6 +56,33 @@ class HttpAuthHandlerMock : public HttpAuthHandler { bool do_init_from_challenge_; }; + HttpAuthHandlerMock(); + + virtual ~HttpAuthHandlerMock(); + + void SetResolveExpectation(Resolve resolve); + + virtual bool NeedsCanonicalName(); + + virtual int ResolveCanonicalName(HostResolver* host_resolver, + CompletionCallback* callback); + + + void SetGenerateExpectation(bool async, int rv); + + void set_connection_based(bool connection_based) { + connection_based_ = connection_based; + } + + const GURL& request_url() const { + return request_url_; + } + + // HttpAuthHandler: + virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( + HttpAuth::ChallengeTokenizer* challenge); + virtual bool NeedsIdentity(); + protected: virtual bool Init(HttpAuth::ChallengeTokenizer* challenge); diff --git a/net/http/http_auth_handler_negotiate.cc b/net/http/http_auth_handler_negotiate.cc index 2544728..a96902d 100644 --- a/net/http/http_auth_handler_negotiate.cc +++ b/net/http/http_auth_handler_negotiate.cc @@ -16,6 +16,68 @@ namespace net { +HttpAuthHandlerNegotiate::Factory::Factory() + : disable_cname_lookup_(false), + use_port_(false), +#if defined(OS_WIN) + max_token_length_(0), + first_creation_(true), + is_unsupported_(false), +#endif + auth_library_(NULL) { +} + +HttpAuthHandlerNegotiate::Factory::~Factory() { +} + +void HttpAuthHandlerNegotiate::Factory::set_host_resolver( + HostResolver* resolver) { + resolver_ = resolver; +} + +int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( + HttpAuth::ChallengeTokenizer* challenge, + HttpAuth::Target target, + const GURL& origin, + CreateReason reason, + int digest_nonce_count, + const BoundNetLog& net_log, + scoped_ptr<HttpAuthHandler>* handler) { +#if defined(OS_WIN) + if (is_unsupported_ || reason == CREATE_PREEMPTIVE) + return ERR_UNSUPPORTED_AUTH_SCHEME; + if (max_token_length_ == 0) { + int rv = DetermineMaxTokenLength(auth_library_.get(), NEGOSSP_NAME, + &max_token_length_); + if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) + is_unsupported_ = true; + if (rv != OK) + return rv; + } + // TODO(cbentzel): Move towards model of parsing in the factory + // method and only constructing when valid. + scoped_ptr<HttpAuthHandler> tmp_handler( + new HttpAuthHandlerNegotiate(auth_library_.get(), max_token_length_, + url_security_manager(), resolver_, + disable_cname_lookup_, use_port_)); + if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) + return ERR_INVALID_RESPONSE; + handler->swap(tmp_handler); + return OK; +#elif defined(OS_POSIX) + // TODO(ahendrickson): Move towards model of parsing in the factory + // method and only constructing when valid. + scoped_ptr<HttpAuthHandler> tmp_handler( + new HttpAuthHandlerNegotiate(auth_library_.get(), url_security_manager(), + resolver_, disable_cname_lookup_, + use_port_)); + if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) + return ERR_INVALID_RESPONSE; + handler->swap(tmp_handler); + return OK; +#endif +} + HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( AuthLibrary* auth_library, #if defined(OS_WIN) @@ -46,89 +108,6 @@ HttpAuthHandlerNegotiate::HttpAuthHandlerNegotiate( HttpAuthHandlerNegotiate::~HttpAuthHandlerNegotiate() { } -int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( - const string16* username, - const string16* password, - const HttpRequestInfo* request, - CompletionCallback* callback, - std::string* auth_token) { - DCHECK(user_callback_ == NULL); - DCHECK((username == NULL) == (password == NULL)); - DCHECK(auth_token_ == NULL); - auth_token_ = auth_token; - if (already_called_) { - DCHECK((!has_username_and_password_ && username == NULL) || - (has_username_and_password_ && *username == username_ && - *password == password_)); - next_state_ = STATE_GENERATE_AUTH_TOKEN; - } else { - already_called_ = true; - if (username) { - has_username_and_password_ = true; - username_ = *username; - password_ = *password; - } - next_state_ = STATE_RESOLVE_CANONICAL_NAME; - } - int rv = DoLoop(OK); - if (rv == ERR_IO_PENDING) - user_callback_ = callback; - return rv; -} - -// The Negotiate challenge header looks like: -// WWW-Authenticate: NEGOTIATE auth-data -bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { -#if defined(OS_POSIX) - if (!auth_system_.Init()) { - VLOG(1) << "can't initialize GSSAPI library"; - return false; - } - // GSSAPI does not provide a way to enter username/password to - // obtain a TGT. If the default credentials are not allowed for - // a particular site (based on whitelist), fall back to a - // different scheme. - if (!AllowsDefaultCredentials()) - return false; -#endif - if (CanDelegate()) - auth_system_.Delegate(); - auth_scheme_ = AUTH_SCHEME_NEGOTIATE; - scheme_ = "negotiate"; - score_ = 4; - properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; - HttpAuth::AuthorizationResult auth_result = - auth_system_.ParseChallenge(challenge); - return (auth_result == HttpAuth::AUTHORIZATION_RESULT_ACCEPT); -} - -HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) { - return auth_system_.ParseChallenge(challenge); -} - -// Require identity on first pass instead of second. -bool HttpAuthHandlerNegotiate::NeedsIdentity() { - return auth_system_.NeedsIdentity(); -} - -bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { - if (target_ == HttpAuth::AUTH_PROXY) - return true; - if (!url_security_manager_) - return false; - return url_security_manager_->CanUseDefaultCredentials(origin_); -} - -bool HttpAuthHandlerNegotiate::CanDelegate() const { - // TODO(cbentzel): Should delegation be allowed on proxies? - if (target_ == HttpAuth::AUTH_PROXY) - return false; - if (!url_security_manager_) - return false; - return url_security_manager_->CanDelegate(origin_); -} - std::wstring HttpAuthHandlerNegotiate::CreateSPN( const AddressList& address_list, const GURL& origin) { // Kerberos Web Server SPNs are in the form HTTP/<host>:<port> through SSPI, @@ -178,6 +157,93 @@ std::wstring HttpAuthHandlerNegotiate::CreateSPN( } } +HttpAuth::AuthorizationResult HttpAuthHandlerNegotiate::HandleAnotherChallenge( + HttpAuth::ChallengeTokenizer* challenge) { + return auth_system_.ParseChallenge(challenge); +} + +// Require identity on first pass instead of second. +bool HttpAuthHandlerNegotiate::NeedsIdentity() { + return auth_system_.NeedsIdentity(); +} + +bool HttpAuthHandlerNegotiate::AllowsDefaultCredentials() { + if (target_ == HttpAuth::AUTH_PROXY) + return true; + if (!url_security_manager_) + return false; + return url_security_manager_->CanUseDefaultCredentials(origin_); +} + +// The Negotiate challenge header looks like: +// WWW-Authenticate: NEGOTIATE auth-data +bool HttpAuthHandlerNegotiate::Init(HttpAuth::ChallengeTokenizer* challenge) { +#if defined(OS_POSIX) + if (!auth_system_.Init()) { + VLOG(1) << "can't initialize GSSAPI library"; + return false; + } + // GSSAPI does not provide a way to enter username/password to + // obtain a TGT. If the default credentials are not allowed for + // a particular site (based on whitelist), fall back to a + // different scheme. + if (!AllowsDefaultCredentials()) + return false; +#endif + if (CanDelegate()) + auth_system_.Delegate(); + auth_scheme_ = HttpAuth::AUTH_SCHEME_NEGOTIATE; + score_ = 4; + properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; + HttpAuth::AuthorizationResult auth_result = + auth_system_.ParseChallenge(challenge); + return (auth_result == HttpAuth::AUTHORIZATION_RESULT_ACCEPT); +} + +int HttpAuthHandlerNegotiate::GenerateAuthTokenImpl( + const string16* username, + const string16* password, + const HttpRequestInfo* request, + CompletionCallback* callback, + std::string* auth_token) { + DCHECK(user_callback_ == NULL); + DCHECK((username == NULL) == (password == NULL)); + DCHECK(auth_token_ == NULL); + auth_token_ = auth_token; + if (already_called_) { + DCHECK((!has_username_and_password_ && username == NULL) || + (has_username_and_password_ && *username == username_ && + *password == password_)); + next_state_ = STATE_GENERATE_AUTH_TOKEN; + } else { + already_called_ = true; + if (username) { + has_username_and_password_ = true; + username_ = *username; + password_ = *password; + } + next_state_ = STATE_RESOLVE_CANONICAL_NAME; + } + int rv = DoLoop(OK); + if (rv == ERR_IO_PENDING) + user_callback_ = callback; + return rv; +} + +void HttpAuthHandlerNegotiate::OnIOComplete(int result) { + int rv = DoLoop(result); + if (rv != ERR_IO_PENDING) + DoCallback(rv); +} + +void HttpAuthHandlerNegotiate::DoCallback(int rv) { + DCHECK(rv != ERR_IO_PENDING); + DCHECK(user_callback_); + CompletionCallback* callback = user_callback_; + user_callback_ = NULL; + callback->Run(rv); +} + int HttpAuthHandlerNegotiate::DoLoop(int result) { DCHECK(next_state_ != STATE_NONE); @@ -254,80 +320,13 @@ int HttpAuthHandlerNegotiate::DoGenerateAuthTokenComplete(int rv) { return rv; } -void HttpAuthHandlerNegotiate::OnIOComplete(int result) { - int rv = DoLoop(result); - if (rv != ERR_IO_PENDING) - DoCallback(rv); -} - -void HttpAuthHandlerNegotiate::DoCallback(int rv) { - DCHECK(rv != ERR_IO_PENDING); - DCHECK(user_callback_); - CompletionCallback* callback = user_callback_; - user_callback_ = NULL; - callback->Run(rv); -} - -HttpAuthHandlerNegotiate::Factory::Factory() - : disable_cname_lookup_(false), - use_port_(false), -#if defined(OS_WIN) - max_token_length_(0), - first_creation_(true), - is_unsupported_(false), -#endif - auth_library_(NULL) { -} - -HttpAuthHandlerNegotiate::Factory::~Factory() { -} - -void HttpAuthHandlerNegotiate::Factory::set_host_resolver( - HostResolver* resolver) { - resolver_ = resolver; -} - -int HttpAuthHandlerNegotiate::Factory::CreateAuthHandler( - HttpAuth::ChallengeTokenizer* challenge, - HttpAuth::Target target, - const GURL& origin, - CreateReason reason, - int digest_nonce_count, - const BoundNetLog& net_log, - scoped_ptr<HttpAuthHandler>* handler) { -#if defined(OS_WIN) - if (is_unsupported_ || reason == CREATE_PREEMPTIVE) - return ERR_UNSUPPORTED_AUTH_SCHEME; - if (max_token_length_ == 0) { - int rv = DetermineMaxTokenLength(auth_library_.get(), NEGOSSP_NAME, - &max_token_length_); - if (rv == ERR_UNSUPPORTED_AUTH_SCHEME) - is_unsupported_ = true; - if (rv != OK) - return rv; - } - // TODO(cbentzel): Move towards model of parsing in the factory - // method and only constructing when valid. - scoped_ptr<HttpAuthHandler> tmp_handler( - new HttpAuthHandlerNegotiate(auth_library_.get(), max_token_length_, - url_security_manager(), resolver_, - disable_cname_lookup_, use_port_)); - if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) - return ERR_INVALID_RESPONSE; - handler->swap(tmp_handler); - return OK; -#elif defined(OS_POSIX) - // TODO(ahendrickson): Move towards model of parsing in the factory - // method and only constructing when valid. - scoped_ptr<HttpAuthHandler> tmp_handler( - new HttpAuthHandlerNegotiate(auth_library_.get(), url_security_manager(), - resolver_, disable_cname_lookup_, - use_port_)); - if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) - return ERR_INVALID_RESPONSE; - handler->swap(tmp_handler); - return OK; -#endif +bool HttpAuthHandlerNegotiate::CanDelegate() const { + // TODO(cbentzel): Should delegation be allowed on proxies? + if (target_ == HttpAuth::AUTH_PROXY) + return false; + if (!url_security_manager_) + return false; + return url_security_manager_->CanDelegate(origin_); } } // namespace net diff --git a/net/http/http_auth_handler_negotiate.h b/net/http/http_auth_handler_negotiate.h index a19d182..de2a6e6 100644 --- a/net/http/http_auth_handler_negotiate.h +++ b/net/http/http_auth_handler_negotiate.h @@ -64,6 +64,12 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { void set_host_resolver(HostResolver* host_resolver); + // Sets the system library to use, thereby assuming ownership of + // |auth_library|. + void set_library(AuthLibrary* auth_library) { + auth_library_.reset(auth_library); + } + virtual int CreateAuthHandler(HttpAuth::ChallengeTokenizer* challenge, HttpAuth::Target target, const GURL& origin, @@ -72,12 +78,6 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { const BoundNetLog& net_log, scoped_ptr<HttpAuthHandler>* handler); - // Sets the system library to use, thereby assuming ownership of - // |auth_library|. - void set_library(AuthLibrary* auth_library) { - auth_library_.reset(auth_library); - } - private: bool disable_cname_lookup_; bool use_port_; @@ -101,17 +101,16 @@ class HttpAuthHandlerNegotiate : public HttpAuthHandler { virtual ~HttpAuthHandlerNegotiate(); - virtual bool NeedsIdentity(); - - virtual bool AllowsDefaultCredentials(); - - virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge); - // These are public for unit tests std::wstring CreateSPN(const AddressList& address_list, const GURL& orign); const std::wstring& spn() const { return spn_; } + // HttpAuthHandler: + virtual HttpAuth::AuthorizationResult HandleAnotherChallenge( + HttpAuth::ChallengeTokenizer* challenge); + virtual bool NeedsIdentity(); + virtual bool AllowsDefaultCredentials(); + protected: virtual bool Init(HttpAuth::ChallengeTokenizer* challenge); diff --git a/net/http/http_auth_handler_negotiate_unittest.cc b/net/http/http_auth_handler_negotiate_unittest.cc index 684ff55..cdaa5f3 100644 --- a/net/http/http_auth_handler_negotiate_unittest.cc +++ b/net/http/http_auth_handler_negotiate_unittest.cc @@ -10,6 +10,7 @@ #include "net/base/net_errors.h" #include "net/base/test_completion_callback.h" #include "net/http/http_request_info.h" +#include "net/http/mock_allow_url_security_manager.h" #if defined(OS_WIN) #include "net/http/mock_sspi_library_win.h" #elif defined(OS_POSIX) @@ -36,7 +37,7 @@ class HttpAuthHandlerNegotiateTest : public PlatformTest { resolver_->rules()->AddIPLiteralRule("alias", "10.0.0.2", "canonical.example.com"); - url_security_manager_.reset(new URLSecurityManagerAllow()); + url_security_manager_.reset(new MockAllowURLSecurityManager()); factory_.reset(new HttpAuthHandlerNegotiate::Factory()); factory_->set_url_security_manager(url_security_manager_.get()); factory_->set_library(auth_library_); @@ -115,23 +116,20 @@ class HttpAuthHandlerNegotiateTest : public PlatformTest { 1, // Locally initiated 1); // Open test::MockGSSAPILibrary::SecurityContextQuery queries[] = { - { "Negotiate", // Package name + test::MockGSSAPILibrary::SecurityContextQuery( + "Negotiate", // Package name GSS_S_CONTINUE_NEEDED, // Major response code 0, // Minor response code context1, // Context - { 0, NULL }, // Expected input token - { arraysize(kAuthResponse), - const_cast<char*>(kAuthResponse) } // Output token - }, - { "Negotiate", // Package name + NULL, // Expected input token + kAuthResponse), // Output token + test::MockGSSAPILibrary::SecurityContextQuery( + "Negotiate", // Package name GSS_S_COMPLETE, // Major response code 0, // Minor response code context2, // Context - { arraysize(kAuthResponse), - const_cast<char*>(kAuthResponse) }, // Expected input token - { arraysize(kAuthResponse), - const_cast<char*>(kAuthResponse) } // Output token - }, + kAuthResponse, // Expected input token + kAuthResponse) // Output token }; for (size_t i = 0; i < arraysize(queries); ++i) { @@ -158,14 +156,13 @@ class HttpAuthHandlerNegotiateTest : public PlatformTest { 0, // Context flags 1, // Locally initiated 0); // Open - test::MockGSSAPILibrary::SecurityContextQuery query = { - "Negotiate", // Package name - major_status, // Major response code - minor_status, // Minor response code - context, // Context - { 0, NULL }, // Expected input token - { 0, NULL } // Output token - }; + test::MockGSSAPILibrary::SecurityContextQuery query( + "Negotiate", // Package name + major_status, // Major response code + minor_status, // Minor response code + context, // Context + NULL, // Expected input token + NULL); // Output token mock_library->ExpectSecurityContext(query.expected_package, query.response_code, diff --git a/net/http/http_auth_handler_ntlm.cc b/net/http/http_auth_handler_ntlm.cc index f987e48..5090918 100644 --- a/net/http/http_auth_handler_ntlm.cc +++ b/net/http/http_auth_handler_ntlm.cc @@ -15,6 +15,19 @@ namespace net { +HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::HandleAnotherChallenge( + HttpAuth::ChallengeTokenizer* challenge) { + return ParseChallenge(challenge, false); +} + +bool HttpAuthHandlerNTLM::Init(HttpAuth::ChallengeTokenizer* tok) { + auth_scheme_ = HttpAuth::AUTH_SCHEME_NTLM; + score_ = 3; + properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; + + return ParseChallenge(tok, true) == HttpAuth::AUTHORIZATION_RESULT_ACCEPT; +} + int HttpAuthHandlerNTLM::GenerateAuthTokenImpl( const string16* username, const string16* password, @@ -92,20 +105,6 @@ int HttpAuthHandlerNTLM::GenerateAuthTokenImpl( #endif } -bool HttpAuthHandlerNTLM::Init(HttpAuth::ChallengeTokenizer* tok) { - auth_scheme_ = AUTH_SCHEME_NTLM; - scheme_ = "ntlm"; - score_ = 3; - properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED; - - return ParseChallenge(tok, true) == HttpAuth::AUTHORIZATION_RESULT_ACCEPT; -} - -HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::HandleAnotherChallenge( - HttpAuth::ChallengeTokenizer* challenge) { - return ParseChallenge(challenge, false); -} - // The NTLM challenge header looks like: // WWW-Authenticate: NTLM auth-data HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::ParseChallenge( diff --git a/net/http/http_auth_handler_ntlm.h b/net/http/http_auth_handler_ntlm.h index 831e43d..ae7c78b 100644 --- a/net/http/http_auth_handler_ntlm.h +++ b/net/http/http_auth_handler_ntlm.h @@ -114,6 +114,10 @@ class HttpAuthHandlerNTLM : public HttpAuthHandler { HttpAuth::ChallengeTokenizer* challenge); 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); virtual int GenerateAuthTokenImpl(const string16* username, @@ -122,10 +126,6 @@ class HttpAuthHandlerNTLM : public HttpAuthHandler { CompletionCallback* callback, std::string* auth_token); - // This function acquires a credentials handle in the SSPI implementation. - // It does nothing in the portable implementation. - int InitializeBeforeFirstChallenge(); - private: ~HttpAuthHandlerNTLM(); diff --git a/net/http/http_auth_handler_ntlm_portable.cc b/net/http/http_auth_handler_ntlm_portable.cc index d3abc98..fac37c8 100644 --- a/net/http/http_auth_handler_ntlm_portable.cc +++ b/net/http/http_auth_handler_ntlm_portable.cc @@ -643,14 +643,11 @@ HttpAuthHandlerNTLM::get_host_name_proc_ = GetHostName; HttpAuthHandlerNTLM::HttpAuthHandlerNTLM() { } -HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() { - // Wipe our copy of the password from memory, to reduce the chance of being - // written to the paging file on disk. - ZapString(&password_); -} - bool HttpAuthHandlerNTLM::NeedsIdentity() { - return !auth_data_.empty(); + // This gets called for each round-trip. Only require identity on + // the first call (when auth_data_ is empty). On subsequent calls, + // we use the initially established identity. + return auth_data_.empty(); } bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() { @@ -659,6 +656,16 @@ bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() { return false; } +int HttpAuthHandlerNTLM::InitializeBeforeFirstChallenge() { + return OK; +} + +HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() { + // Wipe our copy of the password from memory, to reduce the chance of being + // written to the paging file on disk. + ZapString(&password_); +} + // static HttpAuthHandlerNTLM::GenerateRandomProc HttpAuthHandlerNTLM::SetGenerateRandomProc( @@ -676,6 +683,12 @@ HttpAuthHandlerNTLM::HostNameProc HttpAuthHandlerNTLM::SetHostNameProc( return old_proc; } +HttpAuthHandlerNTLM::Factory::Factory() { +} + +HttpAuthHandlerNTLM::Factory::~Factory() { +} + int HttpAuthHandlerNTLM::GetNextToken(const void* in_token, uint32 in_token_len, void** out_token, @@ -702,16 +715,6 @@ int HttpAuthHandlerNTLM::GetNextToken(const void* in_token, return rv; } -int HttpAuthHandlerNTLM::InitializeBeforeFirstChallenge() { - return OK; -} - -HttpAuthHandlerNTLM::Factory::Factory() { -} - -HttpAuthHandlerNTLM::Factory::~Factory() { -} - int HttpAuthHandlerNTLM::Factory::CreateAuthHandler( HttpAuth::ChallengeTokenizer* challenge, HttpAuth::Target target, diff --git a/net/http/http_auth_sspi_win.cc b/net/http/http_auth_sspi_win.cc index c5264cd..1a629f1 100644 --- a/net/http/http_auth_sspi_win.cc +++ b/net/http/http_auth_sspi_win.cc @@ -27,6 +27,9 @@ int MapAcquireCredentialsStatusToError(SECURITY_STATUS status, case SEC_E_INSUFFICIENT_MEMORY: return ERR_OUT_OF_MEMORY; case SEC_E_INTERNAL_ERROR: + LOG(WARNING) + << "AcquireCredentialsHandle returned unexpected status 0x" + << std::hex << status; return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; case SEC_E_NO_CREDENTIALS: case SEC_E_NOT_OWNER: @@ -37,7 +40,7 @@ int MapAcquireCredentialsStatusToError(SECURITY_STATUS status, return ERR_UNSUPPORTED_AUTH_SCHEME; default: LOG(WARNING) - << "AcquireSecurityCredentials returned undocumented status 0x" + << "AcquireCredentialsHandle returned undocumented status 0x" << std::hex << status; return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; } diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc index 63ae3b0..6788be2 100644 --- a/net/http/http_auth_unittest.cc +++ b/net/http/http_auth_unittest.cc @@ -17,6 +17,7 @@ #include "net/http/http_auth_handler_mock.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" +#include "net/http/mock_allow_url_security_manager.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -26,7 +27,7 @@ namespace { HttpAuthHandlerMock* CreateMockHandler(bool connection_based) { HttpAuthHandlerMock* auth_handler = new HttpAuthHandlerMock(); auth_handler->set_connection_based(connection_based); - std::string challenge_text = "Mock"; + std::string challenge_text = "Basic"; HttpAuth::ChallengeTokenizer challenge(challenge_text.begin(), challenge_text.end()); GURL origin("www.example.com"); @@ -48,7 +49,7 @@ HttpAuth::AuthorizationResult HandleChallengeResponse( std::string* challenge_used) { scoped_ptr<HttpAuthHandlerMock> mock_handler( CreateMockHandler(connection_based)); - std::set<std::string> disabled_schemes; + std::set<HttpAuth::Scheme> disabled_schemes; scoped_refptr<HttpResponseHeaders> headers( HeadersFromResponseText(headers_text)); return HttpAuth::HandleChallengeResponse( @@ -64,57 +65,58 @@ HttpAuth::AuthorizationResult HandleChallengeResponse( TEST(HttpAuthTest, ChooseBestChallenge) { static const struct { const char* headers; - const char* challenge_scheme; + HttpAuth::Scheme challenge_scheme; const char* challenge_realm; } tests[] = { { + // Basic is the only challenge type, pick it. "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" "www-authenticate: Basic realm=\"BasicRealm\"\n", - // Basic is the only challenge type, pick it. - "basic", + HttpAuth::AUTH_SCHEME_BASIC, "BasicRealm", }, { + // Fake is the only challenge type, but it is unsupported. "Y: Digest realm=\"FooBar\", nonce=\"aaaaaaaaaa\"\n" "www-authenticate: Fake realm=\"FooBar\"\n", - // Fake is the only challenge type, but it is unsupported. - "", + HttpAuth::AUTH_SCHEME_MAX, "", }, { + // Pick Digest over Basic. "www-authenticate: Basic realm=\"FooBar\"\n" "www-authenticate: Fake realm=\"FooBar\"\n" "www-authenticate: nonce=\"aaaaaaaaaa\"\n" "www-authenticate: Digest realm=\"DigestRealm\", nonce=\"aaaaaaaaaa\"\n", - // Pick Digset over Basic - "digest", + HttpAuth::AUTH_SCHEME_DIGEST, "DigestRealm", }, { + // Handle an empty header correctly. "Y: Digest realm=\"X\", nonce=\"aaaaaaaaaa\"\n" "www-authenticate:\n", - // Handle null header value. - "", + HttpAuth::AUTH_SCHEME_MAX, "", }, { - "WWW-Authenticate: Negotiate\n" - "WWW-Authenticate: NTLM\n", - + // Choose Negotiate over NTLM on all platforms. // TODO(ahendrickson): This may be flaky on Linux and OSX as it // relies on being able to load one of the known .so files // for gssapi. - "negotiate", + "WWW-Authenticate: Negotiate\n" + "WWW-Authenticate: NTLM\n", + + HttpAuth::AUTH_SCHEME_NEGOTIATE, "", } }; GURL origin("http://www.example.com"); - std::set<std::string> disabled_schemes; - URLSecurityManagerAllow url_security_manager; + std::set<HttpAuth::Scheme> disabled_schemes; + MockAllowURLSecurityManager url_security_manager; scoped_ptr<HostResolver> host_resolver(new MockHostResolver()); scoped_ptr<HttpAuthHandlerRegistryFactory> http_auth_handler_factory( HttpAuthHandlerFactory::CreateDefault(host_resolver.get())); @@ -138,10 +140,10 @@ TEST(HttpAuthTest, ChooseBestChallenge) { &handler); if (handler.get()) { - EXPECT_STREQ(tests[i].challenge_scheme, handler->scheme().c_str()); + EXPECT_EQ(tests[i].challenge_scheme, handler->auth_scheme()); EXPECT_STREQ(tests[i].challenge_realm, handler->realm().c_str()); } else { - EXPECT_STREQ("", tests[i].challenge_scheme); + EXPECT_EQ(HttpAuth::AUTH_SCHEME_MAX, tests[i].challenge_scheme); EXPECT_STREQ("", tests[i].challenge_realm); } } diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc index 061bb30..3e69d7a 100644 --- a/net/http/http_basic_stream.cc +++ b/net/http/http_basic_stream.cc @@ -25,6 +25,8 @@ HttpBasicStream::HttpBasicStream(ClientSocketHandle* connection, request_info_(NULL) { } +HttpBasicStream::~HttpBasicStream() {} + int HttpBasicStream::InitializeStream(const HttpRequestInfo* request_info, const BoundNetLog& net_log, CompletionCallback* callback) { @@ -52,8 +54,6 @@ int HttpBasicStream::SendRequest(const HttpRequestHeaders& headers, callback); } -HttpBasicStream::~HttpBasicStream() {} - uint64 HttpBasicStream::GetUploadProgress() const { return parser_->GetUploadProgress(); } diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index 5710491..7c35f69 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc @@ -34,10 +34,38 @@ #include "net/http/http_response_info.h" #include "net/http/http_util.h" #include "net/socket/ssl_host_info.h" -#include "net/spdy/spdy_session_pool.h" namespace net { +namespace { + +HttpNetworkSession* CreateNetworkSession( + HostResolver* host_resolver, + CertVerifier* cert_verifier, + DnsRRResolver* dnsrr_resolver, + DnsCertProvenanceChecker* dns_cert_checker, + ProxyService* proxy_service, + SSLHostInfoFactory* ssl_host_info_factory, + SSLConfigService* ssl_config_service, + HttpAuthHandlerFactory* http_auth_handler_factory, + HttpNetworkDelegate* network_delegate, + NetLog* net_log) { + HttpNetworkSession::Params params; + params.host_resolver = host_resolver; + params.cert_verifier = cert_verifier; + params.dnsrr_resolver = dnsrr_resolver; + params.dns_cert_checker = dns_cert_checker; + params.proxy_service = proxy_service; + params.ssl_host_info_factory = ssl_host_info_factory; + params.ssl_config_service = ssl_config_service; + params.http_auth_handler_factory = http_auth_handler_factory; + params.network_delegate = network_delegate; + params.net_log = net_log; + return new HttpNetworkSession(params); +} + +} // namespace + HttpCache::DefaultBackend::DefaultBackend(CacheType type, const FilePath& path, int max_bytes, @@ -264,21 +292,23 @@ void HttpCache::MetadataWriter::OnIOComplete(int result) { class HttpCache::SSLHostInfoFactoryAdaptor : public SSLHostInfoFactory { public: - explicit SSLHostInfoFactoryAdaptor(HttpCache* http_cache) - : http_cache_(http_cache) { + SSLHostInfoFactoryAdaptor(CertVerifier* cert_verifier, HttpCache* http_cache) + : cert_verifier_(cert_verifier), + http_cache_(http_cache) { } - SSLHostInfo* GetForHost(const std::string& hostname, - const SSLConfig& ssl_config) { - return new DiskCacheBasedSSLHostInfo(hostname, ssl_config, http_cache_); + virtual SSLHostInfo* GetForHost(const std::string& hostname, + const SSLConfig& ssl_config) { + return new DiskCacheBasedSSLHostInfo( + hostname, ssl_config, cert_verifier_, http_cache_); } private: + CertVerifier* const cert_verifier_; HttpCache* const http_cache_; }; //----------------------------------------------------------------------------- - HttpCache::HttpCache(HostResolver* host_resolver, CertVerifier* cert_verifier, DnsRRResolver* dnsrr_resolver, @@ -294,22 +324,35 @@ HttpCache::HttpCache(HostResolver* host_resolver, building_backend_(false), mode_(NORMAL), ssl_host_info_factory_(new SSLHostInfoFactoryAdaptor( - ALLOW_THIS_IN_INITIALIZER_LIST(this))), - network_layer_(HttpNetworkLayer::CreateFactory(host_resolver, - cert_verifier, dnsrr_resolver, dns_cert_checker_, - ssl_host_info_factory_.get(), - proxy_service, ssl_config_service, - http_auth_handler_factory, network_delegate, net_log)), + cert_verifier, + ALLOW_THIS_IN_INITIALIZER_LIST(this))), + network_layer_( + new HttpNetworkLayer( + CreateNetworkSession( + host_resolver, + cert_verifier, + dnsrr_resolver, + dns_cert_checker_, + proxy_service, + ssl_host_info_factory_.get(), + ssl_config_service, + http_auth_handler_factory, + network_delegate, + net_log))), ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { } + HttpCache::HttpCache(HttpNetworkSession* session, BackendFactory* backend_factory) : net_log_(session->net_log()), backend_factory_(backend_factory), building_backend_(false), mode_(NORMAL), - network_layer_(HttpNetworkLayer::CreateFactory(session)), + ssl_host_info_factory_(new SSLHostInfoFactoryAdaptor( + session->cert_verifier(), + ALLOW_THIS_IN_INITIALIZER_LIST(this))), + network_layer_(new HttpNetworkLayer(session)), ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { } @@ -383,29 +426,6 @@ disk_cache::Backend* HttpCache::GetCurrentBackend() { return disk_cache_.get(); } -int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) { - // Do lazy initialization of disk cache if needed. - if (!disk_cache_.get()) - CreateBackend(NULL, NULL); // We don't care about the result. - - trans->reset(new HttpCache::Transaction(this)); - return OK; -} - -HttpCache* HttpCache::GetCache() { - return this; -} - -HttpNetworkSession* HttpCache::GetSession() { - net::HttpNetworkLayer* network = - static_cast<net::HttpNetworkLayer*>(network_layer_.get()); - return network->GetSession(); -} - -void HttpCache::Suspend(bool suspend) { - network_layer_->Suspend(suspend); -} - // static bool HttpCache::ParseResponseInfo(const char* data, int len, HttpResponseInfo* response_info, @@ -442,6 +462,29 @@ void HttpCache::CloseCurrentConnections() { } } +int HttpCache::CreateTransaction(scoped_ptr<HttpTransaction>* trans) { + // Do lazy initialization of disk cache if needed. + if (!disk_cache_.get()) + CreateBackend(NULL, NULL); // We don't care about the result. + + trans->reset(new HttpCache::Transaction(this)); + return OK; +} + +HttpCache* HttpCache::GetCache() { + return this; +} + +HttpNetworkSession* HttpCache::GetSession() { + net::HttpNetworkLayer* network = + static_cast<net::HttpNetworkLayer*>(network_layer_.get()); + return network->GetSession(); +} + +void HttpCache::Suspend(bool suspend) { + network_layer_->Suspend(suspend); +} + //----------------------------------------------------------------------------- int HttpCache::CreateBackend(disk_cache::Backend** backend, diff --git a/net/http/http_cache.h b/net/http/http_cache.h index 0641ca4..cdbddfe 100644 --- a/net/http/http_cache.h +++ b/net/http/http_cache.h @@ -60,8 +60,6 @@ class HttpCache : public HttpTransactionFactory, public base::SupportsWeakPtr<HttpCache>, public base::NonThreadSafe { public: - ~HttpCache(); - // The cache mode of operation. enum Mode { // Normal mode just behaves like a standard web cache. @@ -133,7 +131,7 @@ class HttpCache : public HttpTransactionFactory, // 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. authenication data). The + // network layers to share information (e.g. authentication data). The // HttpCache takes ownership of the |backend_factory|. HttpCache(HttpNetworkSession* session, BackendFactory* backend_factory); @@ -145,6 +143,8 @@ class HttpCache : public HttpTransactionFactory, NetLog* net_log, BackendFactory* backend_factory); + ~HttpCache(); + HttpTransactionFactory* network_layer() { return network_layer_.get(); } // Retrieves the cache backend for this HttpCache instance. If the backend @@ -157,12 +157,6 @@ class HttpCache : public HttpTransactionFactory, // Returns the current backend (can be NULL). disk_cache::Backend* GetCurrentBackend(); - // HttpTransactionFactory implementation: - virtual int CreateTransaction(scoped_ptr<HttpTransaction>* trans); - virtual HttpCache* GetCache(); - virtual HttpNetworkSession* GetSession(); - virtual void Suspend(bool suspend); - // Given a header data blob, convert it to a response info object. static bool ParseResponseInfo(const char* data, int len, HttpResponseInfo* response_info, @@ -184,6 +178,12 @@ class HttpCache : public HttpTransactionFactory, // immediately, but they will not be reusable. This is for debugging. void CloseCurrentConnections(); + // HttpTransactionFactory implementation: + virtual int CreateTransaction(scoped_ptr<HttpTransaction>* trans); + virtual HttpCache* GetCache(); + virtual HttpNetworkSession* GetSession(); + virtual void Suspend(bool suspend); + protected: // Disk cache entry data indices. enum { @@ -211,20 +211,21 @@ class HttpCache : public HttpTransactionFactory, typedef std::list<WorkItem*> WorkItemList; struct ActiveEntry { + explicit ActiveEntry(disk_cache::Entry* entry); + ~ActiveEntry(); + disk_cache::Entry* disk_entry; Transaction* writer; TransactionList readers; TransactionList pending_queue; bool will_process_pending_queue; bool doomed; - - explicit ActiveEntry(disk_cache::Entry* entry); - ~ActiveEntry(); }; typedef base::hash_map<std::string, ActiveEntry*> ActiveEntriesMap; typedef base::hash_map<std::string, PendingOp*> PendingOpsMap; typedef std::set<ActiveEntry*> ActiveEntriesSet; + typedef base::hash_map<std::string, int> PlaybackCacheMap; // Methods ------------------------------------------------------------------ @@ -355,9 +356,9 @@ class HttpCache : public HttpTransactionFactory, Mode mode_; - scoped_ptr<SSLHostInfoFactoryAdaptor> ssl_host_info_factory_; + const scoped_ptr<SSLHostInfoFactoryAdaptor> ssl_host_info_factory_; - scoped_ptr<HttpTransactionFactory> network_layer_; + const scoped_ptr<HttpTransactionFactory> network_layer_; scoped_ptr<disk_cache::Backend> disk_cache_; // The set of active entries indexed by cache key. @@ -371,7 +372,6 @@ class HttpCache : public HttpTransactionFactory, ScopedRunnableMethodFactory<HttpCache> task_factory_; - typedef base::hash_map<std::string, int> PlaybackCacheMap; scoped_ptr<PlaybackCacheMap> playback_cache_map_; DISALLOW_COPY_AND_ASSIGN(HttpCache); diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index b506edc..32386f0 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc @@ -163,6 +163,70 @@ HttpCache::Transaction::~Transaction() { cache_.reset(); } +int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, + CompletionCallback* callback) { + DCHECK(buf); + DCHECK_GT(buf_len, 0); + DCHECK(callback); + if (!cache_ || !entry_) + return ERR_UNEXPECTED; + + // We don't need to track this operation for anything. + // It could be possible to check if there is something already written and + // avoid writing again (it should be the same, right?), but let's allow the + // caller to "update" the contents with something new. + return entry_->disk_entry->WriteData(kMetadataIndex, 0, buf, buf_len, + callback, true); +} + +// Histogram data from the end of 2010 show the following distribution of +// response headers: +// +// Content-Length............... 87% +// Date......................... 98% +// Last-Modified................ 49% +// Etag......................... 19% +// Accept-Ranges: bytes......... 25% +// Accept-Ranges: none.......... 0.4% +// Strong Validator............. 50% +// Strong Validator + ranges.... 24% +// Strong Validator + CL........ 49% +// +bool HttpCache::Transaction::AddTruncatedFlag() { + DCHECK(mode_ & WRITE); + + // Don't set the flag for sparse entries. + if (partial_.get() && !truncated_) + return true; + + // Double check that there is something worth keeping. + if (!entry_->disk_entry->GetDataSize(kResponseContentIndex)) + return false; + + if (response_.headers->GetContentLength() <= 0 || + response_.headers->HasHeaderValue("Accept-Ranges", "none") || + !response_.headers->HasStrongValidators()) + return false; + + truncated_ = true; + target_state_ = STATE_NONE; + next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE; + DoLoop(OK); + return true; +} + +LoadState HttpCache::Transaction::GetWriterLoadState() const { + if (network_trans_.get()) + return network_trans_->GetLoadState(); + if (entry_ || !request_) + return LOAD_STATE_IDLE; + return LOAD_STATE_WAITING_FOR_CACHE; +} + +const BoundNetLog& HttpCache::Transaction::net_log() const { + return net_log_; +} + int HttpCache::Transaction::Start(const HttpRequestInfo* request, CompletionCallback* callback, const BoundNetLog& net_log) { @@ -338,70 +402,6 @@ uint64 HttpCache::Transaction::GetUploadProgress() const { return final_upload_progress_; } -int HttpCache::Transaction::WriteMetadata(IOBuffer* buf, int buf_len, - CompletionCallback* callback) { - DCHECK(buf); - DCHECK_GT(buf_len, 0); - DCHECK(callback); - if (!cache_ || !entry_) - return ERR_UNEXPECTED; - - // We don't need to track this operation for anything. - // It could be possible to check if there is something already written and - // avoid writing again (it should be the same, right?), but let's allow the - // caller to "update" the contents with something new. - return entry_->disk_entry->WriteData(kMetadataIndex, 0, buf, buf_len, - callback, true); -} - -// Histogram data from the end of 2010 show the following distribution of -// response headers: -// -// Content-Length............... 87% -// Date......................... 98% -// Last-Modified................ 49% -// Etag......................... 19% -// Accept-Ranges: bytes......... 25% -// Accept-Ranges: none.......... 0.4% -// Strong Validator............. 50% -// Strong Validator + ranges.... 24% -// Strong Validator + CL........ 49% -// -bool HttpCache::Transaction::AddTruncatedFlag() { - DCHECK(mode_ & WRITE); - - // Don't set the flag for sparse entries. - if (partial_.get() && !truncated_) - return true; - - // Double check that there is something worth keeping. - if (!entry_->disk_entry->GetDataSize(kResponseContentIndex)) - return false; - - if (response_.headers->GetContentLength() <= 0 || - response_.headers->HasHeaderValue("Accept-Ranges", "none") || - !response_.headers->HasStrongValidators()) - return false; - - truncated_ = true; - target_state_ = STATE_NONE; - next_state_ = STATE_CACHE_WRITE_TRUNCATED_RESPONSE; - DoLoop(OK); - return true; -} - -LoadState HttpCache::Transaction::GetWriterLoadState() const { - if (network_trans_.get()) - return network_trans_->GetLoadState(); - if (entry_ || !request_) - return LOAD_STATE_IDLE; - return LOAD_STATE_WAITING_FOR_CACHE; -} - -const BoundNetLog& HttpCache::Transaction::net_log() const { - return net_log_; -} - //----------------------------------------------------------------------------- void HttpCache::Transaction::DoCallback(int rv) { diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h index 316c15b..81160d5 100644 --- a/net/http/http_cache_transaction.h +++ b/net/http/http_cache_transaction.h @@ -28,25 +28,6 @@ struct HttpRequestInfo; // factory. class HttpCache::Transaction : public HttpTransaction { public: - Transaction(HttpCache* cache); - virtual ~Transaction(); - - // HttpTransaction methods: - virtual int Start(const HttpRequestInfo*, CompletionCallback*, - const BoundNetLog&); - virtual int RestartIgnoringLastError(CompletionCallback* callback); - virtual int RestartWithCertificate(X509Certificate* client_cert, - CompletionCallback* callback); - virtual int RestartWithAuth(const string16& username, - const string16& password, - CompletionCallback* callback); - virtual bool IsReadyToRestartForAuth(); - virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); - virtual void StopCaching(); - virtual const HttpResponseInfo* GetResponseInfo() const; - virtual LoadState GetLoadState() const; - virtual uint64 GetUploadProgress(void) const; - // The transaction has the following modes, which apply to how it may access // its cache entry. // @@ -76,6 +57,9 @@ class HttpCache::Transaction : public HttpTransaction { UPDATE = READ_META | WRITE, // READ_WRITE & ~READ_DATA }; + Transaction(HttpCache* cache); + virtual ~Transaction(); + Mode mode() const { return mode_; } const std::string& key() const { return cache_key_; } @@ -112,6 +96,22 @@ class HttpCache::Transaction : public HttpTransaction { const BoundNetLog& net_log() const; + // HttpTransaction methods: + virtual int Start(const HttpRequestInfo*, CompletionCallback*, + const BoundNetLog&); + virtual int RestartIgnoringLastError(CompletionCallback* callback); + virtual int RestartWithCertificate(X509Certificate* client_cert, + CompletionCallback* callback); + virtual int RestartWithAuth(const string16& username, + const string16& password, + CompletionCallback* callback); + virtual bool IsReadyToRestartForAuth(); + virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); + virtual void StopCaching(); + virtual const HttpResponseInfo* GetResponseInfo() const; + virtual LoadState GetLoadState() const; + virtual uint64 GetUploadProgress(void) const; + private: static const size_t kNumValidationHeaders = 2; // Helper struct to pair a header name with its value, for diff --git a/net/http/http_net_log_params.h b/net/http/http_net_log_params.h index 1631363..c32250e 100644 --- a/net/http/http_net_log_params.h +++ b/net/http/http_net_log_params.h @@ -24,8 +24,6 @@ class NetLogHttpRequestParameter : public NetLog::EventParameters { NetLogHttpRequestParameter(const std::string& line, const HttpRequestHeaders& headers); - virtual Value* ToValue() const; - const HttpRequestHeaders& GetHeaders() const { return headers_; } @@ -34,6 +32,9 @@ class NetLogHttpRequestParameter : public NetLog::EventParameters { return line_; } + // NetLog::EventParameters + virtual Value* ToValue() const; + private: virtual ~NetLogHttpRequestParameter(); @@ -48,12 +49,13 @@ class NetLogHttpResponseParameter : public NetLog::EventParameters { explicit NetLogHttpResponseParameter( const scoped_refptr<HttpResponseHeaders>& headers); - virtual Value* ToValue() const; - const HttpResponseHeaders& GetHeaders() const { return *headers_; } + // NetLog::EventParameters + virtual Value* ToValue() const; + private: virtual ~NetLogHttpResponseParameter(); diff --git a/net/http/http_network_delegate.h b/net/http/http_network_delegate.h index 6b60aba..1533bc3 100644 --- a/net/http/http_network_delegate.h +++ b/net/http/http_network_delegate.h @@ -9,9 +9,13 @@ namespace net { class HttpRequestHeaders; +class URLRequest; class HttpNetworkDelegate { public: + // Called before a request is sent. + virtual void OnBeforeURLRequest(net::URLRequest* request) = 0; + // Called right before the HTTP headers are sent. Allows the delegate to // read/write |headers| before they get sent out. virtual void OnSendHttpRequest(HttpRequestHeaders* headers) = 0; diff --git a/net/http/http_network_layer.cc b/net/http/http_network_layer.cc index 9a11034..73e6f8d 100644 --- a/net/http/http_network_layer.cc +++ b/net/http/http_network_layer.cc @@ -10,121 +10,14 @@ #include "base/string_util.h" #include "net/http/http_network_session.h" #include "net/http/http_network_transaction.h" -#include "net/socket/client_socket_factory.h" #include "net/spdy/spdy_framer.h" #include "net/spdy/spdy_session.h" -#include "net/spdy/spdy_session_pool.h" namespace net { //----------------------------------------------------------------------------- - -// static -HttpTransactionFactory* HttpNetworkLayer::CreateFactory( - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - SSLConfigService* ssl_config_service, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log) { - DCHECK(proxy_service); - - return new HttpNetworkLayer(ClientSocketFactory::GetDefaultFactory(), - host_resolver, cert_verifier, dnsrr_resolver, - dns_cert_checker, - ssl_host_info_factory, proxy_service, - ssl_config_service, http_auth_handler_factory, - network_delegate, - net_log); -} - -// static -HttpTransactionFactory* HttpNetworkLayer::CreateFactory( - HttpNetworkSession* session) { - DCHECK(session); - - return new HttpNetworkLayer(session); -} - -//----------------------------------------------------------------------------- -HttpNetworkLayer::HttpNetworkLayer( - ClientSocketFactory* socket_factory, - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - SSLConfigService* ssl_config_service, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log) - : socket_factory_(socket_factory), - host_resolver_(host_resolver), - cert_verifier_(cert_verifier), - dnsrr_resolver_(dnsrr_resolver), - dns_cert_checker_(dns_cert_checker), - ssl_host_info_factory_(ssl_host_info_factory), - proxy_service_(proxy_service), - ssl_config_service_(ssl_config_service), - session_(NULL), - spdy_session_pool_(NULL), - http_auth_handler_factory_(http_auth_handler_factory), - network_delegate_(network_delegate), - net_log_(net_log), - suspended_(false) { - DCHECK(proxy_service_); - DCHECK(ssl_config_service_.get()); -} - -HttpNetworkLayer::HttpNetworkLayer( - ClientSocketFactory* socket_factory, - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - SSLConfigService* ssl_config_service, - SpdySessionPool* spdy_session_pool, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log) - : socket_factory_(socket_factory), - host_resolver_(host_resolver), - cert_verifier_(cert_verifier), - dnsrr_resolver_(dnsrr_resolver), - dns_cert_checker_(dns_cert_checker), - ssl_host_info_factory_(ssl_host_info_factory), - proxy_service_(proxy_service), - ssl_config_service_(ssl_config_service), - session_(NULL), - spdy_session_pool_(spdy_session_pool), - http_auth_handler_factory_(http_auth_handler_factory), - network_delegate_(network_delegate), - net_log_(net_log), - suspended_(false) { - DCHECK(proxy_service_); - DCHECK(ssl_config_service_.get()); -} - HttpNetworkLayer::HttpNetworkLayer(HttpNetworkSession* session) - : socket_factory_(ClientSocketFactory::GetDefaultFactory()), - host_resolver_(NULL), - cert_verifier_(NULL), - dnsrr_resolver_(NULL), - dns_cert_checker_(NULL), - ssl_host_info_factory_(NULL), - ssl_config_service_(NULL), - session_(session), - spdy_session_pool_(NULL), - http_auth_handler_factory_(NULL), - network_delegate_(NULL), - net_log_(NULL), + : session_(session), suspended_(false) { DCHECK(session_.get()); } @@ -132,56 +25,14 @@ HttpNetworkLayer::HttpNetworkLayer(HttpNetworkSession* session) HttpNetworkLayer::~HttpNetworkLayer() { } -int HttpNetworkLayer::CreateTransaction(scoped_ptr<HttpTransaction>* trans) { - if (suspended_) - return ERR_NETWORK_IO_SUSPENDED; - - trans->reset(new HttpNetworkTransaction(GetSession())); - return OK; -} - -HttpCache* HttpNetworkLayer::GetCache() { - return NULL; -} - -void HttpNetworkLayer::Suspend(bool suspend) { - suspended_ = suspend; +//----------------------------------------------------------------------------- - if (suspend && session_) - session_->tcp_socket_pool()->CloseIdleSockets(); -} +// static +HttpTransactionFactory* HttpNetworkLayer::CreateFactory( + HttpNetworkSession* session) { + DCHECK(session); -HttpNetworkSession* HttpNetworkLayer::GetSession() { - if (!session_) { - DCHECK(proxy_service_); - if (!spdy_session_pool_.get()) - spdy_session_pool_.reset(new SpdySessionPool(ssl_config_service_)); - session_ = new HttpNetworkSession( - host_resolver_, - cert_verifier_, - dnsrr_resolver_, - dns_cert_checker_, - ssl_host_info_factory_, - proxy_service_, - socket_factory_, - ssl_config_service_, - spdy_session_pool_.release(), - http_auth_handler_factory_, - network_delegate_, - net_log_); - // These were just temps for lazy-initializing HttpNetworkSession. - host_resolver_ = NULL; - cert_verifier_ = NULL; - dnsrr_resolver_ = NULL; - dns_cert_checker_ = NULL; - ssl_host_info_factory_ = NULL; - proxy_service_ = NULL; - socket_factory_ = NULL; - http_auth_handler_factory_ = NULL; - net_log_ = NULL; - network_delegate_ = NULL; - } - return session_; + return new HttpNetworkLayer(session); } // static @@ -277,4 +128,30 @@ void HttpNetworkLayer::EnableSpdy(const std::string& mode) { } } } + +//----------------------------------------------------------------------------- + +int HttpNetworkLayer::CreateTransaction(scoped_ptr<HttpTransaction>* trans) { + if (suspended_) + return ERR_NETWORK_IO_SUSPENDED; + + trans->reset(new HttpNetworkTransaction(GetSession())); + return OK; +} + +HttpCache* HttpNetworkLayer::GetCache() { + return NULL; +} + +HttpNetworkSession* HttpNetworkLayer::GetSession() { + return session_; +} + +void HttpNetworkLayer::Suspend(bool suspend) { + suspended_ = suspend; + + if (suspend && session_) + session_->tcp_socket_pool()->CloseIdleSockets(); +} + } // namespace net diff --git a/net/http/http_network_layer.h b/net/http/http_network_layer.h index 730b5c7..da92761 100644 --- a/net/http/http_network_layer.h +++ b/net/http/http_network_layer.h @@ -32,52 +32,11 @@ class SSLHostInfoFactory; class HttpNetworkLayer : public HttpTransactionFactory, public base::NonThreadSafe { public: - // |socket_factory|, |proxy_service|, |host_resolver|, etc. must remain - // valid for the lifetime of HttpNetworkLayer. - // TODO(wtc): we only need the next constructor. - HttpNetworkLayer(ClientSocketFactory* socket_factory, - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - SSLConfigService* ssl_config_service, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log); - HttpNetworkLayer( - ClientSocketFactory* socket_factory, - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - SSLConfigService* ssl_config_service, - SpdySessionPool* spdy_session_pool, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log); - // Construct a HttpNetworkLayer with an existing HttpNetworkSession which // contains a valid ProxyService. explicit HttpNetworkLayer(HttpNetworkSession* session); - ~HttpNetworkLayer(); + virtual ~HttpNetworkLayer(); - // This function hides the details of how a network layer gets instantiated - // and allows other implementations to be substituted. - static HttpTransactionFactory* CreateFactory( - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - SSLConfigService* ssl_config_service, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log); // Create a transaction factory that instantiate a network layer over an // existing network session. Network session contains some valuable // information (e.g. authentication data) that we want to share across @@ -86,12 +45,6 @@ class HttpNetworkLayer : public HttpTransactionFactory, // when network session is shared. static HttpTransactionFactory* CreateFactory(HttpNetworkSession* session); - // HttpTransactionFactory methods: - virtual int CreateTransaction(scoped_ptr<HttpTransaction>* trans); - virtual HttpCache* GetCache(); - virtual HttpNetworkSession* GetSession(); - virtual void Suspend(bool suspend); - // Enable the spdy protocol. // Without calling this function, SPDY is disabled. The mode can be: // "" : (default) SSL and compression are enabled, flow @@ -102,29 +55,14 @@ class HttpNetworkLayer : public HttpTransactionFactory, // "none" : disables both SSL and compression. static void EnableSpdy(const std::string& mode); - private: - // The factory we will use to create network sockets. - ClientSocketFactory* socket_factory_; - - // The host resolver, proxy service, etc. that will be used when lazily - // creating |session_|. - HostResolver* host_resolver_; - CertVerifier* cert_verifier_; - DnsRRResolver* dnsrr_resolver_; - DnsCertProvenanceChecker* dns_cert_checker_; - SSLHostInfoFactory* ssl_host_info_factory_; - scoped_refptr<ProxyService> proxy_service_; - - // The SSL config service being used for the session. - scoped_refptr<SSLConfigService> ssl_config_service_; - - scoped_refptr<HttpNetworkSession> session_; - scoped_ptr<SpdySessionPool> spdy_session_pool_; - - HttpAuthHandlerFactory* http_auth_handler_factory_; - HttpNetworkDelegate* network_delegate_; - NetLog* net_log_; + // HttpTransactionFactory methods: + virtual int CreateTransaction(scoped_ptr<HttpTransaction>* trans); + virtual HttpCache* GetCache(); + virtual HttpNetworkSession* GetSession(); + virtual void Suspend(bool suspend); + private: + const scoped_refptr<HttpNetworkSession> session_; bool suspended_; }; diff --git a/net/http/http_network_layer_unittest.cc b/net/http/http_network_layer_unittest.cc index 2720c10..5120fac 100644 --- a/net/http/http_network_layer_unittest.cc +++ b/net/http/http_network_layer_unittest.cc @@ -7,9 +7,11 @@ #include "net/base/net_log.h" #include "net/base/ssl_config_service_defaults.h" #include "net/http/http_network_layer.h" +#include "net/http/http_network_session.h" #include "net/http/http_transaction_unittest.h" #include "net/proxy/proxy_service.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_session_pool.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" @@ -18,118 +20,93 @@ namespace net { namespace { class HttpNetworkLayerTest : public PlatformTest { + protected: + HttpNetworkLayerTest() + : proxy_service_(ProxyService::CreateDirect()), + ssl_config_service_(new SSLConfigServiceDefaults) { + HttpNetworkSession::Params session_params; + session_params.client_socket_factory = &mock_socket_factory_; + session_params.host_resolver = &host_resolver_; + session_params.cert_verifier = &cert_verifier_; + session_params.proxy_service = proxy_service_; + session_params.ssl_config_service = ssl_config_service_; + network_session_ = new HttpNetworkSession(session_params); + factory_.reset(new HttpNetworkLayer(network_session_)); + } + + MockClientSocketFactory mock_socket_factory_; + MockHostResolver host_resolver_; + CertVerifier cert_verifier_; + const scoped_refptr<ProxyService> proxy_service_; + const scoped_refptr<SSLConfigService> ssl_config_service_; + scoped_refptr<HttpNetworkSession> network_session_; + scoped_ptr<HttpNetworkLayer> factory_; }; TEST_F(HttpNetworkLayerTest, CreateAndDestroy) { - MockHostResolver host_resolver; - net::CertVerifier cert_verifier; - net::HttpNetworkLayer factory( - NULL, - &host_resolver, - &cert_verifier, - NULL /* dnsrr_resolver */, - NULL /* dns_cert_checker */, - NULL /* ssl_host_info_factory */, - net::ProxyService::CreateDirect(), - new net::SSLConfigServiceDefaults, - NULL, - NULL, - NULL); - - scoped_ptr<net::HttpTransaction> trans; - int rv = factory.CreateTransaction(&trans); - EXPECT_EQ(net::OK, rv); + scoped_ptr<HttpTransaction> trans; + int rv = factory_->CreateTransaction(&trans); + EXPECT_EQ(OK, rv); EXPECT_TRUE(trans.get() != NULL); } TEST_F(HttpNetworkLayerTest, Suspend) { - MockHostResolver host_resolver; - net::CertVerifier cert_verifier; - net::HttpNetworkLayer factory( - NULL, - &host_resolver, - &cert_verifier, - NULL /* dnsrr_resolver */, - NULL /* dns_cert_checker */, - NULL /* ssl_host_info_factory */, - net::ProxyService::CreateDirect(), - new net::SSLConfigServiceDefaults, - NULL, - NULL, - NULL); - - scoped_ptr<net::HttpTransaction> trans; - int rv = factory.CreateTransaction(&trans); - EXPECT_EQ(net::OK, rv); + scoped_ptr<HttpTransaction> trans; + int rv = factory_->CreateTransaction(&trans); + EXPECT_EQ(OK, rv); trans.reset(); - factory.Suspend(true); + factory_->Suspend(true); - rv = factory.CreateTransaction(&trans); - EXPECT_EQ(net::ERR_NETWORK_IO_SUSPENDED, rv); + rv = factory_->CreateTransaction(&trans); + EXPECT_EQ(ERR_NETWORK_IO_SUSPENDED, rv); ASSERT_TRUE(trans == NULL); - factory.Suspend(false); + factory_->Suspend(false); - rv = factory.CreateTransaction(&trans); - EXPECT_EQ(net::OK, rv); + rv = factory_->CreateTransaction(&trans); + EXPECT_EQ(OK, rv); } TEST_F(HttpNetworkLayerTest, GET) { - net::MockClientSocketFactory mock_socket_factory; - net::MockRead data_reads[] = { - net::MockRead("HTTP/1.0 200 OK\r\n\r\n"), - net::MockRead("hello world"), - net::MockRead(false, net::OK), + MockRead data_reads[] = { + MockRead("HTTP/1.0 200 OK\r\n\r\n"), + MockRead("hello world"), + MockRead(false, OK), }; - net::MockWrite data_writes[] = { - net::MockWrite("GET / HTTP/1.1\r\n" + MockWrite data_writes[] = { + MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" "Connection: keep-alive\r\n" "User-Agent: Foo/1.0\r\n\r\n"), }; - net::StaticSocketDataProvider data(data_reads, arraysize(data_reads), + StaticSocketDataProvider data(data_reads, arraysize(data_reads), data_writes, arraysize(data_writes)); - mock_socket_factory.AddSocketDataProvider(&data); - - MockHostResolver host_resolver; - net::CertVerifier cert_verifier; - net::HttpNetworkLayer factory( - &mock_socket_factory, - &host_resolver, - &cert_verifier, - NULL /* dnsrr_resolver */, - NULL /* dns_cert_checker */, - NULL /* ssl_host_info_factory */, - net::ProxyService::CreateDirect(), - new net::SSLConfigServiceDefaults, - NULL, - NULL, - NULL); + mock_socket_factory_.AddSocketDataProvider(&data); TestCompletionCallback callback; - scoped_ptr<net::HttpTransaction> trans; - int rv = factory.CreateTransaction(&trans); - EXPECT_EQ(net::OK, rv); - - net::HttpRequestInfo request_info; + HttpRequestInfo request_info; request_info.url = GURL("http://www.google.com/"); request_info.method = "GET"; - request_info.extra_headers.SetHeader(net::HttpRequestHeaders::kUserAgent, + request_info.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Foo/1.0"); - request_info.load_flags = net::LOAD_NORMAL; + request_info.load_flags = LOAD_NORMAL; + + scoped_ptr<HttpTransaction> trans; + int rv = factory_->CreateTransaction(&trans); + EXPECT_EQ(OK, rv); - rv = trans->Start(&request_info, &callback, net::BoundNetLog()); - if (rv == net::ERR_IO_PENDING) + rv = trans->Start(&request_info, &callback, BoundNetLog()); + if (rv == ERR_IO_PENDING) rv = callback.WaitForResult(); - ASSERT_EQ(net::OK, rv); + ASSERT_EQ(OK, rv); std::string contents; rv = ReadTransaction(trans.get(), &contents); - EXPECT_EQ(net::OK, rv); + EXPECT_EQ(OK, rv); EXPECT_EQ("hello world", contents); } diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc index e3de475..b50b6e9 100644 --- a/net/http/http_network_session.cc +++ b/net/http/http_network_session.cc @@ -13,51 +13,38 @@ #include "net/http/http_auth_handler_factory.h" #include "net/http/http_response_body_drainer.h" #include "net/http/url_security_manager.h" +#include "net/socket/client_socket_factory.h" #include "net/spdy/spdy_session_pool.h" namespace net { // TODO(mbelshe): Move the socket factories into HttpStreamFactory. -HttpNetworkSession::HttpNetworkSession( - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - ClientSocketFactory* client_socket_factory, - SSLConfigService* ssl_config_service, - SpdySessionPool* spdy_session_pool, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log) - : socket_factory_(client_socket_factory), - host_resolver_(host_resolver), - cert_verifier_(cert_verifier), - dnsrr_resolver_(dnsrr_resolver), - dns_cert_checker_(dns_cert_checker), - proxy_service_(proxy_service), - ssl_config_service_(ssl_config_service), - socket_pool_manager_(net_log, - client_socket_factory, - host_resolver, - cert_verifier, - dnsrr_resolver, - dns_cert_checker, - ssl_host_info_factory, - proxy_service, - ssl_config_service), - spdy_session_pool_(spdy_session_pool), - http_auth_handler_factory_(http_auth_handler_factory), - network_delegate_(network_delegate), - net_log_(net_log) { - DCHECK(proxy_service); - DCHECK(ssl_config_service); +HttpNetworkSession::HttpNetworkSession(const Params& params) + : cert_verifier_(NULL), + proxy_service_(params.proxy_service), + ssl_config_service_(params.ssl_config_service), + socket_pool_manager_(params.net_log, + params.client_socket_factory ? + params.client_socket_factory : + ClientSocketFactory::GetDefaultFactory(), + params.host_resolver, + params.cert_verifier, + params.dnsrr_resolver, + params.dns_cert_checker, + params.ssl_host_info_factory, + params.proxy_service, + params.ssl_config_service), + spdy_session_pool_(params.ssl_config_service), + http_auth_handler_factory_(params.http_auth_handler_factory), + network_delegate_(params.network_delegate), + net_log_(params.net_log) { + DCHECK(params.proxy_service); + DCHECK(params.ssl_config_service); } HttpNetworkSession::~HttpNetworkSession() { STLDeleteElements(&response_drainers_); - spdy_session_pool_->CloseAllSessions(); + spdy_session_pool_.CloseAllSessions(); } void HttpNetworkSession::AddResponseDrainer(HttpResponseBodyDrainer* drainer) { @@ -72,7 +59,7 @@ void HttpNetworkSession::RemoveResponseDrainer( } Value* HttpNetworkSession::SpdySessionPoolInfoToValue() const { - return spdy_session_pool_->SpdySessionPoolInfoToValue(); + return spdy_session_pool_.SpdySessionPoolInfoToValue(); } } // namespace net diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index b36d7f7..36cb652 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h @@ -6,22 +6,17 @@ #define NET_HTTP_HTTP_NETWORK_SESSION_H_ #pragma once -#include <map> #include <set> #include "base/ref_counted.h" -#include "base/scoped_ptr.h" #include "base/threading/non_thread_safe.h" #include "net/base/host_port_pair.h" #include "net/base/host_resolver.h" #include "net/base/ssl_client_auth_cache.h" -#include "net/base/ssl_config_service.h" #include "net/http/http_alternate_protocols.h" #include "net/http/http_auth_cache.h" -#include "net/http/http_network_delegate.h" -#include "net/http/http_network_transaction.h" #include "net/http/http_stream_factory.h" -#include "net/proxy/proxy_service.h" #include "net/socket/client_socket_pool_manager.h" +#include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_settings_storage.h" class Value; @@ -32,34 +27,49 @@ class CertVerifier; class ClientSocketFactory; class DnsCertProvenanceChecker; class DnsRRResolver; +class HostResolver; class HttpAuthHandlerFactory; class HttpNetworkDelegate; class HttpNetworkSessionPeer; class HttpProxyClientSocketPool; class HttpResponseBodyDrainer; -class SpdySessionPool; -class SOCKSClientSocketPool; -class SSLClientSocketPool; +class NetLog; +class ProxyService; +class SSLConfigService; class SSLHostInfoFactory; -class TCPClientSocketPool; // This class holds session objects used by HttpNetworkTransaction objects. class HttpNetworkSession : public base::RefCounted<HttpNetworkSession>, public base::NonThreadSafe { public: - HttpNetworkSession( - HostResolver* host_resolver, - CertVerifier* cert_verifier, - DnsRRResolver* dnsrr_resolver, - DnsCertProvenanceChecker* dns_cert_checker, - SSLHostInfoFactory* ssl_host_info_factory, - ProxyService* proxy_service, - ClientSocketFactory* client_socket_factory, - SSLConfigService* ssl_config_service, - SpdySessionPool* spdy_session_pool, - HttpAuthHandlerFactory* http_auth_handler_factory, - HttpNetworkDelegate* network_delegate, - NetLog* net_log); + struct Params { + Params() + : client_socket_factory(NULL), + host_resolver(NULL), + cert_verifier(NULL), + dnsrr_resolver(NULL), + dns_cert_checker(NULL), + proxy_service(NULL), + ssl_host_info_factory(NULL), + ssl_config_service(NULL), + http_auth_handler_factory(NULL), + network_delegate(NULL), + net_log(NULL) {} + + ClientSocketFactory* client_socket_factory; + HostResolver* host_resolver; + CertVerifier* cert_verifier; + DnsRRResolver* dnsrr_resolver; + DnsCertProvenanceChecker* dns_cert_checker; + ProxyService* proxy_service; + SSLHostInfoFactory* ssl_host_info_factory; + SSLConfigService* ssl_config_service; + HttpAuthHandlerFactory* http_auth_handler_factory; + HttpNetworkDelegate* network_delegate; + NetLog* net_log; + }; + + explicit HttpNetworkSession(const Params& params); HttpAuthCache* auth_cache() { return &auth_cache_; } SSLClientAuthCache* ssl_client_auth_cache() { @@ -108,17 +118,10 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession>, return socket_pool_manager_.GetSocketPoolForSSLWithProxy(proxy_server); } - // SSL sockets come from the socket_factory(). - ClientSocketFactory* socket_factory() { return socket_factory_; } - HostResolver* host_resolver() { return host_resolver_; } CertVerifier* cert_verifier() { return cert_verifier_; } - DnsRRResolver* dnsrr_resolver() { return dnsrr_resolver_; } - DnsCertProvenanceChecker* dns_cert_checker() { - return dns_cert_checker_; - } ProxyService* proxy_service() { return proxy_service_; } SSLConfigService* ssl_config_service() { return ssl_config_service_; } - SpdySessionPool* spdy_session_pool() { return spdy_session_pool_.get(); } + SpdySessionPool* spdy_session_pool() { return &spdy_session_pool_; } HttpAuthHandlerFactory* http_auth_handler_factory() { return http_auth_handler_factory_; } @@ -154,24 +157,19 @@ class HttpNetworkSession : public base::RefCounted<HttpNetworkSession>, ~HttpNetworkSession(); - ClientSocketFactory* const socket_factory_; HttpAuthCache auth_cache_; SSLClientAuthCache ssl_client_auth_cache_; HttpAlternateProtocols alternate_protocols_; - HostResolver* const host_resolver_; CertVerifier* cert_verifier_; - DnsRRResolver* dnsrr_resolver_; - DnsCertProvenanceChecker* dns_cert_checker_; + // Not const since it's modified by HttpNetworkSessionPeer for testing. scoped_refptr<ProxyService> proxy_service_; - scoped_refptr<SSLConfigService> ssl_config_service_; + const scoped_refptr<SSLConfigService> ssl_config_service_; ClientSocketPoolManager socket_pool_manager_; - // TODO(willchan): Move this out to IOThread so it can be shared across - // URLRequestContexts. - scoped_ptr<SpdySessionPool> spdy_session_pool_; + SpdySessionPool spdy_session_pool_; HttpStreamFactory http_stream_factory_; - HttpAuthHandlerFactory* http_auth_handler_factory_; + HttpAuthHandlerFactory* const http_auth_handler_factory_; HttpNetworkDelegate* const network_delegate_; - NetLog* net_log_; + NetLog* const net_log_; SpdySettingsStorage spdy_settings_; std::set<HttpResponseBodyDrainer*> response_drainers_; }; diff --git a/net/http/http_network_session_peer.h b/net/http/http_network_session_peer.h index 13f3fa7..398488b 100644 --- a/net/http/http_network_session_peer.h +++ b/net/http/http_network_session_peer.h @@ -6,66 +6,41 @@ #define NET_HTTP_HTTP_NETWORK_SESSION_PEER_H_ #pragma once -#include "net/http/http_network_session.h" -#include "net/http/http_proxy_client_socket_pool.h" -#include "net/socket/socks_client_socket_pool.h" -#include "net/socket/ssl_client_socket_pool.h" +#include "base/ref_counted.h" namespace net { +class HostPortPair; +class HttpNetworkSession; +class HttpProxyClientSocketPool; +class ProxyService; +class SOCKSClientSocketPool; +class SSLClientSocketPool; +class TCPClientSocketPool; + class HttpNetworkSessionPeer { public: explicit HttpNetworkSessionPeer( - const scoped_refptr<HttpNetworkSession>& session) - : session_(session) {} + const scoped_refptr<HttpNetworkSession>& session); + ~HttpNetworkSessionPeer(); - void SetTCPSocketPool(TCPClientSocketPool* pool) { - session_->socket_pool_manager_.tcp_socket_pool_.reset(pool); - } + void SetTCPSocketPool(TCPClientSocketPool* pool); void SetSocketPoolForSOCKSProxy( const HostPortPair& socks_proxy, - SOCKSClientSocketPool* pool) { - ClientSocketPoolManager* socket_pool_manager = - &session_->socket_pool_manager_; - - // Call through the public interface to force initialization of the - // wrapped socket pools. - delete socket_pool_manager->GetSocketPoolForSOCKSProxy(socks_proxy); - socket_pool_manager->socks_socket_pools_[socks_proxy] = pool; - } + SOCKSClientSocketPool* pool); void SetSocketPoolForHTTPProxy( const HostPortPair& http_proxy, - HttpProxyClientSocketPool* pool) { - ClientSocketPoolManager* socket_pool_manager = - &session_->socket_pool_manager_; + HttpProxyClientSocketPool* pool); - // Call through the public interface to force initialization of the - // wrapped socket pools. - delete socket_pool_manager->GetSocketPoolForHTTPProxy(http_proxy); - socket_pool_manager->http_proxy_socket_pools_[http_proxy] = pool; - } - - void SetSSLSocketPool(SSLClientSocketPool* pool) { - session_->socket_pool_manager_.ssl_socket_pool_.reset(pool); - } + void SetSSLSocketPool(SSLClientSocketPool* pool); void SetSocketPoolForSSLWithProxy( const HostPortPair& proxy_host, - SSLClientSocketPool* pool) { - ClientSocketPoolManager* socket_pool_manager = - &session_->socket_pool_manager_; - - // Call through the public interface to force initialization of the - // wrapped socket pools. - delete socket_pool_manager->GetSocketPoolForSSLWithProxy(proxy_host); - socket_pool_manager->ssl_socket_pools_for_proxies_[proxy_host] = pool; - } + SSLClientSocketPool* pool); - void SetProxyService(ProxyService* proxy_service) { - session_->proxy_service_ = proxy_service; - } + void SetProxyService(ProxyService* proxy_service); private: const scoped_refptr<HttpNetworkSession> session_; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index 64a4fa7..128497f 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -33,6 +33,7 @@ #include "net/http/http_basic_stream.h" #include "net/http/http_chunked_decoder.h" #include "net/http/http_net_log_params.h" +#include "net/http/http_network_delegate.h" #include "net/http/http_network_session.h" #include "net/http/http_proxy_client_socket.h" #include "net/http/http_proxy_client_socket_pool.h" @@ -76,6 +77,19 @@ void ProcessAlternateProtocol(HttpStreamFactory* factory, http_host_port_pair); } +// Returns true if |error| is a client certificate authentication error. +bool IsClientCertificateError(int error) { + switch (error) { + case ERR_BAD_SSL_CLIENT_AUTH_CERT: + case ERR_SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED: + case ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY: + case ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED: + return true; + default: + return false; + } +} + } // namespace //----------------------------------------------------------------------------- @@ -486,7 +500,8 @@ int HttpNetworkTransaction::DoLoop(int result) { break; case STATE_SEND_REQUEST_COMPLETE: rv = DoSendRequestComplete(rv); - net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST, NULL); + net_log_.EndEventWithNetErrorCode( + NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST, rv); break; case STATE_READ_HEADERS: DCHECK_EQ(OK, rv); @@ -495,7 +510,8 @@ int HttpNetworkTransaction::DoLoop(int result) { break; case STATE_READ_HEADERS_COMPLETE: rv = DoReadHeadersComplete(rv); - net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS, NULL); + net_log_.EndEventWithNetErrorCode( + NetLog::TYPE_HTTP_TRANSACTION_READ_HEADERS, rv); break; case STATE_READ_BODY: DCHECK_EQ(OK, rv); @@ -504,7 +520,8 @@ int HttpNetworkTransaction::DoLoop(int result) { break; case STATE_READ_BODY_COMPLETE: rv = DoReadBodyComplete(rv); - net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_READ_BODY, NULL); + net_log_.EndEventWithNetErrorCode( + NetLog::TYPE_HTTP_TRANSACTION_READ_BODY, rv); break; case STATE_DRAIN_BODY_FOR_AUTH_RESTART: DCHECK_EQ(OK, rv); @@ -514,8 +531,8 @@ int HttpNetworkTransaction::DoLoop(int result) { break; case STATE_DRAIN_BODY_FOR_AUTH_RESTART_COMPLETE: rv = DoDrainBodyForAuthRestartComplete(rv); - net_log_.EndEvent( - NetLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART, NULL); + net_log_.EndEventWithNetErrorCode( + NetLog::TYPE_HTTP_TRANSACTION_DRAIN_BODY_FOR_AUTH_RESTART, rv); break; default: NOTREACHED() << "bad state"; @@ -554,6 +571,10 @@ int HttpNetworkTransaction::DoCreateStreamComplete(int result) { return OK; } + // Handle possible handshake errors that may have occurred if the stream + // used SSL for one or more of the layers. + result = HandleSSLHandshakeError(result); + // At this point we are done with the stream_request_. stream_request_.reset(); return result; @@ -696,23 +717,6 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { result = HandleCertificateRequest(result); if (result == OK) return result; - } else if ((result == ERR_SSL_DECOMPRESSION_FAILURE_ALERT || - result == ERR_SSL_BAD_RECORD_MAC_ALERT) && - ssl_config_.tls1_enabled && - !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())) { - // Some buggy servers select DEFLATE compression when offered and then - // fail to ever decompress anything. They will send a fatal alert telling - // us this. Normally we would pick this up during the handshake because - // our Finished message is compressed and we'll never get the server's - // Finished if it fails to process ours. - // - // However, with False Start, we'll believe that the handshake is - // complete as soon as we've /sent/ our Finished message. In this case, - // we only find out that the server is buggy here, when we try to read - // the initial reply. - session_->http_stream_factory()->AddTLSIntolerantServer(request_->url); - ResetConnectionAndRequestForResend(); - return OK; } if (result < 0 && result != ERR_CONNECTION_CLOSED) @@ -1024,11 +1028,61 @@ int HttpNetworkTransaction::HandleCertificateRequest(int error) { return OK; } +// TODO(rch): This does not correctly handle errors when an SSL proxy is +// being used, as all of the errors are handled as if they were generated +// by the endpoint host, request_->url, rather than considering if they were +// generated by the SSL proxy. http://crbug.com/69329 +int HttpNetworkTransaction::HandleSSLHandshakeError(int error) { + DCHECK(request_); + if (ssl_config_.send_client_cert && + (error == ERR_SSL_PROTOCOL_ERROR || IsClientCertificateError(error))) { + session_->ssl_client_auth_cache()->Remove( + GetHostAndPort(request_->url)); + } + + switch (error) { + case ERR_SSL_PROTOCOL_ERROR: + case ERR_SSL_VERSION_OR_CIPHER_MISMATCH: + case ERR_SSL_DECOMPRESSION_FAILURE_ALERT: + case ERR_SSL_BAD_RECORD_MAC_ALERT: + if (ssl_config_.tls1_enabled && + !SSLConfigService::IsKnownStrictTLSServer(request_->url.host())) { + // This could be a TLS-intolerant server, an SSL 3.0 server that + // chose a TLS-only cipher suite or a server with buggy DEFLATE + // support. Turn off TLS 1.0, DEFLATE support and retry. + session_->http_stream_factory()->AddTLSIntolerantServer(request_->url); + ResetConnectionAndRequestForResend(); + error = OK; + } + break; + case ERR_SSL_SNAP_START_NPN_MISPREDICTION: + // This means that we tried to Snap Start a connection, but we + // mispredicted the NPN result. This isn't a problem from the point of + // view of the SSL layer because the server will ignore the application + // data in the Snap Start extension. However, at the HTTP layer, we have + // already decided that it's a HTTP or SPDY connection and it's easier to + // abort and start again. + ResetConnectionAndRequestForResend(); + error = OK; + break; + } + return error; +} + // This method determines whether it is safe to resend the request after an // IO error. It can only be called in response to request header or body // write errors or response header read errors. It should not be used in // other cases, such as a Connect error. int HttpNetworkTransaction::HandleIOError(int error) { + // SSL errors may happen at any time during the stream and indicate issues + // with the underlying connection. Because the peer may request + // renegotiation at any time, check and handle any possible SSL handshake + // related errors. In addition to renegotiation, TLS False/Snap Start may + // cause SSL handshake errors to be delayed until the first or second Write + // (Snap Start) or the first Read (False & Snap Start) on the underlying + // connection. + error = HandleSSLHandshakeError(error); + switch (error) { // If we try to reuse a connection that the server is in the process of // closing, we may end up successfully writing out our request (or a @@ -1042,16 +1096,6 @@ int HttpNetworkTransaction::HandleIOError(int error) { error = OK; } break; - case ERR_SSL_SNAP_START_NPN_MISPREDICTION: - // This means that we tried to Snap Start a connection, but we - // mispredicted the NPN result. This isn't a problem from the point of - // view of the SSL layer because the server will ignore the application - // data in the Snap Start extension. However, at the HTTP layer, we have - // already decided that it's a HTTP or SPDY connection and it's easier to - // abort and start again. - ResetConnectionAndRequestForResend(); - error = OK; - break; } return error; } @@ -1191,22 +1235,6 @@ std::string HttpNetworkTransaction::DescribeState(State state) { return description; } -// TODO(gavinp): re-adjust this once SPDY v3 has three priority bits, -// eliminating the need for this folding. -int ConvertRequestPriorityToSpdyPriority(const RequestPriority priority) { - DCHECK(HIGHEST <= priority && priority < NUM_PRIORITIES); - switch (priority) { - case LOWEST: - return SPDY_PRIORITY_LOWEST-1; - case IDLE: - return SPDY_PRIORITY_LOWEST; - default: - return priority; - } -} - - - #undef STATE_CASE } // namespace net diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index dfcee41..66b6b29 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -137,6 +137,11 @@ class HttpNetworkTransaction : public HttpTransaction, // Called to handle a client certificate request. int HandleCertificateRequest(int error); + // Called to possibly recover from an SSL handshake error. Sets next_state_ + // and returns OK if recovering from the error. Otherwise, the same error + // code is returned. + int HandleSSLHandshakeError(int error); + // Called to possibly recover from the given error. Sets next_state_ and // returns OK if recovering from the error. Otherwise, the same error code // is returned. @@ -249,8 +254,6 @@ class HttpNetworkTransaction : public HttpTransaction, DISALLOW_COPY_AND_ASSIGN(HttpNetworkTransaction); }; -int ConvertRequestPriorityToSpdyPriority(RequestPriority priority); - } // namespace net #endif // NET_HTTP_HTTP_NETWORK_TRANSACTION_H_ diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 260de13..8c87136 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -20,6 +20,7 @@ #include "net/base/net_log.h" #include "net/base/net_log_unittest.h" #include "net/base/request_priority.h" +#include "net/base/ssl_cert_request_info.h" #include "net/base/ssl_config_service_defaults.h" #include "net/base/ssl_info.h" #include "net/base/test_completion_callback.h" @@ -101,18 +102,16 @@ struct SessionDependencies { }; HttpNetworkSession* CreateSession(SessionDependencies* session_deps) { - return new HttpNetworkSession(session_deps->host_resolver.get(), - session_deps->cert_verifier.get(), - NULL /* dnsrr_resolver */, - NULL /* dns_cert_checker */, - NULL /* ssl_host_info_factory */, - session_deps->proxy_service, - &session_deps->socket_factory, - session_deps->ssl_config_service, - new SpdySessionPool(NULL), - session_deps->http_auth_handler_factory.get(), - NULL, - session_deps->net_log); + net::HttpNetworkSession::Params params; + params.client_socket_factory = &session_deps->socket_factory; + params.host_resolver = session_deps->host_resolver.get(); + params.cert_verifier = session_deps->cert_verifier.get(); + params.proxy_service = session_deps->proxy_service; + params.ssl_config_service = session_deps->ssl_config_service; + params.http_auth_handler_factory = + session_deps->http_auth_handler_factory.get(); + params.net_log = session_deps->net_log; + return new HttpNetworkSession(params); } class HttpNetworkTransactionTest : public PlatformTest { @@ -147,15 +146,15 @@ class HttpNetworkTransactionTest : public PlatformTest { size_t reads_count) { SimpleGetHelperResult out; - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + StaticSocketDataProvider data(data_reads, reads_count, NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data); @@ -253,7 +252,8 @@ std::string MockGetHostName() { template<typename ParentPool> class CaptureGroupNameSocketPool : public ParentPool { public: - explicit CaptureGroupNameSocketPool(HttpNetworkSession* session); + CaptureGroupNameSocketPool(HostResolver* host_resolver, + CertVerifier* cert_verifier); const std::string last_group_name_received() const { return last_group_name_; @@ -271,7 +271,8 @@ class CaptureGroupNameSocketPool : public ParentPool { virtual void CancelRequest(const std::string& group_name, ClientSocketHandle* handle) {} virtual void ReleaseSocket(const std::string& group_name, - ClientSocket* socket) {} + ClientSocket* socket, + int id) {} virtual void CloseIdleSockets() {} virtual int IdleSocketCount() const { return 0; @@ -302,20 +303,21 @@ CaptureGroupNameSSLSocketPool; template<typename ParentPool> CaptureGroupNameSocketPool<ParentPool>::CaptureGroupNameSocketPool( - HttpNetworkSession* session) - : ParentPool(0, 0, NULL, session->host_resolver(), NULL, NULL) {} + HostResolver* host_resolver, + CertVerifier* /* cert_verifier */) + : ParentPool(0, 0, NULL, host_resolver, NULL, NULL) {} template<> CaptureGroupNameHttpProxySocketPool::CaptureGroupNameSocketPool( - HttpNetworkSession* session) - : HttpProxyClientSocketPool(0, 0, NULL, session->host_resolver(), NULL, - NULL, NULL) {} + HostResolver* host_resolver, + CertVerifier* /* cert_verifier */) + : HttpProxyClientSocketPool(0, 0, NULL, host_resolver, NULL, NULL, NULL) {} template<> CaptureGroupNameSSLSocketPool::CaptureGroupNameSocketPool( - HttpNetworkSession* session) - : SSLClientSocketPool(0, 0, NULL, session->host_resolver(), - session->cert_verifier(), NULL, NULL, + HostResolver* host_resolver, + CertVerifier* cert_verifier) + : SSLClientSocketPool(0, 0, NULL, host_resolver, cert_verifier, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) {} //----------------------------------------------------------------------------- @@ -563,15 +565,15 @@ TEST_F(HttpNetworkTransactionTest, // Do a request using the HEAD method. Verify that we don't try to read the // message body (since HEAD has none). TEST_F(HttpNetworkTransactionTest, Head) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "HEAD"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes1[] = { MockWrite("HEAD / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -641,13 +643,13 @@ TEST_F(HttpNetworkTransactionTest, ReuseConnection) { }; for (int i = 0; i < 2; ++i) { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + TestCompletionCallback callback; int rv = trans->Start(&request, &callback, BoundNetLog()); @@ -670,10 +672,6 @@ TEST_F(HttpNetworkTransactionTest, ReuseConnection) { } TEST_F(HttpNetworkTransactionTest, Ignores100) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.foo.com/"); @@ -681,6 +679,10 @@ TEST_F(HttpNetworkTransactionTest, Ignores100) { request.upload_data->AppendBytes("foo", 3); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockRead data_reads[] = { MockRead("HTTP/1.0 100 Continue\r\n\r\n"), MockRead("HTTP/1.0 200 OK\r\n\r\n"), @@ -714,15 +716,15 @@ TEST_F(HttpNetworkTransactionTest, Ignores100) { // a 102 instead of a 100. Also, instead of HTTP/1.0 the response is // HTTP/1.1 and the two status headers are read in one read. TEST_F(HttpNetworkTransactionTest, Ignores1xx) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.foo.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockRead data_reads[] = { MockRead("HTTP/1.1 102 Unspecified status code\r\n\r\n" "HTTP/1.1 200 OK\r\n\r\n"), @@ -753,15 +755,15 @@ TEST_F(HttpNetworkTransactionTest, Ignores1xx) { } TEST_F(HttpNetworkTransactionTest, Incomplete100ThenEOF) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.foo.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockRead data_reads[] = { MockRead(false, "HTTP/1.0 100 Continue\r\n"), MockRead(true, 0), @@ -784,15 +786,15 @@ TEST_F(HttpNetworkTransactionTest, Incomplete100ThenEOF) { } TEST_F(HttpNetworkTransactionTest, EmptyResponse) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.foo.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockRead data_reads[] = { MockRead(true, 0), }; @@ -812,14 +814,14 @@ TEST_F(HttpNetworkTransactionTest, EmptyResponse) { // transaction to resend the request. void HttpNetworkTransactionTest::KeepAliveConnectionResendRequestTest( const MockRead& read_failure) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.foo.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + MockRead data1_reads[] = { MockRead("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"), MockRead("hello"), @@ -875,15 +877,15 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveConnectionEOF) { } TEST_F(HttpNetworkTransactionTest, NonKeepAliveConnectionReset) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockRead data_reads[] = { MockRead(true, ERR_CONNECTION_RESET), MockRead("HTTP/1.0 200 OK\r\n\r\n"), // Should not be used @@ -929,14 +931,14 @@ TEST_F(HttpNetworkTransactionTest, NonKeepAliveConnectionEOF) { // Test that we correctly reuse a keep-alive connection after not explicitly // reading the body. TEST_F(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.foo.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + // Note that because all these reads happen in the same // StaticSocketDataProvider, it shows that the same socket is being reused for // all transactions. @@ -1024,15 +1026,15 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) { // Test the request-challenge-retry sequence for basic auth. // (basic auth is the easiest to mock, because it has no randomness). TEST_F(HttpNetworkTransactionTest, BasicAuth) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -1109,15 +1111,15 @@ TEST_F(HttpNetworkTransactionTest, BasicAuth) { } TEST_F(HttpNetworkTransactionTest, DoNotSendAuth) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -1152,14 +1154,14 @@ TEST_F(HttpNetworkTransactionTest, DoNotSendAuth) { // Test the request-challenge-retry sequence for basic auth, over a keep-alive // connection. TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAlive) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -1227,14 +1229,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAlive) { // Test the request-challenge-retry sequence for basic auth, over a keep-alive // connection and with no response body to drain. TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveNoBody) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -1300,14 +1302,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveNoBody) { // Test the request-challenge-retry sequence for basic auth, over a keep-alive // connection and with a large response body to drain. TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveLargeBody) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -1381,14 +1383,14 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveLargeBody) { // Test the request-challenge-retry sequence for basic auth, over a keep-alive // connection, but the server gets impatient and closes the connection. TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -1472,18 +1474,18 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthKeepAliveImpatientServer) { // Test the request-challenge-retry sequence for basic auth, over a connection // that requires a restart when setting up an SSL tunnel. TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) { - // Configure against proxy server "myproxy:70". - SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); - CapturingBoundNetLog log(CapturingNetLog::kUnbounded); - session_deps.net_log = log.bound().net_log(); - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("https://www.google.com/"); // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; + // Configure against proxy server "myproxy:70". + SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" @@ -1582,6 +1584,13 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyNoKeepAlive) { // Test the request-challenge-retry sequence for basic auth, over a keep-alive // proxy connection, when setting up an SSL tunnel. TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + // Ensure that proxy authentication is attempted even + // when the no authentication data flag is set. + request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; + // Configure against proxy server "myproxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); @@ -1590,13 +1599,6 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) { scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - // Ensure that proxy authentication is attempted even - // when the no authentication data flag is set. - request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; - // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" @@ -1696,6 +1698,11 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyKeepAlive) { // Test that we don't read the response body when we fail to establish a tunnel, // even if the user cancels the proxy's auth attempt. TEST_F(HttpNetworkTransactionTest, BasicAuthProxyCancelTunnel) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + request.load_flags = 0; + // Configure against proxy server "myproxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); @@ -1703,11 +1710,6 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyCancelTunnel) { scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - // Since we have proxy, should try to establish tunnel. MockWrite data_writes[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" @@ -1754,16 +1756,16 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyCancelTunnel) { // Test when a server (non-proxy) returns a 407 (proxy-authenticate). // The request should fail with ERR_UNEXPECTED_PROXY_AUTH. TEST_F(HttpNetworkTransactionTest, UnexpectedProxyAuth) { - // We are using a DIRECT connection (i.e. no proxy) for this session. - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + // We are using a DIRECT connection (i.e. no proxy) for this session. + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -1799,15 +1801,15 @@ TEST_F(HttpNetworkTransactionTest, UnexpectedProxyAuth) { // response came from the proxy or the server, so it is treated as if the proxy // issued the challenge. TEST_F(HttpNetworkTransactionTest, HttpsServerRequestsProxyAuthThroughProxy) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); session_deps.net_log = log.bound().net_log(); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" @@ -1856,16 +1858,16 @@ TEST_F(HttpNetworkTransactionTest, HttpsServerRequestsProxyAuthThroughProxy) { // Test a simple get through an HTTPS Proxy. TEST_F(HttpNetworkTransactionTest, HttpsProxyGet) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + // Configure against https proxy server "proxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("https://proxy:70")); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); session_deps.net_log = log.bound().net_log(); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - // Since we have proxy, should use full url MockWrite data_writes1[] = { MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" @@ -1910,17 +1912,17 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxyGet) { // Test a SPDY get through an HTTPS Proxy. TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGet) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + // Configure against https proxy server "proxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("https://proxy:70")); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); session_deps.net_log = log.bound().net_log(); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - request.load_flags = 0; - // fetch http://www.google.com/ via SPDY scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0, false, 1, LOWEST, false)); @@ -1969,6 +1971,11 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGet) { // Test a SPDY get through an HTTPS Proxy. TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + // Configure against https proxy server "proxy:70". SessionDependencies session_deps( ProxyService::CreateFixed("https://proxy:70")); @@ -1976,11 +1983,6 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) { session_deps.net_log = log.bound().net_log(); scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - request.load_flags = 0; - // The first request will be a bare GET, the second request will be a // GET with a Proxy-Authorization header. scoped_ptr<spdy::SpdyFrame> req_get( @@ -2076,6 +2078,11 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) { // Test a SPDY CONNECT through an HTTPS Proxy to an HTTPS (non-SPDY) Server. TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectHttps) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + request.load_flags = 0; + // Configure against https proxy server "proxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("https://proxy:70")); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); @@ -2084,11 +2091,6 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectHttps) { scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - // CONNECT to www.google.com:443 via SPDY scoped_ptr<spdy::SpdyFrame> connect(ConstructSpdyConnect(NULL, 0, 1)); // fetch https://www.google.com/ via HTTP @@ -2154,6 +2156,11 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectHttps) { // Test a SPDY CONNECT through an HTTPS Proxy to a SPDY server. TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectSpdy) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + request.load_flags = 0; + // Configure against https proxy server "proxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("https://proxy:70")); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); @@ -2162,11 +2169,6 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectSpdy) { scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - // CONNECT to www.google.com:443 via SPDY scoped_ptr<spdy::SpdyFrame> connect(ConstructSpdyConnect(NULL, 0, 1)); // fetch https://www.google.com/ via SPDY @@ -2228,6 +2230,11 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectSpdy) { // Test a SPDY CONNECT failure through an HTTPS Proxy. TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectFailure) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + request.load_flags = 0; + // Configure against https proxy server "proxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("https://proxy:70")); CapturingBoundNetLog log(CapturingNetLog::kUnbounded); @@ -2236,11 +2243,6 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectFailure) { scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - // CONNECT to www.google.com:443 via SPDY scoped_ptr<spdy::SpdyFrame> connect(ConstructSpdyConnect(NULL, 0, 1)); scoped_ptr<spdy::SpdyFrame> get(ConstructSpdyRstStream(1, spdy::CANCEL)); @@ -2289,18 +2291,18 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyConnectFailure) { // Test the challenge-response-retry sequence through an HTTPS Proxy TEST_F(HttpNetworkTransactionTest, HttpsProxyAuthRetry) { - // Configure against https proxy server "proxy:70". - SessionDependencies session_deps(ProxyService::CreateFixed("https://proxy:70")); - CapturingBoundNetLog log(CapturingNetLog::kUnbounded); - session_deps.net_log = log.bound().net_log(); - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); // when the no authentication data flag is set. request.load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA; + // Configure against https proxy server "proxy:70". + SessionDependencies session_deps(ProxyService::CreateFixed("https://proxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + // Since we have proxy, should use full url MockWrite data_writes1[] = { MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" @@ -2381,16 +2383,16 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxyAuthRetry) { void HttpNetworkTransactionTest::ConnectStatusHelperWithExpectedStatus( const MockRead& status, int expected_status) { - // Configure against proxy server "myproxy:70". - SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); - - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("https://www.google.com/"); request.load_flags = 0; + // Configure against proxy server "myproxy:70". + SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + // Since we have proxy, should try to establish tunnel. MockWrite data_writes[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" @@ -2593,17 +2595,17 @@ TEST_F(HttpNetworkTransactionTest, ConnectStatus505) { // authentication. Again, this uses basic auth for both since that is // the simplest to mock. TEST_F(HttpNetworkTransactionTest, BasicAuthProxyThenServer) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); // Configure against proxy server "myproxy:70". scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction( CreateSession(&session_deps))); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - request.load_flags = 0; - MockWrite data_writes1[] = { MockWrite("GET http://www.google.com/ HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -2730,16 +2732,16 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthProxyThenServer) { // Enter the correct password and authenticate successfully. TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { - HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom1, - MockGetHostName); - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://172.22.68.17/kids/login.aspx"); request.load_flags = 0; + HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom1, + MockGetHostName); + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + MockWrite data_writes1[] = { MockWrite("GET /kids/login.aspx HTTP/1.1\r\n" "Host: 172.22.68.17\r\n" @@ -2822,28 +2824,37 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); - EXPECT_TRUE(trans->IsReadyToRestartForAuth()); - TestCompletionCallback callback2; - rv = trans->RestartWithAuth(string16(), string16(), &callback2); - EXPECT_EQ(ERR_IO_PENDING, rv); - rv = callback2.WaitForResult(); - EXPECT_EQ(OK, rv); EXPECT_FALSE(trans->IsReadyToRestartForAuth()); const HttpResponseInfo* response = trans->GetResponseInfo(); - ASSERT_FALSE(response == NULL); + ASSERT_TRUE(response != NULL); // The password prompt info should have been set in // response->auth_challenge. - EXPECT_FALSE(response->auth_challenge.get() == NULL); + ASSERT_FALSE(response->auth_challenge.get() == NULL); EXPECT_EQ(L"172.22.68.17:80", response->auth_challenge->host_and_port); EXPECT_EQ(L"", response->auth_challenge->realm); EXPECT_EQ(L"ntlm", response->auth_challenge->scheme); + TestCompletionCallback callback2; + + rv = trans->RestartWithAuth(kTestingNTLM, kTestingNTLM, &callback2); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback2.WaitForResult(); + EXPECT_EQ(OK, rv); + + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + + response = trans->GetResponseInfo(); + ASSERT_TRUE(response != NULL); + + EXPECT_TRUE(response->auth_challenge.get() == NULL); + TestCompletionCallback callback3; - rv = trans->RestartWithAuth(kTestingNTLM, kTestingNTLM, &callback3); + rv = trans->RestartWithAuth(string16(), string16(), &callback3); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback3.WaitForResult(); @@ -2851,23 +2862,22 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth1) { response = trans->GetResponseInfo(); ASSERT_FALSE(response == NULL); - EXPECT_TRUE(response->auth_challenge.get() == NULL); EXPECT_EQ(13, response->headers->GetContentLength()); } // Enter a wrong password, and then the correct one. TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { - HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom2, - MockGetHostName); - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://172.22.68.17/kids/login.aspx"); request.load_flags = 0; + HttpAuthHandlerNTLM::ScopedProcSetter proc_setter(MockGenerateRandom2, + MockGetHostName); + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + MockWrite data_writes1[] = { MockWrite("GET /kids/login.aspx HTTP/1.1\r\n" "Host: 172.22.68.17\r\n" @@ -3001,12 +3011,6 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { rv = callback1.WaitForResult(); EXPECT_EQ(OK, rv); - EXPECT_TRUE(trans->IsReadyToRestartForAuth()); - TestCompletionCallback callback2; - rv = trans->RestartWithAuth(string16(), string16(), &callback2); - EXPECT_EQ(ERR_IO_PENDING, rv); - rv = callback2.WaitForResult(); - EXPECT_EQ(OK, rv); EXPECT_FALSE(trans->IsReadyToRestartForAuth()); const HttpResponseInfo* response = trans->GetResponseInfo(); @@ -3019,25 +3023,25 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { EXPECT_EQ(L"", response->auth_challenge->realm); EXPECT_EQ(L"ntlm", response->auth_challenge->scheme); - TestCompletionCallback callback3; + TestCompletionCallback callback2; // Enter the wrong password. - rv = trans->RestartWithAuth(kTestingNTLM, kWrongPassword, &callback3); + rv = trans->RestartWithAuth(kTestingNTLM, kWrongPassword, &callback2); EXPECT_EQ(ERR_IO_PENDING, rv); - rv = callback3.WaitForResult(); + rv = callback2.WaitForResult(); EXPECT_EQ(OK, rv); EXPECT_TRUE(trans->IsReadyToRestartForAuth()); - TestCompletionCallback callback4; - rv = trans->RestartWithAuth(string16(), string16(), &callback4); + TestCompletionCallback callback3; + rv = trans->RestartWithAuth(string16(), string16(), &callback3); EXPECT_EQ(ERR_IO_PENDING, rv); - rv = callback4.WaitForResult(); + rv = callback3.WaitForResult(); EXPECT_EQ(OK, rv); EXPECT_FALSE(trans->IsReadyToRestartForAuth()); response = trans->GetResponseInfo(); - EXPECT_FALSE(response == NULL); + ASSERT_TRUE(response != NULL); // The password prompt info should have been set in response->auth_challenge. EXPECT_FALSE(response->auth_challenge.get() == NULL); @@ -3046,10 +3050,21 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { EXPECT_EQ(L"", response->auth_challenge->realm); EXPECT_EQ(L"ntlm", response->auth_challenge->scheme); - TestCompletionCallback callback5; + TestCompletionCallback callback4; // Now enter the right password. - rv = trans->RestartWithAuth(kTestingNTLM, kTestingNTLM, &callback5); + rv = trans->RestartWithAuth(kTestingNTLM, kTestingNTLM, &callback4); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback4.WaitForResult(); + EXPECT_EQ(OK, rv); + + EXPECT_TRUE(trans->IsReadyToRestartForAuth()); + + TestCompletionCallback callback5; + + // One more roundtrip + rv = trans->RestartWithAuth(string16(), string16(), &callback5); EXPECT_EQ(ERR_IO_PENDING, rv); rv = callback5.WaitForResult(); @@ -3065,15 +3080,15 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuth2) { // After some maximum number of bytes is consumed, the transaction should // fail with ERR_RESPONSE_HEADERS_TOO_BIG. TEST_F(HttpNetworkTransactionTest, LargeHeadersNoBody) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + // Respond with 300 kb of headers (we should fail after 256 kb). std::string large_headers_string; FillLargeHeadersString(&large_headers_string, 300 * 1024); @@ -3103,6 +3118,11 @@ TEST_F(HttpNetworkTransactionTest, LargeHeadersNoBody) { // establish tunnel. // http://code.google.com/p/chromium/issues/detail?id=3772 TEST_F(HttpNetworkTransactionTest, DontRecycleTCPSocketForSSLTunnel) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + request.load_flags = 0; + // Configure against proxy server "myproxy:70". SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); @@ -3110,11 +3130,6 @@ TEST_F(HttpNetworkTransactionTest, DontRecycleTCPSocketForSSLTunnel) { scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - // Since we have proxy, should try to establish tunnel. MockWrite data_writes1[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" @@ -3161,16 +3176,16 @@ TEST_F(HttpNetworkTransactionTest, DontRecycleTCPSocketForSSLTunnel) { // Make sure that we recycle a socket after reading all of the response body. TEST_F(HttpNetworkTransactionTest, RecycleSocket) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockRead data_reads[] = { // A part of the response body is received with the response headers. MockRead("HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nhel"), @@ -3371,11 +3386,6 @@ TEST_F(HttpNetworkTransactionTest, RecycleDeadSSLSocket) { // Make sure that we recycle a socket after a zero-length response. // http://crbug.com/9880 TEST_F(HttpNetworkTransactionTest, RecycleSocketAfterZeroContentLength) { - SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/csi?v=3&s=web&action=&" @@ -3384,6 +3394,11 @@ TEST_F(HttpNetworkTransactionTest, RecycleSocketAfterZeroContentLength) { "rt=prt.2642,ol.2649,xjs.2951"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockRead data_reads[] = { MockRead("HTTP/1.1 204 No Content\r\n" "Content-Length: 0\r\n" @@ -3516,15 +3531,15 @@ TEST_F(HttpNetworkTransactionTest, ResendRequestOnWriteBodyError) { // an identity in the URL. The request should be sent as normal, but when // it fails the identity from the URL is used to answer the challenge. TEST_F(HttpNetworkTransactionTest, AuthIdentityInURL) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; // Note: the URL has a username:password in it. request.url = GURL("http://foo:b@r@www.google.com/"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + // The password contains an escaped character -- for this test to pass it // will need to be unescaped by HttpNetworkTransaction. EXPECT_EQ("b%40r", request.url.password()); @@ -3598,10 +3613,6 @@ TEST_F(HttpNetworkTransactionTest, AuthIdentityInURL) { // an incorrect identity in the URL. The identity from the URL should be used // only once. TEST_F(HttpNetworkTransactionTest, WrongAuthIdentityInURL) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; // Note: the URL has a username:password in it. The password "baz" is @@ -3610,6 +3621,10 @@ TEST_F(HttpNetworkTransactionTest, WrongAuthIdentityInURL) { request.load_flags = LOAD_NORMAL; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes1[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -3715,13 +3730,13 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { // Transaction 1: authenticate (foo, bar) on MyRealm1 { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/x/y/z"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockWrite data_writes1[] = { MockWrite("GET /x/y/z HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -3794,8 +3809,6 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { // Transaction 2: authenticate (foo2, bar2) on MyRealm2 { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; // Note that Transaction 1 was at /x/y/z, so this is in the same @@ -3803,6 +3816,8 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { request.url = GURL("http://www.google.com/x/y/a/b"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockWrite data_writes1[] = { MockWrite("GET /x/y/a/b HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -3880,13 +3895,13 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { // Transaction 3: Resend a request in MyRealm's protection space -- // succeed with preemptive authorization. { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/x/y/z2"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockWrite data_writes1[] = { MockWrite("GET /x/y/z2 HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -3927,13 +3942,13 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { // Transaction 4: request another URL in MyRealm (however the // url is not known to belong to the protection space, so no pre-auth). { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/x/1"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockWrite data_writes1[] = { MockWrite("GET /x/1 HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -3996,13 +4011,13 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { // Transaction 5: request a URL in MyRealm, but the server rejects the // cached identity. Should invalidate and re-prompt. { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/p/q/t"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockWrite data_writes1[] = { MockWrite("GET /p/q/t HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4114,13 +4129,13 @@ TEST_F(HttpNetworkTransactionTest, DigestPreAuthNonceCount) { // Transaction 1: authenticate (foo, bar) on MyRealm1 { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/x/y/z"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockWrite data_writes1[] = { MockWrite("GET /x/y/z HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4196,8 +4211,6 @@ TEST_F(HttpNetworkTransactionTest, DigestPreAuthNonceCount) { // This will preemptively add an Authorization header which should have an // "nc" value of 2 (as compared to 1 in the first use. { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; // Note that Transaction 1 was at /x/y/z, so this is in the same @@ -4205,6 +4218,8 @@ TEST_F(HttpNetworkTransactionTest, DigestPreAuthNonceCount) { request.url = GURL("http://www.google.com/x/y/a/b"); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + MockWrite data_writes1[] = { MockWrite("GET /x/y/a/b HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4285,15 +4300,15 @@ TEST_F(HttpNetworkTransactionTest, ResetStateForRestart) { // Test HTTPS connections to a site with a bad certificate TEST_F(HttpNetworkTransactionTest, HTTPSBadCertificate) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("https://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4786,16 +4801,16 @@ TEST_F(HttpNetworkTransactionTest, HTTPSBadCertificateViaHttpsProxy) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_UserAgent) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Chromium Ultra Awesome X Edition"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4825,16 +4840,16 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_UserAgent) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_UserAgentOverTunnel) { - SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("https://www.google.com/"); request.extra_headers.SetHeader(HttpRequestHeaders::kUserAgent, "Chromium Ultra Awesome X Edition"); + SessionDependencies session_deps(ProxyService::CreateFixed("myproxy:70")); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4863,16 +4878,16 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_UserAgentOverTunnel) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_Referer) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; request.referrer = GURL("http://the.previous.site.com/"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4902,14 +4917,14 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_Referer) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_PostContentLengthZero) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.google.com/"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("POST / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4939,14 +4954,14 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_PostContentLengthZero) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_PutContentLengthZero) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "PUT"; request.url = GURL("http://www.google.com/"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("PUT / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -4976,14 +4991,14 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_PutContentLengthZero) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_HeadContentLengthZero) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "HEAD"; request.url = GURL("http://www.google.com/"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("HEAD / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -5013,15 +5028,15 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_HeadContentLengthZero) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_CacheControlNoCache) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = LOAD_BYPASS_CACHE; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -5053,15 +5068,15 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_CacheControlNoCache) { TEST_F(HttpNetworkTransactionTest, BuildRequest_CacheControlValidateCache) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = LOAD_VALIDATE_CACHE; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -5091,15 +5106,15 @@ TEST_F(HttpNetworkTransactionTest, } TEST_F(HttpNetworkTransactionTest, BuildRequest_ExtraHeaders) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.extra_headers.SetHeader("FooHeader", "Bar"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -5129,10 +5144,6 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_ExtraHeaders) { } TEST_F(HttpNetworkTransactionTest, BuildRequest_ExtraHeadersStripped) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); @@ -5140,6 +5151,10 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_ExtraHeadersStripped) { request.extra_headers.SetHeader("hEllo", "Kitty"); request.extra_headers.SetHeader("FoO", "bar"); + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockWrite data_writes[] = { MockWrite("GET / HTTP/1.1\r\n" "Host: www.google.com\r\n" @@ -5170,17 +5185,17 @@ TEST_F(HttpNetworkTransactionTest, BuildRequest_ExtraHeadersStripped) { } TEST_F(HttpNetworkTransactionTest, SOCKS4_HTTP_GET) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + SessionDependencies session_deps( ProxyService::CreateFixed("socks4://myproxy:1080")); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - request.load_flags = 0; - char write_buffer[] = { 0x04, 0x01, 0x00, 0x50, 127, 0, 0, 1, 0 }; char read_buffer[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; @@ -5221,17 +5236,17 @@ TEST_F(HttpNetworkTransactionTest, SOCKS4_HTTP_GET) { } TEST_F(HttpNetworkTransactionTest, SOCKS4_SSL_GET) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + request.load_flags = 0; + SessionDependencies session_deps( ProxyService::CreateFixed("socks4://myproxy:1080")); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - unsigned char write_buffer[] = { 0x04, 0x01, 0x01, 0xBB, 127, 0, 0, 1, 0 }; unsigned char read_buffer[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; @@ -5277,17 +5292,17 @@ TEST_F(HttpNetworkTransactionTest, SOCKS4_SSL_GET) { } TEST_F(HttpNetworkTransactionTest, SOCKS5_HTTP_GET) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + request.load_flags = 0; + SessionDependencies session_deps( ProxyService::CreateFixed("socks5://myproxy:1080")); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - request.load_flags = 0; - const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 }; const char kSOCKS5GreetResponse[] = { 0x05, 0x00 }; const char kSOCKS5OkRequest[] = { @@ -5342,17 +5357,17 @@ TEST_F(HttpNetworkTransactionTest, SOCKS5_HTTP_GET) { } TEST_F(HttpNetworkTransactionTest, SOCKS5_SSL_GET) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("https://www.google.com/"); + request.load_flags = 0; + SessionDependencies session_deps( ProxyService::CreateFixed("socks5://myproxy:1080")); scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("https://www.google.com/"); - request.load_flags = 0; - const char kSOCKS5GreetRequest[] = { 0x05, 0x01, 0x00 }; const char kSOCKS5GreetResponse[] = { 0x05, 0x00 }; const unsigned char kSOCKS5OkRequest[] = { @@ -5437,13 +5452,13 @@ scoped_refptr<HttpNetworkSession> SetupSessionForGroupNameTests( int GroupNameTransactionHelper( const std::string& url, const scoped_refptr<HttpNetworkSession>& session) { - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - HttpRequestInfo request; request.method = "GET"; request.url = GURL(url); request.load_flags = 0; + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + TestCompletionCallback callback; // We do not complete this request, the dtor will clean the transaction up. @@ -5494,10 +5509,10 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForDirectConnections) { HttpNetworkSessionPeer peer(session); CaptureGroupNameTCPSocketPool* tcp_conn_pool = - new CaptureGroupNameTCPSocketPool(session); + new CaptureGroupNameTCPSocketPool(NULL, NULL); peer.SetTCPSocketPool(tcp_conn_pool); CaptureGroupNameSSLSocketPool* ssl_conn_pool = - new CaptureGroupNameSSLSocketPool(session.get()); + new CaptureGroupNameSSLSocketPool(NULL, NULL); peer.SetSSLSocketPool(ssl_conn_pool); EXPECT_EQ(ERR_IO_PENDING, @@ -5548,10 +5563,10 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForHTTPProxyConnections) { HostPortPair proxy_host("http_proxy", 80); CaptureGroupNameHttpProxySocketPool* http_proxy_pool = - new CaptureGroupNameHttpProxySocketPool(session); + new CaptureGroupNameHttpProxySocketPool(NULL, NULL); peer.SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool); CaptureGroupNameSSLSocketPool* ssl_conn_pool = - new CaptureGroupNameSSLSocketPool(session); + new CaptureGroupNameSSLSocketPool(NULL, NULL); peer.SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool); EXPECT_EQ(ERR_IO_PENDING, @@ -5613,10 +5628,10 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) { HostPortPair proxy_host("socks_proxy", 1080); CaptureGroupNameSOCKSSocketPool* socks_conn_pool = - new CaptureGroupNameSOCKSSocketPool(session); + new CaptureGroupNameSOCKSSocketPool(NULL, NULL); peer.SetSocketPoolForSOCKSProxy(proxy_host, socks_conn_pool); CaptureGroupNameSSLSocketPool* ssl_conn_pool = - new CaptureGroupNameSSLSocketPool(session); + new CaptureGroupNameSSLSocketPool(NULL, NULL); peer.SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool); scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); @@ -5635,6 +5650,10 @@ TEST_F(HttpNetworkTransactionTest, GroupNameForSOCKSConnections) { } TEST_F(HttpNetworkTransactionTest, ReconsiderProxyAfterFailedConnection) { + HttpRequestInfo request; + request.method = "GET"; + request.url = GURL("http://www.google.com/"); + SessionDependencies session_deps( ProxyService::CreateFixed("myproxy:70;foobar:80")); @@ -5645,10 +5664,6 @@ TEST_F(HttpNetworkTransactionTest, ReconsiderProxyAfterFailedConnection) { scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; - request.method = "GET"; - request.url = GURL("http://www.google.com/"); - TestCompletionCallback callback; int rv = trans->Start(&request, &callback, BoundNetLog()); @@ -5705,6 +5720,12 @@ TEST_F(HttpNetworkTransactionTest, ResolveMadeWithReferrer) { EXPECT_TRUE(referrer.is_valid()); ResolutionReferrerObserver resolution_observer(referrer); + // Issue a request, containing an HTTP referrer. + HttpRequestInfo request; + request.method = "GET"; + request.referrer = referrer; + request.url = GURL("http://www.google.com/"); + SessionDependencies session_deps; scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction( CreateSession(&session_deps))); @@ -5719,12 +5740,6 @@ TEST_F(HttpNetworkTransactionTest, ResolveMadeWithReferrer) { StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data); - // Issue a request, containing an HTTP referrer. - HttpRequestInfo request; - request.method = "GET"; - request.referrer = referrer; - request.url = GURL("http://www.google.com/"); - // Run the request until it fails reading from the socket. TestCompletionCallback callback; int rv = trans->Start(&request, &callback, BoundNetLog()); @@ -5739,6 +5754,12 @@ TEST_F(HttpNetworkTransactionTest, ResolveMadeWithReferrer) { // Base test to make sure that when the load flags for a request specify to // bypass the cache, the DNS cache is not used. void BypassHostCacheOnRefreshHelper(int load_flags) { + // Issue a request, asking to bypass the cache(s). + HttpRequestInfo request; + request.method = "GET"; + request.load_flags = load_flags; + request.url = GURL("http://www.google.com/"); + SessionDependencies session_deps; // Select a host resolver that does caching. @@ -5774,12 +5795,6 @@ void BypassHostCacheOnRefreshHelper(int load_flags) { StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); session_deps.socket_factory.AddSocketDataProvider(&data); - // Issue a request, asking to bypass the cache(s). - HttpRequestInfo request; - request.method = "GET"; - request.load_flags = load_flags; - request.url = GURL("http://www.google.com/"); - // Run the request. TestCompletionCallback callback; rv = trans->Start(&request, &callback, BoundNetLog()); @@ -5995,15 +6010,15 @@ TEST_F(HttpNetworkTransactionTest, HTTPSViaProxyWithExtraData) { } TEST_F(HttpNetworkTransactionTest, LargeContentLengthThenClose) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + MockRead data_reads[] = { MockRead("HTTP/1.0 200 OK\r\nContent-Length:6719476739\r\n\r\n"), MockRead(false, OK), @@ -6031,16 +6046,16 @@ TEST_F(HttpNetworkTransactionTest, LargeContentLengthThenClose) { } TEST_F(HttpNetworkTransactionTest, UploadFileSmallerThanLength) { - SessionDependencies session_deps; - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "POST"; request.url = GURL("http://www.google.com/upload"); request.upload_data = new UploadData; request.load_flags = 0; + SessionDependencies session_deps; + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + FilePath temp_file_path; ASSERT_TRUE(file_util::CreateTemporaryFile(&temp_file_path)); const uint64 kFakeSize = 100000; // file is actually blank @@ -6084,6 +6099,12 @@ TEST_F(HttpNetworkTransactionTest, UploadFileSmallerThanLength) { } TEST_F(HttpNetworkTransactionTest, UploadUnreadableFile) { + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.google.com/upload"); + request.upload_data = new UploadData; + request.load_flags = 0; + // If we try to upload an unreadable file, the network stack should report // the file size as zero and upload zero bytes for that file. SessionDependencies session_deps; @@ -6097,12 +6118,6 @@ TEST_F(HttpNetworkTransactionTest, UploadUnreadableFile) { temp_file_content.length())); ASSERT_TRUE(file_util::MakeFileUnreadable(temp_file)); - HttpRequestInfo request; - request.method = "POST"; - request.url = GURL("http://www.google.com/upload"); - request.upload_data = new UploadData; - request.load_flags = 0; - std::vector<UploadData::Element> elements; UploadData::Element element; element.SetToFilePath(temp_file); @@ -6141,6 +6156,12 @@ TEST_F(HttpNetworkTransactionTest, UploadUnreadableFile) { } TEST_F(HttpNetworkTransactionTest, UnreadableUploadFileAfterAuthRestart) { + HttpRequestInfo request; + request.method = "POST"; + request.url = GURL("http://www.google.com/upload"); + request.upload_data = new UploadData; + request.load_flags = 0; + SessionDependencies session_deps; scoped_ptr<HttpTransaction> trans( new HttpNetworkTransaction(CreateSession(&session_deps))); @@ -6152,12 +6173,6 @@ TEST_F(HttpNetworkTransactionTest, UnreadableUploadFileAfterAuthRestart) { ASSERT_TRUE(file_util::WriteFile(temp_file, temp_file_contents.c_str(), temp_file_contents.length())); - HttpRequestInfo request; - request.method = "POST"; - request.url = GURL("http://www.google.com/upload"); - request.upload_data = new UploadData; - request.load_flags = 0; - std::vector<UploadData::Element> elements; UploadData::Element element; element.SetToFilePath(temp_file); @@ -6919,15 +6934,14 @@ TEST_F(HttpNetworkTransactionTest, SSLConfig ssl_config; session->ssl_config_service()->GetSSLConfig(&ssl_config); - ClientSocket* socket = connection->release_socket(); - socket = session->socket_factory()->CreateSSLClientSocket( - socket, HostPortPair("" , 443), ssl_config, NULL /* ssl_host_info */, - session->cert_verifier()); - connection->set_socket(socket); - EXPECT_EQ(ERR_IO_PENDING, socket->Connect(&callback)); + scoped_ptr<ClientSocketHandle> ssl_connection(new ClientSocketHandle); + ssl_connection->set_socket(session_deps.socket_factory.CreateSSLClientSocket( + connection.release(), HostPortPair("" , 443), ssl_config, + NULL /* ssl_host_info */, session_deps.cert_verifier.get(), NULL)); + EXPECT_EQ(ERR_IO_PENDING, ssl_connection->socket()->Connect(&callback)); EXPECT_EQ(OK, callback.WaitForResult()); - EXPECT_EQ(OK, spdy_session->InitializeWithSocket(connection.release(), + EXPECT_EQ(OK, spdy_session->InitializeWithSocket(ssl_connection.release(), true, OK)); trans.reset(new HttpNetworkTransaction(session)); @@ -7383,15 +7397,30 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { origin, BoundNetLog()); auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_SERVER); - scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); - scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); - int rv = OK; const HttpResponseInfo* response = NULL; HttpRequestInfo request; request.method = "GET"; request.url = origin; request.load_flags = 0; + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + + // Use a TCP Socket Pool with only one connection per group. This is used + // to validate that the TCP socket is not released to the pool between + // each round of multi-round authentication. + HttpNetworkSessionPeer session_peer(session); + ClientSocketPoolHistograms tcp_pool_histograms("SmallTCP"); + TCPClientSocketPool* tcp_pool = new TCPClientSocketPool( + 50, // Max sockets for pool + 1, // Max sockets per group + &tcp_pool_histograms, + session_deps.host_resolver.get(), + &session_deps.socket_factory, + session_deps.net_log); + session_peer.SetTCPSocketPool(tcp_pool); + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); TestCompletionCallback callback; const MockWrite kGet( @@ -7424,7 +7453,9 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { // Third round kGetAuth, // Fourth round - kGetAuth + kGetAuth, + // Competing request + kGet, }; MockRead reads[] = { // First round @@ -7435,12 +7466,16 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { kServerChallenge, // Fourth round kSuccess, + // Competing response + kSuccess, }; StaticSocketDataProvider data_provider(reads, arraysize(reads), writes, arraysize(writes)); session_deps.socket_factory.AddSocketDataProvider(&data_provider); - // First round + const char* const kSocketGroup = "www.example.com:80"; + + // First round of authentication. auth_handler->SetGenerateExpectation(false, OK); rv = trans->Start(&request, &callback, BoundNetLog()); if (rv == ERR_IO_PENDING) @@ -7449,8 +7484,21 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { response = trans->GetResponseInfo(); ASSERT_FALSE(response == NULL); EXPECT_FALSE(response->auth_challenge.get() == NULL); + EXPECT_EQ(0, tcp_pool->IdleSocketCountInGroup(kSocketGroup)); + + // In between rounds, another request comes in for the same domain. + // It should not be able to grab the TCP socket that trans has already + // claimed. + scoped_ptr<HttpTransaction> trans_compete( + new HttpNetworkTransaction(session)); + TestCompletionCallback callback_compete; + rv = trans_compete->Start(&request, &callback_compete, BoundNetLog()); + EXPECT_EQ(ERR_IO_PENDING, rv); + // callback_compete.WaitForResult at this point would stall forever, + // since the HttpNetworkTransaction does not release the request back to + // the pool until after authentication completes. - // Second round + // Second round of authentication. auth_handler->SetGenerateExpectation(false, OK); rv = trans->RestartWithAuth(kFoo, kBar, &callback); if (rv == ERR_IO_PENDING) @@ -7459,8 +7507,9 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { response = trans->GetResponseInfo(); ASSERT_FALSE(response == NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); + EXPECT_EQ(0, tcp_pool->IdleSocketCountInGroup(kSocketGroup)); - // Third round + // Third round of authentication. auth_handler->SetGenerateExpectation(false, OK); rv = trans->RestartWithAuth(string16(), string16(), &callback); if (rv == ERR_IO_PENDING) @@ -7469,8 +7518,9 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { response = trans->GetResponseInfo(); ASSERT_FALSE(response == NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); + EXPECT_EQ(0, tcp_pool->IdleSocketCountInGroup(kSocketGroup)); - // Fourth round + // Fourth round of authentication, which completes successfully. auth_handler->SetGenerateExpectation(false, OK); rv = trans->RestartWithAuth(string16(), string16(), &callback); if (rv == ERR_IO_PENDING) @@ -7479,6 +7529,34 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { response = trans->GetResponseInfo(); ASSERT_FALSE(response == NULL); EXPECT_TRUE(response->auth_challenge.get() == NULL); + EXPECT_EQ(0, tcp_pool->IdleSocketCountInGroup(kSocketGroup)); + + // Read the body since the fourth round was successful. This will also + // release the socket back to the pool. + scoped_refptr<IOBufferWithSize> io_buf(new IOBufferWithSize(50)); + rv = trans->Read(io_buf, io_buf->size(), &callback); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_EQ(3, rv); + rv = trans->Read(io_buf, io_buf->size(), &callback); + EXPECT_EQ(0, rv); + // There are still 0 idle sockets, since the trans_compete transaction + // will be handed it immediately after trans releases it to the group. + EXPECT_EQ(0, tcp_pool->IdleSocketCountInGroup(kSocketGroup)); + + // The competing request can now finish. Wait for the headers and then + // read the body. + rv = callback_compete.WaitForResult(); + EXPECT_EQ(OK, rv); + rv = trans_compete->Read(io_buf, io_buf->size(), &callback); + if (rv == ERR_IO_PENDING) + rv = callback.WaitForResult(); + EXPECT_EQ(3, rv); + rv = trans_compete->Read(io_buf, io_buf->size(), &callback); + EXPECT_EQ(0, rv); + + // Finally, the socket is released to the group. + EXPECT_EQ(1, tcp_pool->IdleSocketCountInGroup(kSocketGroup)); } class TLSDecompressionFailureSocketDataProvider : public SocketDataProvider { @@ -7858,16 +7936,16 @@ TEST_F(HttpNetworkTransactionTest, SimpleCancel) { MockRead(false, OK), }; - SessionDependencies session_deps; - session_deps.host_resolver->set_synchronous_mode(true); - scoped_ptr<HttpTransaction> trans( - new HttpNetworkTransaction(CreateSession(&session_deps))); - HttpRequestInfo request; request.method = "GET"; request.url = GURL("http://www.google.com/"); request.load_flags = 0; + SessionDependencies session_deps; + session_deps.host_resolver->set_synchronous_mode(true); + scoped_ptr<HttpTransaction> trans( + new HttpNetworkTransaction(CreateSession(&session_deps))); + StaticSocketDataProvider data(data_reads, arraysize(data_reads), NULL, 0); data.set_connect_data(mock_connect); session_deps.socket_factory.AddSocketDataProvider(&data); @@ -8118,6 +8196,11 @@ TEST_F(HttpNetworkTransactionTest, PreconnectWithExistingSpdySession) { // Given a net error, cause that error to be returned from the first Write() // call and verify that the HttpTransaction fails with that error. static void CheckErrorIsPassedBack(int error, bool async) { + net::HttpRequestInfo request_info; + request_info.url = GURL("https://www.example.com/"); + request_info.method = "GET"; + request_info.load_flags = net::LOAD_NORMAL; + SessionDependencies session_deps; SSLSocketDataProvider ssl_data(async, OK); @@ -8132,11 +8215,6 @@ static void CheckErrorIsPassedBack(int error, bool async) { scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); - net::HttpRequestInfo request_info; - request_info.url = GURL("https://www.example.com/"); - request_info.method = "GET"; - request_info.load_flags = net::LOAD_NORMAL; - TestCompletionCallback callback; int rv = trans->Start(&request_info, &callback, net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -8159,6 +8237,11 @@ TEST_F(HttpNetworkTransactionTest, SSLWriteCertError) { // Test that the transaction is restarted in the event of an NPN misprediction. TEST_F(HttpNetworkTransactionTest, NPNMispredict) { + net::HttpRequestInfo request_info; + request_info.url = GURL("https://www.example.com/"); + request_info.method = "GET"; + request_info.load_flags = net::LOAD_NORMAL; + SessionDependencies session_deps; SSLSocketDataProvider ssl_data1(true /* async */, OK); @@ -8190,11 +8273,6 @@ TEST_F(HttpNetworkTransactionTest, NPNMispredict) { scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); - net::HttpRequestInfo request_info; - request_info.url = GURL("https://www.example.com/"); - request_info.method = "GET"; - request_info.load_flags = net::LOAD_NORMAL; - TestCompletionCallback callback; int rv = trans->Start(&request_info, &callback, net::BoundNetLog()); if (rv == net::ERR_IO_PENDING) @@ -8207,4 +8285,303 @@ TEST_F(HttpNetworkTransactionTest, NPNMispredict) { EXPECT_EQ("hello world", contents); } +// Ensure that a client certificate is removed from the SSL client auth +// cache when: +// 1) No proxy is involved. +// 2) TLS False Start is disabled. +// 3) The initial TLS handshake requests a client certificate. +// 4) The client supplies an invalid/unacceptable certificate. +TEST_F(HttpNetworkTransactionTest, ClientAuthCertCache_Direct_NoFalseStart) { + net::HttpRequestInfo request_info; + request_info.url = GURL("https://www.example.com/"); + request_info.method = "GET"; + request_info.load_flags = net::LOAD_NORMAL; + + SessionDependencies session_deps; + + scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); + cert_request->host_and_port = "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 + // be aborted to allow the caller to provide a certificate. + SSLSocketDataProvider ssl_data1(true /* async */, + net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); + ssl_data1.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1); + net::StaticSocketDataProvider data1(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data1); + + // [ssl_]data2 contains the data for the second SSL handshake. When TLS + // False Start is not being used, the result of the SSL handshake will be + // returned as part of the SSLClientSocket::Connect() call. This test + // matches the result of a server sending a handshake_failure alert, + // rather than a Finished message, because it requires a client + // certificate and none was supplied. + SSLSocketDataProvider ssl_data2(true /* async */, + net::ERR_SSL_PROTOCOL_ERROR); + ssl_data2.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2); + net::StaticSocketDataProvider data2(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data2); + + // [ssl_]data3 contains the data for the third SSL handshake. When a + // connection to a server fails during an SSL handshake, + // HttpNetworkTransaction will attempt to fallback to SSLv3 if the initial + // connection was attempted with TLSv1. This is transparent to the caller + // of the HttpNetworkTransaction. Because this test failure is due to + // requiring a client certificate, this fallback handshake should also + // fail. + SSLSocketDataProvider ssl_data3(true /* async */, + net::ERR_SSL_PROTOCOL_ERROR); + ssl_data3.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); + net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data3); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); + + // Begin the SSL handshake with the peer. This consumes ssl_data1. + TestCompletionCallback callback; + int rv = trans->Start(&request_info, &callback, net::BoundNetLog()); + ASSERT_EQ(net::ERR_IO_PENDING, rv); + + // Complete the SSL handshake, which should abort due to requiring a + // client certificate. + rv = callback.WaitForResult(); + ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); + + // Indicate that no certificate should be supplied. From the perspective + // of SSLClientCertCache, NULL is just as meaningful as a real + // certificate, so this is the same as supply a + // legitimate-but-unacceptable certificate. + rv = trans->RestartWithCertificate(NULL, &callback); + ASSERT_EQ(net::ERR_IO_PENDING, rv); + + // 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_EQ(NULL, client_cert.get()); + + // Restart the handshake. This will consume ssl_data2, which fails, and + // then consume ssl_data3, which should also fail. The result code is + // checked against what ssl_data3 should return. + rv = callback.WaitForResult(); + ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); + + // 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)); +} + +// Ensure that a client certificate is removed from the SSL client auth +// cache when: +// 1) No proxy is involved. +// 2) TLS False Start is enabled. +// 3) The initial TLS handshake requests a client certificate. +// 4) The client supplies an invalid/unacceptable certificate. +TEST_F(HttpNetworkTransactionTest, ClientAuthCertCache_Direct_FalseStart) { + net::HttpRequestInfo request_info; + request_info.url = GURL("https://www.example.com/"); + request_info.method = "GET"; + request_info.load_flags = net::LOAD_NORMAL; + + SessionDependencies session_deps; + + scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); + cert_request->host_and_port = "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. + // This is to allow the caller to call SSLClientSocket::Write(), which can + // enqueue application data to be sent in the same packet as the + // ChangeCipherSpec and Finished messages. + // The actual handshake will be finished when SSLClientSocket::Read() is + // called, which expects to process the peer's ChangeCipherSpec and + // Finished messages. If there was an error negotiating with the peer, + // such as due to the peer requiring a client certificate when none was + // supplied, the alert sent by the peer won't be processed until Read() is + // called. + + // Like the non-False Start case, when a client certificate is requested by + // the peer, the handshake is aborted during the Connect() call. + // [ssl_]data1 represents the initial SSL handshake with the peer. + SSLSocketDataProvider ssl_data1(true /* async */, + net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); + ssl_data1.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1); + net::StaticSocketDataProvider data1(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data1); + + // When a client certificate is supplied, Connect() will not be aborted + // when the peer requests the certificate. Instead, the handshake will + // artificially succeed, allowing the caller to write the HTTP request to + // the socket. The handshake messages are not processed until Read() is + // called, which then detects that the handshake was aborted, due to the + // peer sending a handshake_failure because it requires a client + // certificate. + SSLSocketDataProvider ssl_data2(true /* async */, net::OK); + ssl_data2.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2); + net::MockRead data2_reads[] = { + net::MockRead(true /* async */, net::ERR_SSL_PROTOCOL_ERROR), + }; + net::StaticSocketDataProvider data2( + data2_reads, arraysize(data2_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data2); + + // As described in ClientAuthCertCache_Direct_NoFalseStart, [ssl_]data3 is + // the data for the SSL handshake once the TLSv1 connection falls back to + // SSLv3. It has the same behaviour as [ssl_]data2. + SSLSocketDataProvider ssl_data3(true /* async */, net::OK); + ssl_data3.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); + net::StaticSocketDataProvider data3( + data2_reads, arraysize(data2_reads), NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data3); + + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + scoped_ptr<HttpNetworkTransaction> trans(new HttpNetworkTransaction(session)); + + // Begin the initial SSL handshake. + TestCompletionCallback callback; + int rv = trans->Start(&request_info, &callback, net::BoundNetLog()); + ASSERT_EQ(net::ERR_IO_PENDING, rv); + + // Complete the SSL handshake, which should abort due to requiring a + // client certificate. + rv = callback.WaitForResult(); + ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); + + // Indicate that no certificate should be supplied. From the perspective + // of SSLClientCertCache, NULL is just as meaningful as a real + // certificate, so this is the same as supply a + // legitimate-but-unacceptable certificate. + rv = trans->RestartWithCertificate(NULL, &callback); + ASSERT_EQ(net::ERR_IO_PENDING, rv); + + // 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_EQ(NULL, client_cert.get()); + + + // Restart the handshake. This will consume ssl_data2, which fails, and + // then consume ssl_data3, which should also fail. The result code is + // checked against what ssl_data3 should return. + rv = callback.WaitForResult(); + ASSERT_EQ(net::ERR_SSL_PROTOCOL_ERROR, rv); + + // 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)); +} + +// Ensure that a client certificate is removed from the SSL client auth +// cache when: +// 1) An HTTPS proxy is involved. +// 3) The HTTPS proxy requests a client certificate. +// 4) The client supplies an invalid/unacceptable certificate for the +// proxy. +// The test is repeated twice, first for connecting to an HTTPS endpoint, +// then for connecting to an HTTP endpoint. +TEST_F(HttpNetworkTransactionTest, ClientAuthCertCache_Proxy_Fail) { + SessionDependencies session_deps( + ProxyService::CreateFixed("https://proxy:70")); + CapturingBoundNetLog log(CapturingNetLog::kUnbounded); + session_deps.net_log = log.bound().net_log(); + + scoped_refptr<SSLCertRequestInfo> cert_request(new SSLCertRequestInfo()); + cert_request->host_and_port = "proxy:70"; + + // See ClientAuthCertCache_Direct_NoFalseStart for the explanation of + // [ssl_]data[1-3]. Rather than represending the endpoint + // (www.example.com:443), they represent failures with the HTTPS proxy + // (proxy:70). + SSLSocketDataProvider ssl_data1(true /* async */, + net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED); + ssl_data1.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data1); + net::StaticSocketDataProvider data1(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data1); + + SSLSocketDataProvider ssl_data2(true /* async */, + net::ERR_SSL_PROTOCOL_ERROR); + ssl_data2.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data2); + net::StaticSocketDataProvider data2(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data2); + + SSLSocketDataProvider ssl_data3(true /* async */, + net::ERR_SSL_PROTOCOL_ERROR); + ssl_data3.cert_request_info = cert_request.get(); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl_data3); + net::StaticSocketDataProvider data3(NULL, 0, NULL, 0); + session_deps.socket_factory.AddSocketDataProvider(&data3); + + net::HttpRequestInfo requests[2]; + requests[0].url = GURL("https://www.example.com/"); + requests[0].method = "GET"; + requests[0].load_flags = net::LOAD_NORMAL; + + requests[1].url = GURL("http://www.example.com/"); + requests[1].method = "GET"; + requests[1].load_flags = net::LOAD_NORMAL; + + for (size_t i = 0; i < arraysize(requests); ++i) { + session_deps.socket_factory.ResetNextMockIndexes(); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + scoped_ptr<HttpNetworkTransaction> trans( + new HttpNetworkTransaction(session)); + + // Begin the SSL handshake with the proxy. + TestCompletionCallback callback; + int rv = trans->Start(&requests[i], &callback, net::BoundNetLog()); + ASSERT_EQ(net::ERR_IO_PENDING, rv); + + // Complete the SSL handshake, which should abort due to requiring a + // client certificate. + rv = callback.WaitForResult(); + ASSERT_EQ(net::ERR_SSL_CLIENT_AUTH_CERT_NEEDED, rv); + + // Indicate that no certificate should be supplied. From the perspective + // of SSLClientCertCache, NULL is just as meaningful as a real + // certificate, so this is the same as supply a + // legitimate-but-unacceptable certificate. + rv = trans->RestartWithCertificate(NULL, &callback); + ASSERT_EQ(net::ERR_IO_PENDING, rv); + + // 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_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)); + + // Restart the handshake. This will consume ssl_data2, which fails, and + // then consume ssl_data3, which should also fail. The result code is + // checked against what ssl_data3 should return. + rv = callback.WaitForResult(); + ASSERT_EQ(net::ERR_PROXY_CONNECTION_FAILED, rv); + + // 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)); + } +} + } // namespace net diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index ce108b1..4932da0 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc @@ -62,6 +62,24 @@ HttpProxyClientSocket::~HttpProxyClientSocket() { Disconnect(); } +int HttpProxyClientSocket::RestartWithAuth(CompletionCallback* callback) { + DCHECK_EQ(STATE_NONE, next_state_); + DCHECK(!user_callback_); + + int rv = PrepareForAuthRestart(); + if (rv != OK) + return rv; + + rv = DoLoop(OK); + if (rv == ERR_IO_PENDING) + user_callback_ = callback; + return rv; +} + +const HttpResponseInfo* HttpProxyClientSocket::GetConnectResponseInfo() const { + return response_.headers ? &response_ : NULL; +} + HttpStream* HttpProxyClientSocket::CreateConnectResponseStream() { return new HttpBasicStream(transport_.release(), http_stream_parser_.release(), false); @@ -99,67 +117,6 @@ int HttpProxyClientSocket::Connect(CompletionCallback* callback return rv; } -int HttpProxyClientSocket::RestartWithAuth(CompletionCallback* callback) { - DCHECK_EQ(STATE_NONE, next_state_); - DCHECK(!user_callback_); - - int rv = PrepareForAuthRestart(); - if (rv != OK) - return rv; - - rv = DoLoop(OK); - if (rv == ERR_IO_PENDING) - user_callback_ = callback; - return rv; -} - -int HttpProxyClientSocket::PrepareForAuthRestart() { - if (!response_.headers.get()) - return ERR_CONNECTION_RESET; - - bool keep_alive = false; - if (response_.headers->IsKeepAlive() && - http_stream_parser_->CanFindEndOfResponse()) { - if (!http_stream_parser_->IsResponseBodyComplete()) { - next_state_ = STATE_DRAIN_BODY; - drain_buf_ = new IOBuffer(kDrainBodyBufferSize); - return OK; - } - keep_alive = true; - } - - // We don't need to drain the response body, so we act as if we had drained - // the response body. - return DidDrainBodyForAuthRestart(keep_alive); -} - -int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { - if (keep_alive && transport_->socket()->IsConnectedAndIdle()) { - next_state_ = STATE_GENERATE_AUTH_TOKEN; - transport_->set_is_reused(true); - } else { - // This assumes that the underlying transport socket is a TCP socket, - // since only TCP sockets are restartable. - next_state_ = STATE_TCP_RESTART; - transport_->socket()->Disconnect(); - } - - // Reset the other member variables. - drain_buf_ = NULL; - parser_buf_ = NULL; - http_stream_parser_.reset(); - request_line_.clear(); - request_headers_.Clear(); - response_ = HttpResponseInfo(); - return OK; -} - -void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const { - LOG(WARNING) << "Blocked proxy response with status " << response_code - << " to CONNECT request for " - << GetHostAndPort(request_.url) << "."; -} - void HttpProxyClientSocket::Disconnect() { if (transport_.get()) transport_->socket()->Disconnect(); @@ -255,6 +212,64 @@ int HttpProxyClientSocket::GetPeerAddress(AddressList* address) const { return transport_->socket()->GetPeerAddress(address); } +int HttpProxyClientSocket::PrepareForAuthRestart() { + if (!response_.headers.get()) + return ERR_CONNECTION_RESET; + + bool keep_alive = false; + if (response_.headers->IsKeepAlive() && + http_stream_parser_->CanFindEndOfResponse()) { + if (!http_stream_parser_->IsResponseBodyComplete()) { + next_state_ = STATE_DRAIN_BODY; + drain_buf_ = new IOBuffer(kDrainBodyBufferSize); + return OK; + } + keep_alive = true; + } + + // We don't need to drain the response body, so we act as if we had drained + // the response body. + return DidDrainBodyForAuthRestart(keep_alive); +} + +int HttpProxyClientSocket::DidDrainBodyForAuthRestart(bool keep_alive) { + if (keep_alive && transport_->socket()->IsConnectedAndIdle()) { + next_state_ = STATE_GENERATE_AUTH_TOKEN; + transport_->set_is_reused(true); + } else { + // This assumes that the underlying transport socket is a TCP socket, + // since only TCP sockets are restartable. + next_state_ = STATE_TCP_RESTART; + transport_->socket()->Disconnect(); + } + + // Reset the other member variables. + drain_buf_ = NULL; + parser_buf_ = NULL; + http_stream_parser_.reset(); + request_line_.clear(); + request_headers_.Clear(); + response_ = HttpResponseInfo(); + return OK; +} + +int HttpProxyClientSocket::HandleAuthChallenge() { + DCHECK(response_.headers); + + int rv = auth_->HandleAuthChallenge(response_.headers, false, true, net_log_); + response_.auth_challenge = auth_->auth_info(); + if (rv == OK) + return ERR_PROXY_AUTH_REQUESTED; + + return rv; +} + +void HttpProxyClientSocket::LogBlockedTunnelResponse(int response_code) const { + LOG(WARNING) << "Blocked proxy response with status " << response_code + << " to CONNECT request for " + << GetHostAndPort(request_.url) << "."; +} + void HttpProxyClientSocket::DoCallback(int result) { DCHECK_NE(ERR_IO_PENDING, result); DCHECK(user_callback_); @@ -291,25 +306,25 @@ int HttpProxyClientSocket::DoLoop(int last_io_result) { break; case STATE_SEND_REQUEST: DCHECK_EQ(OK, rv); - net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, - NULL); + net_log_.BeginEvent( + NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, NULL); rv = DoSendRequest(); break; case STATE_SEND_REQUEST_COMPLETE: rv = DoSendRequestComplete(rv); - net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, - NULL); + net_log_.EndEventWithNetErrorCode( + NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_SEND_REQUEST, rv); break; case STATE_READ_HEADERS: DCHECK_EQ(OK, rv); - net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, - NULL); + net_log_.BeginEvent( + NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, NULL); rv = DoReadHeaders(); break; case STATE_READ_HEADERS_COMPLETE: rv = DoReadHeadersComplete(rv); - net_log_.EndEvent(NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, - NULL); + net_log_.EndEventWithNetErrorCode( + NetLog::TYPE_HTTP_TRANSACTION_TUNNEL_READ_HEADERS, rv); break; case STATE_DRAIN_BODY: DCHECK_EQ(OK, rv); @@ -479,15 +494,4 @@ int HttpProxyClientSocket::DoTCPRestartComplete(int result) { return result; } -int HttpProxyClientSocket::HandleAuthChallenge() { - DCHECK(response_.headers); - - int rv = auth_->HandleAuthChallenge(response_.headers, false, true, net_log_); - response_.auth_challenge = auth_->auth_info(); - if (rv == OK) - return ERR_PROXY_AUTH_REQUESTED; - - return rv; -} - } // namespace net diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index ef53c2d..daede3c 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -56,12 +56,6 @@ class HttpProxyClientSocket : public ProxyClientSocket { // RestartWithAuth. int RestartWithAuth(CompletionCallback* callback); - const HttpResponseInfo* GetConnectResponseInfo() const { - return response_.headers ? &response_ : NULL; - } - - virtual HttpStream* CreateConnectResponseStream(); - const scoped_refptr<HttpAuthController>& auth_controller() { return auth_; } @@ -70,14 +64,21 @@ class HttpProxyClientSocket : public ProxyClientSocket { return using_spdy_; } - // ClientSocket methods: + // ProxyClientSocket methods: + virtual const HttpResponseInfo* GetConnectResponseInfo() const; + virtual HttpStream* CreateConnectResponseStream(); +<<<<<<< HEAD // Authenticates to the Http Proxy and then passes data freely. virtual int Connect(CompletionCallback* callback #ifdef ANDROID , bool wait_for_connect #endif ); +======= + // ClientSocket methods: + virtual int Connect(CompletionCallback* callback); +>>>>>>> chromium.org at r11.0.672.0 virtual void Disconnect(); virtual bool IsConnected() const; virtual bool IsConnectedAndIdle() const; @@ -90,10 +91,8 @@ class HttpProxyClientSocket : public ProxyClientSocket { // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); virtual int Write(IOBuffer* buf, int buf_len, CompletionCallback* callback); - virtual bool SetReceiveBufferSize(int32 size); virtual bool SetSendBufferSize(int32 size); - virtual int GetPeerAddress(AddressList* address) const; private: diff --git a/net/http/http_proxy_client_socket_pool.cc b/net/http/http_proxy_client_socket_pool.cc index d297542..fd875ca 100644 --- a/net/http/http_proxy_client_socket_pool.cc +++ b/net/http/http_proxy_client_socket_pool.cc @@ -111,12 +111,11 @@ LoadState HttpProxyConnectJob::GetLoadState() const { } } -int HttpProxyConnectJob::ConnectInternal() { - if (params_->tcp_params()) - next_state_ = STATE_TCP_CONNECT; - else - next_state_ = STATE_SSL_CONNECT; - return DoLoop(OK); +void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) { + if (error_response_info_.cert_request_info) { + handle->set_ssl_error_response_info(error_response_info_); + handle->set_is_ssl_error(true); + } } void HttpProxyConnectJob::OnIOComplete(int result) { @@ -254,11 +253,33 @@ int HttpProxyConnectJob::DoSSLConnectComplete(int result) { return result; } -void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) { - if (error_response_info_.cert_request_info) { - handle->set_ssl_error_response_info(error_response_info_); - handle->set_is_ssl_error(true); +int HttpProxyConnectJob::DoHttpProxyConnect() { + next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE; + const HostResolver::RequestInfo& tcp_destination = params_->destination(); + const HostPortPair& proxy_server = tcp_destination.host_port_pair(); + + // Add a HttpProxy connection on top of the tcp socket. + transport_socket_.reset( + new HttpProxyClientSocket(transport_socket_handle_.release(), + params_->request_url(), + params_->user_agent(), + params_->endpoint(), + proxy_server, + params_->http_auth_cache(), + params_->http_auth_handler_factory(), + params_->tunnel(), + using_spdy_, + params_->ssl_params() != NULL)); + return transport_socket_->Connect(&callback_); +} + +int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) { + if (result == OK || result == ERR_PROXY_AUTH_REQUESTED || + result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE) { + set_socket(transport_socket_.release()); } + + return result; } int HttpProxyConnectJob::DoSpdyProxyCreateStream() { @@ -316,6 +337,7 @@ int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) { ); } +<<<<<<< HEAD #ifdef ANDROID // TODO(kristianm): Find out if Connect should block #endif @@ -350,6 +372,14 @@ int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) { } return result; +======= +int HttpProxyConnectJob::ConnectInternal() { + if (params_->tcp_params()) + next_state_ = STATE_TCP_CONNECT; + else + next_state_ = STATE_SSL_CONNECT; + return DoLoop(OK); +>>>>>>> chromium.org at r11.0.672.0 } HttpProxyClientSocketPool:: diff --git a/net/http/http_proxy_client_socket_pool.h b/net/http/http_proxy_client_socket_pool.h index 39a8896..70d4bba 100644 --- a/net/http/http_proxy_client_socket_pool.h +++ b/net/http/http_proxy_client_socket_pool.h @@ -129,15 +129,6 @@ class HttpProxyConnectJob : public ConnectJob { STATE_NONE, }; - // Begins the tcp connection and the optional Http proxy tunnel. If the - // request is not immediately servicable (likely), the request will return - // ERR_IO_PENDING. An OK return from this function or the callback means - // that the connection is established; ERR_PROXY_AUTH_REQUESTED means - // that the tunnel needs authentication credentials, the socket will be - // returned in this case, and must be release back to the pool; or - // a standard net error code will be returned. - virtual int ConnectInternal(); - void OnIOComplete(int result); // Runs the state transition loop. @@ -156,6 +147,15 @@ class HttpProxyConnectJob : public ConnectJob { int DoSpdyProxyCreateStream(); int DoSpdyProxyCreateStreamComplete(int result); + // Begins the tcp connection and the optional Http proxy tunnel. If the + // request is not immediately servicable (likely), the request will return + // ERR_IO_PENDING. An OK return from this function or the callback means + // that the connection is established; ERR_PROXY_AUTH_REQUESTED means + // that the tunnel needs authentication credentials, the socket will be + // returned in this case, and must be release back to the pool; or + // a standard net error code will be returned. + virtual int ConnectInternal(); + scoped_refptr<HttpProxySocketParams> params_; TCPClientSocketPool* const tcp_pool_; SSLClientSocketPool* const ssl_pool_; diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc index 8c6d545..359a058 100644 --- a/net/http/http_proxy_client_socket_pool_unittest.cc +++ b/net/http/http_proxy_client_socket_pool_unittest.cc @@ -20,7 +20,6 @@ #include "net/socket/client_socket_pool_histograms.h" #include "net/socket/socket_test_util.h" #include "net/spdy/spdy_protocol.h" -#include "net/spdy/spdy_session_pool.h" #include "net/spdy/spdy_test_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -60,13 +59,12 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam { &tcp_histograms_, &socket_factory_), ssl_histograms_("MockSSL"), + proxy_service_(ProxyService::CreateDirect()), ssl_config_service_(new SSLConfigServiceDefaults), - host_resolver_(new MockHostResolver), - cert_verifier_(new CertVerifier), ssl_socket_pool_(kMaxSockets, kMaxSocketsPerGroup, &ssl_histograms_, - host_resolver_.get(), - cert_verifier_.get(), + &host_resolver_, + &cert_verifier_, NULL /* dnsrr_resolver */, NULL /* dns_cert_checker */, NULL /* ssl_host_info_factory */, @@ -77,19 +75,8 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam { ssl_config_service_.get(), BoundNetLog().net_log()), http_auth_handler_factory_( - HttpAuthHandlerFactory::CreateDefault(host_resolver_.get())), - session_(new HttpNetworkSession(host_resolver_.get(), - cert_verifier_.get(), - NULL /* dnsrr_resolver */, - NULL /* dns_cert_checker */, - NULL /* ssl_host_info_factory */, - ProxyService::CreateDirect(), - &socket_factory_, - new SSLConfigServiceDefaults, - new SpdySessionPool(NULL), - http_auth_handler_factory_.get(), - NULL, - NULL)), + HttpAuthHandlerFactory::CreateDefault(&host_resolver_)), + session_(CreateNetworkSession()), http_proxy_histograms_("HttpProxyUnitTest"), ssl_data_(NULL), data_(NULL), @@ -107,8 +94,13 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam { void AddAuthToCache() { const string16 kFoo(ASCIIToUTF16("foo")); const string16 kBar(ASCIIToUTF16("bar")); - session_->auth_cache()->Add(GURL("http://proxy/"), "MyRealm1", "Basic", - "Basic realm=MyRealm1", kFoo, kBar, "/"); + session_->auth_cache()->Add(GURL("http://proxy/"), + "MyRealm1", + HttpAuth::AUTH_SCHEME_BASIC, + "Basic realm=MyRealm1", + kFoo, + kBar, + "/"); } scoped_refptr<TCPSocketParams> GetTcpParams() { @@ -184,6 +176,17 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam { ssl_data_->was_npn_negotiated = true; } + HttpNetworkSession* CreateNetworkSession() { + HttpNetworkSession::Params params; + params.host_resolver = &host_resolver_; + params.cert_verifier = &cert_verifier_; + params.proxy_service = proxy_service_; + params.client_socket_factory = &socket_factory_; + params.ssl_config_service = ssl_config_service_; + params.http_auth_handler_factory = http_auth_handler_factory_.get(); + return new HttpNetworkSession(params); + } + private: SSLConfig ssl_config_; @@ -193,13 +196,14 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam { DeterministicMockClientSocketFactory socket_factory_; MockTCPClientSocketPool tcp_socket_pool_; ClientSocketPoolHistograms ssl_histograms_; - scoped_refptr<SSLConfigService> ssl_config_service_; - scoped_ptr<HostResolver> host_resolver_; - scoped_ptr<CertVerifier> cert_verifier_; + MockHostResolver host_resolver_; + CertVerifier cert_verifier_; + const scoped_refptr<ProxyService> proxy_service_; + const scoped_refptr<SSLConfigService> ssl_config_service_; SSLClientSocketPool ssl_socket_pool_; - scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_; - scoped_refptr<HttpNetworkSession> session_; + const scoped_ptr<HttpAuthHandlerFactory> http_auth_handler_factory_; + const scoped_refptr<HttpNetworkSession> session_; ClientSocketPoolHistograms http_proxy_histograms_; protected: diff --git a/net/http/http_request_headers.cc b/net/http/http_request_headers.cc index fc2ebcd..9d523c1 100644 --- a/net/http/http_request_headers.cc +++ b/net/http/http_request_headers.cc @@ -30,6 +30,7 @@ const char HttpRequestHeaders::kProxyConnection[] = "Proxy-Connection"; const char HttpRequestHeaders::kRange[] = "Range"; const char HttpRequestHeaders::kReferer[] = "Referer"; const char HttpRequestHeaders::kUserAgent[] = "User-Agent"; +const char HttpRequestHeaders::kTransferEncoding[] = "Transfer-Encoding"; HttpRequestHeaders::HeaderKeyValuePair::HeaderKeyValuePair() { } @@ -85,6 +86,13 @@ void HttpRequestHeaders::SetHeader(const base::StringPiece& key, headers_.push_back(HeaderKeyValuePair(key.as_string(), value.as_string())); } +void HttpRequestHeaders::SetHeaderIfMissing(const base::StringPiece& key, + const base::StringPiece& value) { + HeaderVector::iterator it = FindHeader(key); + if (it == headers_.end()) + headers_.push_back(HeaderKeyValuePair(key.as_string(), value.as_string())); +} + void HttpRequestHeaders::RemoveHeader(const base::StringPiece& key) { HeaderVector::iterator it = FindHeader(key); if (it != headers_.end()) diff --git a/net/http/http_request_headers.h b/net/http/http_request_headers.h index 734194a..ef4b60d 100644 --- a/net/http/http_request_headers.h +++ b/net/http/http_request_headers.h @@ -73,6 +73,7 @@ class HttpRequestHeaders { static const char kRange[]; static const char kReferer[]; static const char kUserAgent[]; + static const char kTransferEncoding[]; HttpRequestHeaders(); ~HttpRequestHeaders(); @@ -95,6 +96,12 @@ class HttpRequestHeaders { // in the vector remains the same. When comparing |key|, case is ignored. void SetHeader(const base::StringPiece& key, const base::StringPiece& value); + // Sets the header value pair for |key| and |value|, if |key| does not exist. + // If |key| already exists, the call is a no-op. + // When comparing |key|, case is ignored. + void SetHeaderIfMissing(const base::StringPiece& key, + const base::StringPiece& value); + // Removes the first header that matches (case insensitive) |key|. void RemoveHeader(const base::StringPiece& key); diff --git a/net/http/http_request_headers_unittest.cc b/net/http/http_request_headers_unittest.cc index f3abfbe..384ceeb 100644 --- a/net/http/http_request_headers_unittest.cc +++ b/net/http/http_request_headers_unittest.cc @@ -67,6 +67,14 @@ TEST(HttpRequestHeaders, SetEmptyHeader) { EXPECT_EQ("Foo: Bar\r\nBar:\r\n\r\n", headers.ToString()); } +TEST(HttpRequestHeaders, SetHeaderIfMissing) { + HttpRequestHeaders headers; + headers.SetHeaderIfMissing("Foo", "Bar"); + EXPECT_EQ("Foo: Bar\r\n\r\n", headers.ToString()); + headers.SetHeaderIfMissing("Foo", "Baz"); + EXPECT_EQ("Foo: Bar\r\n\r\n", headers.ToString()); +} + TEST(HttpRequestHeaders, RemoveHeader) { HttpRequestHeaders headers; headers.SetHeader("Foo", "bar"); diff --git a/net/http/http_response_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc index 76304f8..5745865 100644 --- a/net/http/http_response_body_drainer_unittest.cc +++ b/net/http/http_response_body_drainer_unittest.cc @@ -16,7 +16,6 @@ #include "net/http/http_network_session.h" #include "net/http/http_stream.h" #include "net/proxy/proxy_service.h" -#include "net/spdy/spdy_session_pool.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -174,23 +173,23 @@ void MockHttpStream::CompleteRead() { class HttpResponseBodyDrainerTest : public testing::Test { protected: HttpResponseBodyDrainerTest() - : session_(new HttpNetworkSession( - NULL /* host_resolver */, - NULL /* dnsrr_resolver */, - NULL /* dns_cert_checker */, - NULL, - NULL /* ssl_host_info_factory */, - ProxyService::CreateDirect(), - NULL, - new SSLConfigServiceDefaults, - new SpdySessionPool(NULL), - NULL, - NULL, - NULL)), + : proxy_service_(ProxyService::CreateDirect()), + ssl_config_service_(new SSLConfigServiceDefaults), + session_(CreateNetworkSession()), mock_stream_(new MockHttpStream(&result_waiter_)), drainer_(new HttpResponseBodyDrainer(mock_stream_)) {} + ~HttpResponseBodyDrainerTest() {} + HttpNetworkSession* CreateNetworkSession() const { + HttpNetworkSession::Params params; + params.proxy_service = proxy_service_; + params.ssl_config_service = ssl_config_service_; + return new HttpNetworkSession(params); + } + + scoped_refptr<ProxyService> proxy_service_; + scoped_refptr<SSLConfigService> ssl_config_service_; const scoped_refptr<HttpNetworkSession> session_; CloseResultWaiter result_waiter_; MockHttpStream* const mock_stream_; // Owned by |drainer_|. diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index c2d098c..5bf2beb 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -12,6 +12,7 @@ #include <algorithm> #include "base/logging.h" +#include "base/metrics/histogram.h" #include "base/pickle.h" #include "base/string_number_conversions.h" #include "base/string_util.h" @@ -85,13 +86,69 @@ bool ShouldUpdateHeader(const std::string::const_iterator& name_begin, return true; } +// Functions for histogram initialization. The code 0 is put in the +// response map to track response codes that are invalid. +// TODO(gavinp): Greatly prune the collected codes once we learn which +// ones are not sent in practice, to reduce upload size & memory use. + +enum { + HISTOGRAM_MIN_HTTP_RESPONSE_CODE = 100, + HISTOGRAM_MAX_HTTP_RESPONSE_CODE = 599, +}; + +std::vector<int> GetAllHttpResponseCodes() { + std::vector<int> codes; + codes.reserve( + HISTOGRAM_MAX_HTTP_RESPONSE_CODE - HISTOGRAM_MIN_HTTP_RESPONSE_CODE + 2); + codes.push_back(0); + for (int i = HISTOGRAM_MIN_HTTP_RESPONSE_CODE; + i <= HISTOGRAM_MAX_HTTP_RESPONSE_CODE; ++i) + codes.push_back(i); + return codes; +} + +int MapHttpResponseCode(int code) { + if (HISTOGRAM_MIN_HTTP_RESPONSE_CODE <= code && + code <= HISTOGRAM_MAX_HTTP_RESPONSE_CODE) + return code; + return 0; +} + } // namespace +struct HttpResponseHeaders::ParsedHeader { + // A header "continuation" contains only a subsequent value for the + // preceding header. (Header values are comma separated.) + bool is_continuation() const { return name_begin == name_end; } + + std::string::const_iterator name_begin; + std::string::const_iterator name_end; + std::string::const_iterator value_begin; + std::string::const_iterator value_end; +}; + //----------------------------------------------------------------------------- HttpResponseHeaders::HttpResponseHeaders(const std::string& raw_input) : response_code_(-1) { Parse(raw_input); + + // The most important thing to do with this histogram is find out + // the existence of unusual HTTP response codes. As it happens + // right now, there aren't double-constructions of response headers + // using this constructor, so our counts should also be accurate, + // without instantiating the histogram in two places. It is also + // important that this histogram not collect data in the other + // constructor, which rebuilds an histogram from a pickle, since + // that would actually create a double call between the original + // HttpResponseHeader that was serialized, and initialization of the + // new object from that pickle. + UMA_HISTOGRAM_CUSTOM_ENUMERATION("Net.HttpResponseCode", + MapHttpResponseCode(response_code_), + // Note the third argument is only + // evaluated once, see macro + // definition for details. + GetAllHttpResponseCodes()); } HttpResponseHeaders::HttpResponseHeaders(const Pickle& pickle, void** iter) @@ -359,30 +416,6 @@ void HttpResponseHeaders::GetNormalizedHeaders(std::string* output) const { output->push_back('\n'); } -void HttpResponseHeaders::GetRawHeaders(std::string* output) const { - if (!output) - return; - output->erase(); - const char* headers_string = raw_headers().c_str(); - size_t headers_length = raw_headers().length(); - if (!headers_string) - return; - // The headers_string is a NULL-terminated status line, followed by NULL- - // terminated headers. - std::string raw_string = headers_string; - size_t current_length = strlen(headers_string) + 1; - while (headers_length > current_length) { - // Move to the next header, and append it. - headers_string += current_length; - headers_length -= current_length; - raw_string += "\n"; - raw_string += headers_string; - // Get the next header location. - current_length = strlen(headers_string) + 1; - } - *output = raw_string; -} - bool HttpResponseHeaders::GetNormalizedHeader(const std::string& name, std::string* value) const { // If you hit this assertion, please use EnumerateHeader instead! @@ -494,7 +527,7 @@ bool HttpResponseHeaders::HasHeader(const std::string& name) const { return FindHeader(0, name) != std::string::npos; } -HttpResponseHeaders::HttpResponseHeaders() { +HttpResponseHeaders::HttpResponseHeaders() : response_code_(-1) { } HttpResponseHeaders::~HttpResponseHeaders() { diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h index 2b556b3..3c2eae0 100644 --- a/net/http/http_response_headers.h +++ b/net/http/http_response_headers.h @@ -100,9 +100,6 @@ class HttpResponseHeaders // void GetNormalizedHeaders(std::string* output) const; - // Gets the raw stored headers, in human-readable form. - void GetRawHeaders(std::string* output) const; - // Fetch the "normalized" value of a single header, where all values for the // header name are separated by commas. See the GetNormalizedHeaders for // format details. Returns false if this header wasn't found. @@ -254,16 +251,7 @@ class HttpResponseHeaders typedef base::hash_set<std::string> HeaderSet; // The members of this structure point into raw_headers_. - struct ParsedHeader { - std::string::const_iterator name_begin; - std::string::const_iterator name_end; - std::string::const_iterator value_begin; - std::string::const_iterator value_end; - - // A header "continuation" contains only a subsequent value for the - // preceding header. (Header values are comma separated.) - bool is_continuation() const { return name_begin == name_end; } - }; + struct ParsedHeader; typedef std::vector<ParsedHeader> HeaderList; HttpResponseHeaders(); diff --git a/net/http/http_stream_factory.cc b/net/http/http_stream_factory.cc index b575f48..4673d58 100644 --- a/net/http/http_stream_factory.cc +++ b/net/http/http_stream_factory.cc @@ -32,14 +32,6 @@ std::list<HostPortPair>* HttpStreamFactory::forced_spdy_exclusions_ = NULL; // static bool HttpStreamFactory::ignore_certificate_errors_ = false; -// static -void HttpStreamFactory::SetHostMappingRules(const std::string& rules) { - HostMappingRules* host_mapping_rules = new HostMappingRules(); - host_mapping_rules->SetRulesFromString(rules); - delete host_mapping_rules_; - host_mapping_rules_ = host_mapping_rules; -} - HttpStreamFactory::HttpStreamFactory() { } @@ -53,6 +45,14 @@ HttpStreamFactory::~HttpStreamFactory() { } } +// static +void HttpStreamFactory::SetHostMappingRules(const std::string& rules) { + HostMappingRules* host_mapping_rules = new HostMappingRules(); + host_mapping_rules->SetRulesFromString(rules); + delete host_mapping_rules_; + host_mapping_rules_ = host_mapping_rules; +} + StreamRequest* HttpStreamFactory::RequestStream( const HttpRequestInfo* request_info, SSLConfig* ssl_config, diff --git a/net/http/http_stream_factory.h b/net/http/http_stream_factory.h index 3bb6d2f..97bd79c 100644 --- a/net/http/http_stream_factory.h +++ b/net/http/http_stream_factory.h @@ -34,35 +34,6 @@ class HttpStreamFactory : public StreamFactory, HttpStreamFactory(); virtual ~HttpStreamFactory(); - // StreamFactory Interface - virtual StreamRequest* RequestStream(const HttpRequestInfo* info, - SSLConfig* ssl_config, - ProxyInfo* proxy_info, - HttpNetworkSession* session, - StreamRequest::Delegate* delegate, - const BoundNetLog& net_log); - - virtual int PreconnectStreams(int num_streams, - const HttpRequestInfo* info, - SSLConfig* ssl_config, - ProxyInfo* proxy_info, - HttpNetworkSession* session, - const BoundNetLog& net_log, - CompletionCallback* callback); - - virtual void AddTLSIntolerantServer(const GURL& url); - virtual bool IsTLSIntolerantServer(const GURL& url); - - virtual void ProcessAlternateProtocol( - HttpAlternateProtocols* alternate_protocols, - const std::string& alternate_protocol_str, - const HostPortPair& http_host_port_pair); - - virtual GURL ApplyHostMappingRules(const GURL& url, HostPortPair* endpoint); - - // HttpStreamRequest::PreconnectDelegate API - virtual void OnPreconnectsComplete(HttpStreamRequest* request, int result); - // Static settings // Turns spdy on or off. @@ -122,6 +93,31 @@ class HttpStreamFactory : public StreamFactory, static void SetHostMappingRules(const std::string& rules); + // StreamFactory Interface + virtual StreamRequest* RequestStream(const HttpRequestInfo* info, + SSLConfig* ssl_config, + ProxyInfo* proxy_info, + HttpNetworkSession* session, + StreamRequest::Delegate* delegate, + const BoundNetLog& net_log); + virtual int PreconnectStreams(int num_streams, + const HttpRequestInfo* info, + SSLConfig* ssl_config, + ProxyInfo* proxy_info, + HttpNetworkSession* session, + const BoundNetLog& net_log, + CompletionCallback* callback); + virtual void AddTLSIntolerantServer(const GURL& url); + virtual bool IsTLSIntolerantServer(const GURL& url); + virtual void ProcessAlternateProtocol( + HttpAlternateProtocols* alternate_protocols, + const std::string& alternate_protocol_str, + const HostPortPair& http_host_port_pair); + virtual GURL ApplyHostMappingRules(const GURL& url, HostPortPair* endpoint); + + // HttpStreamRequest::PreconnectDelegate API + virtual void OnPreconnectsComplete(HttpStreamRequest* request, int result); + private: typedef std::map<HttpStreamRequest*, CompletionCallback*> RequestCallbackMap; RequestCallbackMap request_callback_map_; diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc index 646f79c..617d885 100644 --- a/net/http/http_stream_factory_unittest.cc +++ b/net/http/http_stream_factory_unittest.cc @@ -17,6 +17,7 @@ #include "net/http/http_network_session_peer.h" #include "net/http/http_request_info.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" #include "testing/gtest/include/gtest/gtest.h" @@ -45,18 +46,16 @@ struct SessionDependencies { }; HttpNetworkSession* CreateSession(SessionDependencies* session_deps) { - return new HttpNetworkSession(session_deps->host_resolver.get(), - session_deps->cert_verifier.get(), - NULL /* dnsrr_resolver */, - NULL /* dns_cert_checker */, - NULL /* ssl_host_info_factory */, - session_deps->proxy_service, - &session_deps->socket_factory, - session_deps->ssl_config_service, - new SpdySessionPool(NULL), - session_deps->http_auth_handler_factory.get(), - NULL, - session_deps->net_log); + HttpNetworkSession::Params params; + params.host_resolver = session_deps->host_resolver.get(); + params.cert_verifier = session_deps->cert_verifier.get(); + params.proxy_service = session_deps->proxy_service; + params.ssl_config_service = session_deps->ssl_config_service; + params.client_socket_factory = &session_deps->socket_factory; + params.http_auth_handler_factory = + session_deps->http_auth_handler_factory.get(); + params.net_log = session_deps->net_log; + return new HttpNetworkSession(params); } struct TestCase { @@ -96,7 +95,8 @@ int PreconnectHelper(const TestCase& test, template<typename ParentPool> class CapturePreconnectsSocketPool : public ParentPool { public: - explicit CapturePreconnectsSocketPool(HttpNetworkSession* session); + CapturePreconnectsSocketPool(HostResolver* host_resolver, + CertVerifier* cert_verifier); int last_num_streams() const { return last_num_streams_; @@ -124,7 +124,8 @@ class CapturePreconnectsSocketPool : public ParentPool { ADD_FAILURE(); } virtual void ReleaseSocket(const std::string& group_name, - ClientSocket* socket) { + ClientSocket* socket, + int id) { ADD_FAILURE(); } virtual void CloseIdleSockets() { @@ -162,21 +163,22 @@ CapturePreconnectsSSLSocketPool; template<typename ParentPool> CapturePreconnectsSocketPool<ParentPool>::CapturePreconnectsSocketPool( - HttpNetworkSession* session) - : ParentPool(0, 0, NULL, session->host_resolver(), NULL, NULL) {} + HostResolver* host_resolver, CertVerifier* /* cert_verifier */) + : ParentPool(0, 0, NULL, host_resolver, NULL, NULL), + last_num_streams_(-1) {} template<> CapturePreconnectsHttpProxySocketPool::CapturePreconnectsSocketPool( - HttpNetworkSession* session) - : HttpProxyClientSocketPool(0, 0, NULL, session->host_resolver(), NULL, - NULL, NULL) {} + HostResolver* host_resolver, CertVerifier* /* cert_verifier */) + : HttpProxyClientSocketPool(0, 0, NULL, host_resolver, NULL, NULL, NULL), + last_num_streams_(-1) {} template<> CapturePreconnectsSSLSocketPool::CapturePreconnectsSocketPool( - HttpNetworkSession* session) - : SSLClientSocketPool(0, 0, NULL, session->host_resolver(), - session->cert_verifier(), NULL, NULL, - NULL, NULL, NULL, NULL, NULL, NULL, NULL) {} + HostResolver* host_resolver, CertVerifier* cert_verifier) + : SSLClientSocketPool(0, 0, NULL, host_resolver, cert_verifier, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL), + last_num_streams_(-1) {} TEST(HttpStreamFactoryTest, PreconnectDirect) { for (size_t i = 0; i < arraysize(kTests); ++i) { @@ -184,10 +186,14 @@ TEST(HttpStreamFactoryTest, PreconnectDirect) { scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); HttpNetworkSessionPeer peer(session); CapturePreconnectsTCPSocketPool* tcp_conn_pool = - new CapturePreconnectsTCPSocketPool(session); + new CapturePreconnectsTCPSocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); peer.SetTCPSocketPool(tcp_conn_pool); CapturePreconnectsSSLSocketPool* ssl_conn_pool = - new CapturePreconnectsSSLSocketPool(session.get()); + new CapturePreconnectsSSLSocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); peer.SetSSLSocketPool(ssl_conn_pool); EXPECT_EQ(OK, PreconnectHelper(kTests[i], session)); if (kTests[i].ssl) @@ -204,10 +210,14 @@ TEST(HttpStreamFactoryTest, PreconnectHttpProxy) { HttpNetworkSessionPeer peer(session); HostPortPair proxy_host("http_proxy", 80); CapturePreconnectsHttpProxySocketPool* http_proxy_pool = - new CapturePreconnectsHttpProxySocketPool(session); + new CapturePreconnectsHttpProxySocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); peer.SetSocketPoolForHTTPProxy(proxy_host, http_proxy_pool); CapturePreconnectsSSLSocketPool* ssl_conn_pool = - new CapturePreconnectsSSLSocketPool(session); + new CapturePreconnectsSSLSocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); peer.SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool); EXPECT_EQ(OK, PreconnectHelper(kTests[i], session)); if (kTests[i].ssl) @@ -225,10 +235,14 @@ TEST(HttpStreamFactoryTest, PreconnectSocksProxy) { HttpNetworkSessionPeer peer(session); HostPortPair proxy_host("socks_proxy", 1080); CapturePreconnectsSOCKSSocketPool* socks_proxy_pool = - new CapturePreconnectsSOCKSSocketPool(session); + new CapturePreconnectsSOCKSSocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); peer.SetSocketPoolForSOCKSProxy(proxy_host, socks_proxy_pool); CapturePreconnectsSSLSocketPool* ssl_conn_pool = - new CapturePreconnectsSSLSocketPool(session); + new CapturePreconnectsSSLSocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); peer.SetSocketPoolForSSLWithProxy(proxy_host, ssl_conn_pool); EXPECT_EQ(OK, PreconnectHelper(kTests[i], session)); if (kTests[i].ssl) @@ -238,6 +252,39 @@ TEST(HttpStreamFactoryTest, PreconnectSocksProxy) { } } +TEST(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) { + for (size_t i = 0; i < arraysize(kTests); ++i) { + SessionDependencies session_deps(ProxyService::CreateDirect()); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); + HttpNetworkSessionPeer peer(session); + + // Set an existing SpdySession in the pool. + HostPortPair host_port_pair("www.google.com", 443); + HostPortProxyPair pair(host_port_pair, ProxyServer::Direct()); + scoped_refptr<SpdySession> spdy_session = + session->spdy_session_pool()->Get( + pair, session->mutable_spdy_settings(), BoundNetLog()); + + CapturePreconnectsTCPSocketPool* tcp_conn_pool = + new CapturePreconnectsTCPSocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); + peer.SetTCPSocketPool(tcp_conn_pool); + CapturePreconnectsSSLSocketPool* ssl_conn_pool = + new CapturePreconnectsSSLSocketPool( + session_deps.host_resolver.get(), + session_deps.cert_verifier.get()); + peer.SetSSLSocketPool(ssl_conn_pool); + EXPECT_EQ(OK, PreconnectHelper(kTests[i], session)); + // We shouldn't be preconnecting if we have an existing session, which is + // the case for https://www.google.com. + if (kTests[i].ssl) + EXPECT_EQ(-1, ssl_conn_pool->last_num_streams()); + else + EXPECT_EQ(kTests[i].num_streams, tcp_conn_pool->last_num_streams()); + } +} + } // namespace } // namespace net diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc index 2a3fb15..20ebd87 100644 --- a/net/http/http_stream_parser.cc +++ b/net/http/http_stream_parser.cc @@ -43,7 +43,10 @@ HttpStreamParser::HttpStreamParser(ClientSocketHandle* connection, DCHECK_EQ(0, read_buffer->offset()); } -HttpStreamParser::~HttpStreamParser() {} +HttpStreamParser::~HttpStreamParser() { + if (request_body_ != NULL && request_body_->is_chunked()) + request_body_->set_chunk_callback(NULL); +} int HttpStreamParser::SendRequest(const std::string& request_line, const HttpRequestHeaders& headers, @@ -67,6 +70,8 @@ int HttpStreamParser::SendRequest(const std::string& request_line, request_headers_ = new DrainableIOBuffer(headers_io_buf, headers_io_buf->size()); request_body_.reset(request_body); + if (request_body_ != NULL && request_body_->is_chunked()) + request_body_->set_chunk_callback(this); io_state_ = STATE_SENDING_HEADERS; int result = DoLoop(OK); @@ -143,6 +148,16 @@ void HttpStreamParser::OnIOComplete(int result) { } } +void HttpStreamParser::OnChunkAvailable() { + // This method may get called while sending the headers or body, so check + // before processing the new data. If we were still initializing or sending + // headers, we will automatically start reading the chunks once we get into + // STATE_SENDING_BODY so nothing to do here. + DCHECK(io_state_ == STATE_SENDING_HEADERS || io_state_ == STATE_SENDING_BODY); + if (io_state_ == STATE_SENDING_BODY) + OnIOComplete(0); +} + int HttpStreamParser::DoLoop(int result) { bool can_do_more = true; do { @@ -169,7 +184,8 @@ int HttpStreamParser::DoLoop(int result) { break; case STATE_READ_HEADERS_COMPLETE: result = DoReadHeadersComplete(result); - net_log_.EndEvent(NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS, NULL); + net_log_.EndEventWithNetErrorCode( + NetLog::TYPE_HTTP_STREAM_PARSER_READ_HEADERS, result); break; case STATE_BODY_PENDING: DCHECK(result != ERR_IO_PENDING); @@ -208,12 +224,16 @@ int HttpStreamParser::DoSendHeaders(int result) { // We'll record the count of uncoalesced packets IFF coalescing will help, // and otherwise we'll use an enum to tell why it won't help. enum COALESCE_POTENTIAL { - NO_ADVANTAGE = 0, // Coalescing won't reduce packet count. - HEADER_ONLY = 1, // There is only a header packet (can't coalesce). - COALESCE_POTENTIAL_MAX = 30 // Various cases of coalasced savings. + // Coalescing won't reduce packet count. + NO_ADVANTAGE = 0, + // There is only a header packet or we have a request body but the + // request body isn't available yet (can't coalesce). + HEADER_ONLY = 1, + // Various cases of coalasced savings. + COALESCE_POTENTIAL_MAX = 30 }; size_t coalesce = HEADER_ONLY; - if (request_body_ != NULL) { + if (request_body_ != NULL && !request_body_->is_chunked()) { const size_t kBytesPerPacket = 1430; uint64 body_packets = (request_body_->size() + kBytesPerPacket - 1) / kBytesPerPacket; @@ -236,7 +256,8 @@ int HttpStreamParser::DoSendHeaders(int result) { result = connection_->socket()->Write(request_headers_, bytes_remaining, &io_callback_); - } else if (request_body_ != NULL && request_body_->size()) { + } else if (request_body_ != NULL && + (request_body_->is_chunked() || request_body_->size())) { io_state_ = STATE_SENDING_BODY; result = OK; } else { @@ -246,13 +267,17 @@ int HttpStreamParser::DoSendHeaders(int result) { } int HttpStreamParser::DoSendBody(int result) { - if (result > 0) - request_body_->DidConsume(result); + request_body_->MarkConsumedAndFillBuffer(result); if (!request_body_->eof()) { int buf_len = static_cast<int>(request_body_->buf_len()); - result = connection_->socket()->Write(request_body_->buf(), buf_len, - &io_callback_); + if (buf_len) { + result = connection_->socket()->Write(request_body_->buf(), buf_len, + &io_callback_); + } else { + // More POST data is to come hence wait for the callback. + result = ERR_IO_PENDING; + } } else { io_state_ = STATE_REQUEST_SENT; } diff --git a/net/http/http_stream_parser.h b/net/http/http_stream_parser.h index bbd551f..5f7e943 100644 --- a/net/http/http_stream_parser.h +++ b/net/http/http_stream_parser.h @@ -26,7 +26,7 @@ class IOBuffer; class SSLCertRequestInfo; class SSLInfo; -class HttpStreamParser { +class HttpStreamParser : public ChunkCallback { public: // Any data in |read_buffer| will be used before reading from the socket // and any data left over after parsing the stream will be put into @@ -71,6 +71,9 @@ class HttpStreamParser { void GetSSLCertRequestInfo(SSLCertRequestInfo* cert_request_info); + // ChunkCallback methods. + virtual void OnChunkAvailable(); + private: // FOO_COMPLETE states implement the second half of potentially asynchronous // operations and don't necessarily mean that FOO is complete. diff --git a/net/http/http_stream_request.cc b/net/http/http_stream_request.cc index 242f899..8794ea2 100644 --- a/net/http/http_stream_request.cc +++ b/net/http/http_stream_request.cc @@ -492,8 +492,11 @@ int HttpStreamRequest::DoInitConnection() { // Check first if we have a spdy session for this group. If so, then go // straight to using that. HostPortProxyPair pair(endpoint_, proxy_info()->proxy_server()); - if (!preconnect_delegate_ && - session_->spdy_session_pool()->HasSession(pair)) { + if (session_->spdy_session_pool()->HasSession(pair)) { + // If we're preconnecting, but we already have a SpdySession, we don't + // actually need to preconnect any sockets, so we're done. + if (preconnect_delegate_) + return OK; using_spdy_ = true; next_state_ = STATE_CREATE_STREAM; return OK; @@ -769,7 +772,7 @@ int HttpStreamRequest::DoInitConnectionComplete(int result) { } } if (result < 0) - return HandleSSLHandshakeError(result); + return result; } next_state_ = STATE_CREATE_STREAM; @@ -917,6 +920,18 @@ scoped_refptr<SSLSocketParams> HttpStreamRequest::GenerateSSLParams( ssl_config()->tls1_enabled = false; } + if (proxy_info()->is_https() && ssl_config()->send_client_cert) { + // When connecting through an HTTPS proxy, disable TLS False Start so + // that client authentication errors can be distinguished between those + // originating from the proxy server (ERR_PROXY_CONNECTION_FAILED) and + // those originating from the endpoint (ERR_SSL_PROTOCOL_ERROR / + // ERR_BAD_SSL_CLIENT_AUTH_CERT). + // TODO(rch): This assumes that the HTTPS proxy will only request a + // client certificate during the initial handshake. + // http://crbug.com/59292 + ssl_config()->false_start_enabled = false; + } + UMA_HISTOGRAM_ENUMERATION("Net.ConnectionUsedSSLv3Fallback", static_cast<int>(ssl_config()->ssl3_fallback), 2); @@ -1005,6 +1020,11 @@ int HttpStreamRequest::ReconsiderProxyAfterError(int error) { return error; } + if (proxy_info()->is_https() && ssl_config()->send_client_cert) { + session_->ssl_client_auth_cache()->Remove( + proxy_info()->proxy_server().host_port_pair().ToString()); + } + int rv = session_->proxy_service()->ReconsiderProxyAfterError( request_info().url, proxy_info(), &io_callback_, &pac_request_, net_log_); @@ -1051,35 +1071,6 @@ int HttpStreamRequest::HandleCertificateError(int error) { return error; } -int HttpStreamRequest::HandleSSLHandshakeError(int error) { - if (ssl_config()->send_client_cert && - (error == ERR_SSL_PROTOCOL_ERROR || - error == ERR_BAD_SSL_CLIENT_AUTH_CERT)) { - session_->ssl_client_auth_cache()->Remove( - GetHostAndPort(request_info().url)); - } - - switch (error) { - case ERR_SSL_PROTOCOL_ERROR: - case ERR_SSL_VERSION_OR_CIPHER_MISMATCH: - case ERR_SSL_DECOMPRESSION_FAILURE_ALERT: - case ERR_SSL_BAD_RECORD_MAC_ALERT: - if (ssl_config()->tls1_enabled && - !SSLConfigService::IsKnownStrictTLSServer( - request_info().url.host())) { - // This could be a TLS-intolerant server, an SSL 3.0 server that - // chose a TLS-only cipher suite or a server with buggy DEFLATE - // support. Turn off TLS 1.0, DEFLATE support and retry. - factory_->AddTLSIntolerantServer(request_info().url); - next_state_ = STATE_INIT_CONNECTION; - DCHECK(!connection_.get() || !connection_->socket()); - error = OK; - } - break; - } - return error; -} - void HttpStreamRequest::SwitchToSpdyMode() { if (HttpStreamFactory::spdy_enabled()) using_spdy_ = true; diff --git a/net/http/http_stream_request.h b/net/http/http_stream_request.h index 62ba5d8..fca2332 100644 --- a/net/http/http_stream_request.h +++ b/net/http/http_stream_request.h @@ -172,11 +172,6 @@ class HttpStreamRequest : public StreamRequest { // Called to handle a client certificate request. int HandleCertificateRequest(int error); - // Called to possibly recover from an SSL handshake error. Sets next_state_ - // and returns OK if recovering from the error. Otherwise, the same error - // code is returned. - int HandleSSLHandshakeError(int error); - // Moves this stream request into SPDY mode. void SwitchToSpdyMode(); diff --git a/net/http/http_transaction_unittest.cc b/net/http/http_transaction_unittest.cc index 518c1f8..fabfb01 100644 --- a/net/http/http_transaction_unittest.cc +++ b/net/http/http_transaction_unittest.cc @@ -1,10 +1,11 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// 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. #include "net/http/http_transaction_unittest.h" -#include "base/hash_tables.h" +#include <algorithm> + #include "base/message_loop.h" #include "base/string_util.h" #include "net/base/net_errors.h" @@ -16,6 +17,11 @@ #include "net/http/http_transaction.h" #include "testing/gtest/include/gtest/gtest.h" +namespace { +typedef base::hash_map<std::string, const MockTransaction*> MockTransactionMap; +static MockTransactionMap mock_transactions; +} // namespace + //----------------------------------------------------------------------------- // mock transaction data @@ -104,18 +110,6 @@ static const MockTransaction* const kBuiltinMockTransactions[] = { &kRangeGET_Transaction }; -typedef base::hash_map<std::string, const MockTransaction*> -MockTransactionMap; -static MockTransactionMap mock_transactions; - -void AddMockTransaction(const MockTransaction* trans) { - mock_transactions[GURL(trans->url).spec()] = trans; -} - -void RemoveMockTransaction(const MockTransaction* trans) { - mock_transactions.erase(GURL(trans->url).spec()); -} - const MockTransaction* FindMockTransaction(const GURL& url) { // look for overrides: MockTransactionMap::const_iterator it = mock_transactions.find(url.spec()); @@ -130,12 +124,222 @@ const MockTransaction* FindMockTransaction(const GURL& url) { return NULL; } +void AddMockTransaction(const MockTransaction* trans) { + mock_transactions[GURL(trans->url).spec()] = trans; +} + +void RemoveMockTransaction(const MockTransaction* trans) { + mock_transactions.erase(GURL(trans->url).spec()); +} + +MockHttpRequest::MockHttpRequest(const MockTransaction& t) { + url = GURL(t.url); + method = t.method; + extra_headers.AddHeadersFromString(t.request_headers); + load_flags = t.load_flags; +} //----------------------------------------------------------------------------- // static int TestTransactionConsumer::quit_counter_ = 0; +TestTransactionConsumer::TestTransactionConsumer( + net::HttpTransactionFactory* factory) + : state_(IDLE), + trans_(NULL), + error_(net::OK) { + // Disregard the error code. + factory->CreateTransaction(&trans_); + ++quit_counter_; +} + +TestTransactionConsumer::~TestTransactionConsumer() { +} + +void TestTransactionConsumer::Start(const net::HttpRequestInfo* request, + const net::BoundNetLog& net_log) { + state_ = STARTING; + int result = trans_->Start(request, this, net_log); + if (result != net::ERR_IO_PENDING) + DidStart(result); +} + +void TestTransactionConsumer::DidStart(int result) { + if (result != net::OK) { + DidFinish(result); + } else { + Read(); + } +} + +void TestTransactionConsumer::DidRead(int result) { + if (result <= 0) { + DidFinish(result); + } else { + content_.append(read_buf_->data(), result); + Read(); + } +} + +void TestTransactionConsumer::DidFinish(int result) { + state_ = DONE; + error_ = result; + if (--quit_counter_ == 0) + MessageLoop::current()->Quit(); +} + +void TestTransactionConsumer::Read() { + state_ = READING; + read_buf_ = new net::IOBuffer(1024); + int result = trans_->Read(read_buf_, 1024, this); + if (result != net::ERR_IO_PENDING) + DidRead(result); +} + +void TestTransactionConsumer::RunWithParams(const Tuple1<int>& params) { + int result = params.a; + switch (state_) { + case STARTING: + DidStart(result); + break; + case READING: + DidRead(result); + break; + default: + NOTREACHED(); + } +} + + +MockNetworkTransaction::MockNetworkTransaction() : + ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), data_cursor_(0) { +} + +MockNetworkTransaction::~MockNetworkTransaction() {} + +int MockNetworkTransaction::Start(const net::HttpRequestInfo* request, + net::CompletionCallback* callback, + const net::BoundNetLog& net_log) { + const MockTransaction* t = FindMockTransaction(request->url); + if (!t) + return net::ERR_FAILED; + + 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_.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_.ssl_info.cert_status = t->cert_status; + data_ = resp_data; + test_mode_ = t->test_mode; + + if (test_mode_ & TEST_MODE_SYNC_NET_START) + return net::OK; + + CallbackLater(callback, net::OK); + return net::ERR_IO_PENDING; +} + +int MockNetworkTransaction::RestartIgnoringLastError( + net::CompletionCallback* callback) { + return net::ERR_FAILED; +} + +int MockNetworkTransaction::RestartWithCertificate( + net::X509Certificate* client_cert, + net::CompletionCallback* callback) { + return net::ERR_FAILED; +} + +int MockNetworkTransaction::RestartWithAuth(const string16& username, + const string16& password, + net::CompletionCallback* callback) { + return net::ERR_FAILED; +} + +bool MockNetworkTransaction::IsReadyToRestartForAuth() { + return false; +} + +int MockNetworkTransaction::Read(net::IOBuffer* buf, int buf_len, + net::CompletionCallback* callback) { + int data_len = static_cast<int>(data_.size()); + int num = std::min(buf_len, data_len - data_cursor_); + if (num) { + memcpy(buf->data(), data_.data() + data_cursor_, num); + data_cursor_ += num; + } + if (test_mode_ & TEST_MODE_SYNC_NET_READ) + return num; + + CallbackLater(callback, num); + return net::ERR_IO_PENDING; +} + +void MockNetworkTransaction::StopCaching() {} + +const net::HttpResponseInfo* MockNetworkTransaction::GetResponseInfo() const { + return &response_; +} + +net::LoadState MockNetworkTransaction::GetLoadState() const { + if (data_cursor_) + return net::LOAD_STATE_READING_RESPONSE; + return net::LOAD_STATE_IDLE; +} + +uint64 MockNetworkTransaction::GetUploadProgress() const { + return 0; +} + +void MockNetworkTransaction::CallbackLater(net::CompletionCallback* callback, + int result) { + MessageLoop::current()->PostTask(FROM_HERE, task_factory_.NewRunnableMethod( + &MockNetworkTransaction::RunCallback, callback, result)); +} + +void MockNetworkTransaction::RunCallback(net::CompletionCallback* callback, + int result) { + callback->Run(result); +} + +MockNetworkLayer::MockNetworkLayer() : transaction_count_(0) {} + +MockNetworkLayer::~MockNetworkLayer() {} + +int MockNetworkLayer::CreateTransaction( + scoped_ptr<net::HttpTransaction>* trans) { + transaction_count_++; + trans->reset(new MockNetworkTransaction()); + return net::OK; +} + +net::HttpCache* MockNetworkLayer::GetCache() { + return NULL; +} + +net::HttpNetworkSession* MockNetworkLayer::GetSession() { + return NULL; +} + +void MockNetworkLayer::Suspend(bool suspend) {} //----------------------------------------------------------------------------- // helpers diff --git a/net/http/http_transaction_unittest.h b/net/http/http_transaction_unittest.h index 3149534..0a11bb5 100644 --- a/net/http/http_transaction_unittest.h +++ b/net/http/http_transaction_unittest.h @@ -8,15 +8,11 @@ #include "net/http/http_transaction.h" -#include <algorithm> #include <string> #include "base/callback.h" #include "base/compiler_specific.h" -#include "base/message_loop.h" #include "base/string16.h" -#include "base/string_util.h" -#include "base/stringprintf.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" @@ -27,6 +23,10 @@ #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" +namespace net { +class IOBuffer; +} + //----------------------------------------------------------------------------- // mock transaction data @@ -97,12 +97,7 @@ struct ScopedMockTransaction : MockTransaction { class MockHttpRequest : public net::HttpRequestInfo { public: - explicit MockHttpRequest(const MockTransaction& t) { - url = GURL(t.url); - method = t.method; - extra_headers.AddHeadersFromString(t.request_headers); - load_flags = t.load_flags; - } + explicit MockHttpRequest(const MockTransaction& t); }; //----------------------------------------------------------------------------- @@ -110,25 +105,11 @@ class MockHttpRequest : public net::HttpRequestInfo { class TestTransactionConsumer : public CallbackRunner< Tuple1<int> > { public: - explicit TestTransactionConsumer(net::HttpTransactionFactory* factory) - : state_(IDLE), - trans_(NULL), - error_(net::OK) { - // Disregard the error code. - factory->CreateTransaction(&trans_); - ++quit_counter_; - } - - ~TestTransactionConsumer() { - } + explicit TestTransactionConsumer(net::HttpTransactionFactory* factory); + virtual ~TestTransactionConsumer(); void Start(const net::HttpRequestInfo* request, - const net::BoundNetLog& net_log) { - state_ = STARTING; - int result = trans_->Start(request, this, net_log); - if (result != net::ERR_IO_PENDING) - DidStart(result); - } + const net::BoundNetLog& net_log); bool is_done() const { return state_ == DONE; } int error() const { return error_; } @@ -139,60 +120,22 @@ class TestTransactionConsumer : public CallbackRunner< Tuple1<int> > { const std::string& content() const { return content_; } private: - // Callback implementation: - virtual void RunWithParams(const Tuple1<int>& params) { - int result = params.a; - switch (state_) { - case STARTING: - DidStart(result); - break; - case READING: - DidRead(result); - break; - default: - NOTREACHED(); - } - } - - void DidStart(int result) { - if (result != net::OK) { - DidFinish(result); - } else { - Read(); - } - } - - void DidRead(int result) { - if (result <= 0) { - DidFinish(result); - } else { - content_.append(read_buf_->data(), result); - Read(); - } - } - - void DidFinish(int result) { - state_ = DONE; - error_ = result; - if (--quit_counter_ == 0) - MessageLoop::current()->Quit(); - } - - void Read() { - state_ = READING; - read_buf_ = new net::IOBuffer(1024); - int result = trans_->Read(read_buf_, 1024, this); - if (result != net::ERR_IO_PENDING) - DidRead(result); - } - enum State { IDLE, STARTING, READING, DONE - } state_; + }; + + void DidStart(int result); + void DidRead(int result); + void DidFinish(int result); + void Read(); + + // Callback implementation: + virtual void RunWithParams(const Tuple1<int>& params); + State state_; scoped_ptr<net::HttpTransaction> trans_; std::string content_; scoped_refptr<net::IOBuffer> read_buf_; @@ -210,107 +153,38 @@ class TestTransactionConsumer : public CallbackRunner< Tuple1<int> > { // HttpCache implementation. class MockNetworkTransaction : public net::HttpTransaction { public: - MockNetworkTransaction() : - ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)), data_cursor_(0) { - } + MockNetworkTransaction(); + virtual ~MockNetworkTransaction(); virtual int Start(const net::HttpRequestInfo* request, net::CompletionCallback* callback, - const net::BoundNetLog& net_log) { - const MockTransaction* t = FindMockTransaction(request->url); - if (!t) - return net::ERR_FAILED; - - 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; + const net::BoundNetLog& net_log); - 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_.ssl_info.cert_status = t->cert_status; - data_ = resp_data; - test_mode_ = t->test_mode; - - if (test_mode_ & TEST_MODE_SYNC_NET_START) - return net::OK; - - CallbackLater(callback, net::OK); - return net::ERR_IO_PENDING; - } - - virtual int RestartIgnoringLastError(net::CompletionCallback* callback) { - return net::ERR_FAILED; - } + virtual int RestartIgnoringLastError(net::CompletionCallback* callback); virtual int RestartWithCertificate(net::X509Certificate* client_cert, - net::CompletionCallback* callback) { - return net::ERR_FAILED; - } + net::CompletionCallback* callback); virtual int RestartWithAuth(const string16& username, const string16& password, - net::CompletionCallback* callback) { - return net::ERR_FAILED; - } + net::CompletionCallback* callback); - virtual bool IsReadyToRestartForAuth() { - return false; - } + virtual bool IsReadyToRestartForAuth(); virtual int Read(net::IOBuffer* buf, int buf_len, - net::CompletionCallback* callback) { - int data_len = static_cast<int>(data_.size()); - int num = std::min(buf_len, data_len - data_cursor_); - if (num) { - memcpy(buf->data(), data_.data() + data_cursor_, num); - data_cursor_ += num; - } - if (test_mode_ & TEST_MODE_SYNC_NET_READ) - return num; - - CallbackLater(callback, num); - return net::ERR_IO_PENDING; - } + net::CompletionCallback* callback); - virtual void StopCaching() {} + virtual void StopCaching(); - virtual const net::HttpResponseInfo* GetResponseInfo() const { - return &response_; - } + virtual const net::HttpResponseInfo* GetResponseInfo() const; - virtual net::LoadState GetLoadState() const { - if (data_cursor_) - return net::LOAD_STATE_READING_RESPONSE; - return net::LOAD_STATE_IDLE; - } + virtual net::LoadState GetLoadState() const; - virtual uint64 GetUploadProgress() const { - return 0; - } + virtual uint64 GetUploadProgress() const; private: - void CallbackLater(net::CompletionCallback* callback, int result) { - MessageLoop::current()->PostTask(FROM_HERE, task_factory_.NewRunnableMethod( - &MockNetworkTransaction::RunCallback, callback, result)); - } - void RunCallback(net::CompletionCallback* callback, int result) { - callback->Run(result); - } + void CallbackLater(net::CompletionCallback* callback, int result); + void RunCallback(net::CompletionCallback* callback, int result); ScopedRunnableMethodFactory<MockNetworkTransaction> task_factory_; net::HttpResponseInfo response_; @@ -321,32 +195,21 @@ class MockNetworkTransaction : public net::HttpTransaction { class MockNetworkLayer : public net::HttpTransactionFactory { public: - MockNetworkLayer() : transaction_count_(0) { - } - - virtual int CreateTransaction(scoped_ptr<net::HttpTransaction>* trans) { - transaction_count_++; - trans->reset(new MockNetworkTransaction()); - return net::OK; - } - - virtual net::HttpCache* GetCache() { - return NULL; - } - - virtual net::HttpNetworkSession* GetSession() { - return NULL; - } - - virtual void Suspend(bool suspend) {} + MockNetworkLayer(); + virtual ~MockNetworkLayer(); int transaction_count() const { return transaction_count_; } + // net::HttpTransactionFactory: + virtual int CreateTransaction(scoped_ptr<net::HttpTransaction>* trans); + virtual net::HttpCache* GetCache(); + virtual net::HttpNetworkSession* GetSession(); + virtual void Suspend(bool suspend); + private: int transaction_count_; }; - //----------------------------------------------------------------------------- // helpers diff --git a/net/http/http_util.cc b/net/http/http_util.cc index bf56136..0f28c83 100644 --- a/net/http/http_util.cc +++ b/net/http/http_util.cc @@ -674,9 +674,14 @@ void HttpUtil::BuildRequestHeaders(const HttpRequestInfo* request_info, // Add a content length header? if (upload_data_stream) { - request_headers->SetHeader( - HttpRequestHeaders::kContentLength, - base::Uint64ToString(upload_data_stream->size())); + if (upload_data_stream->is_chunked()) { + request_headers->SetHeader( + HttpRequestHeaders::kTransferEncoding, "chunked"); + } else { + request_headers->SetHeader( + HttpRequestHeaders::kContentLength, + base::Uint64ToString(upload_data_stream->size())); + } } else if (request_info->method == "POST" || request_info->method == "PUT" || request_info->method == "HEAD") { // An empty POST/PUT request still needs a content length. As for HEAD, diff --git a/net/http/mock_gssapi_library_posix.cc b/net/http/mock_gssapi_library_posix.cc index ec69964..5ae4721 100644 --- a/net/http/mock_gssapi_library_posix.cc +++ b/net/http/mock_gssapi_library_posix.cc @@ -188,12 +188,70 @@ void GssContextMockImpl::Assign( open = other.open; } +MockGSSAPILibrary::SecurityContextQuery::SecurityContextQuery() + : expected_package(), + response_code(0), + minor_response_code(0), + context_info() { + expected_input_token.length = 0; + expected_input_token.value = NULL; + output_token.length = 0; + output_token.value = NULL; +} + +MockGSSAPILibrary::SecurityContextQuery::SecurityContextQuery( + const std::string& in_expected_package, + OM_uint32 in_response_code, + OM_uint32 in_minor_response_code, + const test::GssContextMockImpl& in_context_info, + const char* in_expected_input_token, + const char* in_output_token) + : expected_package(in_expected_package), + response_code(in_response_code), + minor_response_code(in_minor_response_code), + context_info(in_context_info) { + if (in_expected_input_token) { + expected_input_token.length = strlen(in_expected_input_token); + expected_input_token.value = const_cast<char*>(in_expected_input_token); + } else { + expected_input_token.length = 0; + expected_input_token.value = NULL; + } + + if (in_output_token) { + output_token.length = strlen(in_output_token); + output_token.value = const_cast<char*>(in_output_token); + } else { + output_token.length = 0; + output_token.value = NULL; + } +} + +MockGSSAPILibrary::SecurityContextQuery::~SecurityContextQuery() {} + MockGSSAPILibrary::MockGSSAPILibrary() { } MockGSSAPILibrary::~MockGSSAPILibrary() { } +void MockGSSAPILibrary::ExpectSecurityContext( + const std::string& expected_package, + OM_uint32 response_code, + OM_uint32 minor_response_code, + const GssContextMockImpl& context_info, + const gss_buffer_desc& expected_input_token, + const gss_buffer_desc& output_token) { + SecurityContextQuery security_query; + security_query.expected_package = expected_package; + security_query.response_code = response_code; + security_query.minor_response_code = minor_response_code; + security_query.context_info.Assign(context_info); + security_query.expected_input_token = expected_input_token; + security_query.output_token = output_token; + expected_security_queries_.push_back(security_query); +} + bool MockGSSAPILibrary::Init() { return true; } @@ -417,23 +475,6 @@ OM_uint32 MockGSSAPILibrary::inquire_context( return GSS_S_COMPLETE; } -void MockGSSAPILibrary::ExpectSecurityContext( - const std::string& expected_package, - OM_uint32 response_code, - OM_uint32 minor_response_code, - const GssContextMockImpl& context_info, - const gss_buffer_desc& expected_input_token, - const gss_buffer_desc& output_token) { - SecurityContextQuery security_query; - security_query.expected_package = expected_package; - security_query.response_code = response_code; - security_query.minor_response_code = minor_response_code; - security_query.context_info.Assign(context_info); - security_query.expected_input_token = expected_input_token; - security_query.output_token = output_token; - expected_security_queries_.push_back(security_query); -} - } // namespace test } // namespace net diff --git a/net/http/mock_gssapi_library_posix.h b/net/http/mock_gssapi_library_posix.h index 15e14f2..aad5de8 100644 --- a/net/http/mock_gssapi_library_posix.h +++ b/net/http/mock_gssapi_library_posix.h @@ -45,10 +45,70 @@ class GssContextMockImpl { // the system GSSAPI library calls. class MockGSSAPILibrary : public GSSAPILibrary { public: + // Unit tests need access to this. "Friend"ing didn't help. + struct SecurityContextQuery { + SecurityContextQuery(); + SecurityContextQuery(const std::string& expected_package, + OM_uint32 response_code, + OM_uint32 minor_response_code, + const test::GssContextMockImpl& context_info, + const char* expected_input_token, + const char* output_token); + ~SecurityContextQuery(); + + std::string expected_package; + OM_uint32 response_code; + OM_uint32 minor_response_code; + test::GssContextMockImpl context_info; + gss_buffer_desc expected_input_token; + gss_buffer_desc output_token; + }; MockGSSAPILibrary(); virtual ~MockGSSAPILibrary(); + // Establishes an expectation for a |init_sec_context()| call. + // + // Each expectation established by |ExpectSecurityContext()| must be + // matched by a call to |init_sec_context()| during the lifetime of + // the MockGSSAPILibrary. The |expected_package| argument must equal the + // value associated with the |target_name| argument to |init_sec_context()| + // for there to be a match. The expectations also establish an explicit + // ordering. + // + // For example, this sequence will be successful. + // MockGSSAPILibrary lib; + // lib.ExpectSecurityContext("NTLM", ...) + // lib.ExpectSecurityContext("Negotiate", ...) + // lib.init_sec_context("NTLM", ...) + // lib.init_sec_context("Negotiate", ...) + // + // This sequence will fail since the queries do not occur in the order + // established by the expectations. + // MockGSSAPILibrary lib; + // lib.ExpectSecurityContext("NTLM", ...) + // lib.ExpectSecurityContext("Negotiate", ...) + // lib.init_sec_context("Negotiate", ...) + // lib.init_sec_context("NTLM", ...) + // + // This sequence will fail because there were not enough queries. + // MockGSSAPILibrary lib; + // lib.ExpectSecurityContext("NTLM", ...) + // lib.ExpectSecurityContext("Negotiate", ...) + // lib.init_sec_context("NTLM", ...) + // + // |response_code| is used as the return value for |init_sec_context()|. + // If |response_code| is GSS_S_COMPLETE, + // + // |context_info| is the expected value of the |**context_handle| in after + // |init_sec_context()| returns. + void ExpectSecurityContext(const std::string& expected_package, + OM_uint32 response_code, + OM_uint32 minor_response_code, + const test::GssContextMockImpl& context_info, + const gss_buffer_desc& expected_input_token, + const gss_buffer_desc& output_token); + // GSSAPILibrary methods: // Initializes the library, including any necessary dynamic libraries. @@ -116,58 +176,6 @@ class MockGSSAPILibrary : public GSSAPILibrary { int* locally_initiated, int* open); - // Establishes an expectation for a |init_sec_context()| call. - // - // Each expectation established by |ExpectSecurityContext()| must be - // matched by a call to |init_sec_context()| during the lifetime of - // the MockGSSAPILibrary. The |expected_package| argument must equal the - // value associated with the |target_name| argument to |init_sec_context()| - // for there to be a match. The expectations also establish an explicit - // ordering. - // - // For example, this sequence will be successful. - // MockGSSAPILibrary lib; - // lib.ExpectSecurityContext("NTLM", ...) - // lib.ExpectSecurityContext("Negotiate", ...) - // lib.init_sec_context("NTLM", ...) - // lib.init_sec_context("Negotiate", ...) - // - // This sequence will fail since the queries do not occur in the order - // established by the expectations. - // MockGSSAPILibrary lib; - // lib.ExpectSecurityContext("NTLM", ...) - // lib.ExpectSecurityContext("Negotiate", ...) - // lib.init_sec_context("Negotiate", ...) - // lib.init_sec_context("NTLM", ...) - // - // This sequence will fail because there were not enough queries. - // MockGSSAPILibrary lib; - // lib.ExpectSecurityContext("NTLM", ...) - // lib.ExpectSecurityContext("Negotiate", ...) - // lib.init_sec_context("NTLM", ...) - // - // |response_code| is used as the return value for |init_sec_context()|. - // If |response_code| is GSS_S_COMPLETE, - // - // |context_info| is the expected value of the |**context_handle| in after - // |init_sec_context()| returns. - void ExpectSecurityContext(const std::string& expected_package, - OM_uint32 response_code, - OM_uint32 minor_response_code, - const test::GssContextMockImpl& context_info, - const gss_buffer_desc& expected_input_token, - const gss_buffer_desc& output_token); - - // Unit tests need access to this. "Friend"ing didn't help. - struct SecurityContextQuery { - std::string expected_package; - OM_uint32 response_code; - OM_uint32 minor_response_code; - test::GssContextMockImpl context_info; - gss_buffer_desc expected_input_token; - gss_buffer_desc output_token; - }; - private: FRIEND_TEST_ALL_PREFIXES(HttpAuthGSSAPIPOSIXTest, GSSAPICycle); diff --git a/net/http/url_security_manager.h b/net/http/url_security_manager.h index c6a5ec9..119d6bd 100644 --- a/net/http/url_security_manager.h +++ b/net/http/url_security_manager.h @@ -74,25 +74,6 @@ class URLSecurityManagerWhitelist : public URLSecurityManager { DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerWhitelist); }; -#if defined(UNIT_TEST) -// An URLSecurityManager which is very permissive. -class URLSecurityManagerAllow : public URLSecurityManager { - public: - URLSecurityManagerAllow() {} - virtual ~URLSecurityManagerAllow() {} - - virtual bool CanUseDefaultCredentials(const GURL& auth_origin) const { - return true; - } - virtual bool CanDelegate(const GURL& auth_origin) const { - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(URLSecurityManagerAllow); -}; -#endif // defined(UNIT_TEST) - } // namespace net #endif // NET_HTTP_URL_SECURITY_MANAGER_H_ |