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 "net/reporting/reporting_service.h" |
| 6 |
| 7 #include <string> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/bind.h" |
| 11 #include "base/json/json_reader.h" |
| 12 #include "base/memory/ptr_util.h" |
| 13 #include "base/test/simple_test_tick_clock.h" |
| 14 #include "base/values.h" |
| 15 #include "net/reporting/reporting_uploader.h" |
| 16 #include "testing/gtest/include/gtest/gtest.h" |
| 17 #include "url/gurl.h" |
| 18 |
| 19 namespace net { |
| 20 namespace { |
| 21 |
| 22 class MockUploader : public ReportingUploader { |
| 23 public: |
| 24 class PendingDelivery { |
| 25 public: |
| 26 PendingDelivery(MockUploader* uploader, |
| 27 const GURL& url, |
| 28 const std::string& json, |
| 29 const Callback& callback) |
| 30 : uploader_(uploader), url_(url), json_(json), callback_(callback) { |
| 31 DCHECK(uploader_); |
| 32 } |
| 33 |
| 34 ~PendingDelivery() {} |
| 35 |
| 36 void Complete(Outcome outcome) { |
| 37 callback_.Run(outcome); |
| 38 // Deletes |this|. |
| 39 uploader_->OnDeliveryComplete(this); |
| 40 } |
| 41 |
| 42 const GURL& url() const { return url_; } |
| 43 const std::string& json() const { return json_; } |
| 44 |
| 45 private: |
| 46 MockUploader* uploader_; |
| 47 GURL url_; |
| 48 std::string json_; |
| 49 Callback callback_; |
| 50 }; |
| 51 |
| 52 MockUploader() {} |
| 53 ~MockUploader() override {} |
| 54 |
| 55 void AttemptDelivery(const GURL& url, |
| 56 const std::string& json, |
| 57 const Callback& callback) override { |
| 58 deliveries_.push_back( |
| 59 base::MakeUnique<PendingDelivery>(this, url, json, callback)); |
| 60 } |
| 61 |
| 62 const std::vector<std::unique_ptr<PendingDelivery>>& GetPendingDeliveries() |
| 63 const { |
| 64 return deliveries_; |
| 65 } |
| 66 |
| 67 void OnDeliveryComplete(PendingDelivery* delivery) { |
| 68 for (auto it = deliveries_.begin(); it != deliveries_.end(); ++it) { |
| 69 if (it->get() == delivery) { |
| 70 deliveries_.erase(it); |
| 71 return; |
| 72 } |
| 73 } |
| 74 NOTREACHED(); |
| 75 } |
| 76 |
| 77 private: |
| 78 std::vector<std::unique_ptr<PendingDelivery>> deliveries_; |
| 79 }; |
| 80 |
| 81 class ReportingServiceTest : public ::testing::Test { |
| 82 protected: |
| 83 ReportingServiceTest() {} |
| 84 |
| 85 void CreateServiceWithDefaultPolicy() { |
| 86 ReportingService::Policy policy = ReportingService::Policy::GetDefault(); |
| 87 CreateServiceWithPolicy(policy); |
| 88 } |
| 89 |
| 90 void CreateServiceWithPolicy(const ReportingService::Policy& policy) { |
| 91 policy_ = policy; |
| 92 service_ = base::MakeUnique<ReportingService>(policy); |
| 93 clock_ = new base::SimpleTestTickClock(); |
| 94 service_->set_clock_for_testing(base::WrapUnique(clock_)); |
| 95 uploader_ = new MockUploader(); |
| 96 service_->set_uploader(base::WrapUnique(uploader_)); |
| 97 } |
| 98 |
| 99 void QueueReport(const GURL& url) { |
| 100 service_->QueueReport(base::MakeUnique<base::DictionaryValue>(), url, |
| 101 url.GetOrigin(), "default", "test"); |
| 102 } |
| 103 |
| 104 const std::vector<std::unique_ptr<MockUploader::PendingDelivery>>& |
| 105 GetPendingDeliveries() { |
| 106 return uploader_->GetPendingDeliveries(); |
| 107 } |
| 108 |
| 109 bool HasEndpoint(const std::string& endpoint_url) { |
| 110 return service_->HasEndpointForTesting(GURL(endpoint_url)); |
| 111 } |
| 112 |
| 113 bool HasClient(const std::string& endpoint_url, |
| 114 const std::string& origin_url) { |
| 115 return service_->HasClientForTesting(GURL(endpoint_url), GURL(origin_url)); |
| 116 } |
| 117 |
| 118 int GetEndpointFailures(const std::string& endpoint_url) { |
| 119 return service_->GetEndpointFailuresForTesting(GURL(endpoint_url)); |
| 120 } |
| 121 |
| 122 // |policy_| is a copy of the policy used to configure |service_|, for easier |
| 123 // reference. |
| 124 ReportingService::Policy policy_; |
| 125 // |clock_| and |uploader_| are owned by |service_|, not the test. |
| 126 base::SimpleTestTickClock* clock_; |
| 127 MockUploader* uploader_; |
| 128 std::unique_ptr<ReportingService> service_; |
| 129 }; |
| 130 |
| 131 TEST_F(ReportingServiceTest, ProcessHeaderFromInsecureOrigin) { |
| 132 CreateServiceWithDefaultPolicy(); |
| 133 |
| 134 service_->ProcessHeader(GURL("http://insecure/"), |
| 135 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 136 EXPECT_FALSE(HasClient("https://endpoint/", "http://insecure")); |
| 137 } |
| 138 |
| 139 TEST_F(ReportingServiceTest, ProcessInvalidHeaders) { |
| 140 static const struct { |
| 141 const char* header_value; |
| 142 const char* description; |
| 143 } kInvalidHeaderTestCases[] = { |
| 144 {"{\"max-age\":1}", "missing url"}, |
| 145 {"{\"url\":0,\"max-age\":1}", "non-string url"}, |
| 146 {"{\"url\":\"http://insecure/\",\"max-age\":1}", "insecure url"}, |
| 147 |
| 148 {"{\"url\":\"https://endpoint/\"}", "missing max-age"}, |
| 149 {"{\"url\":\"https://endpoint/\",\"max-age\":\"\"}", |
| 150 "non-integer max-age"}, |
| 151 {"{\"url\":\"https://endpoint/\",\"max-age\":-1}", "negative max-age"}, |
| 152 |
| 153 {"{\"url\":\"https://endpoint/\",\"max-age\":1,\"group\":0}", |
| 154 "non-string group"}, |
| 155 |
| 156 {"{\"url\":\"https://endpoint/\",\"max-age\":1,\"includeSubdomains\":0}", |
| 157 "non-boolean includeSubdomains"}, |
| 158 }; |
| 159 |
| 160 CreateServiceWithDefaultPolicy(); |
| 161 |
| 162 for (size_t i = 0; i < arraysize(kInvalidHeaderTestCases); ++i) { |
| 163 auto& test_case = kInvalidHeaderTestCases[i]; |
| 164 service_->ProcessHeader(GURL("https://origin/"), test_case.header_value); |
| 165 EXPECT_FALSE(HasClient("https://endpoint/", "https://origin/")); |
| 166 } |
| 167 } |
| 168 |
| 169 TEST_F(ReportingServiceTest, ProcessValidHeader) { |
| 170 CreateServiceWithDefaultPolicy(); |
| 171 |
| 172 service_->ProcessHeader(GURL("https://origin/"), |
| 173 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 174 EXPECT_TRUE(HasClient("https://endpoint/", "https://origin/")); |
| 175 |
| 176 #if 0 |
| 177 EXPECT_EQ(1u, GetCache().GetEndpoints().size()); |
| 178 const std::unique_ptr<ReportingEndpoint>* endpoint_ptr = |
| 179 GetCache().GetEndpoint(GURL("https://endpoint/")); |
| 180 ASSERT_TRUE(endpoint_ptr); |
| 181 const std::unique_ptr<ReportingEndpoint>& endpoint = *endpoint_ptr; |
| 182 EXPECT_EQ(GURL("https://endpoint/"), endpoint->url); |
| 183 |
| 184 EXPECT_EQ(1u, endpoint->clients.count(GURL("https://origin/"))); |
| 185 auto client_it = endpoint->clients.find(GURL("https://origin/")); |
| 186 ASSERT_TRUE(client_it != endpoint->clients.end()); |
| 187 const ReportingClient& client = client_it->second; |
| 188 EXPECT_EQ(GURL("https://origin"), client.origin); |
| 189 EXPECT_FALSE(client.subdomains); |
| 190 EXPECT_EQ("default", client.group); |
| 191 EXPECT_EQ(base::TimeDelta::FromSeconds(1), client.ttl); |
| 192 #endif |
| 193 } |
| 194 |
| 195 TEST_F(ReportingServiceTest, ProcessZeroMaxAgeHeader) { |
| 196 CreateServiceWithDefaultPolicy(); |
| 197 |
| 198 service_->ProcessHeader(GURL("https://origin/"), |
| 199 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 200 EXPECT_TRUE(HasClient("https://endpoint/", "https://origin/")); |
| 201 service_->ProcessHeader(GURL("https://origin/"), |
| 202 "{\"url\":\"https://endpoint/\",\"max-age\":0}"); |
| 203 EXPECT_FALSE(HasClient("https://endpoint/", "https://origin/")); |
| 204 } |
| 205 |
| 206 TEST_F(ReportingServiceTest, DeliverySuccess) { |
| 207 CreateServiceWithDefaultPolicy(); |
| 208 |
| 209 service_->ProcessHeader(GURL("https://origin/"), |
| 210 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 211 QueueReport(GURL("https://origin/path")); |
| 212 service_->SendReports(); |
| 213 |
| 214 ASSERT_EQ(1u, GetPendingDeliveries().size()); |
| 215 const std::unique_ptr<MockUploader::PendingDelivery>& delivery = |
| 216 GetPendingDeliveries()[0]; |
| 217 EXPECT_EQ(GURL("https://endpoint/"), delivery->url()); |
| 218 { |
| 219 auto reports_value = base::JSONReader::Read(delivery->json()); |
| 220 const base::ListValue* reports; |
| 221 ASSERT_TRUE(reports_value->GetAsList(&reports)); |
| 222 ASSERT_EQ(1u, reports->GetSize()); |
| 223 const base::DictionaryValue* report; |
| 224 ASSERT_TRUE(reports->GetDictionary(0u, &report)); |
| 225 std::string type; |
| 226 ASSERT_TRUE(report->GetString("type", &type)); |
| 227 EXPECT_EQ("test", type); |
| 228 std::string url; |
| 229 ASSERT_TRUE(report->GetString("url", &url)); |
| 230 EXPECT_EQ("https://origin/path", url); |
| 231 } |
| 232 |
| 233 delivery->Complete(ReportingUploader::SUCCESS); |
| 234 |
| 235 EXPECT_EQ(0, GetEndpointFailures("https://endpoint/")); |
| 236 } |
| 237 |
| 238 TEST_F(ReportingServiceTest, DeliveryFailure) { |
| 239 CreateServiceWithDefaultPolicy(); |
| 240 |
| 241 service_->ProcessHeader(GURL("https://origin/"), |
| 242 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 243 QueueReport(GURL("https://origin/path")); |
| 244 service_->SendReports(); |
| 245 |
| 246 GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE); |
| 247 |
| 248 EXPECT_EQ(1, GetEndpointFailures("https://endpoint/")); |
| 249 } |
| 250 |
| 251 TEST_F(ReportingServiceTest, DeliveryRemoveEndpoint) { |
| 252 CreateServiceWithDefaultPolicy(); |
| 253 |
| 254 service_->ProcessHeader(GURL("https://origin/"), |
| 255 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 256 QueueReport(GURL("https://origin/path")); |
| 257 service_->SendReports(); |
| 258 |
| 259 GetPendingDeliveries()[0]->Complete(ReportingUploader::REMOVE_ENDPOINT); |
| 260 |
| 261 EXPECT_FALSE(HasClient("https://endpoint/", "https://origin/")); |
| 262 } |
| 263 |
| 264 TEST_F(ReportingServiceTest, NoDeliveryToPendingEndpoint) { |
| 265 CreateServiceWithDefaultPolicy(); |
| 266 |
| 267 service_->ProcessHeader(GURL("https://origin/"), |
| 268 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 269 QueueReport(GURL("https://origin/")); |
| 270 service_->SendReports(); |
| 271 |
| 272 GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE); |
| 273 |
| 274 // Without advancing the clock, the endpoint should still be pending, so the |
| 275 // Manager should not try to deliver reports to it. |
| 276 service_->SendReports(); |
| 277 |
| 278 EXPECT_TRUE(GetPendingDeliveries().empty()); |
| 279 } |
| 280 |
| 281 TEST_F(ReportingServiceTest, NoDeliveryToMismatchedEndpoint) { |
| 282 CreateServiceWithDefaultPolicy(); |
| 283 |
| 284 service_->ProcessHeader(GURL("https://origin1/"), |
| 285 "{\"url\":\"https://endpoint1/\",\"max-age\":1}"); |
| 286 QueueReport(GURL("https://origin2/")); |
| 287 service_->SendReports(); |
| 288 |
| 289 EXPECT_TRUE(GetPendingDeliveries().empty()); |
| 290 } |
| 291 |
| 292 TEST_F(ReportingServiceTest, BatchReportsForSameOrigin) { |
| 293 CreateServiceWithDefaultPolicy(); |
| 294 |
| 295 service_->ProcessHeader(GURL("https://origin/"), |
| 296 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 297 QueueReport(GURL("https://origin/")); |
| 298 QueueReport(GURL("https://origin/")); |
| 299 service_->SendReports(); |
| 300 |
| 301 EXPECT_EQ(1u, GetPendingDeliveries().size()); |
| 302 |
| 303 GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE); |
| 304 } |
| 305 |
| 306 TEST_F(ReportingServiceTest, BatchReportsForSameEndpoint) { |
| 307 CreateServiceWithDefaultPolicy(); |
| 308 |
| 309 service_->ProcessHeader(GURL("https://origin1/"), |
| 310 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 311 service_->ProcessHeader(GURL("https://origin2/"), |
| 312 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 313 QueueReport(GURL("https://origin1/")); |
| 314 QueueReport(GURL("https://origin2/")); |
| 315 service_->SendReports(); |
| 316 |
| 317 EXPECT_EQ(1u, GetPendingDeliveries().size()); |
| 318 |
| 319 GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE); |
| 320 } |
| 321 |
| 322 TEST_F(ReportingServiceTest, ExpiredEndpoint) { |
| 323 CreateServiceWithDefaultPolicy(); |
| 324 |
| 325 service_->ProcessHeader(GURL("https://origin/"), |
| 326 "{\"url\":\"https://endpoint/\",\"max-age\":1}"); |
| 327 EXPECT_TRUE(HasEndpoint("https://endpoint/")); |
| 328 |
| 329 clock_->Advance(base::TimeDelta::FromSeconds(2)); |
| 330 service_->CollectGarbageForTesting(); |
| 331 EXPECT_FALSE(HasEndpoint("https://endpoint/")); |
| 332 } |
| 333 |
| 334 TEST_F(ReportingServiceTest, UnusedEndpoint) { |
| 335 CreateServiceWithDefaultPolicy(); |
| 336 |
| 337 service_->ProcessHeader( |
| 338 GURL("https://origin/"), |
| 339 "{\"url\":\"https://endpoint/\",\"max-age\":999999999}"); |
| 340 EXPECT_TRUE(HasEndpoint("https://endpoint/")); |
| 341 |
| 342 clock_->Advance(2 * policy_.endpoint_lifetime); |
| 343 service_->CollectGarbageForTesting(); |
| 344 EXPECT_FALSE(HasEndpoint("https://endpoint/")); |
| 345 } |
| 346 |
| 347 TEST_F(ReportingServiceTest, FailedEndpoint) { |
| 348 ReportingService::Policy policy = ReportingService::Policy::GetDefault(); |
| 349 policy.endpoint_backoff.initial_delay_ms = 0; |
| 350 policy.endpoint_backoff.maximum_backoff_ms = 0; |
| 351 policy.endpoint_backoff.entry_lifetime_ms = 1000; |
| 352 policy.max_endpoint_failures = 5; |
| 353 policy.max_report_failures = 999; |
| 354 CreateServiceWithPolicy(policy); |
| 355 |
| 356 service_->ProcessHeader( |
| 357 GURL("https://origin/"), |
| 358 "{\"url\":\"https://endpoint/\",\"max-age\":999999999}"); |
| 359 |
| 360 QueueReport(GURL("https://origin/")); |
| 361 |
| 362 for (int i = 0; i < policy_.max_endpoint_failures + 1; i++) { |
| 363 EXPECT_TRUE(HasEndpoint("https://endpoint/")); |
| 364 service_->SendReports(); |
| 365 EXPECT_EQ(1u, GetPendingDeliveries().size()); |
| 366 GetPendingDeliveries()[0]->Complete(ReportingUploader::FAILURE); |
| 367 } |
| 368 service_->CollectGarbageForTesting(); |
| 369 EXPECT_TRUE(HasEndpoint("https://endpoint/")); |
| 370 |
| 371 clock_->Advance(base::TimeDelta::FromMilliseconds( |
| 372 2 * policy_.endpoint_backoff.entry_lifetime_ms)); |
| 373 |
| 374 service_->CollectGarbageForTesting(); |
| 375 EXPECT_FALSE(HasEndpoint("https://endpoint/")); |
| 376 } |
| 377 |
| 378 TEST_F(ReportingServiceTest, EvictedEndpoint) { |
| 379 ReportingService::Policy policy = ReportingService::Policy::GetDefault(); |
| 380 policy.max_endpoint_count = 1; |
| 381 CreateServiceWithPolicy(policy); |
| 382 |
| 383 service_->ProcessHeader(GURL("https://origin1/"), |
| 384 "{\"url\":\"https://endpoint1/\",\"max-age\":1}"); |
| 385 EXPECT_TRUE(HasEndpoint("https://endpoint1")); |
| 386 |
| 387 service_->ProcessHeader(GURL("https://origin2/"), |
| 388 "{\"url\":\"https://endpoint2/\",\"max-age\":1}"); |
| 389 EXPECT_FALSE(HasEndpoint("https://endpoint1")); |
| 390 EXPECT_TRUE(HasEndpoint("https://endpoint2")); |
| 391 } |
| 392 |
| 393 // TODO: Test: |
| 394 // Expired report (happens after lifetime passes -> GC) |
| 395 // Failed report (happens after failed attempt -> attempt) |
| 396 // Evicted report (happens after queue or failed attempt -> queue) |
| 397 // No-action cases (e.g. don't evict pending report). |
| 398 |
| 399 } // namespace |
| 400 } // namespace net |
OLD | NEW |