| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2017 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 #include "components/safe_browsing/password_protection/password_protection_reque
st.h" |
| 5 |
| 6 #include "base/memory/ptr_util.h" |
| 7 #include "base/memory/weak_ptr.h" |
| 8 #include "base/metrics/histogram_macros.h" |
| 9 #include "components/data_use_measurement/core/data_use_user_data.h" |
| 10 #include "content/public/browser/browser_thread.h" |
| 11 #include "net/base/escape.h" |
| 12 #include "net/base/load_flags.h" |
| 13 #include "net/base/url_util.h" |
| 14 #include "net/http/http_status_code.h" |
| 15 |
| 16 using content::BrowserThread; |
| 17 |
| 18 namespace safe_browsing { |
| 19 |
| 20 PasswordProtectionRequest::PasswordProtectionRequest( |
| 21 const GURL& main_frame_url, |
| 22 LoginReputationClientRequest::TriggerType type, |
| 23 bool is_extended_reporting, |
| 24 bool is_incognito, |
| 25 base::WeakPtr<PasswordProtectionService> pps, |
| 26 int request_timeout_in_ms) |
| 27 : main_frame_url_(main_frame_url), |
| 28 request_type_(type), |
| 29 is_extended_reporting_(is_extended_reporting), |
| 30 is_incognito_(is_incognito), |
| 31 password_protection_service_(pps), |
| 32 request_timeout_in_ms_(request_timeout_in_ms), |
| 33 weakptr_factory_(this) { |
| 34 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 35 } |
| 36 |
| 37 PasswordProtectionRequest::~PasswordProtectionRequest() { |
| 38 weakptr_factory_.InvalidateWeakPtrs(); |
| 39 } |
| 40 |
| 41 void PasswordProtectionRequest::Start() { |
| 42 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 43 // Initially we only send ping for Safe Browsing Extended Reporting users when |
| 44 // they are not in incognito mode. We may loose these conditions later. |
| 45 if (is_incognito_) { |
| 46 Finish(RequestOutcome::INCOGNITO, nullptr); |
| 47 return; |
| 48 } |
| 49 if (!is_extended_reporting_) { |
| 50 Finish(RequestOutcome::NO_EXTENDED_REPORTING, nullptr); |
| 51 return; |
| 52 } |
| 53 |
| 54 CheckWhitelistsOnUIThread(); |
| 55 } |
| 56 |
| 57 void PasswordProtectionRequest::CheckWhitelistsOnUIThread() { |
| 58 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 59 DCHECK(password_protection_service_); |
| 60 |
| 61 BrowserThread::PostTask( |
| 62 BrowserThread::IO, FROM_HERE, |
| 63 base::Bind(&PasswordProtectionService::CheckCsdWhitelistOnIOThread, |
| 64 password_protection_service_, main_frame_url_, |
| 65 base::Bind(&PasswordProtectionRequest::OnWhitelistCheckDone, |
| 66 GetWeakPtr()))); |
| 67 } |
| 68 |
| 69 void PasswordProtectionRequest::OnWhitelistCheckDone(bool match_whitelist) { |
| 70 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 71 if (match_whitelist) |
| 72 Finish(RequestOutcome::MATCHED_WHITELIST, nullptr); |
| 73 else |
| 74 CheckCachedVerdicts(); |
| 75 } |
| 76 |
| 77 void PasswordProtectionRequest::CheckCachedVerdicts() { |
| 78 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 79 if (!password_protection_service_) { |
| 80 Finish(RequestOutcome::SERVICE_DESTROYED, nullptr); |
| 81 return; |
| 82 } |
| 83 |
| 84 std::unique_ptr<LoginReputationClientResponse> cached_response = |
| 85 base::MakeUnique<LoginReputationClientResponse>(); |
| 86 auto verdict = password_protection_service_->GetCachedVerdict( |
| 87 password_protection_service_->GetSettingMapForActiveProfile(), |
| 88 main_frame_url_, cached_response.get()); |
| 89 if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED) |
| 90 Finish(RequestOutcome::RESPONSE_ALREADY_CACHED, std::move(cached_response)); |
| 91 else |
| 92 SendRequest(); |
| 93 } |
| 94 |
| 95 void PasswordProtectionRequest::SendRequest() { |
| 96 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 97 |
| 98 LoginReputationClientRequest request; |
| 99 request.set_page_url(main_frame_url_.spec()); |
| 100 request.set_trigger_type(request_type_); |
| 101 request.set_stored_verdict_cnt( |
| 102 password_protection_service_->GetStoredVerdictCount()); |
| 103 |
| 104 std::string serialized_request; |
| 105 if (!request.SerializeToString(&serialized_request)) { |
| 106 Finish(RequestOutcome::REQUEST_MALFORMED, nullptr); |
| 107 return; |
| 108 } |
| 109 |
| 110 // In case the request take too long, we set a timer to cancel this request. |
| 111 StartTimeout(); |
| 112 |
| 113 fetcher_ = net::URLFetcher::Create( |
| 114 0, PasswordProtectionService::GetPasswordProtectionRequestUrl(), |
| 115 net::URLFetcher::POST, this); |
| 116 data_use_measurement::DataUseUserData::AttachToFetcher( |
| 117 fetcher_.get(), data_use_measurement::DataUseUserData::SAFE_BROWSING); |
| 118 fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE); |
| 119 fetcher_->SetAutomaticallyRetryOn5xx(false); |
| 120 fetcher_->SetRequestContext( |
| 121 password_protection_service_->request_context_getter().get()); |
| 122 fetcher_->SetUploadData("application/octet-stream", serialized_request); |
| 123 request_start_time_ = base::TimeTicks::Now(); |
| 124 fetcher_->Start(); |
| 125 } |
| 126 |
| 127 void PasswordProtectionRequest::StartTimeout() { |
| 128 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 129 |
| 130 // If request is not done withing 10 seconds, we cancel this request. |
| 131 // The weak pointer used for the timeout will be invalidated (and |
| 132 // hence would prevent the timeout) if the check completes on time and |
| 133 // execution reaches Finish(). |
| 134 BrowserThread::PostDelayedTask( |
| 135 BrowserThread::UI, FROM_HERE, |
| 136 base::Bind(&PasswordProtectionRequest::Cancel, GetWeakPtr(), true), |
| 137 base::TimeDelta::FromMilliseconds(request_timeout_in_ms_)); |
| 138 } |
| 139 |
| 140 void PasswordProtectionRequest::OnURLFetchComplete( |
| 141 const net::URLFetcher* source) { |
| 142 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 143 net::URLRequestStatus status = source->GetStatus(); |
| 144 const bool is_success = status.is_success(); |
| 145 const int response_code = source->GetResponseCode(); |
| 146 |
| 147 UMA_HISTOGRAM_SPARSE_SLOWLY( |
| 148 "PasswordProtection.PasswordProtectionResponseOrErrorCode", |
| 149 is_success ? response_code : status.error()); |
| 150 |
| 151 if (!is_success || net::HTTP_OK != response_code) { |
| 152 Finish(RequestOutcome::FETCH_FAILED, nullptr); |
| 153 return; |
| 154 } |
| 155 |
| 156 std::unique_ptr<LoginReputationClientResponse> response = |
| 157 base::MakeUnique<LoginReputationClientResponse>(); |
| 158 std::string response_body; |
| 159 bool received_data = source->GetResponseAsString(&response_body); |
| 160 DCHECK(received_data); |
| 161 fetcher_.reset(); // We don't need it anymore. |
| 162 UMA_HISTOGRAM_TIMES("PasswordProtection.RequestNetworkDuration", |
| 163 base::TimeTicks::Now() - request_start_time_); |
| 164 if (response->ParseFromString(response_body)) { |
| 165 Finish(RequestOutcome::SUCCEEDED, std::move(response)); |
| 166 } else { |
| 167 Finish(RequestOutcome::RESPONSE_MALFORMED, nullptr); |
| 168 } |
| 169 } |
| 170 |
| 171 void PasswordProtectionRequest::Finish( |
| 172 RequestOutcome outcome, |
| 173 std::unique_ptr<LoginReputationClientResponse> response) { |
| 174 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 175 |
| 176 UMA_HISTOGRAM_ENUMERATION("PasswordProtection.RequestOutcome", outcome, |
| 177 RequestOutcome::MAX_OUTCOME); |
| 178 |
| 179 if (response) { |
| 180 switch (request_type_) { |
| 181 case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: |
| 182 UMA_HISTOGRAM_ENUMERATION( |
| 183 "PasswordProtection.UnfamiliarLoginPageVerdict", |
| 184 response->verdict_type(), |
| 185 LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1); |
| 186 break; |
| 187 case LoginReputationClientRequest::PASSWORD_REUSE_EVENT: |
| 188 UMA_HISTOGRAM_ENUMERATION( |
| 189 "PasswordProtection.PasswordReuseEventVerdict", |
| 190 response->verdict_type(), |
| 191 LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1); |
| 192 break; |
| 193 default: |
| 194 NOTREACHED(); |
| 195 } |
| 196 } |
| 197 |
| 198 DCHECK(password_protection_service_); |
| 199 password_protection_service_->RequestFinished(this, std::move(response)); |
| 200 } |
| 201 |
| 202 void PasswordProtectionRequest::Cancel(bool timed_out) { |
| 203 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 204 fetcher_.reset(); |
| 205 |
| 206 Finish(timed_out ? TIMEDOUT : CANCELED, nullptr); |
| 207 } |
| 208 |
| 209 } // namespace safe_browsing |
| OLD | NEW |