1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
content / browser / interest_group / trusted_signals_fetcher.h [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 CONTENT_BROWSER_INTEREST_GROUP_TRUSTED_SIGNALS_FETCHER_H_
#define CONTENT_BROWSER_INTEREST_GROUP_TRUSTED_SIGNALS_FETCHER_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <vector>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/weak_ptr.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "content/common/content_export.h"
#include "content/services/auction_worklet/public/mojom/trusted_signals_cache.mojom.h"
#include "net/third_party/quiche/src/quiche/oblivious_http/buffers/oblivious_http_request.h"
#include "services/data_decoder/public/cpp/data_decoder.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
struct BiddingAndAuctionServerKey;
// Single-use network fetcher for versions 2+ of the key-value server API.
// It takes a list compression groups and partitions, and asynchronously returns
// a set of responses, one per compression group. The responses are provided as
// still compressed compression group bodies, so the cache layer can store
// compressed responses and to minimize IPC size. The responses will be
// decompressed before use in the appropriate Javascript process.
//
// Bidding and scoring signals need different structs when sending requests, but
// they use the same response format, since it's only the compressed data itself
// that varies based on signals type.
//
// TODO(https://crbug.com/333445540): This is currently only an API, with no
// implementation. Need to actually implement the API.
class CONTENT_EXPORT TrustedSignalsFetcher {
public:
static constexpr std::string_view kRequestMediaType =
"message/ad-auction-trusted-signals-request";
static constexpr std::string_view kResponseMediaType =
"message/ad-auction-trusted-signals-response";
// All the data needed to request a particular bidding signals partition.
struct CONTENT_EXPORT BiddingPartition {
// Pointer arguments must remain valid until the BiddingPartition is
// destroyed.
BiddingPartition(int partition_id,
const std::set<std::string>* interest_group_names,
const std::set<std::string>* keys,
const base::Value::Dict* additional_params);
BiddingPartition(BiddingPartition&&);
~BiddingPartition();
BiddingPartition& operator=(BiddingPartition&&);
int partition_id;
base::raw_ref<const std::set<std::string>> interest_group_names;
base::raw_ref<const std::set<std::string>> keys;
// At the moment, valid keys are "experimentGroupId", "slotSize", and
// "allSlotsRequestedSizes". We could take them separately, but seems better
// to take one field rather than several?
base::raw_ref<const base::Value::Dict> additional_params;
};
// All the data needed to request a particular scoring signals partition.
struct CONTENT_EXPORT ScoringPartition {
// Pointer arguments must remain valid until the ScoringPartition is
// destroyed.
ScoringPartition(int partition_id,
const GURL* render_url,
const std::set<GURL>* component_render_urls,
const base::Value::Dict* additional_params);
ScoringPartition(ScoringPartition&&);
~ScoringPartition();
ScoringPartition& operator=(ScoringPartition&&);
int partition_id;
// Currently, TrustedSignalsCacheImpl puts the values from each bid in its
// own partition, so there will always be only one `render_url`.
base::raw_ref<const GURL> render_url;
base::raw_ref<const std::set<GURL>> component_render_urls;
// At the moment, valid keys are "experimentGroupId", "slotSize", and
// "allSlotsRequestedSizes". We could take them separately, but seems better
// to take one field rather than several?
base::raw_ref<const base::Value::Dict> additional_params;
};
// While buying and scoring signals partitions need different structs when
// sending requests, the responses use the same format.
// The received result for a particular compression group. Only returned on
// success.
struct CONTENT_EXPORT CompressionGroupResult {
CompressionGroupResult();
CompressionGroupResult(CompressionGroupResult&&);
~CompressionGroupResult();
CompressionGroupResult& operator=(CompressionGroupResult&&);
// The compression scheme used by `compression_group_data`, as indicated by
// the server.
auction_worklet::mojom::TrustedSignalsCompressionScheme compression_scheme;
// The still-compressed data for the compression group.
base::Value::BlobStorage compression_group_data;
// Time until the response expires.
base::TimeDelta ttl;
};
// A map of compression group ids to results, in the case of success.
using CompressionGroupResultMap = std::map<int, CompressionGroupResult>;
// The result of a fetch. Either the entire fetch succeeds or it fails with a
// single error.
using SignalsFetchResult =
base::expected<CompressionGroupResultMap, std::string>;
using Callback = base::OnceCallback<void(SignalsFetchResult)>;
TrustedSignalsFetcher();
// Virtual for tests.
virtual ~TrustedSignalsFetcher();
TrustedSignalsFetcher(const TrustedSignalsFetcher&) = delete;
TrustedSignalsFetcher& operator=(const TrustedSignalsFetcher&) = delete;
// `partitions` is a map of all partitions in the request, indexed by
// compression group id. Virtual for tests.
virtual void FetchBiddingSignals(
network::mojom::URLLoaderFactory* url_loader_factory,
std::string_view hostname,
const GURL& trusted_bidding_signals_url,
const BiddingAndAuctionServerKey& bidding_and_auction_key,
const std::map<int, std::vector<BiddingPartition>>& compression_groups,
Callback callback);
// `partitions` is a map of all partitions in the request, indexed by
// compression group id. Virtual for tests.
virtual void FetchScoringSignals(
network::mojom::URLLoaderFactory* url_loader_factory,
std::string_view hostname,
const GURL& trusted_scoring_signals_url,
const BiddingAndAuctionServerKey& bidding_and_auction_key,
const std::map<int, std::vector<ScoringPartition>>& compression_groups,
Callback callback);
private:
// Encrypts `plaintext_body` using `bidding_and_auction_key`, and then creates
// a SimpleURLLoader and starts a request. Once the request body has been
// created, everything else (including response body parsing) is identical for
// bidding and scoring signals, as only the data inside compression groups is
// different for bidding and scoring signals, and that layer is not parsed by
// this class.
void EncryptRequestBodyAndStart(
network::mojom::URLLoaderFactory* url_loader_factory,
const GURL& trusted_signals_url,
const BiddingAndAuctionServerKey& bidding_and_auction_key,
std::string plaintext_request_body,
Callback callback);
void OnRequestComplete(std::unique_ptr<std::string> response_body);
void OnCborParsed(data_decoder::DataDecoder::ValueOrError value_or_error);
// Attempts to parse the base::Value result from having the DataDecoder parse
// the CBOR contents of the fetch.
SignalsFetchResult ParseDataDecoderResult(
data_decoder::DataDecoder::ValueOrError value_or_error);
// Attempts to parse a single compression group object.
// `compression_group_value` should be a value from the `compressionGroups`
// array of the parsed CBOR value. On success, returns a
// CompressionGroupResult and sets `compression_group_id` to the ID from the
// passed in value. On failure, leaves `compression_group_id` alone, and
// returns a string.
base::expected<CompressionGroupResult, std::string> ParseCompressionGroup(
base::Value compression_group_value,
int& compression_group_id);
// Returns a string error message, prefixing the passed in message with the
// URL.
std::string CreateError(const std::string& error_message);
// The URL being fetched. Cached for using in error strings.
GURL trusted_signals_url_;
Callback callback_;
std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
// Context needed to decrypt the response. Initialized while encrypting the
// request body.
std::unique_ptr<quiche::ObliviousHttpRequest::Context> ohttp_context_;
// Compression scheme used by all compression groups. Populated when reading
// the response.
auction_worklet::mojom::TrustedSignalsCompressionScheme compression_scheme_ =
auction_worklet::mojom::TrustedSignalsCompressionScheme::kNone;
base::WeakPtrFactory<TrustedSignalsFetcher> weak_ptr_factory_{this};
};
} // namespace content
#endif // CONTENT_BROWSER_INTEREST_GROUP_TRUSTED_SIGNALS_FETCHER_H_