Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 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 "chrome/browser/safe_browsing/certificate_reporting_service_test_utils. h" | |
| 6 | |
| 7 #include "base/threading/thread_task_runner_handle.h" | |
| 8 #include "components/certificate_reporting/encrypted_cert_logger.pb.h" | |
| 9 #include "content/public/browser/browser_thread.h" | |
| 10 #include "content/public/test/test_utils.h" | |
| 11 #include "crypto/curve25519.h" | |
| 12 #include "net/base/upload_bytes_element_reader.h" | |
| 13 #include "net/base/upload_data_stream.h" | |
| 14 #include "net/test/url_request/url_request_failed_job.h" | |
| 15 #include "net/test/url_request/url_request_mock_data_job.h" | |
| 16 #include "net/url_request/url_request_filter.h" | |
| 17 #include "testing/gtest/include/gtest/gtest.h" | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 const uint32_t kServerPublicKeyTestVersion = 16; | |
| 22 | |
| 23 void SetUpURLHandlersOnIOThread( | |
| 24 std::unique_ptr<net::URLRequestInterceptor> url_request_interceptor) { | |
| 25 net::URLRequestFilter* filter = net::URLRequestFilter::GetInstance(); | |
| 26 filter->AddUrlInterceptor( | |
| 27 GURL(CertificateReportingService::kExtendedReportingUploadUrlInsecure), | |
| 28 std::move(url_request_interceptor)); | |
| 29 } | |
| 30 | |
| 31 std::string GetUploadData(net::URLRequest* request) { | |
| 32 const net::UploadDataStream* stream = request->get_upload(); | |
| 33 EXPECT_TRUE(stream); | |
| 34 EXPECT_TRUE(stream->GetElementReaders()); | |
| 35 EXPECT_EQ(1u, stream->GetElementReaders()->size()); | |
| 36 const net::UploadBytesElementReader* reader = | |
| 37 (*stream->GetElementReaders())[0]->AsBytesReader(); | |
| 38 return std::string(reader->bytes(), reader->length()); | |
| 39 } | |
| 40 | |
| 41 std::string GetReportContents(net::URLRequest* request, | |
| 42 const uint8_t* server_private_key) { | |
| 43 std::string serialized_report(GetUploadData(request)); | |
| 44 certificate_reporting::EncryptedCertLoggerRequest encrypted_request; | |
| 45 EXPECT_TRUE(encrypted_request.ParseFromString(serialized_report)); | |
| 46 EXPECT_EQ(kServerPublicKeyTestVersion, | |
| 47 encrypted_request.server_public_key_version()); | |
| 48 EXPECT_EQ(certificate_reporting::EncryptedCertLoggerRequest:: | |
| 49 AEAD_ECDH_AES_128_CTR_HMAC_SHA256, | |
| 50 encrypted_request.algorithm()); | |
| 51 std::string decrypted_report; | |
| 52 certificate_reporting::ErrorReporter::DecryptErrorReport( | |
| 53 server_private_key, encrypted_request, &decrypted_report); | |
| 54 return decrypted_report; | |
| 55 } | |
| 56 | |
| 57 } // namespace | |
| 58 | |
| 59 namespace certificate_reporting_test_utils { | |
| 60 | |
| 61 CertificateReportingServiceTestNetworkDelegate:: | |
|
estark
2016/12/07 00:42:55
nit: I think the order of things here doesn't matc
meacer
2016/12/07 21:37:33
Done.
| |
| 62 CertificateReportingServiceTestNetworkDelegate( | |
| 63 const base::Callback<void(net::URLRequest* request)>& | |
| 64 url_request_destroyed_callback) | |
| 65 : url_request_destroyed_callback_(url_request_destroyed_callback) {} | |
| 66 | |
| 67 CertificateReportingServiceTestNetworkDelegate:: | |
| 68 ~CertificateReportingServiceTestNetworkDelegate() {} | |
| 69 | |
| 70 void CertificateReportingServiceTestNetworkDelegate::OnURLRequestDestroyed( | |
| 71 net::URLRequest* request) { | |
| 72 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 73 content::BrowserThread::PostTask( | |
| 74 content::BrowserThread::UI, FROM_HERE, | |
| 75 base::Bind(url_request_destroyed_callback_, request)); | |
|
estark
2016/12/07 00:42:55
Is it safe to pass URLRequests to the UI thread?
meacer
2016/12/07 21:37:33
Removed the request.
| |
| 76 } | |
| 77 | |
| 78 DelayableCertReportURLRequestJob::DelayableCertReportURLRequestJob( | |
| 79 net::URLRequest* request, | |
| 80 net::NetworkDelegate* network_delegate) | |
| 81 : net::URLRequestJob(request, network_delegate), weak_factory_(this) {} | |
| 82 | |
| 83 DelayableCertReportURLRequestJob::~DelayableCertReportURLRequestJob() {} | |
| 84 | |
| 85 void DelayableCertReportURLRequestJob::Start() { | |
| 86 started_ = true; | |
| 87 if (delayed_) { | |
| 88 // Do nothing until Resume() is called. | |
| 89 return; | |
| 90 } | |
| 91 Resume(); | |
| 92 } | |
| 93 | |
| 94 int DelayableCertReportURLRequestJob::ReadRawData(net::IOBuffer* buf, | |
| 95 int buf_size) { | |
| 96 return 0; | |
| 97 } | |
| 98 | |
| 99 int DelayableCertReportURLRequestJob::GetResponseCode() const { | |
| 100 return 200; | |
| 101 } | |
| 102 | |
| 103 void DelayableCertReportURLRequestJob::GetResponseInfo( | |
| 104 net::HttpResponseInfo* info) {} | |
|
estark
2016/12/07 00:42:55
nit: maybe put a comment here explaining that the
meacer
2016/12/07 21:37:33
Done.
| |
| 105 | |
| 106 void DelayableCertReportURLRequestJob::Resume() { | |
| 107 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 108 DCHECK(delayed_); | |
| 109 if (!started_) { | |
| 110 // If Start() hasn't been called yet, then unset |delayed_| so | |
| 111 // that when Start() is called, the request will begin | |
| 112 // immediately. | |
| 113 delayed_ = false; | |
| 114 return; | |
| 115 } | |
| 116 // Start reading asynchronously as would a normal network request. | |
| 117 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 118 FROM_HERE, | |
| 119 base::Bind(&DelayableCertReportURLRequestJob::NotifyHeadersComplete, | |
| 120 weak_factory_.GetWeakPtr())); | |
| 121 } | |
| 122 | |
| 123 void CertReportJobInterceptor::RequestCreated( | |
| 124 const std::string& uploaded_report, | |
| 125 ReportSendingResult expected_report_result) { | |
| 126 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 127 switch (expected_report_result) { | |
| 128 case REPORTS_SUCCESSFUL: | |
| 129 successful_reports_.insert(uploaded_report); | |
| 130 break; | |
| 131 case REPORTS_FAIL: | |
| 132 failed_reports_.insert(uploaded_report); | |
| 133 break; | |
| 134 case REPORTS_DELAY: | |
| 135 delayed_reports_.insert(uploaded_report); | |
| 136 break; | |
| 137 } | |
| 138 } | |
| 139 | |
| 140 void CertReportJobInterceptor::Clear() { | |
| 141 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 142 successful_reports_.clear(); | |
| 143 failed_reports_.clear(); | |
| 144 delayed_reports_.clear(); | |
| 145 } | |
| 146 | |
| 147 CertReportJobInterceptor::CertReportJobInterceptor( | |
| 148 ReportSendingResult expected_report_result, | |
| 149 const uint8_t* server_private_key) | |
| 150 : expected_report_result_(expected_report_result), | |
| 151 server_private_key_(server_private_key), | |
| 152 weak_factory_(this) {} | |
| 153 | |
| 154 CertReportJobInterceptor::~CertReportJobInterceptor() {} | |
| 155 | |
| 156 net::URLRequestJob* CertReportJobInterceptor::MaybeInterceptRequest( | |
| 157 net::URLRequest* request, | |
| 158 net::NetworkDelegate* network_delegate) const { | |
| 159 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 160 | |
| 161 const std::string uploaded_report = | |
| 162 GetReportContents(request, server_private_key_); | |
| 163 content::BrowserThread::PostTask( | |
| 164 content::BrowserThread::UI, FROM_HERE, | |
| 165 base::Bind(&CertReportJobInterceptor::RequestCreated, | |
| 166 weak_factory_.GetWeakPtr(), uploaded_report, | |
| 167 expected_report_result_)); | |
| 168 | |
| 169 if (expected_report_result_ == REPORTS_FAIL) { | |
| 170 return new net::URLRequestFailedJob(request, network_delegate, | |
| 171 net::ERR_SSL_PROTOCOL_ERROR); | |
| 172 } else if (expected_report_result_ == REPORTS_DELAY) { | |
| 173 DCHECK(!delayed_request_) << "Supports only one delayed request at a time"; | |
| 174 DelayableCertReportURLRequestJob* job = | |
| 175 new DelayableCertReportURLRequestJob(request, network_delegate); | |
| 176 delayed_request_ = job->GetWeakPtr(); | |
| 177 return job; | |
| 178 } | |
| 179 // Successful url request job. | |
| 180 return new net::URLRequestMockDataJob(request, network_delegate, "some data", | |
| 181 1, false); | |
| 182 } | |
| 183 | |
| 184 void CertReportJobInterceptor::SetFailureMode( | |
| 185 ReportSendingResult expected_report_result) { | |
| 186 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 187 content::BrowserThread::PostTask( | |
| 188 content::BrowserThread::IO, FROM_HERE, | |
| 189 base::Bind(&CertReportJobInterceptor::SetFailureModeOnIOThread, | |
| 190 weak_factory_.GetWeakPtr(), expected_report_result)); | |
| 191 } | |
| 192 | |
| 193 void CertReportJobInterceptor::Resume() { | |
| 194 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 195 content::BrowserThread::PostTask( | |
| 196 content::BrowserThread::IO, FROM_HERE, | |
| 197 base::Bind(&CertReportJobInterceptor::ResumeOnIOThread, | |
| 198 weak_factory_.GetWeakPtr())); | |
| 199 } | |
| 200 | |
| 201 const std::set<std::string>& CertReportJobInterceptor::successful_reports() | |
| 202 const { | |
| 203 return successful_reports_; | |
| 204 } | |
| 205 | |
| 206 const std::set<std::string>& CertReportJobInterceptor::failed_reports() const { | |
| 207 return failed_reports_; | |
| 208 } | |
| 209 | |
| 210 const std::set<std::string>& CertReportJobInterceptor::delayed_reports() const { | |
| 211 return delayed_reports_; | |
| 212 } | |
| 213 | |
| 214 void CertReportJobInterceptor::SetFailureModeOnIOThread( | |
| 215 ReportSendingResult expected_report_result) { | |
| 216 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 217 expected_report_result_ = expected_report_result; | |
| 218 } | |
| 219 | |
| 220 void CertReportJobInterceptor::ResumeOnIOThread() { | |
| 221 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 222 EXPECT_EQ(REPORTS_DELAY, expected_report_result_); | |
| 223 if (delayed_request_) | |
| 224 delayed_request_->Resume(); | |
| 225 } | |
| 226 | |
| 227 CertificateReportingServiceTestBase::CertificateReportingServiceTestBase() | |
| 228 : network_delegate_( | |
| 229 base::Bind(&CertificateReportingServiceTestBase::OnRequestDeleted, | |
| 230 base::Unretained(this))), | |
| 231 num_request_deletions_to_wait_for_(0), | |
| 232 num_deleted_requests_(0) { | |
| 233 memset(server_private_key_, 1, sizeof(server_private_key_)); | |
| 234 crypto::curve25519::ScalarBaseMult(server_private_key_, server_public_key_); | |
| 235 } | |
| 236 | |
| 237 CertificateReportingServiceTestBase::~CertificateReportingServiceTestBase() {} | |
| 238 | |
| 239 void CertificateReportingServiceTestBase::SetUpInterceptor() { | |
| 240 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 241 url_request_interceptor_ = | |
| 242 new certificate_reporting_test_utils::CertReportJobInterceptor( | |
| 243 certificate_reporting_test_utils::REPORTS_FAIL, server_private_key_); | |
| 244 | |
| 245 content::BrowserThread::PostTask( | |
| 246 content::BrowserThread::IO, FROM_HERE, | |
| 247 base::Bind(&SetUpURLHandlersOnIOThread, | |
| 248 base::Passed(std::unique_ptr<net::URLRequestInterceptor>( | |
| 249 url_request_interceptor_)))); | |
| 250 } | |
| 251 | |
| 252 void CertificateReportingServiceTestBase::ClearObservedReports() { | |
| 253 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 254 url_request_interceptor_->Clear(); | |
| 255 } | |
| 256 | |
| 257 uint32_t CertificateReportingServiceTestBase::server_public_key_version() | |
| 258 const { | |
| 259 return kServerPublicKeyTestVersion; | |
| 260 } | |
| 261 | |
| 262 // Changes the behavior of report uploads to fail or succeed. | |
| 263 void CertificateReportingServiceTestBase::SetFailureMode( | |
| 264 certificate_reporting_test_utils::ReportSendingResult | |
| 265 expected_report_result) { | |
| 266 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 267 url_request_interceptor_->SetFailureMode(expected_report_result); | |
| 268 } | |
| 269 | |
| 270 void CertificateReportingServiceTestBase::OnRequestDeleted( | |
| 271 net::URLRequest* request) { | |
| 272 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 273 num_deleted_requests_++; | |
| 274 if (!run_loop_) { | |
| 275 return; | |
| 276 } | |
| 277 EXPECT_LE(num_deleted_requests_, num_request_deletions_to_wait_for_); | |
| 278 if (num_deleted_requests_ == num_request_deletions_to_wait_for_) { | |
| 279 num_request_deletions_to_wait_for_ = 0; | |
| 280 num_deleted_requests_ = 0; | |
| 281 run_loop_->Quit(); | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 CertificateReportingServiceTestBase::ReportExpectation::ReportExpectation() {} | |
| 286 | |
| 287 CertificateReportingServiceTestBase::ReportExpectation::~ReportExpectation() {} | |
| 288 | |
| 289 CertificateReportingServiceTestBase::ReportExpectation& | |
| 290 CertificateReportingServiceTestBase::ReportExpectation::Successful( | |
|
estark
2016/12/07 00:42:55
Oh, cool, I've never seen this pattern in chrome b
meacer
2016/12/07 21:37:33
Yes, but the unit tests only check for a single ty
| |
| 291 const std::set<std::string>& reports) { | |
| 292 successful_reports = reports; | |
| 293 return *this; | |
| 294 } | |
| 295 | |
| 296 CertificateReportingServiceTestBase::ReportExpectation& | |
| 297 CertificateReportingServiceTestBase::ReportExpectation::Failed( | |
| 298 const std::set<std::string>& reports) { | |
| 299 failed_reports = reports; | |
| 300 return *this; | |
| 301 } | |
| 302 | |
| 303 CertificateReportingServiceTestBase::ReportExpectation& | |
| 304 CertificateReportingServiceTestBase::ReportExpectation::Delayed( | |
| 305 const std::set<std::string>& reports) { | |
| 306 delayed_reports = reports; | |
| 307 return *this; | |
| 308 } | |
| 309 | |
| 310 void CertificateReportingServiceTestBase::WaitForRequestDeletions( | |
| 311 const ReportExpectation& expectation) { | |
| 312 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 313 DCHECK(!run_loop_); | |
| 314 | |
| 315 const int num_request_deletions_to_wait_for = | |
| 316 expectation.successful_reports.size() + | |
| 317 expectation.failed_reports.size() + expectation.delayed_reports.size(); | |
| 318 | |
| 319 if (num_deleted_requests_ < num_request_deletions_to_wait_for) { | |
| 320 num_request_deletions_to_wait_for_ = num_request_deletions_to_wait_for; | |
| 321 run_loop_.reset(new base::RunLoop()); | |
| 322 run_loop_->Run(); | |
| 323 EXPECT_EQ(0, num_deleted_requests_); | |
| 324 EXPECT_EQ(0, num_request_deletions_to_wait_for_); | |
| 325 } else if (num_deleted_requests_ == num_request_deletions_to_wait_for) { | |
| 326 num_deleted_requests_ = 0; | |
| 327 num_request_deletions_to_wait_for_ = 0; | |
| 328 } else { | |
| 329 NOTREACHED() << "Observed unexpected report"; | |
|
estark
2016/12/07 00:42:55
nit: maybe make this an ASSERT_LE above line 319
meacer
2016/12/07 21:37:33
Done.
| |
| 330 } | |
| 331 | |
| 332 EXPECT_EQ(expectation.successful_reports, | |
| 333 url_request_interceptor_->successful_reports()); | |
| 334 EXPECT_EQ(expectation.failed_reports, | |
| 335 url_request_interceptor_->failed_reports()); | |
| 336 EXPECT_EQ(expectation.delayed_reports, | |
| 337 url_request_interceptor_->delayed_reports()); | |
| 338 ClearObservedReports(); | |
| 339 } | |
| 340 | |
| 341 void CertificateReportingServiceTestBase::ResumeDelayedRequest() { | |
| 342 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 343 url_request_interceptor_->Resume(); | |
| 344 } | |
| 345 | |
| 346 } // namespace certificate_reporting_test_utils | |
| OLD | NEW |