Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/trusted-server-adapter-fastly/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ fn build_ec_request_state(
match EcContext::read_from_request_with_geo(settings, req, services, geo_info.as_ref()) {
Ok(mut context) => {
context.set_device_signals(device_signals);
context.set_recovery_eligible(is_real_browser && is_navigation_request(req));
(context, None)
}
Err(report) => (EcContext::default(), Some(report)),
Expand Down
10 changes: 5 additions & 5 deletions crates/trusted-server-adapter-fastly/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,9 @@ fn edgezero_main(mut req: FastlyRequest) {
policy.apply_after_route_finalization(&mut response);
}

if let Some(ec_state) = ec_state {
if let Some(mut ec_state) = ec_state {
if let Some(settings) = settings_snapshot.as_deref() {
match apply_edgezero_ec_finalize(settings, &ec_state, &mut response) {
match apply_edgezero_ec_finalize(settings, &mut ec_state, &mut response) {
Ok(partner_registry) => {
send_edgezero_response(response, request_filter_effects.as_ref());
run_edgezero_pull_sync_after_send(settings, &partner_registry, &ec_state);
Expand All @@ -222,7 +222,7 @@ fn edgezero_main(mut req: FastlyRequest) {
} else {
match load_settings_from_config_store() {
Ok(settings) => {
match apply_edgezero_ec_finalize(&settings, &ec_state, &mut response) {
match apply_edgezero_ec_finalize(&settings, &mut ec_state, &mut response) {
Ok(partner_registry) => {
send_edgezero_response(response, request_filter_effects.as_ref());
run_edgezero_pull_sync_after_send(
Expand Down Expand Up @@ -286,7 +286,7 @@ fn apply_entry_point_finalize_headers(

fn apply_edgezero_ec_finalize(
settings: &Settings,
ec_state: &EcFinalizeState,
ec_state: &mut EcFinalizeState,
response: &mut HttpResponse,
) -> Result<PartnerRegistry, Report<TrustedServerError>> {
let partner_registry = PartnerRegistry::from_config(&settings.ec.partners)?;
Expand All @@ -297,7 +297,7 @@ fn apply_edgezero_ec_finalize(
};
ec_finalize_response(
settings,
&ec_state.ec_context,
&mut ec_state.ec_context,
finalize_kv_graph.as_ref(),
&partner_registry,
ec_state.eids_cookie.as_deref(),
Expand Down
56 changes: 27 additions & 29 deletions crates/trusted-server-core/src/auction/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ use crate::constants::COOKIE_TS_EIDS;
use crate::ec::eids::{resolve_partner_ids, to_eids};
use crate::ec::kv::KvIdentityGraph;
use crate::ec::kv_types::MAX_UID_LENGTH;
use crate::ec::log_id;
use crate::ec::prebid_eids::parse_prebid_eids_cookie;
use crate::ec::registry::PartnerRegistry;
use crate::ec::EcContext;
use crate::ec::{EcContext, EcKvSnapshot};
use crate::error::TrustedServerError;
use crate::openrtb::{Eid, Uid};
use crate::platform::RuntimeServices;
Expand Down Expand Up @@ -247,7 +246,11 @@ pub async fn handle_auction(

// Resolve partner EIDs from the KV identity graph when the user has
// a valid EC and both KV and partner stores are available.
let eids = resolve_auction_eids(kv, registry, ec_context);
let auction_kv_snapshot = match (kv, ec_id) {
(Some(graph), Some(ec_id)) => graph.load_snapshot(ec_id),
_ => EcKvSnapshot::NotRead,
};
let eids = resolve_auction_eids(&auction_kv_snapshot, registry, ec_context);

// Look up geo for device info.
let geo = services
Expand Down Expand Up @@ -345,11 +348,10 @@ pub async fn handle_auction(
/// store, no EC, consent denied). On KV or partner-resolution errors, logs a
/// warning and returns empty EIDs so the auction can proceed in degraded mode.
pub(crate) fn resolve_auction_eids(
kv: Option<&KvIdentityGraph>,
snapshot: &EcKvSnapshot,
registry: Option<&PartnerRegistry>,
ec_context: &EcContext,
) -> Option<Vec<Eid>> {
let kv = kv?;
let registry = registry?;

if !ec_context.ec_allowed() {
Expand All @@ -358,19 +360,15 @@ pub(crate) fn resolve_auction_eids(

let ec_id = ec_context.ec_value()?;

let entry = match kv.get(ec_id) {
Ok(Some((entry, _generation))) => entry,
Ok(None) => return Some(Vec::new()),
Err(err) => {
log::warn!(
"Auction KV read failed for EC ID '{}': {err:?}",
log_id(ec_id)
);
return Some(Vec::new());
}
let Some(entry) = snapshot.entry_for(ec_id) else {
return Some(Vec::new());
};

let resolved = resolve_partner_ids(registry, &entry);
if !entry.consent.ok {
return Some(Vec::new());
}

let resolved = resolve_partner_ids(registry, entry);
Some(to_eids(&resolved))
}

Expand Down Expand Up @@ -858,22 +856,24 @@ mod tests {
}

#[test]
fn resolve_auction_eids_returns_none_without_kv() {
fn resolve_auction_eids_returns_empty_without_snapshot() {
let registry = PartnerRegistry::empty();
let ec_id = format!("{}.ABC123", "a".repeat(64));
let ec_context = make_ec_context(Jurisdiction::NonRegulated, Some(&ec_id));

let result = resolve_auction_eids(None, Some(&registry), &ec_context);
assert!(result.is_none(), "should return None when KV is missing");
let result = resolve_auction_eids(&EcKvSnapshot::NotRead, Some(&registry), &ec_context);
assert!(
result.is_some_and(|eids| eids.is_empty()),
"should degrade to empty EIDs without a snapshot"
);
}

#[test]
fn resolve_auction_eids_returns_none_without_registry() {
let kv = KvIdentityGraph::failing("test_store");
let ec_id = format!("{}.ABC123", "a".repeat(64));
let ec_context = make_ec_context(Jurisdiction::NonRegulated, Some(&ec_id));

let result = resolve_auction_eids(Some(&kv), None, &ec_context);
let result = resolve_auction_eids(&EcKvSnapshot::NotRead, None, &ec_context);
assert!(
result.is_none(),
"should return None when registry is missing"
Expand All @@ -882,12 +882,11 @@ mod tests {

#[test]
fn resolve_auction_eids_returns_none_when_consent_denied() {
let kv = KvIdentityGraph::failing("test_store");
let registry = PartnerRegistry::empty();
let ec_id = format!("{}.ABC123", "a".repeat(64));
let ec_context = make_ec_context(Jurisdiction::Unknown, Some(&ec_id));

let result = resolve_auction_eids(Some(&kv), Some(&registry), &ec_context);
let result = resolve_auction_eids(&EcKvSnapshot::NotRead, Some(&registry), &ec_context);
assert!(
result.is_none(),
"should return None when consent is denied"
Expand All @@ -896,11 +895,10 @@ mod tests {

#[test]
fn resolve_auction_eids_returns_none_when_no_ec() {
let kv = KvIdentityGraph::failing("test_store");
let registry = PartnerRegistry::empty();
let ec_context = make_ec_context(Jurisdiction::NonRegulated, None);

let result = resolve_auction_eids(Some(&kv), Some(&registry), &ec_context);
let result = resolve_auction_eids(&EcKvSnapshot::NotRead, Some(&registry), &ec_context);
assert!(
result.is_none(),
"should return None when no EC value is present"
Expand All @@ -909,14 +907,14 @@ mod tests {

#[test]
fn resolve_auction_eids_returns_empty_on_kv_miss() {
let kv = KvIdentityGraph::failing("nonexistent_store");
let registry = PartnerRegistry::empty();
let ec_id = format!("{}.ABC123", "a".repeat(64));
let ec_context = make_ec_context(Jurisdiction::NonRegulated, Some(&ec_id));

// KV store doesn't exist, so the get() call will error — should return
// empty Vec (degraded mode), not None.
let result = resolve_auction_eids(Some(&kv), Some(&registry), &ec_context);
let snapshot = EcKvSnapshot::Failed {
ec_id: ec_id.clone(),
};
let result = resolve_auction_eids(&snapshot, Some(&registry), &ec_context);
let eids = result.expect("should return Some on KV error (degraded mode)");
assert!(
eids.is_empty(),
Expand Down
Loading
Loading