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_