Wire up trusted bidding signals KVv2 worklet call flow
Add trusted bidding signals KVv2 support in TrustedSignalsRequestManager. This CL is primarily a wiring change that imports some helper methods from previous CLs, containing a significant number of error messages, totaling about 2k. Another reason for its complexity is that this CL invokes both the request builder and response helper, making it difficult to split them into two parts for testing purposes (as they must handle encryption and decryption simultaneously using a saved context). While it may be possible to pass the trybots by reducing the length of the error strings and adding them back in a subsequent CL, this seems unnecessary. Bug: 337917489 Change-Id: If699cd280b9ef958a63a514ef3882fac026f7648 Binary-Size: See commit description. Fuchsia-Binary-Size: See commit description. Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5769805 Reviewed-by: mmenke <mmenke@chromium.org> Commit-Queue: Tianyang Xu <xtlsheep@google.com> Cr-Commit-Position: refs/heads/main@{#1356264}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
ed7395cf50
commit
58e692a38b
@ -191,6 +191,7 @@ source_set("tests") {
|
||||
"//components/cbor:cbor",
|
||||
"//content/common:for_content_tests",
|
||||
"//content/public/common:common",
|
||||
"//content/services/auction_worklet/public/cpp:test_support",
|
||||
"//gin",
|
||||
"//net",
|
||||
"//net:test_support",
|
||||
|
@ -667,7 +667,6 @@ TrustedBiddingSignalsKVv2RequestHelperBuilder::
|
||||
CHECK_NE(pos, std::string::npos);
|
||||
std::string key = trusted_bidding_signals_slot_size_param.substr(0, pos);
|
||||
std::string value = trusted_bidding_signals_slot_size_param.substr(pos + 1);
|
||||
CHECK(key == "slotSize" || key == "allSlotsRequestedSizes");
|
||||
trusted_bidding_signals_slot_size_param_ = {std::move(key),
|
||||
std::move(value)};
|
||||
}
|
||||
|
@ -22,10 +22,19 @@
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/notreached.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/services/auction_worklet/auction_v8_helper.h"
|
||||
#include "content/services/auction_worklet/public/cpp/auction_network_events_delegate.h"
|
||||
#include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
|
||||
#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
|
||||
#include "content/services/auction_worklet/trusted_kvv2_signals.h"
|
||||
#include "content/services/auction_worklet/trusted_signals.h"
|
||||
#include "content/services/auction_worklet/trusted_signals_kvv2_helper.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
#include "url/gurl.h"
|
||||
@ -299,6 +308,7 @@ TrustedSignalsRequestManager::RequestBiddingSignals(
|
||||
int32_t max_trusted_bidding_signals_url_length,
|
||||
LoadSignalsCallback load_signals_callback) {
|
||||
DCHECK_EQ(Type::kBiddingSignals, type_);
|
||||
DCHECK(!public_key_);
|
||||
|
||||
std::unique_ptr<RequestImpl> request = std::make_unique<RequestImpl>(
|
||||
this, interest_group_name,
|
||||
@ -309,6 +319,25 @@ TrustedSignalsRequestManager::RequestBiddingSignals(
|
||||
return request;
|
||||
}
|
||||
|
||||
std::unique_ptr<TrustedSignalsRequestManager::Request>
|
||||
TrustedSignalsRequestManager::RequestKVv2BiddingSignals(
|
||||
const std::string& interest_group_name,
|
||||
const std::optional<std::vector<std::string>>& keys,
|
||||
const url::Origin& joining_origin,
|
||||
blink::mojom::InterestGroup::ExecutionMode execution_mode,
|
||||
LoadSignalsCallback load_signals_callback) {
|
||||
DCHECK_EQ(Type::kBiddingSignals, type_);
|
||||
DCHECK(public_key_);
|
||||
|
||||
std::unique_ptr<RequestImpl> request = std::make_unique<RequestImpl>(
|
||||
this, interest_group_name,
|
||||
keys ? std::set<std::string>(keys->begin(), keys->end())
|
||||
: std::set<std::string>(),
|
||||
joining_origin, execution_mode, std::move(load_signals_callback));
|
||||
QueueRequest(request.get());
|
||||
return request;
|
||||
}
|
||||
|
||||
std::unique_ptr<TrustedSignalsRequestManager::Request>
|
||||
TrustedSignalsRequestManager::RequestScoringSignals(
|
||||
const GURL& render_url,
|
||||
@ -375,6 +404,66 @@ void TrustedSignalsRequestManager::StartBatchedTrustedSignalsRequest() {
|
||||
// No need to continue running the timer, if it's running.
|
||||
timer_.Stop();
|
||||
|
||||
// Trusted Signals KVv2 feature call flow.
|
||||
if (public_key_) {
|
||||
DCHECK(base::FeatureList::IsEnabled(
|
||||
blink::features::kFledgeTrustedSignalsKVv2Support));
|
||||
|
||||
BatchedTrustedSignalsRequest* batched_request =
|
||||
batched_requests_
|
||||
.emplace(std::make_unique<BatchedTrustedSignalsRequest>())
|
||||
.first->get();
|
||||
batched_request->requests = std::move(queued_requests_);
|
||||
|
||||
if (type_ == Type::kBiddingSignals) {
|
||||
// Append all interest group names and keys into a single set, and clear
|
||||
// them from each request, as they're no longer needed.
|
||||
std::set<std::string> interest_group_names;
|
||||
std::set<std::string> bidding_keys;
|
||||
|
||||
std::unique_ptr<TrustedBiddingSignalsKVv2RequestHelperBuilder>
|
||||
helper_builder(
|
||||
std::make_unique<TrustedBiddingSignalsKVv2RequestHelperBuilder>(
|
||||
top_level_origin_.host(), experiment_group_id_,
|
||||
public_key_->Clone(),
|
||||
trusted_bidding_signals_slot_size_param_));
|
||||
|
||||
for (auto& request : batched_request->requests) {
|
||||
CHECK(request->interest_group_name_.has_value());
|
||||
CHECK(request->bidder_keys_.has_value());
|
||||
CHECK(request->joining_origin_.has_value());
|
||||
CHECK(request->execution_mode_.has_value());
|
||||
|
||||
request->SetKVv2IsolationIndex(helper_builder->AddTrustedSignalsRequest(
|
||||
request->interest_group_name_.value(),
|
||||
request->bidder_keys_.value(), request->joining_origin_.value(),
|
||||
request->execution_mode_.value()));
|
||||
interest_group_names.emplace(
|
||||
std::move(request->interest_group_name_).value());
|
||||
bidding_keys.insert(request->bidder_keys_->begin(),
|
||||
request->bidder_keys_->end());
|
||||
request->bidder_keys_.reset();
|
||||
request->batched_request_ = batched_request;
|
||||
}
|
||||
|
||||
batched_request->trusted_kvv2_signals =
|
||||
TrustedKVv2Signals::LoadKVv2BiddingSignals(
|
||||
url_loader_factory_, /*auction_network_events_handler=*/
|
||||
CreateNewAuctionNetworkEventsHandlerRemote(
|
||||
auction_network_events_handler_),
|
||||
interest_group_names, bidding_keys, trusted_signals_url_,
|
||||
std::move(helper_builder), v8_helper_,
|
||||
base::BindOnce(&TrustedSignalsRequestManager::OnKVv2SignalsLoaded,
|
||||
base::Unretained(this), batched_request));
|
||||
} else {
|
||||
// TODO(crbug.com/337917489): Add trusted scoring KVv2 signals handling
|
||||
// here and remove NOTREACHED().
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<TrustedSignalsUrlBuilder> url_builder;
|
||||
bool split_fetch = base::FeatureList::IsEnabled(
|
||||
blink::features::kFledgeSplitTrustedSignalsFetchingURL);
|
||||
@ -422,6 +511,20 @@ TrustedSignalsRequestManager::RequestImpl::RequestImpl(
|
||||
}
|
||||
}
|
||||
|
||||
TrustedSignalsRequestManager::RequestImpl::RequestImpl(
|
||||
TrustedSignalsRequestManager* trusted_signals_request_manager,
|
||||
const std::string& interest_group_name,
|
||||
std::set<std::string> bidder_keys,
|
||||
const url::Origin& joining_origin,
|
||||
blink::mojom::InterestGroup::ExecutionMode execution_mode,
|
||||
LoadSignalsCallback load_signals_callback)
|
||||
: interest_group_name_(interest_group_name),
|
||||
bidder_keys_(std::move(bidder_keys)),
|
||||
joining_origin_(joining_origin),
|
||||
execution_mode_(execution_mode),
|
||||
load_signals_callback_(std::move(load_signals_callback)),
|
||||
trusted_signals_request_manager_(trusted_signals_request_manager) {}
|
||||
|
||||
TrustedSignalsRequestManager::RequestImpl::RequestImpl(
|
||||
TrustedSignalsRequestManager* trusted_signals_request_manager,
|
||||
const GURL& render_url,
|
||||
@ -447,6 +550,11 @@ TrustedSignalsRequestManager::RequestImpl::~RequestImpl() {
|
||||
}
|
||||
}
|
||||
|
||||
void TrustedSignalsRequestManager::RequestImpl::SetKVv2IsolationIndex(
|
||||
TrustedSignalsKVv2RequestHelperBuilder::IsolationIndex index) {
|
||||
kvv2_isolation_index_ = index;
|
||||
}
|
||||
|
||||
TrustedSignalsRequestManager::BatchedTrustedSignalsRequest::
|
||||
BatchedTrustedSignalsRequest() = default;
|
||||
|
||||
@ -489,6 +597,42 @@ void TrustedSignalsRequestManager::OnSignalsLoaded(
|
||||
batched_requests_.erase(batched_requests_.find(batched_request));
|
||||
}
|
||||
|
||||
void TrustedSignalsRequestManager::OnKVv2SignalsLoaded(
|
||||
BatchedTrustedSignalsRequest* batched_request,
|
||||
std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
|
||||
result_map,
|
||||
std::optional<std::string> error_msg) {
|
||||
DCHECK(batched_requests_.find(batched_request) != batched_requests_.end());
|
||||
for (RequestImpl* request : batched_request->requests) {
|
||||
DCHECK_EQ(request->batched_request_, batched_request);
|
||||
|
||||
// Remove association with `this` and `batched_request` before invoking
|
||||
// callback, which may destroy the Request.
|
||||
request->trusted_signals_request_manager_ = nullptr;
|
||||
request->batched_request_ = nullptr;
|
||||
|
||||
if (result_map.has_value()) {
|
||||
DCHECK(request->kvv2_isolation_index_);
|
||||
auto result_it = result_map->find(request->kvv2_isolation_index_.value());
|
||||
if (result_it != result_map->end()) {
|
||||
std::move(request->load_signals_callback_)
|
||||
.Run(result_it->second, error_msg);
|
||||
} else {
|
||||
std::move(request->load_signals_callback_)
|
||||
.Run(nullptr,
|
||||
base::StringPrintf(
|
||||
"Failed to locate compression group \"%d\" and "
|
||||
"parition \"%d\" in the result map.",
|
||||
request->kvv2_isolation_index_->compression_group_id,
|
||||
request->kvv2_isolation_index_->partition_id));
|
||||
}
|
||||
} else {
|
||||
std::move(request->load_signals_callback_).Run(nullptr, error_msg);
|
||||
}
|
||||
}
|
||||
batched_requests_.erase(batched_requests_.find(batched_request));
|
||||
}
|
||||
|
||||
void TrustedSignalsRequestManager::OnRequestDestroyed(RequestImpl* request) {
|
||||
// If the request is not assigned to a BatchedTrustedSignalsRequest, it's
|
||||
// still in `queued_requests_`, so remove it from that.
|
||||
|
@ -22,7 +22,10 @@
|
||||
#include "base/timer/timer.h"
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
|
||||
#include "content/services/auction_worklet/public/mojom/bidder_worklet.mojom.h"
|
||||
#include "content/services/auction_worklet/trusted_kvv2_signals.h"
|
||||
#include "content/services/auction_worklet/trusted_signals.h"
|
||||
#include "content/services/auction_worklet/trusted_signals_kvv2_helper.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
|
||||
@ -125,6 +128,16 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
int32_t max_trusted_bidding_signals_url_length,
|
||||
LoadSignalsCallback load_signals_callback);
|
||||
|
||||
// Like `RequestBiddingSignals()`, but for trusted bidding signals KVv2
|
||||
// support. Requires `joining_origin` and `execution_mode` instead of
|
||||
// `max_trusted_bidding_signals_url_length`.
|
||||
std::unique_ptr<Request> RequestKVv2BiddingSignals(
|
||||
const std::string& interest_group_name,
|
||||
const std::optional<std::vector<std::string>>& keys,
|
||||
const url::Origin& joining_origin,
|
||||
blink::mojom::InterestGroup::ExecutionMode execution_mode,
|
||||
LoadSignalsCallback load_signals_callback);
|
||||
|
||||
// Queues a scoring signals request. Does not start a network request until
|
||||
// StartBatchedTrustedSignalsRequest() is invoked. `this` must be of Type
|
||||
// kScoringSignals.
|
||||
@ -155,6 +168,14 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
int32_t max_trusted_bidding_signals_url_length,
|
||||
LoadSignalsCallback load_signals_callback);
|
||||
|
||||
// Constructor for trusted bidding signals KVv2 support.
|
||||
RequestImpl(TrustedSignalsRequestManager* trusted_signals_request_manager,
|
||||
const std::string& interest_group_name,
|
||||
std::set<std::string> bidder_keys,
|
||||
const url::Origin& joining_origin,
|
||||
blink::mojom::InterestGroup::ExecutionMode execution_mode,
|
||||
LoadSignalsCallback load_signals_callback);
|
||||
|
||||
RequestImpl(TrustedSignalsRequestManager* trusted_signals_request_manager,
|
||||
const GURL& render_url,
|
||||
std::set<std::string> ad_component_render_urls,
|
||||
@ -165,6 +186,9 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
RequestImpl& operator=(RequestImpl&) = delete;
|
||||
~RequestImpl() override;
|
||||
|
||||
void SetKVv2IsolationIndex(
|
||||
TrustedSignalsKVv2RequestHelperBuilder::IsolationIndex index);
|
||||
|
||||
private:
|
||||
friend class TrustedSignalsRequestManager;
|
||||
|
||||
@ -172,6 +196,8 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
// bidder signals requests, null for scoring signals requests.
|
||||
std::optional<std::string> interest_group_name_;
|
||||
std::optional<std::set<std::string>> bidder_keys_;
|
||||
std::optional<url::Origin> joining_origin_;
|
||||
std::optional<blink::mojom::InterestGroup::ExecutionMode> execution_mode_;
|
||||
|
||||
// Used for requests for scoring signals. `render_url_` must be non-null
|
||||
// and non-empty for scoring signals requests, and
|
||||
@ -193,6 +219,14 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
// If this request is currently assigned to a batched request, points to
|
||||
// that request. nullptr otherwise.
|
||||
raw_ptr<BatchedTrustedSignalsRequest> batched_request_ = nullptr;
|
||||
|
||||
// When a request is added to TrustedBiddingSignalsKVv2RequestHelperBuilder,
|
||||
// it returns the assigned partition ID and compression group ID for the
|
||||
// request. These returned IDs are stored in the request to locate the
|
||||
// correct compression group and partition in the response, avoiding the
|
||||
// need to search the entire map.
|
||||
std::optional<TrustedSignalsKVv2RequestHelperBuilder::IsolationIndex>
|
||||
kvv2_isolation_index_;
|
||||
};
|
||||
|
||||
// Use interest group name or render url as customized comparator for bidding
|
||||
@ -219,6 +253,7 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
~BatchedTrustedSignalsRequest();
|
||||
|
||||
std::unique_ptr<TrustedSignals> trusted_signals;
|
||||
std::unique_ptr<TrustedKVv2Signals> trusted_kvv2_signals;
|
||||
|
||||
// The batched Requests this is for.
|
||||
std::set<raw_ptr<RequestImpl, SetExperimental>, CompareRequestImpl>
|
||||
@ -232,6 +267,15 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
scoped_refptr<Result> result,
|
||||
std::optional<std::string> error_msg);
|
||||
|
||||
// Callback for the trusted signals KVv2 process, where the return value of
|
||||
// `DeliverKVv2CallbackOnUserThread` in `TrustedKVv2Signals` is an optional
|
||||
// `TrustedKVv2Signals::TrustedSignalsResultMap`.
|
||||
void OnKVv2SignalsLoaded(
|
||||
BatchedTrustedSignalsRequest* batched_request,
|
||||
std::optional<TrustedSignalsKVv2ResponseParser::TrustedSignalsResultMap>
|
||||
result_map,
|
||||
std::optional<std::string> error_msg);
|
||||
|
||||
// Called when a request is destroyed. If it's in `queued_requests_`, removes
|
||||
// it. If there's a BatchedTrustedSignalsRequest for it, disassociates the
|
||||
// request with it, cancelling the request if it's no longer needed.
|
||||
@ -246,6 +290,9 @@ class CONTENT_EXPORT TrustedSignalsRequestManager {
|
||||
const GURL trusted_signals_url_;
|
||||
const std::optional<uint16_t> experiment_group_id_;
|
||||
const std::string trusted_bidding_signals_slot_size_param_;
|
||||
// Fetched in the browser process based on the trusted bidding/scoring signals
|
||||
// coordinator, to be used for encrypting the trusted KVv2 signals request
|
||||
// body.
|
||||
const mojom::TrustedSignalsPublicKeyPtr public_key_;
|
||||
const scoped_refptr<AuctionV8Helper> v8_helper_;
|
||||
|
||||
|
@ -11,23 +11,37 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/span.h"
|
||||
#include "base/containers/span_reader.h"
|
||||
#include "base/containers/span_writer.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "components/cbor/reader.h"
|
||||
#include "components/cbor/values.h"
|
||||
#include "components/cbor/writer.h"
|
||||
#include "content/services/auction_worklet/auction_v8_helper.h"
|
||||
#include "content/services/auction_worklet/public/cpp/cbor_test_util.h"
|
||||
#include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom-forward.h"
|
||||
#include "content/services/auction_worklet/trusted_signals.h"
|
||||
#include "content/services/auction_worklet/worklet_test_util.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
#include "net/test/embedded_test_server/embedded_test_server.h"
|
||||
#include "net/third_party/quiche/src/quiche/oblivious_http/oblivious_http_gateway.h"
|
||||
#include "services/network/test/test_shared_url_loader_factory.h"
|
||||
#include "services/network/test/test_url_loader_factory.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "third_party/blink/public/common/features.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/origin.h"
|
||||
#include "v8/include/v8-context.h"
|
||||
#include "v8/include/v8-forward.h"
|
||||
|
||||
@ -78,7 +92,102 @@ const char kBaseScoringExpectedResult[] =
|
||||
R"("adComponentRenderUrls":{"https://foosub.test/":2,)"
|
||||
R"("https://barsub.test/":[3],"https://bazsub.test/":"4"}})";
|
||||
|
||||
const char kKVv2BiddingBase1[] =
|
||||
R"(
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"keyGroupOutputs": [
|
||||
{
|
||||
"tags": [
|
||||
"interestGroupNames"
|
||||
],
|
||||
"keyValues": {
|
||||
"name1": {
|
||||
"value": "{\"priorityVector\":{\"foo\":1}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"tags": [
|
||||
"keys"
|
||||
],
|
||||
"keyValues": {
|
||||
"key1": {
|
||||
"value": "1"
|
||||
},
|
||||
"key2": {
|
||||
"value": "[2]"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
])";
|
||||
|
||||
const char kKVv2BiddingBase2[] =
|
||||
R"(
|
||||
[
|
||||
{
|
||||
"id": 0,
|
||||
"keyGroupOutputs": [
|
||||
{
|
||||
"tags": [
|
||||
"interestGroupNames"
|
||||
],
|
||||
"keyValues": {
|
||||
"name2": {
|
||||
"value": "{\"priorityVector\":{\"foo\":2}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"tags": [
|
||||
"keys"
|
||||
],
|
||||
"keyValues": {
|
||||
"key2": {
|
||||
"value": "[2]"
|
||||
},
|
||||
"key3": {
|
||||
"value": "\"3\""
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
])";
|
||||
|
||||
const char kTopLevelOrigin[] = "https://publisher";
|
||||
const char kJoiningOriginFoo[] = "https://foo.test";
|
||||
const char kJoiningOriginBar[] = "https://bar.test";
|
||||
const char kTrustedKVv2SignalsHost[] = "a.test";
|
||||
const char kTrustedKVv2BiddingSignalsPath[] = "/bidder-signals";
|
||||
const uint8_t kKeyId = 0xAA;
|
||||
|
||||
// These keys were randomly generated as follows:
|
||||
// EVP_HPKE_KEY keys;
|
||||
// EVP_HPKE_KEY_generate(&keys, EVP_hpke_x25519_hkdf_sha256());
|
||||
// and then EVP_HPKE_KEY_public_key and EVP_HPKE_KEY_private_key were used to
|
||||
// extract the keys.
|
||||
const uint8_t kTestPrivateKey[] = {
|
||||
0xff, 0x1f, 0x47, 0xb1, 0x68, 0xb6, 0xb9, 0xea, 0x65, 0xf7, 0x97,
|
||||
0x4f, 0xf2, 0x2e, 0xf2, 0x36, 0x94, 0xe2, 0xf6, 0xb6, 0x8d, 0x66,
|
||||
0xf3, 0xa7, 0x64, 0x14, 0x28, 0xd4, 0x45, 0x35, 0x01, 0x8f,
|
||||
};
|
||||
|
||||
const uint8_t kTestPublicKey[] = {
|
||||
0xa1, 0x5f, 0x40, 0x65, 0x86, 0xfa, 0xc4, 0x7b, 0x99, 0x59, 0x70,
|
||||
0xf1, 0x85, 0xd9, 0xd8, 0x91, 0xc7, 0x4d, 0xcf, 0x1e, 0xb9, 0x1a,
|
||||
0x7d, 0x50, 0xa5, 0x8b, 0x01, 0x68, 0x3e, 0x60, 0x05, 0x2d,
|
||||
};
|
||||
|
||||
mojom::TrustedSignalsPublicKeyPtr CreateDefaultPublicKey() {
|
||||
return mojom::TrustedSignalsPublicKey::New(
|
||||
std::string(reinterpret_cast<const char*>(&kTestPublicKey[0]),
|
||||
sizeof(kTestPublicKey)),
|
||||
kKeyId);
|
||||
}
|
||||
|
||||
// Callback for loading signals that stores the result and runs a closure to
|
||||
// exit a message loop.
|
||||
@ -102,7 +211,8 @@ void NeverInvokedLoadSignalsCallback(
|
||||
|
||||
class TrustedSignalsRequestManagerTest : public testing::Test {
|
||||
public:
|
||||
TrustedSignalsRequestManagerTest()
|
||||
explicit TrustedSignalsRequestManagerTest(
|
||||
mojom::TrustedSignalsPublicKeyPtr public_key = nullptr)
|
||||
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
|
||||
v8_helper_(
|
||||
AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner())),
|
||||
@ -116,7 +226,7 @@ class TrustedSignalsRequestManagerTest : public testing::Test {
|
||||
trusted_signals_url_,
|
||||
/*experiment_group_id=*/std::nullopt,
|
||||
"trusted_bidding_signals_slot_size_param=foo",
|
||||
/*public_key=*/nullptr,
|
||||
std::move(public_key),
|
||||
v8_helper_.get()),
|
||||
scoring_request_manager_(
|
||||
TrustedSignalsRequestManager::Type::kScoringSignals,
|
||||
@ -2423,5 +2533,711 @@ TEST_F(TrustedSignalsRequestManagerTest, ScoringSignalsIdenticalRequests) {
|
||||
"Received URL: " + kUrl, "Completion Status: net::OK"));
|
||||
}
|
||||
|
||||
class TrustedSignalsRequestManagerKVv2Test
|
||||
: public TrustedSignalsRequestManagerTest {
|
||||
public:
|
||||
TrustedSignalsRequestManagerKVv2Test()
|
||||
: TrustedSignalsRequestManagerTest(CreateDefaultPublicKey()) {
|
||||
feature_list_.InitWithFeatures(
|
||||
{blink::features::kFledgeTrustedSignalsKVv2Support}, {});
|
||||
}
|
||||
|
||||
protected:
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
url::Origin joining_origin_{url::Origin::Create(GURL(kJoiningOriginFoo))};
|
||||
blink::mojom::InterestGroup::ExecutionMode execution_mode_{
|
||||
blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode};
|
||||
};
|
||||
|
||||
// Make two requests, try to start a network request, then cancel both requests.
|
||||
// The network request should be cancelled. Only test bidders, since sellers
|
||||
// have no significant differences in this path.
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2Test, CancelAllLiveRequests) {
|
||||
const std::vector<std::string> kKeys1{"key1"};
|
||||
const std::vector<std::string> kKeys2{"key2"};
|
||||
|
||||
auto request1 = bidding_request_manager_.RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys1, joining_origin_, execution_mode_,
|
||||
base::BindOnce(&NeverInvokedLoadSignalsCallback));
|
||||
auto request2 = bidding_request_manager_.RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys2, joining_origin_, execution_mode_,
|
||||
base::BindOnce(&NeverInvokedLoadSignalsCallback));
|
||||
|
||||
// Wait for network request to be made, which should include both keys in the
|
||||
// URLs.
|
||||
bidding_request_manager_.StartBatchedTrustedSignalsRequest();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
ASSERT_TRUE(url_loader_factory_.IsPending(trusted_signals_url_.spec()));
|
||||
|
||||
// Cancel both requests, which should cancel the network request.
|
||||
request1.reset();
|
||||
request2.reset();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
EXPECT_FALSE(url_loader_factory_.GetPendingRequest(0)->client.is_connected());
|
||||
}
|
||||
|
||||
// Make two requests, cancel both, then try to start a network request. No
|
||||
// requests should be made. Only test bidders, since sellers have no significant
|
||||
// differences in this path.
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2Test, CancelAllQueuedRequests) {
|
||||
const std::vector<std::string> kKeys1{"key1"};
|
||||
const std::vector<std::string> kKeys2{"key2"};
|
||||
|
||||
auto request1 = bidding_request_manager_.RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys1, joining_origin_, execution_mode_,
|
||||
base::BindOnce(&NeverInvokedLoadSignalsCallback));
|
||||
auto request2 = bidding_request_manager_.RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys2, joining_origin_, execution_mode_,
|
||||
base::BindOnce(&NeverInvokedLoadSignalsCallback));
|
||||
|
||||
request1.reset();
|
||||
request2.reset();
|
||||
|
||||
bidding_request_manager_.StartBatchedTrustedSignalsRequest();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
EXPECT_EQ(0, url_loader_factory_.NumPending());
|
||||
}
|
||||
|
||||
class TrustedSignalsRequestManagerKVv2EmbeddedTest : public testing::Test {
|
||||
public:
|
||||
TrustedSignalsRequestManagerKVv2EmbeddedTest() {
|
||||
response_status_code_ = net::HttpStatusCode::HTTP_OK;
|
||||
|
||||
feature_list_.InitWithFeatures(
|
||||
{blink::features::kFledgeTrustedSignalsKVv2Support}, {});
|
||||
|
||||
embedded_test_server_.SetSSLConfig(
|
||||
net::EmbeddedTestServer::CERT_TEST_NAMES);
|
||||
embedded_test_server_.RegisterRequestHandler(base::BindRepeating(
|
||||
&TrustedSignalsRequestManagerKVv2EmbeddedTest::HandleSignalsRequest,
|
||||
base::Unretained(this)));
|
||||
EXPECT_TRUE(embedded_test_server_.Start());
|
||||
|
||||
bidding_request_manager_ = std::make_unique<TrustedSignalsRequestManager>(
|
||||
TrustedSignalsRequestManager::Type::kBiddingSignals,
|
||||
url_loader_factory_.get(),
|
||||
/*auction_network_events_handler=*/
|
||||
auction_network_events_handler_.CreateRemote(),
|
||||
/*automatically_send_requests=*/false,
|
||||
url::Origin::Create(GURL(kTopLevelOrigin)), TrustedBiddingSignalsUrl(),
|
||||
/*experiment_group_id=*/std::nullopt,
|
||||
"trusted_bidding_signals_slot_size_param=foo", CreateDefaultPublicKey(),
|
||||
v8_helper_.get());
|
||||
}
|
||||
|
||||
~TrustedSignalsRequestManagerKVv2EmbeddedTest() override {
|
||||
// Wait until idle to ensure all requests have been observed within the
|
||||
// `auction_network_events_handler_`.
|
||||
task_environment_.RunUntilIdle();
|
||||
bidding_request_manager_.reset();
|
||||
}
|
||||
|
||||
GURL TrustedBiddingSignalsUrl() const {
|
||||
return embedded_test_server_.GetURL(kTrustedKVv2SignalsHost,
|
||||
kTrustedKVv2BiddingSignalsPath);
|
||||
}
|
||||
|
||||
// Fetch bidding signals and wait for completion. Return nullptr on failure.
|
||||
scoped_refptr<TrustedSignals::Result> FetchBiddingSignals(
|
||||
const std::string& interest_group_name,
|
||||
const std::optional<std::vector<std::string>>&
|
||||
trusted_bidding_signals_keys,
|
||||
const url::Origin& joining_origin,
|
||||
blink::mojom::InterestGroup::ExecutionMode execution_mode) {
|
||||
scoped_refptr<TrustedSignals::Result> signals;
|
||||
base::RunLoop run_loop;
|
||||
auto request = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
interest_group_name, trusted_bidding_signals_keys, joining_origin,
|
||||
execution_mode,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals, &error_msg_,
|
||||
run_loop.QuitClosure()));
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
run_loop.Run();
|
||||
return signals;
|
||||
}
|
||||
|
||||
// Return the results of calling TrustedSignals::Result::GetBiddingSignals()
|
||||
// with `trusted_bidding_signals_keys`. Return value as a JSON std::string,
|
||||
// for easy testing.
|
||||
std::string ExtractBiddingSignals(
|
||||
TrustedSignals::Result* signals,
|
||||
std::vector<std::string> trusted_bidding_signals_keys) {
|
||||
base::RunLoop run_loop;
|
||||
|
||||
std::string result;
|
||||
v8_helper_->v8_runner()->PostTask(
|
||||
FROM_HERE, base::BindLambdaForTesting([&]() {
|
||||
AuctionV8Helper::FullIsolateScope isolate_scope(v8_helper_.get());
|
||||
v8::Isolate* isolate = v8_helper_->isolate();
|
||||
// Could use the scratch context, but using a separate one more
|
||||
// closely resembles actual use.
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope context_scope(context);
|
||||
|
||||
v8::Local<v8::Value> value = signals->GetBiddingSignals(
|
||||
v8_helper_.get(), context, trusted_bidding_signals_keys);
|
||||
|
||||
if (v8_helper_->ExtractJson(context, value,
|
||||
/*script_timeout=*/nullptr, &result) !=
|
||||
AuctionV8Helper::Result::kSuccess) {
|
||||
result = "JSON extraction failed.";
|
||||
}
|
||||
run_loop.Quit();
|
||||
}));
|
||||
run_loop.Run();
|
||||
return result;
|
||||
}
|
||||
|
||||
// Set the response status code.
|
||||
void SetResponseStatusCode(net::HttpStatusCode code) {
|
||||
base::AutoLock auto_lock(lock_);
|
||||
response_status_code_ = code;
|
||||
}
|
||||
|
||||
protected:
|
||||
static std::string BuildResponseBody(const std::string& hex_string) {
|
||||
std::vector<uint8_t> bytes;
|
||||
CHECK(base::HexStringToBytes(hex_string, &bytes));
|
||||
|
||||
return test::CreateKVv2ResponseBody(base::as_string_view(bytes));
|
||||
}
|
||||
|
||||
std::unique_ptr<net::test_server::HttpResponse> HandleSignalsRequest(
|
||||
const net::test_server::HttpRequest& request) {
|
||||
if (request.relative_url != kTrustedKVv2BiddingSignalsPath) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
base::AutoLock auto_lock(lock_);
|
||||
|
||||
// Decrypt the request.
|
||||
auto response_key_config = quiche::ObliviousHttpHeaderKeyConfig::Create(
|
||||
kKeyId, EVP_HPKE_DHKEM_X25519_HKDF_SHA256, EVP_HPKE_HKDF_SHA256,
|
||||
EVP_HPKE_AES_256_GCM);
|
||||
CHECK(response_key_config.ok()) << response_key_config.status();
|
||||
|
||||
auto ohttp_gateway =
|
||||
quiche::ObliviousHttpGateway::Create(
|
||||
std::string(reinterpret_cast<const char*>(&kTestPrivateKey[0]),
|
||||
sizeof(kTestPrivateKey)),
|
||||
response_key_config.value())
|
||||
.value();
|
||||
|
||||
auto received_request = ohttp_gateway.DecryptObliviousHttpRequest(
|
||||
request.content, kTrustedSignalsKVv2EncryptionRequestMediaType);
|
||||
CHECK(received_request.ok()) << received_request.status();
|
||||
|
||||
// Parse request and get interest group names.
|
||||
base::span<const uint8_t> body_span =
|
||||
base::as_byte_span(received_request->GetPlaintextData());
|
||||
base::SpanReader reader(body_span);
|
||||
// Skip the first 1 byte since the compression format is always set as
|
||||
// uncompressed in this test.
|
||||
reader.Skip(1u);
|
||||
uint32_t length;
|
||||
reader.ReadU32BigEndian(length);
|
||||
auto cbor_bytes = reader.Read(length);
|
||||
CHECK(cbor_bytes);
|
||||
std::optional<cbor::Value> request_body =
|
||||
cbor::Reader::Read(base::span(cbor_bytes.value()));
|
||||
CHECK(request_body);
|
||||
|
||||
std::vector<std::string> interest_group_names;
|
||||
for (const auto& partition :
|
||||
request_body->GetMap().at(cbor::Value("partitions")).GetArray()) {
|
||||
for (const auto& argument :
|
||||
partition.GetMap().at(cbor::Value("arguments")).GetArray()) {
|
||||
const cbor::Value::MapValue& argument_map = argument.GetMap();
|
||||
if (argument_map.at(cbor::Value("tags")).GetArray().at(0).GetString() ==
|
||||
"interestGroupNames") {
|
||||
for (const auto& data :
|
||||
argument_map.at(cbor::Value("data")).GetArray()) {
|
||||
interest_group_names.emplace_back(std::move(data.GetString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return different response based on `interest_group_names`.
|
||||
int compression_group_id = 0;
|
||||
cbor::Value::ArrayValue compression_groups;
|
||||
for (const auto& name : interest_group_names) {
|
||||
cbor::Value::MapValue compression_group;
|
||||
compression_group.try_emplace(cbor::Value("compressionGroupId"),
|
||||
cbor::Value(compression_group_id));
|
||||
compression_group_id++;
|
||||
|
||||
compression_group.try_emplace(cbor::Value("ttlMs"), cbor::Value(100));
|
||||
if (name == "name1") {
|
||||
compression_group.try_emplace(
|
||||
cbor::Value("content"),
|
||||
cbor::Value(test::ToCborVector(kKVv2BiddingBase1)));
|
||||
} else if (name == "name2") {
|
||||
compression_group.try_emplace(
|
||||
cbor::Value("content"),
|
||||
cbor::Value(test::ToCborVector(kKVv2BiddingBase2)));
|
||||
}
|
||||
|
||||
compression_groups.emplace_back(std::move(compression_group));
|
||||
}
|
||||
|
||||
cbor::Value::MapValue body_map;
|
||||
body_map.try_emplace(cbor::Value("compressionGroups"),
|
||||
cbor::Value(std::move(compression_groups)));
|
||||
|
||||
cbor::Value body_value(std::move(body_map));
|
||||
std::optional<std::vector<uint8_t>> maybe_body_bytes =
|
||||
cbor::Writer::Write(body_value);
|
||||
CHECK(maybe_body_bytes);
|
||||
|
||||
std::string response_body =
|
||||
BuildResponseBody(base::HexEncode(std::move(maybe_body_bytes).value()));
|
||||
|
||||
auto response_context =
|
||||
std::move(received_request).value().ReleaseContext();
|
||||
|
||||
// Encrypt the response body.
|
||||
auto maybe_response = ohttp_gateway.CreateObliviousHttpResponse(
|
||||
response_body, response_context,
|
||||
kTrustedSignalsKVv2EncryptionResponseMediaType);
|
||||
CHECK(maybe_response.ok()) << maybe_response.status();
|
||||
|
||||
auto response = std::make_unique<net::test_server::BasicHttpResponse>();
|
||||
response->set_code(response_status_code_);
|
||||
response->set_content_type(kAdAuctionTrustedSignalsMimeType);
|
||||
response->set_content(maybe_response->EncapsulateAndSerialize());
|
||||
response->AddCustomHeader("Ad-Auction-Allowed", "true");
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Need to use an IO thread for the TestSharedURLLoaderFactory, which lives
|
||||
// on the thread it's created on, to make network requests.
|
||||
base::test::TaskEnvironment task_environment_{
|
||||
base::test::TaskEnvironment::MainThreadType::IO};
|
||||
|
||||
// The fetch helpers store the most recent error message, if any, here.
|
||||
std::optional<std::string> error_msg_;
|
||||
scoped_refptr<AuctionV8Helper> v8_helper_{
|
||||
AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner())};
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
TestAuctionNetworkEventsHandler auction_network_events_handler_;
|
||||
std::unique_ptr<TrustedSignalsRequestManager> bidding_request_manager_;
|
||||
|
||||
url::Origin joining_origin_foo_{url::Origin::Create(GURL(kJoiningOriginFoo))};
|
||||
url::Origin joining_origin_bar_{url::Origin::Create(GURL(kJoiningOriginBar))};
|
||||
blink::mojom::InterestGroup::ExecutionMode execution_mode_{
|
||||
blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode};
|
||||
|
||||
base::Lock lock_;
|
||||
net::HttpStatusCode response_status_code_ GUARDED_BY(lock_);
|
||||
|
||||
// URLLoaderFactory that makes real network requests.
|
||||
scoped_refptr<network::TestSharedURLLoaderFactory> url_loader_factory_{
|
||||
base::MakeRefCounted<network::TestSharedURLLoaderFactory>(
|
||||
/*network_service=*/nullptr,
|
||||
/*is_trusted=*/true)};
|
||||
|
||||
net::test_server::EmbeddedTestServer embedded_test_server_{
|
||||
net::test_server::EmbeddedTestServer::TYPE_HTTPS};
|
||||
};
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest, BiddingSignalsOneRequest) {
|
||||
const std::vector<std::string> kKeys{"key2", "key1"};
|
||||
scoped_refptr<TrustedSignals::Result> signals =
|
||||
FetchBiddingSignals("name1", kKeys, joining_origin_foo_, execution_mode_);
|
||||
ASSERT_TRUE(signals);
|
||||
EXPECT_FALSE(error_msg_.has_value());
|
||||
EXPECT_EQ(R"({"key2":[2],"key1":1})",
|
||||
ExtractBiddingSignals(signals.get(), kKeys));
|
||||
const auto priority_vector =
|
||||
signals->GetPerGroupData("name1")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 1}}),
|
||||
*priority_vector);
|
||||
|
||||
// Wait until idle to ensure all requests have been observed within the
|
||||
// `auction_network_events_handler_`.
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
// Use `kDefaultValue` as the execution mode when adding the signals, where the
|
||||
// isolation index will be partition ID (1) and compression group ID (0).
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsIsolationIndexNotFound) {
|
||||
base::RunLoop run_loop;
|
||||
scoped_refptr<TrustedSignals::Result> signals;
|
||||
std::optional<std::string> error_msg;
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, {{"key1"}}, joining_origin_foo_,
|
||||
blink::mojom::InterestGroup::ExecutionMode::kDefaultValue,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals, &error_msg,
|
||||
run_loop.QuitClosure()));
|
||||
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
|
||||
run_loop.Run();
|
||||
EXPECT_FALSE(signals);
|
||||
EXPECT_EQ(error_msg,
|
||||
"Failed to locate compression group \"0\" and "
|
||||
"parition \"1\" in the result map.");
|
||||
|
||||
// Wait until idle to ensure all requests have been observed within the
|
||||
// `auction_network_events_handler_`.
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
// Build a request by adding two signals with isolation index compression group
|
||||
// 0, partition 0 and compression group 0, partition 1. The response does
|
||||
// contain the data for `name2`, but in compressiong group 1 and parititon 0.
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsMissingPartition) {
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, {{"key1"}}, joining_origin_foo_,
|
||||
blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name2"}, {{"key2"}}, joining_origin_foo_,
|
||||
blink::mojom::InterestGroup::ExecutionMode::kDefaultValue,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
|
||||
run_loop1.Run();
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_FALSE(error_msg1.has_value());
|
||||
EXPECT_EQ(R"({"key1":1})", ExtractBiddingSignals(signals1.get(), {"key1"}));
|
||||
const auto priority_vector =
|
||||
signals1->GetPerGroupData("name1")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 1}}),
|
||||
*priority_vector);
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(signals2);
|
||||
EXPECT_EQ(error_msg2,
|
||||
"Failed to locate compression group \"0\" and "
|
||||
"parition \"1\" in the result map.");
|
||||
|
||||
// Wait until idle to ensure all requests have been observed within the
|
||||
// `auction_network_events_handler_`.
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
// Build a request by adding two signals with isolation index compression group
|
||||
// 0, partition 0 and compression group 1, partition 1. The response does
|
||||
// contain the data for `name2`, but in compressiong group 1 and parititon 0.
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsMissingCompressionGroup) {
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, {{"key1"}}, joining_origin_foo_,
|
||||
blink::mojom::InterestGroup::ExecutionMode::kGroupedByOriginMode,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name2"}, {{"key2"}}, joining_origin_bar_,
|
||||
blink::mojom::InterestGroup::ExecutionMode::kDefaultValue,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
|
||||
run_loop1.Run();
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_FALSE(error_msg1.has_value());
|
||||
EXPECT_EQ(R"({"key1":1})", ExtractBiddingSignals(signals1.get(), {"key1"}));
|
||||
const auto priority_vector =
|
||||
signals1->GetPerGroupData("name1")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 1}}),
|
||||
*priority_vector);
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(signals2);
|
||||
EXPECT_EQ(error_msg2,
|
||||
"Failed to locate compression group \"1\" and "
|
||||
"parition \"1\" in the result map.");
|
||||
|
||||
// Wait until idle to ensure all requests have been observed within the
|
||||
// `auction_network_events_handler_`.
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsOneRequestNullKeys) {
|
||||
scoped_refptr<TrustedSignals::Result> signals =
|
||||
FetchBiddingSignals({"name1"},
|
||||
/*trusted_bidding_signals_keys=*/std::nullopt,
|
||||
joining_origin_foo_, execution_mode_);
|
||||
ASSERT_TRUE(signals);
|
||||
EXPECT_FALSE(error_msg_.has_value());
|
||||
const auto priority_vector =
|
||||
signals->GetPerGroupData("name1")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 1}}),
|
||||
*priority_vector);
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsBatchedRequest) {
|
||||
// Use partially overlapping keys, to cover both the shared and distinct key
|
||||
// cases.
|
||||
const std::vector<std::string> kKeys1{"key1", "key2"};
|
||||
const std::vector<std::string> kKeys2{"key2", "key3"};
|
||||
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys1, joining_origin_foo_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name2"}, kKeys2, joining_origin_bar_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(error_msg1);
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_EQ(R"({"key1":1,"key2":[2]})",
|
||||
ExtractBiddingSignals(signals1.get(), kKeys1));
|
||||
auto priority_vector = signals1->GetPerGroupData("name1")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 1}}),
|
||||
*priority_vector);
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2);
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"key2":[2],"key3":"3"})",
|
||||
ExtractBiddingSignals(signals2.get(), kKeys2));
|
||||
priority_vector = signals2->GetPerGroupData("name2")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 2}}),
|
||||
*priority_vector);
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsBatchedRequestError) {
|
||||
SetResponseStatusCode(net::HttpStatusCode::HTTP_NOT_FOUND);
|
||||
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, {{"key1"}}, joining_origin_foo_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name2"}, {{"key2"}}, joining_origin_bar_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
|
||||
const std::string kExpectedError = base::StringPrintf(
|
||||
"Failed to load %s HTTP status = 404 "
|
||||
"Not Found.",
|
||||
TrustedBiddingSignalsUrl().spec().c_str());
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(signals1);
|
||||
EXPECT_EQ(kExpectedError, error_msg1);
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(signals2);
|
||||
EXPECT_EQ(kExpectedError, error_msg2);
|
||||
|
||||
// Wait until idle to ensure all requests have been observed within the
|
||||
// `auction_network_events_handler_`.
|
||||
task_environment_.RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsSequentialRequests) {
|
||||
// Use partially overlapping keys, to cover both the shared and distinct key
|
||||
// cases.
|
||||
const std::vector<std::string> kKeys1{"key1", "key2"};
|
||||
const std::vector<std::string> kKeys2{"key2", "key3"};
|
||||
|
||||
// Note that these responses use different values for the shared key.
|
||||
scoped_refptr<TrustedSignals::Result> signals1 = FetchBiddingSignals(
|
||||
{"name1"}, kKeys1, joining_origin_foo_, execution_mode_);
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_FALSE(error_msg_.has_value());
|
||||
EXPECT_EQ(R"({"key1":1,"key2":[2]})",
|
||||
ExtractBiddingSignals(signals1.get(), kKeys1));
|
||||
auto priority_vector = signals1->GetPerGroupData("name1")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 1}}),
|
||||
*priority_vector);
|
||||
|
||||
scoped_refptr<TrustedSignals::Result> signals2 = FetchBiddingSignals(
|
||||
{"name2"}, kKeys2, joining_origin_foo_, execution_mode_);
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_FALSE(error_msg_.has_value());
|
||||
EXPECT_EQ(R"({"key2":[2],"key3":"3"})",
|
||||
ExtractBiddingSignals(signals2.get(), kKeys2));
|
||||
priority_vector = signals2->GetPerGroupData("name2")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 2}}),
|
||||
*priority_vector);
|
||||
}
|
||||
|
||||
// Test the case where there are multiple network requests live at once.
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest,
|
||||
BiddingSignalsSimultaneousNetworkRequests) {
|
||||
// Use partially overlapping keys, to cover both the shared and distinct key
|
||||
// cases.
|
||||
const std::vector<std::string> kKeys1{"key1", "key2"};
|
||||
const std::vector<std::string> kKeys2{"key2", "key3"};
|
||||
|
||||
base::RunLoop run_loop1;
|
||||
scoped_refptr<TrustedSignals::Result> signals1;
|
||||
std::optional<std::string> error_msg1;
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys1, joining_origin_foo_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals1, &error_msg1,
|
||||
run_loop1.QuitClosure()));
|
||||
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name2"}, kKeys2, joining_origin_foo_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
run_loop1.Run();
|
||||
EXPECT_FALSE(error_msg1);
|
||||
ASSERT_TRUE(signals1);
|
||||
EXPECT_EQ(R"({"key1":1,"key2":[2]})",
|
||||
ExtractBiddingSignals(signals1.get(), kKeys1));
|
||||
auto priority_vector = signals1->GetPerGroupData("name1")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 1}}),
|
||||
*priority_vector);
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2);
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"key2":[2],"key3":"3"})",
|
||||
ExtractBiddingSignals(signals2.get(), kKeys2));
|
||||
priority_vector = signals2->GetPerGroupData("name2")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 2}}),
|
||||
*priority_vector);
|
||||
}
|
||||
|
||||
// Make two requests, cancel the first one, then try to start a network request.
|
||||
// A request should be made, but only for the key in the request that was not
|
||||
// cancelled. Only test bidders, since sellers have no significant differences
|
||||
// in this path.
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest, CancelOneRequest) {
|
||||
const std::vector<std::string> kKeys1{"key1"};
|
||||
const std::vector<std::string> kKeys2{"key2"};
|
||||
|
||||
// The request for `key1` will be cancelled before the network request is
|
||||
// created.
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys1, joining_origin_foo_, execution_mode_,
|
||||
base::BindOnce(&NeverInvokedLoadSignalsCallback));
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name2"}, kKeys2, joining_origin_foo_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
request1.reset();
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2);
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"key2":[2]})", ExtractBiddingSignals(signals2.get(), kKeys2));
|
||||
const auto priority_vector =
|
||||
signals2->GetPerGroupData("name2")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 2}}),
|
||||
*priority_vector);
|
||||
}
|
||||
|
||||
// Make two requests, try to start a network request, then cancel the first one.
|
||||
// The request that was not cancelled should complete normally. Only test
|
||||
// bidders, since sellers have no significant differences in this path.
|
||||
TEST_F(TrustedSignalsRequestManagerKVv2EmbeddedTest, CancelOneLiveRequest) {
|
||||
const std::vector<std::string> kKeys1{"key1"};
|
||||
const std::vector<std::string> kKeys2{"key2"};
|
||||
|
||||
auto request1 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name1"}, kKeys1, joining_origin_foo_, execution_mode_,
|
||||
base::BindOnce(&NeverInvokedLoadSignalsCallback));
|
||||
|
||||
base::RunLoop run_loop2;
|
||||
scoped_refptr<TrustedSignals::Result> signals2;
|
||||
std::optional<std::string> error_msg2;
|
||||
auto request2 = bidding_request_manager_->RequestKVv2BiddingSignals(
|
||||
{"name2"}, kKeys2, joining_origin_bar_, execution_mode_,
|
||||
base::BindOnce(&LoadSignalsCallback, &signals2, &error_msg2,
|
||||
run_loop2.QuitClosure()));
|
||||
|
||||
// Wait for network request to be made, which should include both keys in the
|
||||
// URLs.
|
||||
bidding_request_manager_->StartBatchedTrustedSignalsRequest();
|
||||
base::RunLoop().RunUntilIdle();
|
||||
|
||||
request1.reset();
|
||||
// `request2` should still complete.
|
||||
run_loop2.Run();
|
||||
EXPECT_FALSE(error_msg2);
|
||||
ASSERT_TRUE(signals2);
|
||||
EXPECT_EQ(R"({"key2":[2]})", ExtractBiddingSignals(signals2.get(), kKeys2));
|
||||
const auto priority_vector =
|
||||
signals2->GetPerGroupData("name2")->priority_vector;
|
||||
ASSERT_TRUE(priority_vector);
|
||||
EXPECT_EQ((TrustedSignals::Result::PriorityVector{{"foo", 2}}),
|
||||
*priority_vector);
|
||||
|
||||
// The callback of `request1` should not be invoked, since it was cancelled.
|
||||
base::RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace auction_worklet
|
||||
|
Reference in New Issue
Block a user