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 base::Time::Exploded exploded;
113 expiration_.LocalExplode(&exploded);
114 return base::StringPrintf(
115 "%s, expires %04d-%02d-%02d %02d:%02d:%02d",
116 alternative_service_.ToString().c_str(), exploded.year, exploded.month,
117 exploded.day_of_month, exploded.hour, exploded.minute, exploded.second);
118 }
119
120 // static
TransportVersionLessThan(const quic::ParsedQuicVersion & lhs,const quic::ParsedQuicVersion & rhs)121 bool AlternativeServiceInfo::TransportVersionLessThan(
122 const quic::ParsedQuicVersion& lhs,
123 const quic::ParsedQuicVersion& rhs) {
124 return lhs.transport_version < rhs.transport_version;
125 }
126
operator <<(std::ostream & os,const AlternativeService & alternative_service)127 std::ostream& operator<<(std::ostream& os,
128 const AlternativeService& alternative_service) {
129 os << alternative_service.ToString();
130 return os;
131 }
132
ProcessAlternativeServices(const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector & alternative_service_vector,bool is_http2_enabled,bool is_quic_enabled,const quic::ParsedQuicVersionVector & supported_quic_versions)133 AlternativeServiceInfoVector ProcessAlternativeServices(
134 const spdy::SpdyAltSvcWireFormat::AlternativeServiceVector&
135 alternative_service_vector,
136 bool is_http2_enabled,
137 bool is_quic_enabled,
138 const quic::ParsedQuicVersionVector& supported_quic_versions) {
139 // Convert spdy::SpdyAltSvcWireFormat::AlternativeService entries
140 // to net::AlternativeServiceInfo.
141 AlternativeServiceInfoVector alternative_service_info_vector;
142 for (const spdy::SpdyAltSvcWireFormat::AlternativeService&
143 alternative_service_entry : alternative_service_vector) {
144 if (!IsPortValid(alternative_service_entry.port))
145 continue;
146
147 NextProto protocol =
148 NextProtoFromString(alternative_service_entry.protocol_id);
149 quic::ParsedQuicVersionVector advertised_versions;
150 if (protocol == kProtoQUIC) {
151 continue; // Ignore legacy QUIC alt-svc advertisements.
152 } else if (!IsAlternateProtocolValid(protocol)) {
153 quic::ParsedQuicVersion version =
154 quic::SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
155 alternative_service_entry, supported_quic_versions);
156 if (version == quic::ParsedQuicVersion::Unsupported()) {
157 continue;
158 }
159 protocol = kProtoQUIC;
160 advertised_versions = {version};
161 }
162 if (!IsAlternateProtocolValid(protocol) ||
163 !IsProtocolEnabled(protocol, is_http2_enabled, is_quic_enabled)) {
164 continue;
165 }
166
167 AlternativeService alternative_service(protocol,
168 alternative_service_entry.host,
169 alternative_service_entry.port);
170 base::Time expiration =
171 base::Time::Now() +
172 base::Seconds(alternative_service_entry.max_age_seconds);
173 AlternativeServiceInfo alternative_service_info;
174 if (protocol == kProtoQUIC) {
175 alternative_service_info =
176 AlternativeServiceInfo::CreateQuicAlternativeServiceInfo(
177 alternative_service, expiration, advertised_versions);
178 } else {
179 alternative_service_info =
180 AlternativeServiceInfo::CreateHttp2AlternativeServiceInfo(
181 alternative_service, expiration);
182 }
183 alternative_service_info_vector.push_back(alternative_service_info);
184 }
185 return alternative_service_info_vector;
186 }
187
188 } // namespace net
189