1 // Copyright 2014 The Chromium Authors. All rights reserved.
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 "components/domain_reliability/util.h"
6
7 #include <stddef.h>
8
9 #include "base/callback.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/notreached.h"
12 #include "base/stl_util.h"
13 #include "base/time/time.h"
14 #include "base/timer/timer.h"
15 #include "net/base/net_errors.h"
16
17 namespace domain_reliability {
18
19 namespace {
20
21 const struct NetErrorMapping {
22 int net_error;
23 const char* beacon_status;
24 } net_error_map[] = {
25 {net::OK, "ok"},
26 {net::ERR_ABORTED, "aborted"},
27 {net::ERR_TIMED_OUT, "tcp.connection.timed_out"},
28 {net::ERR_CONNECTION_CLOSED, "tcp.connection.closed"},
29 {net::ERR_CONNECTION_RESET, "tcp.connection.reset"},
30 {net::ERR_CONNECTION_REFUSED, "tcp.connection.refused"},
31 {net::ERR_CONNECTION_ABORTED, "tcp.connection.aborted"},
32 {net::ERR_CONNECTION_FAILED, "tcp.connection.failed"},
33 {net::ERR_NAME_NOT_RESOLVED, "dns"},
34 {net::ERR_SSL_PROTOCOL_ERROR, "ssl.protocol.error"},
35 {net::ERR_ADDRESS_INVALID, "tcp.connection.address_invalid"},
36 {net::ERR_ADDRESS_UNREACHABLE, "tcp.connection.address_unreachable"},
37 {net::ERR_CONNECTION_TIMED_OUT, "tcp.connection.timed_out"},
38 {net::ERR_NAME_RESOLUTION_FAILED, "dns"},
39 {net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN,
40 "ssl.cert.pinned_key_not_in_cert_chain"},
41 {net::ERR_CERT_COMMON_NAME_INVALID, "ssl.cert.name_invalid"},
42 {net::ERR_CERT_DATE_INVALID, "ssl.cert.date_invalid"},
43 {net::ERR_CERT_AUTHORITY_INVALID, "ssl.cert.authority_invalid"},
44 {net::ERR_CERT_KNOWN_INTERCEPTION_BLOCKED, "ssl.cert.authority_invalid"},
45 {net::ERR_CERT_REVOKED, "ssl.cert.revoked"},
46 {net::ERR_CERT_INVALID, "ssl.cert.invalid"},
47 {net::ERR_EMPTY_RESPONSE, "http.response.empty"},
48 {net::ERR_HTTP2_PING_FAILED, "spdy.ping_failed"},
49 {net::ERR_HTTP2_PROTOCOL_ERROR, "spdy.protocol"},
50 {net::ERR_QUIC_PROTOCOL_ERROR, "quic.protocol"},
51 {net::ERR_DNS_MALFORMED_RESPONSE, "dns.protocol"},
52 {net::ERR_DNS_SERVER_FAILED, "dns.server"},
53 {net::ERR_DNS_TIMED_OUT, "dns.timed_out"},
54 {net::ERR_INSECURE_RESPONSE, "ssl"},
55 {net::ERR_CONTENT_LENGTH_MISMATCH, "http.response.content_length_mismatch"},
56 {net::ERR_INCOMPLETE_CHUNKED_ENCODING,
57 "http.response.incomplete_chunked_encoding"},
58 {net::ERR_SSL_VERSION_OR_CIPHER_MISMATCH, "ssl.version_or_cipher_mismatch"},
59 {net::ERR_BAD_SSL_CLIENT_AUTH_CERT, "ssl.bad_client_auth_cert"},
60 {net::ERR_INVALID_CHUNKED_ENCODING,
61 "http.response.invalid_chunked_encoding"},
62 {net::ERR_RESPONSE_HEADERS_TRUNCATED, "http.response.headers.truncated"},
63 {net::ERR_REQUEST_RANGE_NOT_SATISFIABLE,
64 "http.request.range_not_satisfiable"},
65 {net::ERR_INVALID_RESPONSE, "http.response.invalid"},
66 {net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION,
67 "http.response.headers.multiple_content_disposition"},
68 {net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH,
69 "http.response.headers.multiple_content_length"},
70 {net::ERR_SSL_UNRECOGNIZED_NAME_ALERT, "ssl.unrecognized_name_alert"}};
71
CanReportFullBeaconURLToCollector(const GURL & beacon_url,const GURL & collector_url)72 bool CanReportFullBeaconURLToCollector(const GURL& beacon_url,
73 const GURL& collector_url) {
74 return beacon_url.GetOrigin() == collector_url.GetOrigin();
75 }
76
77 } // namespace
78
79 // static
GetDomainReliabilityBeaconStatus(int net_error,int http_response_code,std::string * beacon_status_out)80 bool GetDomainReliabilityBeaconStatus(
81 int net_error,
82 int http_response_code,
83 std::string* beacon_status_out) {
84 if (net_error == net::OK) {
85 if (http_response_code >= 400 && http_response_code < 600)
86 *beacon_status_out = "http.error";
87 else
88 *beacon_status_out = "ok";
89 return true;
90 }
91
92 // TODO(juliatuttle): Consider sorting and using binary search?
93 for (size_t i = 0; i < base::size(net_error_map); i++) {
94 if (net_error_map[i].net_error == net_error) {
95 *beacon_status_out = net_error_map[i].beacon_status;
96 return true;
97 }
98 }
99 return false;
100 }
101
102 // TODO(juliatuttle): Consider using NPN/ALPN instead, if there's a good way to
103 // differentiate HTTP and HTTPS.
GetDomainReliabilityProtocol(net::HttpResponseInfo::ConnectionInfo connection_info,bool ssl_info_populated)104 std::string GetDomainReliabilityProtocol(
105 net::HttpResponseInfo::ConnectionInfo connection_info,
106 bool ssl_info_populated) {
107 switch (net::HttpResponseInfo::ConnectionInfoToCoarse(connection_info)) {
108 case net::HttpResponseInfo::CONNECTION_INFO_COARSE_HTTP1:
109 return ssl_info_populated ? "HTTPS" : "HTTP";
110 case net::HttpResponseInfo::CONNECTION_INFO_COARSE_HTTP2:
111 return "SPDY";
112 case net::HttpResponseInfo::CONNECTION_INFO_COARSE_QUIC:
113 return "QUIC";
114 case net::HttpResponseInfo::CONNECTION_INFO_COARSE_OTHER:
115 return "";
116 }
117 NOTREACHED();
118 return "";
119 }
120
GetUploadResultFromResponseDetails(int net_error,int http_response_code,base::TimeDelta retry_after)121 DomainReliabilityUploader::UploadResult GetUploadResultFromResponseDetails(
122 int net_error,
123 int http_response_code,
124 base::TimeDelta retry_after) {
125 DomainReliabilityUploader::UploadResult result;
126 if (net_error == net::OK && http_response_code == 200) {
127 result.status = DomainReliabilityUploader::UploadResult::SUCCESS;
128 return result;
129 }
130
131 if (net_error == net::OK && http_response_code == 503 &&
132 !retry_after.is_zero()) {
133 result.status = DomainReliabilityUploader::UploadResult::RETRY_AFTER;
134 result.retry_after = retry_after;
135 return result;
136 }
137
138 result.status = DomainReliabilityUploader::UploadResult::FAILURE;
139 return result;
140 }
141
142 // N.B. This uses a std::vector<std::unique_ptr<>> because that's what
143 // JSONValueConverter uses for repeated fields of any type, and Config uses
144 // JSONValueConverter to parse JSON configs.
SanitizeURLForReport(const GURL & beacon_url,const GURL & collector_url,const std::vector<std::unique_ptr<std::string>> & path_prefixes)145 GURL SanitizeURLForReport(
146 const GURL& beacon_url,
147 const GURL& collector_url,
148 const std::vector<std::unique_ptr<std::string>>& path_prefixes) {
149 if (CanReportFullBeaconURLToCollector(beacon_url, collector_url))
150 return beacon_url.GetAsReferrer();
151
152 std::string path = beacon_url.path();
153 const std::string empty_path;
154 const std::string* longest_path_prefix = &empty_path;
155 for (const auto& path_prefix : path_prefixes) {
156 if (path.substr(0, path_prefix->length()) == *path_prefix &&
157 path_prefix->length() > longest_path_prefix->length()) {
158 longest_path_prefix = path_prefix.get();
159 }
160 }
161
162 GURL::Replacements replacements;
163 replacements.ClearUsername();
164 replacements.ClearPassword();
165 replacements.SetPathStr(*longest_path_prefix);
166 replacements.ClearQuery();
167 replacements.ClearRef();
168 return beacon_url.ReplaceComponents(replacements);
169 }
170
171 namespace {
172
173 class ActualTimer : public MockableTime::Timer {
174 public:
ActualTimer()175 ActualTimer() {}
~ActualTimer()176 ~ActualTimer() override {}
177
178 // MockableTime::Timer implementation:
Start(const base::Location & posted_from,base::TimeDelta delay,base::OnceClosure user_task)179 void Start(const base::Location& posted_from,
180 base::TimeDelta delay,
181 base::OnceClosure user_task) override {
182 base_timer_.Start(posted_from, delay, std::move(user_task));
183 }
184
Stop()185 void Stop() override { base_timer_.Stop(); }
186
IsRunning()187 bool IsRunning() override { return base_timer_.IsRunning(); }
188
189 private:
190 base::OneShotTimer base_timer_;
191 };
192
193 } // namespace
194
~Timer()195 MockableTime::Timer::~Timer() {}
Timer()196 MockableTime::Timer::Timer() {}
197
~MockableTime()198 MockableTime::~MockableTime() {}
MockableTime()199 MockableTime::MockableTime() {}
200
ActualTime()201 ActualTime::ActualTime() {}
~ActualTime()202 ActualTime::~ActualTime() {}
203
Now() const204 base::Time ActualTime::Now() const {
205 return base::Time::Now();
206 }
NowTicks() const207 base::TimeTicks ActualTime::NowTicks() const {
208 return base::TimeTicks::Now();
209 }
210
CreateTimer()211 std::unique_ptr<MockableTime::Timer> ActualTime::CreateTimer() {
212 return std::unique_ptr<MockableTime::Timer>(new ActualTimer());
213 }
214
215 } // namespace domain_reliability
216