Chromium Code Reviews| Index: components/safe_browsing/password_protection/password_protection_request.cc |
| diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c422c9e8a42cf175073d5cba2deebe44cbef8118 |
| --- /dev/null |
| +++ b/components/safe_browsing/password_protection/password_protection_request.cc |
| @@ -0,0 +1,215 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| +#include "components/safe_browsing/password_protection/password_protection_request.h" |
| + |
| +#include "base/memory/ptr_util.h" |
| +#include "base/memory/weak_ptr.h" |
| +#include "base/metrics/histogram_macros.h" |
| +#include "components/data_use_measurement/core/data_use_user_data.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "net/base/escape.h" |
| +#include "net/base/load_flags.h" |
| +#include "net/base/url_util.h" |
| +#include "net/http/http_status_code.h" |
| + |
| +using content::BrowserThread; |
| + |
| +namespace safe_browsing { |
| + |
| +PasswordProtectionRequest::PasswordProtectionRequest( |
| + const GURL& main_frame_url, |
| + LoginReputationClientRequest::TriggerType type, |
| + bool is_extended_reporting, |
| + bool is_incognito, |
| + base::WeakPtr<PasswordProtectionService> pps, |
| + int request_timeout_in_ms) |
| + : main_frame_url_(main_frame_url), |
| + request_type_(type), |
| + is_extended_reporting_(is_extended_reporting), |
| + is_incognito_(is_incognito), |
| + password_protection_service_(pps), |
| + request_timeout_in_ms_(request_timeout_in_ms), |
| + weakptr_factory_(this) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| +} |
| + |
| +PasswordProtectionRequest::~PasswordProtectionRequest() { |
| + weakptr_factory_.InvalidateWeakPtrs(); |
| +} |
| + |
| +void PasswordProtectionRequest::Start() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + // Initially we only send ping for Safe Browsing Extended Reporting users when |
| + // they are not in incognito mode. We may loose these conditions later. |
| + if (is_incognito_) { |
| + Finish(RequestOutcome::INCOGNITO, nullptr); |
| + return; |
| + } |
| + if (!is_extended_reporting_) { |
| + Finish(RequestOutcome::NO_EXTENDED_REPORTING, nullptr); |
| + return; |
| + } |
| + |
| + // In case the request take too long, we set a timer to cancel this request. |
| + StartTimeout(); |
| + |
| + CheckWhitelistsOnUIThread(); |
| +} |
| + |
| +void PasswordProtectionRequest::Cancel(bool timed_out) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + if (fetcher_.get()) { |
|
Nathan Parker
2017/03/23 20:45:50
nit: You can .reset() unconditionally
Jialiu Lin
2017/03/23 22:42:59
Done.
|
| + // If there is already a URLFetcher started, we should cancel it. |
| + fetcher_.reset(); |
| + } |
| + |
| + Finish(timed_out ? TIMEDOUT : CANCELED, nullptr); |
|
Nathan Parker
2017/03/23 20:45:50
Is the expectation that Cancel() be followed by de
Jialiu Lin
2017/03/23 22:42:59
Yes, and Finish(..) will destroy this object by as
Nathan Parker
2017/03/24 00:41:50
Can you add a comment on Cancel() in the .h to tha
Jialiu Lin
2017/03/24 01:42:42
Already did. :-)
|
| +} |
| + |
| +void PasswordProtectionRequest::StartTimeout() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + timeout_start_time_ = base::TimeTicks::Now(); |
| + |
| + // If request is not done withing 10 milliseconds, we cancel this request. |
|
Nathan Parker
2017/03/23 20:45:49
nit: 10 seconds
Jialiu Lin
2017/03/23 22:42:59
Done.
|
| + // The weak pointer used for the timeout will be invalidated (and |
| + // hence would prevent the timeout) if the check completes on time and |
| + // execution reaches Finish(). |
| + BrowserThread::PostDelayedTask( |
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(&PasswordProtectionRequest::Cancel, GetWeakPtr(), true), |
| + base::TimeDelta::FromMilliseconds(request_timeout_in_ms_)); |
| +} |
| + |
| +void PasswordProtectionRequest::Finish( |
| + RequestOutcome outcome, |
| + std::unique_ptr<LoginReputationClientResponse> response) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + |
| + UMA_HISTOGRAM_ENUMERATION("PasswordProtection.RequestOutcome", outcome, |
| + RequestOutcome::MAX_OUTCOME); |
| + if (!timeout_start_time_.is_null()) { |
| + UMA_HISTOGRAM_TIMES("PasswordProtection.RequestDuration", |
| + base::TimeTicks::Now() - timeout_start_time_); |
|
Nathan Parker
2017/03/23 20:45:49
Is this recorded time any different from PasswordP
Jialiu Lin
2017/03/23 22:42:59
NetworkDuration measures the time from SendRequest
Nathan Parker
2017/03/24 00:41:50
I suspect the only delay > 1 ms is the network cal
Jialiu Lin
2017/03/24 01:42:42
you're right. The differences will be tiny. Let m
|
| + } |
| + |
| + if (response) { |
| + if (request_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) { |
|
Nathan Parker
2017/03/23 20:45:50
How about a switch statement with no default, to e
Nathan Parker
2017/03/23 20:45:50
I thought this was going to be LOW_REPUTATION -- m
Jialiu Lin
2017/03/23 22:42:59
UNFAMILIAR_LOGIN_PAGE is the request trigger type
Jialiu Lin
2017/03/23 22:43:00
Done.
Nathan Parker
2017/03/24 00:41:50
AH yes, nm.
|
| + UMA_HISTOGRAM_ENUMERATION( |
| + "PasswordProtection.UnfamiliarLoginPageVerdict", |
| + response->verdict_type(), |
| + LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1); |
| + } else if (request_type_ == |
| + LoginReputationClientRequest::PASSWORD_REUSE_EVENT) { |
| + UMA_HISTOGRAM_ENUMERATION( |
| + "PasswordProtection.PasswordReuseEventVerdict", |
| + response->verdict_type(), |
| + LoginReputationClientResponse_VerdictType_VerdictType_MAX + 1); |
| + } |
| + } |
| + |
| + DCHECK(password_protection_service_); |
| + password_protection_service_->RequestFinished(this, std::move(response)); |
| +} |
| + |
| +// TODO(jialiul): there are some duplicated code |
| +void PasswordProtectionRequest::CheckWhitelistsOnUIThread() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + if (!password_protection_service_) { |
|
Nathan Parker
2017/03/23 20:45:50
Is this possible? The PPS is shut down on the UI
Jialiu Lin
2017/03/23 22:42:59
Just a safe measure, since PPS is a weakptr. But i
|
| + Finish(RequestOutcome::SERVICE_DESTROYED, nullptr); |
| + return; |
| + } |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::IO, FROM_HERE, |
| + base::Bind(&PasswordProtectionService::CheckCsdWhitelistOnIOThread, |
| + password_protection_service_, main_frame_url_, |
| + base::Bind(&PasswordProtectionRequest::OnWhitelistCheckDone, |
| + GetWeakPtr()))); |
| +} |
| + |
| +void PasswordProtectionRequest::OnWhitelistCheckDone(bool match_whitelist) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + if (match_whitelist) |
| + Finish(RequestOutcome::MATCHED_WHITELIST, nullptr); |
| + else |
| + CheckCachedVerdicts(); |
| +} |
| + |
| +void PasswordProtectionRequest::CheckCachedVerdicts() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + if (!password_protection_service_) { |
| + Finish(RequestOutcome::SERVICE_DESTROYED, nullptr); |
| + return; |
| + } |
| + |
| + std::unique_ptr<LoginReputationClientResponse> cached_response = |
| + base::MakeUnique<LoginReputationClientResponse>(); |
| + auto verdict = password_protection_service_->GetCachedVerdict( |
| + password_protection_service_->GetSettingMapForActiveProfile(), |
| + main_frame_url_, cached_response.get()); |
| + if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED) |
| + Finish(RequestOutcome::RESPONSE_ALREADY_CACHED, std::move(cached_response)); |
| + else |
| + SendRequest(); |
| +} |
| + |
| +void PasswordProtectionRequest::OnURLFetchComplete( |
| + const net::URLFetcher* source) { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + if (!source->GetStatus().is_success() || |
| + net::HTTP_OK != source->GetResponseCode()) { |
| + Finish(RequestOutcome::FETCH_FAILED, nullptr); |
|
Nathan Parker
2017/03/23 20:45:50
Add todo: log to UMA the httpResponseOrErrorCode l
Jialiu Lin
2017/03/23 22:42:59
Done. UMA metrics added.
|
| + return; |
| + } |
| + std::unique_ptr<LoginReputationClientResponse> response = |
| + base::MakeUnique<LoginReputationClientResponse>(); |
| + std::string response_body; |
| + bool received_data = source->GetResponseAsString(&response_body); |
| + DCHECK(received_data); |
| + fetcher_.reset(); // We don't need it anymore. |
| + UMA_HISTOGRAM_TIMES("PasswordProtection.RequestNetworkDuration", |
| + base::TimeTicks::Now() - request_start_time_); |
| + if (response->ParseFromString(response_body)) { |
| + Finish(RequestOutcome::SUCCEEDED, std::move(response)); |
| + } else { |
| + Finish(RequestOutcome::RESPONSE_MALFORMED, nullptr); |
| + } |
| +} |
| + |
| +void PasswordProtectionRequest::SendRequest() { |
| + DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| + // This is the last chance to check whether password_protection_service_ is |
| + // still available. If not, drop the request. |
| + if (!password_protection_service_) { |
|
Nathan Parker
2017/03/23 20:45:50
This was already checked in CheckCachedVerdicts, w
Jialiu Lin
2017/03/23 22:42:59
Right. removed this check.
|
| + Finish(RequestOutcome::SERVICE_DESTROYED, nullptr); |
| + return; |
| + } |
| + |
| + LoginReputationClientRequest request; |
| + request.set_page_url(main_frame_url_.spec()); |
| + request.set_trigger_type(request_type_); |
| + request.set_stored_verdict_cnt( |
| + password_protection_service_->GetStoredVerdictCount()); |
| + |
| + std::string serialized_request; |
| + if (!request.SerializeToString(&serialized_request)) { |
| + Finish(RequestOutcome::REQUEST_MALFORMED, nullptr); |
| + return; |
| + } |
| + |
| + fetcher_ = net::URLFetcher::Create( |
| + 0, PasswordProtectionService::GetPasswordProtectionRequestUrl(), |
| + net::URLFetcher::POST, this); |
| + data_use_measurement::DataUseUserData::AttachToFetcher( |
| + fetcher_.get(), data_use_measurement::DataUseUserData::SAFE_BROWSING); |
| + fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE); |
| + fetcher_->SetAutomaticallyRetryOn5xx(false); |
| + fetcher_->SetRequestContext( |
| + password_protection_service_->request_context_getter().get()); |
| + fetcher_->SetUploadData("application/octet-stream", serialized_request); |
| + request_start_time_ = base::TimeTicks::Now(); |
| + fetcher_->Start(); |
| +} |
| + |
| +} // namespace safe_browsing |