File: affiliation_fetch_throttler.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (159 lines) | stat: -rw-r--r-- 7,357 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_AFFILIATIONS_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_H_
#define COMPONENTS_AFFILIATIONS_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_H_

#include <stdint.h>

#include <memory>

#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "net/base/backoff_entry.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/network_connection_tracker.h"

namespace base {
class TickClock;
class SequencedTaskRunner;
}  // namespace base

namespace affiliations {

class AffiliationFetchThrottlerDelegate;

// Implements the throttling logic that the AffiliationBackend will use when it
// needs to issue requests over the network to fetch affiliation information.
//
// This class manages only the scheduling of the requests. It is up to the
// consumer (the AffiliationBackend) to actually assemble and send the requests,
// to report back with their status, and to retry them if desired.
// The process goes like this:
//   1.) The consumer calls SignalNetworkRequestNeeded().
//   2.) Once appropriate, OnCanSendNetworkRequest() is called on the delegate.
//   3.) The consumer sends the request, and waits until it completes.
//   4.) The consumer calls InformOfNetworkRequestComplete().
// Note that only a single request at a time is supported.
//
// If the request fails in Step 3, the consumer should not automatically retry
// it. Instead it should always proceed to Step 4, and then -- if retrying the
// request is desired -- proceed immediately to Step 1. That is, it should act
// as if another request was needed right away.
//
// Essentially, this class implements exponential backoff in case of network and
// server errors with 2 additional constraints:
// 1. No requests will be issued in the first place without network
// connectivity. This prevents the exponential backoff delay from growing huge
// during long offline periods, so that requests will not be held back for too
// long after connectivity is restored.
// 2. When a request fails in a way that indicates an immediate retry won't
// succeed (e.g., the server returns a 429 status), throttler will significantly
// increase the delay before allowing the next request. This is needed to
// prevent overwhelming the server, especially during potential outages.
// However, since the requested facet is already displayed in the Password
// Manager UI, Affiliation Backend must still eventually retrieve the server's
// response to ensure the UI information is accurate.
class AffiliationFetchThrottler
    : public network::NetworkConnectionTracker::NetworkConnectionObserver {
 public:
  // Creates an instance that will use |tick_clock| as its tick source, and will
  // post to |task_runner| to call the |delegate|'s OnSendNetworkRequest(). The
  // |delegate| and |tick_clock| should outlive the throttler.
  AffiliationFetchThrottler(
      AffiliationFetchThrottlerDelegate* delegate,
      const scoped_refptr<base::SequencedTaskRunner>& task_runner,
      network::NetworkConnectionTracker* network_connection_tracker,
      const base::TickClock* tick_clock);

  AffiliationFetchThrottler(const AffiliationFetchThrottler&) = delete;
  AffiliationFetchThrottler& operator=(const AffiliationFetchThrottler&) =
      delete;

  ~AffiliationFetchThrottler() override;

  // Signals to the throttling logic that a network request is needed, and that
  // OnCanSendNetworkRequest() should be called as soon as the request can be
  // sent. OnCanSendNetworkRequest() will always be called asynchronously.
  //
  // Calls to this method will be ignored when a request is already known to be
  // needed or while a request is in flight. To signal that another request will
  // be needed right away after the current one, call this method after calling
  // InformOfNetworkRequestComplete().
  virtual void SignalNetworkRequestNeeded();

  // Informs the back-off logic that the in-flight network request has been
  // completed. This will decide if the response code is a retryable error or
  // not and apply a long delay on non-retryable errors.
  virtual void InformOfNetworkRequestComplete(
      bool success,
      std::optional<net::HttpStatusCode> http_status_code);

  // Returns whether there is internet connection or not.
  virtual bool HasInternetConnection() const;

 protected:
  raw_ptr<AffiliationFetchThrottlerDelegate> delegate_;

 private:
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FailedRequests);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           NonRetryableFailedRequestsSetLongDelay);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           GracePeriodAfterConnectivityIsRestored);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           GracePeriodAfterConnectivityIsRestored2);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           GracePeriodAfterConnectivityIsRestored3);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           ConnectivityLostDuringBackoff);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           ConnectivityLostAndRestoredDuringBackoff);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest, FlakyConnectivity);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           ConnectivityLostDuringRequest);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           ConnectivityLostAndRestoredDuringRequest);
  FRIEND_TEST_ALL_PREFIXES(AffiliationFetchThrottlerTest,
                           ConnectivityLostAndRestoredDuringRequest2);

  enum State { IDLE, FETCH_NEEDED, FETCH_IN_FLIGHT };

  // Exponential backoff parameters in case of network and server errors
  static const net::BackoffEntry::Policy kBackoffPolicy;

  // Minimum delay before sending the first request once network connectivity is
  // restored. The fuzzing factor in |kBackoffParameters.jitter_factor| applies.
  static const int64_t kGracePeriodAfterReconnectMs;

  // Minimum delay set after receiving a non-retryable error from Affiliation
  // Server.
  static const int64_t kBackoffAfterNonRetryableErrorHours;

  // Ensures that OnBackoffDelayExpiredCallback() is scheduled to be called back
  // once the |exponential_backoff_| delay expires.
  void EnsureCallbackIsScheduled();

  // Called back when the |exponential_backoff_| delay expires.
  void OnBackoffDelayExpiredCallback();

  // network::NetworkConnectionTracker::NetworkConnectionObserver:
  void OnConnectionChanged(network::mojom::ConnectionType type) override;

  scoped_refptr<base::SequencedTaskRunner> task_runner_;
  raw_ptr<network::NetworkConnectionTracker> network_connection_tracker_;
  raw_ptr<const base::TickClock> tick_clock_;
  State state_;
  bool has_network_connectivity_;
  bool is_fetch_scheduled_;
  std::unique_ptr<net::BackoffEntry> exponential_backoff_;

  base::WeakPtrFactory<AffiliationFetchThrottler> weak_ptr_factory_{this};
};

}  // namespace affiliations

#endif  // COMPONENTS_AFFILIATIONS_CORE_BROWSER_AFFILIATION_FETCH_THROTTLER_H_