#ifndef NET_DEVICE_BOUND_SESSIONS_SESSION_SERVICE_IMPL_H_
#define NET_DEVICE_BOUND_SESSIONS_SESSION_SERVICE_IMPL_H_
#include <map>
#include <memory>
#include <optional>
#include <ranges>
#include <string>
#include <unordered_map>
#include <vector>
#include "base/containers/unique_ptr_adapters.h"
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/elapsed_timer.h"
#include "net/base/net_export.h"
#include "net/device_bound_sessions/registration_fetcher.h"
#include "net/device_bound_sessions/registration_fetcher_param.h"
#include "net/device_bound_sessions/session.h"
#include "net/device_bound_sessions/session_key.h"
#include "net/device_bound_sessions/session_service.h"
namespace net {
class URLRequest;
class URLRequestContext;
class SchemefulSite;
}
namespace unexportable_keys {
class UnexportableKeyService;
}
namespace net::device_bound_sessions {
class SessionStore;
struct DeferredURLRequest {
explicit DeferredURLRequest(SessionService::RefreshCompleteCallback callback);
DeferredURLRequest(DeferredURLRequest&& other) noexcept;
DeferredURLRequest& operator=(DeferredURLRequest&& other) noexcept;
~DeferredURLRequest();
base::ElapsedTimer timer;
SessionService::RefreshCompleteCallback callback;
};
class NET_EXPORT SessionServiceImpl : public SessionService {
public:
enum class ProactiveRefreshAttempt {
kExistingDeferringRefresh = 0,
kExistingProactiveRefresh = 1,
kMissingKey = 2,
kAttempted = 3,
kPreviousFailedProactiveRefresh = 4,
kSigningQuota = 5,
kBackoff = 6,
kMaxValue = kBackoff,
};
SessionServiceImpl(unexportable_keys::UnexportableKeyService& key_service,
const URLRequestContext* request_context,
SessionStore* store);
~SessionServiceImpl() override;
void LoadSessionsAsync();
void RegisterBoundSession(
OnAccessCallback on_access_callback,
RegistrationFetcherParam registration_params,
const IsolationInfo& isolation_info,
const NetLogWithSource& net_log,
const std::optional<url::Origin>& original_request_initiator) override;
std::optional<DeferralParams> ShouldDefer(
URLRequest* request,
HttpRequestHeaders* extra_headers,
const FirstPartySetMetadata& first_party_set_metadata) override;
void DeferRequestForRefresh(URLRequest* request,
DeferralParams deferral,
RefreshCompleteCallback callback) override;
void SetChallengeForBoundSession(
OnAccessCallback on_access_callback,
const URLRequest& request,
const FirstPartySetMetadata& first_party_set_metadata,
const SessionChallengeParam& param) override;
void GetAllSessionsAsync(
base::OnceCallback<void(const std::vector<SessionKey>&)> callback)
override;
void DeleteSessionAndNotify(
DeletionReason reason,
const SessionKey& session_key,
SessionService::OnAccessCallback per_request_callback) override;
void DeleteAllSessions(
DeletionReason reason,
std::optional<base::Time> created_after_time,
std::optional<base::Time> created_before_time,
base::RepeatingCallback<bool(const url::Origin&,
const net::SchemefulSite&)>
origin_and_site_matcher,
base::OnceClosure completion_callback) override;
base::ScopedClosureRunner AddObserver(
const GURL& url,
base::RepeatingCallback<void(const SessionAccess&)> callback) override;
const Session* GetSession(const SessionKey& session_key) const override;
void AddSession(
const SchemefulSite& site,
SessionParams params,
base::span<const uint8_t> wrapped_key,
base::OnceCallback<void(SessionError::ErrorType)> callback) override;
const SignedRefreshChallenge* GetLatestSignedRefreshChallenge(
const SessionKey& session_key) override;
void SetLatestSignedRefreshChallenge(
SessionKey session_key,
SignedRefreshChallenge signed_refresh_challenge) override;
bool SigningQuotaExceeded(const SchemefulSite& site) override;
void AddSigningOccurrence(const SchemefulSite& site) override;
Session* GetSession(const SessionKey& session_key);
private:
friend class SessionServiceImplWithStoreTest;
using SessionsMap = std::map<SessionKey, std::unique_ptr<Session>>;
using DeferredRequestsMap =
std::map<SessionKey, absl::InlinedVector<DeferredURLRequest, 1>>;
using ProactiveRefreshMap = std::map<SessionKey, base::ElapsedTimer>;
using LatestSignedRefreshChallengesMap =
std::map<SessionKey, SignedRefreshChallenge>;
struct Observer {
Observer(const GURL& url,
base::RepeatingCallback<void(const SessionAccess&)> callback);
Observer(const Observer&) = delete;
Observer& operator=(const Observer&) = delete;
~Observer();
GURL url;
base::RepeatingCallback<void(const SessionAccess&)> callback;
};
using ObserverSet =
std::set<std::unique_ptr<Observer>, base::UniquePtrComparator>;
enum class RefreshTrigger {
kMissingCookie,
kProactive,
};
void OnLoadSessionsComplete(SessionsMap sessions);
void OnRegistrationComplete(OnAccessCallback on_access_callback,
bool is_google_subdomain_for_histograms,
bool is_federated_registration_for_histograms,
RegistrationFetcher* fetcher,
RegistrationResult result);
void OnRefreshRequestCompletion(RefreshTrigger trigger,
OnAccessCallback on_access_callback,
SessionKey session_key,
RegistrationFetcher* fetcher,
RegistrationResult result);
void AddSession(const SchemefulSite& site, std::unique_ptr<Session> session);
void UnblockDeferredRequests(
const SessionKey& session_key,
RefreshResult result,
std::optional<bool> is_proactive_refresh_candidate = std::nullopt,
std::optional<base::TimeDelta> minimum_proactive_refresh_threshold =
std::nullopt);
std::ranges::subrange<SessionsMap::iterator> GetSessionsForSite(
const SchemefulSite& site);
void DeleteSessionAndNotifyInternal(
DeletionReason reason,
SessionsMap::iterator it,
SessionService::OnAccessCallback per_request_callback);
void NotifySessionAccess(
SessionService::OnAccessCallback per_request_callback,
SessionAccess::AccessType access_type,
const SessionKey& session_key,
const Session& session);
void RemoveObserver(net::SchemefulSite site, Observer* observer);
SessionError::ErrorType OnRegistrationCompleteInternal(
OnAccessCallback on_access_callback,
RegistrationFetcher* fetcher,
RegistrationResult result);
SessionError::ErrorType OnRefreshRequestCompletionInternal(
OnAccessCallback on_access_callback,
const SessionKey& session_key,
RegistrationFetcher* fetcher,
RegistrationResult result);
void RestoreSessionKey(
const SessionKey& session_key,
OnAccessCallback on_access_callback,
base::OnceCallback<
void(std::optional<unexportable_keys::UnexportableKeyId>)> callback);
void OnSessionKeyRestored(
const SessionKey& session_key,
OnAccessCallback on_access_callback,
base::OnceCallback<
void(std::optional<unexportable_keys::UnexportableKeyId>)> callback,
Session::KeyIdOrError key_id_or_error);
void RefreshSessionInternal(
RefreshTrigger trigger,
base::WeakPtr<URLRequest> request,
const SessionKey& session_key,
std::optional<unexportable_keys::UnexportableKeyId> key_id);
bool RefreshQuotaExceeded(const SchemefulSite& site);
void AddDebugHeader(URLRequest* request);
void RemoveFetcher(RegistrationFetcher* fetcher);
void GetFederatedProviderSessionIfValid(
GURL provider_url,
Session::Id provider_session_id,
std::string provider_key_thumbprint,
OnAccessCallback on_access_callback,
base::OnceCallback<void(base::expected<Session*, SessionError>)>
callback);
void CheckFederatedProviderKey(
SessionKey provider_session_key,
std::string provider_key_thumbprint,
base::OnceCallback<void(base::expected<Session*, SessionError>)> callback,
std::optional<unexportable_keys::UnexportableKeyId> provider_key);
void OnAddSessionKeyRestored(
const SchemefulSite& site,
SessionParams params,
base::OnceCallback<void(SessionError::ErrorType)> callback,
unexportable_keys::ServiceErrorOr<unexportable_keys::UnexportableKeyId>
key_or_error);
void MaybeStartProactiveRefresh(
SessionService::OnAccessCallback per_request_callback,
URLRequest* request,
const SessionKey& session_key,
base::TimeDelta minimum_cookie_lifetime);
void RegisterBoundSessionInternal(
OnAccessCallback on_access_callback,
RegistrationFetcherParam registration_params,
const IsolationInfo& isolation_info,
const NetLogWithSource& net_log,
const std::optional<url::Origin>& original_request_initiator,
base::expected<Session*, SessionError> federated_provider_session);
bool pending_initialization_ = false;
std::vector<base::OnceClosure> queued_operations_;
size_t requests_before_initialization_ = 0;
const raw_ref<unexportable_keys::UnexportableKeyService> key_service_;
raw_ptr<const URLRequestContext> context_;
raw_ptr<SessionStore> session_store_ = nullptr;
bool ignore_refresh_quota_ = false;
DeferredRequestsMap deferred_requests_;
ProactiveRefreshMap proactive_requests_;
SessionsMap unpartitioned_sessions_;
std::map<net::SchemefulSite, ObserverSet> observers_by_site_;
std::map<net::SchemefulSite, std::vector<base::TimeTicks>> refresh_times_;
std::map<net::SchemefulSite, SessionError> refresh_last_result_;
std::map<net::SchemefulSite, std::vector<base::TimeTicks>> signing_times_;
LatestSignedRefreshChallengesMap latest_signed_refresh_challenges_;
std::set<std::unique_ptr<RegistrationFetcher>, base::UniquePtrComparator>
registration_fetchers_;
base::WeakPtrFactory<SessionServiceImpl> weak_factory_{this};
};
}
#endif