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