blob: e17b1c20127ede60a0a164ece4c670e1c4423a22 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#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 <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 net
namespace unexportable_keys {
class UnexportableKeyService;
}
namespace net::device_bound_sessions {
class SessionStore;
struct DeferredURLRequest {
DeferredURLRequest(const URLRequest* request,
SessionService::RefreshCompleteCallback restart_callback,
SessionService::RefreshCompleteCallback continue_callback);
DeferredURLRequest(DeferredURLRequest&& other) noexcept;
DeferredURLRequest& operator=(DeferredURLRequest&& other) noexcept;
~DeferredURLRequest();
raw_ptr<const URLRequest> request = nullptr;
base::ElapsedTimer timer;
SessionService::RefreshCompleteCallback restart_callback;
SessionService::RefreshCompleteCallback continue_callback;
};
class NET_EXPORT SessionServiceImpl : public SessionService {
public:
SessionServiceImpl(unexportable_keys::UnexportableKeyService& key_service,
const URLRequestContext* request_context,
SessionStore* store);
~SessionServiceImpl() override;
// Loads saved session data from disk if a `SessionStore` object is provided
// during construction. Otherwise, it is a no-op.
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,
const FirstPartySetMetadata& first_party_set_metadata) override;
void DeferRequestForRefresh(
URLRequest* request,
DeferralParams deferral,
RefreshCompleteCallback restart_callback,
RefreshCompleteCallback continue_callback) override;
void SetChallengeForBoundSession(OnAccessCallback on_access_callback,
const GURL& request_url,
const SessionChallengeParam& param) override;
void GetAllSessionsAsync(
base::OnceCallback<void(const std::vector<SessionKey>&)> callback)
override;
void DeleteSessionAndNotify(
const SchemefulSite& site,
const Session::Id& id,
SessionService::OnAccessCallback per_request_callback) override;
void DeleteAllSessions(
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;
Session* GetSession(const SchemefulSite& site,
const Session::Id& session_id) const;
private:
friend class SessionServiceImplWithStoreTest;
// The key is the site (eTLD+1) of the session's origin.
using SessionsMap = std::multimap<SchemefulSite, std::unique_ptr<Session>>;
using DeferredRequestsMap =
std::unordered_map<Session::Id,
absl::InlinedVector<DeferredURLRequest, 1>>;
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>;
void OnLoadSessionsComplete(SessionsMap sessions);
void OnRegistrationComplete(
OnAccessCallback on_access_callback,
base::expected<SessionParams, SessionError> params_or_error);
void OnRefreshRequestCompletion(
OnAccessCallback on_access_callback,
SchemefulSite site,
Session::Id session_id,
base::expected<SessionParams, SessionError> params_or_error);
void AddSession(const SchemefulSite& site, std::unique_ptr<Session> session);
void UnblockDeferredRequests(const Session::Id& session_id,
bool is_cookie_refreshed);
// Get all the unexpired sessions for a given site. This also removes
// expired sessions for the site and extends the TTL of used sessions.
std::pair<SessionsMap::iterator, SessionsMap::iterator> GetSessionsForSite(
const SchemefulSite& site);
// Remove a session from the session map. It also clears the session
// from `session_store_` and notifies any observers (including
// `per_request_callback`) about the termination.
// Return the iterator to the next session in the map.
[[nodiscard]] SessionsMap::iterator DeleteSessionAndNotifyInternal(
SessionsMap::iterator it,
SessionService::OnAccessCallback per_request_callback);
// Notify all observers about an access to a session. Will update
// `per_request_callback` unconditionally, and any observers in
// `observers_` which have a URL in the scope of `session`.
void NotifySessionAccess(
SessionService::OnAccessCallback per_request_callback,
SessionAccess::AccessType access_type,
const SchemefulSite& site,
const Session& session);
// Remove an observer by site and pointer.
void RemoveObserver(net::SchemefulSite site, Observer* observer);
// Helper function encapsulating the processing of registration
SessionError::ErrorType OnRegistrationCompleteInternal(
OnAccessCallback on_access_callback,
base::expected<SessionParams, SessionError> params_or_error);
// Helper function encapsulating the processing of refresh
SessionError::ErrorType OnRefreshRequestCompletionInternal(
OnAccessCallback on_access_callback,
const SchemefulSite& site,
const Session::Id& session_id,
base::expected<SessionParams, SessionError> params_or_error);
// Callback after unwrapping a session key
void OnSessionKeyRestored(URLRequest* request,
const SchemefulSite& site,
const Session::Id& session_id,
Session::KeyIdOrError key_id_or_error);
// Helper function for starting a refresh
void RefreshSessionInternal(URLRequest* request,
const SchemefulSite& site,
Session* session,
unexportable_keys::UnexportableKeyId key_id);
// Whether the site has exceeded its refresh quota.
bool RefreshQuotaExceeded(const SchemefulSite& site);
// Whether we are waiting on the initial load of saved sessions to complete.
bool pending_initialization_ = false;
// Functions to call once initialization completes.
std::vector<base::OnceClosure> queued_operations_;
// Number of requests deferred due to pending initialization.
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;
// When true, the refresh quota is not enforced. This is only ever set to
// true for testing purposes.
bool ignore_refresh_quota_ = false;
// Deferred requests are stored by session ID.
DeferredRequestsMap deferred_requests_;
// Storage is similar to how CookieMonster stores its cookies.
SessionsMap unpartitioned_sessions_;
// All observers of sessions.
std::map<net::SchemefulSite, ObserverSet> observers_by_site_;
// Per-site session refresh quota. In order to be robust across
// session parameter changes, we enforce refresh quota for a site.
std::map<net::SchemefulSite, std::vector<base::TimeTicks>> refresh_times_;
base::WeakPtrFactory<SessionServiceImpl> weak_factory_{this};
};
} // namespace net::device_bound_sessions
#endif // NET_DEVICE_BOUND_SESSIONS_SESSION_SERVICE_IMPL_H_