diff options
author | Ben Murdoch <benm@google.com> | 2010-11-18 18:32:45 +0000 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2010-11-18 18:38:07 +0000 |
commit | 513209b27ff55e2841eac0e4120199c23acce758 (patch) | |
tree | aeba30bb08c5f47c57003544e378a377c297eee6 /net/http | |
parent | 164f7496de0fbee436b385a79ead9e3cb81a50c1 (diff) | |
download | external_chromium-513209b27ff55e2841eac0e4120199c23acce758.zip external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.gz external_chromium-513209b27ff55e2841eac0e4120199c23acce758.tar.bz2 |
Merge Chromium at r65505: Initial merge by git.
Change-Id: I31d8f1d8cd33caaf7f47ffa7350aef42d5fbdb45
Diffstat (limited to 'net/http')
31 files changed, 476 insertions, 254 deletions
diff --git a/net/http/des.cc b/net/http/des.cc index a083149..943f612 100644 --- a/net/http/des.cc +++ b/net/http/des.cc @@ -84,7 +84,14 @@ void DESMakeKey(const uint8* raw, uint8* key) { key[7] = DESSetKeyParity((raw[6] << 1)); } -#if defined(USE_NSS) +#if defined(USE_OPENSSL) + +void DESEncrypt(const uint8* key, const uint8* src, uint8* hash) { + // TODO(joth): When implementing consider splitting up this file by platform. + NOTIMPLEMENTED(); +} + +#elif defined(USE_NSS) void DESEncrypt(const uint8* key, const uint8* src, uint8* hash) { CK_MECHANISM_TYPE cipher_mech = CKM_DES_ECB; diff --git a/net/http/disk_cache_based_ssl_host_info.cc b/net/http/disk_cache_based_ssl_host_info.cc index 10b2e15..cd1cac8 100644 --- a/net/http/disk_cache_based_ssl_host_info.cc +++ b/net/http/disk_cache_based_ssl_host_info.cc @@ -13,8 +13,11 @@ namespace net { DiskCacheBasedSSLHostInfo::DiskCacheBasedSSLHostInfo( - const std::string& hostname, HttpCache* http_cache) - : callback_(new CancelableCompletionCallback<DiskCacheBasedSSLHostInfo>( + const std::string& hostname, + const SSLConfig& ssl_config, + HttpCache* http_cache) + : SSLHostInfo(hostname, ssl_config), + callback_(new CancelableCompletionCallback<DiskCacheBasedSSLHostInfo>( ALLOW_THIS_IN_INITIALIZER_LIST(this), &DiskCacheBasedSSLHostInfo::DoLoop)), state_(GET_BACKEND), diff --git a/net/http/disk_cache_based_ssl_host_info.h b/net/http/disk_cache_based_ssl_host_info.h index 51a4a91..1d53b90 100644 --- a/net/http/disk_cache_based_ssl_host_info.h +++ b/net/http/disk_cache_based_ssl_host_info.h @@ -16,8 +16,9 @@ namespace net { -class IOBuffer; class HttpCache; +class IOBuffer; +struct SSLConfig; // DiskCacheBasedSSLHostInfo fetches information about an SSL host from our // standard disk cache. Since the information is defined to be non-sensitive, @@ -25,7 +26,9 @@ class HttpCache; class DiskCacheBasedSSLHostInfo : public SSLHostInfo, public NonThreadSafe { public: - DiskCacheBasedSSLHostInfo(const std::string& hostname, HttpCache* http_cache); + DiskCacheBasedSSLHostInfo(const std::string& hostname, + const SSLConfig& ssl_config, + HttpCache* http_cache); // Implementation of SSLHostInfo virtual void Start(); diff --git a/net/http/http_auth.h b/net/http/http_auth.h index 3611612..0034b1f 100644 --- a/net/http/http_auth.h +++ b/net/http/http_auth.h @@ -133,7 +133,8 @@ class HttpAuth { // // |headers| must be non-NULL and contain the new HTTP response. // - // |target| specifies whether the headers came from a server or proxy. + // |target| specifies whether the authentication challenge response came + // from a server or a proxy. // // |disabled_schemes| are the authentication schemes to ignore. // diff --git a/net/http/http_auth_handler_basic.cc b/net/http/http_auth_handler_basic.cc index 054357a..35b088f 100644 --- a/net/http/http_auth_handler_basic.cc +++ b/net/http/http_auth_handler_basic.cc @@ -41,7 +41,7 @@ bool HttpAuthHandlerBasic::ParseChallenge( std::string realm; while (parameters.GetNext()) { if (LowerCaseEqualsASCII(parameters.name(), "realm")) - realm = parameters.unquoted_value(); + realm = parameters.value(); } if (!parameters.valid()) diff --git a/net/http/http_auth_handler_digest.cc b/net/http/http_auth_handler_digest.cc index f7f178c..82aa9af 100644 --- a/net/http/http_auth_handler_digest.cc +++ b/net/http/http_auth_handler_digest.cc @@ -221,7 +221,7 @@ HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge( while (parameters.GetNext()) { if (!LowerCaseEqualsASCII(parameters.name(), "stale")) continue; - if (LowerCaseEqualsASCII(parameters.unquoted_value(), "true")) + if (LowerCaseEqualsASCII(parameters.value(), "true")) return HttpAuth::AUTHORIZATION_RESULT_STALE; } @@ -266,14 +266,9 @@ bool HttpAuthHandlerDigest::ParseChallenge( // Loop through all the properties. while (parameters.GetNext()) { - if (parameters.value().empty()) { - DVLOG(1) << "Invalid digest property"; - return false; - } - // FAIL -- couldn't parse a property. if (!ParseChallengeProperty(parameters.name(), - parameters.unquoted_value())) + parameters.value())) return false; } diff --git a/net/http/http_auth_unittest.cc b/net/http/http_auth_unittest.cc index 3068135..63ae3b0 100644 --- a/net/http/http_auth_unittest.cc +++ b/net/http/http_auth_unittest.cc @@ -149,22 +149,22 @@ TEST(HttpAuthTest, ChooseBestChallenge) { TEST(HttpAuthTest, HandleChallengeResponse) { std::string challenge_used; - const char* kMockChallenge = + const char* const kMockChallenge = "HTTP/1.1 401 Unauthorized\n" "WWW-Authenticate: Mock token_here\n"; - const char* kBasicChallenge = + const char* const kBasicChallenge = "HTTP/1.1 401 Unauthorized\n" "WWW-Authenticate: Basic realm=\"happy\"\n"; - const char* kMissingChallenge = + const char* const kMissingChallenge = "HTTP/1.1 401 Unauthorized\n"; - const char* kEmptyChallenge = + const char* const kEmptyChallenge = "HTTP/1.1 401 Unauthorized\n" "WWW-Authenticate: \n"; - const char* kBasicAndMockChallenges = + const char* const kBasicAndMockChallenges = "HTTP/1.1 401 Unauthorized\n" "WWW-Authenticate: Basic realm=\"happy\"\n" "WWW-Authenticate: Mock token_here\n"; - const char* kTwoMockChallenges = + const char* const kTwoMockChallenges = "HTTP/1.1 401 Unauthorized\n" "WWW-Authenticate: Mock token_a\n" "WWW-Authenticate: Mock token_b\n"; @@ -249,9 +249,7 @@ TEST(HttpAuthTest, ChallengeTokenizer) { EXPECT_TRUE(parameters.GetNext()); EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("foobar"), parameters.unquoted_value()); - EXPECT_EQ(std::string("\"foobar\""), parameters.value()); - EXPECT_TRUE(parameters.value_is_quoted()); + EXPECT_EQ(std::string("foobar"), parameters.value()); EXPECT_FALSE(parameters.GetNext()); } @@ -268,8 +266,6 @@ TEST(HttpAuthTest, ChallengeTokenizerNoQuotes) { EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("realm"), parameters.name()); EXPECT_EQ(std::string("foobar@baz.com"), parameters.value()); - EXPECT_EQ(std::string("foobar@baz.com"), parameters.unquoted_value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_FALSE(parameters.GetNext()); } @@ -286,8 +282,6 @@ TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotes) { EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("realm"), parameters.name()); EXPECT_EQ(std::string("foobar@baz.com"), parameters.value()); - EXPECT_EQ(std::string("foobar@baz.com"), parameters.unquoted_value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_FALSE(parameters.GetNext()); } @@ -304,7 +298,6 @@ TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesNoValue) { EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("realm"), parameters.name()); EXPECT_EQ(std::string(""), parameters.value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_FALSE(parameters.GetNext()); } @@ -322,15 +315,13 @@ TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesSpaces) { EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("realm"), parameters.name()); EXPECT_EQ(std::string("foo bar"), parameters.value()); - EXPECT_EQ(std::string("foo bar"), parameters.unquoted_value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_FALSE(parameters.GetNext()); } // Use multiple name=value properties with mismatching quote marks in the last // value. TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesMultiple) { - std::string challenge_str = "Digest qop=, algorithm=md5, realm=\"foo"; + std::string challenge_str = "Digest qop=auth-int, algorithm=md5, realm=\"foo"; HttpAuth::ChallengeTokenizer challenge(challenge_str.begin(), challenge_str.end()); HttpUtil::NameValuePairsIterator parameters = challenge.param_pairs(); @@ -340,20 +331,15 @@ TEST(HttpAuthTest, ChallengeTokenizerMismatchedQuotesMultiple) { EXPECT_TRUE(parameters.GetNext()); EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("qop"), parameters.name()); - EXPECT_EQ(std::string(""), parameters.value()); - EXPECT_FALSE(parameters.value_is_quoted()); + EXPECT_EQ(std::string("auth-int"), parameters.value()); EXPECT_TRUE(parameters.GetNext()); EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("algorithm"), parameters.name()); EXPECT_EQ(std::string("md5"), parameters.value()); - EXPECT_EQ(std::string("md5"), parameters.unquoted_value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_TRUE(parameters.GetNext()); EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("realm"), parameters.name()); EXPECT_EQ(std::string("foo"), parameters.value()); - EXPECT_EQ(std::string("foo"), parameters.unquoted_value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_FALSE(parameters.GetNext()); } @@ -366,12 +352,8 @@ TEST(HttpAuthTest, ChallengeTokenizerNoValue) { EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("Digest"), challenge.scheme()); - EXPECT_TRUE(parameters.GetNext()); - EXPECT_TRUE(parameters.valid()); - EXPECT_EQ(std::string("qop"), parameters.name()); - EXPECT_EQ(std::string(""), parameters.value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_FALSE(parameters.GetNext()); + EXPECT_FALSE(parameters.valid()); } // Specify multiple properties, comma separated. @@ -388,18 +370,16 @@ TEST(HttpAuthTest, ChallengeTokenizerMultiple) { EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("algorithm"), parameters.name()); EXPECT_EQ(std::string("md5"), parameters.value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_TRUE(parameters.GetNext()); EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("realm"), parameters.name()); - EXPECT_EQ(std::string("Oblivion"), parameters.unquoted_value()); - EXPECT_TRUE(parameters.value_is_quoted()); + EXPECT_EQ(std::string("Oblivion"), parameters.value()); EXPECT_TRUE(parameters.GetNext()); EXPECT_TRUE(parameters.valid()); EXPECT_EQ(std::string("qop"), parameters.name()); EXPECT_EQ(std::string("auth-int"), parameters.value()); - EXPECT_FALSE(parameters.value_is_quoted()); EXPECT_FALSE(parameters.GetNext()); + EXPECT_TRUE(parameters.valid()); } // Use a challenge which has no property. diff --git a/net/http/http_basic_stream.cc b/net/http/http_basic_stream.cc index ecd692b..433b08e 100644 --- a/net/http/http_basic_stream.cc +++ b/net/http/http_basic_stream.cc @@ -4,33 +4,48 @@ #include "net/http/http_basic_stream.h" +#include "base/stringprintf.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/http/http_request_headers.h" +#include "net/http/http_request_info.h" #include "net/http/http_stream_parser.h" +#include "net/http/http_util.h" #include "net/socket/client_socket_handle.h" namespace net { -HttpBasicStream::HttpBasicStream(ClientSocketHandle* connection) +HttpBasicStream::HttpBasicStream(ClientSocketHandle* connection, + bool using_proxy) : read_buf_(new GrowableIOBuffer()), - connection_(connection) { + connection_(connection), + using_proxy_(using_proxy), + request_info_(NULL) { } int HttpBasicStream::InitializeStream(const HttpRequestInfo* request_info, const BoundNetLog& net_log, CompletionCallback* callback) { + request_info_ = request_info; parser_.reset(new HttpStreamParser(connection_.get(), request_info, read_buf_, net_log)); return OK; } -int HttpBasicStream::SendRequest(const std::string& headers, +int HttpBasicStream::SendRequest(const HttpRequestHeaders& headers, UploadDataStream* request_body, HttpResponseInfo* response, CompletionCallback* callback) { DCHECK(parser_.get()); - return parser_->SendRequest(headers, request_body, response, callback); + const std::string path = using_proxy_ ? + HttpUtil::SpecForRequest(request_info_->url) : + HttpUtil::PathForRequest(request_info_->url); + request_line_ = base::StringPrintf("%s %s HTTP/1.1\r\n", + request_info_->method.c_str(), + path.c_str()) + headers.ToString(); + + return parser_->SendRequest(request_line_, request_body, response, callback); } HttpBasicStream::~HttpBasicStream() {} @@ -60,7 +75,7 @@ HttpStream* HttpBasicStream::RenewStreamForAuth() { DCHECK(IsResponseBodyComplete()); DCHECK(!IsMoreDataBuffered()); parser_.reset(); - return new HttpBasicStream(connection_.release()); + return new HttpBasicStream(connection_.release(), using_proxy_); } bool HttpBasicStream::IsResponseBodyComplete() const { diff --git a/net/http/http_basic_stream.h b/net/http/http_basic_stream.h index ffab967..83136c0 100644 --- a/net/http/http_basic_stream.h +++ b/net/http/http_basic_stream.h @@ -23,13 +23,14 @@ class ClientSocketHandle; class GrowableIOBuffer; class HttpResponseInfo; struct HttpRequestInfo; +class HttpRequestHeaders; class HttpStreamParser; class IOBuffer; class UploadDataStream; class HttpBasicStream : public HttpStream { public: - explicit HttpBasicStream(ClientSocketHandle* connection); + HttpBasicStream(ClientSocketHandle* connection, bool using_proxy); virtual ~HttpBasicStream(); // HttpStream methods: @@ -37,7 +38,7 @@ class HttpBasicStream : public HttpStream { const BoundNetLog& net_log, CompletionCallback* callback); - virtual int SendRequest(const std::string& headers, + virtual int SendRequest(const HttpRequestHeaders& headers, UploadDataStream* request_body, HttpResponseInfo* response, CompletionCallback* callback); @@ -76,6 +77,12 @@ class HttpBasicStream : public HttpStream { scoped_ptr<ClientSocketHandle> connection_; + bool using_proxy_; + + std::string request_line_; + + const HttpRequestInfo* request_info_; + DISALLOW_COPY_AND_ASSIGN(HttpBasicStream); }; diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc index eca95ef..0372222 100644 --- a/net/http/http_cache.cc +++ b/net/http/http_cache.cc @@ -267,8 +267,9 @@ class HttpCache::SSLHostInfoFactoryAdaptor : public SSLHostInfoFactory { : http_cache_(http_cache) { } - SSLHostInfo* GetForHost(const std::string& hostname) { - return new DiskCacheBasedSSLHostInfo(hostname, http_cache_); + SSLHostInfo* GetForHost(const std::string& hostname, + const SSLConfig& ssl_config) { + return new DiskCacheBasedSSLHostInfo(hostname, ssl_config, http_cache_); } private: @@ -601,11 +602,10 @@ HttpCache::ActiveEntry* HttpCache::FindActiveEntry(const std::string& key) { } HttpCache::ActiveEntry* HttpCache::ActivateEntry( - const std::string& key, disk_cache::Entry* disk_entry) { - DCHECK(!FindActiveEntry(key)); + DCHECK(!FindActiveEntry(disk_entry->GetKey())); ActiveEntry* entry = new ActiveEntry(disk_entry); - active_entries_[key] = entry; + active_entries_[disk_entry->GetKey()] = entry; return entry; } @@ -990,7 +990,7 @@ void HttpCache::OnIOComplete(int result, PendingOp* pending_op) { fail_requests = true; } else if (item->IsValid()) { key = pending_op->disk_entry->GetKey(); - entry = ActivateEntry(key, pending_op->disk_entry); + entry = ActivateEntry(pending_op->disk_entry); } else { // The writer transaction is gone. if (op == WI_CREATE_ENTRY) diff --git a/net/http/http_cache.h b/net/http/http_cache.h index 255d7b6..0ce22e5 100644 --- a/net/http/http_cache.h +++ b/net/http/http_cache.h @@ -257,10 +257,8 @@ class HttpCache : public HttpTransactionFactory, ActiveEntry* FindActiveEntry(const std::string& key); // Creates a new ActiveEntry and starts tracking it. |disk_entry| is the disk - // cache entry that corresponds to the desired |key|. - // TODO(rvargas): remove the |key| argument. - ActiveEntry* ActivateEntry(const std::string& key, - disk_cache::Entry* disk_entry); + // cache entry. + ActiveEntry* ActivateEntry(disk_cache::Entry* disk_entry); // Deletes an ActiveEntry. void DeactivateEntry(ActiveEntry* entry); diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index 58288f4..873ccf4 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc @@ -1801,7 +1801,7 @@ int HttpCache::Transaction::WriteResponseInfoToEntry(bool truncated) { DCHECK_EQ(200, response_.headers->response_code()); } - scoped_refptr<PickledIOBuffer> data = new PickledIOBuffer(); + scoped_refptr<PickledIOBuffer> data(new PickledIOBuffer()); response_.Persist(data->pickle(), skip_transient_headers, truncated); data->Done(); diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc index eec8098..f71cec9 100644 --- a/net/http/http_cache_unittest.cc +++ b/net/http/http_cache_unittest.cc @@ -573,7 +573,7 @@ class MockHttpCache { int size = disk_entry->GetDataSize(0); TestCompletionCallback cb; - scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(size); + scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(size)); int rv = disk_entry->ReadData(0, 0, buffer, size, &cb); rv = cb.GetResult(rv); EXPECT_EQ(size, rv); @@ -593,8 +593,8 @@ class MockHttpCache { &pickle, skip_transient_headers, response_truncated); TestCompletionCallback cb; - scoped_refptr<net::WrappedIOBuffer> data = new net::WrappedIOBuffer( - reinterpret_cast<const char*>(pickle.data())); + scoped_refptr<net::WrappedIOBuffer> data(new net::WrappedIOBuffer( + reinterpret_cast<const char*>(pickle.data()))); int len = static_cast<int>(pickle.size()); int rv = disk_entry->WriteData(0, 0, data, len, &cb, true); @@ -951,8 +951,8 @@ const MockTransaction kRangeGET_TransactionOK = { void Verify206Response(std::string response, int start, int end) { std::string raw_headers(net::HttpUtil::AssembleRawHeaders(response.data(), response.size())); - scoped_refptr<net::HttpResponseHeaders> headers = - new net::HttpResponseHeaders(raw_headers); + scoped_refptr<net::HttpResponseHeaders> headers( + new net::HttpResponseHeaders(raw_headers)); ASSERT_EQ(206, headers->response_code()); @@ -1854,7 +1854,7 @@ TEST(HttpCache, SimpleGET_AbandonedCacheRead) { rv = callback.WaitForResult(); ASSERT_EQ(net::OK, rv); - scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(256); + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); rv = trans->Read(buf, 256, &callback); EXPECT_EQ(net::ERR_IO_PENDING, rv); @@ -3544,7 +3544,7 @@ TEST(HttpCache, RangeGET_Cancel) { EXPECT_EQ(1, cache.disk_cache()->create_count()); // Make sure that the entry has some data stored. - scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(10); + scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(10)); rv = c->trans->Read(buf, buf->size(), &c->callback); if (rv == net::ERR_IO_PENDING) rv = c->callback.WaitForResult(); @@ -3585,7 +3585,7 @@ TEST(HttpCache, RangeGET_Cancel2) { // Make sure that we revalidate the entry and read from the cache (a single // read will return while waiting for the network). - scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(5); + scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(5)); rv = c->trans->Read(buf, buf->size(), &c->callback); EXPECT_EQ(5, c->callback.GetResult(rv)); rv = c->trans->Read(buf, buf->size(), &c->callback); @@ -3631,7 +3631,7 @@ TEST(HttpCache, RangeGET_Cancel3) { // Make sure that we revalidate the entry and read from the cache (a single // read will return while waiting for the network). - scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(5); + scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(5)); rv = c->trans->Read(buf, buf->size(), &c->callback); EXPECT_EQ(5, c->callback.GetResult(rv)); rv = c->trans->Read(buf, buf->size(), &c->callback); @@ -4029,7 +4029,7 @@ TEST(HttpCache, DoomOnDestruction2) { EXPECT_EQ(1, cache.disk_cache()->create_count()); // Make sure that the entry has some data stored. - scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(10); + scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(10)); rv = c->trans->Read(buf, buf->size(), &c->callback); if (rv == net::ERR_IO_PENDING) rv = c->callback.WaitForResult(); @@ -4073,7 +4073,7 @@ TEST(HttpCache, DoomOnDestruction3) { EXPECT_EQ(1, cache.disk_cache()->create_count()); // Make sure that the entry has some data stored. - scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(10); + scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(10)); rv = c->trans->Read(buf, buf->size(), &c->callback); if (rv == net::ERR_IO_PENDING) rv = c->callback.WaitForResult(); @@ -4117,7 +4117,7 @@ TEST(HttpCache, Set_Truncated_Flag) { EXPECT_EQ(1, cache.disk_cache()->create_count()); // Make sure that the entry has some data stored. - scoped_refptr<net::IOBufferWithSize> buf = new net::IOBufferWithSize(10); + scoped_refptr<net::IOBufferWithSize> buf(new net::IOBufferWithSize(10)); rv = c->trans->Read(buf, buf->size(), &c->callback); if (rv == net::ERR_IO_PENDING) rv = c->callback.WaitForResult(); diff --git a/net/http/http_chunked_decoder.cc b/net/http/http_chunked_decoder.cc index 455c9ed..d5b16dd 100644 --- a/net/http/http_chunked_decoder.cc +++ b/net/http/http_chunked_decoder.cc @@ -192,7 +192,7 @@ bool HttpChunkedDecoder::ParseChunkSize(const char* start, int len, int* out) { return false; int parsed_number; - bool ok = base::HexStringToInt(std::string(start, len), &parsed_number); + bool ok = base::HexStringToInt(start, start + len, &parsed_number); if (ok && parsed_number >= 0) { *out = parsed_number; return true; diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc index e0b9c39..bc2d322 100644 --- a/net/http/http_network_transaction.cc +++ b/net/http/http_network_transaction.cc @@ -89,6 +89,7 @@ HttpNetworkTransaction::HttpNetworkTransaction(HttpNetworkSession* session) request_(NULL), headers_valid_(false), logged_response_time_(false), + request_headers_(), read_buf_len_(0), next_state_(STATE_NONE), establishing_tunnel_(false) { @@ -284,7 +285,7 @@ int HttpNetworkTransaction::Read(IOBuffer* buf, int buf_len, State next_state = STATE_NONE; - scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); + scoped_refptr<HttpResponseHeaders> headers(GetResponseHeaders()); if (headers_valid_ && headers.get() && stream_request_.get()) { // We're trying to read the body of the response but we're still trying // to establish an SSL tunnel through the proxy. We can't read these @@ -535,6 +536,8 @@ int HttpNetworkTransaction::DoCreateStreamComplete(int result) { if (result == OK) { next_state_ = STATE_INIT_STREAM; DCHECK(stream_.get()); + } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { + result = HandleCertificateRequest(result); } // At this point we are done with the stream_request_. @@ -552,9 +555,6 @@ int HttpNetworkTransaction::DoInitStreamComplete(int result) { if (result == OK) { next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; } else { - if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) - result = HandleCertificateRequest(result); - if (result < 0) result = HandleIOError(result); @@ -624,38 +624,22 @@ int HttpNetworkTransaction::DoSendRequest() { // This is constructed lazily (instead of within our Start method), so that // we have proxy info available. - if (request_headers_.empty() && !response_.was_fetched_via_spdy) { + if (request_headers_.IsEmpty()) { bool using_proxy = (proxy_info_.is_http()|| proxy_info_.is_https()) && !is_https_request(); - const std::string path = using_proxy ? - HttpUtil::SpecForRequest(request_->url) : - HttpUtil::PathForRequest(request_->url); - std::string request_line = base::StringPrintf( - "%s %s HTTP/1.1\r\n", request_->method.c_str(), path.c_str()); - - HttpRequestHeaders request_headers; HttpUtil::BuildRequestHeaders(request_, request_body, auth_controllers_, ShouldApplyServerAuth(), ShouldApplyProxyAuth(), using_proxy, - &request_headers); + &request_headers_); if (session_->network_delegate()) - session_->network_delegate()->OnSendHttpRequest(&request_headers); - - if (net_log_.IsLoggingAllEvents()) { - net_log_.AddEvent( - NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS, - new NetLogHttpRequestParameter(request_line, request_headers)); - } - - request_headers_ = request_line + request_headers.ToString(); - } else { - if (net_log_.IsLoggingAllEvents()) { - net_log_.AddEvent( - NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS, - new NetLogHttpRequestParameter(request_->url.spec(), - request_->extra_headers)); - } + session_->network_delegate()->OnSendHttpRequest(&request_headers_); + } + if (net_log_.IsLoggingAllEvents()) { + net_log_.AddEvent( + NetLog::TYPE_HTTP_TRANSACTION_SEND_REQUEST_HEADERS, + make_scoped_refptr(new NetLogHttpRequestParameter( + request_->url.spec(), request_->extra_headers))); } headers_valid_ = false; @@ -748,7 +732,7 @@ int HttpNetworkTransaction::DoReadHeadersComplete(int result) { if (net_log_.IsLoggingAllEvents()) { net_log_.AddEvent( NetLog::TYPE_HTTP_TRANSACTION_READ_RESPONSE_HEADERS, - new NetLogHttpResponseParameter(response_.headers)); + make_scoped_refptr(new NetLogHttpResponseParameter(response_.headers))); } if (response_.headers->GetParsedHttpVersion() < HttpVersion(1, 0)) { @@ -1050,7 +1034,7 @@ void HttpNetworkTransaction::ResetStateForAuthRestart() { read_buf_ = NULL; read_buf_len_ = 0; headers_valid_ = false; - request_headers_.clear(); + request_headers_.Clear(); response_ = HttpResponseInfo(); establishing_tunnel_ = false; } @@ -1080,7 +1064,7 @@ void HttpNetworkTransaction::ResetConnectionAndRequestForResend() { // We need to clear request_headers_ because it contains the real request // headers, but we may need to resend the CONNECT request first to recreate // the SSL tunnel. - request_headers_.clear(); + request_headers_.Clear(); next_state_ = STATE_CREATE_STREAM; // Resend the request. } @@ -1094,7 +1078,7 @@ bool HttpNetworkTransaction::ShouldApplyServerAuth() const { } int HttpNetworkTransaction::HandleAuthChallenge() { - scoped_refptr<HttpResponseHeaders> headers = GetResponseHeaders(); + scoped_refptr<HttpResponseHeaders> headers(GetResponseHeaders()); DCHECK(headers); int status = headers->response_code(); @@ -1105,6 +1089,11 @@ int HttpNetworkTransaction::HandleAuthChallenge() { if (target == HttpAuth::AUTH_PROXY && proxy_info_.is_direct()) return ERR_UNEXPECTED_PROXY_AUTH; + // This case can trigger when an HTTPS server responds with a 407 status + // code through a non-authenticating proxy. + if (!auth_controllers_[target].get()) + return ERR_UNEXPECTED_PROXY_AUTH; + int rv = auth_controllers_[target]->HandleAuthChallenge( headers, (request_->load_flags & LOAD_DO_NOT_SEND_AUTH_DATA) != 0, false, net_log_); diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h index ce0c5bd..255fcdf 100644 --- a/net/http/http_network_transaction.h +++ b/net/http/http_network_transaction.h @@ -17,6 +17,7 @@ #include "net/base/request_priority.h" #include "net/base/ssl_config_service.h" #include "net/http/http_auth.h" +#include "net/http/http_request_headers.h" #include "net/http/http_response_info.h" #include "net/http/http_transaction.h" #include "net/http/stream_factory.h" @@ -222,7 +223,7 @@ class HttpNetworkTransaction : public HttpTransaction, SSLConfig ssl_config_; - std::string request_headers_; + HttpRequestHeaders request_headers_; // The size in bytes of the buffer we use to drain the response body that // we want to throw away. The response body is typically a small error diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 2066264..b060d7c 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -460,6 +460,67 @@ TEST_F(HttpNetworkTransactionTest, } TEST_F(HttpNetworkTransactionTest, + DuplicateContentLengthHeadersNoTransferEncoding) { + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Content-Length: 5\r\n"), + MockRead("Content-Length: 5\r\n\r\n"), + MockRead("Hello"), + }; + SimpleGetHelperResult out = SimpleGetHelper(data_reads, + arraysize(data_reads)); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("Hello", out.response_data); +} + +TEST_F(HttpNetworkTransactionTest, + ComplexContentLengthHeadersNoTransferEncoding) { + // More than 2 dupes. + { + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Content-Length: 5\r\n"), + MockRead("Content-Length: 5\r\n"), + MockRead("Content-Length: 5\r\n\r\n"), + MockRead("Hello"), + }; + SimpleGetHelperResult out = SimpleGetHelper(data_reads, + arraysize(data_reads)); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.1 200 OK", out.status_line); + EXPECT_EQ("Hello", out.response_data); + } + // HTTP/1.0 + { + MockRead data_reads[] = { + MockRead("HTTP/1.0 200 OK\r\n"), + MockRead("Content-Length: 5\r\n"), + MockRead("Content-Length: 5\r\n"), + MockRead("Content-Length: 5\r\n\r\n"), + MockRead("Hello"), + }; + SimpleGetHelperResult out = SimpleGetHelper(data_reads, + arraysize(data_reads)); + EXPECT_EQ(OK, out.rv); + EXPECT_EQ("HTTP/1.0 200 OK", out.status_line); + EXPECT_EQ("Hello", out.response_data); + } + // 2 dupes and one mismatched. + { + MockRead data_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), + MockRead("Content-Length: 10\r\n"), + MockRead("Content-Length: 10\r\n"), + MockRead("Content-Length: 5\r\n\r\n"), + }; + SimpleGetHelperResult out = SimpleGetHelper(data_reads, + arraysize(data_reads)); + EXPECT_EQ(ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH, out.rv); + } +} + +TEST_F(HttpNetworkTransactionTest, MultipleContentLengthHeadersTransferEncoding) { MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\n"), @@ -544,7 +605,7 @@ TEST_F(HttpNetworkTransactionTest, Head) { TEST_F(HttpNetworkTransactionTest, ReuseConnection) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); MockRead data_reads[] = { MockRead("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n"), @@ -733,7 +794,7 @@ TEST_F(HttpNetworkTransactionTest, EmptyResponse) { void HttpNetworkTransactionTest::KeepAliveConnectionResendRequestTest( const MockRead& read_failure) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); HttpRequestInfo request; request.method = "GET"; @@ -850,7 +911,7 @@ TEST_F(HttpNetworkTransactionTest, NonKeepAliveConnectionEOF) { // reading the body. TEST_F(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); HttpRequestInfo request; request.method = "GET"; @@ -1707,6 +1768,66 @@ TEST_F(HttpNetworkTransactionTest, UnexpectedProxyAuth) { EXPECT_EQ(ERR_UNEXPECTED_PROXY_AUTH, rv); } +// Tests when an HTTPS server (non-proxy) returns a 407 (proxy-authentication) +// through a non-authenticating proxy. The request should fail with +// ERR_UNEXPECTED_PROXY_AUTH. +// Note that it is impossible to detect if an HTTP server returns a 407 through +// a non-authenticating proxy - there is nothing to indicate whether the +// response came from the proxy or the server, so it is treated as if the proxy +// issued the challenge. +TEST_F(HttpNetworkTransactionTest, HttpsServerRequestsProxyAuthThroughProxy) { + 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" + "Host: www.google.com\r\n" + "Proxy-Connection: keep-alive\r\n\r\n"), + + MockWrite("GET / HTTP/1.1\r\n" + "Host: www.google.com\r\n" + "Connection: keep-alive\r\n\r\n"), + }; + + MockRead data_reads1[] = { + MockRead("HTTP/1.1 200 Connection Established\r\n\r\n"), + + MockRead("HTTP/1.1 407 Unauthorized\r\n"), + MockRead("Proxy-Authenticate: Basic realm=\"MyRealm1\"\r\n"), + MockRead("\r\n"), + MockRead(false, OK), + }; + + StaticSocketDataProvider data1(data_reads1, arraysize(data_reads1), + data_writes1, arraysize(data_writes1)); + session_deps.socket_factory.AddSocketDataProvider(&data1); + SSLSocketDataProvider ssl(true, OK); + session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); + + TestCompletionCallback callback1; + + scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); + + int rv = trans->Start(&request, &callback1, log.bound()); + EXPECT_EQ(ERR_IO_PENDING, rv); + + rv = callback1.WaitForResult(); + EXPECT_EQ(ERR_UNEXPECTED_PROXY_AUTH, rv); + size_t pos = ExpectLogContainsSomewhere( + log.entries(), 0, NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, + NetLog::PHASE_NONE); + ExpectLogContainsSomewhere( + log.entries(), pos, + NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, + NetLog::PHASE_NONE); +} // Test a simple get through an HTTPS Proxy. TEST_F(HttpNetworkTransactionTest, HttpsProxyGet) { @@ -3189,7 +3310,7 @@ TEST_F(HttpNetworkTransactionTest, ResendRequestOnWriteBodyError) { request[1].load_flags = 0; SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); // The first socket is used for transaction 1 and the first attempt of // transaction 2. @@ -3457,7 +3578,7 @@ TEST_F(HttpNetworkTransactionTest, WrongAuthIdentityInURL) { // Test that previously tried username/passwords for a realm get re-used. TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); // Transaction 1: authenticate (foo, bar) on MyRealm1 { @@ -3850,7 +3971,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthCacheAndPreauth) { // are started with the same nonce. TEST_F(HttpNetworkTransactionTest, DigestPreAuthNonceCount) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); HttpAuthHandlerDigest::SetFixedCnonce(true); // Transaction 1: authenticate (foo, bar) on MyRealm1 @@ -3991,7 +4112,7 @@ TEST_F(HttpNetworkTransactionTest, ResetStateForRestart) { // Setup some state (which we expect ResetStateForRestart() will clear). trans->read_buf_ = new IOBuffer(15); trans->read_buf_len_ = 15; - trans->request_headers_ = "Authorization: NTLM"; + trans->request_headers_.SetHeader("Authorization", "NTLM"); // Setup state in response_ HttpResponseInfo* response = &trans->response_; @@ -4004,7 +4125,7 @@ TEST_F(HttpNetworkTransactionTest, ResetStateForRestart) { HttpRequestInfo request; std::string temp("HTTP/1.1 200 OK\nVary: foo, bar\n\n"); std::replace(temp.begin(), temp.end(), '\n', '\0'); - scoped_refptr<HttpResponseHeaders> headers = new HttpResponseHeaders(temp); + scoped_refptr<HttpResponseHeaders> headers(new HttpResponseHeaders(temp)); request.extra_headers.SetHeader("Foo", "1"); request.extra_headers.SetHeader("bar", "23"); EXPECT_TRUE(response->vary_data.Init(request, *headers)); @@ -4016,7 +4137,7 @@ TEST_F(HttpNetworkTransactionTest, ResetStateForRestart) { // Verify that the state that needed to be reset, has been reset. EXPECT_TRUE(trans->read_buf_.get() == NULL); EXPECT_EQ(0, trans->read_buf_len_); - EXPECT_EQ(0U, trans->request_headers_.size()); + EXPECT_TRUE(trans->request_headers_.IsEmpty()); EXPECT_TRUE(response->auth_challenge.get() == NULL); EXPECT_TRUE(response->headers.get() == NULL); EXPECT_FALSE(response->was_cached); @@ -5317,7 +5438,7 @@ TEST_F(HttpNetworkTransactionTest, BypassHostCacheOnRefresh3) { // Make sure we can handle an error when writing the request. TEST_F(HttpNetworkTransactionTest, RequestWriteError) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); HttpRequestInfo request; request.method = "GET"; @@ -5346,7 +5467,7 @@ TEST_F(HttpNetworkTransactionTest, RequestWriteError) { // Check that a connection closed after the start of the headers finishes ok. TEST_F(HttpNetworkTransactionTest, ConnectionClosedAfterStartOfHeaders) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); HttpRequestInfo request; request.method = "GET"; @@ -5388,7 +5509,7 @@ TEST_F(HttpNetworkTransactionTest, ConnectionClosedAfterStartOfHeaders) { // restart does the right thing. TEST_F(HttpNetworkTransactionTest, DrainResetOK) { SessionDependencies session_deps; - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); HttpRequestInfo request; request.method = "GET"; @@ -6412,8 +6533,8 @@ TEST_F(HttpNetworkTransactionTest, scoped_refptr<SpdySession> spdy_session = session->spdy_session_pool()->Get(pair, session->mutable_spdy_settings(), BoundNetLog()); - scoped_refptr<TCPSocketParams> tcp_params = - new TCPSocketParams("www.google.com", 443, MEDIUM, GURL(), false); + scoped_refptr<TCPSocketParams> tcp_params( + new TCPSocketParams("www.google.com", 443, MEDIUM, GURL(), false)); scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); EXPECT_EQ(ERR_IO_PENDING, @@ -6887,7 +7008,7 @@ TEST_F(HttpNetworkTransactionTest, MultiRoundAuth) { origin, BoundNetLog()); auth_factory->set_mock_handler(auth_handler, HttpAuth::AUTH_SERVER); - scoped_refptr<HttpNetworkSession> session = CreateSession(&session_deps); + scoped_refptr<HttpNetworkSession> session(CreateSession(&session_deps)); scoped_ptr<HttpTransaction> trans(new HttpNetworkTransaction(session)); int rv = OK; @@ -7588,8 +7709,8 @@ TEST_F(HttpNetworkTransactionTest, PreconnectWithExistingSpdySession) { scoped_refptr<SpdySession> spdy_session = session->spdy_session_pool()->Get(pair, session->mutable_spdy_settings(), BoundNetLog()); - scoped_refptr<TCPSocketParams> tcp_params = - new TCPSocketParams("www.google.com", 443, MEDIUM, GURL(), false); + scoped_refptr<TCPSocketParams> tcp_params( + new TCPSocketParams("www.google.com", 443, MEDIUM, GURL(), false)); TestCompletionCallback callback; scoped_ptr<ClientSocketHandle> connection(new ClientSocketHandle); diff --git a/net/http/http_proxy_client_socket.cc b/net/http/http_proxy_client_socket.cc index b5ede3d..186b459 100644 --- a/net/http/http_proxy_client_socket.cc +++ b/net/http/http_proxy_client_socket.cc @@ -185,6 +185,14 @@ bool HttpProxyClientSocket::WasEverUsed() const { return false; } +bool HttpProxyClientSocket::UsingTCPFastOpen() const { + if (transport_.get() && transport_->socket()) { + return transport_->socket()->UsingTCPFastOpen(); + } + NOTREACHED(); + return false; +} + int HttpProxyClientSocket::Read(IOBuffer* buf, int buf_len, CompletionCallback* callback) { DCHECK(!user_callback_); @@ -335,8 +343,8 @@ int HttpProxyClientSocket::DoSendRequest() { if (net_log_.IsLoggingAllEvents()) { net_log_.AddEvent( NetLog::TYPE_HTTP_TRANSACTION_SEND_TUNNEL_HEADERS, - new NetLogHttpRequestParameter( - request_line, request_headers)); + make_scoped_refptr(new NetLogHttpRequestParameter( + request_line, request_headers))); } request_headers_ = request_line + request_headers.ToString(); } @@ -373,7 +381,7 @@ int HttpProxyClientSocket::DoReadHeadersComplete(int result) { if (net_log_.IsLoggingAllEvents()) { net_log_.AddEvent( NetLog::TYPE_HTTP_TRANSACTION_READ_TUNNEL_RESPONSE_HEADERS, - new NetLogHttpResponseParameter(response_.headers)); + make_scoped_refptr(new NetLogHttpResponseParameter(response_.headers))); } switch (response_.headers->response_code()) { diff --git a/net/http/http_proxy_client_socket.h b/net/http/http_proxy_client_socket.h index 6530285..b42c78b 100644 --- a/net/http/http_proxy_client_socket.h +++ b/net/http/http_proxy_client_socket.h @@ -78,6 +78,7 @@ class HttpProxyClientSocket : public ClientSocket { virtual void SetSubresourceSpeculation(); virtual void SetOmniboxSpeculation(); virtual bool WasEverUsed() const; + virtual bool UsingTCPFastOpen() const; // Socket methods: virtual int Read(IOBuffer* buf, int buf_len, CompletionCallback* callback); diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc index e1ca2fe..ae84ecc 100644 --- a/net/http/http_proxy_client_socket_pool_unittest.cc +++ b/net/http/http_proxy_client_socket_pool_unittest.cc @@ -244,7 +244,8 @@ TEST_P(HttpProxyClientSocketPoolTest, NeedAuth) { CreateMockWrite(*rst, 2, true), }; scoped_ptr<spdy::SpdyFrame> resp( - ConstructSpdySynReplyError("407 Proxy Authentication Required", 1)); + ConstructSpdySynReplyError( + "407 Proxy Authentication Required", NULL, 0, 1)); MockRead spdy_reads[] = { CreateMockWrite(*resp, 1, true), MockRead(true, 0, 3) diff --git a/net/http/http_response_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc index d57952d..d8c9bb7 100644 --- a/net/http/http_response_body_drainer_unittest.cc +++ b/net/http/http_response_body_drainer_unittest.cc @@ -78,7 +78,7 @@ class MockHttpStream : public HttpStream { CompletionCallback* callback) { return ERR_UNEXPECTED; } - virtual int SendRequest(const std::string& request_headers, + virtual int SendRequest(const HttpRequestHeaders& request_headers, UploadDataStream* request_body, HttpResponseInfo* response, CompletionCallback* callback) { diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index 99c5404..c2d098c 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -484,7 +484,7 @@ bool HttpResponseHeaders::HasHeaderValue(const std::string& name, while (EnumerateHeader(&iter, name, &temp)) { if (value.size() == temp.size() && std::equal(temp.begin(), temp.end(), value.begin(), - CaseInsensitiveCompare<char>())) + base::CaseInsensitiveCompare<char>())) return true; } return false; @@ -598,7 +598,7 @@ void HttpResponseHeaders::ParseStatusLine( raw_headers_.push_back(' '); raw_headers_.append(code, p); raw_headers_.push_back(' '); - base::StringToInt(std::string(code, p), &response_code_); + base::StringToInt(code, p, &response_code_); // Skip whitespace. while (*p == ' ') @@ -629,7 +629,7 @@ size_t HttpResponseHeaders::FindHeader(size_t from, const std::string::const_iterator& name_end = parsed_[i].name_end; if (static_cast<size_t>(name_end - name_begin) == search.size() && std::equal(name_begin, name_end, search.begin(), - CaseInsensitiveCompare<char>())) + base::CaseInsensitiveCompare<char>())) return i; } @@ -973,7 +973,9 @@ bool HttpResponseHeaders::GetMaxAgeValue(TimeDelta* result) const { value.begin() + kMaxAgePrefixLen, kMaxAgePrefix)) { int64 seconds; - base::StringToInt64(value.substr(kMaxAgePrefixLen), &seconds); + base::StringToInt64(value.begin() + kMaxAgePrefixLen, + value.end(), + &seconds); *result = TimeDelta::FromSeconds(seconds); return true; } @@ -1148,9 +1150,9 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position, byte_range_resp_spec.begin() + minus_position; HttpUtil::TrimLWS(&first_byte_pos_begin, &first_byte_pos_end); - bool ok = base::StringToInt64( - std::string(first_byte_pos_begin, first_byte_pos_end), - first_byte_position); + bool ok = base::StringToInt64(first_byte_pos_begin, + first_byte_pos_end, + first_byte_position); // Obtain last-byte-pos. std::string::const_iterator last_byte_pos_begin = @@ -1159,9 +1161,9 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position, byte_range_resp_spec.end(); HttpUtil::TrimLWS(&last_byte_pos_begin, &last_byte_pos_end); - ok &= base::StringToInt64( - std::string(last_byte_pos_begin, last_byte_pos_end), - last_byte_position); + ok &= base::StringToInt64(last_byte_pos_begin, + last_byte_pos_end, + last_byte_position); if (!ok) { *first_byte_position = *last_byte_position = -1; return false; @@ -1184,9 +1186,9 @@ bool HttpResponseHeaders::GetContentRange(int64* first_byte_position, if (LowerCaseEqualsASCII(instance_length_begin, instance_length_end, "*")) { return false; - } else if (!base::StringToInt64( - std::string(instance_length_begin, instance_length_end), - instance_length)) { + } else if (!base::StringToInt64(instance_length_begin, + instance_length_end, + instance_length)) { *instance_length = -1; return false; } diff --git a/net/http/http_response_headers_unittest.cc b/net/http/http_response_headers_unittest.cc index 18cd1c4..6986723 100644 --- a/net/http/http_response_headers_unittest.cc +++ b/net/http/http_response_headers_unittest.cc @@ -51,8 +51,8 @@ void TestCommon(const TestData& test) { string expected_headers(test.expected_headers); string headers; - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(raw_headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(raw_headers)); parsed->GetNormalizedHeaders(&headers); // Transform to readable output format (so it's easier to see diffs). @@ -281,7 +281,7 @@ TEST(HttpResponseHeadersTest, GetNormalizedHeader) { "Cache-control: private\n" "cache-Control: no-store\n"; HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); std::string value; EXPECT_TRUE(parsed->GetNormalizedHeader("cache-control", &value)); @@ -448,15 +448,15 @@ TEST(HttpResponseHeadersTest, Persist) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { std::string headers = tests[i].raw_headers; HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed1 = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed1( + new HttpResponseHeaders(headers)); Pickle pickle; parsed1->Persist(&pickle, tests[i].options); void* iter = NULL; - scoped_refptr<HttpResponseHeaders> parsed2 = - new HttpResponseHeaders(pickle, &iter); + scoped_refptr<HttpResponseHeaders> parsed2( + new HttpResponseHeaders(pickle, &iter)); std::string h2; parsed2->GetNormalizedHeaders(&h2); @@ -472,7 +472,7 @@ TEST(HttpResponseHeadersTest, EnumerateHeader_Coalesced) { "Cache-control:private , no-cache=\"set-cookie,server\" \n" "cache-Control: no-store\n"; HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); void* iter = NULL; std::string value; @@ -493,7 +493,7 @@ TEST(HttpResponseHeadersTest, EnumerateHeader_Challenge) { "WWW-Authenticate:Digest realm=foobar, nonce=x, domain=y\n" "WWW-Authenticate:Basic realm=quatar\n"; HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); void* iter = NULL; std::string value; @@ -512,7 +512,7 @@ TEST(HttpResponseHeadersTest, EnumerateHeader_DateValued) { "Date: Tue, 07 Aug 2007 23:10:55 GMT\n" "Last-Modified: Wed, 01 Aug 2007 23:23:45 GMT\n"; HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); std::string value; EXPECT_TRUE(parsed->EnumerateHeader(NULL, "date", &value)); @@ -656,8 +656,8 @@ TEST(HttpResponseHeadersTest, GetMimeType) { for (size_t i = 0; i < arraysize(tests); ++i) { string headers(tests[i].raw_headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); std::string value; EXPECT_EQ(tests[i].has_mimetype, parsed->GetMimeType(&value)); @@ -807,8 +807,8 @@ TEST(HttpResponseHeadersTest, RequiresValidation) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string headers(tests[i].headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); bool requires_validation = parsed->RequiresValidation(request_time, response_time, current_time); @@ -871,13 +871,13 @@ TEST(HttpResponseHeadersTest, Update) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string orig_headers(tests[i].orig_headers); HeadersToRaw(&orig_headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(orig_headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(orig_headers)); string new_headers(tests[i].new_headers); HeadersToRaw(&new_headers); - scoped_refptr<HttpResponseHeaders> new_parsed = - new HttpResponseHeaders(new_headers); + scoped_refptr<HttpResponseHeaders> new_parsed( + new HttpResponseHeaders(new_headers)); parsed->Update(*new_parsed); @@ -917,8 +917,8 @@ TEST(HttpResponseHeadersTest, EnumerateHeaderLines) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string headers(tests[i].headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); string name, value, lines; @@ -1001,8 +1001,8 @@ TEST(HttpResponseHeadersTest, IsRedirect) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string headers(tests[i].headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); std::string location; EXPECT_EQ(parsed->IsRedirect(&location), tests[i].is_redirect); @@ -1087,8 +1087,8 @@ TEST(HttpResponseHeadersTest, GetContentLength) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string headers(tests[i].headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); EXPECT_EQ(tests[i].expected_len, parsed->GetContentLength()); } @@ -1338,8 +1338,8 @@ TEST(HttpResponseHeaders, GetContentRange) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string headers(tests[i].headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); int64 first_byte_position; int64 last_byte_position; @@ -1420,8 +1420,8 @@ TEST(HttpResponseHeadersTest, IsKeepAlive) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string headers(tests[i].headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); EXPECT_EQ(tests[i].expected_keep_alive, parsed->IsKeepAlive()); } @@ -1473,8 +1473,8 @@ TEST(HttpResponseHeadersTest, HasStrongValidators) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string headers(tests[i].headers); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(headers)); EXPECT_EQ(tests[i].expected_result, parsed->HasStrongValidators()) << "Failed test case " << i; @@ -1484,14 +1484,14 @@ TEST(HttpResponseHeadersTest, HasStrongValidators) { TEST(HttpResponseHeadersTest, GetStatusText) { std::string headers("HTTP/1.1 404 Not Found"); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText()); } TEST(HttpResponseHeadersTest, GetStatusTextMissing) { std::string headers("HTTP/1.1 404"); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); // Since the status line gets normalized, we have OK EXPECT_EQ(std::string("OK"), parsed->GetStatusText()); } @@ -1499,14 +1499,14 @@ TEST(HttpResponseHeadersTest, GetStatusTextMissing) { TEST(HttpResponseHeadersTest, GetStatusTextMultiSpace) { std::string headers("HTTP/1.0 404 Not Found"); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); EXPECT_EQ(std::string("Not Found"), parsed->GetStatusText()); } TEST(HttpResponseHeadersTest, GetStatusBadStatusLine) { std::string headers("Foo bar."); HeadersToRaw(&headers); - scoped_refptr<HttpResponseHeaders> parsed = new HttpResponseHeaders(headers); + scoped_refptr<HttpResponseHeaders> parsed(new HttpResponseHeaders(headers)); // The bad status line would have gotten rewritten as // HTTP/1.0 200 OK. EXPECT_EQ(std::string("OK"), parsed->GetStatusText()); @@ -1545,8 +1545,8 @@ TEST(HttpResponseHeadersTest, AddHeader) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string orig_headers(tests[i].orig_headers); HeadersToRaw(&orig_headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(orig_headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(orig_headers)); string new_header(tests[i].new_header); parsed->AddHeader(new_header); @@ -1590,8 +1590,8 @@ TEST(HttpResponseHeadersTest, RemoveHeader) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string orig_headers(tests[i].orig_headers); HeadersToRaw(&orig_headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(orig_headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(orig_headers)); string name(tests[i].to_remove); parsed->RemoveHeader(name); @@ -1645,8 +1645,8 @@ TEST(HttpResponseHeadersTest, ReplaceStatus) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { string orig_headers(tests[i].orig_headers); HeadersToRaw(&orig_headers); - scoped_refptr<HttpResponseHeaders> parsed = - new HttpResponseHeaders(orig_headers); + scoped_refptr<HttpResponseHeaders> parsed( + new HttpResponseHeaders(orig_headers)); string name(tests[i].new_status); parsed->ReplaceStatusLine(name); diff --git a/net/http/http_response_info.cc b/net/http/http_response_info.cc index dc90d1f..00bfb92 100644 --- a/net/http/http_response_info.cc +++ b/net/http/http_response_info.cc @@ -182,6 +182,7 @@ void HttpResponseInfo::Persist(Pickle* pickle, } if (ssl_info.security_bits != -1) flags |= RESPONSE_INFO_HAS_SECURITY_BITS; + // TODO(wtc): we should persist ssl_info.connection_status. if (vary_data.is_valid()) flags |= RESPONSE_INFO_HAS_VARY_DATA; if (response_truncated) diff --git a/net/http/http_stream.h b/net/http/http_stream.h index 5aa84e0..16f84cc 100644 --- a/net/http/http_stream.h +++ b/net/http/http_stream.h @@ -22,12 +22,13 @@ namespace net { class BoundNetLog; +class HttpRequestHeaders; +struct HttpRequestInfo; class HttpResponseInfo; class IOBuffer; class SSLCertRequestInfo; class SSLInfo; class UploadDataStream; -struct HttpRequestInfo; class HttpStream { public: @@ -45,7 +46,7 @@ class HttpStream { // synchronously, in which case the result will be passed to the callback // when available. Returns OK on success. The HttpStream takes ownership // of the request_body. - virtual int SendRequest(const std::string& request_headers, + virtual int SendRequest(const HttpRequestHeaders& request_headers, UploadDataStream* request_body, HttpResponseInfo* response, CompletionCallback* callback) = 0; diff --git a/net/http/http_stream_parser.cc b/net/http/http_stream_parser.cc index 0e7610c..d3e4abd 100644 --- a/net/http/http_stream_parser.cc +++ b/net/http/http_stream_parser.cc @@ -53,7 +53,7 @@ int HttpStreamParser::SendRequest(const std::string& headers, DCHECK(response); response_ = response; - scoped_refptr<StringIOBuffer> headers_io_buf = new StringIOBuffer(headers); + scoped_refptr<StringIOBuffer> headers_io_buf(new StringIOBuffer(headers)); request_headers_ = new DrainableIOBuffer(headers_io_buf, headers_io_buf->size()); request_body_.reset(request_body); @@ -510,13 +510,20 @@ int HttpStreamParser::DoParseResponseHeaders(int end_offset) { void* it = NULL; const std::string content_length_header("Content-Length"); - std::string ignored_header_value; + std::string content_length_value; if (!headers->HasHeader("Transfer-Encoding") && headers->EnumerateHeader( - &it, content_length_header, &ignored_header_value) && - headers->EnumerateHeader( - &it, content_length_header, &ignored_header_value)) { - return ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH; + &it, content_length_header, &content_length_value)) { + // Ok, there's no Transfer-Encoding header and there's at least one + // Content-Length header. Check if there are any more Content-Length + // headers, and if so, make sure they have the same value. Otherwise, it's + // a possible response smuggling attack. + std::string content_length_value2; + while (headers->EnumerateHeader( + &it, content_length_header, &content_length_value2)) { + if (content_length_value != content_length_value2) + return ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH; + } } response_->headers = headers; diff --git a/net/http/http_stream_request.cc b/net/http/http_stream_request.cc index 2409d86..71bbdda 100644 --- a/net/http/http_stream_request.cc +++ b/net/http/http_stream_request.cc @@ -21,7 +21,6 @@ #include "net/socket/client_socket_pool.h" #include "net/socket/socks_client_socket_pool.h" #include "net/socket/ssl_client_socket.h" -#include "net/socket/ssl_client_socket.h" #include "net/socket/ssl_client_socket_pool.h" #include "net/socket/tcp_client_socket_pool.h" #include "net/spdy/spdy_http_stream.h" @@ -481,9 +480,9 @@ int HttpStreamRequest::DoInitConnection() { } else { ProxyServer proxy_server = proxy_info()->proxy_server(); proxy_host_port.reset(new HostPortPair(proxy_server.host_port_pair())); - scoped_refptr<TCPSocketParams> proxy_tcp_params = + scoped_refptr<TCPSocketParams> proxy_tcp_params( new TCPSocketParams(*proxy_host_port, request_info().priority, - request_info().referrer, disable_resolver_cache); + request_info().referrer, disable_resolver_cache)); if (proxy_info()->is_http() || proxy_info()->is_https()) { GURL authentication_url = request_info().url; @@ -727,8 +726,12 @@ int HttpStreamRequest::DoCreateStream() { if (connection_->socket() && !connection_->is_reused()) SetSocketMotivation(); + const ProxyServer& proxy_server = proxy_info()->proxy_server(); + if (!using_spdy_) { - stream_.reset(new HttpBasicStream(connection_.release())); + bool using_proxy = (proxy_info()->is_http() || proxy_info()->is_https()) && + request_info().url.SchemeIs("http"); + stream_.reset(new HttpBasicStream(connection_.release(), using_proxy)); return OK; } @@ -738,7 +741,6 @@ int HttpStreamRequest::DoCreateStream() { SpdySessionPool* spdy_pool = session_->spdy_session_pool(); scoped_refptr<SpdySession> spdy_session; - const ProxyServer& proxy_server = proxy_info()->proxy_server(); HostPortProxyPair pair(endpoint_, proxy_server); if (spdy_pool->HasSession(pair)) { // We have a SPDY session to the origin server. This might be a direct @@ -850,17 +852,17 @@ scoped_refptr<SSLSocketParams> HttpStreamRequest::GenerateSslParams( if (request_info().load_flags & LOAD_VERIFY_EV_CERT) ssl_config()->verify_ev_cert = true; - if (proxy_info()->proxy_server().scheme() == ProxyServer::SCHEME_HTTP || - proxy_info()->proxy_server().scheme() == ProxyServer::SCHEME_HTTPS) { - ssl_config()->mitm_proxies_allowed = true; - } + if (proxy_info()->proxy_server().scheme() == ProxyServer::SCHEME_HTTP || + proxy_info()->proxy_server().scheme() == ProxyServer::SCHEME_HTTPS) { + ssl_config()->mitm_proxies_allowed = true; + } - scoped_refptr<SSLSocketParams> ssl_params = + scoped_refptr<SSLSocketParams> ssl_params( new SSLSocketParams(tcp_params, socks_params, http_proxy_params, proxy_scheme, hostname, *ssl_config(), load_flags, force_spdy_always_ && force_spdy_over_ssl_, - want_spdy_over_npn); + want_spdy_over_npn)); return ssl_params; } diff --git a/net/http/http_transaction_unittest.cc b/net/http/http_transaction_unittest.cc index 4d9daa5..518c1f8 100644 --- a/net/http/http_transaction_unittest.cc +++ b/net/http/http_transaction_unittest.cc @@ -147,7 +147,7 @@ int ReadTransaction(net::HttpTransaction* trans, std::string* result) { std::string content; do { - scoped_refptr<net::IOBuffer> buf = new net::IOBuffer(256); + scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(256)); rv = trans->Read(buf, 256, &callback); if (rv == net::ERR_IO_PENDING) rv = callback.WaitForResult(); diff --git a/net/http/http_util.cc b/net/http/http_util.cc index 5c7a4fc..a3d1ec5 100644 --- a/net/http/http_util.cc +++ b/net/http/http_util.cc @@ -292,7 +292,7 @@ bool HttpUtil::HasHeader(const std::string& headers, const char* name) { headers.end(), name, name + name_len, - CaseInsensitiveCompareASCII<char>()); + base::CaseInsensitiveCompareASCII<char>()); if (it == headers.end()) return false; @@ -828,12 +828,12 @@ bool HttpUtil::NameValuePairsIterator::GetNext() { // Scan for the equals sign. std::string::const_iterator equals = std::find(value_begin_, value_end_, '='); if (equals == value_end_ || equals == value_begin_) - return valid_ = false; // Malformed + return valid_ = false; // Malformed, no equals sign // Verify that the equals sign we found wasn't inside of quote marks. for (std::string::const_iterator it = value_begin_; it != equals; ++it) { if (HttpUtil::IsQuote(*it)) - return valid_ = false; // Malformed + return valid_ = false; // Malformed, quote appears before equals sign } name_begin_ = value_begin_; @@ -843,25 +843,28 @@ bool HttpUtil::NameValuePairsIterator::GetNext() { TrimLWS(&name_begin_, &name_end_); TrimLWS(&value_begin_, &value_end_); value_is_quoted_ = false; - if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) { + unquoted_value_.clear(); + + if (value_begin_ == value_end_) + return valid_ = false; // Malformed, value is empty + + if (HttpUtil::IsQuote(*value_begin_)) { // Trim surrounding quotemarks off the value - if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) + if (*value_begin_ != *(value_end_ - 1) || value_begin_ + 1 == value_end_) { // NOTE: This is not as graceful as it sounds: // * quoted-pairs will no longer be unquoted // (["\"hello] should give ["hello]). // * Does not detect when the final quote is escaped // (["value\"] should give [value"]) ++value_begin_; // Gracefully recover from mismatching quotes. - else + } else { value_is_quoted_ = true; + // Do not store iterators into this. See declaration of unquoted_value_. + unquoted_value_ = HttpUtil::Unquote(value_begin_, value_end_); + } } return true; } -// If value() has quotemarks, unquote it. -std::string HttpUtil::NameValuePairsIterator::unquoted_value() const { - return HttpUtil::Unquote(value_begin_, value_end_); -} - } // namespace net diff --git a/net/http/http_util.h b/net/http/http_util.h index f41c4e4..2f5bd85 100644 --- a/net/http/http_util.h +++ b/net/http/http_util.h @@ -275,6 +275,9 @@ class HttpUtil { // Each pair consists of a token (the name), an equals sign, and either a // token or quoted-string (the value). Arbitrary HTTP LWS is permitted outside // of and between names, values, and delimiters. + // + // String iterators returned from this class' methods may be invalidated upon + // calls to GetNext() or after the NameValuePairsIterator is destroyed. class NameValuePairsIterator { public: NameValuePairsIterator(std::string::const_iterator begin, @@ -295,15 +298,16 @@ class HttpUtil { std::string name() const { return std::string(name_begin_, name_end_); } // The value of the current name-value pair. - std::string::const_iterator value_begin() const { return value_begin_; } - std::string::const_iterator value_end() const { return value_end_; } - std::string value() const { return std::string(value_begin_, value_end_); } - - // If value() has quotemarks, unquote it. - std::string unquoted_value() const; - - // True if the name-value pair's value has quote marks. - bool value_is_quoted() const { return value_is_quoted_; } + std::string::const_iterator value_begin() const { + return value_is_quoted_ ? unquoted_value_.begin() : value_begin_; + } + std::string::const_iterator value_end() const { + return value_is_quoted_ ? unquoted_value_.end() : value_end_; + } + std::string value() const { + return value_is_quoted_ ? unquoted_value_ : std::string(value_begin_, + value_end_); + } private: HttpUtil::ValuesIterator props_; @@ -318,6 +322,11 @@ class HttpUtil { std::string::const_iterator value_begin_; std::string::const_iterator value_end_; + // Do not store iterators into this string. The NameValuePairsIterator + // is copyable/assignable, and if copied the copy's iterators would point + // into the original's unquoted_value_ member. + std::string unquoted_value_; + bool value_is_quoted_; }; }; diff --git a/net/http/http_util_unittest.cc b/net/http/http_util_unittest.cc index 4ebbd2e..47242d2 100644 --- a/net/http/http_util_unittest.cc +++ b/net/http/http_util_unittest.cc @@ -673,27 +673,48 @@ TEST(HttpUtilTest, ParseRanges) { } namespace { - -void CheckNameValuePair(HttpUtil::NameValuePairsIterator* parser, - bool expect_next, - bool expect_valid, - std::string expected_name, - std::string expected_value, - std::string expected_unquoted_value) { - ASSERT_EQ(expect_next, parser->GetNext()); +void CheckCurrentNameValuePair(HttpUtil::NameValuePairsIterator* parser, + bool expect_valid, + std::string expected_name, + std::string expected_value) { ASSERT_EQ(expect_valid, parser->valid()); - if (!expect_next || !expect_valid) { + if (!expect_valid) { return; } + + // Let's make sure that these never change (i.e., when a quoted value is + // unquoted, it should be cached on the first calls and not regenerated + // later). + std::string::const_iterator first_value_begin = parser->value_begin(); + std::string::const_iterator first_value_end = parser->value_end(); + ASSERT_EQ(expected_name, std::string(parser->name_begin(), parser->name_end())); ASSERT_EQ(expected_name, parser->name()); ASSERT_EQ(expected_value, std::string(parser->value_begin(), parser->value_end())); ASSERT_EQ(expected_value, parser->value()); - ASSERT_EQ(expected_unquoted_value, parser->unquoted_value()); - ASSERT_EQ(expected_value != expected_unquoted_value, - parser->value_is_quoted()); + + // Make sure they didn't/don't change. + ASSERT_TRUE(first_value_begin == parser->value_begin()); + ASSERT_TRUE(first_value_end == parser->value_end()); +} + +void CheckNextNameValuePair(HttpUtil::NameValuePairsIterator* parser, + bool expect_next, + bool expect_valid, + std::string expected_name, + std::string expected_value) { + ASSERT_EQ(expect_next, parser->GetNext()); + ASSERT_EQ(expect_valid, parser->valid()); + if (!expect_next || !expect_valid) { + return; + } + + CheckCurrentNameValuePair(parser, + expect_valid, + expected_name, + expected_value); } void CheckInvalidNameValuePair(std::string valid_part, @@ -731,35 +752,78 @@ void CheckInvalidNameValuePair(std::string valid_part, } // anonymous namespace +TEST(HttpUtilTest, NameValuePairsIteratorCopyAndAssign) { + std::string data = "alpha='\\'a\\''; beta=\" b \"; cappa='c;'; delta=\"d\""; + HttpUtil::NameValuePairsIterator parser_a(data.begin(), data.end(), ';'); + + EXPECT_TRUE(parser_a.valid()); + ASSERT_NO_FATAL_FAILURE( + CheckNextNameValuePair(&parser_a, true, true, "alpha", "'a'")); + + HttpUtil::NameValuePairsIterator parser_b(parser_a); + // a and b now point to same location + ASSERT_NO_FATAL_FAILURE( + CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'")); + ASSERT_NO_FATAL_FAILURE( + CheckCurrentNameValuePair(&parser_a, true, "alpha", "'a'")); + + // advance a, no effect on b + ASSERT_NO_FATAL_FAILURE( + CheckNextNameValuePair(&parser_a, true, true, "beta", " b ")); + ASSERT_NO_FATAL_FAILURE( + CheckCurrentNameValuePair(&parser_b, true, "alpha", "'a'")); + + // assign b the current state of a, no effect on a + parser_b = parser_a; + ASSERT_NO_FATAL_FAILURE( + CheckCurrentNameValuePair(&parser_b, true, "beta", " b ")); + ASSERT_NO_FATAL_FAILURE( + CheckCurrentNameValuePair(&parser_a, true, "beta", " b ")); + + // advance b, no effect on a + ASSERT_NO_FATAL_FAILURE( + CheckNextNameValuePair(&parser_b, true, true, "cappa", "c;")); + ASSERT_NO_FATAL_FAILURE( + CheckCurrentNameValuePair(&parser_a, true, "beta", " b ")); +} + TEST(HttpUtilTest, NameValuePairsIteratorEmptyInput) { std::string data = ""; HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); EXPECT_TRUE(parser.valid()); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, false, true, "", "", "")); + CheckNextNameValuePair(&parser, false, true, "", "")); } TEST(HttpUtilTest, NameValuePairsIterator) { - std::string data = "alpha=1; beta= 2 ;cappa =' 3 ';" - "delta= \" 4 \"; e= \" '5'\"; e=6"; + std::string data = "alpha=1; beta= 2 ;cappa =' 3; ';" + "delta= \" \\\"4\\\" \"; e= \" '5'\"; e=6;" + "f='\\'\\h\\e\\l\\l\\o\\ \\w\\o\\r\\l\\d\\'';" + "g=''; h='hello'"; HttpUtil::NameValuePairsIterator parser(data.begin(), data.end(), ';'); EXPECT_TRUE(parser.valid()); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "alpha", "1", "1")); + CheckNextNameValuePair(&parser, true, true, "alpha", "1")); + ASSERT_NO_FATAL_FAILURE( + CheckNextNameValuePair(&parser, true, true, "beta", "2")); + ASSERT_NO_FATAL_FAILURE( + CheckNextNameValuePair(&parser, true, true, "cappa", " 3; ")); + ASSERT_NO_FATAL_FAILURE( + CheckNextNameValuePair(&parser, true, true, "delta", " \"4\" ")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "beta", "2", "2")); + CheckNextNameValuePair(&parser, true, true, "e", " '5'")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "cappa", "' 3 '", " 3 ")); + CheckNextNameValuePair(&parser, true, true, "e", "6")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "delta", "\" 4 \"", " 4 ")); + CheckNextNameValuePair(&parser, true, true, "f", "'hello world'")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "e", "\" '5'\"", " '5'")); + CheckNextNameValuePair(&parser, true, true, "g", "")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "e", "6", "6")); + CheckNextNameValuePair(&parser, true, true, "h", "hello")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, false, true, "", "", "")); + CheckNextNameValuePair(&parser, false, true, "", "")); } TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) { @@ -768,6 +832,9 @@ TEST(HttpUtilTest, NameValuePairsIteratorIllegalInputs) { ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", "; 'beta'=2")); ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("", "'beta'=2")); + ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", ";beta=")); + ASSERT_NO_FATAL_FAILURE(CheckInvalidNameValuePair("alpha=1", + ";beta=;cappa=2")); // According to the spec this is an error, but it doesn't seem appropriate to // change our behaviour to be less permissive at this time. @@ -783,13 +850,13 @@ TEST(HttpUtilTest, NameValuePairsIteratorExtraSeparators) { EXPECT_TRUE(parser.valid()); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "alpha", "1", "1")); + CheckNextNameValuePair(&parser, true, true, "alpha", "1")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "beta", "2", "2")); + CheckNextNameValuePair(&parser, true, true, "beta", "2")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "cappa", "3", "3")); + CheckNextNameValuePair(&parser, true, true, "cappa", "3")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, false, true, "", "", "")); + CheckNextNameValuePair(&parser, false, true, "", "")); } // See comments on the implementation of NameValuePairsIterator::GetNext @@ -800,7 +867,7 @@ TEST(HttpUtilTest, NameValuePairsIteratorMissingEndQuote) { EXPECT_TRUE(parser.valid()); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, true, true, "name", "value", "value")); + CheckNextNameValuePair(&parser, true, true, "name", "value")); ASSERT_NO_FATAL_FAILURE( - CheckNameValuePair(&parser, false, true, "", "", "")); + CheckNextNameValuePair(&parser, false, true, "", "")); } |