• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "net/third_party/quiche/src/quiche/quic/core/quic_versions.h"
15 
16 namespace net {
17 
HistogramAlternateProtocolUsage(AlternateProtocolUsage usage,bool is_google_host)18 void HistogramAlternateProtocolUsage(AlternateProtocolUsage usage,
19                                      bool is_google_host) {
20   UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsage", usage,
21                             ALTERNATE_PROTOCOL_USAGE_MAX);
22   if (is_google_host) {
23     UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolUsageGoogle", usage,
24                               ALTERNATE_PROTOCOL_USAGE_MAX);
25   }
26 }
27 
HistogramBrokenAlternateProtocolLocation(BrokenAlternateProtocolLocation location)28 void HistogramBrokenAlternateProtocolLocation(
29     BrokenAlternateProtocolLocation location) {
30   UMA_HISTOGRAM_ENUMERATION("Net.AlternateProtocolBrokenLocation", location,
31                             BROKEN_ALTERNATE_PROTOCOL_LOCATION_MAX);
32 }
33 
IsAlternateProtocolValid(NextProto protocol)34 bool IsAlternateProtocolValid(NextProto protocol) {
35   switch (protocol) {
36     case kProtoUnknown:
37       return false;
38     case kProtoHTTP11:
39       return false;
40     case kProtoHTTP2:
41       return true;
42     case kProtoQUIC:
43       return true;
44   }
45   NOTREACHED();
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     case kProtoHTTP11:
55       return true;
56     case kProtoHTTP2:
57       return is_http2_enabled;
58     case kProtoQUIC:
59       return is_quic_enabled;
60   }
61   NOTREACHED();
62 }
63 
AlternativeService(NextProto protocol,std::string_view host,uint16_t port)64 AlternativeService::AlternativeService(NextProto protocol,
65                                        std::string_view host,
66                                        uint16_t port)
67     : protocol(protocol), host(host), port(port) {}
68 
AlternativeService(NextProto protocol,const HostPortPair & host_port_pair)69 AlternativeService::AlternativeService(NextProto protocol,
70                                        const HostPortPair& host_port_pair)
71     : AlternativeService(protocol,
72                          host_port_pair.host(),
73                          host_port_pair.port()) {}
74 
75 AlternativeService::AlternativeService(
76     const AlternativeService& alternative_service) = default;
77 AlternativeService::AlternativeService(AlternativeService&&) noexcept = default;
78 
79 AlternativeService& AlternativeService::operator=(
80     const AlternativeService& alternative_service) = default;
81 AlternativeService& AlternativeService::operator=(AlternativeService&&) =
82     default;
83 
GetHostPortPair() const84 HostPortPair AlternativeService::GetHostPortPair() const {
85   return HostPortPair(host, port);
86 }
87 
88 std::strong_ordering AlternativeService::operator<=>(
89     const AlternativeService& other) const = default;
90 
ToString() const91 std::string AlternativeService::ToString() const {
92   return base::StringPrintf("%s %s:%d", NextProtoToString(protocol),
93                             host.c_str(), port);
94 }
95 
operator <<(std::ostream & os,const AlternativeService & alternative_service)96 std::ostream& operator<<(std::ostream& os,
97                          const AlternativeService& alternative_service) {
98   os << alternative_service.ToString();
99   return os;
100 }
101 
102 // static
103 AlternativeServiceInfo
CreateHttp2AlternativeServiceInfo(const AlternativeService & alternative_service,base::Time expiration)104 AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
105     const AlternativeService& alternative_service,
106     base::Time expiration) {
107   DCHECK_EQ(alternative_service.protocol, kProtoHTTP2);
108   return AlternativeServiceInfo(alternative_service, expiration,
109                                 quic::ParsedQuicVersionVector());
110 }
111 
112 // static
CreateQuicAlternativeServiceInfo(const AlternativeService & alternative_service,base::Time expiration,const quic::ParsedQuicVersionVector & advertised_versions)113 AlternativeServiceInfo AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
114     const AlternativeService& alternative_service,
115     base::Time expiration,
116     const quic::ParsedQuicVersionVector& advertised_versions) {
117   DCHECK_EQ(alternative_service.protocol, kProtoQUIC);
118   return AlternativeServiceInfo(alternative_service, expiration,
119                                 advertised_versions);
120 }
121 
122 AlternativeServiceInfo::AlternativeServiceInfo() = default;
123 
124 AlternativeServiceInfo::AlternativeServiceInfo(
125     const AlternativeServiceInfo& alternative_service_info) = default;
126 AlternativeServiceInfo::AlternativeServiceInfo(
127     AlternativeServiceInfo&&) noexcept = default;
128 
129 AlternativeServiceInfo& AlternativeServiceInfo::operator=(
130     AlternativeServiceInfo&&) = default;
131 AlternativeServiceInfo& AlternativeServiceInfo::operator=(
132     const AlternativeServiceInfo& alternative_service_info) = default;
133 
134 AlternativeServiceInfo::~AlternativeServiceInfo() = default;
135 
136 bool AlternativeServiceInfo::operator==(
137     const AlternativeServiceInfo& other) const = default;
138 
ToString() const139 std::string AlternativeServiceInfo::ToString() const {
140   // NOTE: Cannot use `base::UnlocalizedTimeFormatWithPattern()` since
141   // `net/DEPS` disallows `base/i18n`.
142   base::Time::Exploded exploded;
143   expiration_.LocalExplode(&exploded);
144   return base::StringPrintf(
145       "%s, expires %04d-%02d-%02d %02d:%02d:%02d",
146       alternative_service_.ToString().c_str(), exploded.year, exploded.month,
147       exploded.day_of_month, exploded.hour, exploded.minute, exploded.second);
148 }
149 
SetAdvertisedVersions(const quic::ParsedQuicVersionVector & advertised_versions)150 void AlternativeServiceInfo::SetAdvertisedVersions(
151     const quic::ParsedQuicVersionVector& advertised_versions) {
152   if (alternative_service_.protocol != kProtoQUIC) {
153     return;
154   }
155 
156   advertised_versions_ = advertised_versions;
157   std::ranges::sort(advertised_versions_, {},
158                     &quic::ParsedQuicVersion::transport_version);
159 }
160 
ProcessAlternativeServices(const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector & alternative_service_vector,bool is_http2_enabled,bool is_quic_enabled,const quic::ParsedQuicVersionVector & supported_quic_versions)161 AlternativeServiceInfoVector ProcessAlternativeServices(
162     const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector&
163         alternative_service_vector,
164     bool is_http2_enabled,
165     bool is_quic_enabled,
166     const quic::ParsedQuicVersionVector& supported_quic_versions) {
167   // Convert spdy::SpdyAltSvcWireFormat::AlternativeService entries
168   // to AlternativeServiceInfo.
169   AlternativeServiceInfoVector alternative_service_info_vector;
170   for (const spdy::SpdyAltSvcWireFormat::AlternativeService&
171            alternative_service_entry : alternative_service_vector) {
172     if (!IsPortValid(alternative_service_entry.port)) {
173       continue;
174     }
175 
176     NextProto protocol =
177         NextProtoFromString(alternative_service_entry.protocol_id);
178     quic::ParsedQuicVersionVector advertised_versions;
179     if (protocol == kProtoQUIC) {
180       continue;  // Ignore legacy QUIC alt-svc advertisements.
181     } else if (!IsAlternateProtocolValid(protocol)) {
182       quic::ParsedQuicVersion version =
183           quic::SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
184               alternative_service_entry, supported_quic_versions);
185       if (version == quic::ParsedQuicVersion::Unsupported()) {
186         continue;
187       }
188       protocol = kProtoQUIC;
189       advertised_versions = {version};
190     }
191     if (!IsAlternateProtocolValid(protocol) ||
192         !IsProtocolEnabled(protocol, is_http2_enabled, is_quic_enabled)) {
193       continue;
194     }
195 
196     AlternativeService alternative_service(protocol,
197                                            alternative_service_entry.host,
198                                            alternative_service_entry.port);
199     base::Time expiration =
200         base::Time::Now() +
201         base::Seconds(alternative_service_entry.max_age_seconds);
202     AlternativeServiceInfo alternative_service_info;
203     if (protocol == kProtoQUIC) {
204       alternative_service_info =
205           AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
206               alternative_service, expiration, advertised_versions);
207     } else {
208       alternative_service_info =
209           AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
210               alternative_service, expiration);
211     }
212     alternative_service_info_vector.push_back(alternative_service_info);
213   }
214   return alternative_service_info_vector;
215 }
216 
AlternativeServiceInfo(const AlternativeService & alternative_service,base::Time expiration,const quic::ParsedQuicVersionVector & advertised_versions)217 AlternativeServiceInfo::AlternativeServiceInfo(
218     const AlternativeService& alternative_service,
219     base::Time expiration,
220     const quic::ParsedQuicVersionVector& advertised_versions)
221     : alternative_service_(alternative_service), expiration_(expiration) {
222   if (alternative_service_.protocol == kProtoQUIC) {
223     advertised_versions_ = advertised_versions;
224   }
225 }
226 
227 }  // namespace net
228