blob: 5ce935d50660c1fa0b347745597181a6cd6e2bfa [file] [log] [blame]
// Copyright (c) 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "quic/core/crypto/quic_client_session_cache.h"
#include "quic/core/quic_clock.h"
namespace quic {
namespace {
const size_t kDefaultMaxEntries = 1024;
// Returns false if the SSL |session| doesn't exist or it is expired at |now|.
bool IsValid(SSL_SESSION* session, uint64_t now) {
if (!session) return false;
// now_u64 may be slightly behind because of differences in how
// time is calculated at this layer versus BoringSSL.
// Add a second of wiggle room to account for this.
return !(now + 1 < SSL_SESSION_get_time(session) ||
now >= SSL_SESSION_get_time(session) +
SSL_SESSION_get_timeout(session));
}
bool DoApplicationStatesMatch(const ApplicationState* state,
ApplicationState* other) {
if ((state && !other) || (!state && other)) return false;
if ((!state && !other) || *state == *other) return true;
return false;
}
} // namespace
QuicClientSessionCache::QuicClientSessionCache()
: QuicClientSessionCache(kDefaultMaxEntries) {}
QuicClientSessionCache::QuicClientSessionCache(size_t max_entries)
: cache_(max_entries) {}
QuicClientSessionCache::~QuicClientSessionCache() { Clear(); }
void QuicClientSessionCache::Insert(const QuicServerId& server_id,
bssl::UniquePtr<SSL_SESSION> session,
const TransportParameters& params,
const ApplicationState* application_state) {
QUICHE_DCHECK(session) << "TLS session is not inserted into client cache.";
auto iter = cache_.Lookup(server_id);
if (iter == cache_.end()) {
CreateAndInsertEntry(server_id, std::move(session), params,
application_state);
return;
}
QUICHE_DCHECK(iter->second->params);
// The states are both the same, so only need to insert sessions.
if (params == *iter->second->params &&
DoApplicationStatesMatch(application_state,
iter->second->application_state.get())) {
iter->second->PushSession(std::move(session));
return;
}
// Erase the existing entry because this Insert call must come from a
// different QUIC session.
cache_.Erase(iter);
CreateAndInsertEntry(server_id, std::move(session), params,
application_state);
}
std::unique_ptr<QuicResumptionState> QuicClientSessionCache::Lookup(
const QuicServerId& server_id, QuicWallTime now, const SSL_CTX* /*ctx*/) {
auto iter = cache_.Lookup(server_id);
if (iter == cache_.end()) return nullptr;
if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) {
QUIC_DLOG(INFO) << "TLS Session expired for host:" << server_id.host();
cache_.Erase(iter);
return nullptr;
}
auto state = std::make_unique<QuicResumptionState>();
state->tls_session = iter->second->PopSession();
if (iter->second->params != nullptr) {
state->transport_params =
std::make_unique<TransportParameters>(*iter->second->params);
}
if (iter->second->application_state != nullptr) {
state->application_state =
std::make_unique<ApplicationState>(*iter->second->application_state);
}
if (GetQuicReloadableFlag(quic_tls_use_token_in_session_cache) &&
!iter->second->token.empty()) {
state->token = iter->second->token;
// Clear token after use.
iter->second->token.clear();
}
return state;
}
void QuicClientSessionCache::ClearEarlyData(const QuicServerId& server_id) {
auto iter = cache_.Lookup(server_id);
if (iter == cache_.end()) return;
for (auto& session : iter->second->sessions) {
if (session) {
QUIC_DLOG(INFO) << "Clear early data for for host: " << server_id.host();
session.reset(SSL_SESSION_copy_without_early_data(session.get()));
}
}
}
void QuicClientSessionCache::OnNewTokenReceived(const QuicServerId& server_id,
absl::string_view token) {
if (token.empty()) {
return;
}
auto iter = cache_.Lookup(server_id);
if (iter == cache_.end()) {
return;
}
iter->second->token = std::string(token);
}
void QuicClientSessionCache::RemoveExpiredEntries(QuicWallTime now) {
auto iter = cache_.begin();
while (iter != cache_.end()) {
if (!IsValid(iter->second->PeekSession(), now.ToUNIXSeconds())) {
iter = cache_.Erase(iter);
} else {
++iter;
}
}
}
void QuicClientSessionCache::Clear() { cache_.Clear(); }
void QuicClientSessionCache::CreateAndInsertEntry(
const QuicServerId& server_id, bssl::UniquePtr<SSL_SESSION> session,
const TransportParameters& params,
const ApplicationState* application_state) {
auto entry = std::make_unique<Entry>();
entry->PushSession(std::move(session));
entry->params = std::make_unique<TransportParameters>(params);
if (application_state) {
entry->application_state =
std::make_unique<ApplicationState>(*application_state);
}
cache_.Insert(server_id, std::move(entry));
}
QuicClientSessionCache::Entry::Entry() = default;
QuicClientSessionCache::Entry::Entry(Entry&&) = default;
QuicClientSessionCache::Entry::~Entry() = default;
void QuicClientSessionCache::Entry::PushSession(
bssl::UniquePtr<SSL_SESSION> session) {
if (sessions[0] != nullptr) {
sessions[1] = std::move(sessions[0]);
}
sessions[0] = std::move(session);
}
bssl::UniquePtr<SSL_SESSION> QuicClientSessionCache::Entry::PopSession() {
if (sessions[0] == nullptr) return nullptr;
bssl::UniquePtr<SSL_SESSION> session = std::move(sessions[0]);
sessions[0] = std::move(sessions[1]);
sessions[1] = nullptr;
return session;
}
SSL_SESSION* QuicClientSessionCache::Entry::PeekSession() {
return sessions[0].get();
}
} // namespace quic