diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-16 09:59:13 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-05-20 10:28:53 +0000 |
commit | 6c11fb357ec39bf087b8b632e2b1e375aef1b38b (patch) | |
tree | c8315530db18a8ee566521c39ab8a6af4f72bc03 /chromium/net/spdy | |
parent | 3ffaed019d0772e59d6cdb2d0d32fe4834c31f72 (diff) | |
download | qtwebengine-chromium-6c11fb357ec39bf087b8b632e2b1e375aef1b38b.tar.gz |
BASELINE: Update Chromium to 74.0.3729.159
Change-Id: I8d2497da544c275415aedd94dd25328d555de811
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/net/spdy')
22 files changed, 655 insertions, 320 deletions
diff --git a/chromium/net/spdy/bidirectional_stream_spdy_impl.cc b/chromium/net/spdy/bidirectional_stream_spdy_impl.cc index 89f217aaec7..f12814553c7 100644 --- a/chromium/net/spdy/bidirectional_stream_spdy_impl.cc +++ b/chromium/net/spdy/bidirectional_stream_spdy_impl.cc @@ -69,8 +69,8 @@ void BidirectionalStreamSpdyImpl::Start( if (!spdy_session_) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&BidirectionalStreamSpdyImpl::NotifyError, - weak_factory_.GetWeakPtr(), ERR_CONNECTION_CLOSED)); + base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError, + weak_factory_.GetWeakPtr(), ERR_CONNECTION_CLOSED)); return; } @@ -122,8 +122,8 @@ void BidirectionalStreamSpdyImpl::SendvData( if (written_end_of_stream_) { LOG(ERROR) << "Writing after end of stream is written."; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError, - weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); + FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError, + weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); return; } @@ -397,14 +397,14 @@ bool BidirectionalStreamSpdyImpl::MaybeHandleStreamClosedInSendData() { // blackhole any pending write data. crbug.com/650438. if (stream_closed_ && closed_stream_status_ == OK) { base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::OnDataSent, - weak_factory_.GetWeakPtr())); + FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::OnDataSent, + weak_factory_.GetWeakPtr())); return true; } LOG(ERROR) << "Trying to send data after stream has been destroyed."; base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&BidirectionalStreamSpdyImpl::NotifyError, - weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); + FROM_HERE, base::BindOnce(&BidirectionalStreamSpdyImpl::NotifyError, + weak_factory_.GetWeakPtr(), ERR_UNEXPECTED)); return true; } diff --git a/chromium/net/spdy/bidirectional_stream_spdy_impl_unittest.cc b/chromium/net/spdy/bidirectional_stream_spdy_impl_unittest.cc index 73d06111fda..1a314666e47 100644 --- a/chromium/net/spdy/bidirectional_stream_spdy_impl_unittest.cc +++ b/chromium/net/spdy/bidirectional_stream_spdy_impl_unittest.cc @@ -13,6 +13,7 @@ #include "base/strings/string_piece.h" #include "base/time/time.h" #include "base/timer/mock_timer.h" +#include "base/timer/timer.h" #include "net/base/load_timing_info.h" #include "net/base/load_timing_info_test_util.h" #include "net/base/net_errors.h" @@ -442,9 +443,9 @@ TEST_F(BidirectionalStreamSpdyImplTest, SendDataAfterStreamFailed) { EXPECT_EQ(0, delegate->GetTotalReceivedBytes()); } -INSTANTIATE_TEST_CASE_P(BidirectionalStreamSpdyImplTests, - BidirectionalStreamSpdyImplTest, - ::testing::Bool()); +INSTANTIATE_TEST_SUITE_P(BidirectionalStreamSpdyImplTests, + BidirectionalStreamSpdyImplTest, + ::testing::Bool()); // Tests that when received RST_STREAM with NO_ERROR, BidirectionalStream does // not crash when processing pending writes. See crbug.com/650438. diff --git a/chromium/net/spdy/spdy_http_stream.cc b/chromium/net/spdy/spdy_http_stream.cc index 62e070b32fa..a4ef27f8499 100644 --- a/chromium/net/spdy/spdy_http_stream.cc +++ b/chromium/net/spdy/spdy_http_stream.cc @@ -17,7 +17,7 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" -#include "net/base/host_port_pair.h" +#include "net/base/ip_endpoint.h" #include "net/base/upload_data_stream.h" #include "net/http/http_request_headers.h" #include "net/http/http_request_info.h" @@ -322,7 +322,7 @@ int SpdyHttpStream::SendRequest(const HttpRequestHeaders& request_headers, int result = stream_->GetPeerAddress(&address); if (result != OK) return result; - response_info_->socket_address = HostPortPair::FromIPEndPoint(address); + response_info_->remote_endpoint = address; if (stream_->type() == SPDY_PUSH_STREAM) { // Pushed streams do not send any data, and should always be diff --git a/chromium/net/spdy/spdy_http_stream.h b/chromium/net/spdy/spdy_http_stream.h index 315cba5b217..60fb740a30d 100644 --- a/chromium/net/spdy/spdy_http_stream.h +++ b/chromium/net/spdy/spdy_http_stream.h @@ -14,6 +14,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "net/base/completion_once_callback.h" +#include "net/base/load_timing_info.h" #include "net/base/net_export.h" #include "net/log/net_log_source.h" #include "net/spdy/multiplexed_http_stream.h" diff --git a/chromium/net/spdy/spdy_http_stream_unittest.cc b/chromium/net/spdy/spdy_http_stream_unittest.cc index 4ee7ac8aa96..a17f6b3db81 100644 --- a/chromium/net/spdy/spdy_http_stream_unittest.cc +++ b/chromium/net/spdy/spdy_http_stream_unittest.cc @@ -8,6 +8,7 @@ #include <string> +#include "base/bind.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" diff --git a/chromium/net/spdy/spdy_network_transaction_unittest.cc b/chromium/net/spdy/spdy_network_transaction_unittest.cc index ea86d7cd12b..36bb9bcbd7e 100644 --- a/chromium/net/spdy/spdy_network_transaction_unittest.cc +++ b/chromium/net/spdy/spdy_network_transaction_unittest.cc @@ -21,6 +21,7 @@ #include "net/base/chunked_upload_data_stream.h" #include "net/base/completion_once_callback.h" #include "net/base/elements_upload_data_stream.h" +#include "net/base/ip_endpoint.h" #include "net/base/proxy_delegate.h" #include "net/base/proxy_server.h" #include "net/base/request_priority.h" @@ -178,8 +179,8 @@ class SpdyNetworkTransactionTest : public TestWithScopedTaskEnvironment { EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); EXPECT_TRUE(response->was_fetched_via_spdy); EXPECT_TRUE(response->was_alpn_negotiated); - EXPECT_EQ("127.0.0.1", response->socket_address.host()); - EXPECT_EQ(443, response->socket_address.port()); + EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort()); + EXPECT_EQ(443, response->remote_endpoint.port()); output_.status_line = response->headers->GetStatusLine(); output_.response_info = *response; // Make a copy so we can verify. output_.rv = ReadTransaction(trans_.get(), &output_.response_data); @@ -4865,8 +4866,8 @@ TEST_F(SpdyNetworkTransactionTest, GracefulGoaway) { EXPECT_EQ("HTTP/1.1 200", response->headers->GetStatusLine()); EXPECT_TRUE(response->was_fetched_via_spdy); EXPECT_TRUE(response->was_alpn_negotiated); - EXPECT_EQ("127.0.0.1", response->socket_address.host()); - EXPECT_EQ(443, response->socket_address.port()); + EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort()); + EXPECT_EQ(443, response->remote_endpoint.port()); std::string response_data; rv = ReadTransaction(&trans2, &response_data); EXPECT_THAT(rv, IsOk()); @@ -4998,8 +4999,8 @@ TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredRetry) { response->connection_info); EXPECT_TRUE(response->was_alpn_negotiated); EXPECT_TRUE(request_.url.SchemeIs("https")); - EXPECT_EQ("127.0.0.1", response->socket_address.host()); - EXPECT_EQ(443, response->socket_address.port()); + EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort()); + EXPECT_EQ(443, response->remote_endpoint.port()); std::string response_data; ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk()); EXPECT_EQ("hello", response_data); @@ -5088,8 +5089,8 @@ TEST_F(SpdyNetworkTransactionTest, HTTP11RequiredProxyRetry) { response->connection_info); EXPECT_FALSE(response->was_alpn_negotiated); EXPECT_TRUE(request_.url.SchemeIs("https")); - EXPECT_EQ("127.0.0.1", response->socket_address.host()); - EXPECT_EQ(70, response->socket_address.port()); + EXPECT_EQ("127.0.0.1", response->remote_endpoint.ToStringWithoutPort()); + EXPECT_EQ(70, response->remote_endpoint.port()); std::string response_data; ASSERT_THAT(ReadTransaction(helper.trans(), &response_data), IsOk()); EXPECT_EQ("hello", response_data); @@ -5656,9 +5657,9 @@ class SpdyNetworkTransactionPushHeaderTest } }; -INSTANTIATE_TEST_CASE_P(, - SpdyNetworkTransactionPushHeaderTest, - ::testing::ValuesIn(push_header_test_cases)); +INSTANTIATE_TEST_SUITE_P(, + SpdyNetworkTransactionPushHeaderTest, + ::testing::ValuesIn(push_header_test_cases)); TEST_P(SpdyNetworkTransactionPushHeaderTest, PushedResponseHeadersReceivedBeforeRequest) { @@ -5863,9 +5864,9 @@ class SpdyNetworkTransactionPushUrlTest } }; -INSTANTIATE_TEST_CASE_P(, - SpdyNetworkTransactionPushUrlTest, - ::testing::ValuesIn(push_url_test_cases)); +INSTANTIATE_TEST_SUITE_P(, + SpdyNetworkTransactionPushUrlTest, + ::testing::ValuesIn(push_url_test_cases)); TEST_P(SpdyNetworkTransactionPushUrlTest, PushUrlTest) { RunTest(); @@ -8126,7 +8127,7 @@ TEST_F(SpdyNetworkTransactionTest, SecureWebSocketOverHttp2Proxy) { response->connection_info); EXPECT_TRUE(response->was_alpn_negotiated); EXPECT_FALSE(response->was_fetched_via_spdy); - EXPECT_EQ(70, response->socket_address.port()); + EXPECT_EQ(70, response->remote_endpoint.port()); ASSERT_TRUE(response->headers); EXPECT_EQ("HTTP/1.1 101 Switching Protocols", response->headers->GetStatusLine()); diff --git a/chromium/net/spdy/spdy_proxy_client_socket.cc b/chromium/net/spdy/spdy_proxy_client_socket.cc index f8d4713d0ab..c726ddc645d 100644 --- a/chromium/net/spdy/spdy_proxy_client_socket.cc +++ b/chromium/net/spdy/spdy_proxy_client_socket.cc @@ -22,7 +22,6 @@ #include "net/http/http_auth_handler_factory.h" #include "net/http/http_request_info.h" #include "net/http/http_response_headers.h" -#include "net/http/proxy_connect_redirect_http_stream.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_source_type.h" #include "net/spdy/spdy_http_utils.h" @@ -45,7 +44,6 @@ SpdyProxyClientSocket::SpdyProxyClientSocket( user_buffer_len_(0), write_buffer_len_(0), was_ever_used_(false), - redirect_has_load_timing_info_(false), net_log_(NetLogWithSource::Make(spdy_stream->net_log().net_log(), NetLogSourceType::PROXY_CLIENT_SOCKET)), source_dependency_(source_net_log.source()), @@ -82,7 +80,7 @@ int SpdyProxyClientSocket::RestartWithAuth(CompletionOnceCallback callback) { // stream may not be reused and a new SpdyProxyClientSocket must be // created (possibly on top of the same SPDY Session). next_state_ = STATE_DISCONNECTED; - return OK; + return ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH; } bool SpdyProxyClientSocket::IsUsingSpdy() const { @@ -93,15 +91,13 @@ NextProto SpdyProxyClientSocket::GetProxyNegotiatedProtocol() const { return spdy_stream_->GetNegotiatedProtocol(); } -void SpdyProxyClientSocket::SetStreamPriority(RequestPriority priority) { - spdy_stream_->SetPriority(priority); -} - -std::unique_ptr<HttpStream> -SpdyProxyClientSocket::CreateConnectResponseStream() { - return std::make_unique<ProxyConnectRedirectHttpStream>( - redirect_has_load_timing_info_ ? &redirect_load_timing_info_ : nullptr); -} +// Ignore priority changes, just use priority of initial request. Since multiple +// requests are pooled on the SpdyProxyClientSocket, reprioritization doesn't +// really work. +// +// TODO(mmenke): Use a single priority value for all SpdyProxyClientSockets, +// regardless of what priority they're created with. +void SpdyProxyClientSocket::SetStreamPriority(RequestPriority priority) {} // Sends a HEADERS frame to the proxy with a CONNECT request // for the specified endpoint. Waits for the server to send back @@ -413,8 +409,6 @@ int SpdyProxyClientSocket::DoReadReplyComplete(int result) { if (!SanitizeProxyRedirect(&response_)) return ERR_TUNNEL_CONNECTION_FAILED; - redirect_has_load_timing_info_ = - spdy_stream_->GetLoadTimingInfo(&redirect_load_timing_info_); // Note that this triggers a spdy::ERROR_CODE_CANCEL. spdy_stream_->DetachDelegate(); next_state_ = STATE_DISCONNECTED; diff --git a/chromium/net/spdy/spdy_proxy_client_socket.h b/chromium/net/spdy/spdy_proxy_client_socket.h index 4465e5b6ee8..ca6e0750e19 100644 --- a/chromium/net/spdy/spdy_proxy_client_socket.h +++ b/chromium/net/spdy/spdy_proxy_client_socket.h @@ -18,7 +18,6 @@ #include "net/base/completion_callback.h" #include "net/base/completion_once_callback.h" #include "net/base/host_port_pair.h" -#include "net/base/load_timing_info.h" #include "net/base/net_export.h" #include "net/http/http_auth_controller.h" #include "net/http/http_request_headers.h" @@ -36,7 +35,6 @@ namespace net { -class HttpStream; class IOBuffer; class SpdyStream; @@ -58,7 +56,6 @@ class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket, // ProxyClientSocket methods: const HttpResponseInfo* GetConnectResponseInfo() const override; - std::unique_ptr<HttpStream> CreateConnectResponseStream() override; const scoped_refptr<HttpAuthController>& GetAuthController() const override; int RestartWithAuth(CompletionOnceCallback callback) override; bool IsUsingSpdy() const override; @@ -173,10 +170,6 @@ class NET_EXPORT_PRIVATE SpdyProxyClientSocket : public ProxyClientSocket, // True if the transport socket has ever sent data. bool was_ever_used_; - // Used only for redirects. - bool redirect_has_load_timing_info_; - LoadTimingInfo redirect_load_timing_info_; - const NetLogWithSource net_log_; const NetLogSource source_dependency_; diff --git a/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc b/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc index 8e53e3084bc..c7899bdb448 100644 --- a/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc +++ b/chromium/net/spdy/spdy_proxy_client_socket_unittest.cc @@ -13,9 +13,11 @@ #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" #include "net/base/address_list.h" +#include "net/base/load_timing_info.h" #include "net/base/test_completion_callback.h" #include "net/base/winsock_init.h" #include "net/dns/mock_host_resolver.h" +#include "net/http/http_proxy_connect_job.h" #include "net/http/http_response_headers.h" #include "net/http/http_response_info.h" #include "net/log/net_log_event_type.h" @@ -24,9 +26,15 @@ #include "net/log/test_net_log_entry.h" #include "net/log/test_net_log_util.h" #include "net/socket/client_socket_factory.h" +#include "net/socket/connect_job_test_util.h" #include "net/socket/socket_tag.h" #include "net/socket/socket_test_util.h" +#include "net/socket/socks_connect_job.h" +#include "net/socket/ssl_client_socket.h" +#include "net/socket/ssl_connect_job.h" +#include "net/socket/stream_socket.h" #include "net/socket/tcp_client_socket.h" +#include "net/socket/transport_connect_job.h" #include "net/spdy/buffered_spdy_framer.h" #include "net/spdy/spdy_http_utils.h" #include "net/spdy/spdy_session_pool.h" @@ -46,6 +54,8 @@ using net::test::IsOk; //----------------------------------------------------------------------------- +namespace net { + namespace { static const char kRequestUrl[] = "https://www.google.com/"; @@ -72,9 +82,60 @@ static const int kLen333 = kLen3 + kLen3 + kLen3; static const char kRedirectUrl[] = "https://example.com/"; -} // anonymous namespace +// Creates a SpdySession with a StreamSocket, instead of a ClientSocketHandle. +base::WeakPtr<SpdySession> CreateSpdyProxySession( + HttpNetworkSession* http_session, + SpdySessionDependencies* session_deps, + const SpdySessionKey& key) { + EXPECT_FALSE(http_session->spdy_session_pool()->FindAvailableSession( + key, true /* enable_ip_based_pooling */, false /* is_websocket */, + NetLogWithSource())); + + auto transport_params = base::MakeRefCounted<TransportSocketParams>( + key.host_port_pair(), false /* disable_resolver_cache */, + OnHostResolutionCallback()); + + SSLConfig ssl_config; + auto ssl_params = base::MakeRefCounted<SSLSocketParams>( + transport_params, nullptr, nullptr, key.host_port_pair(), ssl_config, + key.privacy_mode()); + TestConnectJobDelegate connect_job_delegate; + SSLConnectJob connect_job( + MEDIUM, + CommonConnectJobParams( + SocketTag(), session_deps->socket_factory.get(), + session_deps->host_resolver.get(), nullptr /* proxy_delegate */, + SSLClientSocketContext(session_deps->cert_verifier.get(), + session_deps->channel_id_service.get(), + session_deps->transport_security_state.get(), + session_deps->cert_transparency_verifier.get(), + session_deps->ct_policy_enforcer.get(), + nullptr /* ssl_client_session_cache_arg */), + SSLClientSocketContext(session_deps->cert_verifier.get(), + session_deps->channel_id_service.get(), + session_deps->transport_security_state.get(), + session_deps->cert_transparency_verifier.get(), + session_deps->ct_policy_enforcer.get(), + nullptr /* ssl_client_session_cache_arg */), + nullptr /* socket_performance_watcher_factory */, + nullptr /* network_quality_estimator */, session_deps->net_log, + nullptr /* websocket_endpoint_lock_manager */), + ssl_params, &connect_job_delegate, nullptr /* net_log */); + connect_job_delegate.StartJobExpectingResult(&connect_job, OK, + false /* expect_sync_result */); + + base::WeakPtr<SpdySession> spdy_session = + http_session->spdy_session_pool()->CreateAvailableSessionFromSocket( + key, false /* is_trusted_proxy */, + connect_job_delegate.ReleaseSocket(), LoadTimingInfo::ConnectTiming(), + NetLogWithSource()); + // Failure is reported asynchronously. + EXPECT_TRUE(spdy_session); + EXPECT_TRUE(HasSpdySession(http_session->spdy_session_pool(), key)); + return spdy_session; +} -namespace net { +} // namespace class SpdyProxyClientSocketTest : public PlatformTest, public WithScopedTaskEnvironment, @@ -91,7 +152,7 @@ class SpdyProxyClientSocketTest : public PlatformTest, void PopulateConnectRequestIR(spdy::SpdyHeaderBlock* syn_ir); void PopulateConnectReplyIR(spdy::SpdyHeaderBlock* block, const char* status); spdy::SpdySerializedFrame ConstructConnectRequestFrame( - RequestPriority priority); + RequestPriority priority = LOWEST); spdy::SpdySerializedFrame ConstructConnectAuthRequestFrame(); spdy::SpdySerializedFrame ConstructConnectReplyFrame(); spdy::SpdySerializedFrame ConstructConnectAuthReplyFrame(); @@ -206,8 +267,9 @@ void SpdyProxyClientSocketTest::Initialize(base::span<const MockRead> reads, session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); // Creates the SPDY session and stream. - spdy_session_ = CreateSpdySession(session_.get(), endpoint_spdy_session_key_, - NetLogWithSource()); + spdy_session_ = CreateSpdyProxySession(session_.get(), &session_deps_, + endpoint_spdy_session_key_); + base::WeakPtr<SpdyStream> spdy_stream( CreateStreamSynchronously( SPDY_BIDIRECTIONAL_STREAM, spdy_session_, url_, LOWEST, @@ -219,7 +281,8 @@ void SpdyProxyClientSocketTest::Initialize(base::span<const MockRead> reads, spdy_stream, user_agent_, endpoint_host_port_pair_, net_log_.bound(), new HttpAuthController( HttpAuth::AUTH_PROXY, GURL("https://" + proxy_host_port_.ToString()), - session_->http_auth_cache(), session_->http_auth_handler_factory())); + session_->http_auth_cache(), session_->http_auth_handler_factory(), + session_->host_resolver())); } scoped_refptr<IOBufferWithSize> SpdyProxyClientSocketTest::CreateBuffer( @@ -356,7 +419,7 @@ void SpdyProxyClientSocketTest::PopulateConnectReplyIR( // Constructs a standard SPDY HEADERS frame for a CONNECT request. spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructConnectRequestFrame( - RequestPriority priority = LOWEST) { + RequestPriority priority) { spdy::SpdyHeaderBlock block; PopulateConnectRequestIR(&block); return spdy_util_.ConstructSpdyHeaders(kStreamId, std::move(block), priority, @@ -413,16 +476,15 @@ SpdyProxyClientSocketTest::ConstructConnectErrorReplyFrame() { spdy::SpdySerializedFrame SpdyProxyClientSocketTest::ConstructBodyFrame( const char* data, int length) { - return spdy_util_.ConstructSpdyDataFrame(kStreamId, - base::StringPiece(data, length), - /*fin=*/false); + return spdy_util_.ConstructSpdyDataFrame( + kStreamId, base::StringPiece(data, length), false /* fin */); } // ----------- Connect -INSTANTIATE_TEST_CASE_P(/* no prefix */, - SpdyProxyClientSocketTest, - ::testing::Bool()); +INSTANTIATE_TEST_SUITE_P(/* no prefix */, + SpdyProxyClientSocketTest, + ::testing::Bool()); TEST_P(SpdyProxyClientSocketTest, ConnectSendsCorrectRequest) { spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame()); @@ -537,7 +599,7 @@ TEST_P(SpdyProxyClientSocketTest, ConnectFails) { } TEST_P(SpdyProxyClientSocketTest, SetStreamPriority) { - spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame(HIGHEST)); + spdy::SpdySerializedFrame conn(ConstructConnectRequestFrame(LOWEST)); MockWrite writes[] = { CreateMockWrite(conn, 0, SYNCHRONOUS), }; @@ -550,6 +612,8 @@ TEST_P(SpdyProxyClientSocketTest, SetStreamPriority) { Initialize(reads, writes); + // Set the stream priority. Since a connection was already established, it's + // too late to adjust the HTTP2 stream's priority, and the request is ignored. sock_->SetStreamPriority(HIGHEST); AssertConnectSucceeds(); diff --git a/chromium/net/spdy/spdy_session.cc b/chromium/net/spdy/spdy_session.cc index 9c3605b30ad..f63c1271b5e 100644 --- a/chromium/net/spdy/spdy_session.cc +++ b/chromium/net/spdy/spdy_session.cc @@ -44,6 +44,7 @@ #include "net/log/net_log_with_source.h" #include "net/nqe/network_quality_estimator.h" #include "net/quic/quic_http_utils.h" +#include "net/socket/client_socket_handle.h" #include "net/socket/socket.h" #include "net/socket/ssl_client_socket.h" #include "net/spdy/header_coalescer.h" @@ -844,6 +845,7 @@ SpdySession::SpdySession( http_server_properties_(http_server_properties), transport_security_state_(transport_security_state), ssl_config_service_(ssl_config_service), + socket_(nullptr), stream_hi_water_mark_(kFirstStreamId), last_accepted_push_stream_id_(0), push_delegate_(push_delegate), @@ -920,11 +922,10 @@ SpdySession::~SpdySession() { CHECK(!in_io_loop_); DcheckDraining(); - // TODO(akalin): Check connection->is_initialized() instead. This - // requires re-working CreateFakeSpdySession(), though. - DCHECK(connection_->socket()); + // TODO(akalin): Check connection->is_initialized(). + DCHECK(socket_); // With SPDY we can't recycle sockets. - connection_->socket()->Disconnect(); + socket_->Disconnect(); RecordHistograms(); @@ -981,48 +982,40 @@ void SpdySession::CancelPush(const GURL& url) { ResetStream(stream_id, ERR_ABORTED, "Cancelled push stream."); } -void SpdySession::InitializeWithSocket( - std::unique_ptr<ClientSocketHandle> connection, +void SpdySession::InitializeWithSocketHandle( + std::unique_ptr<ClientSocketHandle> client_socket_handle, SpdySessionPool* pool) { - CHECK(!in_io_loop_); - DCHECK_EQ(availability_state_, STATE_AVAILABLE); - DCHECK_EQ(read_state_, READ_STATE_DO_READ); - DCHECK_EQ(write_state_, WRITE_STATE_IDLE); - DCHECK(!connection_); + DCHECK(!client_socket_handle_); + DCHECK(!owned_stream_socket_); + DCHECK(!socket_); // TODO(akalin): Check connection->is_initialized() instead. This // requires re-working CreateFakeSpdySession(), though. - DCHECK(connection->socket()); + DCHECK(client_socket_handle->socket()); - connection_ = std::move(connection); + client_socket_handle_ = std::move(client_socket_handle); + socket_ = client_socket_handle_->socket(); + client_socket_handle_->AddHigherLayeredPool(this); - session_send_window_size_ = kDefaultInitialWindowSize; - session_recv_window_size_ = kDefaultInitialWindowSize; + InitializeInternal(pool); +} - auto it = initial_settings_.find(spdy::SETTINGS_MAX_HEADER_LIST_SIZE); - uint32_t spdy_max_header_list_size = - (it == initial_settings_.end()) ? kSpdyMaxHeaderListSize : it->second; - buffered_spdy_framer_ = std::make_unique<BufferedSpdyFramer>( - spdy_max_header_list_size, net_log_, time_func_); - buffered_spdy_framer_->set_visitor(this); - buffered_spdy_framer_->set_debug_visitor(this); - buffered_spdy_framer_->UpdateHeaderDecoderTableSize(max_header_table_size_); +void SpdySession::InitializeWithSocket( + std::unique_ptr<StreamSocket> stream_socket, + const LoadTimingInfo::ConnectTiming& connect_timing, + SpdySessionPool* pool) { + DCHECK(!client_socket_handle_); + DCHECK(!owned_stream_socket_); + DCHECK(!socket_); - net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_INITIALIZED, - base::Bind(&NetLogSpdyInitializedCallback, - connection_->socket()->NetLog().source())); + DCHECK(stream_socket); - DCHECK_EQ(availability_state_, STATE_AVAILABLE); - connection_->AddHigherLayeredPool(this); - if (enable_sending_initial_data_) - SendInitialData(); - pool_ = pool; + owned_stream_socket_ = std::move(stream_socket); + socket_ = owned_stream_socket_.get(); + connect_timing_ = + std::make_unique<LoadTimingInfo::ConnectTiming>(connect_timing); - // Bootstrap the read loop. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), - READ_STATE_DO_READ, OK)); + InitializeInternal(pool); } bool SpdySession::VerifyDomainAuthentication(const std::string& domain) const { @@ -1283,15 +1276,15 @@ bool SpdySession::GetRemoteEndpoint(IPEndPoint* endpoint) { } bool SpdySession::GetSSLInfo(SSLInfo* ssl_info) const { - return connection_->socket()->GetSSLInfo(ssl_info); + return socket_->GetSSLInfo(ssl_info); } bool SpdySession::WasAlpnNegotiated() const { - return connection_->socket()->WasAlpnNegotiated(); + return socket_->WasAlpnNegotiated(); } NextProto SpdySession::GetNegotiatedProtocol() const { - return connection_->socket()->GetNegotiatedProtocol(); + return socket_->GetNegotiatedProtocol(); } void SpdySession::SendStreamWindowUpdate(spdy::SpdyStreamId stream_id, @@ -1388,9 +1381,8 @@ std::unique_ptr<base::Value> SpdySession::GetInfoAsValue() const { dict->SetInteger("unclaimed_pushed_streams", pool_->push_promise_index()->CountStreamsForSession(this)); - dict->SetString( - "negotiated_protocol", - NextProtoToString(connection_->socket()->GetNegotiatedProtocol())); + dict->SetString("negotiated_protocol", + NextProtoToString(socket_->GetNegotiatedProtocol())); dict->SetInteger("error", error_on_close_); dict->SetInteger("max_concurrent_streams", max_concurrent_streams_); @@ -1411,26 +1403,50 @@ std::unique_ptr<base::Value> SpdySession::GetInfoAsValue() const { } bool SpdySession::IsReused() const { - return buffered_spdy_framer_->frames_received() > 0 || - connection_->reuse_type() == ClientSocketHandle::UNUSED_IDLE; + if (buffered_spdy_framer_->frames_received() > 0) + return true; + + // If there's no socket pool in use (i.e., |owned_stream_socket_| is + // non-null), then the SpdySession could only have been created with freshly + // connected socket, since canceling the H2 session request would have + // destroyed the socket. + return owned_stream_socket_ || + client_socket_handle_->reuse_type() == ClientSocketHandle::UNUSED_IDLE; } bool SpdySession::GetLoadTimingInfo(spdy::SpdyStreamId stream_id, LoadTimingInfo* load_timing_info) const { - return connection_->GetLoadTimingInfo(stream_id != kFirstStreamId, - load_timing_info); + if (client_socket_handle_) { + DCHECK(!connect_timing_); + return client_socket_handle_->GetLoadTimingInfo(stream_id != kFirstStreamId, + load_timing_info); + } + + DCHECK(connect_timing_); + DCHECK(socket_); + + // The socket is considered "fresh" (not reused) only for the first stream on + // a SPDY session. All others consider it reused, and don't return connection + // establishment timing information. + load_timing_info->socket_reused = (stream_id != kFirstStreamId); + if (!load_timing_info->socket_reused) + load_timing_info->connect_timing = *connect_timing_; + + load_timing_info->socket_log_id = socket_->NetLog().source().id; + + return true; } int SpdySession::GetPeerAddress(IPEndPoint* address) const { - if (connection_->socket()) - return connection_->socket()->GetPeerAddress(address); + if (socket_) + return socket_->GetPeerAddress(address); return ERR_SOCKET_NOT_CONNECTED; } int SpdySession::GetLocalAddress(IPEndPoint* address) const { - if (connection_->socket()) - return connection_->socket()->GetLocalAddress(address); + if (socket_) + return socket_->GetLocalAddress(address); return ERR_SOCKET_NOT_CONNECTED; } @@ -1523,7 +1539,7 @@ size_t SpdySession::DumpMemoryStats(StreamSocket::SocketMemoryStats* stats, // TODO(xunjieli): Include |pending_create_stream_queues_| when WeakPtr is // supported in memory_usage_estimator.h. *is_session_active = is_active(); - connection_->DumpMemoryStats(stats); + socket_->DumpMemoryStats(stats); // |connection_| is estimated in stats->total_size. |read_buffer_| is // estimated in |read_buffer_size|. TODO(xunjieli): Make them use EMU(). @@ -1542,7 +1558,7 @@ size_t SpdySession::DumpMemoryStats(StreamSocket::SocketMemoryStats* stats, } bool SpdySession::ChangeSocketTag(const SocketTag& new_tag) { - if (!IsAvailable() || !connection_->socket()) + if (!IsAvailable() || !socket_) return false; // Changing the tag on the underlying socket will affect all streams, @@ -1550,7 +1566,7 @@ bool SpdySession::ChangeSocketTag(const SocketTag& new_tag) { if (is_active()) return false; - connection_->socket()->ApplySocketTag(new_tag); + socket_->ApplySocketTag(new_tag); SpdySessionKey new_key(spdy_session_key_.host_port_pair(), spdy_session_key_.proxy_server(), @@ -1567,6 +1583,40 @@ void SpdySession::RecordSpdyPushedStreamFateHistogram( UMA_HISTOGRAM_ENUMERATION("Net.SpdyPushedStreamFate", value); } +void SpdySession::InitializeInternal(SpdySessionPool* pool) { + CHECK(!in_io_loop_); + DCHECK_EQ(availability_state_, STATE_AVAILABLE); + DCHECK_EQ(read_state_, READ_STATE_DO_READ); + DCHECK_EQ(write_state_, WRITE_STATE_IDLE); + + session_send_window_size_ = kDefaultInitialWindowSize; + session_recv_window_size_ = kDefaultInitialWindowSize; + + auto it = initial_settings_.find(spdy::SETTINGS_MAX_HEADER_LIST_SIZE); + uint32_t spdy_max_header_list_size = + (it == initial_settings_.end()) ? kSpdyMaxHeaderListSize : it->second; + buffered_spdy_framer_ = std::make_unique<BufferedSpdyFramer>( + spdy_max_header_list_size, net_log_, time_func_); + buffered_spdy_framer_->set_visitor(this); + buffered_spdy_framer_->set_debug_visitor(this); + buffered_spdy_framer_->UpdateHeaderDecoderTableSize(max_header_table_size_); + + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_INITIALIZED, + base::BindRepeating(&NetLogSpdyInitializedCallback, + socket_->NetLog().source())); + + DCHECK_EQ(availability_state_, STATE_AVAILABLE); + if (enable_sending_initial_data_) + SendInitialData(); + pool_ = pool; + + // Bootstrap the read loop. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindRepeating(&SpdySession::PumpReadLoop, + weak_factory_.GetWeakPtr(), READ_STATE_DO_READ, OK)); +} + // {,Try}CreateStream() can be called with |in_io_loop_| set if a stream is // being created in response to another being closed due to received data. @@ -1615,10 +1665,10 @@ int SpdySession::CreateStream(const SpdyStreamRequest& request, if (availability_state_ == STATE_DRAINING) return ERR_CONNECTION_CLOSED; - DCHECK(connection_->socket()); + DCHECK(socket_); UMA_HISTOGRAM_BOOLEAN("Net.SpdySession.CreateStreamWithSocketConnected", - connection_->socket()->IsConnected()); - if (!connection_->socket()->IsConnected()) { + socket_->IsConnected()); + if (!socket_->IsConnected()) { DoDrainSession( ERR_CONNECTION_CLOSED, "Tried to create SPDY stream for a closed socket connection."); @@ -1711,8 +1761,8 @@ void SpdySession::ProcessPendingStreamRequests() { // possible that the un-stalled stream will be stalled again if it loses. // TODO(jgraettinger): Provide stronger ordering guarantees. base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&SpdySession::CompleteStreamRequest, - weak_factory_.GetWeakPtr(), pending_request)); + FROM_HERE, base::BindOnce(&SpdySession::CompleteStreamRequest, + weak_factory_.GetWeakPtr(), pending_request)); } } @@ -1867,8 +1917,8 @@ void SpdySession::TryCreatePushStream(spdy::SpdyStreamId stream_id, base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, - base::Bind(&SpdySession::CancelPushedStreamIfUnclaimed, GetWeakPtr(), - stream_id), + base::BindOnce(&SpdySession::CancelPushedStreamIfUnclaimed, GetWeakPtr(), + stream_id), base::TimeDelta::FromSeconds(kPushedStreamLifetimeSeconds)); net::NetworkTrafficAnnotationTag traffic_annotation = @@ -1962,10 +2012,11 @@ void SpdySession::CloseActiveStreamIterator(ActiveStreamMap::iterator it, DeleteStream(std::move(owned_stream), status); - // If there are no active streams and the socket pool is stalled, close the - // session to free up a socket slot. - if (active_streams_.empty() && created_streams_.empty() && - connection_->IsPoolStalled()) { + // If the socket belongs to a socket pool, and there are no active streams, + // and the socket pool is stalled, then close the session to free up a socket + // slot. + if (client_socket_handle_ && active_streams_.empty() && + created_streams_.empty() && client_socket_handle_->IsPoolStalled()) { DoDrainSession(ERR_CONNECTION_CLOSED, "Closing idle connection."); } } @@ -2095,8 +2146,8 @@ int SpdySession::DoReadLoop(ReadState expected_read_state, int result) { time_func_() > yield_after_time)) { base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), - READ_STATE_DO_READ, OK)); + base::BindOnce(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), + READ_STATE_DO_READ, OK)); result = ERR_IO_PENDING; break; } @@ -2112,13 +2163,12 @@ int SpdySession::DoRead() { DCHECK(!read_buffer_); CHECK(in_io_loop_); - CHECK(connection_); - CHECK(connection_->socket()); + CHECK(socket_); read_state_ = READ_STATE_DO_READ_COMPLETE; int rv = ERR_READ_IF_READY_NOT_IMPLEMENTED; read_buffer_ = base::MakeRefCounted<IOBuffer>(kReadBufferSize); if (base::FeatureList::IsEnabled(Socket::kReadIfReadyExperiment)) { - rv = connection_->socket()->ReadIfReady( + rv = socket_->ReadIfReady( read_buffer_.get(), kReadBufferSize, base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), READ_STATE_DO_READ)); @@ -2130,7 +2180,7 @@ int SpdySession::DoRead() { } if (rv == ERR_READ_IF_READY_NOT_IMPLEMENTED) { // Fallback to regular Read(). - return connection_->socket()->Read( + return socket_->Read( read_buffer_.get(), kReadBufferSize, base::Bind(&SpdySession::PumpReadLoop, weak_factory_.GetWeakPtr(), READ_STATE_DO_READ_COMPLETE)); @@ -2201,8 +2251,8 @@ void SpdySession::MaybePostWriteLoop() { write_state_ = WRITE_STATE_DO_WRITE; base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, - base::Bind(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(), - WRITE_STATE_DO_WRITE, OK)); + base::BindOnce(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(), + WRITE_STATE_DO_WRITE, OK)); } } @@ -2297,7 +2347,7 @@ int SpdySession::DoWrite() { scoped_refptr<IOBuffer> write_io_buffer = in_flight_write_->GetIOBufferForRemainingData(); - return connection_->socket()->Write( + return socket_->Write( write_io_buffer.get(), in_flight_write_->GetRemainingSize(), base::Bind(&SpdySession::PumpWriteLoop, weak_factory_.GetWeakPtr(), WRITE_STATE_DO_WRITE_COMPLETE), @@ -2553,8 +2603,9 @@ void SpdySession::PlanToCheckPingStatus() { check_ping_status_pending_ = true; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&SpdySession::CheckPingStatus, - weak_factory_.GetWeakPtr(), time_func_()), + FROM_HERE, + base::BindOnce(&SpdySession::CheckPingStatus, weak_factory_.GetWeakPtr(), + time_func_()), hung_interval_); } @@ -2579,8 +2630,9 @@ void SpdySession::CheckPingStatus(base::TimeTicks last_check_time) { // Check the status of connection after a delay. const base::TimeDelta delay = last_read_time_ + hung_interval_ - now; base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( - FROM_HERE, base::Bind(&SpdySession::CheckPingStatus, - weak_factory_.GetWeakPtr(), now), + FROM_HERE, + base::BindOnce(&SpdySession::CheckPingStatus, weak_factory_.GetWeakPtr(), + now), delay); } @@ -3114,7 +3166,7 @@ void SpdySession::OnWindowUpdate(spdy::SpdyStreamId stream_id, DoDrainSession( ERR_SPDY_PROTOCOL_ERROR, "Received WINDOW_UPDATE with an invalid delta_window_size " + - base::IntToString(delta_window_size)); + base::NumberToString(delta_window_size)); return; } @@ -3353,9 +3405,9 @@ void SpdySession::IncreaseSendWindowSize(int delta_window_size) { DoDrainSession( ERR_SPDY_PROTOCOL_ERROR, "Received WINDOW_UPDATE [delta: " + - base::IntToString(delta_window_size) + + base::NumberToString(delta_window_size) + "] for session overflows session_send_window_size_ [current: " + - base::IntToString(session_send_window_size_) + "]"); + base::NumberToString(session_send_window_size_) + "]"); return; } @@ -3432,9 +3484,10 @@ void SpdySession::DecreaseRecvWindowSize(int32_t delta_window_size) { RecordProtocolErrorHistogram(PROTOCOL_ERROR_RECEIVE_WINDOW_VIOLATION); DoDrainSession( ERR_SPDY_FLOW_CONTROL_ERROR, - "delta_window_size is " + base::IntToString(delta_window_size) + + "delta_window_size is " + base::NumberToString(delta_window_size) + " in DecreaseRecvWindowSize, which is larger than the receive " + - "window size of " + base::IntToString(session_recv_window_size_)); + "window size of " + + base::NumberToString(session_recv_window_size_)); return; } diff --git a/chromium/net/spdy/spdy_session.h b/chromium/net/spdy/spdy_session.h index 2f846ffdf36..235c85a2181 100644 --- a/chromium/net/spdy/spdy_session.h +++ b/chromium/net/spdy/spdy_session.h @@ -27,11 +27,11 @@ #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/load_states.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/net_export.h" #include "net/base/request_priority.h" #include "net/log/net_log_source.h" -#include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool.h" #include "net/socket/next_proto.h" #include "net/socket/ssl_client_socket.h" @@ -86,7 +86,6 @@ const int kYieldAfterDurationMilliseconds = 20; const spdy::SpdyStreamId kFirstStreamId = 1; const spdy::SpdyStreamId kLastStreamId = 0x7fffffff; -struct LoadTimingInfo; class NetLog; class NetworkQualityEstimator; class SpdyStream; @@ -179,7 +178,8 @@ enum class SpdyPushedStreamFate { kAcceptedMatchingVary = 18, kPushDisabled = 19, kAlreadyInCache = 20, - kMaxValue = kAlreadyInCache + kUnsupportedStatusCode = 21, + kMaxValue = kUnsupportedStatusCode }; // If these compile asserts fail then SpdyProtocolErrorDetails needs @@ -348,10 +348,19 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, // |pool| is the SpdySessionPool that owns us. Its lifetime must // strictly be greater than |this|. // - // The session begins reading from |connection| on a subsequent event loop - // iteration, so the SpdySession may close immediately afterwards if the first - // read of |connection| fails. - void InitializeWithSocket(std::unique_ptr<ClientSocketHandle> connection, + // The session begins reading from |client_socket_handle| on a subsequent + // event loop iteration, so the SpdySession may close immediately afterwards + // if the first read of |client_socket_handle| fails. + void InitializeWithSocketHandle( + std::unique_ptr<ClientSocketHandle> client_socket_handle, + SpdySessionPool* pool); + + // Just like InitializeWithSocketHandle(), but for use when the session is not + // on top of a socket pool, but instead directly on top of a socket, which the + // session has sole ownership of, and is responsible for deleting directly + // itself. + void InitializeWithSocket(std::unique_ptr<StreamSocket> stream_socket, + const LoadTimingInfo::ConnectTiming& connect_timing, SpdySessionPool* pool); // Check to see if this SPDY session can support an additional domain. @@ -476,9 +485,7 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, // Returns true if the underlying transport socket ever had any reads or // writes. - bool WasEverUsed() const { - return connection_->socket()->WasEverUsed(); - } + bool WasEverUsed() const { return socket_->WasEverUsed(); } // Returns the load timing information from the perspective of the given // stream. If it's not the first stream, the connection is considered reused @@ -595,6 +602,9 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, WRITE_STATE_DO_WRITE_COMPLETE, }; + // Has the shared logic for the other two Initialize methods that call it. + void InitializeInternal(SpdySessionPool* pool); + // Called by SpdyStreamRequest to start a request to create a // stream. If OK is returned, then |stream| will be filled in with a // valid stream. If ERR_IO_PENDING is returned, then @@ -937,8 +947,18 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, TransportSecurityState* transport_security_state_; SSLConfigService* ssl_config_service_; - // The socket handle for this session. - std::unique_ptr<ClientSocketHandle> connection_; + // One of these two owns the socket for this session, which is stored in + // |socket_|. If |client_socket_handle_| is non-null, this session is on top + // of a socket in a socket pool. If |owned_stream_socket_| is non-null, this + // session is directly on top of a socket, which is not in a socket pool. + std::unique_ptr<ClientSocketHandle> client_socket_handle_; + std::unique_ptr<StreamSocket> owned_stream_socket_; + + // This is non-null only if |owned_stream_socket_| is non-null. + std::unique_ptr<LoadTimingInfo::ConnectTiming> connect_timing_; + + // The socket for this session. + StreamSocket* socket_; // The read buffer used to read data from the socket. // Non-null if there is a Read() pending. diff --git a/chromium/net/spdy/spdy_session_fuzzer.cc b/chromium/net/spdy/spdy_session_fuzzer.cc index 93a942911a7..4b92029986c 100644 --- a/chromium/net/spdy/spdy_session_fuzzer.cc +++ b/chromium/net/spdy/spdy_session_fuzzer.cc @@ -12,7 +12,6 @@ #include "net/cert/x509_certificate.h" #include "net/log/net_log_source.h" #include "net/log/test_net_log.h" -#include "net/socket/client_socket_handle.h" #include "net/socket/fuzzed_socket_factory.h" #include "net/socket/socket_tag.h" #include "net/socket/socket_test_util.h" @@ -65,7 +64,7 @@ class FuzzedSocketFactoryWithMockSSLData : public FuzzedSocketFactory { void AddSSLSocketDataProvider(SSLSocketDataProvider* socket); std::unique_ptr<SSLClientSocket> CreateSSLClientSocket( - std::unique_ptr<ClientSocketHandle> transport_socket, + std::unique_ptr<StreamSocket> nested_socket, const HostPortPair& host_and_port, const SSLConfig& ssl_config, const SSLClientSocketContext& context) override; @@ -85,11 +84,11 @@ void FuzzedSocketFactoryWithMockSSLData::AddSSLSocketDataProvider( std::unique_ptr<SSLClientSocket> FuzzedSocketFactoryWithMockSSLData::CreateSSLClientSocket( - std::unique_ptr<ClientSocketHandle> transport_socket, + std::unique_ptr<StreamSocket> nested_socket, const HostPortPair& host_and_port, const SSLConfig& ssl_config, const SSLClientSocketContext& context) { - return std::make_unique<MockSSLClientSocket>(std::move(transport_socket), + return std::make_unique<MockSSLClientSocket>(std::move(nested_socket), host_and_port, ssl_config, mock_ssl_data_.GetNext()); } diff --git a/chromium/net/spdy/spdy_session_pool.cc b/chromium/net/spdy/spdy_session_pool.cc index 27e4f5635a3..c252a15400a 100644 --- a/chromium/net/spdy/spdy_session_pool.cc +++ b/chromium/net/spdy/spdy_session_pool.cc @@ -7,10 +7,12 @@ #include <algorithm> #include <utility> +#include "base/bind.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" +#include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_usage_estimator.h" #include "base/trace_event/process_memory_dump.h" @@ -19,6 +21,8 @@ #include "build/build_config.h" #include "net/base/address_list.h" #include "net/base/trace_constants.h" +#include "net/dns/host_resolver.h" +#include "net/dns/host_resolver_source.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties.h" #include "net/http/http_stream_request.h" @@ -98,48 +102,38 @@ SpdySessionPool::~SpdySessionPool() { CertDatabase::GetInstance()->RemoveObserver(this); } -base::WeakPtr<SpdySession> SpdySessionPool::CreateAvailableSessionFromSocket( +base::WeakPtr<SpdySession> +SpdySessionPool::CreateAvailableSessionFromSocketHandle( const SpdySessionKey& key, bool is_trusted_proxy, - std::unique_ptr<ClientSocketHandle> connection, + std::unique_ptr<ClientSocketHandle> client_socket_handle, const NetLogWithSource& net_log) { TRACE_EVENT0(NetTracingCategory(), - "SpdySessionPool::CreateAvailableSessionFromSocket"); - - UMA_HISTOGRAM_ENUMERATION( - "Net.SpdySessionGet", IMPORTED_FROM_SOCKET, SPDY_SESSION_GET_MAX); - - auto new_session = std::make_unique<SpdySession>( - key, http_server_properties_, transport_security_state_, - ssl_config_service_, quic_supported_versions_, - enable_sending_initial_data_, enable_ping_based_connection_checking_, - support_ietf_format_quic_altsvc_, is_trusted_proxy, - session_max_recv_window_size_, initial_settings_, greased_http2_frame_, - time_func_, push_delegate_, network_quality_estimator_, - net_log.net_log()); + "SpdySessionPool::CreateAvailableSessionFromSocketHandle"); - new_session->InitializeWithSocket(std::move(connection), this); + std::unique_ptr<SpdySession> new_session = + CreateSession(key, is_trusted_proxy, net_log.net_log()); + new_session->InitializeWithSocketHandle(std::move(client_socket_handle), + this); + return InsertSession(key, std::move(new_session), net_log); +} - base::WeakPtr<SpdySession> available_session = new_session->GetWeakPtr(); - sessions_.insert(new_session.release()); - MapKeyToAvailableSession(key, available_session); +base::WeakPtr<SpdySession> SpdySessionPool::CreateAvailableSessionFromSocket( + const SpdySessionKey& key, + bool is_trusted_proxy, + std::unique_ptr<StreamSocket> socket_stream, + const LoadTimingInfo::ConnectTiming& connect_timing, + const NetLogWithSource& net_log) { + TRACE_EVENT0(NetTracingCategory(), + "SpdySessionPool::CreateAvailableSessionFromSocket"); - net_log.AddEvent( - NetLogEventType::HTTP2_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET, - available_session->net_log().source().ToEventParametersCallback()); + std::unique_ptr<SpdySession> new_session = + CreateSession(key, is_trusted_proxy, net_log.net_log()); - // Look up the IP address for this session so that we can match - // future sessions (potentially to different domains) which can - // potentially be pooled with this one. Because GetPeerAddress() - // reports the proxy's address instead of the origin server, check - // to see if this is a direct connection. - if (key.proxy_server().is_direct()) { - IPEndPoint address; - if (available_session->GetPeerAddress(&address) == OK) - aliases_.insert(AliasMap::value_type(address, key)); - } + new_session->InitializeWithSocket(std::move(socket_stream), connect_timing, + this); - return available_session; + return InsertSession(key, std::move(new_session), net_log); } base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession( @@ -183,18 +177,19 @@ base::WeakPtr<SpdySession> SpdySessionPool::FindAvailableSession( return base::WeakPtr<SpdySession>(); // Look up IP addresses from resolver cache. - HostResolver::RequestInfo resolve_info(key.host_port_pair()); - AddressList addresses; - int rv = resolver_->ResolveFromCache(resolve_info, &addresses, net_log); + HostResolver::ResolveHostParameters parameters; + parameters.source = HostResolverSource::LOCAL_ONLY; + std::unique_ptr<HostResolver::ResolveHostRequest> request = + resolver_->CreateRequest(key.host_port_pair(), net_log, parameters); + + int rv = request->Start(base::BindOnce([](int error) { NOTREACHED(); })); DCHECK_NE(rv, ERR_IO_PENDING); if (rv != OK) return base::WeakPtr<SpdySession>(); // Check if we have a session through a domain alias. - for (AddressList::const_iterator address_it = addresses.begin(); - address_it != addresses.end(); - ++address_it) { - auto range = aliases_.equal_range(*address_it); + for (const auto& address : request->GetAddressResults().value().endpoints()) { + auto range = aliases_.equal_range(address); for (auto alias_it = range.first; alias_it != range.second; ++alias_it) { // We found an alias. const SpdySessionKey& alias_key = alias_it->second; @@ -602,4 +597,46 @@ void SpdySessionPool::CloseCurrentSessionsHelper(Error error, } } +std::unique_ptr<SpdySession> SpdySessionPool::CreateSession( + const SpdySessionKey& key, + bool is_trusted_proxy, + NetLog* net_log) { + UMA_HISTOGRAM_ENUMERATION("Net.SpdySessionGet", IMPORTED_FROM_SOCKET, + SPDY_SESSION_GET_MAX); + + return std::make_unique<SpdySession>( + key, http_server_properties_, transport_security_state_, + ssl_config_service_, quic_supported_versions_, + enable_sending_initial_data_, enable_ping_based_connection_checking_, + support_ietf_format_quic_altsvc_, is_trusted_proxy, + session_max_recv_window_size_, initial_settings_, greased_http2_frame_, + time_func_, push_delegate_, network_quality_estimator_, net_log); +} + +base::WeakPtr<SpdySession> SpdySessionPool::InsertSession( + const SpdySessionKey& key, + std::unique_ptr<SpdySession> new_session, + const NetLogWithSource& source_net_log) { + base::WeakPtr<SpdySession> available_session = new_session->GetWeakPtr(); + sessions_.insert(new_session.release()); + MapKeyToAvailableSession(key, available_session); + + source_net_log.AddEvent( + NetLogEventType::HTTP2_SESSION_POOL_IMPORTED_SESSION_FROM_SOCKET, + available_session->net_log().source().ToEventParametersCallback()); + + // Look up the IP address for this session so that we can match + // future sessions (potentially to different domains) which can + // potentially be pooled with this one. Because GetPeerAddress() + // reports the proxy's address instead of the origin server, check + // to see if this is a direct connection. + if (key.proxy_server().is_direct()) { + IPEndPoint address; + if (available_session->GetPeerAddress(&address) == OK) + aliases_.insert(AliasMap::value_type(address, key)); + } + + return available_session; +} + } // namespace net diff --git a/chromium/net/spdy/spdy_session_pool.h b/chromium/net/spdy/spdy_session_pool.h index 4ff38a3f8a8..755d0cd8392 100644 --- a/chromium/net/spdy/spdy_session_pool.h +++ b/chromium/net/spdy/spdy_session_pool.h @@ -20,6 +20,7 @@ #include "base/optional.h" #include "net/base/host_port_pair.h" #include "net/base/ip_endpoint.h" +#include "net/base/load_timing_info.h" #include "net/base/net_errors.h" #include "net/base/net_export.h" #include "net/base/network_change_notifier.h" @@ -48,6 +49,7 @@ class HttpStreamRequest; class NetLogWithSource; class NetworkQualityEstimator; class SpdySession; +class StreamSocket; class TransportSecurityState; // This is a very simple pool for open SpdySessions. @@ -95,12 +97,28 @@ class NET_EXPORT SpdySessionPool // not already be a session for the given key. // // Returns the new SpdySession. Note that the SpdySession begins reading from - // |connection| on a subsequent event loop iteration, so it may be closed - // immediately afterwards if the first read of |connection| fails. + // |client_socket_handle| on a subsequent event loop iteration, so it may be + // closed immediately afterwards if the first read of |client_socket_handle| + // fails. + base::WeakPtr<SpdySession> CreateAvailableSessionFromSocketHandle( + const SpdySessionKey& key, + bool is_trusted_proxy, + std::unique_ptr<ClientSocketHandle> client_socket_handle, + const NetLogWithSource& net_log); + + // Just like the above method, except it takes a SocketStream instead of a + // ClientSocketHandle, and separate connect timing information. When this + // constructor is used, there is no socket pool beneath the SpdySession. + // Instead, the session takes exclusive ownership of the underting socket, and + // destroying the session will directly destroy the socket, as opposed to + // disconnected it and then returning it to the socket pool. This is intended + // for use with H2 proxies, which are layered beneath the socket pools and + // can have sockets above them for tunnels, which are put in a socket pool. base::WeakPtr<SpdySession> CreateAvailableSessionFromSocket( const SpdySessionKey& key, bool is_trusted_proxy, - std::unique_ptr<ClientSocketHandle> connection, + std::unique_ptr<StreamSocket> socket_stream, + const LoadTimingInfo::ConnectTiming& connect_timing, const NetLogWithSource& net_log); // If there is an available session for |key|, return it. @@ -254,6 +272,18 @@ class NET_EXPORT SpdySessionPool const std::string& description, bool idle_only); + // Creates a new session. The session must be initialized before + // InsertSession() is invoked. + std::unique_ptr<SpdySession> CreateSession(const SpdySessionKey& key, + bool is_trusted_proxy, + NetLog* net_log); + // Adds a new session previously created with CreateSession to the pool. + // |source_net_log| is the NetLog for the object that created the session. + base::WeakPtr<SpdySession> InsertSession( + const SpdySessionKey& key, + std::unique_ptr<SpdySession> new_session, + const NetLogWithSource& source_net_log); + HttpServerProperties* http_server_properties_; TransportSecurityState* transport_security_state_; diff --git a/chromium/net/spdy/spdy_session_pool_unittest.cc b/chromium/net/spdy/spdy_session_pool_unittest.cc index 36461432fb4..d294ff9e0d8 100644 --- a/chromium/net/spdy/spdy_session_pool_unittest.cc +++ b/chromium/net/spdy/spdy_session_pool_unittest.cc @@ -15,7 +15,6 @@ #include "base/trace_event/process_memory_dump.h" #include "base/trace_event/traced_value.h" #include "build/build_config.h" -#include "net/base/completion_once_callback.h" #include "net/dns/host_cache.h" #include "net/http/http_network_session.h" #include "net/log/net_log_with_source.h" @@ -29,6 +28,7 @@ #include "net/spdy/spdy_test_util_common.h" #include "net/test/cert_test_util.h" #include "net/test/gtest_util.h" +#include "net/test/test_certificate_data.h" #include "net/test/test_data_directory.h" #include "net/test/test_with_scoped_task_environment.h" #include "testing/gmock/include/gmock/gmock.h" @@ -70,6 +70,7 @@ class SpdySessionPoolTest : public TestWithScopedTaskEnvironment { } void RunIPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type); + void RunIPPoolingDisabledTest(SSLSocketDataProvider* ssl); size_t num_active_streams(base::WeakPtr<SpdySession> session) { return session->active_streams_.size(); @@ -343,7 +344,6 @@ void SpdySessionPoolTest::RunIPPoolingTest( std::string name; std::string iplist; SpdySessionKey key; - AddressList addresses; } test_hosts[] = { {"http://www.example.org", "www.example.org", "192.0.2.33,192.168.0.1,192.168.0.5"}, @@ -360,11 +360,8 @@ void SpdySessionPoolTest::RunIPPoolingTest( // This test requires that the HostResolver cache be populated. Normal // code would have done this already, but we do it manually. - HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); - std::unique_ptr<HostResolver::Request> request; - int rv = session_deps_.host_resolver->Resolve( - info, DEFAULT_PRIORITY, &test_hosts[i].addresses, - CompletionOnceCallback(), &request, NetLogWithSource()); + int rv = session_deps_.host_resolver->LoadIntoCache( + HostPortPair(test_hosts[i].name, kTestPort), base::nullopt); EXPECT_THAT(rv, IsOk()); // Setup a SpdySessionKey. @@ -519,6 +516,54 @@ void SpdySessionPoolTest::RunIPPoolingTest( EXPECT_FALSE(HasSpdySession(spdy_session_pool_, test_hosts[2].key)); } +void SpdySessionPoolTest::RunIPPoolingDisabledTest(SSLSocketDataProvider* ssl) { + const int kTestPort = 80; + struct TestHosts { + std::string name; + std::string iplist; + SpdySessionKey key; + } test_hosts[] = { + {"www.webkit.org", "192.0.2.33,192.168.0.1,192.168.0.5"}, + {"js.webkit.com", "192.168.0.4,192.168.0.1,192.0.2.33"}, + }; + + session_deps_.host_resolver->set_synchronous_mode(true); + for (size_t i = 0; i < base::size(test_hosts); i++) { + session_deps_.host_resolver->rules()->AddIPLiteralRule( + test_hosts[i].name, test_hosts[i].iplist, std::string()); + + // This test requires that the HostResolver cache be populated. Normal + // code would have done this already, but we do it manually. + int rv = session_deps_.host_resolver->LoadIntoCache( + HostPortPair(test_hosts[i].name, kTestPort), base::nullopt); + EXPECT_THAT(rv, IsOk()); + + // Setup a SpdySessionKey + test_hosts[i].key = + SpdySessionKey(HostPortPair(test_hosts[i].name, kTestPort), + ProxyServer::Direct(), PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag()); + } + + MockRead reads[] = { + MockRead(ASYNC, ERR_IO_PENDING), + }; + StaticSocketDataProvider data(reads, base::span<MockWrite>()); + session_deps_.socket_factory->AddSocketDataProvider(&data); + session_deps_.socket_factory->AddSSLSocketDataProvider(ssl); + + CreateNetworkSession(); + + base::WeakPtr<SpdySession> spdy_session = CreateSpdySession( + http_session_.get(), test_hosts[0].key, NetLogWithSource()); + EXPECT_TRUE( + HasSpdySession(http_session_->spdy_session_pool(), test_hosts[0].key)); + EXPECT_FALSE( + HasSpdySession(http_session_->spdy_session_pool(), test_hosts[1].key)); + + http_session_->spdy_session_pool()->CloseAllSessions(); +} + TEST_F(SpdySessionPoolTest, IPPooling) { RunIPPoolingTest(SPDY_POOL_CLOSE_SESSIONS_MANUALLY); } @@ -539,7 +584,6 @@ TEST_F(SpdySessionPoolTest, IPPoolingNetLog) { std::string name; std::string iplist; SpdySessionKey key; - AddressList addresses; } test_hosts[] = { {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"}, }; @@ -550,11 +594,8 @@ TEST_F(SpdySessionPoolTest, IPPoolingNetLog) { session_deps_.host_resolver->rules()->AddIPLiteralRule( test_hosts[i].name, test_hosts[i].iplist, std::string()); - HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); - std::unique_ptr<HostResolver::Request> request; - int rv = session_deps_.host_resolver->Resolve( - info, DEFAULT_PRIORITY, &test_hosts[i].addresses, - CompletionOnceCallback(), &request, NetLogWithSource()); + int rv = session_deps_.host_resolver->LoadIntoCache( + HostPortPair(test_hosts[i].name, kTestPort), base::nullopt); EXPECT_THAT(rv, IsOk()); test_hosts[i].key = @@ -621,7 +662,6 @@ TEST_F(SpdySessionPoolTest, IPPoolingDisabled) { std::string name; std::string iplist; SpdySessionKey key; - AddressList addresses; } test_hosts[] = { {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"}, }; @@ -632,11 +672,8 @@ TEST_F(SpdySessionPoolTest, IPPoolingDisabled) { session_deps_.host_resolver->rules()->AddIPLiteralRule( test_hosts[i].name, test_hosts[i].iplist, std::string()); - HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); - std::unique_ptr<HostResolver::Request> request; - int rv = session_deps_.host_resolver->Resolve( - info, DEFAULT_PRIORITY, &test_hosts[i].addresses, - CompletionOnceCallback(), &request, NetLogWithSource()); + int rv = session_deps_.host_resolver->LoadIntoCache( + HostPortPair(test_hosts[i].name, kTestPort), base::nullopt); EXPECT_THAT(rv, IsOk()); test_hosts[i].key = @@ -688,6 +725,26 @@ TEST_F(SpdySessionPoolTest, IPPoolingDisabled) { EXPECT_NE(session0.get(), session1.get()); } +// Verifies that an SSL connection with client authentication disables SPDY IP +// pooling. +TEST_F(SpdySessionPoolTest, IPPoolingClientCert) { + SSLSocketDataProvider ssl(ASYNC, OK); + ssl.ssl_info.cert = X509Certificate::CreateFromBytes( + reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der)); + ASSERT_TRUE(ssl.ssl_info.cert); + ssl.ssl_info.client_cert_sent = true; + ssl.next_proto = kProtoHTTP2; + RunIPPoolingDisabledTest(&ssl); +} + +// Verifies that an SSL connection with channel ID disables SPDY IP pooling. +TEST_F(SpdySessionPoolTest, IPPoolingChannelID) { + SSLSocketDataProvider ssl(ASYNC, OK); + ssl.ssl_info.channel_id_sent = true; + ssl.next_proto = kProtoHTTP2; + RunIPPoolingDisabledTest(&ssl); +} + // Construct a Pool with SpdySessions in various availability states. Simulate // an IP address change. Ensure sessions gracefully shut down. Regression test // for crbug.com/379469. @@ -929,7 +986,7 @@ class SpdySessionMemoryDumpTest public testing::WithParamInterface< base::trace_event::MemoryDumpLevelOfDetail> {}; -INSTANTIATE_TEST_CASE_P( +INSTANTIATE_TEST_SUITE_P( /* no prefix */, SpdySessionMemoryDumpTest, ::testing::Values(base::trace_event::MemoryDumpLevelOfDetail::DETAILED, @@ -990,7 +1047,6 @@ TEST_F(SpdySessionPoolTest, FindAvailableSessionForWebSocket) { std::string name; std::string iplist; SpdySessionKey key; - AddressList addresses; } test_hosts[] = { {"www.example.org", "192.168.0.1"}, {"mail.example.org", "192.168.0.1"}, }; @@ -1001,11 +1057,8 @@ TEST_F(SpdySessionPoolTest, FindAvailableSessionForWebSocket) { session_deps_.host_resolver->rules()->AddIPLiteralRule( test_hosts[i].name, test_hosts[i].iplist, std::string()); - HostResolver::RequestInfo info(HostPortPair(test_hosts[i].name, kTestPort)); - std::unique_ptr<HostResolver::Request> request; - int rv = session_deps_.host_resolver->Resolve( - info, DEFAULT_PRIORITY, &test_hosts[i].addresses, - CompletionOnceCallback(), &request, NetLogWithSource()); + int rv = session_deps_.host_resolver->LoadIntoCache( + HostPortPair(test_hosts[i].name, kTestPort), base::nullopt); EXPECT_THAT(rv, IsOk()); test_hosts[i].key = diff --git a/chromium/net/spdy/spdy_session_unittest.cc b/chromium/net/spdy/spdy_session_unittest.cc index 878560da271..0b86dc391c4 100644 --- a/chromium/net/spdy/spdy_session_unittest.cc +++ b/chromium/net/spdy/spdy_session_unittest.cc @@ -134,8 +134,8 @@ class SpdySessionTest : public PlatformTest, public WithScopedTaskEnvironment { protected: SpdySessionTest() - : SpdySessionTest( - base::test::ScopedTaskEnvironment::MainThreadType::IO){}; + : SpdySessionTest(base::test::ScopedTaskEnvironment::MainThreadType::IO) { + } explicit SpdySessionTest( base::test::ScopedTaskEnvironment::MainThreadType type) @@ -368,7 +368,7 @@ class SpdySessionTestWithMockTime : public SpdySessionTest { protected: SpdySessionTestWithMockTime() : SpdySessionTest( - base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME){}; + base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {} }; // Try to create a SPDY session that will fail during @@ -950,6 +950,71 @@ TEST_F(SpdySessionTest, HeadersAfterGoAway) { histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1); } +// Regression test for https://crbug.com/903737: pushed response with status +// code different from 2xx or 3xx or 416 should be rejected. +TEST_F(SpdySessionTest, UnsupportedPushedStatusCode) { + base::HistogramTester histogram_tester; + + spdy::SpdyHeaderBlock push_promise_header_block; + push_promise_header_block[spdy::kHttp2MethodHeader] = "GET"; + spdy_util_.AddUrlToHeaderBlock(kPushedUrl, &push_promise_header_block); + spdy::SpdySerializedFrame push_promise_frame( + spdy_util_.ConstructSpdyPushPromise( + 1, 2, std::move(push_promise_header_block))); + + spdy::SpdyHeaderBlock response_header_block; + response_header_block[spdy::kHttp2StatusHeader] = "401"; + spdy::SpdySerializedFrame response_headers_frame( + spdy_util_.ConstructSpdyResponseHeaders( + 2, std::move(response_header_block), false)); + + MockRead reads[] = { + MockRead(ASYNC, ERR_IO_PENDING, 1), CreateMockRead(push_promise_frame, 2), + CreateMockRead(response_headers_frame, 4), MockRead(ASYNC, 0, 6)}; + + spdy::SpdySerializedFrame req( + spdy_util_.ConstructSpdyGet(nullptr, 0, 1, MEDIUM)); + spdy::SpdySerializedFrame priority( + spdy_util_.ConstructSpdyPriority(2, 1, IDLE, true)); + spdy::SpdySerializedFrame rst( + spdy_util_.ConstructSpdyRstStream(2, spdy::ERROR_CODE_REFUSED_STREAM)); + + MockWrite writes[] = {CreateMockWrite(req, 0), CreateMockWrite(priority, 3), + CreateMockWrite(rst, 5)}; + + SequencedSocketData data(reads, writes); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + AddSSLSocketData(); + + CreateNetworkSession(); + CreateSpdySession(); + + base::WeakPtr<SpdyStream> spdy_stream = + CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, session_, + test_url_, MEDIUM, NetLogWithSource()); + test::StreamDelegateDoNothing delegate(spdy_stream); + spdy_stream->SetDelegate(&delegate); + + spdy::SpdyHeaderBlock headers( + spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); + spdy_stream->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND); + + base::RunLoop().RunUntilIdle(); + + EXPECT_EQ(1u, spdy_stream->stream_id()); + EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_)); + + // Read the PUSH_PROMISE and HEADERS frames. + data.Resume(); + base::RunLoop().RunUntilIdle(); + + histogram_tester.ExpectBucketCount( + "Net.SpdyPushedStreamFate", + static_cast<int>(SpdyPushedStreamFate::kUnsupportedStatusCode), 1); + histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1); +} + // A session observing a network change with active streams should close // when the last active stream is closed. TEST_F(SpdySessionTest, NetworkChangeWithActiveStreams) { @@ -1076,7 +1141,7 @@ TEST_F(SpdySessionTestWithMockTime, ClientPing) { EXPECT_THAT(delegate.WaitForClose(), IsError(ERR_CONNECTION_CLOSED)); - EXPECT_FALSE(MainThreadHasPendingTask()); + EXPECT_TRUE(MainThreadIsIdle()); EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_)); EXPECT_FALSE(session_); EXPECT_FALSE(spdy_stream1); @@ -1808,7 +1873,7 @@ TEST_F(SpdySessionTestWithMockTime, FailedPing) { // Since no response to PING has been received, // CheckPingStatus() closes the connection. - EXPECT_FALSE(MainThreadHasPendingTask()); + EXPECT_TRUE(MainThreadIsIdle()); EXPECT_FALSE(HasSpdySession(spdy_session_pool_, key_)); EXPECT_FALSE(session_); EXPECT_FALSE(spdy_stream1); @@ -3470,9 +3535,8 @@ TEST_F(SpdySessionTest, CloseOneIdleConnection) { CreateNetworkSession(); - TransportClientSocketPool* pool = - http_session_->GetTransportSocketPool( - HttpNetworkSession::NORMAL_SOCKET_POOL); + TransportClientSocketPool* pool = http_session_->GetSocketPool( + HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()); // Create an idle SPDY session. CreateSpdySession(); @@ -3491,7 +3555,9 @@ TEST_F(SpdySessionTest, CloseOneIdleConnection) { CreateFromTransportSocketParams(params2), DEFAULT_PRIORITY, SocketTag(), ClientSocketPool::RespectLimits::ENABLED, - callback2.callback(), pool, NetLogWithSource())); + callback2.callback(), + ClientSocketPool::ProxyAuthCallback(), pool, + NetLogWithSource())); EXPECT_TRUE(pool->IsStalled()); // The socket pool should close the connection asynchronously and establish a @@ -3530,9 +3596,8 @@ TEST_F(SpdySessionTest, CloseOneIdleConnectionWithAlias) { CreateNetworkSession(); - TransportClientSocketPool* pool = - http_session_->GetTransportSocketPool( - HttpNetworkSession::NORMAL_SOCKET_POOL); + TransportClientSocketPool* pool = http_session_->GetSocketPool( + HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()); // Create an idle SPDY session. SpdySessionKey key1(HostPortPair("www.example.org", 80), @@ -3546,14 +3611,10 @@ TEST_F(SpdySessionTest, CloseOneIdleConnectionWithAlias) { SpdySessionKey key2(HostPortPair("mail.example.org", 80), ProxyServer::Direct(), PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse, SocketTag()); - HostResolver::RequestInfo info(key2.host_port_pair()); - AddressList addresses; - std::unique_ptr<HostResolver::Request> request; // Pre-populate the DNS cache, since a cached entry is required in order to // create the alias. - int rv = session_deps_.host_resolver->Resolve( - info, DEFAULT_PRIORITY, &addresses, CompletionOnceCallback(), &request, - NetLogWithSource()); + int rv = session_deps_.host_resolver->LoadIntoCache(key2.host_port_pair(), + base::nullopt); EXPECT_THAT(rv, IsOk()); // Get a session for |key2|, which should return the session created earlier. @@ -3577,7 +3638,9 @@ TEST_F(SpdySessionTest, CloseOneIdleConnectionWithAlias) { CreateFromTransportSocketParams(params3), DEFAULT_PRIORITY, SocketTag(), ClientSocketPool::RespectLimits::ENABLED, - callback3.callback(), pool, NetLogWithSource())); + callback3.callback(), + ClientSocketPool::ProxyAuthCallback(), pool, + NetLogWithSource())); EXPECT_TRUE(pool->IsStalled()); // The socket pool should close the connection asynchronously and establish a @@ -3619,9 +3682,8 @@ TEST_F(SpdySessionTest, CloseSessionOnIdleWhenPoolStalled) { CreateNetworkSession(); - TransportClientSocketPool* pool = - http_session_->GetTransportSocketPool( - HttpNetworkSession::NORMAL_SOCKET_POOL); + TransportClientSocketPool* pool = http_session_->GetSocketPool( + HttpNetworkSession::NORMAL_SOCKET_POOL, ProxyServer::Direct()); // Create a SPDY session. CreateSpdySession(); @@ -3657,7 +3719,9 @@ TEST_F(SpdySessionTest, CloseSessionOnIdleWhenPoolStalled) { CreateFromTransportSocketParams(params2), DEFAULT_PRIORITY, SocketTag(), ClientSocketPool::RespectLimits::ENABLED, - callback2.callback(), pool, NetLogWithSource())); + callback2.callback(), + ClientSocketPool::ProxyAuthCallback(), pool, + NetLogWithSource())); EXPECT_TRUE(pool->IsStalled()); // Running the message loop should cause the socket pool to ask the SPDY @@ -6066,11 +6130,11 @@ class SpdySessionReadIfReadyTest base::test::ScopedFeatureList scoped_feature_list_; }; -INSTANTIATE_TEST_CASE_P(/* no prefix */, - SpdySessionReadIfReadyTest, - testing::Values(READ_IF_READY_ENABLED_SUPPORTED, - READ_IF_READY_ENABLED_NOT_SUPPORTED, - READ_IF_READY_DISABLED)); +INSTANTIATE_TEST_SUITE_P(/* no prefix */, + SpdySessionReadIfReadyTest, + testing::Values(READ_IF_READY_ENABLED_SUPPORTED, + READ_IF_READY_ENABLED_NOT_SUPPORTED, + READ_IF_READY_DISABLED)); // Tests basic functionality of ReadIfReady() when it is enabled or disabled. TEST_P(SpdySessionReadIfReadyTest, ReadIfReady) { diff --git a/chromium/net/spdy/spdy_stream.cc b/chromium/net/spdy/spdy_stream.cc index 804ee2c0677..f871d64ed78 100644 --- a/chromium/net/spdy/spdy_stream.cc +++ b/chromium/net/spdy/spdy_stream.cc @@ -20,6 +20,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/trace_event/memory_usage_estimator.h" #include "base/values.h" +#include "net/base/load_timing_info.h" #include "net/log/net_log.h" #include "net/log/net_log_capture_mode.h" #include "net/log/net_log_event_type.h" @@ -105,7 +106,6 @@ SpdyStream::SpdyStream(SpdyStreamType type, request_time_(base::Time::Now()), response_state_(READY_FOR_HEADERS), io_state_(STATE_IDLE), - response_status_(OK), net_log_(net_log), raw_received_bytes_(0), raw_sent_bytes_(0), @@ -138,7 +138,8 @@ void SpdyStream::SetDelegate(Delegate* delegate) { if (io_state_ == STATE_HALF_CLOSED_LOCAL_UNCLAIMED) { DCHECK_EQ(type_, SPDY_PUSH_STREAM); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::Bind(&SpdyStream::PushedStreamReplay, GetWeakPtr())); + FROM_HERE, + base::BindOnce(&SpdyStream::PushedStreamReplay, GetWeakPtr())); } } @@ -344,9 +345,9 @@ void SpdyStream::DecreaseRecvWindowSize(int32_t delta_window_size) { if (delta_window_size > recv_window_size_ - unacked_recv_window_bytes_) { session_->ResetStream( stream_id_, ERR_SPDY_FLOW_CONTROL_ERROR, - "delta_window_size is " + base::IntToString(delta_window_size) + + "delta_window_size is " + base::NumberToString(delta_window_size) + " in DecreaseRecvWindowSize, which is larger than the receive " + - "window size of " + base::IntToString(recv_window_size_)); + "window size of " + base::NumberToString(recv_window_size_)); return; } @@ -382,44 +383,42 @@ void SpdyStream::OnHeadersReceived( base::Time response_time, base::TimeTicks recv_first_byte_time) { switch (response_state_) { - case READY_FOR_HEADERS: + case READY_FOR_HEADERS: { // No header block has been received yet. DCHECK(response_headers_.empty()); - { - spdy::SpdyHeaderBlock::const_iterator it = - response_headers.find(spdy::kHttp2StatusHeader); - if (it == response_headers.end()) { - const std::string error("Response headers do not include :status."); - LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error); - session_->ResetStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR, error); - return; - } - - int status; - if (!StringToInt(it->second, &status)) { - const std::string error("Cannot parse :status."); - LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error); - session_->ResetStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR, error); - return; - } - - base::UmaHistogramSparse("Net.SpdyResponseCode", status); - - // Include 1XX responses in the TTFB as per the resource timing spec - // for responseStart. - if (recv_first_byte_time_.is_null()) - recv_first_byte_time_ = recv_first_byte_time; - - // Ignore informational headers like 103 Early Hints. - // TODO(bnc): Add support for 103 Early Hints, https://crbug.com/671310. - // However, do not ignore 101 Switching Protocols, because broken - // servers might send this as a response to a WebSocket request, - // in which case it needs to pass through so that the WebSocket layer - // can signal an error. - if (status / 100 == 1 && status != 101) { - return; - } + spdy::SpdyHeaderBlock::const_iterator it = + response_headers.find(spdy::kHttp2StatusHeader); + if (it == response_headers.end()) { + const std::string error("Response headers do not include :status."); + LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error); + session_->ResetStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR, error); + return; + } + + int status; + if (!StringToInt(it->second, &status)) { + const std::string error("Cannot parse :status."); + LogStreamError(ERR_SPDY_PROTOCOL_ERROR, error); + session_->ResetStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR, error); + return; + } + + base::UmaHistogramSparse("Net.SpdyResponseCode", status); + + // Include 1XX responses in the TTFB as per the resource timing spec + // for responseStart. + if (recv_first_byte_time_.is_null()) + recv_first_byte_time_ = recv_first_byte_time; + + // Ignore informational headers like 103 Early Hints. + // TODO(bnc): Add support for 103 Early Hints, https://crbug.com/671310. + // However, do not ignore 101 Switching Protocols, because broken + // servers might send this as a response to a WebSocket request, + // in which case it needs to pass through so that the WebSocket layer + // can signal an error. + if (status / 100 == 1 && status != 101) { + return; } response_state_ = READY_FOR_DATA_OR_TRAILERS; @@ -453,10 +452,10 @@ void SpdyStream::OnHeadersReceived( DCHECK_NE(io_state_, STATE_IDLE); response_time_ = response_time; - SaveResponseHeaders(response_headers); + SaveResponseHeaders(response_headers, status); break; - + } case READY_FOR_DATA_OR_TRAILERS: // Second header block is trailers. if (type_ == SPDY_PUSH_STREAM) { @@ -686,7 +685,6 @@ void SpdyStream::OnClose(int status) { status = OK; } } - response_status_ = status; Delegate* delegate = delegate_; delegate_ = NULL; if (delegate) @@ -913,7 +911,8 @@ void SpdyStream::QueueNextDataFrame() { } void SpdyStream::SaveResponseHeaders( - const spdy::SpdyHeaderBlock& response_headers) { + const spdy::SpdyHeaderBlock& response_headers, + int status) { DCHECK(response_headers_.empty()); if (response_headers.find("transfer-encoding") != response_headers.end()) { session_->ResetStream(stream_id_, ERR_SPDY_PROTOCOL_ERROR, @@ -926,6 +925,17 @@ void SpdyStream::SaveResponseHeaders( response_headers_.insert(*it); } + // Reject pushed stream with unsupported status code regardless of whether + // delegate is already attached or not. + if (type_ == SPDY_PUSH_STREAM && + (status / 100 != 2 && status / 100 != 3 && status != 416)) { + SpdySession::RecordSpdyPushedStreamFateHistogram( + SpdyPushedStreamFate::kUnsupportedStatusCode); + session_->ResetStream(stream_id_, ERR_SPDY_CLIENT_REFUSED_STREAM, + "Unsupported status code for pushed stream."); + return; + } + // If delegate is not yet attached, OnHeadersReceived() will be called after // the delegate gets attached to the stream. if (!delegate_) diff --git a/chromium/net/spdy/spdy_stream.h b/chromium/net/spdy/spdy_stream.h index 35fdf444afa..2f5917513c3 100644 --- a/chromium/net/spdy/spdy_stream.h +++ b/chromium/net/spdy/spdy_stream.h @@ -370,8 +370,6 @@ class NET_EXPORT_PRIVATE SpdyStream { // yet. bool IsReservedRemote() const; - int response_status() const { return response_status_; } - void AddRawReceivedBytes(size_t received_bytes); void AddRawSentBytes(size_t sent_bytes); @@ -451,7 +449,8 @@ class NET_EXPORT_PRIVATE SpdyStream { // Saves the given headers into |response_headers_| and calls // OnHeadersReceived() on the delegate if attached. - void SaveResponseHeaders(const spdy::SpdyHeaderBlock& response_headers); + void SaveResponseHeaders(const spdy::SpdyHeaderBlock& response_headers, + int status); static std::string DescribeState(State state); @@ -511,10 +510,6 @@ class NET_EXPORT_PRIVATE SpdyStream { State io_state_; - // Since we buffer the response, we also buffer the response status. - // Not valid until the stream is closed. - int response_status_; - NetLogWithSource net_log_; base::TimeTicks send_time_; diff --git a/chromium/net/spdy/spdy_stream_unittest.cc b/chromium/net/spdy/spdy_stream_unittest.cc index 0923dd0bfbf..e0dc4d759fa 100644 --- a/chromium/net/spdy/spdy_stream_unittest.cc +++ b/chromium/net/spdy/spdy_stream_unittest.cc @@ -14,6 +14,7 @@ #include <utility> #include <vector> +#include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/run_loop.h" #include "base/stl_util.h" diff --git a/chromium/net/spdy/spdy_test_util_common.cc b/chromium/net/spdy/spdy_test_util_common.cc index c47fc5e5a10..036ad98cd32 100644 --- a/chromium/net/spdy/spdy_test_util_common.cc +++ b/chromium/net/spdy/spdy_test_util_common.cc @@ -8,6 +8,7 @@ #include <utility> #include "base/base64.h" +#include "base/bind.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" @@ -18,13 +19,17 @@ #include "net/cert/do_nothing_ct_verifier.h" #include "net/cert/mock_cert_verifier.h" #include "net/cert/signed_certificate_timestamp_and_status.h" +#include "net/dns/host_resolver.h" #include "net/http/http_cache.h" #include "net/http/http_network_transaction.h" +#include "net/http/http_proxy_connect_job.h" #include "net/log/net_log_with_source.h" #include "net/socket/client_socket_handle.h" #include "net/socket/next_proto.h" #include "net/socket/socket_tag.h" +#include "net/socket/socks_connect_job.h" #include "net/socket/ssl_client_socket.h" +#include "net/socket/ssl_connect_job.h" #include "net/socket/transport_client_socket_pool.h" #include "net/socket/transport_connect_job.h" #include "net/spdy/buffered_spdy_framer.h" @@ -324,8 +329,7 @@ SpdySessionDependencies::SpdySessionDependencies( proxy_resolution_service(std::move(proxy_resolution_service)), ssl_config_service(std::make_unique<SSLConfigServiceDefaults>()), socket_factory(std::make_unique<MockClientSocketFactory>()), - http_auth_handler_factory( - HttpAuthHandlerFactory::CreateDefault(host_resolver.get())), + http_auth_handler_factory(HttpAuthHandlerFactory::CreateDefault()), http_server_properties(std::make_unique<HttpServerPropertiesImpl>()), enable_ip_pooling(true), enable_ping(false), @@ -398,7 +402,7 @@ HttpNetworkSession::Context SpdySessionDependencies::CreateSessionContext( SpdySessionDependencies* session_deps) { HttpNetworkSession::Context context; context.client_socket_factory = session_deps->socket_factory.get(); - context.host_resolver = session_deps->host_resolver.get(); + context.host_resolver = session_deps->GetHostResolver(); context.cert_verifier = session_deps->cert_verifier.get(); context.channel_id_service = session_deps->channel_id_service.get(); context.transport_security_state = @@ -432,7 +436,7 @@ SpdyURLRequestContext::SpdyURLRequestContext() : storage_(this) { std::make_unique<DoNothingCTVerifier>()); storage_.set_ssl_config_service(std::make_unique<SSLConfigServiceDefaults>()); storage_.set_http_auth_handler_factory( - HttpAuthHandlerFactory::CreateDefault(host_resolver())); + HttpAuthHandlerFactory::CreateDefault()); storage_.set_http_server_properties( std::make_unique<HttpServerPropertiesImpl>()); storage_.set_job_factory(std::make_unique<URLRequestJobFactoryImpl>()); @@ -494,15 +498,19 @@ base::WeakPtr<SpdySession> CreateSpdySessionHelper( transport_params, nullptr, nullptr, key.host_port_pair(), ssl_config, key.privacy_mode()); int rv = connection->Init( - key.host_port_pair().ToString(), ssl_params, MEDIUM, key.socket_tag(), - ClientSocketPool::RespectLimits::ENABLED, callback.callback(), - http_session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL), + key.host_port_pair().ToString(), + TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams( + ssl_params), + MEDIUM, key.socket_tag(), ClientSocketPool::RespectLimits::ENABLED, + callback.callback(), ClientSocketPool::ProxyAuthCallback(), + http_session->GetSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL, + ProxyServer::Direct()), net_log); rv = callback.GetResult(rv); EXPECT_THAT(rv, IsOk()); base::WeakPtr<SpdySession> spdy_session = - http_session->spdy_session_pool()->CreateAvailableSessionFromSocket( + http_session->spdy_session_pool()->CreateAvailableSessionFromSocketHandle( key, is_trusted_proxy, std::move(connection), net_log); // Failure is reported asynchronously. EXPECT_TRUE(spdy_session); @@ -605,7 +613,7 @@ base::WeakPtr<SpdySession> CreateFakeSpdySessionHelper( handle->SetSocket(std::make_unique<FakeSpdySessionClientSocket>( expected_status == OK ? ERR_IO_PENDING : expected_status)); base::WeakPtr<SpdySession> spdy_session = - pool->CreateAvailableSessionFromSocket( + pool->CreateAvailableSessionFromSocketHandle( key, /*is_trusted_proxy=*/false, std::move(handle), NetLogWithSource()); // Failure is reported asynchronously. @@ -951,7 +959,8 @@ spdy::SpdySerializedFrame SpdyTestUtil::ConstructSpdyReplyError( block["hello"] = "bye"; AppendToHeaderBlock(extra_headers, extra_header_count, &block); - return ConstructSpdyReply(stream_id, std::move(block)); + spdy::SpdyHeadersIR reply(stream_id, std::move(block)); + return spdy::SpdySerializedFrame(response_spdy_framer_.SerializeFrame(reply)); } spdy::SpdySerializedFrame SpdyTestUtil::ConstructSpdyReplyError(int stream_id) { @@ -1065,7 +1074,7 @@ spdy::SpdyHeaderBlock SpdyTestUtil::ConstructHeaderBlock( headers[spdy::kHttp2SchemeHeader] = scheme.c_str(); headers[spdy::kHttp2PathHeader] = path.c_str(); if (content_length) { - std::string length_str = base::Int64ToString(*content_length); + std::string length_str = base::NumberToString(*content_length); headers["content-length"] = length_str; } return headers; diff --git a/chromium/net/spdy/spdy_test_util_common.h b/chromium/net/spdy/spdy_test_util_common.h index 0f480ab4289..a15d79ea052 100644 --- a/chromium/net/spdy/spdy_test_util_common.h +++ b/chromium/net/spdy/spdy_test_util_common.h @@ -52,6 +52,7 @@ class CTVerifier; class CTPolicyEnforcer; class HashValue; class HostPortPair; +class HostResolver; class NetLogWithSource; class SpdySessionKey; class SpdyStream; @@ -183,6 +184,11 @@ struct SpdySessionDependencies { ~SpdySessionDependencies(); + HostResolver* GetHostResolver() { + return alternate_host_resolver ? alternate_host_resolver.get() + : host_resolver.get(); + } + static std::unique_ptr<HttpNetworkSession> SpdyCreateSession( SpdySessionDependencies* session_deps); @@ -198,6 +204,8 @@ struct SpdySessionDependencies { // NOTE: host_resolver must be ordered before http_auth_handler_factory. std::unique_ptr<MockHostResolverBase> host_resolver; + // For using a HostResolver not derived from MockHostResolverBase. + std::unique_ptr<HostResolver> alternate_host_resolver; std::unique_ptr<CertVerifier> cert_verifier; std::unique_ptr<ChannelIDService> channel_id_service; std::unique_ptr<TransportSecurityState> transport_security_state; diff --git a/chromium/net/spdy/spdy_write_queue_unittest.cc b/chromium/net/spdy/spdy_write_queue_unittest.cc index b561ae9c7f7..e5d6a258500 100644 --- a/chromium/net/spdy/spdy_write_queue_unittest.cc +++ b/chromium/net/spdy/spdy_write_queue_unittest.cc @@ -9,6 +9,7 @@ #include <string> #include <utility> +#include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/stl_util.h" @@ -44,7 +45,7 @@ std::unique_ptr<SpdyBufferProducer> StringToProducer(const std::string& s) { // Makes a SpdyBufferProducer producing a frame with the data in the // given int (converted to a string). std::unique_ptr<SpdyBufferProducer> IntToProducer(int i) { - return StringToProducer(base::IntToString(i)); + return StringToProducer(base::NumberToString(i)); } // Producer whose produced buffer will enqueue yet another buffer into the |