diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-12 14:27:29 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-10-13 09:35:20 +0000 |
commit | c30a6232df03e1efbd9f3b226777b07e087a1122 (patch) | |
tree | e992f45784689f373bcc38d1b79a239ebe17ee23 /chromium/net/spdy | |
parent | 7b5b123ac58f58ffde0f4f6e488bcd09aa4decd3 (diff) | |
download | qtwebengine-chromium-c30a6232df03e1efbd9f3b226777b07e087a1122.tar.gz |
BASELINE: Update Chromium to 85.0.4183.14085-based
Change-Id: Iaa42f4680837c57725b1344f108c0196741f6057
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/spdy')
-rw-r--r-- | chromium/net/spdy/OWNERS | 1 | ||||
-rw-r--r-- | chromium/net/spdy/platform/impl/spdy_logging_impl.h | 2 | ||||
-rw-r--r-- | chromium/net/spdy/platform/impl/spdy_macros_impl.h | 1 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_network_transaction_unittest.cc | 99 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_session.cc | 156 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_session.h | 7 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_session_unittest.cc | 212 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_stream.cc | 13 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_stream.h | 4 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_stream_test_util.cc | 6 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_stream_test_util.h | 6 | ||||
-rw-r--r-- | chromium/net/spdy/spdy_stream_unittest.cc | 63 |
12 files changed, 431 insertions, 139 deletions
diff --git a/chromium/net/spdy/OWNERS b/chromium/net/spdy/OWNERS index b608e73eafc..e041764c5b6 100644 --- a/chromium/net/spdy/OWNERS +++ b/chromium/net/spdy/OWNERS @@ -2,7 +2,6 @@ birenroy@chromium.org bnc@chromium.org dahollings@chromium.org diannahu@chromium.org -rch@chromium.org yasong@chromium.org # COMPONENT: Internals>Network>HTTP2 diff --git a/chromium/net/spdy/platform/impl/spdy_logging_impl.h b/chromium/net/spdy/platform/impl/spdy_logging_impl.h index cc79854df01..a9ba72bbb76 100644 --- a/chromium/net/spdy/platform/impl/spdy_logging_impl.h +++ b/chromium/net/spdy/platform/impl/spdy_logging_impl.h @@ -5,7 +5,9 @@ #ifndef NET_SPDY_PLATFORM_IMPL_SPDY_LOGGING_IMPL_H_ #define NET_SPDY_PLATFORM_IMPL_SPDY_LOGGING_IMPL_H_ +#include "base/check_op.h" #include "base/logging.h" +#include "base/notreached.h" #include "build/build_config.h" #include "net/base/net_export.h" diff --git a/chromium/net/spdy/platform/impl/spdy_macros_impl.h b/chromium/net/spdy/platform/impl/spdy_macros_impl.h index fb286bc9ab0..3c1da5102c3 100644 --- a/chromium/net/spdy/platform/impl/spdy_macros_impl.h +++ b/chromium/net/spdy/platform/impl/spdy_macros_impl.h @@ -6,7 +6,6 @@ #define NET_SPDY_PLATFORM_IMPL_SPDY_MACROS_IMPL_H_ #include "base/compiler_specific.h" -#include "base/logging.h" #define SPDY_MUST_USE_RESULT_IMPL WARN_UNUSED_RESULT #define SPDY_UNUSED_IMPL ALLOW_UNUSED_TYPE diff --git a/chromium/net/spdy/spdy_network_transaction_unittest.cc b/chromium/net/spdy/spdy_network_transaction_unittest.cc index 1da839038e0..0caead021a1 100644 --- a/chromium/net/spdy/spdy_network_transaction_unittest.cc +++ b/chromium/net/spdy/spdy_network_transaction_unittest.cc @@ -41,6 +41,8 @@ #include "net/http/http_response_info.h" #include "net/http/http_server_properties.h" #include "net/http/http_transaction_test_util.h" +#include "net/http/test_upload_data_stream_not_allow_http1.h" +#include "net/http/transport_security_state.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_with_source.h" #include "net/log/test_net_log.h" @@ -411,7 +413,8 @@ class SpdyNetworkTransactionTest : public TestWithTaskEnvironment { SpdySessionKey key(HostPortPair::FromURL(request_.url), ProxyServer::Direct(), PRIVACY_MODE_DISABLED, SpdySessionKey::IsProxySession::kFalse, SocketTag(), - NetworkIsolationKey(), false /* disable_secure_dns */); + request_.network_isolation_key, + false /* disable_secure_dns */); HttpNetworkSession* session = helper.session(); base::WeakPtr<SpdySession> spdy_session = session->spdy_session_pool()->FindAvailableSession( @@ -5177,7 +5180,7 @@ TEST_F(SpdyNetworkTransactionTest, FailOnGoAway) { NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr); helper.RunToCompletion(&data); TransactionHelperResult out = helper.output(); - EXPECT_THAT(out.rv, IsError(ERR_ABORTED)); + EXPECT_THAT(out.rv, IsError(ERR_HTTP2_PROTOCOL_ERROR)); } // Request should be retried on a new connection upon receiving a GOAWAY frame @@ -6414,28 +6417,53 @@ struct PushUrlTestParams { const char* url_to_fetch; const char* url_to_push; bool client_cert_sent; + bool expect_ct_error; SpdyPushedStreamFate expected_fate; } push_url_test_cases[] = { // http scheme cannot be pushed (except by trusted proxy). - {"https://www.example.org/foo.html", "http://www.example.org/foo.js", false, + {"https://www.example.org/foo.html", "http://www.example.org/foo.js", + false /* client_cert_sent */, false /* expect_ct_error */, SpdyPushedStreamFate::kNonHttpsPushedScheme}, // ftp scheme cannot be pushed. - {"https://www.example.org/foo.html", "ftp://www.example.org/foo.js", false, + {"https://www.example.org/foo.html", "ftp://www.example.org/foo.js", + false /* client_cert_sent */, false /* expect_ct_error */, SpdyPushedStreamFate::kInvalidUrl}, // Cross subdomain, certificate not valid. {"https://www.example.org/foo.html", "https://blat.www.example.org/foo.js", - false, SpdyPushedStreamFate::kCertificateMismatch}, + false /* client_cert_sent */, false /* expect_ct_error */, + SpdyPushedStreamFate::kCertificateMismatch}, // Cross domain, certificate not valid. - {"https://www.example.org/foo.html", "https://www.foo.com/foo.js", false, + {"https://www.example.org/foo.html", "https://www.foo.com/foo.js", + false /* client_cert_sent */, false /* expect_ct_error */, SpdyPushedStreamFate::kCertificateMismatch}, // Cross domain, certificate valid, but cross-origin push is rejected on a // connection with client certificate. {"https://www.example.org/foo.html", "https://mail.example.org/foo.js", - true, SpdyPushedStreamFate::kCertificateMismatch}}; + true /* client_cert_sent */, false /* expect_ct_error */, + SpdyPushedStreamFate::kCertificateMismatch}, + // Cross domain, certificate valid, but cross-origin push is rejected on a + // connection with an Expect-CT error. + {"https://www.example.org/foo.html", "https://mail.example.org/foo.js", + false /* client_cert_sent */, true /* expect_ct_error */, + SpdyPushedStreamFate::kCertificateMismatch}}; class SpdyNetworkTransactionPushUrlTest : public SpdyNetworkTransactionTest, public ::testing::WithParamInterface<PushUrlTestParams> { + public: + SpdyNetworkTransactionPushUrlTest() { + // Set features needed for the |expect_ct_error| case, where it's important + // to check that NetworkIsolationKeys are respected. + feature_list_.InitWithFeatures( + /* enabled_features */ + {TransportSecurityState::kDynamicExpectCTFeature, + features::kPartitionExpectCTStateByNetworkIsolationKey, + features::kPartitionConnectionsByNetworkIsolationKey, + features::kPartitionSSLSessionsByNetworkIsolationKey}, + /* disabled_features */ + {}); + } + protected: // In this test we want to verify that we can't accidentally push content // which can't be pushed by this content server. @@ -6478,6 +6506,9 @@ class SpdyNetworkTransactionPushUrlTest SequencedSocketData data(reads, writes); request_.url = GURL(GetParam().url_to_fetch); + // Set a NetworkIsolationKey for the |expect_ct_error| case, to make sure + // NetworkIsolationKeys are respected. + request_.network_isolation_key = NetworkIsolationKey::CreateTransient(); // Enable cross-origin push. Since we are not using a proxy, this should // not actually enable cross-origin SPDY push. @@ -6487,15 +6518,33 @@ class SpdyNetworkTransactionPushUrlTest "https://123.45.67.89:443", net::ProxyServer::SCHEME_HTTP)); session_deps->proxy_resolution_service->SetProxyDelegate( proxy_delegate.get()); - NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, - std::move(session_deps)); - - helper.RunPreTestSetup(); auto ssl_provider = std::make_unique<SSLSocketDataProvider>(ASYNC, OK); ssl_provider->ssl_info.client_cert_sent = GetParam().client_cert_sent; ssl_provider->ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + if (GetParam().expect_ct_error) { + ssl_provider->ssl_info.ct_policy_compliance = + ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; + ssl_provider->ssl_info.is_issued_by_known_root = true; + + session_deps->transport_security_state->AddExpectCT( + "mail.example.org", + base::Time::Now() + base::TimeDelta::FromDays(1) /* expiry */, true, + GURL(), request_.network_isolation_key); + } + + NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, + std::move(session_deps)); + + helper.RunPreTestSetup(); + + if (GetParam().expect_ct_error) { + ssl_provider->ssl_info.ct_policy_compliance = + ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; + ssl_provider->ssl_info.is_issued_by_known_root = true; + } + helper.AddDataWithSSLSocketDataProvider(&data, std::move(ssl_provider)); HttpNetworkTransaction* trans = helper.trans(); @@ -6533,6 +6582,8 @@ class SpdyNetworkTransactionPushUrlTest 1); histogram_tester.ExpectTotalCount("Net.SpdyPushedStreamFate", 1); } + + base::test::ScopedFeatureList feature_list_; }; INSTANTIATE_TEST_SUITE_P(All, @@ -10438,4 +10489,30 @@ TEST_F(SpdyNetworkTransactionTest, OnDataSentDoesNotCrashWithGreasedFrameType) { helper.VerifyDataConsumed(); } +TEST_F(SpdyNetworkTransactionTest, NotAllowHTTP1NotBlockH2Post) { + spdy::SpdySerializedFrame req( + spdy_util_.ConstructChunkedSpdyPost(nullptr, 0)); + spdy::SpdySerializedFrame body(spdy_util_.ConstructSpdyDataFrame(1, true)); + MockWrite writes[] = { + CreateMockWrite(req, 0), CreateMockWrite(body, 1), // POST upload frame + }; + spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyPostReply(nullptr, 0)); + MockRead reads[] = { + CreateMockRead(resp, 2), CreateMockRead(body, 3), + MockRead(ASYNC, 0, 4) // EOF + }; + SequencedSocketData data(reads, writes); + + request_.method = "POST"; + UploadDataStreamNotAllowHTTP1 upload_data(kUploadData); + request_.upload_data_stream = &upload_data; + + NormalSpdyTransactionHelper helper(request_, DEFAULT_PRIORITY, log_, nullptr); + helper.RunToCompletion(&data); + TransactionHelperResult out = helper.output(); + EXPECT_THAT(out.rv, IsOk()); + EXPECT_EQ("HTTP/1.1 200", out.status_line); + EXPECT_EQ("hello!", out.response_data); +} + } // namespace net diff --git a/chromium/net/spdy/spdy_session.cc b/chromium/net/spdy/spdy_session.cc index b61963f6716..eef09df91ed 100644 --- a/chromium/net/spdy/spdy_session.cc +++ b/chromium/net/spdy/spdy_session.cc @@ -27,6 +27,7 @@ #include "base/trace_event/memory_usage_estimator.h" #include "base/trace_event/trace_event.h" #include "base/values.h" +#include "net/base/features.h" #include "net/base/url_util.h" #include "net/cert/asn1_util.h" #include "net/cert/cert_verify_result.h" @@ -836,11 +837,13 @@ void SpdyStreamRequest::OnConfirmHandshakeComplete(int rv) { } // static -bool SpdySession::CanPool(TransportSecurityState* transport_security_state, - const SSLInfo& ssl_info, - const SSLConfigService& ssl_config_service, - const std::string& old_hostname, - const std::string& new_hostname) { +bool SpdySession::CanPool( + TransportSecurityState* transport_security_state, + const SSLInfo& ssl_info, + const SSLConfigService& ssl_config_service, + const std::string& old_hostname, + const std::string& new_hostname, + const net::NetworkIsolationKey& network_isolation_key) { // Pooling is prohibited if the server cert is not valid for the new domain, // and for connections on which client certs were sent. It is also prohibited // when channel ID was sent if the hosts are from different eTLDs+1. @@ -875,7 +878,7 @@ bool SpdySession::CanPool(TransportSecurityState* transport_security_state, ssl_info.public_key_hashes, ssl_info.cert.get(), ssl_info.unverified_cert.get(), ssl_info.signed_certificate_timestamps, TransportSecurityState::DISABLE_EXPECT_CT_REPORTS, - ssl_info.ct_policy_compliance)) { + ssl_info.ct_policy_compliance, network_isolation_key)) { case TransportSecurityState::CT_REQUIREMENTS_NOT_MET: return false; case TransportSecurityState::CT_REQUIREMENTS_MET: @@ -1098,7 +1101,8 @@ bool SpdySession::VerifyDomainAuthentication(const std::string& domain) const { return true; // This is not a secure session, so all domains are okay. return CanPool(transport_security_state_, ssl_info, *ssl_config_service_, - host_port_pair().host(), domain); + host_port_pair().host(), domain, + spdy_session_key_.network_isolation_key()); } void SpdySession::EnqueueStreamWrite( @@ -1165,15 +1169,13 @@ std::unique_ptr<spdy::SpdySerializedFrame> SpdySession::CreateHeaders( priority_dependency_state_.OnStreamCreation( stream_id, spdy_priority, &parent_stream_id, &weight, &exclusive); - if (net_log().IsCapturing()) { - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_HEADERS, - [&](NetLogCaptureMode capture_mode) { - return NetLogSpdyHeadersSentParams( - &block, (flags & spdy::CONTROL_FLAG_FIN) != 0, - stream_id, has_priority, weight, parent_stream_id, - exclusive, source_dependency, capture_mode); - }); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_HEADERS, + [&](NetLogCaptureMode capture_mode) { + return NetLogSpdyHeadersSentParams( + &block, (flags & spdy::CONTROL_FLAG_FIN) != 0, + stream_id, has_priority, weight, parent_stream_id, + exclusive, source_dependency, capture_mode); + }); spdy::SpdyHeadersIR headers(stream_id, std::move(block)); headers.set_has_priority(has_priority); @@ -1241,7 +1243,7 @@ std::unique_ptr<SpdyBuffer> SpdySession::CreateDataBuffer( // Even though we're currently stalled only by the stream, we // might end up being stalled by the session also. QueueSendStalledStream(*stream); - net_log().AddEventWithIntParams( + net_log_.AddEventWithIntParams( NetLogEventType::HTTP2_SESSION_STREAM_STALLED_BY_STREAM_SEND_WINDOW, "stream_id", stream_id); return std::unique_ptr<SpdyBuffer>(); @@ -1253,7 +1255,7 @@ std::unique_ptr<SpdyBuffer> SpdySession::CreateDataBuffer( if (send_stalled_by_session) { stream->set_send_stalled_by_flow_control(true); QueueSendStalledStream(*stream); - net_log().AddEventWithIntParams( + net_log_.AddEventWithIntParams( NetLogEventType::HTTP2_SESSION_STREAM_STALLED_BY_SESSION_SEND_WINDOW, "stream_id", stream_id); return std::unique_ptr<SpdyBuffer>(); @@ -1268,12 +1270,10 @@ std::unique_ptr<SpdyBuffer> SpdySession::CreateDataBuffer( if (effective_len < len) flags = static_cast<spdy::SpdyDataFlags>(flags & ~spdy::DATA_FLAG_FIN); - if (net_log().IsCapturing()) { - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_DATA, [&] { - return NetLogSpdyDataParams(stream_id, effective_len, - (flags & spdy::DATA_FLAG_FIN) != 0); - }); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_DATA, [&] { + return NetLogSpdyDataParams(stream_id, effective_len, + (flags & spdy::DATA_FLAG_FIN) != 0); + }); // Send PrefacePing for DATA_FRAMEs with nonzero payload size. if (effective_len > 0) @@ -1316,6 +1316,9 @@ void SpdySession::UpdateStreamPriority(SpdyStream* stream, DCHECK(IsStreamActive(stream_id)); + if (base::FeatureList::IsEnabled(features::kAvoidH2Reprioritization)) + return; + auto updates = priority_dependency_state_.OnStreamUpdate( stream_id, ConvertRequestPriorityToSpdyPriority(new_priority)); for (auto u : updates) { @@ -1745,13 +1748,12 @@ int SpdySession::TryCreateStream( return CreateStream(*request, stream); } - if (net_log().IsCapturing()) { - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_STALLED_MAX_STREAMS, [&] { - return NetLogSpdySessionStalledParams( - active_streams_.size(), created_streams_.size(), num_pushed_streams_, - max_concurrent_streams_, request->url().spec()); - }); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_STALLED_MAX_STREAMS, [&] { + return NetLogSpdySessionStalledParams( + active_streams_.size(), created_streams_.size(), num_pushed_streams_, + max_concurrent_streams_, request->url().spec()); + }); + RequestPriority priority = request->priority(); CHECK_GE(priority, MINIMUM_PRIORITY); CHECK_LE(priority, MAXIMUM_PRIORITY); @@ -1999,7 +2001,8 @@ void SpdySession::TryCreatePushStream(spdy::SpdyStreamId stream_id, SSLInfo ssl_info; CHECK(GetSSLInfo(&ssl_info)); if (!CanPool(transport_security_state_, ssl_info, *ssl_config_service_, - associated_url.host(), gurl.host())) { + associated_url.host(), gurl.host(), + spdy_session_key_.network_isolation_key())) { RecordSpdyPushedStreamFateHistogram( SpdyPushedStreamFate::kCertificateMismatch); EnqueueResetStreamFrame(stream_id, request_priority, @@ -2167,7 +2170,7 @@ void SpdySession::EnqueueResetStreamFrame(spdy::SpdyStreamId stream_id, const std::string& description) { DCHECK_NE(stream_id, 0u); - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_SEND_RST_STREAM, [&] { + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_RST_STREAM, [&] { return NetLogSpdySendRstStreamParams(stream_id, error_code, description); }); @@ -2184,7 +2187,7 @@ void SpdySession::EnqueuePriorityFrame(spdy::SpdyStreamId stream_id, spdy::SpdyStreamId dependency_id, int weight, bool exclusive) { - net_log().AddEvent(NetLogEventType::HTTP2_STREAM_SEND_PRIORITY, [&] { + net_log_.AddEvent(NetLogEventType::HTTP2_STREAM_SEND_PRIORITY, [&] { return NetLogSpdyPriorityParams(stream_id, dependency_id, weight, exclusive); }); @@ -2600,7 +2603,7 @@ void SpdySession::HandleSetting(uint32_t id, uint32_t value) { break; case spdy::SETTINGS_INITIAL_WINDOW_SIZE: { if (value > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) { - net_log().AddEventWithIntParams( + net_log_.AddEventWithIntParams( NetLogEventType::HTTP2_SESSION_INITIAL_WINDOW_SIZE_OUT_OF_RANGE, "initial_window_size", value); return; @@ -2612,7 +2615,7 @@ void SpdySession::HandleSetting(uint32_t id, uint32_t value) { static_cast<int32_t>(value) - stream_initial_send_window_size_; stream_initial_send_window_size_ = static_cast<int32_t>(value); UpdateStreamsSendWindowSize(delta_window_size); - net_log().AddEventWithIntParams( + net_log_.AddEventWithIntParams( NetLogEventType::HTTP2_SESSION_UPDATE_STREAMS_SEND_WINDOW_SIZE, "delta_window_size", delta_window_size); break; @@ -2697,11 +2700,10 @@ void SpdySession::WritePingFrame(spdy::SpdyPingId unique_id, bool is_ack) { EnqueueSessionWrite(HIGHEST, spdy::SpdyFrameType::PING, std::move(ping_frame)); - if (net_log().IsCapturing()) { - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_PING, [&] { - return NetLogSpdyPingParams(unique_id, is_ack, "sent"); - }); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_PING, [&] { + return NetLogSpdyPingParams(unique_id, is_ack, "sent"); + }); + if (!is_ack) { DCHECK(!ping_in_flight_); @@ -3078,7 +3080,7 @@ void SpdySession::OnRstStream(spdy::SpdyStreamId stream_id, spdy::SpdyErrorCode error_code) { CHECK(in_io_loop_); - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_RST_STREAM, [&] { + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_RST_STREAM, [&] { return NetLogSpdyRecvRstStreamParams(stream_id, error_code); }); @@ -3101,19 +3103,15 @@ void SpdySession::OnRstStream(spdy::SpdyStreamId stream_id, CloseActiveStreamIterator(it, ERR_HTTP2_SERVER_REFUSED_STREAM); } else if (error_code == spdy::ERROR_CODE_HTTP_1_1_REQUIRED) { // TODO(bnc): Record histogram with number of open streams capped at 50. - if (net_log().IsCapturing()) { - it->second->LogStreamError(ERR_HTTP_1_1_REQUIRED, - "Closing session because server reset stream " - "with ERR_HTTP_1_1_REQUIRED."); - } + it->second->LogStreamError(ERR_HTTP_1_1_REQUIRED, + "Closing session because server reset stream " + "with ERR_HTTP_1_1_REQUIRED."); DoDrainSession(ERR_HTTP_1_1_REQUIRED, "HTTP_1_1_REQUIRED for stream."); } else { RecordProtocolErrorHistogram( PROTOCOL_ERROR_RST_STREAM_FOR_NON_ACTIVE_STREAM); - if (net_log().IsCapturing()) { - it->second->LogStreamError(ERR_HTTP2_PROTOCOL_ERROR, - "Server reset stream."); - } + it->second->LogStreamError(ERR_HTTP2_PROTOCOL_ERROR, + "Server reset stream."); // TODO(mbelshe): Map from Spdy-protocol errors to something sensical. // For now, it doesn't matter much - it is a protocol error. CloseActiveStreamIterator(it, ERR_HTTP2_PROTOCOL_ERROR); @@ -3142,7 +3140,7 @@ void SpdySession::OnGoAway(spdy::SpdyStreamId last_accepted_stream_id, } else if (error_code == spdy::ERROR_CODE_NO_ERROR) { StartGoingAway(last_accepted_stream_id, ERR_HTTP2_SERVER_REFUSED_STREAM); } else { - StartGoingAway(last_accepted_stream_id, ERR_ABORTED); + StartGoingAway(last_accepted_stream_id, ERR_HTTP2_PROTOCOL_ERROR); } // This is to handle the case when we already don't have any active // streams (i.e., StartGoingAway() did nothing). Otherwise, we have @@ -3174,11 +3172,10 @@ void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id, size_t len) { CHECK(in_io_loop_); DCHECK_LT(len, 1u << 24); - if (net_log().IsCapturing()) { - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_DATA, [&] { - return NetLogSpdyDataParams(stream_id, len, false); - }); - } + + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_DATA, [&] { + return NetLogSpdyDataParams(stream_id, len, false); + }); // Build the buffer as early as possible so that we go through the // session flow control checks and update @@ -3213,11 +3210,8 @@ void SpdySession::OnStreamFrameData(spdy::SpdyStreamId stream_id, void SpdySession::OnStreamEnd(spdy::SpdyStreamId stream_id) { CHECK(in_io_loop_); - if (net_log().IsCapturing()) { - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_DATA, [&] { - return NetLogSpdyDataParams(stream_id, 0, true); - }); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_DATA, + [&] { return NetLogSpdyDataParams(stream_id, 0, true); }); auto it = active_streams_.find(stream_id); // By the time data comes in, the stream may already be inactive. @@ -3249,10 +3243,8 @@ void SpdySession::OnStreamPadding(spdy::SpdyStreamId stream_id, size_t len) { void SpdySession::OnSettings() { CHECK(in_io_loop_); - if (net_log_.IsCapturing()) { - net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTINGS); - net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_SETTINGS_ACK); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTINGS); + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_SEND_SETTINGS_ACK); // Send an acknowledgment of the setting. spdy::SpdySettingsIR settings_ir; @@ -3265,8 +3257,7 @@ void SpdySession::OnSettings() { void SpdySession::OnSettingsAck() { CHECK(in_io_loop_); - if (net_log_.IsCapturing()) - net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTINGS_ACK); + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_SETTINGS_ACK); } void SpdySession::OnSetting(spdy::SpdySettingsId id, uint32_t value) { @@ -3329,14 +3320,12 @@ void SpdySession::OnPushPromise(spdy::SpdyStreamId stream_id, spdy::SpdyHeaderBlock headers) { CHECK(in_io_loop_); - if (net_log_.IsCapturing()) { - net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_PUSH_PROMISE, - [&](NetLogCaptureMode capture_mode) { - return NetLogSpdyPushPromiseReceivedParams( - &headers, stream_id, promised_stream_id, - capture_mode); - }); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_PUSH_PROMISE, + [&](NetLogCaptureMode capture_mode) { + return NetLogSpdyPushPromiseReceivedParams( + &headers, stream_id, promised_stream_id, + capture_mode); + }); TryCreatePushStream(promised_stream_id, stream_id, std::move(headers)); } @@ -3351,13 +3340,11 @@ void SpdySession::OnHeaders(spdy::SpdyStreamId stream_id, base::TimeTicks recv_first_byte_time) { CHECK(in_io_loop_); - if (net_log().IsCapturing()) { - net_log().AddEvent(NetLogEventType::HTTP2_SESSION_RECV_HEADERS, - [&](NetLogCaptureMode capture_mode) { - return NetLogSpdyHeadersReceivedParams( - &headers, fin, stream_id, capture_mode); - }); - } + net_log_.AddEvent(NetLogEventType::HTTP2_SESSION_RECV_HEADERS, + [&](NetLogCaptureMode capture_mode) { + return NetLogSpdyHeadersReceivedParams( + &headers, fin, stream_id, capture_mode); + }); auto it = active_streams_.find(stream_id); if (it == active_streams_.end()) { @@ -3412,7 +3399,8 @@ void SpdySession::OnAltSvc( if (!GetSSLInfo(&ssl_info)) return; if (!CanPool(transport_security_state_, ssl_info, *ssl_config_service_, - host_port_pair().host(), gurl.host())) { + host_port_pair().host(), gurl.host(), + spdy_session_key_.network_isolation_key())) { return; } scheme_host_port = url::SchemeHostPort(gurl); diff --git a/chromium/net/spdy/spdy_session.h b/chromium/net/spdy/spdy_session.h index 9c1c504859f..35bb0836e02 100644 --- a/chromium/net/spdy/spdy_session.h +++ b/chromium/net/spdy/spdy_session.h @@ -329,7 +329,8 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, const SSLInfo& ssl_info, const SSLConfigService& ssl_config_service, const std::string& old_hostname, - const std::string& new_hostname); + const std::string& new_hostname, + const net::NetworkIsolationKey& network_isolation_key); // Create a new SpdySession. // |spdy_session_key| is the host/port that this session connects to, privacy @@ -1106,8 +1107,8 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, // and maximum HPACK dynamic table size. const spdy::SettingsMap initial_settings_; - // If set, an HTTP/2 frame with a reserved frame type will be sent after every - // valid HTTP/2 frame. See + // If set, an HTTP/2 frame with a reserved frame type will be sent after + // every HTTP/2 SETTINGS frame and before every HTTP/2 DATA frame. See // https://tools.ietf.org/html/draft-bishop-httpbis-grease-00. const base::Optional<SpdySessionPool::GreasedHttp2Frame> greased_http2_frame_; diff --git a/chromium/net/spdy/spdy_session_unittest.cc b/chromium/net/spdy/spdy_session_unittest.cc index bffd8d2f7d3..65c23fa9999 100644 --- a/chromium/net/spdy/spdy_session_unittest.cc +++ b/chromium/net/spdy/spdy_session_unittest.cc @@ -22,6 +22,7 @@ #include "net/base/host_port_pair.h" #include "net/base/io_buffer.h" #include "net/base/ip_endpoint.h" +#include "net/base/network_isolation_key.h" #include "net/base/privacy_mode.h" #include "net/base/proxy_delegate.h" #include "net/base/proxy_server.h" @@ -2778,6 +2779,63 @@ TEST_F(SpdySessionTest, VerifyDomainAuthentication) { EXPECT_FALSE(session_->VerifyDomainAuthentication("mail.google.com")); } +// Check that VerifyDomainAuthentication respects Expect-CT failures, and uses +// the correct NetworkIsolationKey. +TEST_F(SpdySessionTest, VerifyDomainAuthenticationExpectCT) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + /* enabled_features */ + {TransportSecurityState::kDynamicExpectCTFeature, + features::kPartitionExpectCTStateByNetworkIsolationKey, + features::kPartitionConnectionsByNetworkIsolationKey, + features::kPartitionSSLSessionsByNetworkIsolationKey}, + /* disabled_features */ + {}); + + key_ = SpdySessionKey(HostPortPair::FromURL(test_url_), ProxyServer::Direct(), + PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag(), + NetworkIsolationKey::CreateTransient(), + false /* disable_secure_dns */); + ssl_.ssl_info.ct_policy_compliance = + ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; + ssl_.ssl_info.is_issued_by_known_root = true; + + // Need to create this after enabling features. + session_deps_.transport_security_state = + std::make_unique<TransportSecurityState>(); + + SequencedSocketData data; + session_deps_.socket_factory->AddSocketDataProvider(&data); + + AddSSLSocketData(); + + CreateNetworkSession(); + CreateSpdySession(); + + EXPECT_TRUE(session_->VerifyDomainAuthentication("www.example.org")); + EXPECT_TRUE(session_->VerifyDomainAuthentication("mail.example.org")); + EXPECT_TRUE(session_->VerifyDomainAuthentication("mail.example.com")); + EXPECT_FALSE(session_->VerifyDomainAuthentication("mail.google.com")); + + // Add Expect-CT data for all three hosts that passed the above checks, using + // different NetworkIsolationKeys. + const base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1); + session_deps_.transport_security_state->AddExpectCT( + "www.example.org", expiry, true, GURL(), NetworkIsolationKey()); + session_deps_.transport_security_state->AddExpectCT( + "mail.example.org", expiry, true, GURL(), key_.network_isolation_key()); + session_deps_.transport_security_state->AddExpectCT( + "mail.example.com", expiry, true, GURL(), + NetworkIsolationKey::CreateTransient()); + + // The host with the Expect-CT data that matches the SpdySession's should fail + // the check now. + EXPECT_TRUE(session_->VerifyDomainAuthentication("www.example.org")); + EXPECT_FALSE(session_->VerifyDomainAuthentication("mail.example.org")); + EXPECT_TRUE(session_->VerifyDomainAuthentication("mail.example.com")); +} + TEST_F(SpdySessionTest, CloseTwoStalledCreateStream) { // TODO(rtenneti): Define a helper class/methods and move the common code in // this file. @@ -6232,7 +6290,11 @@ class AltSvcFrameTest : public SpdySessionTest { "alternative.example.org", 443, 86400, - spdy::SpdyAltSvcWireFormat::VersionVector()) {} + spdy::SpdyAltSvcWireFormat::VersionVector()) { + // Since the default |alternative_service_| is QUIC, need to enable QUIC for + // the not added tests to be meaningful. + session_deps_.enable_quic = true; + } void AddSocketData(const spdy::SpdyAltSvcIR& altsvc_ir) { altsvc_frame_ = spdy_util_.SerializeFrame(altsvc_ir); @@ -6258,8 +6320,6 @@ class AltSvcFrameTest : public SpdySessionTest { }; TEST_F(AltSvcFrameTest, ProcessAltSvcFrame) { - session_deps_.enable_quic = true; - const char origin[] = "https://mail.example.org"; spdy::SpdyAltSvcIR altsvc_ir(/* stream_id = */ 0); altsvc_ir.add_altsvc(alternative_service_); @@ -6290,15 +6350,14 @@ TEST_F(AltSvcFrameTest, ProcessAltSvcFrame) { // Regression test for https://crbug.com/736063. TEST_F(AltSvcFrameTest, IgnoreQuicAltSvcWithUnsupportedVersion) { + session_deps_.enable_quic = true; + + // Note that this test only uses the legacy Google-specific Alt-Svc format. const char origin[] = "https://mail.example.org"; spdy::SpdyAltSvcIR altsvc_ir(/* stream_id = */ 0); spdy::SpdyAltSvcWireFormat::AlternativeService quic_alternative_service( "quic", "alternative.example.org", 443, 86400, spdy::SpdyAltSvcWireFormat::VersionVector()); - // TODO(zhongyi): spdy::SpdyAltSvcWireFormat::ParseHeaderFieldValue expects - // positve versions while VersionVector allows nonnegative verisons. Fix the - // parse function and change the hardcoded invalid version to - // quic::QUIC_VERSION_UNSUPPORTED. quic_alternative_service.version.push_back(/* invalid QUIC version */ 1); altsvc_ir.add_altsvc(quic_alternative_service); altsvc_ir.set_origin(origin); @@ -6323,7 +6382,64 @@ TEST_F(AltSvcFrameTest, IgnoreQuicAltSvcWithUnsupportedVersion) { ASSERT_EQ(0u, altsvc_info_vector.size()); } +TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameWithExpectCTError) { + const char origin[] = "https://mail.example.org"; + + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + /* enabled_features */ + {TransportSecurityState::kDynamicExpectCTFeature, + features::kPartitionExpectCTStateByNetworkIsolationKey, + features::kPartitionConnectionsByNetworkIsolationKey, + features::kPartitionSSLSessionsByNetworkIsolationKey}, + /* disabled_features */ + {}); + + key_ = SpdySessionKey(HostPortPair::FromURL(test_url_), ProxyServer::Direct(), + PRIVACY_MODE_DISABLED, + SpdySessionKey::IsProxySession::kFalse, SocketTag(), + NetworkIsolationKey::CreateTransient(), + false /* disable_secure_dns */); + ssl_.ssl_info.ct_policy_compliance = + ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; + ssl_.ssl_info.is_issued_by_known_root = true; + + // Need to create this after enabling features. + session_deps_.transport_security_state = + std::make_unique<TransportSecurityState>(); + session_deps_.transport_security_state->AddExpectCT( + GURL(origin).host(), + base::Time::Now() + base::TimeDelta::FromDays(1) /* expiry */, true, + GURL(), key_.network_isolation_key()); + + spdy::SpdyAltSvcIR altsvc_ir(/* stream_id = */ 0); + altsvc_ir.add_altsvc(alternative_service_); + altsvc_ir.set_origin(origin); + AddSocketData(altsvc_ir); + AddSSLSocketData(); + + CreateNetworkSession(); + CreateSpdySession(); + + base::RunLoop().RunUntilIdle(); + + const url::SchemeHostPort session_origin("https", test_url_.host(), + test_url_.EffectiveIntPort()); + ASSERT_TRUE(spdy_session_pool_->http_server_properties() + ->GetAlternativeServiceInfos(session_origin, + key_.network_isolation_key()) + .empty()); + + ASSERT_TRUE( + spdy_session_pool_->http_server_properties() + ->GetAlternativeServiceInfos(url::SchemeHostPort(GURL(origin)), + key_.network_isolation_key()) + .empty()); +} + TEST_F(AltSvcFrameTest, DoNotProcessAltSvcFrameForOriginNotCoveredByCert) { + session_deps_.enable_quic = true; + const char origin[] = "https://invalid.example.org"; spdy::SpdyAltSvcIR altsvc_ir(/* stream_id = */ 0); altsvc_ir.add_altsvc(alternative_service_); @@ -6394,8 +6510,6 @@ TEST_F(AltSvcFrameTest, } TEST_F(AltSvcFrameTest, ProcessAltSvcFrameOnActiveStream) { - session_deps_.enable_quic = true; - spdy::SpdyAltSvcIR altsvc_ir(/* stream_id = */ 1); altsvc_ir.add_altsvc(alternative_service_); @@ -6453,8 +6567,6 @@ TEST_F(AltSvcFrameTest, ProcessAltSvcFrameOnActiveStream) { TEST_F(AltSvcFrameTest, ProcessAltSvcFrameOnActiveStreamWithNetworkIsolationKey) { - session_deps_.enable_quic = true; - base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures( // enabled_features @@ -6761,19 +6873,27 @@ TEST(CanPoolTest, CanPool) { "spdy_pooling.pem"); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "www.example.org")); + "www.example.org", "www.example.org", + NetworkIsolationKey())); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.com")); + "www.example.org", "mail.example.com", + NetworkIsolationKey())); EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.google.com")); + "www.example.org", "mail.google.com", + NetworkIsolationKey())); } TEST(CanPoolTest, CanPoolExpectCT) { base::test::ScopedFeatureList feature_list; - feature_list.InitAndEnableFeature( - TransportSecurityState::kDynamicExpectCTFeature); + feature_list.InitWithFeatures( + /* enabled_features */ + {TransportSecurityState::kDynamicExpectCTFeature, + features::kPartitionExpectCTStateByNetworkIsolationKey}, + /* disabled_features */ + {}); // Load a cert that is valid for: // www.example.org // mail.example.org @@ -6789,8 +6909,11 @@ TEST(CanPoolTest, CanPoolExpectCT) { ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; ssl_info.is_issued_by_known_root = true; + net::NetworkIsolationKey network_isolation_key = + NetworkIsolationKey::CreateTransient(); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "www.example.org")); + "www.example.org", "www.example.org", + network_isolation_key)); const base::Time current_time(base::Time::Now()); const base::Time expiry = current_time + base::TimeDelta::FromSeconds(1000); @@ -6798,20 +6921,31 @@ TEST(CanPoolTest, CanPoolExpectCT) { ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; // A different Expect-CT enabled host should not be allowed to pool. - tss.AddExpectCT("mail.example.org", expiry, true, GURL()); + tss.AddExpectCT("mail.example.org", expiry, true, GURL(), + network_isolation_key); EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + network_isolation_key)); // A report-only Expect-CT configuration should not prevent pooling. tss.AddExpectCT("mail.example.org", expiry, false, - GURL("https://report.test")); + GURL("https://report.test"), network_isolation_key); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + network_isolation_key)); // If Expect-CT becomes enabled for the same host for which the connection was // already made, subsequent connections to that host should not be allowed to // pool. - tss.AddExpectCT("www.example.org", expiry, true, GURL()); + tss.AddExpectCT("www.example.org", expiry, true, GURL(), + network_isolation_key); EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "www.example.org")); + "www.example.org", "www.example.org", + network_isolation_key)); + + // With a different NetworkIsolationKey, CanPool() should still return true, + // as CT information is scoped to a single NetworkIsolationKey. + EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, + "www.example.org", "www.example.org", + NetworkIsolationKey::CreateTransient())); } TEST(CanPoolTest, CanNotPoolWithCertErrors) { @@ -6828,7 +6962,8 @@ TEST(CanPoolTest, CanNotPoolWithCertErrors) { ssl_info.cert_status = CERT_STATUS_REVOKED; EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); } TEST(CanPoolTest, CanNotPoolWithClientCerts) { @@ -6845,7 +6980,8 @@ TEST(CanPoolTest, CanNotPoolWithClientCerts) { ssl_info.client_cert_sent = true; EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); } TEST(CanPoolTest, CanNotPoolWithBadPins) { @@ -6862,7 +6998,8 @@ TEST(CanPoolTest, CanNotPoolWithBadPins) { ssl_info.public_key_hashes.push_back(test::GetTestHashValue(bad_pin)); EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "example.test")); + "www.example.org", "example.test", + NetworkIsolationKey())); } TEST(CanPoolTest, CanNotPoolWithBadCTWhenCTRequired) { @@ -6890,7 +7027,8 @@ TEST(CanPoolTest, CanNotPoolWithBadCTWhenCTRequired) { tss.SetRequireCTDelegate(&require_ct_delegate); EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); } TEST(CanPoolTest, CanPoolWithBadCTWhenCTNotRequired) { @@ -6918,7 +7056,8 @@ TEST(CanPoolTest, CanPoolWithBadCTWhenCTNotRequired) { tss.SetRequireCTDelegate(&require_ct_delegate); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); } TEST(CanPoolTest, CanPoolWithGoodCTWhenCTRequired) { @@ -6946,7 +7085,8 @@ TEST(CanPoolTest, CanPoolWithGoodCTWhenCTRequired) { tss.SetRequireCTDelegate(&require_ct_delegate); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); } TEST(CanPoolTest, CanPoolWithAcceptablePins) { @@ -6966,7 +7106,8 @@ TEST(CanPoolTest, CanPoolWithAcceptablePins) { ssl_info.public_key_hashes.push_back(hash); EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); } TEST(CanPoolTest, CanPoolWithClientCertsAndPolicy) { @@ -6986,11 +7127,14 @@ TEST(CanPoolTest, CanPoolWithClientCertsAndPolicy) { // CanShareConnectionWithClientCerts returns true for both hostnames, but not // just one hostname. EXPECT_TRUE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.org")); + "www.example.org", "mail.example.org", + NetworkIsolationKey())); EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "www.example.org", "mail.example.com")); + "www.example.org", "mail.example.com", + NetworkIsolationKey())); EXPECT_FALSE(SpdySession::CanPool(&tss, ssl_info, ssl_config_service, - "mail.example.com", "www.example.org")); + "mail.example.com", "www.example.org", + NetworkIsolationKey())); } TEST(RecordPushedStreamHistogramTest, VaryResponseHeader) { diff --git a/chromium/net/spdy/spdy_stream.cc b/chromium/net/spdy/spdy_stream.cc index ba8ffe66aff..0c28112f6e5 100644 --- a/chromium/net/spdy/spdy_stream.cc +++ b/chromium/net/spdy/spdy_stream.cc @@ -35,11 +35,11 @@ namespace { base::Value NetLogSpdyStreamErrorParams(spdy::SpdyStreamId stream_id, int net_error, - const std::string* description) { + base::StringPiece description) { base::Value dict(base::Value::Type::DICTIONARY); dict.SetIntKey("stream_id", static_cast<int>(stream_id)); dict.SetStringKey("net_error", ErrorToShortString(net_error)); - dict.SetStringKey("description", *description); + dict.SetStringKey("description", description); return dict; } @@ -412,6 +412,10 @@ void SpdyStream::OnHeadersReceived( // in which case it needs to pass through so that the WebSocket layer // can signal an error. if (status / 100 == 1 && status != 101) { + // Record the timing of the 103 Early Hints response for the experiment + // (https://crbug.com/1093693). + if (status == 103 && first_early_hints_time_.is_null()) + first_early_hints_time_ = recv_first_byte_time; return; } @@ -660,9 +664,9 @@ int SpdyStream::OnDataSent(size_t frame_size) { } } -void SpdyStream::LogStreamError(int error, const std::string& description) { +void SpdyStream::LogStreamError(int error, base::StringPiece description) { net_log_.AddEvent(NetLogEventType::HTTP2_STREAM_ERROR, [&] { - return NetLogSpdyStreamErrorParams(stream_id_, error, &description); + return NetLogSpdyStreamErrorParams(stream_id_, error, description); }); } @@ -816,6 +820,7 @@ bool SpdyStream::GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const { // first bytes of the HEADERS frame were received to BufferedSpdyFramer // (https://crbug.com/568024). load_timing_info->receive_headers_start = recv_first_byte_time_; + load_timing_info->first_early_hints_time = first_early_hints_time_; return result; } diff --git a/chromium/net/spdy/spdy_stream.h b/chromium/net/spdy/spdy_stream.h index e9245b01b6f..922c43fefc1 100644 --- a/chromium/net/spdy/spdy_stream.h +++ b/chromium/net/spdy/spdy_stream.h @@ -301,7 +301,7 @@ class NET_EXPORT_PRIVATE SpdyStream { void OnClose(int status); // Called by the SpdySession to log stream related errors. - void LogStreamError(int error, const std::string& description); + void LogStreamError(int error, base::StringPiece description); // If this stream is active, reset it, and close it otherwise. In // either case the stream is deleted. @@ -515,6 +515,8 @@ class NET_EXPORT_PRIVATE SpdyStream { base::TimeTicks send_time_; base::TimeTicks recv_first_byte_time_; base::TimeTicks recv_last_byte_time_; + // The time at which the first 103 Early Hints response is received. + base::TimeTicks first_early_hints_time_; // Number of bytes that have been received on this stream, including frame // overhead and headers. diff --git a/chromium/net/spdy/spdy_stream_test_util.cc b/chromium/net/spdy/spdy_stream_test_util.cc index 8307e999c69..b8309626edb 100644 --- a/chromium/net/spdy/spdy_stream_test_util.cc +++ b/chromium/net/spdy/spdy_stream_test_util.cc @@ -83,6 +83,7 @@ void StreamDelegateBase::OnClose(int status) { if (!stream_.get()) return; stream_id_ = stream_->stream_id(); + stream_->GetLoadTimingInfo(&load_timing_info_); stream_.reset(); callback_.callback().Run(status); } @@ -118,6 +119,11 @@ std::string StreamDelegateBase::GetResponseHeaderValue( : it->second.as_string(); } +const LoadTimingInfo& StreamDelegateBase::GetLoadTimingInfo() { + DCHECK(StreamIsClosed()); + return load_timing_info_; +} + StreamDelegateDoNothing::StreamDelegateDoNothing( const base::WeakPtr<SpdyStream>& stream) : StreamDelegateBase(stream) {} diff --git a/chromium/net/spdy/spdy_stream_test_util.h b/chromium/net/spdy/spdy_stream_test_util.h index cb1dd50875d..2453c78bd41 100644 --- a/chromium/net/spdy/spdy_stream_test_util.h +++ b/chromium/net/spdy/spdy_stream_test_util.h @@ -12,6 +12,7 @@ #include "base/memory/ref_counted.h" #include "base/strings/string_piece.h" #include "net/base/io_buffer.h" +#include "net/base/load_timing_info.h" #include "net/base/test_completion_callback.h" #include "net/log/net_log_source.h" #include "net/spdy/spdy_read_queue.h" @@ -83,6 +84,10 @@ class StreamDelegateBase : public SpdyStream::Delegate { std::string GetResponseHeaderValue(const std::string& name) const; bool send_headers_completed() const { return send_headers_completed_; } + // Returns the load timing info on the stream. This must be called after the + // stream is closed in order to get the up-to-date information. + const LoadTimingInfo& GetLoadTimingInfo(); + protected: const base::WeakPtr<SpdyStream>& stream() { return stream_; } @@ -93,6 +98,7 @@ class StreamDelegateBase : public SpdyStream::Delegate { bool send_headers_completed_; spdy::SpdyHeaderBlock response_headers_; SpdyReadQueue received_data_queue_; + LoadTimingInfo load_timing_info_; }; // Test delegate that does nothing. Used to capture data about the diff --git a/chromium/net/spdy/spdy_stream_unittest.cc b/chromium/net/spdy/spdy_stream_unittest.cc index 0f56bcdb5a8..c3fad7a4173 100644 --- a/chromium/net/spdy/spdy_stream_unittest.cc +++ b/chromium/net/spdy/spdy_stream_unittest.cc @@ -1084,6 +1084,69 @@ TEST_F(SpdyStreamTest, InformationalHeaders) { EXPECT_TRUE(data.AllReadDataConsumed()); } +// 103 Early Hints hasn't been implemented yet, but we collect timing +// information for the experiment (https://crbug.com/1093693). This tests it. +TEST_F(SpdyStreamTest, EarlyHints) { + spdy::SpdySerializedFrame req( + spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST)); + AddWrite(req); + + // Serve the early hints. + spdy::SpdyHeaderBlock informational_headers; + informational_headers[":status"] = "103"; + spdy::SpdySerializedFrame informational_response( + spdy_util_.ConstructSpdyResponseHeaders( + 1, std::move(informational_headers), false)); + AddRead(informational_response); + + spdy::SpdySerializedFrame reply( + spdy_util_.ConstructSpdyGetReply(nullptr, 0, 1)); + AddRead(reply); + + spdy::SpdySerializedFrame body( + spdy_util_.ConstructSpdyDataFrame(1, kPostBodyStringPiece, true)); + AddRead(body); + + AddReadEOF(); + + SequencedSocketData data(GetReads(), GetWrites()); + MockConnect connect_data(SYNCHRONOUS, OK); + data.set_connect_data(connect_data); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + AddSSLSocketData(); + + base::WeakPtr<SpdySession> session(CreateDefaultSpdySession()); + + base::WeakPtr<SpdyStream> stream = CreateStreamSynchronously( + SPDY_REQUEST_RESPONSE_STREAM, session, url_, LOWEST, NetLogWithSource()); + ASSERT_TRUE(stream); + EXPECT_EQ(kDefaultUrl, stream->url().spec()); + + StreamDelegateDoNothing delegate(stream); + stream->SetDelegate(&delegate); + + spdy::SpdyHeaderBlock headers( + spdy_util_.ConstructGetHeaderBlock(kDefaultUrl)); + EXPECT_EQ(ERR_IO_PENDING, stream->SendRequestHeaders(std::move(headers), + NO_MORE_DATA_TO_SEND)); + + EXPECT_THAT(delegate.WaitForClose(), IsOk()); + EXPECT_EQ("200", delegate.GetResponseHeaderValue(spdy::kHttp2StatusHeader)); + EXPECT_EQ(std::string(kPostBody, kPostBodyLength), + delegate.TakeReceivedData()); + + // Check if the timing of the early hints response is captured. + const LoadTimingInfo& load_timing_info = delegate.GetLoadTimingInfo(); + EXPECT_FALSE(load_timing_info.first_early_hints_time.is_null()); + + // Finish async network reads and writes. + base::RunLoop().RunUntilIdle(); + + EXPECT_TRUE(data.AllWriteDataConsumed()); + EXPECT_TRUE(data.AllReadDataConsumed()); +} + TEST_F(SpdyStreamTest, StatusMustBeNumber) { spdy::SpdySerializedFrame req( spdy_util_.ConstructSpdyGet(nullptr, 0, 1, LOWEST)); |