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/monitor.h" 6 7 #include "base/command_line.h" 8 #include "base/logging.h" 9 #include "base/single_thread_task_runner.h" 10 #include "base/task_runner.h" 11 #include "base/time/time.h" 12 #include "components/domain_reliability/baked_in_configs.h" 13 #include "net/base/load_flags.h" 14 #include "net/http/http_response_headers.h" 15 #include "net/url_request/url_request.h" 16 #include "net/url_request/url_request_context.h" 17 #include "net/url_request/url_request_context_getter.h" 18 19 namespace domain_reliability { 20 21 DomainReliabilityMonitor::DomainReliabilityMonitor( 22 const std::string& upload_reporter_string, 23 scoped_refptr<base::SingleThreadTaskRunner> pref_thread, 24 scoped_refptr<base::SingleThreadTaskRunner> network_thread) 25 : time_(new ActualTime()), 26 upload_reporter_string_(upload_reporter_string), 27 scheduler_params_( 28 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()), 29 dispatcher_(time_.get()), 30 pref_task_runner_(pref_thread), 31 network_task_runner_(network_thread), 32 moved_to_network_thread_(false), 33 discard_uploads_set_(false), 34 weak_factory_(this) { 35 DCHECK(OnPrefThread()); 36 } 37 38 DomainReliabilityMonitor::DomainReliabilityMonitor( 39 const std::string& upload_reporter_string, 40 scoped_refptr<base::SingleThreadTaskRunner> pref_thread, 41 scoped_refptr<base::SingleThreadTaskRunner> network_thread, 42 scoped_ptr<MockableTime> time) 43 : time_(time.Pass()), 44 upload_reporter_string_(upload_reporter_string), 45 scheduler_params_( 46 DomainReliabilityScheduler::Params::GetFromFieldTrialsOrDefaults()), 47 dispatcher_(time_.get()), 48 pref_task_runner_(pref_thread), 49 network_task_runner_(network_thread), 50 moved_to_network_thread_(false), 51 discard_uploads_set_(false), 52 weak_factory_(this) { 53 DCHECK(OnPrefThread()); 54 } 55 56 DomainReliabilityMonitor::~DomainReliabilityMonitor() { 57 if (moved_to_network_thread_) 58 DCHECK(OnNetworkThread()); 59 else 60 DCHECK(OnPrefThread()); 61 62 ClearContexts(); 63 } 64 65 void DomainReliabilityMonitor::MoveToNetworkThread() { 66 DCHECK(OnPrefThread()); 67 DCHECK(!moved_to_network_thread_); 68 69 moved_to_network_thread_ = true; 70 } 71 72 void DomainReliabilityMonitor::InitURLRequestContext( 73 net::URLRequestContext* url_request_context) { 74 DCHECK(OnNetworkThread()); 75 DCHECK(moved_to_network_thread_); 76 77 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter = 78 new net::TrivialURLRequestContextGetter(url_request_context, 79 network_task_runner_); 80 InitURLRequestContext(url_request_context_getter); 81 } 82 83 void DomainReliabilityMonitor::InitURLRequestContext( 84 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter) { 85 DCHECK(OnNetworkThread()); 86 DCHECK(moved_to_network_thread_); 87 88 // Make sure the URLRequestContext actually lives on what was declared to be 89 // the network thread. 90 DCHECK(url_request_context_getter->GetNetworkTaskRunner()-> 91 RunsTasksOnCurrentThread()); 92 93 uploader_ = DomainReliabilityUploader::Create(url_request_context_getter); 94 } 95 96 void DomainReliabilityMonitor::AddBakedInConfigs() { 97 DCHECK(OnNetworkThread()); 98 DCHECK(moved_to_network_thread_); 99 100 base::Time now = base::Time::Now(); 101 for (size_t i = 0; kBakedInJsonConfigs[i]; ++i) { 102 std::string json(kBakedInJsonConfigs[i]); 103 scoped_ptr<const DomainReliabilityConfig> config = 104 DomainReliabilityConfig::FromJSON(json); 105 if (config && config->IsExpired(now)) { 106 LOG(WARNING) << "Baked-in Domain Reliability config for " 107 << config->domain << " is expired."; 108 continue; 109 } 110 AddContext(config.Pass()); 111 } 112 } 113 114 void DomainReliabilityMonitor::SetDiscardUploads(bool discard_uploads) { 115 DCHECK(OnNetworkThread()); 116 DCHECK(moved_to_network_thread_); 117 DCHECK(uploader_); 118 119 uploader_->set_discard_uploads(discard_uploads); 120 discard_uploads_set_ = true; 121 } 122 123 void DomainReliabilityMonitor::OnBeforeRedirect(net::URLRequest* request) { 124 DCHECK(OnNetworkThread()); 125 DCHECK(discard_uploads_set_); 126 127 // Record the redirect itself in addition to the final request. 128 OnRequestLegComplete(RequestInfo(*request)); 129 } 130 131 void DomainReliabilityMonitor::OnCompleted(net::URLRequest* request, 132 bool started) { 133 DCHECK(OnNetworkThread()); 134 DCHECK(discard_uploads_set_); 135 136 if (!started) 137 return; 138 RequestInfo request_info(*request); 139 if (request_info.AccessedNetwork()) { 140 OnRequestLegComplete(request_info); 141 // A request was just using the network, so now is a good time to run any 142 // pending and eligible uploads. 143 dispatcher_.RunEligibleTasks(); 144 } 145 } 146 147 void DomainReliabilityMonitor::ClearBrowsingData( 148 DomainReliabilityClearMode mode) { 149 DCHECK(OnNetworkThread()); 150 151 switch (mode) { 152 case CLEAR_BEACONS: { 153 ContextMap::const_iterator it; 154 for (it = contexts_.begin(); it != contexts_.end(); ++it) 155 it->second->ClearBeacons(); 156 break; 157 }; 158 case CLEAR_CONTEXTS: 159 ClearContexts(); 160 break; 161 case MAX_CLEAR_MODE: 162 NOTREACHED(); 163 } 164 } 165 166 scoped_ptr<base::Value> DomainReliabilityMonitor::GetWebUIData() const { 167 DCHECK(OnNetworkThread()); 168 169 base::ListValue* contexts_value = new base::ListValue(); 170 for (ContextMap::const_iterator it = contexts_.begin(); 171 it != contexts_.end(); 172 ++it) { 173 contexts_value->Append(it->second->GetWebUIData().release()); 174 } 175 176 base::DictionaryValue* data_value = new base::DictionaryValue(); 177 data_value->Set("contexts", contexts_value); 178 179 return scoped_ptr<base::Value>(data_value); 180 } 181 182 DomainReliabilityContext* DomainReliabilityMonitor::AddContextForTesting( 183 scoped_ptr<const DomainReliabilityConfig> config) { 184 DCHECK(OnNetworkThread()); 185 186 return AddContext(config.Pass()); 187 } 188 189 DomainReliabilityMonitor::RequestInfo::RequestInfo() {} 190 191 DomainReliabilityMonitor::RequestInfo::RequestInfo( 192 const net::URLRequest& request) 193 : url(request.url()), 194 status(request.status()), 195 response_info(request.response_info()), 196 load_flags(request.load_flags()), 197 is_upload(DomainReliabilityUploader::URLRequestIsUpload(request)) { 198 request.GetLoadTimingInfo(&load_timing_info); 199 } 200 201 DomainReliabilityMonitor::RequestInfo::~RequestInfo() {} 202 203 bool DomainReliabilityMonitor::RequestInfo::AccessedNetwork() const { 204 return status.status() != net::URLRequestStatus::CANCELED && 205 response_info.network_accessed; 206 } 207 208 DomainReliabilityContext* DomainReliabilityMonitor::AddContext( 209 scoped_ptr<const DomainReliabilityConfig> config) { 210 DCHECK(OnNetworkThread()); 211 DCHECK(config); 212 DCHECK(config->IsValid()); 213 214 // Grab a copy of the domain before transferring ownership of |config|. 215 std::string domain = config->domain; 216 217 DomainReliabilityContext* context = 218 new DomainReliabilityContext(time_.get(), 219 scheduler_params_, 220 upload_reporter_string_, 221 &dispatcher_, 222 uploader_.get(), 223 config.Pass()); 224 225 std::pair<ContextMap::iterator, bool> map_it = 226 contexts_.insert(make_pair(domain, context)); 227 // Make sure the domain wasn't already in the map. 228 DCHECK(map_it.second); 229 230 return map_it.first->second; 231 } 232 233 void DomainReliabilityMonitor::ClearContexts() { 234 STLDeleteContainerPairSecondPointers( 235 contexts_.begin(), contexts_.end()); 236 contexts_.clear(); 237 } 238 239 void DomainReliabilityMonitor::OnRequestLegComplete( 240 const RequestInfo& request) { 241 // Check these again because unit tests call this directly. 242 DCHECK(OnNetworkThread()); 243 DCHECK(discard_uploads_set_); 244 245 int response_code; 246 if (request.response_info.headers.get()) 247 response_code = request.response_info.headers->response_code(); 248 else 249 response_code = -1; 250 std::string beacon_status; 251 252 int error_code = net::OK; 253 if (request.status.status() == net::URLRequestStatus::FAILED) 254 error_code = request.status.error(); 255 256 DomainReliabilityContext* context = GetContextForHost(request.url.host()); 257 258 // Ignore requests where: 259 // 1. There is no context for the request host. 260 // 2. The request did not access the network. 261 // 3. The request is not supposed to send cookies (to avoid associating the 262 // request with any potentially unique data in the config). 263 // 4. The request was itself a Domain Reliability upload (to avoid loops). 264 // 5. There is no defined beacon status for the error or HTTP response code 265 // (to avoid leaking network-local errors). 266 if (!context || 267 !request.AccessedNetwork() || 268 (request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) || 269 request.is_upload || 270 !GetDomainReliabilityBeaconStatus( 271 error_code, response_code, &beacon_status)) { 272 return; 273 } 274 275 DomainReliabilityBeacon beacon; 276 beacon.status = beacon_status; 277 beacon.chrome_error = error_code; 278 if (!request.response_info.was_fetched_via_proxy) 279 beacon.server_ip = request.response_info.socket_address.host(); 280 else 281 beacon.server_ip.clear(); 282 beacon.protocol = GetDomainReliabilityProtocol( 283 request.response_info.connection_info, 284 request.response_info.ssl_info.is_valid()); 285 beacon.http_response_code = response_code; 286 beacon.start_time = request.load_timing_info.request_start; 287 beacon.elapsed = time_->NowTicks() - beacon.start_time; 288 beacon.domain = request.url.host(); 289 context->OnBeacon(request.url, beacon); 290 } 291 292 // TODO(ttuttle): Keep a separate wildcard_contexts_ map to avoid having to 293 // prepend '*.' to domains. 294 DomainReliabilityContext* DomainReliabilityMonitor::GetContextForHost( 295 const std::string& host) const { 296 DCHECK(OnNetworkThread()); 297 298 ContextMap::const_iterator context_it; 299 300 context_it = contexts_.find(host); 301 if (context_it != contexts_.end()) 302 return context_it->second; 303 304 std::string host_with_asterisk = "*." + host; 305 context_it = contexts_.find(host_with_asterisk); 306 if (context_it != contexts_.end()) 307 return context_it->second; 308 309 size_t dot_pos = host.find('.'); 310 if (dot_pos == std::string::npos) 311 return NULL; 312 313 // TODO(ttuttle): Make sure parent is not in PSL before using. 314 315 std::string parent_with_asterisk = "*." + host.substr(dot_pos + 1); 316 context_it = contexts_.find(parent_with_asterisk); 317 if (context_it != contexts_.end()) 318 return context_it->second; 319 320 return NULL; 321 } 322 323 base::WeakPtr<DomainReliabilityMonitor> 324 DomainReliabilityMonitor::MakeWeakPtr() { 325 return weak_factory_.GetWeakPtr(); 326 } 327 328 } // namespace domain_reliability 329