diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-05-17 17:24:03 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-06-22 07:51:41 +0000 |
commit | 774f54339e5db91f785733232d3950366db65d07 (patch) | |
tree | 068e1b47bd1af94d77094ed12b604a6b83d9c22a /chromium/net/http | |
parent | f7eaed5286974984ba5f9e3189d8f49d03e99f81 (diff) | |
download | qtwebengine-chromium-774f54339e5db91f785733232d3950366db65d07.tar.gz |
BASELINE: Update Chromium to 102.0.5005.57
Change-Id: I885f714bb40ee724c28f94ca6bd8dbdb39915158
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/http')
81 files changed, 2378 insertions, 832 deletions
diff --git a/chromium/net/http/alternative_service.cc b/chromium/net/http/alternative_service.cc index 457ab2dd12b..f713303c2af 100644 --- a/chromium/net/http/alternative_service.cc +++ b/chromium/net/http/alternative_service.cc @@ -10,7 +10,7 @@ #include "base/notreached.h" #include "base/strings/stringprintf.h" #include "net/base/port_util.h" -#include "net/third_party/quiche/src/quic/core/http/spdy_utils.h" +#include "net/third_party/quiche/src/quiche/quic/core/http/spdy_utils.h" namespace net { diff --git a/chromium/net/http/alternative_service.h b/chromium/net/http/alternative_service.h index 473292ad0ad..91dbc76a7b1 100644 --- a/chromium/net/http/alternative_service.h +++ b/chromium/net/http/alternative_service.h @@ -16,8 +16,8 @@ #include "net/base/net_export.h" #include "net/quic/quic_http_utils.h" #include "net/socket/next_proto.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h" namespace net { diff --git a/chromium/net/http/bidirectional_stream.cc b/chromium/net/http/bidirectional_stream.cc index b009fa6227d..cdd0aa3586e 100644 --- a/chromium/net/http/bidirectional_stream.cc +++ b/chromium/net/http/bidirectional_stream.cc @@ -29,7 +29,7 @@ #include "net/spdy/spdy_log_util.h" #include "net/ssl/ssl_cert_request_info.h" #include "net/ssl/ssl_config.h" -#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_header_block.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "url/gurl.h" @@ -237,7 +237,7 @@ void BidirectionalStream::OnStreamReady(bool request_headers_sent) { void BidirectionalStream::OnHeadersReceived( const spdy::Http2HeaderBlock& response_headers) { HttpResponseInfo response_info; - if (!SpdyHeadersToHttpResponse(response_headers, &response_info)) { + if (SpdyHeadersToHttpResponse(response_headers, &response_info) != OK) { DLOG(WARNING) << "Invalid headers"; NotifyFailed(ERR_FAILED); return; diff --git a/chromium/net/http/bidirectional_stream.h b/chromium/net/http/bidirectional_stream.h index 84e394deb7c..17413441445 100644 --- a/chromium/net/http/bidirectional_stream.h +++ b/chromium/net/http/bidirectional_stream.h @@ -21,7 +21,7 @@ #include "net/http/http_stream_factory.h" #include "net/http/http_stream_request.h" #include "net/log/net_log_with_source.h" -#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_header_block.h" namespace base { class OneShotTimer; diff --git a/chromium/net/http/bidirectional_stream_impl.h b/chromium/net/http/bidirectional_stream_impl.h index 28d7dc2eb72..51a068741db 100644 --- a/chromium/net/http/bidirectional_stream_impl.h +++ b/chromium/net/http/bidirectional_stream_impl.h @@ -14,7 +14,7 @@ #include "net/base/load_timing_info.h" #include "net/base/net_export.h" #include "net/socket/next_proto.h" -#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_header_block.h" #include "net/traffic_annotation/network_traffic_annotation.h" namespace base { diff --git a/chromium/net/http/bidirectional_stream_unittest.cc b/chromium/net/http/bidirectional_stream_unittest.cc index 6fc29ca45f0..60c45dd446a 100644 --- a/chromium/net/http/bidirectional_stream_unittest.cc +++ b/chromium/net/http/bidirectional_stream_unittest.cc @@ -10,7 +10,6 @@ #include <vector> #include "base/containers/span.h" -#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" #include "base/run_loop.h" @@ -55,7 +54,7 @@ namespace net { namespace { const char kBodyData[] = "Body data"; -const size_t kBodyDataSize = base::size(kBodyData); +const size_t kBodyDataSize = std::size(kBodyData); const std::string kBodyDataString(kBodyData, kBodyDataSize); // Size of the buffer to be allocated for each read. const size_t kReadBufferSize = 4096; @@ -1351,7 +1350,7 @@ TEST_F(BidirectionalStreamTest, DeleteStreamAfterSendData) { EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); // Bytes sent excludes the RST frame. EXPECT_EQ( - CountWriteBytes(base::make_span(writes).first(base::size(writes) - 1)), + CountWriteBytes(base::make_span(writes).first(std::size(writes) - 1)), delegate->GetTotalSentBytes()); EXPECT_EQ(CountReadBytes(reads), delegate->GetTotalReceivedBytes()); } @@ -1411,11 +1410,11 @@ TEST_F(BidirectionalStreamTest, DeleteStreamDuringReadData) { EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); // Bytes sent excludes the RST frame. EXPECT_EQ( - CountWriteBytes(base::make_span(writes).first(base::size(writes) - 1)), + CountWriteBytes(base::make_span(writes).first(std::size(writes) - 1)), delegate->GetTotalSentBytes()); // Response body frame isn't read becase stream is deleted once read returns // ERR_IO_PENDING. - EXPECT_EQ(CountReadBytes(base::make_span(reads).first(base::size(reads) - 2)), + EXPECT_EQ(CountReadBytes(base::make_span(reads).first(std::size(reads) - 2)), delegate->GetTotalReceivedBytes()); } @@ -1530,7 +1529,7 @@ TEST_F(BidirectionalStreamTest, DeleteStreamDuringOnHeadersReceived) { EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); // Bytes sent excludes the RST frame. EXPECT_EQ( - CountWriteBytes(base::make_span(writes).first(base::size(writes) - 1)), + CountWriteBytes(base::make_span(writes).first(std::size(writes) - 1)), delegate->GetTotalSentBytes()); EXPECT_EQ(CountReadBytes(reads), delegate->GetTotalReceivedBytes()); } @@ -1585,7 +1584,7 @@ TEST_F(BidirectionalStreamTest, DeleteStreamDuringOnDataRead) { EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); // Bytes sent excludes the RST frame. EXPECT_EQ( - CountWriteBytes(base::make_span(writes).first(base::size(writes) - 1)), + CountWriteBytes(base::make_span(writes).first(std::size(writes) - 1)), delegate->GetTotalSentBytes()); EXPECT_EQ(CountReadBytes(reads), delegate->GetTotalReceivedBytes()); } @@ -1645,7 +1644,7 @@ TEST_F(BidirectionalStreamTest, DeleteStreamDuringOnTrailersReceived) { EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); // Bytes sent excludes the RST frame. EXPECT_EQ( - CountWriteBytes(base::make_span(writes).first(base::size(writes) - 1)), + CountWriteBytes(base::make_span(writes).first(std::size(writes) - 1)), delegate->GetTotalSentBytes()); EXPECT_EQ(CountReadBytes(reads), delegate->GetTotalReceivedBytes()); } @@ -1696,7 +1695,7 @@ TEST_F(BidirectionalStreamTest, DeleteStreamDuringOnFailed) { EXPECT_EQ(kProtoHTTP2, delegate->GetProtocol()); // Bytes sent excludes the RST frame. EXPECT_EQ( - CountWriteBytes(base::make_span(writes).first(base::size(writes) - 1)), + CountWriteBytes(base::make_span(writes).first(std::size(writes) - 1)), delegate->GetTotalSentBytes()); EXPECT_EQ(0, delegate->GetTotalReceivedBytes()); } diff --git a/chromium/net/http/broken_alternative_services.cc b/chromium/net/http/broken_alternative_services.cc index d938b4b7fff..9bde04df272 100644 --- a/chromium/net/http/broken_alternative_services.cc +++ b/chromium/net/http/broken_alternative_services.cc @@ -5,6 +5,7 @@ #include "net/http/broken_alternative_services.h" #include "base/bind.h" +#include "base/containers/adapters.h" #include "base/memory/singleton.h" #include "base/time/tick_clock.h" #include "base/time/time.h" @@ -14,19 +15,44 @@ namespace net { namespace { -// Initial delay for broken alternative services. -const uint64_t kBrokenAlternativeProtocolDelaySecs = 300; +// Default broken alternative services, which is used when +// exponential_backoff_on_initial_delay is false. +constexpr base::TimeDelta kDefaultBrokenAlternativeProtocolDelay = + base::Seconds(300); // Subsequent failures result in exponential (base 2) backoff. -// Limit binary shift to limit delay to approximately 2 days. -const int kBrokenDelayMaxShift = 9; +// Given the shortest broken delay is 1s, limit binary shift to limit delay to +// approximately 2 days. +const int kBrokenDelayMaxShift = 18; +// Lower and upper limits of broken alternative service delay. +constexpr base::TimeDelta kMinBrokenAlternativeProtocolDelay = base::Seconds(1); +constexpr base::TimeDelta kMaxBrokenAlternativeProtocolDelay = base::Days(2); base::TimeDelta ComputeBrokenAlternativeServiceExpirationDelay( - int broken_count) { + int broken_count, + base::TimeDelta initial_delay, + bool exponential_backoff_on_initial_delay) { DCHECK_GE(broken_count, 0); - if (broken_count > kBrokenDelayMaxShift) + // Make sure initial delay is within [1s, 300s]. + if (initial_delay < kMinBrokenAlternativeProtocolDelay) { + initial_delay = kMinBrokenAlternativeProtocolDelay; + } + if (initial_delay > kDefaultBrokenAlternativeProtocolDelay) { + initial_delay = kDefaultBrokenAlternativeProtocolDelay; + } + if (broken_count == 0) { + return initial_delay; + } + // Limit broken_count to avoid overflow. + if (broken_count > kBrokenDelayMaxShift) { broken_count = kBrokenDelayMaxShift; - return base::Seconds(kBrokenAlternativeProtocolDelaySecs) * - (1 << broken_count); + } + base::TimeDelta delay; + if (exponential_backoff_on_initial_delay) { + delay = initial_delay * (1 << broken_count); + } else { + delay = kDefaultBrokenAlternativeProtocolDelay * (1 << (broken_count - 1)); + } + return std::min(delay, kMaxBrokenAlternativeProtocolDelay); } } // namespace @@ -55,7 +81,9 @@ BrokenAlternativeServices::BrokenAlternativeServices( : delegate_(delegate), clock_(clock), recently_broken_alternative_services_( - max_recently_broken_alternative_service_entries) { + max_recently_broken_alternative_service_entries), + initial_delay_(kDefaultBrokenAlternativeProtocolDelay), + exponential_backoff_on_initial_delay_(true) { DCHECK(delegate_); DCHECK(clock_); } @@ -108,7 +136,8 @@ void BrokenAlternativeServices::MarkBrokenImpl( } base::TimeTicks expiration = clock_->NowTicks() + - ComputeBrokenAlternativeServiceExpirationDelay(broken_count); + ComputeBrokenAlternativeServiceExpirationDelay( + broken_count, initial_delay_, exponential_backoff_on_initial_delay_); // Return if alternative service is already in expiration queue. BrokenAlternativeServiceList::iterator list_it; if (!AddToBrokenListAndMap(broken_alternative_service, expiration, @@ -223,11 +252,11 @@ void BrokenAlternativeServices::SetBrokenAndRecentlyBrokenAlternativeServices( *recently_broken_alternative_services); // Add back all existing recently broken alt svcs to cache so they're at // front of recency list (LRUCache::Get() does this automatically). - for (auto it = recently_broken_alternative_services->rbegin(); - it != recently_broken_alternative_services->rend(); ++it) { - if (recently_broken_alternative_services_.Get(it->first) == + for (const auto& [service, broken_count] : + base::Reversed(*recently_broken_alternative_services)) { + if (recently_broken_alternative_services_.Get(service) == recently_broken_alternative_services_.end()) { - recently_broken_alternative_services_.Put(it->first, it->second); + recently_broken_alternative_services_.Put(service, broken_count); } } @@ -286,6 +315,18 @@ void BrokenAlternativeServices::SetBrokenAndRecentlyBrokenAlternativeServices( ScheduleBrokenAlternateProtocolMappingsExpiration(); } +void BrokenAlternativeServices::SetDelayParams( + absl::optional<base::TimeDelta> initial_delay, + absl::optional<bool> exponential_backoff_on_initial_delay) { + if (initial_delay.has_value()) { + initial_delay_ = initial_delay.value(); + } + if (exponential_backoff_on_initial_delay.has_value()) { + exponential_backoff_on_initial_delay_ = + exponential_backoff_on_initial_delay.value(); + } +} + const BrokenAlternativeServiceList& BrokenAlternativeServices::broken_alternative_service_list() const { return broken_alternative_service_list_; diff --git a/chromium/net/http/broken_alternative_services.h b/chromium/net/http/broken_alternative_services.h index c4fd7317317..56c8dfd6b74 100644 --- a/chromium/net/http/broken_alternative_services.h +++ b/chromium/net/http/broken_alternative_services.h @@ -155,6 +155,13 @@ class NET_EXPORT_PRIVATE BrokenAlternativeServices { std::unique_ptr<RecentlyBrokenAlternativeServices> recently_broken_alternative_services); + // If values are present, sets initial_delay_ and + // exponential_backoff_on_initial_delay_ which are used to calculate delay of + // broken alternative services. + void SetDelayParams( + absl::optional<base::TimeDelta> initial_delay, + absl::optional<bool> exponential_backoff_on_initial_delay); + const BrokenAlternativeServiceList& broken_alternative_service_list() const; const RecentlyBrokenAlternativeServices& @@ -215,6 +222,15 @@ class NET_EXPORT_PRIVATE BrokenAlternativeServices { // services. base::OneShotTimer expiration_timer_; + // Delay for the 1st time alternative service is marked broken. + base::TimeDelta initial_delay_; + + // If true, the delay for broken alternative service = + // initial_delay_for_broken_alternative_service * (1 << broken_count). + // Otherwise, the delay would be initial_delay_for_broken_alternative_service, + // 5min, 10min.. and so on. + bool exponential_backoff_on_initial_delay_; + base::WeakPtrFactory<BrokenAlternativeServices> weak_ptr_factory_{this}; }; diff --git a/chromium/net/http/broken_alternative_services_unittest.cc b/chromium/net/http/broken_alternative_services_unittest.cc index 66e99013f69..1f331acfe6d 100644 --- a/chromium/net/http/broken_alternative_services_unittest.cc +++ b/chromium/net/http/broken_alternative_services_unittest.cc @@ -10,6 +10,7 @@ #include "base/memory/raw_ptr.h" #include "base/test/test_mock_time_task_runner.h" #include "base/time/tick_clock.h" +#include "base/time/time.h" #include "net/base/network_isolation_key.h" #include "net/base/schemeful_site.h" #include "testing/gtest/include/gtest/gtest.h" @@ -46,6 +47,9 @@ class BrokenAlternativeServicesTest true /* use_network_isolation_key */)); } + void TestExponentialBackoff(base::TimeDelta initial_delay, + bool exponential_backoff_on_initial_delay); + // All tests will run inside the scope of |test_task_runner_context_|, which // means any task posted to the main message loop will run on // |test_task_runner_|. @@ -640,10 +644,89 @@ TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff) { // Max expiration delay has been reached; subsequent expiration delays from // this point forward should not increase further. broken_services_.MarkBroken(alternative_service); - test_task_runner_->FastForwardBy(base::Minutes(2560) - base::Seconds(1)); + test_task_runner_->FastForwardBy(base::Minutes(2880) - base::Seconds(1)); + EXPECT_TRUE(broken_services_.IsBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::Seconds(1)); + EXPECT_FALSE(broken_services_.IsBroken(alternative_service)); + + broken_services_.MarkBroken(alternative_service); + test_task_runner_->FastForwardBy(base::Minutes(2880) - base::Seconds(1)); + EXPECT_TRUE(broken_services_.IsBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::Seconds(1)); + EXPECT_FALSE(broken_services_.IsBroken(alternative_service)); +} + +void BrokenAlternativeServicesTest::TestExponentialBackoff( + base::TimeDelta initial_delay, + bool exponential_backoff_on_initial_delay) { + // Tests the exponential backoff of the computed expiration delay when an + // alt svc is marked broken. After being marked broken 10 times, the max + // expiration delay will have been reached and exponential backoff will no + // longer apply. + broken_services_.SetDelayParams(initial_delay, + exponential_backoff_on_initial_delay); + + BrokenAlternativeService alternative_service( + AlternativeService(kProtoQUIC, "foo", 443), NetworkIsolationKey(), + true /* use_network_isolation_key */); + + broken_services_.MarkBroken(alternative_service); + test_task_runner_->FastForwardBy(initial_delay - base::Seconds(1)); EXPECT_TRUE(broken_services_.IsBroken(alternative_service)); test_task_runner_->FastForwardBy(base::Seconds(1)); EXPECT_FALSE(broken_services_.IsBroken(alternative_service)); + + for (size_t broken_count = 1; broken_count < 20; ++broken_count) { + broken_services_.MarkBroken(alternative_service); + base::TimeDelta broken_delay; + if (exponential_backoff_on_initial_delay) { + broken_delay = initial_delay * (1 << broken_count); + } else { + broken_delay = base::Seconds(kBrokenAlternativeProtocolDelaySecs) * + (1 << (broken_count - 1)); + } + if (broken_delay > base::Days(2)) { + broken_delay = base::Days(2); + } + test_task_runner_->FastForwardBy(broken_delay - base::Seconds(1)); + EXPECT_TRUE(broken_services_.IsBroken(alternative_service)); + test_task_runner_->FastForwardBy(base::Seconds(1)); + EXPECT_FALSE(broken_services_.IsBroken(alternative_service)); + } +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_OneSecond_True) { + TestExponentialBackoff(base::Seconds(1), true); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_OneSecond_False) { + TestExponentialBackoff(base::Seconds(1), false); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_FiveSeconds_True) { + TestExponentialBackoff(base::Seconds(5), true); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_FiveSeconds_False) { + TestExponentialBackoff(base::Seconds(5), false); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_TenSeconds_True) { + TestExponentialBackoff(base::Seconds(10), true); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_TenSeconds_False) { + TestExponentialBackoff(base::Seconds(10), false); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_FiveMinutes_True) { + TestExponentialBackoff(base::Seconds(kBrokenAlternativeProtocolDelaySecs), + true); +} + +TEST_F(BrokenAlternativeServicesTest, ExponentialBackoff_FiveMinutes_False) { + TestExponentialBackoff(base::Seconds(kBrokenAlternativeProtocolDelaySecs), + false); } TEST_F(BrokenAlternativeServicesTest, RemoveExpiredBrokenAltSvc) { diff --git a/chromium/net/http/http_auth.cc b/chromium/net/http/http_auth.cc index be1cda1b53f..bd9cbc1e57e 100644 --- a/chromium/net/http/http_auth.cc +++ b/chromium/net/http/http_auth.cc @@ -6,7 +6,6 @@ #include <algorithm> -#include "base/cxx17_backports.h" #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "base/values.h" @@ -145,7 +144,7 @@ std::string HttpAuth::GetAuthTargetString(Target target) { // static const char* HttpAuth::SchemeToString(Scheme scheme) { - static_assert(base::size(kSchemeNames) == AUTH_SCHEME_MAX, + static_assert(std::size(kSchemeNames) == AUTH_SCHEME_MAX, "http auth scheme names incorrect size"); if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) { NOTREACHED(); @@ -156,7 +155,7 @@ const char* HttpAuth::SchemeToString(Scheme scheme) { // static HttpAuth::Scheme HttpAuth::StringToScheme(const std::string& str) { - for (uint8_t i = 0; i < base::size(kSchemeNames); i++) { + for (uint8_t i = 0; i < std::size(kSchemeNames); i++) { if (str == kSchemeNames[i]) return static_cast<Scheme>(i); } diff --git a/chromium/net/http/http_auth_cache_unittest.cc b/chromium/net/http/http_auth_cache_unittest.cc index 600b3d91afb..c2f2ce7c6e4 100644 --- a/chromium/net/http/http_auth_cache_unittest.cc +++ b/chromium/net/http/http_auth_cache_unittest.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/simple_test_clock.h" #include "base/test/simple_test_tick_clock.h" +#include "base/time/time.h" #include "net/base/net_errors.h" #include "net/base/network_isolation_key.h" #include "net/base/schemeful_site.h" diff --git a/chromium/net/http/http_auth_controller.cc b/chromium/net/http/http_auth_controller.cc index 40e13b0388e..90509517519 100644 --- a/chromium/net/http/http_auth_controller.cc +++ b/chromium/net/http/http_auth_controller.cc @@ -558,7 +558,7 @@ void HttpAuthController::PopulateAuthChallenge() { auth_info_ = AuthChallengeInfo(); auth_info_->is_proxy = (target_ == HttpAuth::AUTH_PROXY); - auth_info_->challenger = url::Origin::Create(auth_scheme_host_port_.GetURL()); + auth_info_->challenger = auth_scheme_host_port_; auth_info_->scheme = HttpAuth::SchemeToString(handler_->auth_scheme()); auth_info_->realm = handler_->realm(); auth_info_->path = auth_path_; diff --git a/chromium/net/http/http_auth_filter_unittest.cc b/chromium/net/http/http_auth_filter_unittest.cc index 625ac69f30d..5ac316358d0 100644 --- a/chromium/net/http/http_auth_filter_unittest.cc +++ b/chromium/net/http/http_auth_filter_unittest.cc @@ -7,7 +7,6 @@ #include <memory> #include <ostream> -#include "base/cxx17_backports.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" #include "url/scheme_host_port.h" @@ -68,7 +67,7 @@ TEST(HttpAuthFilterTest, EmptyFilter) { TEST(HttpAuthFilterTest, NonEmptyFilter) { // Create an non-empty filter std::string server_allowlist_filter_string; - for (size_t i = 0; i < base::size(server_allowlist_array); ++i) { + for (size_t i = 0; i < std::size(server_allowlist_array); ++i) { if (!server_allowlist_filter_string.empty()) server_allowlist_filter_string += ","; server_allowlist_filter_string += "*"; diff --git a/chromium/net/http/http_auth_gssapi_posix.cc b/chromium/net/http/http_auth_gssapi_posix.cc index 3d649e59b27..e4c25d2b923 100644 --- a/chromium/net/http/http_auth_gssapi_posix.cc +++ b/chromium/net/http/http_auth_gssapi_posix.cc @@ -9,7 +9,6 @@ #include "base/base64.h" #include "base/compiler_specific.h" -#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/format_macros.h" #include "base/logging.h" @@ -380,7 +379,7 @@ base::NativeLibrary GSSAPISharedLibrary::LoadSharedLibrary( #endif }; library_names = kDefaultLibraryNames; - num_lib_names = base::size(kDefaultLibraryNames); + num_lib_names = std::size(kDefaultLibraryNames); } net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_LOAD); diff --git a/chromium/net/http/http_auth_gssapi_posix_unittest.cc b/chromium/net/http/http_auth_gssapi_posix_unittest.cc index 4820b7e3d8a..92a9cd7945c 100644 --- a/chromium/net/http/http_auth_gssapi_posix_unittest.cc +++ b/chromium/net/http/http_auth_gssapi_posix_unittest.cc @@ -9,7 +9,6 @@ #include "base/base_paths.h" #include "base/bind.h" #include "base/check.h" -#include "base/cxx17_backports.h" #include "base/json/json_reader.h" #include "base/native_library.h" #include "base/path_service.h" @@ -69,7 +68,7 @@ void EstablishInitialContext(test::MockGSSAPILibrary* library) { 1, // Locally initiated 0); // Open gss_buffer_desc in_buffer = {0, nullptr}; - gss_buffer_desc out_buffer = {base::size(kInitialAuthResponse), + gss_buffer_desc out_buffer = {std::size(kInitialAuthResponse), const_cast<char*>(kInitialAuthResponse)}; library->ExpectSecurityContext( "Negotiate", @@ -221,7 +220,7 @@ TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) { kAuthResponse) // Output token }; - for (size_t i = 0; i < base::size(queries); ++i) { + for (size_t i = 0; i < std::size(queries); ++i) { mock_library->ExpectSecurityContext(queries[i].expected_package, queries[i].response_code, queries[i].minor_response_code, @@ -244,7 +243,7 @@ TEST(HttpAuthGSSAPIPOSIXTest, GSSAPICycle) { gss_buffer_desc output_token = {0, nullptr}; OM_uint32 ret_flags = 0; OM_uint32 time_rec = 0; - for (size_t i = 0; i < base::size(queries); ++i) { + for (size_t i = 0; i < std::size(queries); ++i) { major_status = mock_library->init_sec_context(&minor_status, initiator_cred_handle, &context_handle, diff --git a/chromium/net/http/http_auth_handler_basic_unittest.cc b/chromium/net/http/http_auth_handler_basic_unittest.cc index ce3064abf19..e87b89b3ee1 100644 --- a/chromium/net/http/http_auth_handler_basic_unittest.cc +++ b/chromium/net/http/http_auth_handler_basic_unittest.cc @@ -7,7 +7,6 @@ #include <memory> #include <string> -#include "base/cxx17_backports.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" @@ -45,7 +44,7 @@ TEST(HttpAuthHandlerBasicTest, GenerateAuthToken) { }; url::SchemeHostPort scheme_host_port(GURL("http://www.example.com")); HttpAuthHandlerBasic::Factory factory; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { std::string challenge = "Basic realm=\"Atlantis\""; SSLInfo null_ssl_info; auto host_resolver = std::make_unique<MockHostResolver>(); @@ -111,7 +110,7 @@ TEST(HttpAuthHandlerBasicTest, HandleAnotherChallenge) { NetworkIsolationKey(), scheme_host_port, NetLogWithSource(), host_resolver.get(), &basic)); - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { std::string challenge(tests[i].challenge); HttpAuthChallengeTokenizer tok(challenge.begin(), challenge.end()); @@ -204,7 +203,7 @@ TEST(HttpAuthHandlerBasicTest, InitFromChallenge) { }; HttpAuthHandlerBasic::Factory factory; url::SchemeHostPort scheme_host_port(GURL("http://www.example.com")); - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { std::string challenge = tests[i].challenge; SSLInfo null_ssl_info; auto host_resolver = std::make_unique<MockHostResolver>(); diff --git a/chromium/net/http/http_auth_handler_digest_unittest.cc b/chromium/net/http/http_auth_handler_digest_unittest.cc index 1fd6c275d64..de6b127775f 100644 --- a/chromium/net/http/http_auth_handler_digest_unittest.cc +++ b/chromium/net/http/http_auth_handler_digest_unittest.cc @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/http/http_auth_handler_digest.h" + #include <string> -#include "base/cxx17_backports.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "net/base/net_errors.h" @@ -12,7 +13,6 @@ #include "net/base/test_completion_callback.h" #include "net/dns/mock_host_resolver.h" #include "net/http/http_auth_challenge_tokenizer.h" -#include "net/http/http_auth_handler_digest.h" #include "net/http/http_request_info.h" #include "net/log/net_log_with_source.h" #include "net/ssl/ssl_info.h" @@ -364,7 +364,7 @@ TEST(HttpAuthHandlerDigestTest, ParseChallenge) { url::SchemeHostPort scheme_host_port(GURL("http://www.example.com")); std::unique_ptr<HttpAuthHandlerDigest::Factory> factory( new HttpAuthHandlerDigest::Factory()); - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { SSLInfo null_ssl_info; auto host_resolver = std::make_unique<MockHostResolver>(); std::unique_ptr<HttpAuthHandler> handler; @@ -530,7 +530,7 @@ TEST(HttpAuthHandlerDigestTest, AssembleCredentials) { url::SchemeHostPort scheme_host_port(GURL("http://www.example.com")); std::unique_ptr<HttpAuthHandlerDigest::Factory> factory( new HttpAuthHandlerDigest::Factory()); - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { SSLInfo null_ssl_info; auto host_resolver = std::make_unique<MockHostResolver>(); std::unique_ptr<HttpAuthHandler> handler; diff --git a/chromium/net/http/http_auth_handler_factory.cc b/chromium/net/http/http_auth_handler_factory.cc index f5cc9aea404..05e0a4416aa 100644 --- a/chromium/net/http/http_auth_handler_factory.cc +++ b/chromium/net/http/http_auth_handler_factory.cc @@ -22,6 +22,7 @@ #include "net/log/net_log_values.h" #include "net/net_buildflags.h" #include "net/ssl/ssl_info.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "url/scheme_host_port.h" #if BUILDFLAG(USE_KERBEROS) diff --git a/chromium/net/http/http_auth_handler_factory.h b/chromium/net/http/http_auth_handler_factory.h index 5a41e68d273..683b63f3f26 100644 --- a/chromium/net/http/http_auth_handler_factory.h +++ b/chromium/net/http/http_auth_handler_factory.h @@ -19,6 +19,7 @@ #include "net/http/http_auth_scheme.h" #include "net/http/url_security_manager.h" #include "net/net_buildflags.h" +#include "third_party/abseil-cpp/absl/types/optional.h" namespace url { class SchemeHostPort; diff --git a/chromium/net/http/http_auth_handler_negotiate_unittest.cc b/chromium/net/http/http_auth_handler_negotiate_unittest.cc index 5178a119440..80b03874963 100644 --- a/chromium/net/http/http_auth_handler_negotiate_unittest.cc +++ b/chromium/net/http/http_auth_handler_negotiate_unittest.cc @@ -8,7 +8,6 @@ #include <string> #include "base/bind.h" -#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" #include "base/memory/raw_ptr.h" #include "base/strings/string_util.h" @@ -179,7 +178,7 @@ class HttpAuthHandlerNegotiateTest : public PlatformTest, kAuthResponse) // Output token }; - for (size_t i = 0; i < base::size(queries); ++i) { + for (size_t i = 0; i < std::size(queries); ++i) { mock_library->ExpectSecurityContext(queries[i].expected_package, queries[i].response_code, queries[i].minor_response_code, diff --git a/chromium/net/http/http_auth_handler_ntlm_portable_unittest.cc b/chromium/net/http/http_auth_handler_ntlm_portable_unittest.cc index f34a02a28b4..19175390090 100644 --- a/chromium/net/http/http_auth_handler_ntlm_portable_unittest.cc +++ b/chromium/net/http/http_auth_handler_ntlm_portable_unittest.cc @@ -7,7 +7,6 @@ #include "base/base64.h" #include "base/containers/span.h" -#include "base/cxx17_backports.h" #include "base/strings/strcat.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -235,7 +234,7 @@ TEST_F(HttpAuthHandlerNtlmPortableTest, NtlmV1AuthenticationSuccess) { // Validate the authenticate message std::string decoded; ASSERT_TRUE(DecodeChallenge(token, &decoded)); - ASSERT_EQ(base::size(ntlm::test::kExpectedAuthenticateMsgSpecResponseV1), + ASSERT_EQ(std::size(ntlm::test::kExpectedAuthenticateMsgSpecResponseV1), decoded.size()); ASSERT_EQ(0, memcmp(decoded.data(), ntlm::test::kExpectedAuthenticateMsgSpecResponseV1, diff --git a/chromium/net/http/http_auth_unittest.cc b/chromium/net/http/http_auth_unittest.cc index c8e37c12c72..5bdc7fb2997 100644 --- a/chromium/net/http/http_auth_unittest.cc +++ b/chromium/net/http/http_auth_unittest.cc @@ -8,7 +8,6 @@ #include <set> #include <string> -#include "base/cxx17_backports.h" #include "base/memory/ref_counted.h" #include "base/strings/string_util.h" #include "build/build_config.h" @@ -139,7 +138,7 @@ TEST(HttpAuthTest, ChooseBestChallenge) { http_auth_handler_factory->SetHttpAuthPreferences(kNegotiateAuthScheme, &http_auth_preferences); - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { // Make a HttpResponseHeaders object. std::string headers_with_status_line("HTTP/1.1 401 Unauthorized\n"); headers_with_status_line += tests[i].headers; diff --git a/chromium/net/http/http_basic_state.cc b/chromium/net/http/http_basic_state.cc index b3212b319ef..fe5233bab8d 100644 --- a/chromium/net/http/http_basic_state.cc +++ b/chromium/net/http/http_basic_state.cc @@ -8,7 +8,6 @@ #include <utility> #include "base/check_op.h" -#include "base/cxx17_backports.h" #include "base/no_destructor.h" #include "net/base/io_buffer.h" #include "net/http/http_request_info.h" @@ -55,7 +54,7 @@ void HttpBasicState::DeleteParser() { parser_.reset(); } std::string HttpBasicState::GenerateRequestLine() const { static const char kSuffix[] = " HTTP/1.1\r\n"; - const size_t kSuffixLen = base::size(kSuffix) - 1; + const size_t kSuffixLen = std::size(kSuffix) - 1; const std::string path = using_proxy_ ? HttpUtil::SpecForRequest(url_) : url_.PathForRequest(); // Don't use StringPrintf for concatenation because it is very inefficient. diff --git a/chromium/net/http/http_basic_stream.cc b/chromium/net/http/http_basic_stream.cc index 03b443c360b..d82e153e47e 100644 --- a/chromium/net/http/http_basic_stream.cc +++ b/chromium/net/http/http_basic_stream.cc @@ -24,13 +24,18 @@ HttpBasicStream::HttpBasicStream(std::unique_ptr<ClientSocketHandle> connection, HttpBasicStream::~HttpBasicStream() = default; -int HttpBasicStream::InitializeStream(const HttpRequestInfo* request_info, - bool can_send_early, +void HttpBasicStream::RegisterRequest(const HttpRequestInfo* request_info) { + DCHECK(request_info); + DCHECK(request_info->traffic_annotation.is_valid()); + request_info_ = request_info; +} + +int HttpBasicStream::InitializeStream(bool can_send_early, RequestPriority priority, const NetLogWithSource& net_log, CompletionOnceCallback callback) { - DCHECK(request_info->traffic_annotation.is_valid()); - state_.Initialize(request_info, priority, net_log); + DCHECK(request_info_); + state_.Initialize(request_info_, priority, net_log); int ret = OK; if (!can_send_early) { // parser() cannot outlive |this|, so we can use base::Unretained(). @@ -38,6 +43,8 @@ int HttpBasicStream::InitializeStream(const HttpRequestInfo* request_info, base::BindOnce(&HttpBasicStream::OnHandshakeConfirmed, base::Unretained(this), std::move(callback))); } + // RequestInfo is no longer needed after this point. + request_info_ = nullptr; return ret; } @@ -107,7 +114,8 @@ void HttpBasicStream::SetConnectionReused() { } bool HttpBasicStream::CanReuseConnection() const { - return state_.connection()->socket() && parser()->CanReuseConnection(); + return parser() && state_.connection()->socket() && + parser()->CanReuseConnection(); } int64_t HttpBasicStream::GetTotalReceivedBytes() const { diff --git a/chromium/net/http/http_basic_stream.h b/chromium/net/http/http_basic_stream.h index cd3c8d003c8..8c765a8e13c 100644 --- a/chromium/net/http/http_basic_stream.h +++ b/chromium/net/http/http_basic_stream.h @@ -45,8 +45,9 @@ class NET_EXPORT_PRIVATE HttpBasicStream : public HttpStream { ~HttpBasicStream() override; // HttpStream methods: - int InitializeStream(const HttpRequestInfo* request_info, - bool can_send_early, + void RegisterRequest(const HttpRequestInfo* request_info) override; + + int InitializeStream(bool can_send_early, RequestPriority priority, const NetLogWithSource& net_log, CompletionOnceCallback callback) override; @@ -108,6 +109,12 @@ class NET_EXPORT_PRIVATE HttpBasicStream : public HttpStream { HttpBasicState state_; base::TimeTicks confirm_handshake_end_; RequestHeadersCallback request_headers_callback_; + // The request to send. + // Set to null before the response body is read. This is to allow |this| to + // be shared for reading and to possibly outlive request_info_'s owner. + // Setting to null happens after headers are completely read or upload data + // stream is uploaded, whichever is later. + raw_ptr<const HttpRequestInfo> request_info_; }; } // namespace net diff --git a/chromium/net/http/http_byte_range_unittest.cc b/chromium/net/http/http_byte_range_unittest.cc index 10fc28d4c93..4e17e45fffc 100644 --- a/chromium/net/http/http_byte_range_unittest.cc +++ b/chromium/net/http/http_byte_range_unittest.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "net/http/http_byte_range.h" -#include "base/cxx17_backports.h" + #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -28,7 +28,7 @@ TEST(HttpByteRangeTest, ValidRanges) { { -1, -1, 100000, true }, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { HttpByteRange range; range.set_first_byte_position(tests[i].first_byte_position); range.set_last_byte_position(tests[i].last_byte_position); @@ -60,7 +60,7 @@ TEST(HttpByteRangeTest, SetInstanceSize) { { 10, 10000, -1, 1000000, true, 10, 10000 }, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { HttpByteRange range; range.set_first_byte_position(tests[i].first_byte_position); range.set_last_byte_position(tests[i].last_byte_position); @@ -93,7 +93,7 @@ TEST(HttpByteRangeTest, GetHeaderValue) { {HttpByteRange::RightUnbounded(100), "bytes=100-"}, {HttpByteRange::Suffix(100), "bytes=-100"}, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { EXPECT_EQ(tests[i].expected, tests[i].range.GetHeaderValue()); } } diff --git a/chromium/net/http/http_cache.cc b/chromium/net/http/http_cache.cc index 6918f291d72..5aed1148698 100644 --- a/chromium/net/http/http_cache.cc +++ b/chromium/net/http/http_cache.cc @@ -28,7 +28,6 @@ #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_clock.h" -#include "base/time/time.h" #include "build/build_config.h" #include "net/base/cache_type.h" #include "net/base/features.h" @@ -101,13 +100,14 @@ int HttpCache::DefaultBackend::CreateBackend( #if BUILDFLAG(IS_ANDROID) if (app_status_listener_) { return disk_cache::CreateCacheBackend( - type_, backend_type_, path_, max_bytes_, reset_handling, net_log, - backend, std::move(callback), app_status_listener_); + type_, backend_type_, /*file_operations=*/nullptr, path_, max_bytes_, + reset_handling, net_log, backend, std::move(callback), + app_status_listener_); } #endif - return disk_cache::CreateCacheBackend(type_, backend_type_, path_, max_bytes_, - reset_handling, net_log, backend, - std::move(callback)); + return disk_cache::CreateCacheBackend( + type_, backend_type_, /*file_operations=*/nullptr, path_, max_bytes_, + reset_handling, net_log, backend, std::move(callback)); } #if BUILDFLAG(IS_ANDROID) diff --git a/chromium/net/http/http_cache_transaction.cc b/chromium/net/http/http_cache_transaction.cc index b2729058b00..6bb6deaafdb 100644 --- a/chromium/net/http/http_cache_transaction.cc +++ b/chromium/net/http/http_cache_transaction.cc @@ -31,6 +31,7 @@ #include "base/task/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/clock.h" +#include "base/trace_event/common/trace_event_common.h" #include "base/trace_event/trace_event.h" #include "base/values.h" #include "net/base/auth.h" @@ -39,6 +40,7 @@ #include "net/base/load_flags.h" #include "net/base/load_timing_info.h" #include "net/base/trace_constants.h" +#include "net/base/transport_info.h" #include "net/base/upload_data_stream.h" #include "net/cert/cert_status_flags.h" #include "net/cert/x509_certificate.h" @@ -72,15 +74,6 @@ bool NonErrorResponse(int status_code) { return status_code_range == 2 || status_code_range == 3; } -void RecordNoStoreHeaderHistogram(int load_flags, - const HttpResponseInfo* response) { - if (load_flags & LOAD_MAIN_FRAME_DEPRECATED) { - UMA_HISTOGRAM_BOOLEAN( - "Net.MainFrameNoStore", - response->headers->HasHeaderValue("cache-control", "no-store")); - } -} - bool IsOnBatteryPower() { if (base::PowerMonitor::IsInitialized()) return base::PowerMonitor::IsOnBatteryPower(); @@ -158,8 +151,7 @@ static bool HeaderMatches(const HttpRequestHeaders& headers, //----------------------------------------------------------------------------- HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) - : next_state_(STATE_NONE), - initial_request_(nullptr), + : initial_request_(nullptr), request_(nullptr), priority_(priority), cache_(cache->GetWeakPtr()), @@ -195,7 +187,7 @@ HttpCache::Transaction::Transaction(RequestPriority priority, HttpCache* cache) TRACE_EVENT1("io", "HttpCacheTransaction::Transaction", "priority", RequestPriorityToString(priority)); static_assert(HttpCache::Transaction::kNumValidationHeaders == - base::size(kValidationHeaders), + std::size(kValidationHeaders), "invalid number of validation headers"); io_callback_ = base::BindRepeating(&Transaction::OnIOComplete, @@ -241,7 +233,9 @@ int HttpCache::Transaction::Start(const HttpRequestInfo* request, const NetLogWithSource& net_log) { DCHECK(request); DCHECK(!callback.is_null()); - TRACE_EVENT1("io", "HttpCacheTransaction::Start", "url", request->url); + TRACE_EVENT_WITH_FLOW1("io", "HttpCacheTransaction::Start", + net_log.source().id, TRACE_EVENT_FLAG_FLOW_OUT, "url", + request->url); // Ensure that we only have one asynchronous call at a time. DCHECK(callback_.is_null()); @@ -608,17 +602,16 @@ int HttpCache::Transaction::ResumeNetworkStart() { return ERR_UNEXPECTED; } -void HttpCache::Transaction::GetConnectionAttempts( - ConnectionAttempts* out) const { - ConnectionAttempts new_connection_attempts; +ConnectionAttempts HttpCache::Transaction::GetConnectionAttempts() const { + ConnectionAttempts attempts; const HttpTransaction* transaction = GetOwnedOrMovedNetworkTransaction(); if (transaction) - transaction->GetConnectionAttempts(&new_connection_attempts); + attempts = transaction->GetConnectionAttempts(); - out->swap(new_connection_attempts); - out->insert(out->begin(), - network_transaction_info_.old_connection_attempts.begin(), - network_transaction_info_.old_connection_attempts.end()); + attempts.insert(attempts.begin(), + network_transaction_info_.old_connection_attempts.begin(), + network_transaction_info_.old_connection_attempts.end()); + return attempts; } void HttpCache::Transaction::CloseConnectionOnDestruction() { @@ -693,7 +686,7 @@ void HttpCache::Transaction::MaybeSetParallelWritingPatternForMetrics( // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* -> // CacheReadResponse* -> CacheDispatchValidation -> // BeginPartialCacheValidation() -> BeginCacheValidation() -> -// SetupEntryForRead() -> FinishHeaders* +// ConnectedCallback* -> SetupEntryForRead() -> FinishHeaders* // // Read(): // CacheReadData* @@ -738,7 +731,7 @@ void HttpCache::Transaction::MaybeSetParallelWritingPatternForMetrics( // // Read() 2: // NetworkReadCacheWrite* -> StartPartialCacheValidation -> -// CompletePartialCacheValidation -> CacheReadData* -> +// CompletePartialCacheValidation -> ConnectedCallback* -> CacheReadData* // // Read() 3: // CacheReadData* -> StartPartialCacheValidation -> @@ -797,7 +790,8 @@ void HttpCache::Transaction::MaybeSetParallelWritingPatternForMetrics( // GetBackend* -> InitEntry -> OpenOrCreateEntry* -> AddToEntry* -> // CacheReadResponse* -> CacheToggleUnusedSincePrefetch* -> // CacheDispatchValidation -> BeginPartialCacheValidation() -> -// BeginCacheValidation() -> SetupEntryForRead() -> FinishHeaders* +// BeginCacheValidation() -> ConnectedCallback* -> SetupEntryForRead() -> +// FinishHeaders* // // Read(): // CacheReadData* @@ -899,6 +893,12 @@ int HttpCache::Transaction::DoLoop(int result) { case STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT_COMPLETE: rv = DoCacheUpdateStaleWhileRevalidateTimeoutComplete(rv); break; + case STATE_CONNECTED_CALLBACK: + rv = DoConnectedCallback(); + break; + case STATE_CONNECTED_CALLBACK_COMPLETE: + rv = DoConnectedCallbackComplete(rv); + break; case STATE_SETUP_ENTRY_FOR_READ: DCHECK_EQ(OK, rv); rv = DoSetupEntryForRead(); @@ -1086,7 +1086,9 @@ int HttpCache::Transaction::DoGetBackendComplete(int result) { } int HttpCache::Transaction::DoInitEntry() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoInitEntry"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoInitEntry", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(!new_entry_); if (!cache_.get()) { @@ -1104,7 +1106,9 @@ int HttpCache::Transaction::DoInitEntry() { } int HttpCache::Transaction::DoOpenOrCreateEntry() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoOpenOrCreateEntry"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoOpenOrCreateEntry", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(!new_entry_); TransitionToState(STATE_OPEN_OR_CREATE_ENTRY_COMPLETE); cache_pending_ = true; @@ -1155,7 +1159,10 @@ int HttpCache::Transaction::DoOpenOrCreateEntry() { } int HttpCache::Transaction::DoOpenOrCreateEntryComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoOpenOrCreateEntryComplete"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoOpenOrCreateEntryComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is // OK, otherwise the cache will end up with an active entry without any // transaction attached. @@ -1223,7 +1230,9 @@ int HttpCache::Transaction::DoOpenOrCreateEntryComplete(int result) { } int HttpCache::Transaction::DoDoomEntry() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoDoomEntry"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoDoomEntry", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TransitionToState(STATE_DOOM_ENTRY_COMPLETE); cache_pending_ = true; if (first_cache_access_since_.is_null()) @@ -1233,7 +1242,9 @@ int HttpCache::Transaction::DoDoomEntry() { } int HttpCache::Transaction::DoDoomEntryComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoDoomEntryComplete"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoDoomEntryComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_DOOM_ENTRY, result); cache_pending_ = false; @@ -1244,7 +1255,9 @@ int HttpCache::Transaction::DoDoomEntryComplete(int result) { } int HttpCache::Transaction::DoCreateEntry() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCreateEntry"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoCreateEntry", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(!new_entry_); TransitionToState(STATE_CREATE_ENTRY_COMPLETE); cache_pending_ = true; @@ -1253,7 +1266,9 @@ int HttpCache::Transaction::DoCreateEntry() { } int HttpCache::Transaction::DoCreateEntryComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCreateEntryComplete"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoCreateEntryComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); // It is important that we go to STATE_ADD_TO_ENTRY whenever the result is // OK, otherwise the cache will end up with an active entry without any // transaction attached. @@ -1293,7 +1308,9 @@ int HttpCache::Transaction::DoCreateEntryComplete(int result) { } int HttpCache::Transaction::DoAddToEntry() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoAddToEntry"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoAddToEntry", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(new_entry_); cache_pending_ = true; net_log_.BeginEvent(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY); @@ -1367,7 +1384,9 @@ void HttpCache::Transaction::AddCacheLockTimeoutHandler(ActiveEntry* entry) { } int HttpCache::Transaction::DoAddToEntryComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoAddToEntryComplete"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoAddToEntryComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_ADD_TO_ENTRY, result); const base::TimeDelta entry_lock_wait = @@ -1459,7 +1478,9 @@ int HttpCache::Transaction::DoDoneHeadersAddToEntryComplete(int result) { } int HttpCache::Transaction::DoCacheReadResponse() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadResponse"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoCacheReadResponse", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(entry_); TransitionToState(STATE_CACHE_READ_RESPONSE_COMPLETE); @@ -1472,7 +1493,10 @@ int HttpCache::Transaction::DoCacheReadResponse() { } int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadResponseComplete"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoCacheReadResponseComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_READ_INFO, result); @@ -1550,8 +1574,10 @@ int HttpCache::Transaction::DoCacheReadResponseComplete(int result) { } int HttpCache::Transaction::DoCacheWriteUpdatedPrefetchResponse(int result) { - TRACE_EVENT0(NetTracingCategory(), - "HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponse"); + TRACE_EVENT_WITH_FLOW0( + "io", "HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponse", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(updated_prefetch_response_); // TODO(jkarlin): If DoUpdateCachedResponse is also called for this // transaction then metadata will be written to cache twice. If prefetching @@ -1562,16 +1588,20 @@ int HttpCache::Transaction::DoCacheWriteUpdatedPrefetchResponse(int result) { int HttpCache::Transaction::DoCacheWriteUpdatedPrefetchResponseComplete( int result) { - TRACE_EVENT0( - NetTracingCategory(), - "HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponseComplete"); + TRACE_EVENT_WITH_FLOW0( + "io", "HttpCacheTransaction::DoCacheWriteUpdatedPrefetchResponseComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); updated_prefetch_response_.reset(); TransitionToState(STATE_CACHE_DISPATCH_VALIDATION); return OnWriteResponseInfoToEntryComplete(result); } int HttpCache::Transaction::DoCacheDispatchValidation() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheDispatchValidation"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoCacheDispatchValidation", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (!entry_) { // Entry got destroyed when twiddling unused-since-prefetch bit. TransitionToState(STATE_HEADERS_PHASE_CANNOT_PROCEED); @@ -1653,7 +1683,9 @@ int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) { &custom_request_->extra_headers); if (reading_ && partial_->IsCurrentRangeCached()) { - TransitionToState(STATE_CACHE_READ_DATA); + // We're about to read a range of bytes from the cache. Signal it to the + // consumer through the "connected" callback. + TransitionToState(STATE_CONNECTED_CALLBACK); return OK; } @@ -1661,8 +1693,10 @@ int HttpCache::Transaction::DoCompletePartialCacheValidation(int result) { } int HttpCache::Transaction::DoCacheUpdateStaleWhileRevalidateTimeout() { - TRACE_EVENT0( - "io", "HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeout"); + TRACE_EVENT_WITH_FLOW0( + "io", "HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeout", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); response_.stale_revalidate_timeout = cache_->clock_->Now() + kStaleRevalidateTimeout; TransitionToState(STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT_COMPLETE); @@ -1671,15 +1705,20 @@ int HttpCache::Transaction::DoCacheUpdateStaleWhileRevalidateTimeout() { int HttpCache::Transaction::DoCacheUpdateStaleWhileRevalidateTimeoutComplete( int result) { - TRACE_EVENT0( + TRACE_EVENT_WITH_FLOW0( "io", - "HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeoutComplete"); - TransitionToState(STATE_SETUP_ENTRY_FOR_READ); + "HttpCacheTransaction::DoCacheUpdateStaleWhileRevalidateTimeoutComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); + DCHECK(!reading_); + TransitionToState(STATE_CONNECTED_CALLBACK); return OnWriteResponseInfoToEntryComplete(result); } int HttpCache::Transaction::DoSendRequest() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoSendRequest"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoSendRequest", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(mode_ & WRITE || mode_ == NONE); DCHECK(!network_trans_.get()); @@ -1716,7 +1755,9 @@ int HttpCache::Transaction::DoSendRequest() { } int HttpCache::Transaction::DoSendRequestComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoSendRequestComplete"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoSendRequestComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (!cache_.get()) { TransitionToState(STATE_FINISH_HEADERS); return ERR_UNEXPECTED; @@ -1749,8 +1790,10 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) { } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { DCHECK(response); response_.cert_request_info = response->cert_request_info; + } else if (result == ERR_INCONSISTENT_IP_ADDRESS_SPACE) { + DoomInconsistentEntry(); } else if (response_.was_cached) { - DoneWithEntry(true); + DoneWithEntry(/*entry_is_complete=*/true); } TransitionToState(STATE_FINISH_HEADERS); @@ -1759,7 +1802,9 @@ int HttpCache::Transaction::DoSendRequestComplete(int result) { // We received the response headers and there is no error. int HttpCache::Transaction::DoSuccessfulSendRequest() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoSuccessfulSendRequest"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoSuccessfulSendRequest", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(!new_response_); const HttpResponseInfo* new_response = network_trans_->GetResponseInfo(); @@ -1845,8 +1890,6 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { request_->is_subframe_document_resource); } - RecordNoStoreHeaderHistogram(request_->load_flags, new_response_); - if (new_response_->headers->response_code() == 416 && (method_ == "GET" || method_ == "POST")) { // If there is an active entry it may be destroyed with this transaction. @@ -1872,7 +1915,9 @@ int HttpCache::Transaction::DoSuccessfulSendRequest() { // We received 304 or 206 and we want to update the cached response headers. int HttpCache::Transaction::DoUpdateCachedResponse() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoUpdateCachedResponse"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoUpdateCachedResponse", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); int rv = OK; // Update the cached response based on the headers and properties of // new_response_. @@ -1916,20 +1961,28 @@ int HttpCache::Transaction::DoUpdateCachedResponse() { } int HttpCache::Transaction::DoCacheWriteUpdatedResponse() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteUpdatedResponse"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoCacheWriteUpdatedResponse", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TransitionToState(STATE_CACHE_WRITE_UPDATED_RESPONSE_COMPLETE); return WriteResponseInfoToEntry(response_, false); } int HttpCache::Transaction::DoCacheWriteUpdatedResponseComplete(int result) { - TRACE_EVENT0("io", - "HttpCacheTransaction::DoCacheWriteUpdatedResponseComplete"); + TRACE_EVENT_WITH_FLOW0( + "io", "HttpCacheTransaction::DoCacheWriteUpdatedResponseComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TransitionToState(STATE_UPDATE_CACHED_RESPONSE_COMPLETE); return OnWriteResponseInfoToEntryComplete(result); } int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoUpdateCachedResponseComplete"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoUpdateCachedResponseComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (mode_ == UPDATE) { DCHECK(!handling_206_); // We got a "not modified" response and already updated the corresponding @@ -1937,6 +1990,7 @@ int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) { // // By stopping to write to the cache now, we make sure that the 304 rather // than the cached 200 response, is what will be returned to the user. + UpdateSecurityHeadersBeforeForwarding(); DoneWithEntry(true); } else if (entry_ && !handling_206_) { DCHECK_EQ(READ_WRITE, mode_); @@ -1964,7 +2018,10 @@ int HttpCache::Transaction::DoUpdateCachedResponseComplete(int result) { } int HttpCache::Transaction::DoOverwriteCachedResponse() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoOverwriteCachedResponse"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoOverwriteCachedResponse", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (mode_ & READ) { TransitionToState(STATE_PARTIAL_HEADERS_RECEIVED); return OK; @@ -1999,8 +2056,9 @@ int HttpCache::Transaction::DoOverwriteCachedResponse() { } int HttpCache::Transaction::DoCacheWriteResponse() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponse"); - + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoCacheWriteResponse", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); // Invalidate any current entry with a successful response if this transaction // cannot write to this entry. This transaction then continues to read from // the network without writing to the backend. @@ -2027,13 +2085,18 @@ int HttpCache::Transaction::DoCacheWriteResponse() { } int HttpCache::Transaction::DoCacheWriteResponseComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheWriteResponseComplete"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoCacheWriteResponseComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TransitionToState(STATE_TRUNCATE_CACHED_DATA); return OnWriteResponseInfoToEntryComplete(result); } int HttpCache::Transaction::DoTruncateCachedData() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoTruncateCachedData"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoTruncateCachedData", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TransitionToState(STATE_TRUNCATE_CACHED_DATA_COMPLETE); if (!entry_) return OK; @@ -2043,7 +2106,10 @@ int HttpCache::Transaction::DoTruncateCachedData() { } int HttpCache::Transaction::DoTruncateCachedDataComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoInitEntry"); + TRACE_EVENT_WITH_FLOW0("io", + "HttpCacheTransaction::DoTruncateCachedDataComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (entry_) { net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_WRITE_DATA, result); @@ -2148,14 +2214,19 @@ int HttpCache::Transaction::DoFinishHeadersComplete(int rv) { } int HttpCache::Transaction::DoNetworkReadCacheWrite() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoNetworkReadCacheWrite"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoNetworkReadCacheWrite", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); DCHECK(InWriters()); TransitionToState(STATE_NETWORK_READ_CACHE_WRITE_COMPLETE); return entry_->writers->Read(read_buf_, read_buf_len_, io_callback_, this); } int HttpCache::Transaction::DoNetworkReadCacheWriteComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoNetworkReadCacheWriteComplete"); + TRACE_EVENT_WITH_FLOW0( + "io", "HttpCacheTransaction::DoNetworkReadCacheWriteComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (!cache_.get()) { TransitionToState(STATE_NONE); return ERR_UNEXPECTED; @@ -2222,13 +2293,17 @@ int HttpCache::Transaction::DoPartialNetworkReadCompleted(int result) { } int HttpCache::Transaction::DoNetworkRead() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoNetworkRead"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoNetworkRead", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); TransitionToState(STATE_NETWORK_READ_COMPLETE); return network_trans_->Read(read_buf_.get(), read_buf_len_, io_callback_); } int HttpCache::Transaction::DoNetworkReadComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoNetworkReadComplete"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoNetworkReadComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (!cache_.get()) { TransitionToState(STATE_NONE); @@ -2243,7 +2318,9 @@ int HttpCache::Transaction::DoNetworkReadComplete(int result) { } int HttpCache::Transaction::DoCacheReadData() { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadData"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoCacheReadData", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); if (method_ == "HEAD") { TransitionToState(STATE_NONE); @@ -2265,7 +2342,9 @@ int HttpCache::Transaction::DoCacheReadData() { } int HttpCache::Transaction::DoCacheReadDataComplete(int result) { - TRACE_EVENT0("io", "HttpCacheTransaction::DoCacheReadDataComplete"); + TRACE_EVENT_WITH_FLOW0("io", "HttpCacheTransaction::DoCacheReadDataComplete", + net_log().source().id, + TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT); net_log_.EndEventWithNetErrorCode(NetLogEventType::HTTP_CACHE_READ_DATA, result); @@ -2339,7 +2418,7 @@ void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log) { if (request_->extra_headers.HasHeader(HttpRequestHeaders::kRange)) range_found = true; - for (size_t i = 0; i < base::size(kSpecialHeaders); ++i) { + for (size_t i = 0; i < std::size(kSpecialHeaders); ++i) { if (HeaderMatches(request_->extra_headers, kSpecialHeaders[i].search)) { effective_load_flags_ |= kSpecialHeaders[i].load_flag; special_headers = true; @@ -2349,7 +2428,7 @@ void HttpCache::Transaction::SetRequest(const NetLogWithSource& net_log) { // Check for conditionalization headers which may correspond with a // cache validation request. - for (size_t i = 0; i < base::size(kValidationHeaders); ++i) { + for (size_t i = 0; i < std::size(kValidationHeaders); ++i) { const ValidationHeaderInfo& info = kValidationHeaders[i]; std::string validation_value; if (request_->extra_headers.GetHeader( @@ -2498,7 +2577,8 @@ int HttpCache::Transaction::BeginCacheValidation() { (truncated_ || response_.headers->response_code() == 206)) { DCHECK(!partial_); if (skip_validation) { - TransitionToState(STATE_SETUP_ENTRY_FOR_READ); + DCHECK(!reading_); + TransitionToState(STATE_CONNECTED_CALLBACK); return OK; } @@ -2540,9 +2620,10 @@ int HttpCache::Transaction::BeginCacheValidation() { if (skip_validation) { UpdateCacheEntryStatus(CacheEntryStatus::ENTRY_USED); + DCHECK(!reading_); TransitionToState(needs_stale_while_revalidate_cache_update ? STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT - : STATE_SETUP_ENTRY_FOR_READ); + : STATE_CONNECTED_CALLBACK); return OK; } else { // Make the network request conditional, to see if we may reuse our cached @@ -2616,7 +2697,7 @@ bool HttpCache::Transaction:: ExternallyConditionalizedValidationHeadersMatchEntry() const { DCHECK(external_validation_.initialized); - for (size_t i = 0; i < base::size(kValidationHeaders); i++) { + for (size_t i = 0; i < std::size(kValidationHeaders); i++) { if (external_validation_.values[i].empty()) continue; @@ -3027,6 +3108,62 @@ void HttpCache::Transaction::IgnoreRangeRequest() { partial_.reset(nullptr); } +// Called to signal to the consumer that we are about to read headers from a +// cached entry originally read from a given IP endpoint. +int HttpCache::Transaction::DoConnectedCallback() { + TransitionToState(STATE_CONNECTED_CALLBACK_COMPLETE); + if (connected_callback_.is_null()) { + return OK; + } + + return connected_callback_.Run( + TransportInfo(TransportType::kCached, response_.remote_endpoint, ""), + io_callback_); +} + +int HttpCache::Transaction::DoConnectedCallbackComplete(int result) { + if (result != OK) { + if (result == ERR_INCONSISTENT_IP_ADDRESS_SPACE) { + DoomInconsistentEntry(); + } else { + // Release the entry for further use - we are done using it. + DoneWithEntry(/*entry_is_complete=*/true); + } + + TransitionToState(STATE_NONE); + return result; + } + + if (reading_) { + // We can only get here if we're reading a partial range of bytes from the + // cache. In that case, proceed to read the bytes themselves. + DCHECK(partial_); + TransitionToState(STATE_CACHE_READ_DATA); + } else { + // Otherwise, we have just read headers from the cache. + TransitionToState(STATE_SETUP_ENTRY_FOR_READ); + } + return OK; +} + +void HttpCache::Transaction::DoomInconsistentEntry() { + // Explicitly call `DoomActiveEntry()` ourselves before calling + // `DoneWithEntry()` because we cannot rely on the latter doing it for us. + // Indeed, `DoneWithEntry(false)` does not call `DoomActiveEntry()` if either + // of the following conditions hold: + // + // - the transaction uses the cache in read-only mode + // - the transaction has passed the headers phase and is reading + // + // Inconsistent cache entries can cause deterministic failures even in + // read-only mode, so they should be doomed anyway. They can also be detected + // during the reading phase in the case of split range requests, since those + // requests can result in multiple connections being obtained to different + // remote endpoints. + cache_->DoomActiveEntry(cache_key_); + DoneWithEntry(/*entry_is_complete=*/false); +} + void HttpCache::Transaction::FixHeadersForHead() { if (response_.headers->response_code() == 206) { response_.headers->RemoveHeader("Content-Range"); @@ -3564,8 +3701,7 @@ void HttpCache::Transaction::SaveNetworkTransactionInfo( transaction.GetTotalReceivedBytes(); network_transaction_info_.total_sent_bytes += transaction.GetTotalSentBytes(); - ConnectionAttempts attempts; - transaction.GetConnectionAttempts(&attempts); + ConnectionAttempts attempts = transaction.GetConnectionAttempts(); for (const auto& attempt : attempts) network_transaction_info_.old_connection_attempts.push_back(attempt); network_transaction_info_.old_remote_endpoint = IPEndPoint(); @@ -3620,4 +3756,17 @@ bool HttpCache::Transaction::ShouldDisableCaching( return disable_caching; } +void HttpCache::Transaction::UpdateSecurityHeadersBeforeForwarding() { + // Because of COEP, we need to add CORP to the 304 of resources that set it + // previously. It will be blocked in the network service otherwise. + std::string stored_corp_header; + response_.headers->GetNormalizedHeader("Cross-Origin-Resource-Policy", + &stored_corp_header); + if (!stored_corp_header.empty()) { + new_response_->headers->SetHeader("Cross-Origin-Resource-Policy", + stored_corp_header); + } + return; +} + } // namespace net diff --git a/chromium/net/http/http_cache_transaction.h b/chromium/net/http/http_cache_transaction.h index d790eb9b869..cc359647d7c 100644 --- a/chromium/net/http/http_cache_transaction.h +++ b/chromium/net/http/http_cache_transaction.h @@ -165,7 +165,7 @@ class NET_EXPORT_PRIVATE HttpCache::Transaction : public HttpTransaction { void SetEarlyResponseHeadersCallback( ResponseHeadersCallback callback) override; int ResumeNetworkStart() override; - void GetConnectionAttempts(ConnectionAttempts* out) const override; + ConnectionAttempts GetConnectionAttempts() const override; void CloseConnectionOnDestruction() override; // Invoked when parallel validation cannot proceed due to response failure @@ -256,6 +256,8 @@ class NET_EXPORT_PRIVATE HttpCache::Transaction : public HttpTransaction { STATE_COMPLETE_PARTIAL_CACHE_VALIDATION, STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT, STATE_CACHE_UPDATE_STALE_WHILE_REVALIDATE_TIMEOUT_COMPLETE, + STATE_CONNECTED_CALLBACK, + STATE_CONNECTED_CALLBACK_COMPLETE, STATE_SETUP_ENTRY_FOR_READ, STATE_SEND_REQUEST, STATE_SEND_REQUEST_COMPLETE, @@ -334,6 +336,8 @@ class NET_EXPORT_PRIVATE HttpCache::Transaction : public HttpTransaction { int DoCacheQueryDataComplete(int result); int DoCacheUpdateStaleWhileRevalidateTimeout(); int DoCacheUpdateStaleWhileRevalidateTimeoutComplete(int result); + int DoConnectedCallback(); + int DoConnectedCallbackComplete(int result); int DoSetupEntryForRead(); int DoStartPartialCacheValidation(); int DoCompletePartialCacheValidation(int result); @@ -486,7 +490,16 @@ class NET_EXPORT_PRIVATE HttpCache::Transaction : public HttpTransaction { // to/from the entry. If |entry_is_complete| is false the result may be either // a truncated or a doomed entry based on whether the stored response can be // resumed or not. - void DoneWithEntry(bool did_finish); + void DoneWithEntry(bool entry_is_complete); + + // Dooms the given entry so that it will not be re-used for other requests, + // then calls `DoneWithEntry()`. + // + // This happens when network conditions have changed since the entry was + // cached, which results in deterministic failures when trying to use the + // cache entry. In order to let future requests succeed, the cache entry + // should be doomed. + void DoomInconsistentEntry(); // Returns an error to signal the caller that the current read failed. The // current operation |result| is also logged. If |restart| is true, the @@ -576,7 +589,11 @@ class NET_EXPORT_PRIVATE HttpCache::Transaction : public HttpTransaction { // headers. bool ShouldDisableCaching(const HttpResponseHeaders& headers) const; - State next_state_; + // 304 revalidations of resources that set security headers and that get + // forwarded might need to set these headers again to avoid being blocked. + void UpdateSecurityHeadersBeforeForwarding(); + + State next_state_{STATE_NONE}; // Initial request with which Start() was invoked. raw_ptr<const HttpRequestInfo> initial_request_; diff --git a/chromium/net/http/http_cache_unittest.cc b/chromium/net/http/http_cache_unittest.cc index 8448f792971..ec0d6f2589c 100644 --- a/chromium/net/http/http_cache_unittest.cc +++ b/chromium/net/http/http_cache_unittest.cc @@ -27,6 +27,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" +#include "base/time/time.h" #include "base/trace_event/memory_allocator_dump.h" #include "base/trace_event/memory_dump_request_args.h" #include "base/trace_event/process_memory_dump.h" @@ -741,10 +742,22 @@ bool LogContainsEventType(const RecordingNetLogObserver& net_log_observer, return !net_log_observer.GetEntriesWithType(expected).empty(); } +// Returns a TransportInfo distinct from the default for mock transactions, +// with the given port number. +TransportInfo TestTransportInfoWithPort(uint16_t port) { + TransportInfo result; + result.endpoint = IPEndPoint(IPAddress(42, 0, 1, 2), port); + return result; +} + // Returns a TransportInfo distinct from the default for mock transactions. TransportInfo TestTransportInfo() { - TransportInfo result; - result.endpoint = IPEndPoint(IPAddress(42, 0, 1, 2), 1337); + return TestTransportInfoWithPort(1337); +} + +TransportInfo CachedTestTransportInfo() { + TransportInfo result = TestTransportInfo(); + result.type = TransportType::kCached; return result; } @@ -857,12 +870,12 @@ TEST_F(HttpCacheTest, SimpleGET) { TEST_F(HttpCacheTest, SimpleGET_ConnectedCallback) { MockHttpCache cache; - ConnectedHandler connected_handler; - ScopedMockTransaction mock_transaction(kSimpleGET_Transaction); mock_transaction.transport_info = TestTransportInfo(); MockHttpRequest request(mock_transaction); + ConnectedHandler connected_handler; + std::unique_ptr<HttpTransaction> transaction; EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); ASSERT_THAT(transaction, NotNull()); @@ -882,18 +895,16 @@ TEST_F(HttpCacheTest, SimpleGET_ConnectedCallback) { // returns an error, the transaction fails with that error. TEST_F(HttpCacheTest, SimpleGET_ConnectedCallbackReturnError) { MockHttpCache cache; - - ConnectedHandler connected_handler; - // The exact error code does not matter. We only care that it is passed to - // the transaction's completion callback unmodified. - connected_handler.set_result(ERR_NOT_IMPLEMENTED); - MockHttpRequest request(kSimpleGET_Transaction); + ConnectedHandler connected_handler; std::unique_ptr<HttpTransaction> transaction; EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); ASSERT_THAT(transaction, NotNull()); + // The exact error code does not matter. We only care that it is passed to + // the transaction's completion callback unmodified. + connected_handler.set_result(ERR_NOT_IMPLEMENTED); transaction->SetConnectedCallback(connected_handler.Callback()); TestCompletionCallback callback; @@ -905,13 +916,15 @@ TEST_F(HttpCacheTest, SimpleGET_ConnectedCallbackReturnError) { // This test verifies that the callback passed to SetConnectedCallback() is // called once for requests that hit the cache. -// TODO(crbug.com/986744): This is not yet the case, but this test serves to -// document the intent. TEST_F(HttpCacheTest, SimpleGET_ConnectedCallbackOnCacheHit) { MockHttpCache cache; - // Populate the cache. - RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction); + { + // Populate the cache. + ScopedMockTransaction mock_transaction(kSimpleGET_Transaction); + mock_transaction.transport_info = TestTransportInfo(); + RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction); + } // Establish a baseline. EXPECT_EQ(1, cache.network_layer()->transaction_count()); @@ -919,7 +932,6 @@ TEST_F(HttpCacheTest, SimpleGET_ConnectedCallbackOnCacheHit) { // Load from the cache (only), observe the callback being called. ConnectedHandler connected_handler; - MockHttpRequest request(kSimpleGET_Transaction); std::unique_ptr<HttpTransaction> transaction; @@ -938,10 +950,129 @@ TEST_F(HttpCacheTest, SimpleGET_ConnectedCallbackOnCacheHit) { // was not called by a second network transaction. EXPECT_EQ(1, cache.network_layer()->transaction_count()); - // TODO(crbug.com/986744): Expect 1 call once HttpCache::Transaction is - // modified to call the callback on cache hits, expect that the endpoint is - // the one from which the cached data was downloaded. - EXPECT_THAT(connected_handler.transports(), IsEmpty()); + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo())); +} + +// This test verifies that when the callback passed to SetConnectedCallback() +// is called for a request that hit the cache and returns an error, the cache +// entry is reusable. +TEST_F(HttpCacheTest, SimpleGET_ConnectedCallbackOnCacheHitReturnError) { + MockHttpCache cache; + + { + // Populate the cache. + ScopedMockTransaction mock_transaction(kSimpleGET_Transaction); + mock_transaction.transport_info = TestTransportInfo(); + RunTransactionTest(cache.http_cache(), kSimpleGET_Transaction); + } + + MockHttpRequest request(kSimpleGET_Transaction); + + { + // Attempt to read from cache entry, but abort transaction due to a + // connected callback error. + ConnectedHandler connected_handler; + connected_handler.set_result(ERR_FAILED); + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsError(ERR_FAILED)); + + // Used the cache entry only. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo())); + } + + { + // Request the same resource once more, observe that it is read from cache. + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + // Used the cache entry only. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo())); + } +} + +// This test verifies that when the callback passed to SetConnectedCallback() +// returns `ERR_INCONSISTENT_IP_ADDRESS_SPACE`, the cache entry is invalidated. +TEST_F(HttpCacheTest, + SimpleGET_ConnectedCallbackOnCacheHitReturnInconsistentIpError) { + MockHttpCache cache; + + ScopedMockTransaction mock_transaction(kSimpleGET_Transaction); + mock_transaction.transport_info = TestTransportInfo(); + + // Populate the cache. + RunTransactionTest(cache.http_cache(), mock_transaction); + + MockHttpRequest request(kSimpleGET_Transaction); + + { + // Attempt to read from cache entry, but abort transaction due to a + // connected callback error. + ConnectedHandler connected_handler; + connected_handler.set_result(ERR_INCONSISTENT_IP_ADDRESS_SPACE); + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), + IsError(ERR_INCONSISTENT_IP_ADDRESS_SPACE)); + + // Used the cache entry only. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo())); + } + + { + // Request the same resource once more, observe that it is not read from + // cache. + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + // Used the network only. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(TestTransportInfo())); + } } class HttpCacheTest_SplitCacheFeature @@ -2052,6 +2183,7 @@ TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackCalledForEachRange) { ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); mock_transaction.request_headers = "Range: bytes = 20-29\r\n" EXTRA_HEADER; mock_transaction.data = "rg: 20-29 "; + mock_transaction.transport_info = TestTransportInfo(); RunTransactionTest(cache.http_cache(), mock_transaction); } @@ -2089,26 +2221,201 @@ TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackCalledForEachRange) { // NOTE: This works because only the mock transaction struct's address is // registered with the mocking framework - the pointee data is consulted // each time it is read. - auto new_transport_info = TestTransportInfo(); - new_transport_info.endpoint = - IPEndPoint(new_transport_info.endpoint.address(), 123); - mock_transaction.transport_info = new_transport_info; + mock_transaction.transport_info = TestTransportInfoWithPort(123); ReadAndVerifyTransaction(transaction.get(), mock_transaction); - // A second call for the last range's network transaction. - // TODO(crbug.com/986744): Expect 3 calls once the callback is notified on - // cache hits as well as network connections. - // TODO(crbug.com/986744): Test that the cached data is served as coming - // from the endpoint whence it was originally downloaded. + // A second call for the cached range, reported as coming from the original + // endpoint it was cached from. A third call for the last range's network + // transaction. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(TestTransportInfo(), CachedTestTransportInfo(), + TestTransportInfoWithPort(123))); + } +} + +// This test verifies that when the ConnectedCallback passed to a cache range +// transaction returns an `ERR_INCONSISTENT_IP_ADDRESS_SPACE` error during a +// partial read from cache, then the cache entry is invalidated. +TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackReturnInconsistentIpError) { + MockHttpCache cache; + + // Request an infix range and populate the cache with it. + { + ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); + mock_transaction.request_headers = "Range: bytes = 20-29\r\n" EXTRA_HEADER; + mock_transaction.data = "rg: 20-29 "; + mock_transaction.transport_info = TestTransportInfo(); + + RunTransactionTest(cache.http_cache(), mock_transaction); + } + + ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); + mock_transaction.request_headers = "Range: bytes = 10-39\r\n" EXTRA_HEADER; + mock_transaction.data = "rg: 10-19 rg: 20-29 rg: 30-39 "; + mock_transaction.transport_info = TestTransportInfo(); + MockHttpRequest request(mock_transaction); + + // Request a surrounding range. This *should* be read in three parts: + // + // 1. for the prefix: from the network + // 2. for the cached infix: from the cache + // 3. for the suffix: from the network + // + // The connected callback returns OK for 1), but fails during 2). As a result, + // the transaction fails partway and 3) is never created. The cache entry is + // invalidated as a result of the specific error code. + { + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + // 1 call for the first range's network transaction. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(TestTransportInfo())); + + // Set the callback to return an error the next time it is called. + connected_handler.set_result(ERR_INCONSISTENT_IP_ADDRESS_SPACE); + + std::string content; + EXPECT_THAT(ReadTransaction(transaction.get(), &content), + IsError(ERR_INCONSISTENT_IP_ADDRESS_SPACE)); + + // A second call that failed. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(TestTransportInfo(), CachedTestTransportInfo())); + } + + // Request the same range again, observe that nothing is read from cache. + { + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + std::string content; + EXPECT_THAT(ReadTransaction(transaction.get(), &content), IsOk()); + EXPECT_EQ(content, mock_transaction.data); + + // 1 call for the network transaction from which the whole response was + // read. The first 20 bytes were cached by the previous two requests, but + // the cache entry was doomed during the last transaction so they are not + // used here. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(TestTransportInfo())); + } +} + +// This test verifies that when the ConnectedCallback passed to a cache range +// transaction returns an `ERR_INCONSISTENT_IP_ADDRESS_SPACE` error during a +// network transaction, then the cache entry is invalidated. +TEST_F(HttpCacheTest, + RangeGET_ConnectedCallbackReturnInconsistentIpErrorForNetwork) { + MockHttpCache cache; + + // Request a prefix range and populate the cache with it. + { + ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); + mock_transaction.request_headers = "Range: bytes = 10-19\r\n" EXTRA_HEADER; + mock_transaction.data = "rg: 10-19 "; + mock_transaction.transport_info = TestTransportInfo(); + + RunTransactionTest(cache.http_cache(), mock_transaction); + } + + ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); + mock_transaction.request_headers = "Range: bytes = 10-29\r\n" EXTRA_HEADER; + mock_transaction.data = "rg: 10-19 rg: 20-29 "; + mock_transaction.transport_info = TestTransportInfo(); + MockHttpRequest request(mock_transaction); + + // Request a longer range. This *should* be read in two parts: + // + // 1. for the prefix: from the cache + // 2. for the suffix: from the network + { + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + // 1 call for the first range's network transaction. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo())); + + // Set the callback to return an error the next time it is called. + connected_handler.set_result(ERR_INCONSISTENT_IP_ADDRESS_SPACE); + + std::string content; + EXPECT_THAT(ReadTransaction(transaction.get(), &content), + IsError(ERR_INCONSISTENT_IP_ADDRESS_SPACE)); + + // A second call that failed. EXPECT_THAT(connected_handler.transports(), - ElementsAre(TestTransportInfo(), new_transport_info)); + ElementsAre(CachedTestTransportInfo(), TestTransportInfo())); + } + + // Request the same range again, observe that nothing is read from cache. + { + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + std::string content; + EXPECT_THAT(ReadTransaction(transaction.get(), &content), IsOk()); + EXPECT_EQ(content, mock_transaction.data); + + // 1 call for the network transaction from which the whole response was + // read. The first 20 bytes were cached by the previous two requests, but + // the cache entry was doomed during the last transaction so they are not + // used here. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(TestTransportInfo())); } } // This test verifies that when the ConnectedCallback passed to a cache // transaction returns an error for the second (or third) subrange transaction, -// the overall cache transaction fails with that error. +// the overall cache transaction fails with that error. The cache entry is still +// usable after that. TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackReturnErrorSecondTime) { MockHttpCache cache; @@ -2117,23 +2424,27 @@ TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackReturnErrorSecondTime) { ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); mock_transaction.request_headers = "Range: bytes = 20-29\r\n" EXTRA_HEADER; mock_transaction.data = "rg: 20-29 "; + mock_transaction.transport_info = TestTransportInfo(); RunTransactionTest(cache.http_cache(), mock_transaction); } - // Request a surrounding range. This results in three transactions: + ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); + mock_transaction.request_headers = "Range: bytes = 10-39\r\n" EXTRA_HEADER; + mock_transaction.data = "rg: 10-19 rg: 20-29 rg: 30-39 "; + mock_transaction.transport_info = TestTransportInfo(); + MockHttpRequest request(mock_transaction); + + // Request a surrounding range. This *should* be read in three parts: // - // 1. for the prefix, from the network - // 2. for the cached infix, from the cache - // 3. for the suffix, from the network + // 1. for the prefix: from the network + // 2. for the cached infix: from the cache + // 3. for the suffix: from the network // - // The connected callback returns OK for 1), but fails after that. + // The connected callback returns OK for 1), but fails during 2). As a result, + // the transaction fails partway and 3) is never created. The prefix is still + // cached, such that the cache entry ends up with both the prefix and infix. { - ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); - mock_transaction.request_headers = "Range: bytes = 10-39\r\n" EXTRA_HEADER; - mock_transaction.data = "rg: 10-19 rg: 20-29 rg: 30-39 "; - MockHttpRequest request(mock_transaction); - ConnectedHandler connected_handler; std::unique_ptr<HttpTransaction> transaction; @@ -2150,7 +2461,7 @@ TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackReturnErrorSecondTime) { // 1 call for the first range's network transaction. EXPECT_THAT(connected_handler.transports(), - ElementsAre(DefaultTransportInfo())); + ElementsAre(TestTransportInfo())); // Set the callback to return an error the next time it is called. The exact // error code is irrelevant, what matters is that it is reflected in the @@ -2163,7 +2474,91 @@ TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackReturnErrorSecondTime) { // A second call that failed. EXPECT_THAT(connected_handler.transports(), - ElementsAre(DefaultTransportInfo(), DefaultTransportInfo())); + ElementsAre(TestTransportInfo(), CachedTestTransportInfo())); + } + + // Request the same range again, observe that the prefix and infix are both + // read from cache. Only the suffix is fetched from the network. + { + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + // 1 call for the first range's cache transaction: the first 20 bytes were + // cached by the previous two requests. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo())); + + std::string content; + EXPECT_THAT(ReadTransaction(transaction.get(), &content), IsOk()); + EXPECT_EQ(content, mock_transaction.data); + + // A second call from the network transaction for the last 10 bytes. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo(), TestTransportInfo())); + } +} + +// This test verifies that the ConnectedCallback passed to a cache transaction +// is called once per subrange in the case of a range request with a partial +// cache hit, even when a prefix of the range is cached. +TEST_F(HttpCacheTest, RangeGET_ConnectedCallbackCalledForEachRangeWithPrefix) { + MockHttpCache cache; + + // Request a prefix range and populate the cache with it. + { + ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); + mock_transaction.request_headers = "Range: bytes = 10-19\r\n" EXTRA_HEADER; + mock_transaction.data = "rg: 10-19 "; + mock_transaction.transport_info = TestTransportInfo(); + + RunTransactionTest(cache.http_cache(), mock_transaction); + } + + // Request a surrounding range and observe that the callback is called once + // per subrange, as split up by cache hits. + { + ScopedMockTransaction mock_transaction(kRangeGET_TransactionOK); + mock_transaction.request_headers = "Range: bytes = 10-39\r\n" EXTRA_HEADER; + mock_transaction.data = "rg: 10-19 rg: 20-29 rg: 30-39 "; + mock_transaction.transport_info = TestTransportInfoWithPort(123); + MockHttpRequest request(mock_transaction); + + ConnectedHandler connected_handler; + + std::unique_ptr<HttpTransaction> transaction; + EXPECT_THAT(cache.CreateTransaction(&transaction), IsOk()); + ASSERT_THAT(transaction, NotNull()); + + transaction->SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + ASSERT_THAT( + transaction->Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + // 1 call for the first range from the cache, reported as coming from the + // endpoint which initially served the cached range. + EXPECT_THAT(connected_handler.transports(), + ElementsAre(CachedTestTransportInfo())); + + ReadAndVerifyTransaction(transaction.get(), mock_transaction); + + // A second call for the last range's network transaction. + EXPECT_THAT( + connected_handler.transports(), + ElementsAre(CachedTestTransportInfo(), TestTransportInfoWithPort(123))); } } @@ -13070,4 +13465,45 @@ TEST_F(HttpCacheTest, DnsAliasesRevalidation) { EXPECT_THAT(response.dns_aliases, testing::ElementsAre("alias3", "alias4")); } +TEST_F(HttpCacheTest, SecurityHeadersAreCopiedToConditionalizedResponse) { + MockHttpCache cache; + HttpResponseInfo response; + ScopedMockTransaction transaction(kSimpleGET_Transaction); + + static const Response kNetResponse1 = { + "HTTP/1.1 200 OK", + "Date: Fri, 12 Jun 2009 21:46:42 GMT\n" + "Server: server1\n" + "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n" + "Cross-Origin-Resource-Policy: cross-origin\n", + "body1"}; + + static const Response kNetResponse2 = { + "HTTP/1.1 304 Not Modified", + "Date: Wed, 22 Jul 2009 03:15:26 GMT\n" + "Server: server2\n" + "Last-Modified: Wed, 06 Feb 2008 22:38:21 GMT\n", + ""}; + + kNetResponse1.AssignTo(&transaction); + RunTransactionTestWithResponseInfo(cache.http_cache(), transaction, + &response); + + // On the second request, the cache is revalidated. + const char kExtraRequestHeaders[] = + "If-Modified-Since: Wed, 06 Feb 2008 22:38:21 GMT\r\n"; + transaction.request_headers = kExtraRequestHeaders; + kNetResponse2.AssignTo(&transaction); + RunTransactionTestWithResponseInfo(cache.http_cache(), transaction, + &response); + + // Verify that the CORP header was carried over to the response. + std::string response_corp_header; + response.headers->GetNormalizedHeader("Cross-Origin-Resource-Policy", + &response_corp_header); + + EXPECT_EQ(304, response.headers->response_code()); + EXPECT_EQ("cross-origin", response_corp_header); +} + } // namespace net diff --git a/chromium/net/http/http_chunked_decoder_unittest.cc b/chromium/net/http/http_chunked_decoder_unittest.cc index 533e11b6e44..4dbefb346f1 100644 --- a/chromium/net/http/http_chunked_decoder_unittest.cc +++ b/chromium/net/http/http_chunked_decoder_unittest.cc @@ -8,7 +8,6 @@ #include <string> #include <vector> -#include "base/cxx17_backports.h" #include "base/format_macros.h" #include "base/strings/stringprintf.h" #include "net/base/net_errors.h" @@ -71,14 +70,14 @@ TEST(HttpChunkedDecoderTest, Basic) { const char* const inputs[] = { "B\r\nhello hello\r\n0\r\n\r\n" }; - RunTest(inputs, base::size(inputs), "hello hello", true, 0); + RunTest(inputs, std::size(inputs), "hello hello", true, 0); } TEST(HttpChunkedDecoderTest, OneChunk) { const char* const inputs[] = { "5\r\nhello\r\n" }; - RunTest(inputs, base::size(inputs), "hello", false, 0); + RunTest(inputs, std::size(inputs), "hello", false, 0); } TEST(HttpChunkedDecoderTest, Typical) { @@ -88,7 +87,7 @@ TEST(HttpChunkedDecoderTest, Typical) { "5\r\nworld\r\n", "0\r\n\r\n" }; - RunTest(inputs, base::size(inputs), "hello world", true, 0); + RunTest(inputs, std::size(inputs), "hello world", true, 0); } TEST(HttpChunkedDecoderTest, Incremental) { @@ -105,7 +104,7 @@ TEST(HttpChunkedDecoderTest, Incremental) { "\r", "\n" }; - RunTest(inputs, base::size(inputs), "hello", true, 0); + RunTest(inputs, std::size(inputs), "hello", true, 0); } // Same as above, but group carriage returns with previous input. @@ -119,7 +118,7 @@ TEST(HttpChunkedDecoderTest, Incremental2) { "\n\r", "\n" }; - RunTest(inputs, base::size(inputs), "hello", true, 0); + RunTest(inputs, std::size(inputs), "hello", true, 0); } TEST(HttpChunkedDecoderTest, LF_InsteadOf_CRLF) { @@ -132,7 +131,7 @@ TEST(HttpChunkedDecoderTest, LF_InsteadOf_CRLF) { "5\nworld\n", "0\n\n" }; - RunTest(inputs, base::size(inputs), "hello world", true, 0); + RunTest(inputs, std::size(inputs), "hello world", true, 0); } TEST(HttpChunkedDecoderTest, Extensions) { @@ -140,7 +139,7 @@ TEST(HttpChunkedDecoderTest, Extensions) { "5;x=0\r\nhello\r\n", "0;y=\"2 \"\r\n\r\n" }; - RunTest(inputs, base::size(inputs), "hello", true, 0); + RunTest(inputs, std::size(inputs), "hello", true, 0); } TEST(HttpChunkedDecoderTest, Trailers) { @@ -151,7 +150,7 @@ TEST(HttpChunkedDecoderTest, Trailers) { "Bar: 2\r\n", "\r\n" }; - RunTest(inputs, base::size(inputs), "hello", true, 0); + RunTest(inputs, std::size(inputs), "hello", true, 0); } TEST(HttpChunkedDecoderTest, TrailersUnfinished) { @@ -160,7 +159,7 @@ TEST(HttpChunkedDecoderTest, TrailersUnfinished) { "0\r\n", "Foo: 1\r\n" }; - RunTest(inputs, base::size(inputs), "hello", false, 0); + RunTest(inputs, std::size(inputs), "hello", false, 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_TooBig) { @@ -171,7 +170,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_TooBig) { "48469410265455838241\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_0X) { @@ -182,7 +181,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_0X) { "0x5\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, ChunkSize_TrailingSpace) { @@ -194,7 +193,7 @@ TEST(HttpChunkedDecoderTest, ChunkSize_TrailingSpace) { "5 \r\nhello\r\n", "0\r\n\r\n" }; - RunTest(inputs, base::size(inputs), "hello", true, 0); + RunTest(inputs, std::size(inputs), "hello", true, 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingTab) { @@ -204,7 +203,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingTab) { "5\t\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingFormFeed) { @@ -215,7 +214,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingFormFeed) { "5\f\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingVerticalTab) { @@ -226,7 +225,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingVerticalTab) { "5\v\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingNonHexDigit) { @@ -237,7 +236,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_TrailingNonHexDigit) { "5H\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_LeadingSpace) { @@ -248,7 +247,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_LeadingSpace) { " 5\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidLeadingSeparator) { @@ -256,7 +255,7 @@ TEST(HttpChunkedDecoderTest, InvalidLeadingSeparator) { "\r\n5\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_NoSeparator) { @@ -265,7 +264,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_NoSeparator) { "1\r\n \r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 1); + RunTestUntilFailure(inputs, std::size(inputs), 1); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_Negative) { @@ -273,7 +272,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_Negative) { "8\r\n12345678\r\n-5\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidChunkSize_Plus) { @@ -284,7 +283,7 @@ TEST(HttpChunkedDecoderTest, InvalidChunkSize_Plus) { "+5\r\nhello\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, InvalidConsecutiveCRLFs) { @@ -293,7 +292,7 @@ TEST(HttpChunkedDecoderTest, InvalidConsecutiveCRLFs) { "\r\n\r\n\r\n\r\n", "0\r\n\r\n" }; - RunTestUntilFailure(inputs, base::size(inputs), 1); + RunTestUntilFailure(inputs, std::size(inputs), 1); } TEST(HttpChunkedDecoderTest, ReallyBigChunks) { @@ -340,7 +339,7 @@ TEST(HttpChunkedDecoderTest, ReallyBigChunks) { // Chunk terminator and the final chunk. char final_chunk[] = "\r\n0\r\n\r\n"; - EXPECT_EQ(OK, decoder.FilterBuf(final_chunk, base::size(final_chunk))); + EXPECT_EQ(OK, decoder.FilterBuf(final_chunk, std::size(final_chunk))); EXPECT_TRUE(decoder.reached_eof()); // Since |data| never included any chunk headers, it should not have been @@ -354,20 +353,20 @@ TEST(HttpChunkedDecoderTest, ReallyBigChunks) { TEST(HttpChunkedDecoderTest, ExcessiveChunkLen) { // Smallest number that can't be represented as a signed int64. const char* const inputs[] = {"8000000000000000\r\nhello\r\n"}; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, ExcessiveChunkLen2) { // Smallest number that can't be represented as an unsigned int64. const char* const inputs[] = {"10000000000000000\r\nhello\r\n"}; - RunTestUntilFailure(inputs, base::size(inputs), 0); + RunTestUntilFailure(inputs, std::size(inputs), 0); } TEST(HttpChunkedDecoderTest, BasicExtraData) { const char* const inputs[] = { "5\r\nhello\r\n0\r\n\r\nextra bytes" }; - RunTest(inputs, base::size(inputs), "hello", true, 11); + RunTest(inputs, std::size(inputs), "hello", true, 11); } TEST(HttpChunkedDecoderTest, IncrementalExtraData) { @@ -384,7 +383,7 @@ TEST(HttpChunkedDecoderTest, IncrementalExtraData) { "\r", "\nextra bytes" }; - RunTest(inputs, base::size(inputs), "hello", true, 11); + RunTest(inputs, std::size(inputs), "hello", true, 11); } TEST(HttpChunkedDecoderTest, MultipleExtraDataBlocks) { @@ -392,7 +391,7 @@ TEST(HttpChunkedDecoderTest, MultipleExtraDataBlocks) { "5\r\nhello\r\n0\r\n\r\nextra", " bytes" }; - RunTest(inputs, base::size(inputs), "hello", true, 11); + RunTest(inputs, std::size(inputs), "hello", true, 11); } // Test when the line with the chunk length is too long. @@ -405,7 +404,7 @@ TEST(HttpChunkedDecoderTest, LongChunkLengthLine) { big_chunk.get(), "5" }; - RunTestUntilFailure(inputs, base::size(inputs), 1); + RunTestUntilFailure(inputs, std::size(inputs), 1); } // Test when the extension portion of the line with the chunk length is too @@ -419,7 +418,7 @@ TEST(HttpChunkedDecoderTest, LongLengthLengthLine) { "5;", big_chunk.get() }; - RunTestUntilFailure(inputs, base::size(inputs), 1); + RunTestUntilFailure(inputs, std::size(inputs), 1); } } // namespace diff --git a/chromium/net/http/http_content_disposition_unittest.cc b/chromium/net/http/http_content_disposition_unittest.cc index 104cbba59a1..2e3dfb25e82 100644 --- a/chromium/net/http/http_content_disposition_unittest.cc +++ b/chromium/net/http/http_content_disposition_unittest.cc @@ -4,7 +4,6 @@ #include "net/http/http_content_disposition.h" -#include "base/cxx17_backports.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" @@ -202,7 +201,7 @@ TEST(HttpContentDispositionTest, Filename) { {"attachment; foobar=x; filename=\"foo.html\"", "", L"foo.html"}, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { HttpContentDisposition header(tests[i].header, tests[i].referrer_charset); EXPECT_EQ(tests[i].expected, base::UTF8ToWide(header.filename())) @@ -415,7 +414,7 @@ TEST(HttpContentDispositionTest, tc2231) { // TODO(abarth): http://greenbytes.de/tech/tc2231/#attrfc2047token // TODO(abarth): http://greenbytes.de/tech/tc2231/#attrfc2047quoted }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { HttpContentDisposition header(tests[i].header, std::string()); EXPECT_EQ(tests[i].expected_type, header.type()) << "Failed on input: " << tests[i].header; @@ -488,7 +487,7 @@ TEST(HttpContentDispositionTest, ParseResult) { HttpContentDisposition::INVALID}, }; - for (size_t i = 0; i < base::size(kTestCases); ++i) { + for (size_t i = 0; i < std::size(kTestCases); ++i) { const ParseResultTestCase& test_case = kTestCases[i]; HttpContentDisposition content_disposition(test_case.header, "utf-8"); int result = content_disposition.parse_result_flags(); diff --git a/chromium/net/http/http_network_layer.cc b/chromium/net/http/http_network_layer.cc index ee643060d8d..a02911e5eaf 100644 --- a/chromium/net/http/http_network_layer.cc +++ b/chromium/net/http/http_network_layer.cc @@ -18,7 +18,7 @@ #include "net/http/http_stream_factory_job.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" -#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_framer.h" namespace net { diff --git a/chromium/net/http/http_network_session.cc b/chromium/net/http/http_network_session.cc index 05a4a5377f3..fd3c01b7fc9 100644 --- a/chromium/net/http/http_network_session.cc +++ b/chromium/net/http/http_network_session.cc @@ -34,10 +34,10 @@ #include "net/socket/ssl_client_socket.h" #include "net/spdy/spdy_session.h" #include "net/spdy/spdy_session_pool.h" -#include "net/third_party/quiche/src/quic/core/crypto/quic_random.h" -#include "net/third_party/quiche/src/quic/core/quic_packets.h" -#include "net/third_party/quiche/src/quic/core/quic_tag.h" -#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quiche/quic/core/crypto/quic_random.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_tag.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h" namespace net { @@ -171,9 +171,7 @@ HttpNetworkSession::HttpNetworkSession(const HttpNetworkSessionParams& params, quic_stream_factory_(context.net_log, context.host_resolver, context.ssl_config_service, - context.client_socket_factory - ? context.client_socket_factory.get() - : ClientSocketFactory::GetDefaultFactory(), + context.client_socket_factory, context.http_server_properties, context.cert_verifier, context.ct_policy_enforcer, @@ -208,6 +206,7 @@ HttpNetworkSession::HttpNetworkSession(const HttpNetworkSessionParams& params, DCHECK(proxy_resolution_service_); DCHECK(ssl_config_service_); CHECK(http_server_properties_); + DCHECK(context_.client_socket_factory); normal_socket_pool_manager_ = std::make_unique<ClientSocketPoolManagerImpl>( CreateCommonConnectJobParams(false /* for_websockets */), @@ -235,6 +234,10 @@ HttpNetworkSession::HttpNetworkSession(const HttpNetworkSessionParams& params, http_server_properties_->SetMaxServerConfigsStoredInProperties( context.quic_context->params()->max_server_configs_stored_in_properties); + http_server_properties_->SetBrokenAlternativeServicesDelayParams( + context.quic_context->params() + ->initial_delay_for_broken_alternative_service, + context.quic_context->params()->exponential_backoff_on_initial_delay); if (!params_.disable_idle_sockets_close_on_memory_pressure) { memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>( @@ -343,8 +346,6 @@ base::Value HttpNetworkSession::QuicInfoToValue() const { quic_params->allow_server_migration); dict.SetBoolKey("race_stale_dns_on_connection", quic_params->race_stale_dns_on_connection); - dict.SetBoolKey("go_away_on_path_degrading", - quic_params->go_away_on_path_degrading); dict.SetBoolKey("estimate_initial_rtt", quic_params->estimate_initial_rtt); dict.SetBoolKey("server_push_cancellation", params_.enable_server_push_cancellation); @@ -398,9 +399,7 @@ CommonConnectJobParams HttpNetworkSession::CreateCommonConnectJobParams( // Use null websocket_endpoint_lock_manager, which is only set for WebSockets, // and only when not using a proxy. return CommonConnectJobParams( - context_.client_socket_factory ? context_.client_socket_factory.get() - : ClientSocketFactory::GetDefaultFactory(), - context_.host_resolver, &http_auth_cache_, + context_.client_socket_factory, context_.host_resolver, &http_auth_cache_, context_.http_auth_handler_factory, &spdy_session_pool_, &context_.quic_context->params()->supported_versions, &quic_stream_factory_, context_.proxy_delegate, diff --git a/chromium/net/http/http_network_session.h b/chromium/net/http/http_network_session.h index ead3f0a4f82..3b96c6c062f 100644 --- a/chromium/net/http/http_network_session.h +++ b/chromium/net/http/http_network_session.h @@ -35,7 +35,7 @@ #include "net/socket/websocket_endpoint_lock_manager.h" #include "net/spdy/spdy_session_pool.h" #include "net/ssl/ssl_client_session_cache.h" -#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h" #include "third_party/abseil-cpp/absl/types/optional.h" namespace base { diff --git a/chromium/net/http/http_network_transaction.cc b/chromium/net/http/http_network_transaction.cc index 6989ba8b6f7..7ae9b23194e 100644 --- a/chromium/net/http/http_network_transaction.cc +++ b/chromium/net/http/http_network_transaction.cc @@ -378,7 +378,7 @@ void HttpNetworkTransaction::DidDrainBodyForAuthRestart(bool keep_alive) { // Renewed streams shouldn't carry over sent or received bytes. DCHECK_EQ(0, new_stream->GetTotalReceivedBytes()); DCHECK_EQ(0, new_stream->GetTotalSentBytes()); - next_state_ = STATE_INIT_STREAM; + next_state_ = STATE_CONNECTED_CALLBACK; } stream_.reset(new_stream); } @@ -678,9 +678,8 @@ void HttpNetworkTransaction::OnQuicBroken() { net_error_details_.quic_broken = true; } -void HttpNetworkTransaction::GetConnectionAttempts( - ConnectionAttempts* out) const { - *out = connection_attempts_; +ConnectionAttempts HttpNetworkTransaction::GetConnectionAttempts() const { + return connection_attempts_; } bool HttpNetworkTransaction::IsSecureRequest() const { @@ -738,6 +737,9 @@ int HttpNetworkTransaction::DoLoop(int result) { case STATE_INIT_STREAM_COMPLETE: rv = DoInitStreamComplete(rv); break; + case STATE_CONNECTED_CALLBACK: + rv = DoConnectedCallback(); + break; case STATE_CONNECTED_CALLBACK_COMPLETE: rv = DoConnectedCallbackComplete(rv); break; @@ -857,7 +859,7 @@ int HttpNetworkTransaction::DoCreateStream() { int HttpNetworkTransaction::DoCreateStreamComplete(int result) { CopyConnectionAttemptsFromStreamRequest(); if (result == OK) { - next_state_ = STATE_INIT_STREAM; + next_state_ = STATE_CONNECTED_CALLBACK; DCHECK(stream_.get()); } else if (result == ERR_HTTP_1_1_REQUIRED || result == ERR_PROXY_HTTP_1_1_REQUIRED) { @@ -877,10 +879,8 @@ int HttpNetworkTransaction::DoInitStream() { DCHECK(stream_.get()); next_state_ = STATE_INIT_STREAM_COMPLETE; - stream_->GetRemoteEndpoint(&remote_endpoint_); - - return stream_->InitializeStream(request_, can_send_early_data_, priority_, - net_log_, io_callback_); + return stream_->InitializeStream(can_send_early_data_, priority_, net_log_, + io_callback_); } int HttpNetworkTransaction::DoInitStreamComplete(int result) { @@ -898,30 +898,47 @@ int HttpNetworkTransaction::DoInitStreamComplete(int result) { return result; } + next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; + return result; +} + +int HttpNetworkTransaction::DoConnectedCallback() { + // Register the HttpRequestInfo object on the stream here so that it's + // available when invoking the `connected_callback_`, as + // HttpStream::GetAcceptChViaAlps() needs the HttpRequestInfo to retrieve + // the ACCEPT_CH frame payload. + stream_->RegisterRequest(request_); + stream_->GetRemoteEndpoint(&remote_endpoint_); next_state_ = STATE_CONNECTED_CALLBACK_COMPLETE; - // Fire off notification that we have successfully connected. - if (!connected_callback_.is_null()) { - TransportType type = TransportType::kDirect; - if (!proxy_info_.is_direct()) { - type = TransportType::kProxied; - } - result = connected_callback_.Run( - TransportInfo(type, remote_endpoint_, - std::string(stream_->GetAcceptChViaAlps())), - base::BindOnce(&HttpNetworkTransaction::ResumeAfterConnected, - base::Unretained(this))); + if (connected_callback_.is_null()) { + return OK; } - return result; + // Fire off notification that we have successfully connected. + TransportType type = TransportType::kDirect; + if (!proxy_info_.is_direct()) { + type = TransportType::kProxied; + } + return connected_callback_.Run( + TransportInfo(type, remote_endpoint_, + std::string{stream_->GetAcceptChViaAlps()}), + base::BindOnce(&HttpNetworkTransaction::ResumeAfterConnected, + base::Unretained(this))); } int HttpNetworkTransaction::DoConnectedCallbackComplete(int result) { - if (result == OK) { - // Only transition if we succeeded. Otherwise stop at STATE_NONE. - next_state_ = STATE_GENERATE_PROXY_AUTH_TOKEN; + if (result != OK) { + if (stream_) { + stream_->Close(/*not_reusable=*/false); + } + + // Stop the state machine here if the call failed. + return result; } - return result; + + next_state_ = STATE_INIT_STREAM; + return OK; } int HttpNetworkTransaction::DoGenerateProxyAuthToken() { diff --git a/chromium/net/http/http_network_transaction.h b/chromium/net/http/http_network_transaction.h index 551612a572d..38120f07544 100644 --- a/chromium/net/http/http_network_transaction.h +++ b/chromium/net/http/http_network_transaction.h @@ -124,7 +124,7 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction SSLCertRequestInfo* cert_info) override; void OnQuicBroken() override; - void GetConnectionAttempts(ConnectionAttempts* out) const override; + ConnectionAttempts GetConnectionAttempts() const override; private: FRIEND_TEST_ALL_PREFIXES(HttpNetworkTransactionTest, ResetStateForRestart); @@ -145,6 +145,7 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction STATE_CREATE_STREAM_COMPLETE, STATE_INIT_STREAM, STATE_INIT_STREAM_COMPLETE, + STATE_CONNECTED_CALLBACK, STATE_CONNECTED_CALLBACK_COMPLETE, STATE_GENERATE_PROXY_AUTH_TOKEN, STATE_GENERATE_PROXY_AUTH_TOKEN_COMPLETE, @@ -186,7 +187,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction int DoCreateStreamComplete(int result); int DoInitStream(); int DoInitStreamComplete(int result); - int DoConnectedCallbackComplete(int results); + int DoConnectedCallback(); + int DoConnectedCallbackComplete(int result); int DoGenerateProxyAuthToken(); int DoGenerateProxyAuthTokenComplete(int result); int DoGenerateServerAuthToken(); @@ -395,7 +397,8 @@ class NET_EXPORT_PRIVATE HttpNetworkTransaction int64_t total_sent_bytes_; // When the transaction started / finished sending the request, including - // the body, if present. + // the body, if present. |send_start_time_| is set to |base::TimeTicks()| + // until |SendRequest()| is called on |stream_|, and reset for auth restarts. base::TimeTicks send_start_time_; base::TimeTicks send_end_time_; diff --git a/chromium/net/http/http_network_transaction_unittest.cc b/chromium/net/http/http_network_transaction_unittest.cc index 3c2eae1ece9..d9225864e3a 100644 --- a/chromium/net/http/http_network_transaction_unittest.cc +++ b/chromium/net/http/http_network_transaction_unittest.cc @@ -17,7 +17,6 @@ #include "base/bind.h" #include "base/compiler_specific.h" -#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/json/json_writer.h" @@ -37,6 +36,7 @@ #include "base/test/task_environment.h" #include "base/test/test_file_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" #include "build/build_config.h" #include "net/base/auth.h" #include "net/base/chunked_upload_data_stream.h" @@ -115,7 +115,7 @@ #include "net/test/gtest_util.h" #include "net/test/test_data_directory.h" #include "net/test/test_with_task_environment.h" -#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_framer.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "net/url_request/static_http_user_agent_settings.h" #include "net/websockets/websocket_handshake_stream_base.h" @@ -576,7 +576,7 @@ class HttpNetworkTransactionTest : public PlatformTest, // The total number of sent bytes should not have changed. EXPECT_EQ(out.total_sent_bytes, trans.GetTotalSentBytes()); - trans.GetConnectionAttempts(&out.connection_attempts); + out.connection_attempts = trans.GetConnectionAttempts(); return out; } @@ -981,6 +981,57 @@ TEST_F(HttpNetworkTransactionTest, ConnectedCallbackFailure) { ElementsAre(EmbeddedHttpServerTransportInfo())); } +// This test verifies that if the ConnectedCallback returns an error, the +// underlying socket is not closed and can be reused by the next transaction. +TEST_F(HttpNetworkTransactionTest, ConnectedCallbackFailureAllowsSocketReuse) { + ConnectedHandler connected_handler; + connected_handler.set_result(ERR_NOT_IMPLEMENTED); + + std::unique_ptr<HttpNetworkSession> session = CreateSession(&session_deps_); + auto request = DefaultRequestInfo(); + + // A single socket should be opened and used for both transactions. Data + // providers are matched to sockets at most once. + MockRead data_reads[] = { + MockRead("HTTP/1.0 200 OK\r\n"), + MockRead("X-Test-Header: foo\r\n\r\n"), + MockRead(SYNCHRONOUS, OK), + }; + StaticSocketDataProvider data(data_reads, base::span<MockWrite>()); + session_deps_.socket_factory->AddSocketDataProvider(&data); + + { + HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session.get()); + transaction.SetConnectedCallback(connected_handler.Callback()); + + TestCompletionCallback callback; + EXPECT_THAT( + transaction.Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsError(ERR_NOT_IMPLEMENTED)); + } + + // The data provider should still be linked to a socket. + EXPECT_TRUE(data.socket()); + auto* socket = data.socket(); + + { + HttpNetworkTransaction transaction(DEFAULT_PRIORITY, session.get()); + + TestCompletionCallback callback; + EXPECT_THAT( + transaction.Start(&request, callback.callback(), NetLogWithSource()), + IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsOk()); + + EXPECT_TRUE(transaction.GetResponseInfo()->headers->HasHeaderValue( + "X-Test-Header", "foo")); + + // Still linked to the same socket. + EXPECT_EQ(data.socket(), socket); + } +} + // This test verifies that the ConnectedCallback is called once in the case of // simple requests. TEST_F(HttpNetworkTransactionTest, ConnectedCallbackCalledOnce) { @@ -1983,7 +2034,7 @@ void HttpNetworkTransactionTest::PreconnectErrorResendRequestTest( ChunkedUploadDataStream upload_data_stream(0); if (chunked_upload) { request.method = "POST"; - upload_data_stream.AppendData(upload_data, base::size(upload_data) - 1, + upload_data_stream.AppendData(upload_data, std::size(upload_data) - 1, true); request.upload_data_stream = &upload_data_stream; } @@ -2524,7 +2575,7 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) { data.set_busy_before_sync_reads(true); session_deps_.socket_factory->AddSocketDataProvider(&data); - const int kNumUnreadBodies = base::size(data_writes) - 1; + const int kNumUnreadBodies = std::size(data_writes) - 1; std::string response_lines[kNumUnreadBodies]; uint32_t first_socket_log_id = NetLogSource::kInvalidId; @@ -2571,7 +2622,7 @@ TEST_F(HttpNetworkTransactionTest, KeepAliveAfterUnreadBody) { "HTTP/1.1 200 Hunky-Dory", }; - static_assert(kNumUnreadBodies == base::size(kStatusLines), + static_assert(kNumUnreadBodies == std::size(kStatusLines), "forgot to update kStatusLines"); for (int i = 0; i < kNumUnreadBodies; ++i) @@ -5999,7 +6050,7 @@ TEST_F(HttpNetworkTransactionTest, SameDestinationForDifferentProxyTypes) { }; MockWrite socks5_writes[] = { MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), - MockWrite(ASYNC, kSOCKS5Request, base::size(kSOCKS5Request)), + MockWrite(ASYNC, kSOCKS5Request, std::size(kSOCKS5Request)), MockWrite(SYNCHRONOUS, "GET /socks5 HTTP/1.1\r\n" "Host: test\r\n" @@ -6687,7 +6738,7 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) { "proxy-authorization", "Basic Zm9vOmJhcg==" }; spdy::SpdySerializedFrame req_get_authorization(spdy_util_.ConstructSpdyGet( - kExtraAuthorizationHeaders, base::size(kExtraAuthorizationHeaders) / 2, 3, + kExtraAuthorizationHeaders, std::size(kExtraAuthorizationHeaders) / 2, 3, LOWEST)); MockWrite spdy_writes[] = { CreateMockWrite(req_get, 0), CreateMockWrite(req_get_authorization, 3), @@ -6702,7 +6753,7 @@ TEST_F(HttpNetworkTransactionTest, HttpsProxySpdyGetWithProxyAuth) { spdy::SpdySerializedFrame resp_authentication( spdy_util_.ConstructSpdyReplyError( "407", kExtraAuthenticationHeaders, - base::size(kExtraAuthenticationHeaders) / 2, 1)); + std::size(kExtraAuthenticationHeaders) / 2, 1)); spdy::SpdySerializedFrame body_authentication( spdy_util_.ConstructSpdyDataFrame(1, true)); spdy::SpdySerializedFrame resp_data( @@ -8647,18 +8698,18 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuthV2) { base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kExpectedNegotiateMsg), - base::size(ntlm::test::kExpectedNegotiateMsg)), + std::size(ntlm::test::kExpectedNegotiateMsg)), &negotiate_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kChallengeMsgFromSpecV2), - base::size(ntlm::test::kChallengeMsgFromSpecV2)), + std::size(ntlm::test::kChallengeMsgFromSpecV2)), &challenge_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2), - base::size( + std::size( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2)), &authenticate_msg); @@ -8799,18 +8850,18 @@ TEST_F(HttpNetworkTransactionTest, NTLMAuthV2WrongThenRightPassword) { base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kExpectedNegotiateMsg), - base::size(ntlm::test::kExpectedNegotiateMsg)), + std::size(ntlm::test::kExpectedNegotiateMsg)), &negotiate_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kChallengeMsgFromSpecV2), - base::size(ntlm::test::kChallengeMsgFromSpecV2)), + std::size(ntlm::test::kChallengeMsgFromSpecV2)), &challenge_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2), - base::size( + std::size( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2)), &authenticate_msg); @@ -9045,18 +9096,18 @@ TEST_F(HttpNetworkTransactionTest, NTLMOverHttp2) { base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kExpectedNegotiateMsg), - base::size(ntlm::test::kExpectedNegotiateMsg)), + std::size(ntlm::test::kExpectedNegotiateMsg)), &negotiate_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kChallengeMsgFromSpecV2), - base::size(ntlm::test::kChallengeMsgFromSpecV2)), + std::size(ntlm::test::kChallengeMsgFromSpecV2)), &challenge_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2), - base::size( + std::size( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2)), &authenticate_msg); @@ -9236,18 +9287,18 @@ TEST_F(HttpNetworkTransactionTest, NTLMOverHttp2WithWebsockets) { base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kExpectedNegotiateMsg), - base::size(ntlm::test::kExpectedNegotiateMsg)), + std::size(ntlm::test::kExpectedNegotiateMsg)), &negotiate_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kChallengeMsgFromSpecV2), - base::size(ntlm::test::kChallengeMsgFromSpecV2)), + std::size(ntlm::test::kChallengeMsgFromSpecV2)), &challenge_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2), - base::size( + std::size( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2)), &authenticate_msg); @@ -9426,18 +9477,18 @@ TEST_F(HttpNetworkTransactionTest, NTLMProxyTLSHandshakeReset) { base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kExpectedNegotiateMsg), - base::size(ntlm::test::kExpectedNegotiateMsg)), + std::size(ntlm::test::kExpectedNegotiateMsg)), &negotiate_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>(ntlm::test::kChallengeMsgFromSpecV2), - base::size(ntlm::test::kChallengeMsgFromSpecV2)), + std::size(ntlm::test::kChallengeMsgFromSpecV2)), &challenge_msg); base::Base64Encode( base::StringPiece( reinterpret_cast<const char*>( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2), - base::size( + std::size( ntlm::test::kExpectedAuthenticateMsgEmptyChannelBindingsV2)), &authenticate_msg); @@ -11535,7 +11586,7 @@ TEST_F(HttpNetworkTransactionTest, RedirectOfHttpsConnectViaSpdyProxy) { "http://login.example.com/", }; spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError( - "302", kExtraHeaders, base::size(kExtraHeaders) / 2, 1)); + "302", kExtraHeaders, std::size(kExtraHeaders) / 2, 1)); MockRead data_reads[] = { // Pause on first read. MockRead(ASYNC, ERR_IO_PENDING, 1), CreateMockRead(resp, 2), @@ -11645,7 +11696,7 @@ TEST_F(HttpNetworkTransactionTest, ErrorResponseToHttpsConnectViaSpdyProxy) { "http://login.example.com/", }; spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyReplyError( - "404", kExtraHeaders, base::size(kExtraHeaders) / 2, 1)); + "404", kExtraHeaders, std::size(kExtraHeaders) / 2, 1)); spdy::SpdySerializedFrame body( spdy_util_.ConstructSpdyDataFrame(1, "The host does not exist", true)); MockRead data_reads[] = { @@ -11707,7 +11758,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthSpdyProxy) { "proxy-authorization", "Basic Zm9vOmJhcg==", }; spdy::SpdySerializedFrame connect2(spdy_util_.ConstructSpdyConnect( - kAuthCredentials, base::size(kAuthCredentials) / 2, 3, + kAuthCredentials, std::size(kAuthCredentials) / 2, 3, HttpProxyConnectJob::kH2QuicTunnelPriority, HostPortPair("www.example.org", 443))); // fetch https://www.example.org/ via HTTP @@ -11730,7 +11781,7 @@ TEST_F(HttpNetworkTransactionTest, BasicAuthSpdyProxy) { "proxy-authenticate", "Basic realm=\"MyRealm1\"", }; spdy::SpdySerializedFrame conn_auth_resp(spdy_util_.ConstructSpdyReplyError( - kAuthStatus, kAuthChallenge, base::size(kAuthChallenge) / 2, 1)); + kAuthStatus, kAuthChallenge, std::size(kAuthChallenge) / 2, 1)); spdy::SpdySerializedFrame conn_resp( spdy_util_.ConstructSpdyGetReply(nullptr, 0, 3)); @@ -12339,13 +12390,13 @@ TEST_F(HttpNetworkTransactionTest, SOCKS4_HTTP_GET) { char read_buffer[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; MockWrite data_writes[] = { - MockWrite(ASYNC, write_buffer, base::size(write_buffer)), + MockWrite(ASYNC, write_buffer, std::size(write_buffer)), MockWrite("GET / HTTP/1.1\r\n" "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { - MockRead(ASYNC, read_buffer, base::size(read_buffer)), + MockRead(ASYNC, read_buffer, std::size(read_buffer)), MockRead("HTTP/1.0 200 OK\r\n"), MockRead("Content-Type: text/html; charset=iso-8859-1\r\n\r\n"), MockRead("Payload"), MockRead(SYNCHRONOUS, OK)}; @@ -12396,14 +12447,14 @@ TEST_F(HttpNetworkTransactionTest, SOCKS4_SSL_GET) { MockWrite data_writes[] = { MockWrite(ASYNC, reinterpret_cast<char*>(write_buffer), - base::size(write_buffer)), + std::size(write_buffer)), MockWrite("GET / HTTP/1.1\r\n" "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { MockRead(ASYNC, reinterpret_cast<char*>(read_buffer), - base::size(read_buffer)), + std::size(read_buffer)), MockRead("HTTP/1.0 200 OK\r\n"), MockRead("Content-Type: text/html; charset=iso-8859-1\r\n\r\n"), MockRead("Payload"), MockRead(SYNCHRONOUS, OK)}; @@ -12456,13 +12507,13 @@ TEST_F(HttpNetworkTransactionTest, SOCKS4_HTTP_GET_no_PAC) { char read_buffer[] = { 0x00, 0x5A, 0x00, 0x00, 0, 0, 0, 0 }; MockWrite data_writes[] = { - MockWrite(ASYNC, write_buffer, base::size(write_buffer)), + MockWrite(ASYNC, write_buffer, std::size(write_buffer)), MockWrite("GET / HTTP/1.1\r\n" "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { - MockRead(ASYNC, read_buffer, base::size(read_buffer)), + MockRead(ASYNC, read_buffer, std::size(read_buffer)), MockRead("HTTP/1.0 200 OK\r\n"), MockRead("Content-Type: text/html; charset=iso-8859-1\r\n\r\n"), MockRead("Payload"), MockRead(SYNCHRONOUS, OK)}; @@ -12521,7 +12572,7 @@ TEST_F(HttpNetworkTransactionTest, SOCKS5_HTTP_GET) { MockWrite data_writes[] = { MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), MockWrite(ASYNC, kSOCKS5ExampleOkRequest, - base::size(kSOCKS5ExampleOkRequest)), + std::size(kSOCKS5ExampleOkRequest)), MockWrite("GET / HTTP/1.1\r\n" "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n")}; @@ -12592,14 +12643,14 @@ TEST_F(HttpNetworkTransactionTest, SOCKS5_SSL_GET) { MockWrite data_writes[] = { MockWrite(ASYNC, kSOCKS5GreetRequest, kSOCKS5GreetRequestLength), MockWrite(ASYNC, reinterpret_cast<const char*>(kSOCKS5ExampleOkRequest), - base::size(kSOCKS5ExampleOkRequest)), + std::size(kSOCKS5ExampleOkRequest)), MockWrite("GET / HTTP/1.1\r\n" "Host: www.example.org\r\n" "Connection: keep-alive\r\n\r\n")}; MockRead data_reads[] = { MockRead(ASYNC, kSOCKS5GreetResponse, kSOCKS5GreetResponseLength), - MockRead(ASYNC, kSOCKS5SslOkResponse, base::size(kSOCKS5SslOkResponse)), + MockRead(ASYNC, kSOCKS5SslOkResponse, std::size(kSOCKS5SslOkResponse)), MockRead("HTTP/1.0 200 OK\r\n"), MockRead("Content-Type: text/html; charset=iso-8859-1\r\n\r\n"), MockRead("Payload"), @@ -12731,7 +12782,7 @@ TEST_F(HttpNetworkTransactionTest, GroupIdForDirectConnections) { }, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { session_deps_.proxy_resolution_service = ConfiguredProxyResolutionService::CreateFixed( tests[i].proxy_server, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -12789,7 +12840,7 @@ TEST_F(HttpNetworkTransactionTest, GroupIdForHTTPProxyConnections) { }, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { session_deps_.proxy_resolution_service = ConfiguredProxyResolutionService::CreateFixed( tests[i].proxy_server, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -12867,7 +12918,7 @@ TEST_F(HttpNetworkTransactionTest, GroupIdForSOCKSConnections) { }, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { session_deps_.proxy_resolution_service = ConfiguredProxyResolutionService::CreateFixed( tests[i].proxy_server, TRAFFIC_ANNOTATION_FOR_TESTS); @@ -16712,7 +16763,7 @@ TEST_F(HttpNetworkTransactionTest, SSLWriteCertError) { ERR_CERT_AUTHORITY_INVALID, ERR_CERT_DATE_INVALID, }; - for (size_t i = 0; i < base::size(kErrors); i++) { + for (size_t i = 0; i < std::size(kErrors); i++) { CheckErrorIsPassedBack(kErrors[i], ASYNC); CheckErrorIsPassedBack(kErrors[i], SYNCHRONOUS); } @@ -18544,8 +18595,7 @@ TEST_F(HttpNetworkTransactionTest, HttpSyncConnectError) { rv = callback.WaitForResult(); EXPECT_THAT(rv, IsError(ERR_NAME_NOT_RESOLVED)); - ConnectionAttempts attempts; - trans.GetConnectionAttempts(&attempts); + ConnectionAttempts attempts = trans.GetConnectionAttempts(); ASSERT_EQ(1u, attempts.size()); EXPECT_THAT(attempts[0].result, IsError(ERR_NAME_NOT_RESOLVED)); @@ -18577,8 +18627,7 @@ TEST_F(HttpNetworkTransactionTest, HttpAsyncConnectError) { rv = callback.WaitForResult(); EXPECT_THAT(rv, IsError(ERR_NAME_NOT_RESOLVED)); - ConnectionAttempts attempts; - trans.GetConnectionAttempts(&attempts); + ConnectionAttempts attempts = trans.GetConnectionAttempts(); ASSERT_EQ(1u, attempts.size()); EXPECT_THAT(attempts[0].result, IsError(ERR_NAME_NOT_RESOLVED)); diff --git a/chromium/net/http/http_proxy_connect_job_unittest.cc b/chromium/net/http/http_proxy_connect_job_unittest.cc index d10e833ba0a..e35978efa98 100644 --- a/chromium/net/http/http_proxy_connect_job_unittest.cc +++ b/chromium/net/http/http_proxy_connect_job_unittest.cc @@ -9,7 +9,6 @@ #include <string> #include <utility> -#include "base/cxx17_backports.h" #include "base/metrics/field_trial.h" #include "base/metrics/field_trial_param_associator.h" #include "base/metrics/field_trial_params.h" @@ -19,6 +18,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/test/metrics/histogram_tester.h" #include "base/test/task_environment.h" +#include "base/time/time.h" #include "build/build_config.h" #include "net/base/host_port_pair.h" #include "net/base/network_isolation_key.h" @@ -27,6 +27,7 @@ #include "net/dns/mock_host_resolver.h" #include "net/dns/public/secure_dns_policy.h" #include "net/http/http_network_session.h" +#include "net/http/http_response_headers.h" #include "net/nqe/network_quality_estimator_test_util.h" #include "net/socket/client_socket_handle.h" #include "net/socket/connect_job_test_util.h" @@ -512,12 +513,12 @@ TEST_P(HttpProxyConnectJobTest, ProxyDelegateExtraHeaders) { kResponseHeaderValue, }; spdy::SpdySerializedFrame req(spdy_util_.ConstructSpdyConnect( - kExtraRequestHeaders, base::size(kExtraRequestHeaders) / 2, 1, + kExtraRequestHeaders, std::size(kExtraRequestHeaders) / 2, 1, HttpProxyConnectJob::kH2QuicTunnelPriority, HostPortPair(kEndpointHost, 443))); MockWrite spdy_writes[] = {CreateMockWrite(req, 0)}; spdy::SpdySerializedFrame resp(spdy_util_.ConstructSpdyGetReply( - kExtraResponseHeaders, base::size(kExtraResponseHeaders) / 2, 1)); + kExtraResponseHeaders, std::size(kExtraResponseHeaders) / 2, 1)); MockRead spdy_reads[] = { CreateMockRead(resp, 1, ASYNC), MockRead(SYNCHRONOUS, ERR_IO_PENDING, 2), @@ -577,7 +578,7 @@ TEST_P(HttpProxyConnectJobTest, NeedAuth) { "Basic Zm9vOmJhcg==", }; spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect( - kSpdyAuthCredentials, base::size(kSpdyAuthCredentials) / 2, 3, + kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3, HttpProxyConnectJob::kH2QuicTunnelPriority, HostPortPair(kEndpointHost, 443))); @@ -596,7 +597,7 @@ TEST_P(HttpProxyConnectJobTest, NeedAuth) { }; spdy::SpdySerializedFrame connect_auth_resp( spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge, - base::size(kAuthChallenge) / 2, 1)); + std::size(kAuthChallenge) / 2, 1)); spdy::SpdySerializedFrame connect2_resp( spdy_util.ConstructSpdyGetReply(nullptr, 0, 3)); @@ -694,7 +695,7 @@ TEST_P(HttpProxyConnectJobTest, NeedAuthTwice) { "Basic Zm9vOmJhcg==", }; spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect( - kSpdyAuthCredentials, base::size(kSpdyAuthCredentials) / 2, 3, + kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3, HttpProxyConnectJob::kH2QuicTunnelPriority, HostPortPair(kEndpointHost, 443))); spdy::SpdySerializedFrame rst2( @@ -702,7 +703,7 @@ TEST_P(HttpProxyConnectJobTest, NeedAuthTwice) { spdy_util.UpdateWithStreamDestruction(3); spdy::SpdySerializedFrame connect3(spdy_util.ConstructSpdyConnect( - kSpdyAuthCredentials, base::size(kSpdyAuthCredentials) / 2, 5, + kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 5, HttpProxyConnectJob::kH2QuicTunnelPriority, HostPortPair(kEndpointHost, 443))); MockWrite spdy_writes[] = { @@ -722,10 +723,10 @@ TEST_P(HttpProxyConnectJobTest, NeedAuthTwice) { }; spdy::SpdySerializedFrame connect_auth_resp( spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge, - base::size(kAuthChallenge) / 2, 1)); + std::size(kAuthChallenge) / 2, 1)); spdy::SpdySerializedFrame connect2_auth_resp( spdy_util.ConstructSpdyReplyError(kAuthStatus, kAuthChallenge, - base::size(kAuthChallenge) / 2, 3)); + std::size(kAuthChallenge) / 2, 3)); spdy::SpdySerializedFrame connect3_resp( spdy_util.ConstructSpdyGetReply(nullptr, 0, 5)); MockRead spdy_reads[] = { @@ -819,7 +820,7 @@ TEST_P(HttpProxyConnectJobTest, HaveAuth) { }; SpdyTestUtil spdy_util; spdy::SpdySerializedFrame connect(spdy_util.ConstructSpdyConnect( - kSpdyAuthCredentials, base::size(kSpdyAuthCredentials) / 2, 1, + kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 1, HttpProxyConnectJob::kH2QuicTunnelPriority, HostPortPair(kEndpointHost, 443))); @@ -1268,7 +1269,7 @@ TEST_P(HttpProxyConnectJobTest, TunnelSetupRedirect) { "set-cookie", "foo=bar", }; - const int responseHeadersSize = base::size(responseHeaders) / 2; + const int responseHeadersSize = std::size(responseHeaders) / 2; spdy::SpdySerializedFrame resp(spdy_util.ConstructSpdyReplyError( "302", responseHeaders, responseHeadersSize, 1)); MockRead spdy_reads[] = { @@ -1358,7 +1359,7 @@ TEST_P(HttpProxyConnectJobTest, TestTimeoutsAuthChallenge) { "Basic Zm9vOmJhcg==", }; spdy::SpdySerializedFrame connect2(spdy_util.ConstructSpdyConnect( - kSpdyAuthCredentials, base::size(kSpdyAuthCredentials) / 2, 3, + kSpdyAuthCredentials, std::size(kSpdyAuthCredentials) / 2, 3, HttpProxyConnectJob::kH2QuicTunnelPriority, HostPortPair(kEndpointHost, 443))); // This may be sent in some tests, either when tearing down a successful @@ -1380,7 +1381,7 @@ TEST_P(HttpProxyConnectJobTest, TestTimeoutsAuthChallenge) { "Basic realm=\"MyRealm1\"", }; spdy::SpdySerializedFrame connect_auth_resp(spdy_util.ConstructSpdyReplyError( - kAuthStatus, kAuthChallenge, base::size(kAuthChallenge) / 2, 1)); + kAuthStatus, kAuthChallenge, std::size(kAuthChallenge) / 2, 1)); spdy::SpdySerializedFrame connect2_resp( spdy_util.ConstructSpdyGetReply(nullptr, 0, 3)); MockRead spdy_reads[] = { diff --git a/chromium/net/http/http_response_body_drainer_unittest.cc b/chromium/net/http/http_response_body_drainer_unittest.cc index d2f92e426ba..ed70a0d0f83 100644 --- a/chromium/net/http/http_response_body_drainer_unittest.cc +++ b/chromium/net/http/http_response_body_drainer_unittest.cc @@ -32,6 +32,7 @@ #include "net/http/transport_security_state.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/quic/quic_context.h" +#include "net/socket/socket_test_util.h" #include "net/ssl/ssl_config_service_defaults.h" #include "net/test/test_with_task_environment.h" #include "testing/gtest/include/gtest/gtest.h" @@ -97,8 +98,8 @@ class MockHttpStream : public HttpStream { ~MockHttpStream() override = default; // HttpStream implementation. - int InitializeStream(const HttpRequestInfo* request_info, - bool can_send_early, + void RegisterRequest(const HttpRequestInfo* request_info) override {} + int InitializeStream(bool can_send_early, RequestPriority priority, const NetLogWithSource& net_log, CompletionOnceCallback callback) override { @@ -256,6 +257,7 @@ class HttpResponseBodyDrainerTest : public TestWithTaskEnvironment { HttpNetworkSession* CreateNetworkSession() { HttpNetworkSessionContext context; + context.client_socket_factory = &socket_factory_; context.proxy_resolution_service = proxy_resolution_service_.get(); context.ssl_config_service = ssl_config_service_.get(); context.http_server_properties = http_server_properties_.get(); @@ -273,6 +275,7 @@ class HttpResponseBodyDrainerTest : public TestWithTaskEnvironment { TransportSecurityState transport_security_state_; DefaultCTPolicyEnforcer ct_policy_enforcer_; QuicContext quic_context_; + MockClientSocketFactory socket_factory_; const std::unique_ptr<HttpNetworkSession> session_; CloseResultWaiter result_waiter_; const raw_ptr<MockHttpStream> mock_stream_; // Owned by |drainer_|. diff --git a/chromium/net/http/http_response_headers.cc b/chromium/net/http/http_response_headers.cc index 4e4cd171d4a..91536513f3d 100644 --- a/chromium/net/http/http_response_headers.cc +++ b/chromium/net/http/http_response_headers.cc @@ -15,7 +15,6 @@ #include <unordered_map> #include <utility> -#include "base/cxx17_backports.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -110,11 +109,11 @@ const char* const kNonUpdatedHeaderPrefixes[] = { }; bool ShouldUpdateHeader(base::StringPiece name) { - for (size_t i = 0; i < base::size(kNonUpdatedHeaders); ++i) { + for (size_t i = 0; i < std::size(kNonUpdatedHeaders); ++i) { if (base::LowerCaseEqualsASCII(name, kNonUpdatedHeaders[i])) return false; } - for (size_t i = 0; i < base::size(kNonUpdatedHeaderPrefixes); ++i) { + for (size_t i = 0; i < std::size(kNonUpdatedHeaderPrefixes); ++i) { if (base::StartsWith(name, kNonUpdatedHeaderPrefixes[i], base::CompareCase::INSENSITIVE_ASCII)) return false; @@ -887,17 +886,17 @@ void HttpResponseHeaders::AddNonCacheableHeaders(HeaderSet* result) const { } void HttpResponseHeaders::AddHopByHopHeaders(HeaderSet* result) { - for (size_t i = 0; i < base::size(kHopByHopResponseHeaders); ++i) + for (size_t i = 0; i < std::size(kHopByHopResponseHeaders); ++i) result->insert(std::string(kHopByHopResponseHeaders[i])); } void HttpResponseHeaders::AddCookieHeaders(HeaderSet* result) { - for (size_t i = 0; i < base::size(kCookieResponseHeaders); ++i) + for (size_t i = 0; i < std::size(kCookieResponseHeaders); ++i) result->insert(std::string(kCookieResponseHeaders[i])); } void HttpResponseHeaders::AddChallengeHeaders(HeaderSet* result) { - for (size_t i = 0; i < base::size(kChallengeResponseHeaders); ++i) + for (size_t i = 0; i < std::size(kChallengeResponseHeaders); ++i) result->insert(std::string(kChallengeResponseHeaders[i])); } @@ -906,7 +905,7 @@ void HttpResponseHeaders::AddHopContentRangeHeaders(HeaderSet* result) { } void HttpResponseHeaders::AddSecurityStateHeaders(HeaderSet* result) { - for (size_t i = 0; i < base::size(kSecurityStateHeaders); ++i) + for (size_t i = 0; i < std::size(kSecurityStateHeaders); ++i) result->insert(std::string(kSecurityStateHeaders[i])); } diff --git a/chromium/net/http/http_response_info.cc b/chromium/net/http/http_response_info.cc index 111817b508b..cc35dd83961 100644 --- a/chromium/net/http/http_response_info.cc +++ b/chromium/net/http/http_response_info.cc @@ -15,7 +15,7 @@ #include "net/http/http_response_headers.h" #include "net/ssl/ssl_cert_request_info.h" #include "net/ssl/ssl_connection_status_flags.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h" #include "third_party/boringssl/src/include/openssl/ssl.h" using base::Time; diff --git a/chromium/net/http/http_security_headers_unittest.cc b/chromium/net/http/http_security_headers_unittest.cc index 63223ba9e01..4d4369c2b4a 100644 --- a/chromium/net/http/http_security_headers_unittest.cc +++ b/chromium/net/http/http_security_headers_unittest.cc @@ -4,6 +4,8 @@ #include <stdint.h> +#include <iterator> + #include "base/base64.h" #include "base/stl_util.h" #include "base/strings/string_piece.h" @@ -21,6 +23,7 @@ namespace net { namespace { namespace test_default { +#include "base/time/time.h" #include "net/http/transport_security_state_static_unittest_default.h" } diff --git a/chromium/net/http/http_server_properties.cc b/chromium/net/http/http_server_properties.cc index 39e8b65038b..0ac003c574e 100644 --- a/chromium/net/http/http_server_properties.cc +++ b/chromium/net/http/http_server_properties.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/check_op.h" +#include "base/containers/adapters.h" #include "base/containers/contains.h" #include "base/feature_list.h" #include "base/location.h" @@ -551,10 +552,9 @@ void HttpServerProperties::SetMaxServerConfigsStoredInProperties( // Update the |canonical_server_info_map_| as well, so it stays in sync with // |quic_server_info_map_|. canonical_server_info_map_ = QuicCanonicalMap(); - for (auto it = quic_server_info_map_.rbegin(); - it != quic_server_info_map_.rend(); ++it) { - temp_map.Put(it->first, it->second); - UpdateCanonicalServerInfoMap(it->first); + for (const auto& [key, server_info] : base::Reversed(quic_server_info_map_)) { + temp_map.Put(key, server_info); + UpdateCanonicalServerInfoMap(key); } quic_server_info_map_.Swap(temp_map); @@ -564,6 +564,13 @@ void HttpServerProperties::SetMaxServerConfigsStoredInProperties( } } +void HttpServerProperties::SetBrokenAlternativeServicesDelayParams( + absl::optional<base::TimeDelta> initial_delay, + absl::optional<bool> exponential_backoff_on_initial_delay) { + broken_alternative_services_.SetDelayParams( + initial_delay, exponential_backoff_on_initial_delay); +} + bool HttpServerProperties::IsInitialized() const { return is_initialized_; } @@ -1119,28 +1126,27 @@ void HttpServerProperties::OnServerInfoLoaded( server_info_map_.Swap(*server_info_map); // Add the entries from the memory cache. - for (auto it = server_info_map->rbegin(); it != server_info_map->rend(); - ++it) { + for (auto& [key, server_info] : base::Reversed(*server_info_map)) { // If there's no corresponding old entry, add the new entry directly. - auto old_entry = server_info_map_.Get(it->first); + auto old_entry = server_info_map_.Get(key); if (old_entry == server_info_map_.end()) { - server_info_map_.Put(it->first, std::move(it->second)); + server_info_map_.Put(key, std::move(server_info)); continue; } // Otherwise, merge the old and new entries. Prefer values from older // entries. if (!old_entry->second.supports_spdy.has_value()) - old_entry->second.supports_spdy = it->second.supports_spdy; + old_entry->second.supports_spdy = server_info.supports_spdy; if (!old_entry->second.alternative_services.has_value()) - old_entry->second.alternative_services = it->second.alternative_services; + old_entry->second.alternative_services = server_info.alternative_services; if (!old_entry->second.server_network_stats.has_value()) - old_entry->second.server_network_stats = it->second.server_network_stats; + old_entry->second.server_network_stats = server_info.server_network_stats; // |requires_http11| isn't saved to prefs, so the loaded entry should not // have it set. Unconditionally copy it from the new entry. DCHECK(!old_entry->second.requires_http11.has_value()); - old_entry->second.requires_http11 = it->second.requires_http11; + old_entry->second.requires_http11 = server_info.requires_http11; } // Attempt to find canonical servers. Canonical suffix only apply to HTTPS. @@ -1184,19 +1190,17 @@ void HttpServerProperties::OnQuicServerInfoMapLoaded( quic_server_info_map_.Swap(*quic_server_info_map); // Add the entries from the memory cache. - for (auto it = quic_server_info_map->rbegin(); - it != quic_server_info_map->rend(); ++it) { - if (quic_server_info_map_.Get(it->first) == quic_server_info_map_.end()) { - quic_server_info_map_.Put(it->first, it->second); + for (const auto& [key, server_info] : base::Reversed(*quic_server_info_map)) { + if (quic_server_info_map_.Get(key) == quic_server_info_map_.end()) { + quic_server_info_map_.Put(key, server_info); } } // Repopulate |canonical_server_info_map_| to stay in sync with // |quic_server_info_map_|. canonical_server_info_map_.clear(); - for (auto it = quic_server_info_map_.rbegin(); - it != quic_server_info_map_.rend(); ++it) { - UpdateCanonicalServerInfoMap(it->first); + for (const auto& [key, server_info] : base::Reversed(quic_server_info_map_)) { + UpdateCanonicalServerInfoMap(key); } } diff --git a/chromium/net/http/http_server_properties.h b/chromium/net/http/http_server_properties.h index 2ea3bf4cbe0..2d782f38455 100644 --- a/chromium/net/http/http_server_properties.h +++ b/chromium/net/http/http_server_properties.h @@ -29,11 +29,11 @@ #include "net/base/network_isolation_key.h" #include "net/http/alternative_service.h" #include "net/http/broken_alternative_services.h" -#include "net/third_party/quiche/src/quic/core/quic_bandwidth.h" -#include "net/third_party/quiche/src/quic/core/quic_server_id.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" -#include "net/third_party/quiche/src/spdy/core/spdy_framer.h" // TODO(willchan): Reconsider this. -#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_bandwidth.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_framer.h" // TODO(willchan): Reconsider this. +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "url/scheme_host_port.h" @@ -413,6 +413,13 @@ class NET_EXPORT HttpServerProperties void SetMaxServerConfigsStoredInProperties( size_t max_server_configs_stored_in_properties); + // If values are present, sets initial_delay and + // exponential_backoff_on_initial_delay which are used to calculate delay of + // broken alternative services. + void SetBrokenAlternativeServicesDelayParams( + absl::optional<base::TimeDelta> initial_delay, + absl::optional<bool> exponential_backoff_on_initial_delay); + // Returns whether HttpServerProperties is initialized. bool IsInitialized() const; diff --git a/chromium/net/http/http_server_properties_manager.cc b/chromium/net/http/http_server_properties_manager.cc index a6836ff2555..3513f4fd685 100644 --- a/chromium/net/http/http_server_properties_manager.cc +++ b/chromium/net/http/http_server_properties_manager.cc @@ -7,10 +7,12 @@ #include <utility> #include "base/bind.h" +#include "base/containers/adapters.h" #include "base/feature_list.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" #include "base/time/tick_clock.h" +#include "base/time/time.h" #include "base/values.h" #include "net/base/features.h" #include "net/base/host_port_pair.h" @@ -18,7 +20,7 @@ #include "net/base/port_util.h" #include "net/base/privacy_mode.h" #include "net/http/http_server_properties.h" -#include "net/third_party/quiche/src/quic/platform/api/quic_hostname_utils.h" +#include "net/third_party/quiche/src/quiche/quic/platform/api/quic_hostname_utils.h" #include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" #include "url/scheme_host_port.h" @@ -717,11 +719,7 @@ void HttpServerPropertiesManager::WriteToPrefs( // Convert |server_info_map| to a dictionary Value and add it to // |http_server_properties_dict|. base::Value servers_list(base::Value::Type::LIST); - for (auto map_it = server_info_map.rbegin(); map_it != server_info_map.rend(); - ++map_it) { - const HttpServerProperties::ServerInfoMapKey key = map_it->first; - const HttpServerProperties::ServerInfo& server_info = map_it->second; - + for (const auto& [key, server_info] : base::Reversed(server_info_map)) { // If can't convert the NetworkIsolationKey to a value, don't save to disk. // Generally happens because the key is for a unique origin. base::Value network_isolation_key_value; @@ -844,10 +842,7 @@ void HttpServerPropertiesManager::SaveQuicServerInfoMapToServerPrefs( if (quic_server_info_map.empty()) return; base::Value quic_servers_list(base::Value::Type::LIST); - for (auto it = quic_server_info_map.rbegin(); - it != quic_server_info_map.rend(); ++it) { - const HttpServerProperties::QuicServerInfoMapKey& key = it->first; - + for (const auto& [key, server_info] : base::Reversed(quic_server_info_map)) { base::Value network_isolation_key_value; // Don't save entries with ephemeral NIKs. if (!key.network_isolation_key.ToValue(&network_isolation_key_value)) @@ -858,7 +853,7 @@ void HttpServerPropertiesManager::SaveQuicServerInfoMapToServerPrefs( QuicServerIdToString(key.server_id)); quic_server_pref_dict.SetKey(kNetworkIsolationKey, std::move(network_isolation_key_value)); - quic_server_pref_dict.SetStringKey(kServerInfoKey, it->second); + quic_server_pref_dict.SetStringKey(kServerInfoKey, server_info); quic_servers_list.Append(std::move(quic_server_pref_dict)); } @@ -886,11 +881,8 @@ void HttpServerPropertiesManager::SaveBrokenAlternativeServicesToPrefs( std::map<BrokenAlternativeService, size_t> json_list_index_map; if (!recently_broken_alternative_services.empty()) { - for (auto it = recently_broken_alternative_services.rbegin(); - it != recently_broken_alternative_services.rend(); ++it) { - const BrokenAlternativeService& broken_alt_service = it->first; - int broken_count = it->second; - + for (const auto& [broken_alt_service, broken_count] : + base::Reversed(recently_broken_alternative_services)) { base::Value entry_dict(base::Value::Type::DICTIONARY); if (!TryAddBrokenAlternativeServiceFieldsToDictionaryValue( broken_alt_service, &entry_dict)) { diff --git a/chromium/net/http/http_server_properties_manager_unittest.cc b/chromium/net/http/http_server_properties_manager_unittest.cc index 0b3b339a0e2..598d9c934f7 100644 --- a/chromium/net/http/http_server_properties_manager_unittest.cc +++ b/chromium/net/http/http_server_properties_manager_unittest.cc @@ -7,6 +7,7 @@ #include <utility> #include "base/bind.h" +#include "base/callback.h" #include "base/feature_list.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" @@ -20,6 +21,7 @@ #include "base/test/scoped_feature_list.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_tick_clock.h" +#include "base/time/time.h" #include "base/values.h" #include "net/base/features.h" #include "net/base/ip_address.h" diff --git a/chromium/net/http/http_server_properties_unittest.cc b/chromium/net/http/http_server_properties_unittest.cc index 91a1dc3bb2f..d933e6cab06 100644 --- a/chromium/net/http/http_server_properties_unittest.cc +++ b/chromium/net/http/http_server_properties_unittest.cc @@ -9,6 +9,7 @@ #include <vector> #include "base/bind.h" +#include "base/callback.h" #include "base/check.h" #include "base/feature_list.h" #include "base/json/json_writer.h" @@ -17,6 +18,7 @@ #include "base/test/scoped_feature_list.h" #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" +#include "base/time/time.h" #include "base/values.h" #include "net/base/features.h" #include "net/base/host_port_pair.h" @@ -2178,6 +2180,66 @@ TEST_F(AlternateProtocolServerPropertiesTest, RemoveExpiredBrokenAltSvc) { baz_alternative_service, NetworkIsolationKey())); } +TEST_F(AlternateProtocolServerPropertiesTest, + SetBrokenAlternativeServicesDelayParams1) { + url::SchemeHostPort server("https", "foo", 443); + AlternativeService alternative_service(kProtoQUIC, "foo", 443); + SetAlternativeService(server, alternative_service); + + const base::TimeDelta initial_delay = base::Seconds(1); + impl_.SetBrokenAlternativeServicesDelayParams(initial_delay, true); + for (int i = 0; i < 10; ++i) { + impl_.MarkAlternativeServiceBroken(alternative_service, + NetworkIsolationKey()); + // |impl_| should have posted task to expire the brokenness of + // |alternative_service| + EXPECT_EQ(1u, GetPendingMainThreadTaskCount()); + EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, + NetworkIsolationKey())); + + // Advance time by just enough so that |alternative_service|'s brokenness + // expires. + FastForwardBy(initial_delay * (1 << i)); + + // Ensure brokenness of |alternative_service| has expired. + EXPECT_EQ(0u, GetPendingMainThreadTaskCount()); + EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, + NetworkIsolationKey())); + } +} + +TEST_F(AlternateProtocolServerPropertiesTest, + SetBrokenAlternativeServicesDelayParams2) { + url::SchemeHostPort server("https", "foo", 443); + AlternativeService alternative_service(kProtoQUIC, "foo", 443); + SetAlternativeService(server, alternative_service); + + const base::TimeDelta initial_delay = base::Seconds(5); + impl_.SetBrokenAlternativeServicesDelayParams(initial_delay, false); + for (int i = 0; i < 10; ++i) { + impl_.MarkAlternativeServiceBroken(alternative_service, + NetworkIsolationKey()); + // |impl_| should have posted task to expire the brokenness of + // |alternative_service| + EXPECT_EQ(1u, GetPendingMainThreadTaskCount()); + EXPECT_TRUE(impl_.IsAlternativeServiceBroken(alternative_service, + NetworkIsolationKey())); + + // Advance time by just enough so that |alternative_service|'s brokenness + // expires. + if (i == 0) { + FastForwardBy(initial_delay); + } else { + FastForwardBy(base::Seconds(300) * (1 << (i - 1))); + } + + // Ensure brokenness of |alternative_service| has expired. + EXPECT_EQ(0u, GetPendingMainThreadTaskCount()); + EXPECT_FALSE(impl_.IsAlternativeServiceBroken(alternative_service, + NetworkIsolationKey())); + } +} + // Regression test for https://crbug.com/724302 TEST_F(AlternateProtocolServerPropertiesTest, RemoveExpiredBrokenAltSvc2) { // This test will mark an alternative service A that has already been marked diff --git a/chromium/net/http/http_stream.h b/chromium/net/http/http_stream.h index 668b1fff169..04d1507f8ce 100644 --- a/chromium/net/http/http_stream.h +++ b/chromium/net/http/http_stream.h @@ -48,15 +48,21 @@ class NET_EXPORT_PRIVATE HttpStream { virtual ~HttpStream() {} - // Initialize stream. Must be called before calling SendRequest(). - // The consumer should ensure that request_info points to a valid value till - // final response headers are received; after that point, the HttpStream - // will not access |*request_info| and it may be deleted. If |can_send_early| - // is true, this stream may send data early without confirming the handshake - // if this is a resumption of a previously established connection. - // Returns a net error code, possibly ERR_IO_PENDING. - virtual int InitializeStream(const HttpRequestInfo* request_info, - bool can_send_early, + // Registers the HTTP request for the stream. Must be called before calling + // InitializeStream(). Separating the registration of the request from the + // initialization of the stream allows the connection callback to run prior + // to stream initialization. + // + // The consumer should ensure that request_info points to a valid non-null + // value till final response headers are received; after that point, the + // HttpStream will not access |*request_info| and it may be deleted. + virtual void RegisterRequest(const HttpRequestInfo* request_info) = 0; + + // Initializes the stream. Must be called before calling SendRequest(). + // If |can_send_early| is true, this stream may send data early without + // confirming the handshake if this is a resumption of a previously + // established connection. Returns a net error code, possibly ERR_IO_PENDING. + virtual int InitializeStream(bool can_send_early, RequestPriority priority, const NetLogWithSource& net_log, CompletionOnceCallback callback) = 0; diff --git a/chromium/net/http/http_stream_factory.cc b/chromium/net/http/http_stream_factory.cc index cd4437ebdb5..7812973922f 100644 --- a/chromium/net/http/http_stream_factory.cc +++ b/chromium/net/http/http_stream_factory.cc @@ -29,9 +29,9 @@ #include "net/quic/quic_http_utils.h" #include "net/spdy/bidirectional_stream_spdy_impl.h" #include "net/spdy/spdy_http_stream.h" -#include "net/third_party/quiche/src/quic/core/quic_packets.h" -#include "net/third_party/quiche/src/quic/core/quic_server_id.h" -#include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_packets.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_alt_svc_wire_format.h" #include "url/gurl.h" #include "url/scheme_host_port.h" #include "url/url_constants.h" diff --git a/chromium/net/http/http_stream_factory_job.cc b/chromium/net/http/http_stream_factory_job.cc index a01b633bec3..b8583f068a9 100644 --- a/chromium/net/http/http_stream_factory_job.cc +++ b/chromium/net/http/http_stream_factory_job.cc @@ -10,6 +10,7 @@ #include <utility> #include "base/bind.h" +#include "base/callback.h" #include "base/callback_helpers.h" #include "base/check_op.h" #include "base/containers/contains.h" @@ -55,7 +56,7 @@ #include "net/spdy/spdy_http_stream.h" #include "net/spdy/spdy_session.h" #include "net/ssl/ssl_cert_request_info.h" -#include "net/third_party/quiche/src/spdy/core/spdy_protocol.h" +#include "net/third_party/quiche/src/quiche/spdy/core/spdy_protocol.h" #include "url/scheme_host_port.h" #include "url/url_constants.h" diff --git a/chromium/net/http/http_stream_factory_job_controller.cc b/chromium/net/http/http_stream_factory_job_controller.cc index dbd06029d26..98d2dfdc8f5 100644 --- a/chromium/net/http/http_stream_factory_job_controller.cc +++ b/chromium/net/http/http_stream_factory_job_controller.cc @@ -58,12 +58,10 @@ GURL CreateAltSvcUrl(const GURL& origin_url, DCHECK(origin_url.is_valid()); DCHECK(origin_url.IsStandard()); - url::Replacements<char> replacements; + GURL::Replacements replacements; std::string port_str = base::NumberToString(alternative_destination.port()); - replacements.SetPort(port_str.c_str(), url::Component(0, port_str.size())); - replacements.SetHost( - alternative_destination.host().c_str(), - url::Component(0, alternative_destination.host().size())); + replacements.SetPortStr(port_str); + replacements.SetHostStr(alternative_destination.host()); return origin_url.ReplaceComponents(replacements); } diff --git a/chromium/net/http/http_stream_factory_job_controller.h b/chromium/net/http/http_stream_factory_job_controller.h index 030b4684c83..5b5cb2ad651 100644 --- a/chromium/net/http/http_stream_factory_job_controller.h +++ b/chromium/net/http/http_stream_factory_job_controller.h @@ -10,6 +10,7 @@ #include "base/cancelable_callback.h" #include "base/memory/raw_ptr.h" +#include "base/time/time.h" #include "net/base/host_port_pair.h" #include "net/base/privacy_mode.h" #include "net/http/http_stream_factory_job.h" diff --git a/chromium/net/http/http_stream_factory_job_controller_unittest.cc b/chromium/net/http/http_stream_factory_job_controller_unittest.cc index dfc8a8db3af..cd013d32fa1 100644 --- a/chromium/net/http/http_stream_factory_job_controller_unittest.cc +++ b/chromium/net/http/http_stream_factory_job_controller_unittest.cc @@ -32,6 +32,7 @@ #include "net/dns/public/secure_dns_policy.h" #include "net/http/http_basic_stream.h" #include "net/http/http_network_session_peer.h" +#include "net/http/http_response_headers.h" #include "net/http/http_server_properties.h" #include "net/http/http_server_properties_manager.h" #include "net/http/http_stream_factory.h" @@ -55,7 +56,7 @@ #include "net/spdy/spdy_session_key.h" #include "net/spdy/spdy_test_util_common.h" #include "net/test/test_with_task_environment.h" -#include "net/third_party/quiche/src/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/chromium/net/http/http_stream_factory_unittest.cc b/chromium/net/http/http_stream_factory_unittest.cc index a3d9af1983a..7145365d4ac 100644 --- a/chromium/net/http/http_stream_factory_unittest.cc +++ b/chromium/net/http/http_stream_factory_unittest.cc @@ -15,7 +15,6 @@ #include "base/compiler_specific.h" #include "base/containers/contains.h" -#include "base/cxx17_backports.h" #include "base/memory/ptr_util.h" #include "base/no_destructor.h" #include "base/run_loop.h" @@ -77,12 +76,16 @@ #include "net/test/gtest_util.h" #include "net/test/test_data_directory.h" #include "net/test/test_with_task_environment.h" -#include "net/third_party/quiche/src/quic/core/quic_server_id.h" -#include "net/third_party/quiche/src/quic/core/quic_utils.h" -#include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" -#include "net/third_party/quiche/src/quic/test_tools/mock_random.h" -#include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_server_id.h" +#include "net/third_party/quiche/src/quiche/quic/core/quic_utils.h" +#include "net/third_party/quiche/src/quiche/quic/test_tools/crypto_test_utils.h" +#include "net/third_party/quiche/src/quiche/quic/test_tools/mock_random.h" +#include "net/third_party/quiche/src/quiche/quic/test_tools/quic_test_utils.h" #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" +// This file can be included from net/http even though +// it is in net/websockets because it doesn't +// introduce any link dependency to net/websockets. +#include "net/websockets/websocket_handshake_stream_base.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -90,11 +93,6 @@ #include "url/scheme_host_port.h" #include "url/url_constants.h" -// This file can be included from net/http even though -// it is in net/websockets because it doesn't -// introduce any link dependency to net/websockets. -#include "net/websockets/websocket_handshake_stream_base.h" - using ::testing::Contains; using ::testing::ElementsAre; using ::testing::IsEmpty; @@ -129,8 +127,8 @@ class MockWebSocketHandshakeStream : public WebSocketHandshakeStreamBase { StreamType type() const { return type_; } // HttpStream methods - int InitializeStream(const HttpRequestInfo* request_info, - bool can_send_early, + void RegisterRequest(const HttpRequestInfo* request_info) override {} + int InitializeStream(bool can_send_early, RequestPriority priority, const NetLogWithSource& net_log, CompletionOnceCallback callback) override { @@ -505,7 +503,7 @@ class CapturePreconnectsTransportSocketPool : public TransportClientSocketPool { using HttpStreamFactoryTest = TestWithTaskEnvironment; TEST_F(HttpStreamFactoryTest, PreconnectDirect) { - for (size_t i = 0; i < base::size(kTests); ++i) { + for (size_t i = 0; i < std::size(kTests); ++i) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); std::unique_ptr<HttpNetworkSession> session( @@ -530,7 +528,7 @@ TEST_F(HttpStreamFactoryTest, PreconnectDirect) { } TEST_F(HttpStreamFactoryTest, PreconnectHttpProxy) { - for (size_t i = 0; i < base::size(kTests); ++i) { + for (size_t i = 0; i < std::size(kTests); ++i) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixed( "http_proxy", TRAFFIC_ANNOTATION_FOR_TESTS)); @@ -554,7 +552,7 @@ TEST_F(HttpStreamFactoryTest, PreconnectHttpProxy) { } TEST_F(HttpStreamFactoryTest, PreconnectSocksProxy) { - for (size_t i = 0; i < base::size(kTests); ++i) { + for (size_t i = 0; i < std::size(kTests); ++i) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateFixed( "socks4://socks_proxy:1080", TRAFFIC_ANNOTATION_FOR_TESTS)); @@ -578,7 +576,7 @@ TEST_F(HttpStreamFactoryTest, PreconnectSocksProxy) { } TEST_F(HttpStreamFactoryTest, PreconnectDirectWithExistingSpdySession) { - for (size_t i = 0; i < base::size(kTests); ++i) { + for (size_t i = 0; i < std::size(kTests); ++i) { SpdySessionDependencies session_deps( ConfiguredProxyResolutionService::CreateDirect()); std::unique_ptr<HttpNetworkSession> session( @@ -2753,10 +2751,10 @@ TEST_F(HttpStreamFactoryTest, ChangeSocketTag) { // Verify attempting to use the first stream fails because the session's // socket tag has since changed. TestCompletionCallback callback1; - EXPECT_EQ(ERR_FAILED, - waiter1.stream()->InitializeStream( - &request_info1, /* can_send_early = */ false, DEFAULT_PRIORITY, - NetLogWithSource(), callback1.callback())); + waiter1.stream()->RegisterRequest(&request_info1); + EXPECT_EQ(ERR_FAILED, waiter1.stream()->InitializeStream( + /* can_send_early = */ false, DEFAULT_PRIORITY, + NetLogWithSource(), callback1.callback())); // Verify the socket tag can be changed, this time using an IP alias // (different host, same IP). @@ -2787,10 +2785,10 @@ TEST_F(HttpStreamFactoryTest, ChangeSocketTag) { // Initialize the third stream, thus marking the session active, so it cannot // have its socket tag changed. TestCompletionCallback callback3; - EXPECT_EQ(OK, - waiter3.stream()->InitializeStream( - &request_info3, /* can_send_early = */ false, DEFAULT_PRIORITY, - NetLogWithSource(), callback3.callback())); + waiter3.stream()->RegisterRequest(&request_info3); + EXPECT_EQ(OK, waiter3.stream()->InitializeStream( + /* can_send_early = */ false, DEFAULT_PRIORITY, + NetLogWithSource(), callback3.callback())); // Verify a new session is created when a request with a different tag is // started. @@ -2910,10 +2908,10 @@ TEST_F(HttpStreamFactoryTest, ChangeSocketTagAvoidOverwrite) { // Initialize the first stream, thus marking the session active, so it cannot // have its socket tag changed and be reused for the second session. TestCompletionCallback callback1; - EXPECT_EQ(OK, - waiter1.stream()->InitializeStream( - &request_info1, /* can_send_early = */ false, DEFAULT_PRIORITY, - NetLogWithSource(), callback1.callback())); + waiter1.stream()->RegisterRequest(&request_info1); + EXPECT_EQ(OK, waiter1.stream()->InitializeStream( + /* can_send_early = */ false, DEFAULT_PRIORITY, + NetLogWithSource(), callback1.callback())); // Create a second stream with a new tag. StreamRequestWaiter waiter2; @@ -2946,10 +2944,10 @@ TEST_F(HttpStreamFactoryTest, ChangeSocketTagAvoidOverwrite) { // Initialize the second stream, thus marking the session active, so it cannot // have its socket tag changed and be reused for the third session. TestCompletionCallback callback2; - EXPECT_EQ(OK, - waiter2.stream()->InitializeStream( - &request_info2, /* can_send_early = */ false, DEFAULT_PRIORITY, - NetLogWithSource(), callback2.callback())); + waiter2.stream()->RegisterRequest(&request_info2); + EXPECT_EQ(OK, waiter2.stream()->InitializeStream( + /* can_send_early = */ false, DEFAULT_PRIORITY, + NetLogWithSource(), callback2.callback())); // Release first stream so first session can be retagged for third request. waiter1.stream()->Close(/* not_reusable = */ true); diff --git a/chromium/net/http/http_stream_parser.cc b/chromium/net/http/http_stream_parser.cc index aaa22ac7001..0637a502b9a 100644 --- a/chromium/net/http/http_stream_parser.cc +++ b/chromium/net/http/http_stream_parser.cc @@ -52,24 +52,6 @@ std::string GetResponseHeaderLines(const HttpResponseHeaders& headers) { return cr_separated_headers; } -// Return true if |headers| contain multiple |field_name| fields with different -// values. -bool HeadersContainMultipleCopiesOfField(const HttpResponseHeaders& headers, - const std::string& field_name) { - size_t it = 0; - std::string field_value; - if (!headers.EnumerateHeader(&it, field_name, &field_value)) - return false; - // There's at least one |field_name| header. Check if there are any more - // such headers, and if so, return true if they have different values. - std::string field_value2; - while (headers.EnumerateHeader(&it, field_name, &field_value2)) { - if (field_value != field_value2) - return true; - } - return false; -} - base::Value NetLogSendRequestBodyParams(uint64_t length, bool is_chunked, bool did_merge) { @@ -1049,15 +1031,17 @@ int HttpStreamParser::ParseResponseHeaders(int end_offset) { // chunked-encoded. If they exist, and have distinct values, it's a potential // response smuggling attack. if (!headers->IsChunkEncoded()) { - if (HeadersContainMultipleCopiesOfField(*headers, "Content-Length")) + if (HttpUtil::HeadersContainMultipleCopiesOfField(*headers, + "Content-Length")) return ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH; } // Check for multiple Content-Disposition or Location headers. If they exist, // it's also a potential response smuggling attack. - if (HeadersContainMultipleCopiesOfField(*headers, "Content-Disposition")) + if (HttpUtil::HeadersContainMultipleCopiesOfField(*headers, + "Content-Disposition")) return ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION; - if (HeadersContainMultipleCopiesOfField(*headers, "Location")) + if (HttpUtil::HeadersContainMultipleCopiesOfField(*headers, "Location")) return ERR_RESPONSE_HEADERS_MULTIPLE_LOCATION; response_->headers = headers; diff --git a/chromium/net/http/http_stream_parser.h b/chromium/net/http/http_stream_parser.h index 0c0619bdcf4..16b5c49983b 100644 --- a/chromium/net/http/http_stream_parser.h +++ b/chromium/net/http/http_stream_parser.h @@ -15,6 +15,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_piece.h" +#include "base/time/time.h" #include "crypto/ec_private_key.h" #include "net/base/completion_once_callback.h" #include "net/base/completion_repeating_callback.h" diff --git a/chromium/net/http/http_stream_parser_unittest.cc b/chromium/net/http/http_stream_parser_unittest.cc index e25b78539df..62373540a51 100644 --- a/chromium/net/http/http_stream_parser_unittest.cc +++ b/chromium/net/http/http_stream_parser_unittest.cc @@ -13,7 +13,6 @@ #include <vector> #include "base/bind.h" -#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" @@ -273,7 +272,7 @@ TEST(HttpStreamParser, InitAsynchronousUploadDataStream) { callback1.callback()); EXPECT_EQ(ERR_IO_PENDING, result1); base::RunLoop().RunUntilIdle(); - upload_data_stream.AppendData(kChunk, base::size(kChunk) - 1, true); + upload_data_stream.AppendData(kChunk, std::size(kChunk) - 1, true); // Check progress after read completes. progress = upload_data_stream.GetUploadProgress(); @@ -644,11 +643,11 @@ TEST(HttpStreamParser, SentBytesChunkedPostError) { &response, callback.callback())); base::RunLoop().RunUntilIdle(); - upload_data_stream.AppendData(kChunk, base::size(kChunk) - 1, false); + upload_data_stream.AppendData(kChunk, std::size(kChunk) - 1, false); base::RunLoop().RunUntilIdle(); // This write should fail. - upload_data_stream.AppendData(kChunk, base::size(kChunk) - 1, false); + upload_data_stream.AppendData(kChunk, std::size(kChunk) - 1, false); EXPECT_THAT(callback.WaitForResult(), IsError(ERR_FAILED)); EXPECT_EQ(CountWriteBytes(writes), parser.sent_bytes()); @@ -720,7 +719,7 @@ TEST(HttpStreamParser, AsyncSingleChunkAndAsyncSocket) { ASSERT_FALSE(callback.have_result()); // Now append the only chunk and wait for the callback. - upload_stream.AppendData(kChunk, base::size(kChunk) - 1, true); + upload_stream.AppendData(kChunk, std::size(kChunk) - 1, true); ASSERT_THAT(callback.WaitForResult(), IsOk()); // Attempt to read the response status and the response headers. @@ -772,7 +771,7 @@ TEST(HttpStreamParser, SyncSingleChunkAndAsyncSocket) { NetLogWithSource()), IsOk()); // Append the only chunk. - upload_stream.AppendData(kChunk, base::size(kChunk) - 1, true); + upload_stream.AppendData(kChunk, std::size(kChunk) - 1, true); SequencedSocketData data(reads, writes); std::unique_ptr<StreamSocket> stream_socket = CreateConnectedSocket(&data); @@ -853,7 +852,7 @@ TEST(HttpStreamParser, AsyncChunkAndAsyncSocketWithMultipleChunks) { }; ChunkedUploadDataStream upload_stream(0); - upload_stream.AppendData(kChunk1, base::size(kChunk1) - 1, false); + upload_stream.AppendData(kChunk1, std::size(kChunk1) - 1, false); ASSERT_THAT(upload_stream.Init(TestCompletionCallback().callback(), NetLogWithSource()), IsOk()); @@ -889,12 +888,12 @@ TEST(HttpStreamParser, AsyncChunkAndAsyncSocketWithMultipleChunks) { ASSERT_FALSE(callback.have_result()); // Now append another chunk. - upload_stream.AppendData(kChunk2, base::size(kChunk2) - 1, false); + upload_stream.AppendData(kChunk2, std::size(kChunk2) - 1, false); ASSERT_FALSE(callback.have_result()); // Add the final chunk, while the write for the second is still pending, // which should not confuse the state machine. - upload_stream.AppendData(kChunk3, base::size(kChunk3) - 1, true); + upload_stream.AppendData(kChunk3, std::size(kChunk3) - 1, true); ASSERT_FALSE(callback.have_result()); // Wait for writes to complete. @@ -1123,7 +1122,7 @@ TEST(HttpStreamParser, TruncatedHeaders) { for (size_t protocol = 0; protocol < NUM_PROTOCOLS; protocol++) { SCOPED_TRACE(protocol); - for (size_t i = 0; i < base::size(reads); i++) { + for (size_t i = 0; i < std::size(reads); i++) { SCOPED_TRACE(i); SequencedSocketData data(reads[i], writes); std::unique_ptr<StreamSocket> stream_socket(CreateConnectedSocket(&data)); @@ -1152,7 +1151,7 @@ TEST(HttpStreamParser, TruncatedHeaders) { int rv = parser.ReadResponseHeaders(callback.callback()); EXPECT_EQ(CountWriteBytes(writes), parser.sent_bytes()); - if (i == base::size(reads) - 1) { + if (i == std::size(reads) - 1) { EXPECT_THAT(rv, IsOk()); EXPECT_TRUE(response_info.headers.get()); EXPECT_EQ(CountReadBytes(reads[i]), parser.received_bytes()); @@ -1430,7 +1429,7 @@ TEST(HttpStreamParser, NullFails) { // Need to start at 4 because HttpStreamParser will treat the response as // HTTP/0.9 if it doesn't see "HTTP", and need to end at -1 because "\r\n\r" // is currently treated as a valid end of header marker. - for (size_t i = 4; i < base::size(kTestHeaders) - 1; ++i) { + for (size_t i = 4; i < std::size(kTestHeaders) - 1; ++i) { std::string read_data(kTestHeaders); read_data.insert(i, 1, '\0'); read_data.append("body"); diff --git a/chromium/net/http/http_transaction.h b/chromium/net/http/http_transaction.h index f575e1dc4c1..58e46c5123e 100644 --- a/chromium/net/http/http_transaction.h +++ b/chromium/net/http/http_transaction.h @@ -205,7 +205,7 @@ class NET_EXPORT_PRIVATE HttpTransaction { // Resumes the transaction after being deferred. virtual int ResumeNetworkStart() = 0; - virtual void GetConnectionAttempts(ConnectionAttempts* out) const = 0; + virtual ConnectionAttempts GetConnectionAttempts() const = 0; // Configures the transaction to close the network connection, if any, on // destruction. Intended for cases where keeping the socket alive may leak diff --git a/chromium/net/http/http_transaction_test_util.cc b/chromium/net/http/http_transaction_test_util.cc index 5c45e141607..6c750250382 100644 --- a/chromium/net/http/http_transaction_test_util.cc +++ b/chromium/net/http/http_transaction_test_util.cc @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/callback_helpers.h" -#include "base/cxx17_backports.h" #include "base/location.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" @@ -181,7 +180,7 @@ const MockTransaction* FindMockTransaction(const GURL& url) { return it->second; // look for builtins: - for (size_t i = 0; i < base::size(kBuiltinMockTransactions); ++i) { + for (size_t i = 0; i < std::size(kBuiltinMockTransactions); ++i) { if (url == GURL(kBuiltinMockTransactions[i]->url)) return kBuiltinMockTransactions[i]; } @@ -512,6 +511,7 @@ int MockNetworkTransaction::StartInternal(const HttpRequestInfo* request, response_.was_cached = false; response_.network_accessed = true; + response_.remote_endpoint = t->transport_info.endpoint; response_.response_time = transaction_factory_->Now(); if (!t->response_time.is_null()) @@ -575,9 +575,9 @@ int MockNetworkTransaction::ResumeNetworkStart() { return ERR_IO_PENDING; } -void MockNetworkTransaction::GetConnectionAttempts( - ConnectionAttempts* out) const { +ConnectionAttempts MockNetworkTransaction::GetConnectionAttempts() const { NOTIMPLEMENTED(); + return {}; } void MockNetworkTransaction::CloseConnectionOnDestruction() { diff --git a/chromium/net/http/http_transaction_test_util.h b/chromium/net/http/http_transaction_test_util.h index c68a375b70e..18e015452d9 100644 --- a/chromium/net/http/http_transaction_test_util.h +++ b/chromium/net/http/http_transaction_test_util.h @@ -18,6 +18,7 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_refptr.h" #include "base/memory/weak_ptr.h" +#include "base/time/time.h" #include "net/base/completion_once_callback.h" #include "net/base/io_buffer.h" #include "net/base/load_flags.h" @@ -250,7 +251,7 @@ class MockNetworkTransaction int ResumeNetworkStart() override; - void GetConnectionAttempts(ConnectionAttempts* out) const override; + ConnectionAttempts GetConnectionAttempts() const override; void CloseConnectionOnDestruction() override; diff --git a/chromium/net/http/http_util.cc b/chromium/net/http/http_util.cc index 32948a3b469..0978147da37 100644 --- a/chromium/net/http/http_util.cc +++ b/chromium/net/http/http_util.cc @@ -23,6 +23,7 @@ #include "net/base/mime_util.h" #include "net/base/parse_number.h" #include "net/base/url_util.h" +#include "net/http/http_response_headers.h" namespace net { @@ -1122,4 +1123,21 @@ bool HttpUtil::ParseContentEncoding(const std::string& content_encoding, return true; } +bool HttpUtil::HeadersContainMultipleCopiesOfField( + const HttpResponseHeaders& headers, + const std::string& field_name) { + size_t it = 0; + std::string field_value; + if (!headers.EnumerateHeader(&it, field_name, &field_value)) + return false; + // There's at least one `field_name` header. Check if there are any more + // such headers, and if so, return true if they have different values. + std::string field_value2; + while (headers.EnumerateHeader(&it, field_name, &field_value2)) { + if (field_value != field_value2) + return true; + } + return false; +} + } // namespace net diff --git a/chromium/net/http/http_util.h b/chromium/net/http/http_util.h index 7494ae8a8b8..65f64279020 100644 --- a/chromium/net/http/http_util.h +++ b/chromium/net/http/http_util.h @@ -29,6 +29,8 @@ namespace net { +class HttpResponseHeaders; + class NET_EXPORT HttpUtil { public: // Returns the absolute URL, to be used for the http request. This url is @@ -260,6 +262,12 @@ class NET_EXPORT HttpUtil { static bool ParseContentEncoding(const std::string& content_encoding, std::set<std::string>* used_encodings); + // Return true if `headers` contain multiple `field_name` fields with + // different values. + static bool HeadersContainMultipleCopiesOfField( + const HttpResponseHeaders& headers, + const std::string& field_name); + // Used to iterate over the name/value pairs of HTTP headers. To iterate // over the values in a multi-value header, use ValuesIterator. // See AssembleRawHeaders for joining line continuations (this iterator diff --git a/chromium/net/http/http_util_unittest.cc b/chromium/net/http/http_util_unittest.cc index 7ba495f988c..5072a7cbfee 100644 --- a/chromium/net/http/http_util_unittest.cc +++ b/chromium/net/http/http_util_unittest.cc @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/http/http_util.h" + #include <algorithm> #include <limits> -#include "base/cxx17_backports.h" #include "base/strings/string_util.h" -#include "net/http/http_util.h" +#include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -44,7 +45,7 @@ TEST(HttpUtilTest, IsSafeHeader) { "user-agent", "via", }; - for (size_t i = 0; i < base::size(unsafe_headers); ++i) { + for (size_t i = 0; i < std::size(unsafe_headers); ++i) { EXPECT_FALSE(HttpUtil::IsSafeHeader(unsafe_headers[i])) << unsafe_headers[i]; EXPECT_FALSE(HttpUtil::IsSafeHeader(base::ToUpperASCII(unsafe_headers[i]))) @@ -91,7 +92,7 @@ TEST(HttpUtilTest, IsSafeHeader) { "user_agent", "viaa", }; - for (size_t i = 0; i < base::size(safe_headers); ++i) { + for (size_t i = 0; i < std::size(safe_headers); ++i) { EXPECT_TRUE(HttpUtil::IsSafeHeader(safe_headers[i])) << safe_headers[i]; EXPECT_TRUE(HttpUtil::IsSafeHeader(base::ToUpperASCII(safe_headers[i]))) << safe_headers[i]; @@ -338,7 +339,7 @@ TEST(HttpUtilTest, LocateEndOfHeaders) { {"foo\nbar\n\r\njunk", 10}, {"foo\nbar\r\n\njunk", 10}, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { size_t input_len = strlen(tests[i].input); size_t eoh = HttpUtil::LocateEndOfHeaders(tests[i].input, input_len); EXPECT_EQ(tests[i].expected_result, eoh); @@ -362,7 +363,7 @@ TEST(HttpUtilTest, LocateEndOfAdditionalHeaders) { {"foo\nbar\n\r\njunk", 10}, {"foo\nbar\r\n\njunk", 10}, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { size_t input_len = strlen(tests[i].input); size_t eoh = HttpUtil::LocateEndOfAdditionalHeaders(tests[i].input, input_len); @@ -685,7 +686,7 @@ TEST(HttpUtilTest, AssembleRawHeaders) { }, }; // clang-format on - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { std::string input = tests[i].input; std::replace(input.begin(), input.end(), '|', '\0'); std::string raw = HttpUtil::AssembleRawHeaders(input); @@ -725,7 +726,7 @@ TEST(HttpUtilTest, RequestUrlSanitize) { "wss://www.google.com:78/foobar?query=1", } }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { SCOPED_TRACE(i); GURL url(GURL(tests[i].url)); @@ -989,7 +990,7 @@ TEST(HttpUtilTest, ParseContentType) { // TODO(abarth): Add more interesting test cases. }; // clang-format on - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { std::string mime_type; std::string charset; bool had_charset = false; @@ -1097,7 +1098,7 @@ TEST(HttpUtilTest, ParseRetryAfterHeader) { {"Thu, 1 Jan 2015 12:34:56 GMT", true, later - now}, {"Mon, 1 Jan 1900 12:34:56 GMT", false, base::TimeDelta()}}; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { base::TimeDelta retry_after; bool return_value = HttpUtil::ParseRetryAfterHeader( tests[i].retry_after_string, now, &retry_after); @@ -1593,7 +1594,7 @@ TEST(HttpUtilTest, ParseAcceptEncoding) { {"foo,\"bar\"", "INVALID"}, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { std::string value(tests[i].value); std::string reformatted; std::set<std::string> allowed_encodings; @@ -1623,7 +1624,7 @@ TEST(HttpUtilTest, ParseContentEncoding) { {"foo,\"bar\"", "INVALID"}, }; - for (size_t i = 0; i < base::size(tests); ++i) { + for (size_t i = 0; i < std::size(tests); ++i) { std::string value(tests[i].value); std::string reformatted; std::set<std::string> used_encodings; diff --git a/chromium/net/http/http_vary_data_unittest.cc b/chromium/net/http/http_vary_data_unittest.cc index 491454b9361..0b11adf91f6 100644 --- a/chromium/net/http/http_vary_data_unittest.cc +++ b/chromium/net/http/http_vary_data_unittest.cc @@ -2,12 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/http/http_vary_data.h" + #include <algorithm> -#include "base/cxx17_backports.h" #include "net/http/http_request_info.h" #include "net/http/http_response_headers.h" -#include "net/http/http_vary_data.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -15,19 +15,21 @@ namespace net { namespace { typedef testing::Test HttpVaryDataTest; +using ExtraHeaders = std::vector<std::pair<std::string, std::string>>; struct TestTransaction { HttpRequestInfo request; scoped_refptr<HttpResponseHeaders> response; - void Init(const std::string& request_headers, + void Init(const ExtraHeaders& request_headers, const std::string& response_headers) { std::string temp(response_headers); std::replace(temp.begin(), temp.end(), '\n', '\0'); response = new HttpResponseHeaders(temp); request.extra_headers.Clear(); - request.extra_headers.AddHeadersFromString(request_headers); + for (const auto& [key, value] : request_headers) + request.extra_headers.SetHeader(key, value); } }; @@ -44,9 +46,9 @@ TEST(HttpVaryDataTest, IsInvalid) { const bool kExpectedValid[] = {false, true, true, true}; - for (size_t i = 0; i < base::size(kTestResponses); ++i) { + for (size_t i = 0; i < std::size(kTestResponses); ++i) { TestTransaction t; - t.Init(std::string(), kTestResponses[i]); + t.Init(/*request_headers=*/{}, kTestResponses[i]); HttpVaryData v; EXPECT_FALSE(v.is_valid()); @@ -60,23 +62,23 @@ TEST(HttpVaryDataTest, MultipleInit) { // Init to something valid. TestTransaction t1; - t1.Init("Foo: 1\r\nbar: 23", "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); + t1.Init({{"Foo", "1"}, {"bar", "23"}}, "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); EXPECT_TRUE(v.Init(t1.request, *t1.response.get())); EXPECT_TRUE(v.is_valid()); // Now overwrite by initializing to something invalid. TestTransaction t2; - t2.Init("Foo: 1\r\nbar: 23", "HTTP/1.1 200 OK\n\n"); + t2.Init({{"Foo", "1"}, {"bar", "23"}}, "HTTP/1.1 200 OK\n\n"); EXPECT_FALSE(v.Init(t2.request, *t2.response.get())); EXPECT_FALSE(v.is_valid()); } TEST(HttpVaryDataTest, DoesVary) { TestTransaction a; - a.Init("Foo: 1", "HTTP/1.1 200 OK\nVary: foo\n\n"); + a.Init({{"Foo", "1"}}, "HTTP/1.1 200 OK\nVary: foo\n\n"); TestTransaction b; - b.Init("Foo: 2", "HTTP/1.1 200 OK\nVary: foo\n\n"); + b.Init({{"Foo", "2"}}, "HTTP/1.1 200 OK\nVary: foo\n\n"); HttpVaryData v; EXPECT_TRUE(v.Init(a.request, *a.response.get())); @@ -86,10 +88,10 @@ TEST(HttpVaryDataTest, DoesVary) { TEST(HttpVaryDataTest, DoesVary2) { TestTransaction a; - a.Init("Foo: 1\r\nbar: 23", "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); + a.Init({{"Foo", "1"}, {"bar", "23"}}, "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); TestTransaction b; - b.Init("Foo: 12\r\nbar: 3", "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); + b.Init({{"Foo", "12"}, {"bar", "3"}}, "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); HttpVaryData v; EXPECT_TRUE(v.Init(a.request, *a.response.get())); @@ -99,7 +101,7 @@ TEST(HttpVaryDataTest, DoesVary2) { TEST(HttpVaryDataTest, DoesVaryStar) { // Vary: * varies even when headers are identical - const char kRequestHeaders[] = "Foo:1"; + const ExtraHeaders kRequestHeaders = {{"Foo", "1"}}; const char kResponse[] = "HTTP/1.1 200 OK\nVary: *\n\n"; TestTransaction a; @@ -116,10 +118,10 @@ TEST(HttpVaryDataTest, DoesVaryStar) { TEST(HttpVaryDataTest, DoesntVary) { TestTransaction a; - a.Init("Foo: 1", "HTTP/1.1 200 OK\nVary: foo\n\n"); + a.Init({{"Foo", "1"}}, "HTTP/1.1 200 OK\nVary: foo\n\n"); TestTransaction b; - b.Init("Foo: 1", "HTTP/1.1 200 OK\nVary: foo\n\n"); + b.Init({{"Foo", "1"}}, "HTTP/1.1 200 OK\nVary: foo\n\n"); HttpVaryData v; EXPECT_TRUE(v.Init(a.request, *a.response.get())); @@ -129,10 +131,11 @@ TEST(HttpVaryDataTest, DoesntVary) { TEST(HttpVaryDataTest, DoesntVary2) { TestTransaction a; - a.Init("Foo: 1\r\nbAr: 2", "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); + a.Init({{"Foo", "1"}, {"bAr", "2"}}, "HTTP/1.1 200 OK\nVary: foo, bar\n\n"); TestTransaction b; - b.Init("Foo: 1\r\nbaR: 2", "HTTP/1.1 200 OK\nVary: foo\nVary: bar\n\n"); + b.Init({{"Foo", "1"}, {"baR", "2"}}, + "HTTP/1.1 200 OK\nVary: foo\nVary: bar\n\n"); HttpVaryData v; EXPECT_TRUE(v.Init(a.request, *a.response.get())); @@ -142,7 +145,7 @@ TEST(HttpVaryDataTest, DoesntVary2) { TEST(HttpVaryDataTest, DoesntVaryByCookieForRedirect) { TestTransaction a; - a.Init("Cookie: 1", "HTTP/1.1 301 Moved\nLocation: x\n\n"); + a.Init({{"Cookie", "1"}}, "HTTP/1.1 301 Moved\nLocation: x\n\n"); HttpVaryData v; EXPECT_FALSE(v.Init(a.request, *a.response.get())); diff --git a/chromium/net/http/mock_http_cache.cc b/chromium/net/http/mock_http_cache.cc index 1ceb20fdc76..7a4eea72f62 100644 --- a/chromium/net/http/mock_http_cache.cc +++ b/chromium/net/http/mock_http_cache.cc @@ -10,6 +10,7 @@ #include <utility> #include "base/bind.h" +#include "base/callback.h" #include "base/callback_helpers.h" #include "base/feature_list.h" #include "base/location.h" diff --git a/chromium/net/http/mock_sspi_library_win.cc b/chromium/net/http/mock_sspi_library_win.cc index c5b5fb025ef..a6cb1b7087f 100644 --- a/chromium/net/http/mock_sspi_library_win.cc +++ b/chromium/net/http/mock_sspi_library_win.cc @@ -11,6 +11,7 @@ #include "base/check_op.h" #include "base/memory/raw_ptr.h" +#include "base/strings/string_util_win.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" diff --git a/chromium/net/http/structured_headers.cc b/chromium/net/http/structured_headers.cc index 21bf17861c1..58953d7ee4a 100644 --- a/chromium/net/http/structured_headers.cc +++ b/chromium/net/http/structured_headers.cc @@ -28,19 +28,19 @@ namespace { #define TCHAR DIGIT LCALPHA UCALPHA "!#$%&'*+-.^_`|~" // https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09#section-3.9 constexpr char kTokenChars09[] = DIGIT UCALPHA LCALPHA "_-.:%*/"; -// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15#section-3.3.4 -constexpr char kTokenChars15[] = TCHAR ":/"; +// https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3.4 +constexpr char kTokenChars[] = TCHAR ":/"; // https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09#section-3.1 constexpr char kKeyChars09[] = DIGIT LCALPHA "_-"; -// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15#section-3.1.2 -constexpr char kKeyChars15[] = DIGIT LCALPHA "_-.*"; +// https://www.rfc-editor.org/rfc/rfc8941.html#section-3.1.2 +constexpr char kKeyChars[] = DIGIT LCALPHA "_-.*"; constexpr char kSP[] = " "; constexpr char kOWS[] = " \t"; #undef DIGIT #undef LCALPHA #undef UCALPHA -// https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15#section-3.3.1 +// https://www.rfc-editor.org/rfc/rfc8941.html#section-3.3.1 constexpr int64_t kMaxInteger = 999'999'999'999'999L; constexpr int64_t kMinInteger = -999'999'999'999'999L; @@ -51,21 +51,22 @@ constexpr int64_t kMinInteger = -999'999'999'999'999L; constexpr double kTooLargeDecimal = 1e12 - 0.0005; // Parser for (a subset of) Structured Headers for HTTP defined in [SH09] and -// [SH15]. [SH09] compatibility is retained for use by Web Packaging, and can be -// removed once that spec is updated, and users have migrated to new headers. +// [RFC8941]. [SH09] compatibility is retained for use by Web Packaging, and can +// be removed once that spec is updated, and users have migrated to new headers. // [SH09] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-09 -// [SH15] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15 +// [RFC8941] https://www.rfc-editor.org/rfc/rfc8941.html class StructuredHeaderParser { public: enum DraftVersion { kDraft09, - kDraft15, + kFinal, }; explicit StructuredHeaderParser(base::StringPiece str, DraftVersion version) : input_(str), version_(version) { // [SH09] 4.2 Step 1. - // [SH15] 4.2 Step 2. // Discard any leading OWS from input_string. + // [RFC8941] 4.2 Step 2. + // Discard any leading SP characters from input_string. SkipWhitespaces(); } StructuredHeaderParser(const StructuredHeaderParser&) = delete; @@ -74,10 +75,12 @@ class StructuredHeaderParser { // Callers should call this after ReadSomething(), to check if parser has // consumed all the input successfully. bool FinishParsing() { - // [SH09] 4.2 Step 7. [SH15] 4.2 Step 6. + // [SH09] 4.2 Step 7. // Discard any leading OWS from input_string. + // [RFC8941] 4.2 Step 6. + // Discard any leading SP characters from input_string. SkipWhitespaces(); - // [SH09] 4.2 Step 8. [SH15] 4.2 Step 7. + // [SH09] 4.2 Step 8. [RFC8941] 4.2 Step 7. // If input_string is not empty, fail parsing. return input_.empty(); } @@ -107,28 +110,28 @@ class StructuredHeaderParser { return result; } - // Parses a List ([SH15] 4.2.1). + // Parses a List ([RFC8941] 4.2.1). absl::optional<List> ReadList() { - DCHECK_EQ(version_, kDraft15); + DCHECK_EQ(version_, kFinal); List members; while (!input_.empty()) { absl::optional<ParameterizedMember> member(ReadItemOrInnerList()); if (!member) return absl::nullopt; members.push_back(std::move(*member)); - SkipWhitespaces(); + SkipOWS(); if (input_.empty()) break; if (!ConsumeChar(',')) return absl::nullopt; - SkipWhitespaces(); + SkipOWS(); if (input_.empty()) return absl::nullopt; } return members; } - // Parses an Item ([SH15] 4.2.3). + // Parses an Item ([RFC8941] 4.2.3). absl::optional<ParameterizedItem> ReadItem() { absl::optional<Item> item = ReadBareItem(); if (!item) @@ -139,8 +142,8 @@ class StructuredHeaderParser { return ParameterizedItem(std::move(*item), std::move(*parameters)); } - // Parses a bare Item ([SH15] 4.2.3.1, though this is also the algorithm for - // parsing an Item from [SH09] 4.2.7). + // Parses a bare Item ([RFC8941] 4.2.3.1, though this is also the algorithm + // for parsing an Item from [SH09] 4.2.7). absl::optional<Item> ReadBareItem() { if (input_.empty()) { DVLOG(1) << "ReadBareItem: unexpected EOF"; @@ -154,7 +157,7 @@ class StructuredHeaderParser { return ReadByteSequence(); return ReadToken(); case ':': - if (version_ == kDraft15) + if (version_ == kFinal) return ReadByteSequence(); return absl::nullopt; case '?': @@ -168,9 +171,9 @@ class StructuredHeaderParser { } } - // Parses a Dictionary ([SH15] 4.2.2). + // Parses a Dictionary ([RFC8941] 4.2.2). absl::optional<Dictionary> ReadDictionary() { - DCHECK_EQ(version_, kDraft15); + DCHECK_EQ(version_, kFinal); Dictionary members; while (!input_.empty()) { absl::optional<std::string> key(ReadKey()); @@ -189,12 +192,12 @@ class StructuredHeaderParser { member = ParameterizedMember{Item(true), std::move(*parameters)}; } members[*key] = std::move(*member); - SkipWhitespaces(); + SkipOWS(); if (input_.empty()) break; if (!ConsumeChar(',')) return absl::nullopt; - SkipWhitespaces(); + SkipOWS(); if (input_.empty()) return absl::nullopt; } @@ -254,9 +257,9 @@ class StructuredHeaderParser { std::move(parameters)); } - // Parses an Item or Inner List ([SH15] 4.2.1.1). + // Parses an Item or Inner List ([RFC8941] 4.2.1.1). absl::optional<ParameterizedMember> ReadItemOrInnerList() { - DCHECK_EQ(version_, kDraft15); + DCHECK_EQ(version_, kFinal); std::vector<Item> member; bool member_is_inner_list = (!input_.empty() && input_.front() == '('); if (member_is_inner_list) { @@ -270,7 +273,7 @@ class StructuredHeaderParser { } } - // Parses Parameters ([SH15] 4.2.3.2) + // Parses Parameters ([RFC8941] 4.2.3.2) absl::optional<Parameters> ReadParameters() { Parameters parameters; base::flat_set<std::string> keys; @@ -304,9 +307,9 @@ class StructuredHeaderParser { return parameters; } - // Parses an Inner List ([SH15] 4.2.1.2). + // Parses an Inner List ([RFC8941] 4.2.1.2). absl::optional<ParameterizedMember> ReadInnerList() { - DCHECK_EQ(version_, kDraft15); + DCHECK_EQ(version_, kFinal); if (!ConsumeChar('(')) return absl::nullopt; std::vector<ParameterizedItem> inner_list; @@ -331,7 +334,7 @@ class StructuredHeaderParser { return absl::nullopt; } - // Parses a Key ([SH09] 4.2.2, [SH15] 4.2.3.3). + // Parses a Key ([SH09] 4.2.2, [RFC8941] 4.2.3.3). absl::optional<std::string> ReadKey() { if (version_ == kDraft09) { if (input_.empty() || !base::IsAsciiLower(input_.front())) { @@ -346,7 +349,7 @@ class StructuredHeaderParser { } } const char* allowed_chars = - (version_ == kDraft09 ? kKeyChars09 : kKeyChars15); + (version_ == kDraft09 ? kKeyChars09 : kKeyChars); size_t len = input_.find_first_not_of(allowed_chars); if (len == base::StringPiece::npos) len = input_.size(); @@ -355,7 +358,7 @@ class StructuredHeaderParser { return key; } - // Parses a Token ([SH09] 4.2.10, [SH15] 4.2.6). + // Parses a Token ([SH09] 4.2.10, [RFC8941] 4.2.6). absl::optional<Item> ReadToken() { if (input_.empty() || !(base::IsAsciiAlpha(input_.front()) || input_.front() == '*')) { @@ -363,7 +366,7 @@ class StructuredHeaderParser { return absl::nullopt; } size_t len = input_.find_first_not_of(version_ == kDraft09 ? kTokenChars09 - : kTokenChars15); + : kTokenChars); if (len == base::StringPiece::npos) len = input_.size(); std::string token(input_.substr(0, len)); @@ -371,7 +374,7 @@ class StructuredHeaderParser { return Item(std::move(token), Item::kTokenType); } - // Parses a Number ([SH09] 4.2.8, [SH15] 4.2.4). + // Parses a Number ([SH09] 4.2.8, [RFC8941] 4.2.4). absl::optional<Item> ReadNumber() { bool is_negative = ConsumeChar('-'); bool is_decimal = false; @@ -391,21 +394,21 @@ class StructuredHeaderParser { return absl::nullopt; } if (!is_decimal) { - // [SH15] restricts the range of integers further. - if (version_ == kDraft15 && i > 15) { + // [RFC8941] restricts the range of integers further. + if (version_ == kFinal && i > 15) { LogParseError("ReadNumber", "integer too long"); return absl::nullopt; } } else { - if (version_ != kDraft15 && i > 16) { + if (version_ != kFinal && i > 16) { LogParseError("ReadNumber", "float too long"); return absl::nullopt; } - if (version_ == kDraft15 && decimal_position > 12) { + if (version_ == kFinal && decimal_position > 12) { LogParseError("ReadNumber", "decimal too long"); return absl::nullopt; } - if (i - decimal_position > (version_ == kDraft15 ? 4 : 7)) { + if (i - decimal_position > (version_ == kFinal ? 4 : 7)) { LogParseError("ReadNumber", "too many digits after decimal"); return absl::nullopt; } @@ -430,12 +433,12 @@ class StructuredHeaderParser { int64_t n; if (!base::StringToInt64(output_number_string, &n)) return absl::nullopt; - DCHECK(version_ != kDraft15 || (n <= kMaxInteger && n >= kMinInteger)); + DCHECK(version_ != kFinal || (n <= kMaxInteger && n >= kMinInteger)); return Item(is_negative ? -n : n); } } - // Parses a String ([SH09] 4.2.9, [SH15] 4.2.5). + // Parses a String ([SH09] 4.2.9, [RFC8941] 4.2.5). absl::optional<Item> ReadString() { std::string s; if (!ConsumeChar('"')) { @@ -474,7 +477,7 @@ class StructuredHeaderParser { return s; } - // Parses a Byte Sequence ([SH09] 4.2.11, [SH15] 4.2.7). + // Parses a Byte Sequence ([SH09] 4.2.11, [RFC8941] 4.2.7). absl::optional<Item> ReadByteSequence() { char delimiter = (version_ == kDraft09 ? '*' : ':'); if (!ConsumeChar(delimiter)) { @@ -500,7 +503,7 @@ class StructuredHeaderParser { return Item(std::move(binary), Item::kByteSequenceType); } - // Parses a Boolean ([SH15] 4.2.8). + // Parses a Boolean ([RFC8941] 4.2.8). // Note that this only parses ?0 and ?1 forms from SH version 10+, not the // previous ?F and ?T, which were not needed by any consumers of SH version 9. absl::optional<Item> ReadBoolean() { @@ -517,6 +520,9 @@ class StructuredHeaderParser { return absl::nullopt; } + // There are several points in the specs where the handling of whitespace + // differs between Draft 9 and the final RFC. In those cases, Draft 9 allows + // any OWS character, while the RFC allows only a U+0020 SPACE. void SkipWhitespaces() { if (version_ == kDraft09) { input_ = @@ -527,6 +533,11 @@ class StructuredHeaderParser { } } + void SkipOWS() { + input_ = + base::TrimString(input_, base::StringPiece(kOWS), base::TRIM_LEADING); + } + bool ConsumeChar(char expected) { if (!input_.empty() && input_.front() == expected) { input_.remove_prefix(1); @@ -545,8 +556,8 @@ class StructuredHeaderParser { DraftVersion version_; }; -// Serializer for (a subset of) Structured Headers for HTTP defined in [SH15]. -// [SH15] https://tools.ietf.org/html/draft-ietf-httpbis-header-structure-15 +// Serializer for (a subset of) Structured Field Values for HTTP defined in +// [RFC8941]. Note that this serializer does not attempt to support [SH09]. class StructuredHeaderSerializer { public: StructuredHeaderSerializer() = default; @@ -557,7 +568,7 @@ class StructuredHeaderSerializer { std::string Output() { return output_.str(); } - // Serializes a List ([SH15] 4.1.1). + // Serializes a List ([RFC8941] 4.1.1). bool WriteList(const List& value) { bool first = true; for (const auto& member : value) { @@ -570,17 +581,17 @@ class StructuredHeaderSerializer { return true; } - // Serializes an Item ([SH15] 4.1.3). + // Serializes an Item ([RFC8941] 4.1.3). bool WriteItem(const ParameterizedItem& value) { if (!WriteBareItem(value.item)) return false; return WriteParameters(value.params); } - // Serializes an Item ([SH15] 4.1.3). + // Serializes an Item ([RFC8941] 4.1.3). bool WriteBareItem(const Item& value) { if (value.is_string()) { - // Serializes a String ([SH15] 4.1.6). + // Serializes a String ([RFC8941] 4.1.6). output_ << "\""; for (const char& c : value.GetString()) { if (!base::IsAsciiPrintable(c)) @@ -593,19 +604,18 @@ class StructuredHeaderSerializer { return true; } if (value.is_token()) { - // Serializes a Token ([SH15] 4.1.7). + // Serializes a Token ([RFC8941] 4.1.7). if (!value.GetString().size() || !(base::IsAsciiAlpha(value.GetString().front()) || value.GetString().front() == '*')) return false; - if (value.GetString().find_first_not_of(kTokenChars15) != - std::string::npos) + if (value.GetString().find_first_not_of(kTokenChars) != std::string::npos) return false; output_ << value.GetString(); return true; } if (value.is_byte_sequence()) { - // Serializes a Byte Sequence ([SH15] 4.1.8). + // Serializes a Byte Sequence ([RFC8941] 4.1.8). output_ << ":"; output_ << base::Base64Encode( base::as_bytes(base::make_span(value.GetString()))); @@ -613,14 +623,14 @@ class StructuredHeaderSerializer { return true; } if (value.is_integer()) { - // Serializes an Integer ([SH15] 4.1.4). + // Serializes an Integer ([RFC8941] 4.1.4). if (value.GetInteger() > kMaxInteger || value.GetInteger() < kMinInteger) return false; output_ << value.GetInteger(); return true; } if (value.is_decimal()) { - // Serializes a Decimal ([SH15] 4.1.5). + // Serializes a Decimal ([RFC8941] 4.1.5). double decimal_value = value.GetDecimal(); if (!std::isfinite(decimal_value) || fabs(decimal_value) >= kTooLargeDecimal) @@ -650,7 +660,7 @@ class StructuredHeaderSerializer { // Maximum is 12 integer digits, one decimal point, three fractional // digits, and a null terminator. char buffer[17]; - base::snprintf(buffer, base::size(buffer), "%#.3f", decimal_value); + base::snprintf(buffer, std::size(buffer), "%#.3f", decimal_value); // Strip any trailing 0s after the decimal point, but leave at least one // digit after it in all cases. (So 1.230 becomes 1.23, but 1.000 becomes @@ -663,14 +673,14 @@ class StructuredHeaderSerializer { return true; } if (value.is_boolean()) { - // Serializes a Boolean ([SH15] 4.1.9). + // Serializes a Boolean ([RFC8941] 4.1.9). output_ << (value.GetBoolean() ? "?1" : "?0"); return true; } return false; } - // Serializes a Dictionary ([SH15] 4.1.2). + // Serializes a Dictionary ([RFC8941] 4.1.2). bool WriteDictionary(const Dictionary& value) { bool first = true; for (const auto& dict : value) { @@ -696,7 +706,7 @@ class StructuredHeaderSerializer { private: bool WriteParameterizedMember(const ParameterizedMember& value) { - // Serializes a parameterized member ([SH15] 4.1.1). + // Serializes a parameterized member ([RFC8941] 4.1.1). if (value.member_is_inner_list) { if (!WriteInnerList(value.member)) return false; @@ -709,7 +719,7 @@ class StructuredHeaderSerializer { } bool WriteInnerList(const std::vector<ParameterizedItem>& value) { - // Serializes an inner list ([SH15] 4.1.1.1). + // Serializes an inner list ([RFC8941] 4.1.1.1). output_ << "("; bool first = true; for (const ParameterizedItem& member : value) { @@ -724,7 +734,7 @@ class StructuredHeaderSerializer { } bool WriteParameters(const Parameters& value) { - // Serializes a parameter list ([SH15] 4.1.1.2). + // Serializes a parameter list ([RFC8941] 4.1.1.2). for (const auto& param_name_and_value : value) { const std::string& param_name = param_name_and_value.first; const Item& param_value = param_name_and_value.second; @@ -743,10 +753,10 @@ class StructuredHeaderSerializer { } bool WriteKey(const std::string& value) { - // Serializes a Key ([SH15] 4.1.1.3). + // Serializes a Key ([RFC8941] 4.1.1.3). if (!value.size()) return false; - if (value.find_first_not_of(kKeyChars15) != std::string::npos) + if (value.find_first_not_of(kKeyChars) != std::string::npos) return false; if (!base::IsAsciiLower(value[0]) && value[0] != '*') return false; @@ -894,7 +904,7 @@ bool Dictionary::contains(base::StringPiece key) const { } absl::optional<ParameterizedItem> ParseItem(base::StringPiece str) { - StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft15); + StructuredHeaderParser parser(str, StructuredHeaderParser::kFinal); absl::optional<ParameterizedItem> item = parser.ReadItem(); if (item && parser.FinishParsing()) return item; @@ -902,7 +912,7 @@ absl::optional<ParameterizedItem> ParseItem(base::StringPiece str) { } absl::optional<Item> ParseBareItem(base::StringPiece str) { - StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft15); + StructuredHeaderParser parser(str, StructuredHeaderParser::kFinal); absl::optional<Item> item = parser.ReadBareItem(); if (item && parser.FinishParsing()) return item; @@ -927,7 +937,7 @@ absl::optional<ListOfLists> ParseListOfLists(base::StringPiece str) { } absl::optional<List> ParseList(base::StringPiece str) { - StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft15); + StructuredHeaderParser parser(str, StructuredHeaderParser::kFinal); absl::optional<List> list = parser.ReadList(); if (list && parser.FinishParsing()) return list; @@ -935,7 +945,7 @@ absl::optional<List> ParseList(base::StringPiece str) { } absl::optional<Dictionary> ParseDictionary(const base::StringPiece& str) { - StructuredHeaderParser parser(str, StructuredHeaderParser::kDraft15); + StructuredHeaderParser parser(str, StructuredHeaderParser::kFinal); absl::optional<Dictionary> dictionary = parser.ReadDictionary(); if (dictionary && parser.FinishParsing()) return dictionary; diff --git a/chromium/net/http/structured_headers.h b/chromium/net/http/structured_headers.h index 5292f018e3b..262724694a9 100644 --- a/chromium/net/http/structured_headers.h +++ b/chromium/net/http/structured_headers.h @@ -19,14 +19,19 @@ namespace net { namespace structured_headers { // This file implements parsing of HTTP structured headers, as defined in -// https://httpwg.org/http-extensions/draft-ietf-httpbis-header-structure.html. +// RFC8941 (https://www.rfc-editor.org/rfc/rfc8941.html). For compatibility with +// the shipped implementation of Web Packaging, this file also supports a +// previous revision of the standard, referred to here as "Draft 9". +// (https://datatracker.ietf.org/doc/draft-ietf-httpbis-header-structure/09/) // -// Both drafts 9 and 15 are currently supported. The major difference -// between the two drafts is in the various list formats: Draft 9 describes -// Parameterised lists and lists-of-lists, while draft 15 uses a single List -// syntax, whose members may be inner lists. There should be no ambiguity, -// however, as the code which calls this parser should be expecting only a -// single type for a given header. +// The major difference between the two revisions is in the various list +// formats: Draft 9 describes "parameterised lists" and "lists-of-lists", while +// the final RFC uses a single "list" syntax, whose members may be inner lists. +// There should be no ambiguity, however, as the code which calls this parser +// should be expecting only a single type for a given header. +// +// References within the code are tagged with either [SH09] or [RFC8941], +// depending on which revision they refer to. // // Currently supported data types are: // Item: diff --git a/chromium/net/http/structured_headers_generated_unittest.cc b/chromium/net/http/structured_headers_generated_unittest.cc index f2250839902..82d1e01205f 100644 --- a/chromium/net/http/structured_headers_generated_unittest.cc +++ b/chromium/net/http/structured_headers_generated_unittest.cc @@ -13,14 +13,14 @@ // This file contains tests cases for the Structured Header parser and // serializer, taken from the public test case repository at -// https://github.com/httpwg/structured-header-tests. All of the tests are -// named, so a given test case can be found in the JSON files in that repository -// by searching for the test name. This file is generated, with the test cases +// https://github.com/httpwg/structured-field-tests. All of the tests are named, +// so a given test case can be found in the JSON files in that repository by +// searching for the test name. This file is generated, with the test cases // being automatically translated from the JSON source to C++ unit tests. Please // do not modify, as the contents will be overwritten when this is re-generated. -// Generated on 2020-04-10 from structured-header-tests.git @ -// 0a8ab1b649080239abb21ecd3ca15a4aead419aa +// Generated on 2022-03-05 from structured-field-tests.git @ +// 4d33b9c2f4e0a7d7d1d733ccf48783aaead8ca4d namespace net { namespace structured_headers { @@ -129,8 +129,8 @@ const struct ParameterizedItemTestCase { {"Example-BoolHdr", "?1", 2, {{Item(true), {}}}}, // item.json {"empty item", "", 0, absl::nullopt}, - {"leading space", " 1", 3, {{Integer(1), {}}}, "1"}, - {"trailing space", "1 ", 3, {{Integer(1), {}}}, "1"}, + {"leading space", " \t 1", 4, absl::nullopt}, + {"trailing space", "1 \t ", 4, absl::nullopt}, {"leading and trailing space", " 1 ", 5, {{Integer(1), {}}}, "1"}, {"leading and trailing whitespace", " 1 ", 8, {{Integer(1), {}}}, "1"}, // number-generated.json @@ -836,7 +836,6 @@ const struct ParameterizedItemTestCase { // number.json {"basic integer", "42", 2, {{Integer(42), {}}}}, {"zero integer", "0", 1, {{Integer(0), {}}}}, - {"leading 0 zero", "00", 2, {{Integer(0), {}}}, "0"}, {"negative zero", "-0", 2, {{Integer(0), {}}}, "0"}, {"double negative zero", "--0", 3, absl::nullopt}, {"negative integer", "-42", 3, {{Integer(-42), {}}}}, @@ -1009,134 +1008,134 @@ const struct ParameterizedItemTestCase { {"0x7d in string", "\" } \"", 5, {{Item(" } "), {}}}}, {"0x7e in string", "\" ~ \"", 5, {{Item(" ~ "), {}}}}, {"0x7f in string", "\" \177 \"", 5, absl::nullopt}, - {"0x00 in string", "\"\\\000\"", 4, absl::nullopt}, - {"0x01 in string", "\"\\\001\"", 4, absl::nullopt}, - {"0x02 in string", "\"\\\002\"", 4, absl::nullopt}, - {"0x03 in string", "\"\\\003\"", 4, absl::nullopt}, - {"0x04 in string", "\"\\\004\"", 4, absl::nullopt}, - {"0x05 in string", "\"\\\005\"", 4, absl::nullopt}, - {"0x06 in string", "\"\\\006\"", 4, absl::nullopt}, - {"0x07 in string", "\"\\\a\"", 4, absl::nullopt}, - {"0x08 in string", "\"\\\b\"", 4, absl::nullopt}, - {"0x09 in string", "\"\\\t\"", 4, absl::nullopt}, - {"0x0a in string", "\"\\\n\"", 4, absl::nullopt}, - {"0x0b in string", "\"\\\v\"", 4, absl::nullopt}, - {"0x0c in string", "\"\\\f\"", 4, absl::nullopt}, - {"0x0d in string", "\"\\\r\"", 4, absl::nullopt}, - {"0x0e in string", "\"\\\016\"", 4, absl::nullopt}, - {"0x0f in string", "\"\\\017\"", 4, absl::nullopt}, - {"0x10 in string", "\"\\\020\"", 4, absl::nullopt}, - {"0x11 in string", "\"\\\021\"", 4, absl::nullopt}, - {"0x12 in string", "\"\\\022\"", 4, absl::nullopt}, - {"0x13 in string", "\"\\\023\"", 4, absl::nullopt}, - {"0x14 in string", "\"\\\024\"", 4, absl::nullopt}, - {"0x15 in string", "\"\\\025\"", 4, absl::nullopt}, - {"0x16 in string", "\"\\\026\"", 4, absl::nullopt}, - {"0x17 in string", "\"\\\027\"", 4, absl::nullopt}, - {"0x18 in string", "\"\\\030\"", 4, absl::nullopt}, - {"0x19 in string", "\"\\\031\"", 4, absl::nullopt}, - {"0x1a in string", "\"\\\032\"", 4, absl::nullopt}, - {"0x1b in string", "\"\\\033\"", 4, absl::nullopt}, - {"0x1c in string", "\"\\\034\"", 4, absl::nullopt}, - {"0x1d in string", "\"\\\035\"", 4, absl::nullopt}, - {"0x1e in string", "\"\\\036\"", 4, absl::nullopt}, - {"0x1f in string", "\"\\\037\"", 4, absl::nullopt}, - {"0x20 in string", "\"\\ \"", 4, absl::nullopt}, - {"0x21 in string", "\"\\!\"", 4, absl::nullopt}, - {"0x22 in string", "\"\\\"\"", 4, {{Item("\""), {}}}}, - {"0x23 in string", "\"\\#\"", 4, absl::nullopt}, - {"0x24 in string", "\"\\$\"", 4, absl::nullopt}, - {"0x25 in string", "\"\\%\"", 4, absl::nullopt}, - {"0x26 in string", "\"\\&\"", 4, absl::nullopt}, - {"0x27 in string", "\"\\'\"", 4, absl::nullopt}, - {"0x28 in string", "\"\\(\"", 4, absl::nullopt}, - {"0x29 in string", "\"\\)\"", 4, absl::nullopt}, - {"0x2a in string", "\"\\*\"", 4, absl::nullopt}, - {"0x2b in string", "\"\\+\"", 4, absl::nullopt}, - {"0x2c in string", "\"\\,\"", 4, absl::nullopt}, - {"0x2d in string", "\"\\-\"", 4, absl::nullopt}, - {"0x2e in string", "\"\\.\"", 4, absl::nullopt}, - {"0x2f in string", "\"\\/\"", 4, absl::nullopt}, - {"0x30 in string", "\"\\0\"", 4, absl::nullopt}, - {"0x31 in string", "\"\\1\"", 4, absl::nullopt}, - {"0x32 in string", "\"\\2\"", 4, absl::nullopt}, - {"0x33 in string", "\"\\3\"", 4, absl::nullopt}, - {"0x34 in string", "\"\\4\"", 4, absl::nullopt}, - {"0x35 in string", "\"\\5\"", 4, absl::nullopt}, - {"0x36 in string", "\"\\6\"", 4, absl::nullopt}, - {"0x37 in string", "\"\\7\"", 4, absl::nullopt}, - {"0x38 in string", "\"\\8\"", 4, absl::nullopt}, - {"0x39 in string", "\"\\9\"", 4, absl::nullopt}, - {"0x3a in string", "\"\\:\"", 4, absl::nullopt}, - {"0x3b in string", "\"\\;\"", 4, absl::nullopt}, - {"0x3c in string", "\"\\<\"", 4, absl::nullopt}, - {"0x3d in string", "\"\\=\"", 4, absl::nullopt}, - {"0x3e in string", "\"\\>\"", 4, absl::nullopt}, - {"0x3f in string", "\"\\?\"", 4, absl::nullopt}, - {"0x40 in string", "\"\\@\"", 4, absl::nullopt}, - {"0x41 in string", "\"\\A\"", 4, absl::nullopt}, - {"0x42 in string", "\"\\B\"", 4, absl::nullopt}, - {"0x43 in string", "\"\\C\"", 4, absl::nullopt}, - {"0x44 in string", "\"\\D\"", 4, absl::nullopt}, - {"0x45 in string", "\"\\E\"", 4, absl::nullopt}, - {"0x46 in string", "\"\\F\"", 4, absl::nullopt}, - {"0x47 in string", "\"\\G\"", 4, absl::nullopt}, - {"0x48 in string", "\"\\H\"", 4, absl::nullopt}, - {"0x49 in string", "\"\\I\"", 4, absl::nullopt}, - {"0x4a in string", "\"\\J\"", 4, absl::nullopt}, - {"0x4b in string", "\"\\K\"", 4, absl::nullopt}, - {"0x4c in string", "\"\\L\"", 4, absl::nullopt}, - {"0x4d in string", "\"\\M\"", 4, absl::nullopt}, - {"0x4e in string", "\"\\N\"", 4, absl::nullopt}, - {"0x4f in string", "\"\\O\"", 4, absl::nullopt}, - {"0x50 in string", "\"\\P\"", 4, absl::nullopt}, - {"0x51 in string", "\"\\Q\"", 4, absl::nullopt}, - {"0x52 in string", "\"\\R\"", 4, absl::nullopt}, - {"0x53 in string", "\"\\S\"", 4, absl::nullopt}, - {"0x54 in string", "\"\\T\"", 4, absl::nullopt}, - {"0x55 in string", "\"\\U\"", 4, absl::nullopt}, - {"0x56 in string", "\"\\V\"", 4, absl::nullopt}, - {"0x57 in string", "\"\\W\"", 4, absl::nullopt}, - {"0x58 in string", "\"\\X\"", 4, absl::nullopt}, - {"0x59 in string", "\"\\Y\"", 4, absl::nullopt}, - {"0x5a in string", "\"\\Z\"", 4, absl::nullopt}, - {"0x5b in string", "\"\\[\"", 4, absl::nullopt}, - {"0x5c in string", "\"\\\\\"", 4, {{Item("\\"), {}}}}, - {"0x5d in string", "\"\\]\"", 4, absl::nullopt}, - {"0x5e in string", "\"\\^\"", 4, absl::nullopt}, - {"0x5f in string", "\"\\_\"", 4, absl::nullopt}, - {"0x60 in string", "\"\\`\"", 4, absl::nullopt}, - {"0x61 in string", "\"\\a\"", 4, absl::nullopt}, - {"0x62 in string", "\"\\b\"", 4, absl::nullopt}, - {"0x63 in string", "\"\\c\"", 4, absl::nullopt}, - {"0x64 in string", "\"\\d\"", 4, absl::nullopt}, - {"0x65 in string", "\"\\e\"", 4, absl::nullopt}, - {"0x66 in string", "\"\\f\"", 4, absl::nullopt}, - {"0x67 in string", "\"\\g\"", 4, absl::nullopt}, - {"0x68 in string", "\"\\h\"", 4, absl::nullopt}, - {"0x69 in string", "\"\\i\"", 4, absl::nullopt}, - {"0x6a in string", "\"\\j\"", 4, absl::nullopt}, - {"0x6b in string", "\"\\k\"", 4, absl::nullopt}, - {"0x6c in string", "\"\\l\"", 4, absl::nullopt}, - {"0x6d in string", "\"\\m\"", 4, absl::nullopt}, - {"0x6e in string", "\"\\n\"", 4, absl::nullopt}, - {"0x6f in string", "\"\\o\"", 4, absl::nullopt}, - {"0x70 in string", "\"\\p\"", 4, absl::nullopt}, - {"0x71 in string", "\"\\q\"", 4, absl::nullopt}, - {"0x72 in string", "\"\\r\"", 4, absl::nullopt}, - {"0x73 in string", "\"\\s\"", 4, absl::nullopt}, - {"0x74 in string", "\"\\t\"", 4, absl::nullopt}, - {"0x75 in string", "\"\\u\"", 4, absl::nullopt}, - {"0x76 in string", "\"\\v\"", 4, absl::nullopt}, - {"0x77 in string", "\"\\w\"", 4, absl::nullopt}, - {"0x78 in string", "\"\\x\"", 4, absl::nullopt}, - {"0x79 in string", "\"\\y\"", 4, absl::nullopt}, - {"0x7a in string", "\"\\z\"", 4, absl::nullopt}, - {"0x7b in string", "\"\\{\"", 4, absl::nullopt}, - {"0x7c in string", "\"\\|\"", 4, absl::nullopt}, - {"0x7d in string", "\"\\}\"", 4, absl::nullopt}, - {"0x7e in string", "\"\\~\"", 4, absl::nullopt}, - {"0x7f in string", "\"\\\177\"", 4, absl::nullopt}, + {"Escaped 0x00 in string", "\"\\\000\"", 4, absl::nullopt}, + {"Escaped 0x01 in string", "\"\\\001\"", 4, absl::nullopt}, + {"Escaped 0x02 in string", "\"\\\002\"", 4, absl::nullopt}, + {"Escaped 0x03 in string", "\"\\\003\"", 4, absl::nullopt}, + {"Escaped 0x04 in string", "\"\\\004\"", 4, absl::nullopt}, + {"Escaped 0x05 in string", "\"\\\005\"", 4, absl::nullopt}, + {"Escaped 0x06 in string", "\"\\\006\"", 4, absl::nullopt}, + {"Escaped 0x07 in string", "\"\\\a\"", 4, absl::nullopt}, + {"Escaped 0x08 in string", "\"\\\b\"", 4, absl::nullopt}, + {"Escaped 0x09 in string", "\"\\\t\"", 4, absl::nullopt}, + {"Escaped 0x0a in string", "\"\\\n\"", 4, absl::nullopt}, + {"Escaped 0x0b in string", "\"\\\v\"", 4, absl::nullopt}, + {"Escaped 0x0c in string", "\"\\\f\"", 4, absl::nullopt}, + {"Escaped 0x0d in string", "\"\\\r\"", 4, absl::nullopt}, + {"Escaped 0x0e in string", "\"\\\016\"", 4, absl::nullopt}, + {"Escaped 0x0f in string", "\"\\\017\"", 4, absl::nullopt}, + {"Escaped 0x10 in string", "\"\\\020\"", 4, absl::nullopt}, + {"Escaped 0x11 in string", "\"\\\021\"", 4, absl::nullopt}, + {"Escaped 0x12 in string", "\"\\\022\"", 4, absl::nullopt}, + {"Escaped 0x13 in string", "\"\\\023\"", 4, absl::nullopt}, + {"Escaped 0x14 in string", "\"\\\024\"", 4, absl::nullopt}, + {"Escaped 0x15 in string", "\"\\\025\"", 4, absl::nullopt}, + {"Escaped 0x16 in string", "\"\\\026\"", 4, absl::nullopt}, + {"Escaped 0x17 in string", "\"\\\027\"", 4, absl::nullopt}, + {"Escaped 0x18 in string", "\"\\\030\"", 4, absl::nullopt}, + {"Escaped 0x19 in string", "\"\\\031\"", 4, absl::nullopt}, + {"Escaped 0x1a in string", "\"\\\032\"", 4, absl::nullopt}, + {"Escaped 0x1b in string", "\"\\\033\"", 4, absl::nullopt}, + {"Escaped 0x1c in string", "\"\\\034\"", 4, absl::nullopt}, + {"Escaped 0x1d in string", "\"\\\035\"", 4, absl::nullopt}, + {"Escaped 0x1e in string", "\"\\\036\"", 4, absl::nullopt}, + {"Escaped 0x1f in string", "\"\\\037\"", 4, absl::nullopt}, + {"Escaped 0x20 in string", "\"\\ \"", 4, absl::nullopt}, + {"Escaped 0x21 in string", "\"\\!\"", 4, absl::nullopt}, + {"Escaped 0x22 in string", "\"\\\"\"", 4, {{Item("\""), {}}}}, + {"Escaped 0x23 in string", "\"\\#\"", 4, absl::nullopt}, + {"Escaped 0x24 in string", "\"\\$\"", 4, absl::nullopt}, + {"Escaped 0x25 in string", "\"\\%\"", 4, absl::nullopt}, + {"Escaped 0x26 in string", "\"\\&\"", 4, absl::nullopt}, + {"Escaped 0x27 in string", "\"\\'\"", 4, absl::nullopt}, + {"Escaped 0x28 in string", "\"\\(\"", 4, absl::nullopt}, + {"Escaped 0x29 in string", "\"\\)\"", 4, absl::nullopt}, + {"Escaped 0x2a in string", "\"\\*\"", 4, absl::nullopt}, + {"Escaped 0x2b in string", "\"\\+\"", 4, absl::nullopt}, + {"Escaped 0x2c in string", "\"\\,\"", 4, absl::nullopt}, + {"Escaped 0x2d in string", "\"\\-\"", 4, absl::nullopt}, + {"Escaped 0x2e in string", "\"\\.\"", 4, absl::nullopt}, + {"Escaped 0x2f in string", "\"\\/\"", 4, absl::nullopt}, + {"Escaped 0x30 in string", "\"\\0\"", 4, absl::nullopt}, + {"Escaped 0x31 in string", "\"\\1\"", 4, absl::nullopt}, + {"Escaped 0x32 in string", "\"\\2\"", 4, absl::nullopt}, + {"Escaped 0x33 in string", "\"\\3\"", 4, absl::nullopt}, + {"Escaped 0x34 in string", "\"\\4\"", 4, absl::nullopt}, + {"Escaped 0x35 in string", "\"\\5\"", 4, absl::nullopt}, + {"Escaped 0x36 in string", "\"\\6\"", 4, absl::nullopt}, + {"Escaped 0x37 in string", "\"\\7\"", 4, absl::nullopt}, + {"Escaped 0x38 in string", "\"\\8\"", 4, absl::nullopt}, + {"Escaped 0x39 in string", "\"\\9\"", 4, absl::nullopt}, + {"Escaped 0x3a in string", "\"\\:\"", 4, absl::nullopt}, + {"Escaped 0x3b in string", "\"\\;\"", 4, absl::nullopt}, + {"Escaped 0x3c in string", "\"\\<\"", 4, absl::nullopt}, + {"Escaped 0x3d in string", "\"\\=\"", 4, absl::nullopt}, + {"Escaped 0x3e in string", "\"\\>\"", 4, absl::nullopt}, + {"Escaped 0x3f in string", "\"\\?\"", 4, absl::nullopt}, + {"Escaped 0x40 in string", "\"\\@\"", 4, absl::nullopt}, + {"Escaped 0x41 in string", "\"\\A\"", 4, absl::nullopt}, + {"Escaped 0x42 in string", "\"\\B\"", 4, absl::nullopt}, + {"Escaped 0x43 in string", "\"\\C\"", 4, absl::nullopt}, + {"Escaped 0x44 in string", "\"\\D\"", 4, absl::nullopt}, + {"Escaped 0x45 in string", "\"\\E\"", 4, absl::nullopt}, + {"Escaped 0x46 in string", "\"\\F\"", 4, absl::nullopt}, + {"Escaped 0x47 in string", "\"\\G\"", 4, absl::nullopt}, + {"Escaped 0x48 in string", "\"\\H\"", 4, absl::nullopt}, + {"Escaped 0x49 in string", "\"\\I\"", 4, absl::nullopt}, + {"Escaped 0x4a in string", "\"\\J\"", 4, absl::nullopt}, + {"Escaped 0x4b in string", "\"\\K\"", 4, absl::nullopt}, + {"Escaped 0x4c in string", "\"\\L\"", 4, absl::nullopt}, + {"Escaped 0x4d in string", "\"\\M\"", 4, absl::nullopt}, + {"Escaped 0x4e in string", "\"\\N\"", 4, absl::nullopt}, + {"Escaped 0x4f in string", "\"\\O\"", 4, absl::nullopt}, + {"Escaped 0x50 in string", "\"\\P\"", 4, absl::nullopt}, + {"Escaped 0x51 in string", "\"\\Q\"", 4, absl::nullopt}, + {"Escaped 0x52 in string", "\"\\R\"", 4, absl::nullopt}, + {"Escaped 0x53 in string", "\"\\S\"", 4, absl::nullopt}, + {"Escaped 0x54 in string", "\"\\T\"", 4, absl::nullopt}, + {"Escaped 0x55 in string", "\"\\U\"", 4, absl::nullopt}, + {"Escaped 0x56 in string", "\"\\V\"", 4, absl::nullopt}, + {"Escaped 0x57 in string", "\"\\W\"", 4, absl::nullopt}, + {"Escaped 0x58 in string", "\"\\X\"", 4, absl::nullopt}, + {"Escaped 0x59 in string", "\"\\Y\"", 4, absl::nullopt}, + {"Escaped 0x5a in string", "\"\\Z\"", 4, absl::nullopt}, + {"Escaped 0x5b in string", "\"\\[\"", 4, absl::nullopt}, + {"Escaped 0x5c in string", "\"\\\\\"", 4, {{Item("\\"), {}}}}, + {"Escaped 0x5d in string", "\"\\]\"", 4, absl::nullopt}, + {"Escaped 0x5e in string", "\"\\^\"", 4, absl::nullopt}, + {"Escaped 0x5f in string", "\"\\_\"", 4, absl::nullopt}, + {"Escaped 0x60 in string", "\"\\`\"", 4, absl::nullopt}, + {"Escaped 0x61 in string", "\"\\a\"", 4, absl::nullopt}, + {"Escaped 0x62 in string", "\"\\b\"", 4, absl::nullopt}, + {"Escaped 0x63 in string", "\"\\c\"", 4, absl::nullopt}, + {"Escaped 0x64 in string", "\"\\d\"", 4, absl::nullopt}, + {"Escaped 0x65 in string", "\"\\e\"", 4, absl::nullopt}, + {"Escaped 0x66 in string", "\"\\f\"", 4, absl::nullopt}, + {"Escaped 0x67 in string", "\"\\g\"", 4, absl::nullopt}, + {"Escaped 0x68 in string", "\"\\h\"", 4, absl::nullopt}, + {"Escaped 0x69 in string", "\"\\i\"", 4, absl::nullopt}, + {"Escaped 0x6a in string", "\"\\j\"", 4, absl::nullopt}, + {"Escaped 0x6b in string", "\"\\k\"", 4, absl::nullopt}, + {"Escaped 0x6c in string", "\"\\l\"", 4, absl::nullopt}, + {"Escaped 0x6d in string", "\"\\m\"", 4, absl::nullopt}, + {"Escaped 0x6e in string", "\"\\n\"", 4, absl::nullopt}, + {"Escaped 0x6f in string", "\"\\o\"", 4, absl::nullopt}, + {"Escaped 0x70 in string", "\"\\p\"", 4, absl::nullopt}, + {"Escaped 0x71 in string", "\"\\q\"", 4, absl::nullopt}, + {"Escaped 0x72 in string", "\"\\r\"", 4, absl::nullopt}, + {"Escaped 0x73 in string", "\"\\s\"", 4, absl::nullopt}, + {"Escaped 0x74 in string", "\"\\t\"", 4, absl::nullopt}, + {"Escaped 0x75 in string", "\"\\u\"", 4, absl::nullopt}, + {"Escaped 0x76 in string", "\"\\v\"", 4, absl::nullopt}, + {"Escaped 0x77 in string", "\"\\w\"", 4, absl::nullopt}, + {"Escaped 0x78 in string", "\"\\x\"", 4, absl::nullopt}, + {"Escaped 0x79 in string", "\"\\y\"", 4, absl::nullopt}, + {"Escaped 0x7a in string", "\"\\z\"", 4, absl::nullopt}, + {"Escaped 0x7b in string", "\"\\{\"", 4, absl::nullopt}, + {"Escaped 0x7c in string", "\"\\|\"", 4, absl::nullopt}, + {"Escaped 0x7d in string", "\"\\}\"", 4, absl::nullopt}, + {"Escaped 0x7e in string", "\"\\~\"", 4, absl::nullopt}, + {"Escaped 0x7f in string", "\"\\\177\"", 4, absl::nullopt}, // string.json {"basic string", "\"foo bar\"", 9, {{Item("foo bar"), {}}}}, {"empty string", "\"\"", 2, {{Item(""), {}}}}, @@ -1453,8 +1452,6 @@ const struct ListTestCase { // format is identical to raw. } list_test_cases[] = { - // dictionary.json - {"tab separated dictionary", "a=1\t,\tb=2", 9, absl::nullopt}, // examples.json {"Example-StrListHeader", "\"foo\", \"bar\", \"It was the best of times.\"", @@ -2073,7 +2070,11 @@ const struct ListTestCase { 6, {{{Integer(1), {}}, {Integer(42), {}}}}, "1, 42"}, - {"tab separated list", "1\t,\t42", 6, absl::nullopt}, + {"tab separated list", + "1\t,\t42", + 6, + {{{Integer(1), {}}, {Integer(42), {}}}}, + "1, 42"}, {"two line list", "1, 42", 5, @@ -2081,6 +2082,7 @@ const struct ListTestCase { "1, 42"}, {"trailing comma list", "1, 42,", 6, absl::nullopt}, {"empty item list", "1,,42", 5, absl::nullopt}, + {"empty item list (multiple field lines)", "1, , 42", 7, absl::nullopt}, // listlist.json {"basic list of lists", "(1 2), (42 43)", @@ -2110,6 +2112,7 @@ const struct ListTestCase { absl::nullopt}, {"no spaces in inner-list", "(abc\"def\"?0123*dXZ3*xyz)", 24, absl::nullopt}, + {"no closing parenthesis", "(", 1, absl::nullopt}, // param-list.json {"basic parameterised list", "abc_123;a=1;b=2; cdef_456, ghi;q=9;r=\"+w\"", @@ -2167,6 +2170,24 @@ const struct ListTestCase { absl::nullopt}, {"empty item parameterised list", "text/html,,text/plain;q=0.5,", 28, absl::nullopt}, + // param-listlist.json + {"parameterised inner list", + "(abc_123);a=1;b=2, cdef_456", + 27, + {{{{{Item("abc_123", Item::kTokenType), {}}}, + {Param("a", 1), Param("b", 2)}}, + {Item("cdef_456", Item::kTokenType), {}}}}}, + {"parameterised inner list item", + "(abc_123;a=1;b=2;cdef_456)", + 26, + {{{{{Item("abc_123", Item::kTokenType), + {Param("a", 1), Param("b", 2), BooleanParam("cdef_456", true)}}}, + {}}}}}, + {"parameterised inner list with parameterised item", + "(abc_123;a=1;b=2);cdef_456", + 26, + {{{{{Item("abc_123", Item::kTokenType), {Param("a", 1), Param("b", 2)}}}, + {BooleanParam("cdef_456", true)}}}}}, // token.json {"basic token - list", "a_b-c3/*", @@ -2227,6 +2248,11 @@ const struct DictionaryTestCase { 10, {Dictionary{{{"a", {Integer(1), {}}}, {"b", {Integer(2), {}}}}}}, "a=1, b=2"}, + {"tab separated dictionary", + "a=1\t,\tb=2", + 9, + {Dictionary{{{"a", {Integer(1), {}}}, {"b", {Integer(2), {}}}}}}, + "a=1, b=2"}, {"leading whitespace dictionary", " a=1 , b=2", 15, @@ -2325,6 +2351,215 @@ const struct DictionaryTestCase { {Dictionary{{{"foo", {Integer(1), {}}}, {"bar", {Integer(2), {}}}}}}, "foo=1, bar=2"}, // key-generated.json + {"0x00 as a single-character dictionary key", "\000=1", 3, absl::nullopt}, + {"0x01 as a single-character dictionary key", "\001=1", 3, absl::nullopt}, + {"0x02 as a single-character dictionary key", "\002=1", 3, absl::nullopt}, + {"0x03 as a single-character dictionary key", "\003=1", 3, absl::nullopt}, + {"0x04 as a single-character dictionary key", "\004=1", 3, absl::nullopt}, + {"0x05 as a single-character dictionary key", "\005=1", 3, absl::nullopt}, + {"0x06 as a single-character dictionary key", "\006=1", 3, absl::nullopt}, + {"0x07 as a single-character dictionary key", "\a=1", 3, absl::nullopt}, + {"0x08 as a single-character dictionary key", "\b=1", 3, absl::nullopt}, + {"0x09 as a single-character dictionary key", "\t=1", 3, absl::nullopt}, + {"0x0a as a single-character dictionary key", "\n=1", 3, absl::nullopt}, + {"0x0b as a single-character dictionary key", "\v=1", 3, absl::nullopt}, + {"0x0c as a single-character dictionary key", "\f=1", 3, absl::nullopt}, + {"0x0d as a single-character dictionary key", "\r=1", 3, absl::nullopt}, + {"0x0e as a single-character dictionary key", "\016=1", 3, absl::nullopt}, + {"0x0f as a single-character dictionary key", "\017=1", 3, absl::nullopt}, + {"0x10 as a single-character dictionary key", "\020=1", 3, absl::nullopt}, + {"0x11 as a single-character dictionary key", "\021=1", 3, absl::nullopt}, + {"0x12 as a single-character dictionary key", "\022=1", 3, absl::nullopt}, + {"0x13 as a single-character dictionary key", "\023=1", 3, absl::nullopt}, + {"0x14 as a single-character dictionary key", "\024=1", 3, absl::nullopt}, + {"0x15 as a single-character dictionary key", "\025=1", 3, absl::nullopt}, + {"0x16 as a single-character dictionary key", "\026=1", 3, absl::nullopt}, + {"0x17 as a single-character dictionary key", "\027=1", 3, absl::nullopt}, + {"0x18 as a single-character dictionary key", "\030=1", 3, absl::nullopt}, + {"0x19 as a single-character dictionary key", "\031=1", 3, absl::nullopt}, + {"0x1a as a single-character dictionary key", "\032=1", 3, absl::nullopt}, + {"0x1b as a single-character dictionary key", "\033=1", 3, absl::nullopt}, + {"0x1c as a single-character dictionary key", "\034=1", 3, absl::nullopt}, + {"0x1d as a single-character dictionary key", "\035=1", 3, absl::nullopt}, + {"0x1e as a single-character dictionary key", "\036=1", 3, absl::nullopt}, + {"0x1f as a single-character dictionary key", "\037=1", 3, absl::nullopt}, + {"0x20 as a single-character dictionary key", "=1", 2, absl::nullopt}, + {"0x21 as a single-character dictionary key", "!=1", 3, absl::nullopt}, + {"0x22 as a single-character dictionary key", "\"=1", 3, absl::nullopt}, + {"0x23 as a single-character dictionary key", "#=1", 3, absl::nullopt}, + {"0x24 as a single-character dictionary key", "$=1", 3, absl::nullopt}, + {"0x25 as a single-character dictionary key", "%=1", 3, absl::nullopt}, + {"0x26 as a single-character dictionary key", "&=1", 3, absl::nullopt}, + {"0x27 as a single-character dictionary key", "'=1", 3, absl::nullopt}, + {"0x28 as a single-character dictionary key", "(=1", 3, absl::nullopt}, + {"0x29 as a single-character dictionary key", ")=1", 3, absl::nullopt}, + {"0x2a as a single-character dictionary key", + "*=1", + 3, + {Dictionary{{{"*", {Integer(1), {}}}}}}}, + {"0x2b as a single-character dictionary key", "+=1", 3, absl::nullopt}, + {"0x2c as a single-character dictionary key", ",=1", 3, absl::nullopt}, + {"0x2d as a single-character dictionary key", "-=1", 3, absl::nullopt}, + {"0x2e as a single-character dictionary key", ".=1", 3, absl::nullopt}, + {"0x2f as a single-character dictionary key", "/=1", 3, absl::nullopt}, + {"0x30 as a single-character dictionary key", "0=1", 3, absl::nullopt}, + {"0x31 as a single-character dictionary key", "1=1", 3, absl::nullopt}, + {"0x32 as a single-character dictionary key", "2=1", 3, absl::nullopt}, + {"0x33 as a single-character dictionary key", "3=1", 3, absl::nullopt}, + {"0x34 as a single-character dictionary key", "4=1", 3, absl::nullopt}, + {"0x35 as a single-character dictionary key", "5=1", 3, absl::nullopt}, + {"0x36 as a single-character dictionary key", "6=1", 3, absl::nullopt}, + {"0x37 as a single-character dictionary key", "7=1", 3, absl::nullopt}, + {"0x38 as a single-character dictionary key", "8=1", 3, absl::nullopt}, + {"0x39 as a single-character dictionary key", "9=1", 3, absl::nullopt}, + {"0x3a as a single-character dictionary key", ":=1", 3, absl::nullopt}, + {"0x3b as a single-character dictionary key", ";=1", 3, absl::nullopt}, + {"0x3c as a single-character dictionary key", "<=1", 3, absl::nullopt}, + {"0x3d as a single-character dictionary key", "==1", 3, absl::nullopt}, + {"0x3e as a single-character dictionary key", ">=1", 3, absl::nullopt}, + {"0x3f as a single-character dictionary key", "?=1", 3, absl::nullopt}, + {"0x40 as a single-character dictionary key", "@=1", 3, absl::nullopt}, + {"0x41 as a single-character dictionary key", "A=1", 3, absl::nullopt}, + {"0x42 as a single-character dictionary key", "B=1", 3, absl::nullopt}, + {"0x43 as a single-character dictionary key", "C=1", 3, absl::nullopt}, + {"0x44 as a single-character dictionary key", "D=1", 3, absl::nullopt}, + {"0x45 as a single-character dictionary key", "E=1", 3, absl::nullopt}, + {"0x46 as a single-character dictionary key", "F=1", 3, absl::nullopt}, + {"0x47 as a single-character dictionary key", "G=1", 3, absl::nullopt}, + {"0x48 as a single-character dictionary key", "H=1", 3, absl::nullopt}, + {"0x49 as a single-character dictionary key", "I=1", 3, absl::nullopt}, + {"0x4a as a single-character dictionary key", "J=1", 3, absl::nullopt}, + {"0x4b as a single-character dictionary key", "K=1", 3, absl::nullopt}, + {"0x4c as a single-character dictionary key", "L=1", 3, absl::nullopt}, + {"0x4d as a single-character dictionary key", "M=1", 3, absl::nullopt}, + {"0x4e as a single-character dictionary key", "N=1", 3, absl::nullopt}, + {"0x4f as a single-character dictionary key", "O=1", 3, absl::nullopt}, + {"0x50 as a single-character dictionary key", "P=1", 3, absl::nullopt}, + {"0x51 as a single-character dictionary key", "Q=1", 3, absl::nullopt}, + {"0x52 as a single-character dictionary key", "R=1", 3, absl::nullopt}, + {"0x53 as a single-character dictionary key", "S=1", 3, absl::nullopt}, + {"0x54 as a single-character dictionary key", "T=1", 3, absl::nullopt}, + {"0x55 as a single-character dictionary key", "U=1", 3, absl::nullopt}, + {"0x56 as a single-character dictionary key", "V=1", 3, absl::nullopt}, + {"0x57 as a single-character dictionary key", "W=1", 3, absl::nullopt}, + {"0x58 as a single-character dictionary key", "X=1", 3, absl::nullopt}, + {"0x59 as a single-character dictionary key", "Y=1", 3, absl::nullopt}, + {"0x5a as a single-character dictionary key", "Z=1", 3, absl::nullopt}, + {"0x5b as a single-character dictionary key", "[=1", 3, absl::nullopt}, + {"0x5c as a single-character dictionary key", "\\=1", 3, absl::nullopt}, + {"0x5d as a single-character dictionary key", "]=1", 3, absl::nullopt}, + {"0x5e as a single-character dictionary key", "^=1", 3, absl::nullopt}, + {"0x5f as a single-character dictionary key", "_=1", 3, absl::nullopt}, + {"0x60 as a single-character dictionary key", "`=1", 3, absl::nullopt}, + {"0x61 as a single-character dictionary key", + "a=1", + 3, + {Dictionary{{{"a", {Integer(1), {}}}}}}}, + {"0x62 as a single-character dictionary key", + "b=1", + 3, + {Dictionary{{{"b", {Integer(1), {}}}}}}}, + {"0x63 as a single-character dictionary key", + "c=1", + 3, + {Dictionary{{{"c", {Integer(1), {}}}}}}}, + {"0x64 as a single-character dictionary key", + "d=1", + 3, + {Dictionary{{{"d", {Integer(1), {}}}}}}}, + {"0x65 as a single-character dictionary key", + "e=1", + 3, + {Dictionary{{{"e", {Integer(1), {}}}}}}}, + {"0x66 as a single-character dictionary key", + "f=1", + 3, + {Dictionary{{{"f", {Integer(1), {}}}}}}}, + {"0x67 as a single-character dictionary key", + "g=1", + 3, + {Dictionary{{{"g", {Integer(1), {}}}}}}}, + {"0x68 as a single-character dictionary key", + "h=1", + 3, + {Dictionary{{{"h", {Integer(1), {}}}}}}}, + {"0x69 as a single-character dictionary key", + "i=1", + 3, + {Dictionary{{{"i", {Integer(1), {}}}}}}}, + {"0x6a as a single-character dictionary key", + "j=1", + 3, + {Dictionary{{{"j", {Integer(1), {}}}}}}}, + {"0x6b as a single-character dictionary key", + "k=1", + 3, + {Dictionary{{{"k", {Integer(1), {}}}}}}}, + {"0x6c as a single-character dictionary key", + "l=1", + 3, + {Dictionary{{{"l", {Integer(1), {}}}}}}}, + {"0x6d as a single-character dictionary key", + "m=1", + 3, + {Dictionary{{{"m", {Integer(1), {}}}}}}}, + {"0x6e as a single-character dictionary key", + "n=1", + 3, + {Dictionary{{{"n", {Integer(1), {}}}}}}}, + {"0x6f as a single-character dictionary key", + "o=1", + 3, + {Dictionary{{{"o", {Integer(1), {}}}}}}}, + {"0x70 as a single-character dictionary key", + "p=1", + 3, + {Dictionary{{{"p", {Integer(1), {}}}}}}}, + {"0x71 as a single-character dictionary key", + "q=1", + 3, + {Dictionary{{{"q", {Integer(1), {}}}}}}}, + {"0x72 as a single-character dictionary key", + "r=1", + 3, + {Dictionary{{{"r", {Integer(1), {}}}}}}}, + {"0x73 as a single-character dictionary key", + "s=1", + 3, + {Dictionary{{{"s", {Integer(1), {}}}}}}}, + {"0x74 as a single-character dictionary key", + "t=1", + 3, + {Dictionary{{{"t", {Integer(1), {}}}}}}}, + {"0x75 as a single-character dictionary key", + "u=1", + 3, + {Dictionary{{{"u", {Integer(1), {}}}}}}}, + {"0x76 as a single-character dictionary key", + "v=1", + 3, + {Dictionary{{{"v", {Integer(1), {}}}}}}}, + {"0x77 as a single-character dictionary key", + "w=1", + 3, + {Dictionary{{{"w", {Integer(1), {}}}}}}}, + {"0x78 as a single-character dictionary key", + "x=1", + 3, + {Dictionary{{{"x", {Integer(1), {}}}}}}}, + {"0x79 as a single-character dictionary key", + "y=1", + 3, + {Dictionary{{{"y", {Integer(1), {}}}}}}}, + {"0x7a as a single-character dictionary key", + "z=1", + 3, + {Dictionary{{{"z", {Integer(1), {}}}}}}}, + {"0x7b as a single-character dictionary key", "{=1", 3, absl::nullopt}, + {"0x7c as a single-character dictionary key", "|=1", 3, absl::nullopt}, + {"0x7d as a single-character dictionary key", "}=1", 3, absl::nullopt}, + {"0x7e as a single-character dictionary key", "~=1", 3, absl::nullopt}, + {"0x7f as a single-character dictionary key", "\177=1", 3, absl::nullopt}, {"0x00 in dictionary key", "a\000a=1", 5, absl::nullopt}, {"0x01 in dictionary key", "a\001a=1", 5, absl::nullopt}, {"0x02 in dictionary key", "a\002a=1", 5, absl::nullopt}, diff --git a/chromium/net/http/structured_headers_unittest.cc b/chromium/net/http/structured_headers_unittest.cc index 62b53b0c67e..d2848b2b523 100644 --- a/chromium/net/http/structured_headers_unittest.cc +++ b/chromium/net/http/structured_headers_unittest.cc @@ -236,7 +236,10 @@ const struct DictionaryTestCase { "en=\"Applepie\", da=:aGVsbG8=:", {Dictionary{{{"en", {Item("Applepie"), {}}}, {"da", {Item("hello", Item::kByteSequenceType), {}}}}}}}, - {"tab separated dictionary", "a=1\t,\tb=2", absl::nullopt}, + {"tab separated dictionary", + "a=1\t,\tb=2", + {Dictionary{{{"a", {Integer(1L), {}}}, {"b", {Integer(2L), {}}}}}}, + "a=1, b=2"}, {"missing value with params dictionary", "a=1, b;foo=9, c=3", {Dictionary{{{"a", {Integer(1L), {}}}, diff --git a/chromium/net/http/transport_security_persister.cc b/chromium/net/http/transport_security_persister.cc index 0554f03cf62..2b3d0dc57d5 100644 --- a/chromium/net/http/transport_security_persister.cc +++ b/chromium/net/http/transport_security_persister.cc @@ -9,6 +9,7 @@ #include "base/base64.h" #include "base/bind.h" +#include "base/callback.h" #include "base/feature_list.h" #include "base/files/file_path.h" #include "base/files/file_util.h" diff --git a/chromium/net/http/transport_security_state.cc b/chromium/net/http/transport_security_state.cc index 873c2bf85b6..340b2e49df2 100644 --- a/chromium/net/http/transport_security_state.cc +++ b/chromium/net/http/transport_security_state.cc @@ -431,9 +431,7 @@ TransportSecurityState::TransportSecurityState( bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) { STSState unused_sts; PKPState unused_pkp; - return GetDynamicSTSState(host, &unused_sts) || - GetDynamicPKPState(host, &unused_pkp) || - GetStaticDomainState(host, &unused_sts, &unused_pkp); + return GetSTSState(host, &unused_sts) || GetPKPState(host, &unused_pkp); } base::Value TransportSecurityState::NetLogUpgradeToSSLParam( @@ -637,6 +635,29 @@ void TransportSecurityState::SetCTLogListUpdateTime(base::Time update_time) { ct_log_list_last_update_time_ = update_time; } +void TransportSecurityState::UpdatePinList( + const std::vector<PinSet>& pinsets, + const std::vector<PinSetInfo>& host_pins, + base::Time update_time) { + pinsets_ = pinsets; + key_pins_list_last_update_time_ = update_time; + host_pins_ = absl::make_optional( + std::map<std::string, std::pair<PinSet const*, bool>>()); + std::map<std::string, PinSet const*> pinset_names_map; + for (const auto& pinset : pinsets_) { + pinset_names_map[pinset.name()] = &pinset; + } + for (const auto& pin : host_pins) { + if (!base::Contains(pinset_names_map, pin.pinset_name_)) { + // This should never happen, but if the component is bad and missing an + // entry, we will ignore that particular pin. + continue; + } + host_pins_.value()[pin.hostname_] = std::make_pair( + pinset_names_map[pin.pinset_name_], pin.include_subdomains_); + } +} + void TransportSecurityState::AddHSTSInternal( const std::string& host, TransportSecurityState::STSState::UpgradeMode upgrade_mode, @@ -1133,27 +1154,64 @@ TransportSecurityState::CheckPublicKeyPinsImpl( network_isolation_key, failure_log); } -bool TransportSecurityState::GetStaticDomainState(const std::string& host, - STSState* sts_result, - PKPState* pkp_result) const { +bool TransportSecurityState::GetStaticSTSState(const std::string& host, + STSState* sts_result) const { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!IsBuildTimely()) return false; PreloadResult result; - if (!DecodeHSTSPreload(host, &result)) - return false; - - if (hsts_host_bypass_list_.find(host) == hsts_host_bypass_list_.end() && + if (DecodeHSTSPreload(host, &result) && + hsts_host_bypass_list_.find(host) == hsts_host_bypass_list_.end() && result.force_https) { sts_result->domain = host.substr(result.hostname_offset); sts_result->include_subdomains = result.sts_include_subdomains; sts_result->last_observed = base::GetBuildTime(); sts_result->upgrade_mode = STSState::MODE_FORCE_HTTPS; + return true; } - if (enable_static_pins_ && result.has_pins) { + return false; +} + +bool TransportSecurityState::GetStaticPKPState(const std::string& host, + PKPState* pkp_result) const { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (!enable_static_pins_ || !IsStaticPKPListTimely()) + return false; + + PreloadResult result; + if (host_pins_.has_value()) { + auto iter = host_pins_->find(host); + if (iter != host_pins_->end()) { + pkp_result->domain = host; + pkp_result->last_observed = key_pins_list_last_update_time_; + pkp_result->include_subdomains = iter->second.second; + const PinSet* pinset = iter->second.first; + if (!pinset->report_uri().empty()) { + pkp_result->report_uri = GURL(pinset->report_uri()); + } + for (auto hash : pinset->static_spki_hashes()) { + // If the update is malformed, it's preferable to skip the hash than + // crash. + if (hash.size() == 32) { + AddHash(reinterpret_cast<const char*>(hash.data()), + &pkp_result->spki_hashes); + } + } + for (auto hash : pinset->bad_static_spki_hashes()) { + // If the update is malformed, it's preferable to skip the hash than + // crash. + if (hash.size() == 32) { + AddHash(reinterpret_cast<const char*>(hash.data()), + &pkp_result->bad_spki_hashes); + } + } + return true; + } + } else if (DecodeHSTSPreload(host, &result) && result.has_pins) { if (result.pinset_id >= g_hsts_source->pinsets_count) return false; @@ -1180,23 +1238,20 @@ bool TransportSecurityState::GetStaticDomainState(const std::string& host, sha256_hash++; } } + return true; } - return true; + return false; } bool TransportSecurityState::GetSTSState(const std::string& host, STSState* result) { - PKPState unused; - return GetDynamicSTSState(host, result) || - GetStaticDomainState(host, result, &unused); + return GetDynamicSTSState(host, result) || GetStaticSTSState(host, result); } bool TransportSecurityState::GetPKPState(const std::string& host, PKPState* result) { - STSState unused; - return GetDynamicPKPState(host, result) || - GetStaticDomainState(host, &unused, result); + return GetDynamicPKPState(host, result) || GetStaticPKPState(host, result); } bool TransportSecurityState::GetDynamicSTSState(const std::string& host, @@ -1380,6 +1435,26 @@ TransportSecurityState::ExpectCTStateIterator::ExpectCTStateIterator( TransportSecurityState::ExpectCTStateIterator::~ExpectCTStateIterator() = default; +TransportSecurityState::PinSet::PinSet( + std::string name, + std::vector<std::vector<uint8_t>> static_spki_hashes, + std::vector<std::vector<uint8_t>> bad_static_spki_hashes, + std::string report_uri) + : name_(std::move(name)), + static_spki_hashes_(std::move(static_spki_hashes)), + bad_static_spki_hashes_(std::move(bad_static_spki_hashes)), + report_uri_(std::move(report_uri)) {} + +TransportSecurityState::PinSet::PinSet(const PinSet& other) = default; +TransportSecurityState::PinSet::~PinSet() = default; + +TransportSecurityState::PinSetInfo::PinSetInfo(std::string hostname, + std::string pinset_name, + bool include_subdomains) + : hostname_(std::move(hostname)), + pinset_name_(std::move(pinset_name)), + include_subdomains_(std::move(include_subdomains)) {} + bool TransportSecurityState::PKPState::CheckPublicKeyPins( const HashValueVector& hashes, std::string* failure_log) const { @@ -1565,4 +1640,16 @@ bool TransportSecurityState::IsCTLogListTimely() const { 70 /* 10 weeks */; } +bool TransportSecurityState::IsStaticPKPListTimely() const { + // If the list has not been updated via component updater, freshness depends + // on build freshness. + if (!host_pins_.has_value()) { + return IsBuildTimely(); + } + DCHECK(!key_pins_list_last_update_time_.is_null()); + // Else, we use the last update time. + return (base::Time::Now() - key_pins_list_last_update_time_).InDays() < + 70 /* 10 weeks */; +} + } // namespace net diff --git a/chromium/net/http/transport_security_state.h b/chromium/net/http/transport_security_state.h index 73f539e38c3..e8d50dbf23a 100644 --- a/chromium/net/http/transport_security_state.h +++ b/chromium/net/http/transport_security_state.h @@ -25,6 +25,7 @@ #include "net/cert/signed_certificate_timestamp_and_status.h" #include "net/http/transport_security_state_source.h" #include "net/log/net_log_with_source.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" namespace net { @@ -334,6 +335,41 @@ class NET_EXPORT TransportSecurityState { virtual ~ExpectCTReporter() {} }; + class NET_EXPORT PinSet { + public: + PinSet(std::string name, + std::vector<std::vector<uint8_t>> static_spki_hashes, + std::vector<std::vector<uint8_t>> bad_static_spki_hashes, + std::string report_uri); + PinSet(const PinSet& other); + ~PinSet(); + + const std::string& name() const { return name_; } + const std::vector<std::vector<uint8_t>>& static_spki_hashes() const { + return static_spki_hashes_; + } + const std::vector<std::vector<uint8_t>>& bad_static_spki_hashes() const { + return bad_static_spki_hashes_; + } + const std::string& report_uri() const { return report_uri_; } + + private: + std::string name_; + std::vector<std::vector<uint8_t>> static_spki_hashes_; + std::vector<std::vector<uint8_t>> bad_static_spki_hashes_; + std::string report_uri_; + }; + + struct NET_EXPORT PinSetInfo { + std::string hostname_; + std::string pinset_name_; + bool include_subdomains_; + + PinSetInfo(std::string hostname, + std::string pinset_name, + bool include_subdomains); + }; + // Indicates whether or not a public key pin check should send a // report if a violation is detected. enum PublicKeyPinReportStatus { ENABLE_PIN_REPORTS, DISABLE_PIN_REPORTS }; @@ -451,8 +487,19 @@ class NET_EXPORT TransportSecurityState { ct_emergency_disable_ = emergency_disable; } + bool is_ct_emergency_disabled_for_testing() const { + return ct_emergency_disable_; + } + void SetCTLogListUpdateTime(base::Time update_time); + // |pinsets| should include all known pinsets, |host_pins| the information + // related to each hostname's pin, and |update_time| the time at which this + // list was known to be up to date. + void UpdatePinList(const std::vector<PinSet>& pinsets, + const std::vector<PinSetInfo>& host_pins, + base::Time update_time); + // Clears all dynamic data (e.g. HSTS and HPKP data). // // Does NOT persist changes using the Delegate, as this function is only @@ -503,15 +550,14 @@ class NET_EXPORT TransportSecurityState { // // Note that these methods are not const because they opportunistically remove // entries that have expired. - bool GetSTSState(const std::string& host, STSState* result); - bool GetPKPState(const std::string& host, PKPState* result); + bool GetSTSState(const std::string& host, STSState* sts_result); + bool GetPKPState(const std::string& host, PKPState* pkp_result); - // Returns true and updates |*sts_result| and/or |*pkp_result| if there is - // static (built-in) state for |host|. If multiple entries match |host|, - // the most specific match determines the return value. - bool GetStaticDomainState(const std::string& host, - STSState* sts_result, - PKPState* pkp_result) const; + // Returns true and updates |*result| iff |host| has static HSTS/HPKP + // (respectively) state. If multiple entries match |host|, the most specific + // match determines the return value. + bool GetStaticSTSState(const std::string& host, STSState* sts_result) const; + bool GetStaticPKPState(const std::string& host, PKPState* pkp_result) const; // Returns true and updates |*result| iff |host| has dynamic // HSTS/HPKP/Expect-CT (respectively) state. If multiple entries match |host|, @@ -709,6 +755,10 @@ class NET_EXPORT TransportSecurityState { // Returns true if the CT log list has been updated in the last 10 weeks. bool IsCTLogListTimely() const; + // Returns true if the static key pinning list has been updated in the last 10 + // weeks. + bool IsStaticPKPListTimely() const; + // The sets of hosts that have enabled TransportSecurity. |domain| will always // be empty for a STSState, PKPState, or ExpectCTState in these maps; the // domain comes from the map keys instead. In addition, |upgrade_mode| in the @@ -756,6 +806,13 @@ class NET_EXPORT TransportSecurityState { base::Time ct_log_list_last_update_time_; + // The values in host_pins_ maps are references to PinSet objects in the + // pinsets_ vector. + absl::optional<std::map<std::string, std::pair<const PinSet*, bool>>> + host_pins_; + base::Time key_pins_list_last_update_time_; + std::vector<PinSet> pinsets_; + THREAD_CHECKER(thread_checker_); }; diff --git a/chromium/net/http/transport_security_state_static.json.gz b/chromium/net/http/transport_security_state_static.json.gz Binary files differindex 93565f19e71..37ad27c8277 100644 --- a/chromium/net/http/transport_security_state_static.json.gz +++ b/chromium/net/http/transport_security_state_static.json.gz diff --git a/chromium/net/http/transport_security_state_static.template b/chromium/net/http/transport_security_state_static.template index 21542f9b7ce..ba40b5bfefc 100644 --- a/chromium/net/http/transport_security_state_static.template +++ b/chromium/net/http/transport_security_state_static.template @@ -9,7 +9,8 @@ #include <stdint.h> -#include "base/cxx17_backports.h" +#include <iterator> + #include "net/http/transport_security_state_source.h" // These are SubjectPublicKeyInfo hashes for public key pinning. The @@ -47,7 +48,7 @@ static const net::TransportSecurityStateSource kHSTSSource = { kHSTSRootPosition, kExpectCTReportURIs, kPinsets, - base::size(kPinsets) + std::size(kPinsets) }; #endif // NET_HTTP_TRANSPORT_SECURITY_STATE_STATIC_H_ diff --git a/chromium/net/http/transport_security_state_static_fuzzer.cc b/chromium/net/http/transport_security_state_static_fuzzer.cc index af5d1ef7a35..6b3e7a02edb 100644 --- a/chromium/net/http/transport_security_state_static_fuzzer.cc +++ b/chromium/net/http/transport_security_state_static_fuzzer.cc @@ -15,7 +15,8 @@ class TransportSecurityStateStaticFuzzer { state->enable_static_pins_ = true; TransportSecurityState::STSState sts_result; TransportSecurityState::PKPState pkp_result; - return state->GetStaticDomainState(input, &sts_result, &pkp_result); + return state->GetStaticSTSState(input, &sts_result) || + state->GetStaticPKPState(input, &pkp_result); } bool FuzzStaticExpectCTState(TransportSecurityState* state, diff --git a/chromium/net/http/transport_security_state_static_unittest.template b/chromium/net/http/transport_security_state_static_unittest.template index dfb4a331e29..8d08caa1872 100644 --- a/chromium/net/http/transport_security_state_static_unittest.template +++ b/chromium/net/http/transport_security_state_static_unittest.template @@ -6,7 +6,7 @@ // See transport_security_state_static.template for more information on the data // in this file. -// Note that consumers must include <stdint.h>, "base/cxx17_backports.h", and +// Note that consumers must include <stdint.h>, "<iterator>", and // "net/http/transport_security_state_source.h", which this file cannot do // itself, since it's always included in a nested namespace. @@ -15,7 +15,7 @@ static const char* const kExpectCTReportURIs[] = [[EXPECT_CT_REPORT_URIS]]; static const char* const kNoRejectedPublicKeys[] = { - NULL, + nullptr, }; [[ACCEPTABLE_CERTS]] @@ -37,5 +37,5 @@ static const net::TransportSecurityStateSource kHSTSSource = { kHSTSRootPosition, kExpectCTReportURIs, kPinsets, - base::size(kPinsets) + std::size(kPinsets) }; diff --git a/chromium/net/http/transport_security_state_test_util.cc b/chromium/net/http/transport_security_state_test_util.cc index d0d05bb4d33..248a004747d 100644 --- a/chromium/net/http/transport_security_state_test_util.cc +++ b/chromium/net/http/transport_security_state_test_util.cc @@ -4,6 +4,8 @@ #include "net/http/transport_security_state_test_util.h" +#include <iterator> + #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "net/http/transport_security_state.h" diff --git a/chromium/net/http/transport_security_state_unittest.cc b/chromium/net/http/transport_security_state_unittest.cc index 228405ee883..ba3b4e3c3b2 100644 --- a/chromium/net/http/transport_security_state_unittest.cc +++ b/chromium/net/http/transport_security_state_unittest.cc @@ -7,13 +7,13 @@ #include <stdint.h> #include <algorithm> +#include <iterator> #include <memory> #include <string> #include <vector> #include "base/base64.h" #include "base/callback_helpers.h" -#include "base/cxx17_backports.h" #include "base/files/file_path.h" #include "base/json/json_reader.h" #include "base/memory/raw_ptr.h" @@ -26,6 +26,7 @@ #include "base/test/metrics/histogram_tester.h" #include "base/test/mock_entropy_provider.h" #include "base/test/scoped_feature_list.h" +#include "base/time/time.h" #include "base/values.h" #include "build/build_config.h" #include "crypto/openssl_util.h" @@ -405,7 +406,10 @@ class TransportSecurityStateTest : public ::testing::Test, const std::string& host, TransportSecurityState::STSState* sts_result, TransportSecurityState::PKPState* pkp_result) { - return state->GetStaticDomainState(host, sts_result, pkp_result); + bool ret = state->GetStaticSTSState(host, sts_result); + if (state->GetStaticPKPState(host, pkp_result)) + ret = true; + return ret; } bool GetExpectCTState(TransportSecurityState* state, @@ -885,8 +889,9 @@ TEST_F(TransportSecurityStateTest, LongNames) { "WaveletIdDomainAndBlipBlipid"; TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; - // Just checks that we don't hit a NOTREACHED. - EXPECT_FALSE(state.GetStaticDomainState(kLongName, &sts_state, &pkp_state)); + // Just checks that we don't hit a NOTREACHED + EXPECT_FALSE(state.GetStaticSTSState(kLongName, &sts_state)); + EXPECT_FALSE(state.GetStaticPKPState(kLongName, &pkp_state)); EXPECT_FALSE(state.GetDynamicSTSState(kLongName, &sts_state)); EXPECT_FALSE(state.GetDynamicPKPState(kLongName, &pkp_state)); } @@ -913,10 +918,9 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { TransportSecurityState state; EnableStaticPins(&state); - TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; - EXPECT_TRUE(state.GetStaticDomainState("no-rejected-pins-pkp.preloaded.test", - &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("no-rejected-pins-pkp.preloaded.test", + &pkp_state)); EXPECT_TRUE(pkp_state.HasPublicKeyPins()); std::string failure_log; @@ -939,9 +943,7 @@ TEST_F(TransportSecurityStateTest, PreloadedPKPReportUri) { EnableStaticPins(&state); TransportSecurityState::PKPState pkp_state; - TransportSecurityState::STSState unused_sts_state; - ASSERT_TRUE(state.GetStaticDomainState(kPreloadedPinDomain, &unused_sts_state, - &pkp_state)); + ASSERT_TRUE(state.GetStaticPKPState(kPreloadedPinDomain, &pkp_state)); ASSERT_TRUE(pkp_state.HasPublicKeyPins()); GURL report_uri = pkp_state.report_uri; @@ -1435,8 +1437,8 @@ TEST_F(TransportSecurityStateTest, DecodePreloadedMultiplePrefix) { sts_state = TransportSecurityState::STSState(); pkp_state = TransportSecurityState::PKPState(); ct_state = TransportSecurityState::ExpectCTState(); - EXPECT_TRUE(GetStaticDomainState(&state, "expect-ct.example.com", &sts_state, - &pkp_state)); + EXPECT_FALSE(GetStaticDomainState(&state, "expect-ct.example.com", &sts_state, + &pkp_state)); EXPECT_TRUE(sts_state == TransportSecurityState::STSState()); EXPECT_TRUE(pkp_state == TransportSecurityState::PKPState()); EXPECT_TRUE(GetExpectCTState(&state, "expect-ct.example.com", &ct_state)); @@ -1746,9 +1748,35 @@ TEST_F(TransportSecurityStateTest, RequireCTConsultsDelegate) { } } -// Tests that the emergency disable flag causes CT to stop being required +enum class CTEmergencyDisableSwitchKind { + kFinchDrivenFeature, + kComponentUpdaterDrivenSwitch, +}; + +class CTEmergencyDisableTest + : public TransportSecurityStateTest, + public testing::WithParamInterface<CTEmergencyDisableSwitchKind> { + public: + void SetUp() override { + if (GetParam() == + CTEmergencyDisableSwitchKind::kComponentUpdaterDrivenSwitch) { + state_.SetCTEmergencyDisabled(true); + scoped_feature_list_.Init(); + } else { + ASSERT_EQ(GetParam(), CTEmergencyDisableSwitchKind::kFinchDrivenFeature); + scoped_feature_list_.InitAndDisableFeature( + TransportSecurityState::kCertificateTransparencyEnforcement); + } + } + + protected: + base::test::ScopedFeatureList scoped_feature_list_; + TransportSecurityState state_; +}; + +// Tests that the emergency disable flags cause CT to stop being required // regardless of host or delegate status. -TEST_F(TransportSecurityStateTest, CTEmergencyDisable) { +TEST_P(CTEmergencyDisableTest, CTEmergencyDisable) { using ::testing::_; using ::testing::Return; using CTRequirementLevel = @@ -1763,47 +1791,42 @@ TEST_F(TransportSecurityStateTest, CTEmergencyDisable) { hashes.push_back( HashValue(X509Certificate::CalculateFingerprint256(cert->cert_buffer()))); - TransportSecurityState state; - - // Set CT emergency disable flag. - state.SetCTEmergencyDisabled(true); - MockRequireCTDelegate always_require_delegate; EXPECT_CALL(always_require_delegate, IsCTRequiredForHost(_, _, _)) .WillRepeatedly(Return(CTRequirementLevel::REQUIRED)); - state.SetRequireCTDelegate(&always_require_delegate); + state_.SetRequireCTDelegate(&always_require_delegate); EXPECT_EQ(TransportSecurityState::CT_NOT_REQUIRED, - state.CheckCTRequirements( + state_.CheckCTRequirements( HostPortPair("www.example.com", 443), true, hashes, cert.get(), cert.get(), SignedCertificateTimestampAndStatusList(), TransportSecurityState::ENABLE_EXPECT_CT_REPORTS, ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS, NetworkIsolationKey())); EXPECT_EQ(TransportSecurityState::CT_NOT_REQUIRED, - state.CheckCTRequirements( + state_.CheckCTRequirements( HostPortPair("www.example.com", 443), true, hashes, cert.get(), cert.get(), SignedCertificateTimestampAndStatusList(), TransportSecurityState::ENABLE_EXPECT_CT_REPORTS, ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS, NetworkIsolationKey())); EXPECT_EQ(TransportSecurityState::CT_NOT_REQUIRED, - state.CheckCTRequirements( + state_.CheckCTRequirements( HostPortPair("www.example.com", 443), true, hashes, cert.get(), cert.get(), SignedCertificateTimestampAndStatusList(), TransportSecurityState::ENABLE_EXPECT_CT_REPORTS, ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS, NetworkIsolationKey())); EXPECT_EQ(TransportSecurityState::CT_NOT_REQUIRED, - state.CheckCTRequirements( + state_.CheckCTRequirements( HostPortPair("www.example.com", 443), true, hashes, cert.get(), cert.get(), SignedCertificateTimestampAndStatusList(), TransportSecurityState::ENABLE_EXPECT_CT_REPORTS, ct::CTPolicyCompliance::CT_POLICY_BUILD_NOT_TIMELY, NetworkIsolationKey())); - state.SetRequireCTDelegate(nullptr); + state_.SetRequireCTDelegate(nullptr); EXPECT_EQ(TransportSecurityState::CT_NOT_REQUIRED, - state.CheckCTRequirements( + state_.CheckCTRequirements( HostPortPair("www.example.com", 443), true, hashes, cert.get(), cert.get(), SignedCertificateTimestampAndStatusList(), TransportSecurityState::ENABLE_EXPECT_CT_REPORTS, @@ -1811,6 +1834,12 @@ TEST_F(TransportSecurityStateTest, CTEmergencyDisable) { NetworkIsolationKey())); } +INSTANTIATE_TEST_SUITE_P( + CTEmergencyDisable, + CTEmergencyDisableTest, + testing::Values(CTEmergencyDisableSwitchKind::kComponentUpdaterDrivenSwitch, + CTEmergencyDisableSwitchKind::kFinchDrivenFeature)); + // Tests that the if the CT log list last update time is set, it is used for // enforcement decisions. TEST_F(TransportSecurityStateTest, CTTimestampUpdate) { @@ -2668,8 +2697,7 @@ class TransportSecurityStateStaticTest : public TransportSecurityStateTest { static bool StaticShouldRedirect(const char* hostname) { TransportSecurityState state; TransportSecurityState::STSState sts_state; - TransportSecurityState::PKPState pkp_state; - return state.GetStaticDomainState(hostname, &sts_state, &pkp_state) && + return state.GetStaticSTSState(hostname, &sts_state) && sts_state.ShouldUpgradeToSSL(); } @@ -2677,15 +2705,15 @@ static bool HasStaticState(const char* hostname) { TransportSecurityState state; TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; - return state.GetStaticDomainState(hostname, &sts_state, &pkp_state); + return state.GetStaticSTSState(hostname, &sts_state) || + state.GetStaticPKPState(hostname, &pkp_state); } static bool HasStaticPublicKeyPins(const char* hostname) { TransportSecurityState state; TransportSecurityStateTest::EnableStaticPins(&state); - TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; - if (!state.GetStaticDomainState(hostname, &sts_state, &pkp_state)) + if (!state.GetStaticPKPState(hostname, &pkp_state)) return false; return pkp_state.HasPublicKeyPins(); @@ -2696,34 +2724,25 @@ static bool OnlyPinningInStaticState(const char* hostname) { TransportSecurityStateTest::EnableStaticPins(&state); TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; - if (!state.GetStaticDomainState(hostname, &sts_state, &pkp_state)) - return false; - - return (pkp_state.spki_hashes.size() > 0 || - pkp_state.bad_spki_hashes.size() > 0) && - !sts_state.ShouldUpgradeToSSL(); + return HasStaticPublicKeyPins(hostname) && !StaticShouldRedirect(hostname); } TEST_F(TransportSecurityStateStaticTest, EnableStaticPins) { TransportSecurityState state; - TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; EnableStaticPins(&state); - EXPECT_TRUE( - state.GetStaticDomainState("chrome.google.com", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("chrome.google.com", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); } TEST_F(TransportSecurityStateStaticTest, DisableStaticPins) { TransportSecurityState state; - TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; DisableStaticPins(&state); - EXPECT_TRUE( - state.GetStaticDomainState("chrome.google.com", &sts_state, &pkp_state)); + EXPECT_FALSE(state.GetStaticPKPState("chrome.google.com", &pkp_state)); EXPECT_TRUE(pkp_state.spki_hashes.empty()); } @@ -2771,24 +2790,25 @@ TEST_F(TransportSecurityStateStaticTest, PreloadedDomainSet) { // The domain wasn't being set, leading to a blank string in the // chrome://net-internals/#hsts UI. So test that. - EXPECT_TRUE( - state.GetStaticDomainState("market.android.com", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("market.android.com", &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("market.android.com", &sts_state)); EXPECT_EQ(sts_state.domain, "market.android.com"); EXPECT_EQ(pkp_state.domain, "market.android.com"); - EXPECT_TRUE(state.GetStaticDomainState("sub.market.android.com", &sts_state, - &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("sub.market.android.com", &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("sub.market.android.com", &sts_state)); EXPECT_EQ(sts_state.domain, "market.android.com"); EXPECT_EQ(pkp_state.domain, "market.android.com"); } TEST_F(TransportSecurityStateStaticTest, Preloaded) { TransportSecurityState state; + EnableStaticPins(&state); TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; // We do more extensive checks for the first domain. - EXPECT_TRUE( - state.GetStaticDomainState("www.paypal.com", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("www.paypal.com", &sts_state)); + EXPECT_FALSE(state.GetStaticPKPState("www.paypal.com", &pkp_state)); EXPECT_EQ(sts_state.upgrade_mode, TransportSecurityState::STSState::MODE_FORCE_HTTPS); EXPECT_FALSE(sts_state.include_subdomains); @@ -2837,13 +2857,14 @@ TEST_F(TransportSecurityStateStaticTest, Preloaded) { EXPECT_TRUE(StaticShouldRedirect("youtube.com")); // These domains used to be only HSTS when SNI was available. - EXPECT_TRUE(state.GetStaticDomainState("gmail.com", &sts_state, &pkp_state)); - EXPECT_TRUE( - state.GetStaticDomainState("www.gmail.com", &sts_state, &pkp_state)); - EXPECT_TRUE( - state.GetStaticDomainState("googlemail.com", &sts_state, &pkp_state)); - EXPECT_TRUE( - state.GetStaticDomainState("www.googlemail.com", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("gmail.com", &sts_state)); + EXPECT_TRUE(state.GetStaticPKPState("gmail.com", &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("www.gmail.com", &sts_state)); + EXPECT_TRUE(state.GetStaticPKPState("www.gmail.com", &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("googlemail.com", &sts_state)); + EXPECT_TRUE(state.GetStaticPKPState("googlemail.com", &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("www.googlemail.com", &sts_state)); + EXPECT_TRUE(state.GetStaticPKPState("www.googlemail.com", &pkp_state)); // fi.g.co should not force HTTPS because there are still HTTP-only services // on it. @@ -3002,8 +3023,8 @@ TEST_F(TransportSecurityStateStaticTest, PreloadedPins) { TransportSecurityState::PKPState pkp_state; // We do more extensive checks for the first domain. - EXPECT_TRUE( - state.GetStaticDomainState("www.paypal.com", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticSTSState("www.paypal.com", &sts_state)); + EXPECT_FALSE(state.GetStaticPKPState("www.paypal.com", &pkp_state)); EXPECT_EQ(sts_state.upgrade_mode, TransportSecurityState::STSState::MODE_FORCE_HTTPS); EXPECT_FALSE(sts_state.include_subdomains); @@ -3030,39 +3051,33 @@ TEST_F(TransportSecurityStateStaticTest, PreloadedPins) { EXPECT_TRUE(HasStaticPublicKeyPins("blog.torproject.org")); EXPECT_FALSE(HasStaticState("foo.torproject.org")); - EXPECT_TRUE( - state.GetStaticDomainState("torproject.org", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("torproject.org", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); - EXPECT_TRUE( - state.GetStaticDomainState("www.torproject.org", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("www.torproject.org", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); - EXPECT_TRUE(state.GetStaticDomainState("check.torproject.org", &sts_state, - &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("check.torproject.org", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); - EXPECT_TRUE(state.GetStaticDomainState("blog.torproject.org", &sts_state, - &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("blog.torproject.org", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); - // Check that Facebook subdomains have pinning but not HSTS. - EXPECT_TRUE( - state.GetStaticDomainState("facebook.com", &sts_state, &pkp_state)); + // Facebook has pinning and hsts on facebook.com, but only pinning on + // subdomains. + EXPECT_TRUE(state.GetStaticPKPState("facebook.com", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); EXPECT_TRUE(StaticShouldRedirect("facebook.com")); - EXPECT_TRUE( - state.GetStaticDomainState("foo.facebook.com", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("foo.facebook.com", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); EXPECT_FALSE(StaticShouldRedirect("foo.facebook.com")); - EXPECT_TRUE( - state.GetStaticDomainState("www.facebook.com", &sts_state, &pkp_state)); + // www.facebook.com and subdomains have both pinning and hsts. + EXPECT_TRUE(state.GetStaticPKPState("www.facebook.com", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); EXPECT_TRUE(StaticShouldRedirect("www.facebook.com")); - EXPECT_TRUE(state.GetStaticDomainState("foo.www.facebook.com", &sts_state, - &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("foo.www.facebook.com", &pkp_state)); EXPECT_FALSE(pkp_state.spki_hashes.empty()); EXPECT_TRUE(StaticShouldRedirect("foo.www.facebook.com")); } @@ -3070,11 +3085,9 @@ TEST_F(TransportSecurityStateStaticTest, PreloadedPins) { TEST_F(TransportSecurityStateStaticTest, BuiltinCertPins) { TransportSecurityState state; EnableStaticPins(&state); - TransportSecurityState::STSState sts_state; TransportSecurityState::PKPState pkp_state; - EXPECT_TRUE( - state.GetStaticDomainState("chrome.google.com", &sts_state, &pkp_state)); + EXPECT_TRUE(state.GetStaticPKPState("chrome.google.com", &pkp_state)); EXPECT_TRUE(HasStaticPublicKeyPins("chrome.google.com")); HashValueVector hashes; @@ -3868,4 +3881,225 @@ TEST_F(TransportSecurityStateTest, PruneExpectCTNetworkIsolationKeyLimit) { } } +TEST_F(TransportSecurityStateTest, UpdateKeyPinsListValidPin) { + HostPortPair host_port_pair(kHost, kPort); + GURL report_uri(kReportUri); + NetworkIsolationKey network_isolation_key = + NetworkIsolationKey::CreateTransient(); + // Two dummy certs to use as the server-sent and validated chains. The + // contents don't matter. + scoped_refptr<X509Certificate> cert1 = + ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"); + ASSERT_TRUE(cert1); + scoped_refptr<X509Certificate> cert2 = + ImportCertFromFile(GetTestCertsDirectory(), "expired_cert.pem"); + ASSERT_TRUE(cert2); + + HashValueVector bad_hashes; + + for (size_t i = 0; kBadPath[i]; i++) + EXPECT_TRUE(AddHash(kBadPath[i], &bad_hashes)); + + TransportSecurityState state; + EnableStaticPins(&state); + std::string unused_failure_log; + + // Prior to updating the list, bad_hashes should be rejected. + EXPECT_EQ(TransportSecurityState::PKPStatus::VIOLATED, + state.CheckPublicKeyPins( + host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); + + // Update the pins list, adding bad_hashes to the accepted hashes for this + // host. + std::vector<std::vector<uint8_t>> accepted_hashes; + for (size_t i = 0; kBadPath[i]; i++) { + HashValue hash; + ASSERT_TRUE(hash.FromString(kBadPath[i])); + accepted_hashes.emplace_back(hash.data(), hash.data() + hash.size()); + } + TransportSecurityState::PinSet test_pinset( + /*name=*/"test", + /*static_spki_hashes=*/accepted_hashes, + /*bad_static_spki_hashes=*/{}, + /*report_uri=*/kReportUri); + TransportSecurityState::PinSetInfo test_pinsetinfo( + /*hostname=*/kHost, /*pinset_name=*/"test", + /*include_subdomains=*/false); + state.UpdatePinList({test_pinset}, {test_pinsetinfo}, base::Time::Now()); + + // Hashes should now be accepted. + EXPECT_EQ(TransportSecurityState::PKPStatus::OK, + state.CheckPublicKeyPins( + host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); +} + +TEST_F(TransportSecurityStateTest, UpdateKeyPinsListNotValidPin) { + HostPortPair host_port_pair(kHost, kPort); + GURL report_uri(kReportUri); + NetworkIsolationKey network_isolation_key = + NetworkIsolationKey::CreateTransient(); + // Two dummy certs to use as the server-sent and validated chains. The + // contents don't matter. + scoped_refptr<X509Certificate> cert1 = + ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"); + ASSERT_TRUE(cert1); + scoped_refptr<X509Certificate> cert2 = + ImportCertFromFile(GetTestCertsDirectory(), "expired_cert.pem"); + ASSERT_TRUE(cert2); + + HashValueVector good_hashes; + + for (size_t i = 0; kGoodPath[i]; i++) + EXPECT_TRUE(AddHash(kGoodPath[i], &good_hashes)); + + TransportSecurityState state; + EnableStaticPins(&state); + std::string unused_failure_log; + + // Prior to updating the list, good_hashes should be accepted + EXPECT_EQ(TransportSecurityState::PKPStatus::OK, + state.CheckPublicKeyPins( + host_port_pair, true, good_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); + + // Update the pins list, adding good_hashes to the rejected hashes for this + // host. + std::vector<std::vector<uint8_t>> rejected_hashes; + for (size_t i = 0; kGoodPath[i]; i++) { + HashValue hash; + ASSERT_TRUE(hash.FromString(kGoodPath[i])); + rejected_hashes.emplace_back(hash.data(), hash.data() + hash.size()); + } + TransportSecurityState::PinSet test_pinset( + /*name=*/"test", + /*static_spki_hashes=*/{}, + /*bad_static_spki_hashes=*/rejected_hashes, + /*report_uri=*/kReportUri); + TransportSecurityState::PinSetInfo test_pinsetinfo( + /*hostname=*/kHost, /* pinset_name=*/"test", + /*include_subdomains=*/false); + state.UpdatePinList({test_pinset}, {test_pinsetinfo}, base::Time::Now()); + + // Hashes should now be rejected. + EXPECT_EQ(TransportSecurityState::PKPStatus::VIOLATED, + state.CheckPublicKeyPins( + host_port_pair, true, good_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); +} + +TEST_F(TransportSecurityStateTest, UpdateKeyPinsEmptyList) { + HostPortPair host_port_pair(kHost, kPort); + GURL report_uri(kReportUri); + NetworkIsolationKey network_isolation_key = + NetworkIsolationKey::CreateTransient(); + // Two dummy certs to use as the server-sent and validated chains. The + // contents don't matter. + scoped_refptr<X509Certificate> cert1 = + ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"); + ASSERT_TRUE(cert1); + scoped_refptr<X509Certificate> cert2 = + ImportCertFromFile(GetTestCertsDirectory(), "expired_cert.pem"); + ASSERT_TRUE(cert2); + + HashValueVector bad_hashes; + + for (size_t i = 0; kBadPath[i]; i++) + EXPECT_TRUE(AddHash(kBadPath[i], &bad_hashes)); + + TransportSecurityState state; + EnableStaticPins(&state); + std::string unused_failure_log; + + // Prior to updating the list, bad_hashes should be rejected. + EXPECT_EQ(TransportSecurityState::PKPStatus::VIOLATED, + state.CheckPublicKeyPins( + host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); + + // Update the pins list with an empty list. + state.UpdatePinList({}, {}, base::Time::Now()); + + // Hashes should now be accepted. + EXPECT_EQ(TransportSecurityState::PKPStatus::OK, + state.CheckPublicKeyPins( + host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); +} + +TEST_F(TransportSecurityStateTest, UpdateKeyPinsListTimestamp) { + HostPortPair host_port_pair(kHost, kPort); + GURL report_uri(kReportUri); + NetworkIsolationKey network_isolation_key = + NetworkIsolationKey::CreateTransient(); + // Two dummy certs to use as the server-sent and validated chains. The + // contents don't matter. + scoped_refptr<X509Certificate> cert1 = + ImportCertFromFile(GetTestCertsDirectory(), "ok_cert.pem"); + ASSERT_TRUE(cert1); + scoped_refptr<X509Certificate> cert2 = + ImportCertFromFile(GetTestCertsDirectory(), "expired_cert.pem"); + ASSERT_TRUE(cert2); + + HashValueVector bad_hashes; + + for (size_t i = 0; kBadPath[i]; i++) + EXPECT_TRUE(AddHash(kBadPath[i], &bad_hashes)); + + TransportSecurityState state; + EnableStaticPins(&state); + std::string unused_failure_log; + + // Prior to updating the list, bad_hashes should be rejected. + EXPECT_EQ(TransportSecurityState::PKPStatus::VIOLATED, + state.CheckPublicKeyPins( + host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); + + // Update the pins list, with bad hashes as rejected, but a timestamp >70 days + // old. + std::vector<std::vector<uint8_t>> rejected_hashes; + for (size_t i = 0; kBadPath[i]; i++) { + HashValue hash; + ASSERT_TRUE(hash.FromString(kBadPath[i])); + rejected_hashes.emplace_back(hash.data(), hash.data() + hash.size()); + } + TransportSecurityState::PinSet test_pinset( + /*name=*/"test", + /*static_spki_hashes=*/{}, + /*bad_static_spki_hashes=*/rejected_hashes, + /*report_uri=*/kReportUri); + TransportSecurityState::PinSetInfo test_pinsetinfo( + /*hostname=*/kHost, /* pinset_name=*/"test", + /*include_subdomains=*/false); + state.UpdatePinList({test_pinset}, {test_pinsetinfo}, + base::Time::Now() - base::Days(70)); + + // Hashes should now be accepted. + EXPECT_EQ(TransportSecurityState::PKPStatus::OK, + state.CheckPublicKeyPins( + host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); + + // Update the pins list again, with a timestamp <70 days old. + state.UpdatePinList({test_pinset}, {test_pinsetinfo}, + base::Time::Now() - base::Days(69)); + + // Hashes should now be rejected. + EXPECT_EQ(TransportSecurityState::PKPStatus::VIOLATED, + state.CheckPublicKeyPins( + host_port_pair, true, bad_hashes, cert1.get(), cert2.get(), + TransportSecurityState::ENABLE_PIN_REPORTS, + network_isolation_key, &unused_failure_log)); +} + } // namespace net diff --git a/chromium/net/http/url_security_manager_unittest.cc b/chromium/net/http/url_security_manager_unittest.cc index 834f559fdf5..672425027c3 100644 --- a/chromium/net/http/url_security_manager_unittest.cc +++ b/chromium/net/http/url_security_manager_unittest.cc @@ -6,7 +6,6 @@ #include <utility> -#include "base/cxx17_backports.h" #include "net/base/net_errors.h" #include "net/http/http_auth_filter.h" #include "testing/gtest/include/gtest/gtest.h" @@ -56,7 +55,7 @@ TEST(URLSecurityManager, UseDefaultCredentials) { url_security_manager->SetDefaultAllowlist(std::move(auth_filter)); ASSERT_TRUE(url_security_manager.get()); - for (size_t i = 0; i < base::size(kTestDataList); ++i) { + for (size_t i = 0; i < std::size(kTestDataList); ++i) { url::SchemeHostPort scheme_host_port( GURL(kTestDataList[i].scheme_host_port)); bool can_use_default = @@ -78,7 +77,7 @@ TEST(URLSecurityManager, CanDelegate) { url_security_manager->SetDelegateAllowlist(std::move(auth_filter)); ASSERT_TRUE(url_security_manager.get()); - for (size_t i = 0; i < base::size(kTestDataList); ++i) { + for (size_t i = 0; i < std::size(kTestDataList); ++i) { url::SchemeHostPort scheme_host_port( GURL(kTestDataList[i].scheme_host_port)); bool can_delegate = url_security_manager->CanDelegate(scheme_host_port); @@ -94,7 +93,7 @@ TEST(URLSecurityManager, CanDelegate_NoAllowlist) { URLSecurityManager::Create()); ASSERT_TRUE(url_security_manager.get()); - for (size_t i = 0; i < base::size(kTestDataList); ++i) { + for (size_t i = 0; i < std::size(kTestDataList); ++i) { url::SchemeHostPort scheme_host_port( GURL(kTestDataList[i].scheme_host_port)); bool can_delegate = url_security_manager->CanDelegate(scheme_host_port); |