xref: /aosp_15_r20/external/cronet/net/http/alternative_service.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/http/alternative_service.h"
6 
7 #include "base/check_op.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/metrics/histogram_macros_local.h"
10 #include "base/notreached.h"
11 #include "base/strings/stringprintf.h"
12 #include "net/base/port_util.h"
13 #include "net/third_party/quiche/src/quiche/quic/core/http/spdy_utils.h"
14 
15 namespace net {
16 
HistogramAlternateProtocolUsage(AlternateProtocolUsage usage,bool is_google_host)17 void HistogramAlternateProtocolUsage(AlternateProtocolUsage usage,
18                                      bool is_google_host) {
19     UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage", usage,
20                               ALTERNATE_PROTOCOL_USAGE_MAX);
21     if (is_google_host) {
22       UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsageGoogle", usage,
23                                 ALTERNATE_PROTOCOL_USAGE_MAX);
24     }
25 }
26 
HistogramBrokenAlternateProtocolLocation(BrokenAlternateProtocolLocation location)27 void HistogramBrokenAlternateProtocolLocation(
28     BrokenAlternateProtocolLocation location) {
29   UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolBrokenLocation", location,
30                             BROKEN_ALTERNATE_PROTOCOL_LOCATION_MAX);
31 }
32 
IsAlternateProtocolValid(NextProto protocol)33 bool IsAlternateProtocolValid(NextProto protocol) {
34   switch (protocol) {
35     case kProtoUnknown:
36       return false;
37     case kProtoHTTP11:
38       return false;
39     case kProtoHTTP2:
40       return true;
41     case kProtoQUIC:
42       return true;
43   }
44   NOTREACHED();
45   return false;
46 }
47 
IsProtocolEnabled(NextProto protocol,bool is_http2_enabled,bool is_quic_enabled)48 bool IsProtocolEnabled(NextProto protocol,
49                        bool is_http2_enabled,
50                        bool is_quic_enabled) {
51   switch (protocol) {
52     case kProtoUnknown:
53       NOTREACHED();
54       return false;
55     case kProtoHTTP11:
56       return true;
57     case kProtoHTTP2:
58       return is_http2_enabled;
59     case kProtoQUIC:
60       return is_quic_enabled;
61   }
62   NOTREACHED();
63   return false;
64 }
65 
66 // static
67 AlternativeServiceInfo
CreateHttp2AlternativeServiceInfo(const AlternativeService & alternative_service,base::Time expiration)68 AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
69     const AlternativeService& alternative_service,
70     base::Time expiration) {
71   DCHECK_EQ(alternative_service.protocol, kProtoHTTP2);
72   return AlternativeServiceInfo(alternative_service, expiration,
73                                 quic::ParsedQuicVersionVector());
74 }
75 
76 // static
CreateQuicAlternativeServiceInfo(const AlternativeService & alternative_service,base::Time expiration,const quic::ParsedQuicVersionVector & advertised_versions)77 AlternativeServiceInfo AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
78     const AlternativeService& alternative_service,
79     base::Time expiration,
80     const quic::ParsedQuicVersionVector& advertised_versions) {
81   DCHECK_EQ(alternative_service.protocol, kProtoQUIC);
82   return AlternativeServiceInfo(alternative_service, expiration,
83                                 advertised_versions);
84 }
85 
AlternativeServiceInfo()86 AlternativeServiceInfo::AlternativeServiceInfo() : alternative_service_() {}
87 
88 AlternativeServiceInfo::~AlternativeServiceInfo() = default;
89 
AlternativeServiceInfo(const AlternativeService & alternative_service,base::Time expiration,const quic::ParsedQuicVersionVector & advertised_versions)90 AlternativeServiceInfo::AlternativeServiceInfo(
91     const AlternativeService& alternative_service,
92     base::Time expiration,
93     const quic::ParsedQuicVersionVector& advertised_versions)
94     : alternative_service_(alternative_service), expiration_(expiration) {
95   if (alternative_service_.protocol == kProtoQUIC) {
96     advertised_versions_ = advertised_versions;
97   }
98 }
99 
100 AlternativeServiceInfo::AlternativeServiceInfo(
101     const AlternativeServiceInfo& alternative_service_info) = default;
102 
103 AlternativeServiceInfo& AlternativeServiceInfo::operator=(
104     const AlternativeServiceInfo& alternative_service_info) = default;
105 
ToString() const106 std::string AlternativeService::ToString() const {
107   return base::StringPrintf("%s %s:%d", NextProtoToString(protocol),
108                             host.c_str(), port);
109 }
110 
ToString() const111 std::string AlternativeServiceInfo::ToString() const {
112   // NOTE: Cannot use `base::UnlocalizedTimeFormatWithPattern()` since
113   // `net/DEPS` disallows `base/i18n`.
114   base::Time::Exploded exploded;
115   expiration_.LocalExplode(&exploded);
116   return base::StringPrintf(
117       "%s, expires %04d-%02d-%02d %02d:%02d:%02d",
118       alternative_service_.ToString().c_str(), exploded.year, exploded.month,
119       exploded.day_of_month, exploded.hour, exploded.minute, exploded.second);
120 }
121 
122 // static
TransportVersionLessThan(const quic::ParsedQuicVersion & lhs,const quic::ParsedQuicVersion & rhs)123 bool AlternativeServiceInfo::TransportVersionLessThan(
124     const quic::ParsedQuicVersion& lhs,
125     const quic::ParsedQuicVersion& rhs) {
126   return lhs.transport_version < rhs.transport_version;
127 }
128 
operator <<(std::ostream & os,const AlternativeService & alternative_service)129 std::ostream& operator<<(std::ostream& os,
130                          const AlternativeService& alternative_service) {
131   os << alternative_service.ToString();
132   return os;
133 }
134 
ProcessAlternativeServices(const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector & alternative_service_vector,bool is_http2_enabled,bool is_quic_enabled,const quic::ParsedQuicVersionVector & supported_quic_versions)135 AlternativeServiceInfoVector ProcessAlternativeServices(
136     const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector&
137         alternative_service_vector,
138     bool is_http2_enabled,
139     bool is_quic_enabled,
140     const quic::ParsedQuicVersionVector& supported_quic_versions) {
141   // Convert spdy::SpdyAltSvcWireFormat::AlternativeService entries
142   // to net::AlternativeServiceInfo.
143   AlternativeServiceInfoVector alternative_service_info_vector;
144   for (const spdy::SpdyAltSvcWireFormat::AlternativeService&
145            alternative_service_entry : alternative_service_vector) {
146     if (!IsPortValid(alternative_service_entry.port))
147       continue;
148 
149     NextProto protocol =
150         NextProtoFromString(alternative_service_entry.protocol_id);
151     quic::ParsedQuicVersionVector advertised_versions;
152     if (protocol == kProtoQUIC) {
153       continue;  // Ignore legacy QUIC alt-svc advertisements.
154     } else if (!IsAlternateProtocolValid(protocol)) {
155       quic::ParsedQuicVersion version =
156           quic::SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
157               alternative_service_entry, supported_quic_versions);
158       if (version == quic::ParsedQuicVersion::Unsupported()) {
159         continue;
160       }
161       protocol = kProtoQUIC;
162       advertised_versions = {version};
163     }
164     if (!IsAlternateProtocolValid(protocol) ||
165         !IsProtocolEnabled(protocol, is_http2_enabled, is_quic_enabled)) {
166       continue;
167     }
168 
169     AlternativeService alternative_service(protocol,
170                                            alternative_service_entry.host,
171                                            alternative_service_entry.port);
172     base::Time expiration =
173         base::Time::Now() +
174         base::Seconds(alternative_service_entry.max_age_seconds);
175     AlternativeServiceInfo alternative_service_info;
176     if (protocol == kProtoQUIC) {
177       alternative_service_info =
178           AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
179               alternative_service, expiration, advertised_versions);
180     } else {
181       alternative_service_info =
182           AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
183               alternative_service, expiration);
184     }
185     alternative_service_info_vector.push_back(alternative_service_info);
186   }
187   return alternative_service_info_vector;
188 }
189 
190 }  // namespace net
191