910e62b5创建于 1月15日历史提交
// 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.

#include "content/browser/devtools/request_body_collector.h"

#include "base/containers/extend.h"
#include "base/memory/raw_ref.h"
#include "base/numerics/safe_conversions.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/data_pipe.h"
#include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "net/base/net_errors.h"
#include "services/network/public/cpp/data_element.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "services/network/public/mojom/data_pipe_getter.mojom.h"

namespace content {

class RequestBodyCollector::BodyReader : public mojo::DataPipeDrainer::Client {
 public:
  BodyReader(
      RequestBodyCollector& collector,
      mojo::PendingRemote<network::mojom::DataPipeGetter> data_pipe_getter)
      : collector_(collector), data_pipe_getter_(std::move(data_pipe_getter)) {
    data_pipe_getter_.set_disconnect_handler(
        base::BindOnce(&BodyReader::OnFailure, base::Unretained(this)));
    mojo::ScopedDataPipeProducerHandle pipe_producer;
    MojoResult result =
        CreateDataPipe(/*options=*/nullptr, pipe_producer, pipe_consumer_);
    CHECK_EQ(MOJO_RESULT_OK, result);
    data_pipe_getter_->Read(
        std::move(pipe_producer),
        base::BindOnce(&BodyReader::OnReadStarted, base::Unretained(this)));
  }

  ~BodyReader() override = default;

 private:
  // mojo::DataPipeDrainer::Client overrides
  void OnDataAvailable(base::span<const uint8_t> data) override {
    CHECK_NE(expected_size_, 0ul);
    base::Extend(bytes_, data);
  }

  void OnDataComplete() override {
    BodyEntry entry = expected_size_ == bytes_.size()
                          ? BodyEntry(std::move(bytes_))
                          : base::unexpected("Unexpected end of data");
    collector_->OnReaderComplete(this, std::move(entry));
  }

  void OnFailure() {
    collector_->OnReaderComplete(this, base::unexpected("Error reading blob"));
    // `this` is invalid at this point.
  }

  void OnReadStarted(int32_t status, uint64_t size) {
    if (status != net::OK) {
      OnFailure();
      // `this` is invalid at this point.
      return;
    }
    expected_size_ = base::checked_cast<size_t>(size);
    bytes_.reserve(expected_size_);
    pipe_drainer_.emplace(this, std::move(pipe_consumer_));
  }

  const raw_ref<RequestBodyCollector> collector_;
  mojo::Remote<network::mojom::DataPipeGetter> data_pipe_getter_;
  size_t expected_size_ = 0;
  mojo::ScopedDataPipeConsumerHandle pipe_consumer_;
  std::optional<mojo::DataPipeDrainer> pipe_drainer_;
  std::vector<uint8_t> bytes_;
};

// static
std::unique_ptr<RequestBodyCollector> RequestBodyCollector::Collect(
    const network::ResourceRequestBody& request_body,
    CompletionCallback callback) {
  std::vector<BodyEntry> bodies;
  const auto& elements = *request_body.elements();
  bodies.resize(elements.size());
  ReadersMap readers;

  std::unique_ptr<RequestBodyCollector> collector(new RequestBodyCollector());

  for (size_t i = 0; i < elements.size(); ++i) {
    const network::DataElement& element = elements[i];
    switch (element.type()) {
      case network::DataElement::Tag::kBytes:
        bodies[i] = element.As<network::DataElementBytes>().bytes();
        break;
      case network::DataElement::Tag::kDataPipe: {
        mojo::PendingRemote<network::mojom::DataPipeGetter> data_pipe_getter =
            element.As<network::DataElementDataPipe>().CloneDataPipeGetter();
        readers.insert(
            std::make_pair(std::make_unique<BodyReader>(
                               *collector, std::move(data_pipe_getter)),
                           i));
        break;
      }
      case network::DataElement::Tag::kFile:
      case network::DataElement::Tag::kChunkedDataPipe:
        bodies[i] = base::unexpected("Unsupported entry");
        break;
    }
  }
  if (readers.empty()) {
    std::move(callback).Run(std::move(bodies));
    return nullptr;
  }
  collector->callback_ = std::move(callback);
  collector->bodies_ = std::move(bodies);
  collector->readers_ = std::move(readers);

  return collector;
}

RequestBodyCollector::~RequestBodyCollector() = default;

RequestBodyCollector::RequestBodyCollector() = default;

void RequestBodyCollector::OnReaderComplete(BodyReader* reader,
                                            BodyEntry entry) {
  auto it = readers_.find(reader);
  CHECK(it != readers_.end());
  bodies_[it->second] = std::move(entry);
  readers_.erase(it);

  if (!readers_.empty()) {
    return;
  }
  std::move(callback_).Run(bodies_);
  // `this` may be invalid at this point due to callback invocation above.
}

}  // namespace content