blob: 0fa722f1e3398842368bec658f21877717a8766c [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/http/structured_headers.h"
#include <optional>
#include <string>
#include <string_view>
#include "base/feature.h"
#include "base/feature_list.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_view_rust.h"
#include "base/strings/string_view_util.h"
#include "base/time/time.h"
#include "third_party/rust/sfv/v0_15/wrapper/functions.h"
#include "third_party/rust/sfv/v0_15/wrapper/lib.rs.h"
// This namespace defines FFI-friendly functions that are called from Rust in
// //third_party/rust/sfv/v0_15/wrapper/.
namespace sfv {
Item& list_append_item(List& list) {
return *list.emplace_back(Item()).GetIfItem();
}
InnerList& list_append_inner_list(List& list) {
return *list.emplace_back(InnerList()).GetIfInnerList();
}
Item& dictionary_set_item(Dictionary& dictionary, rust::Str key) {
auto& member = dictionary[base::RustStrToStringView(key)];
member = net::structured_headers::ParameterizedMember(Item());
return *member.GetIfItem();
}
InnerList& dictionary_set_inner_list(Dictionary& dictionary, rust::Str key) {
auto& member = dictionary[base::RustStrToStringView(key)];
member = net::structured_headers::ParameterizedMember(InnerList());
return *member.GetIfInnerList();
}
Item& inner_list_append_item(InnerList& inner_list) {
return inner_list.items.emplace_back();
}
BareItem& get_item_bare_item(Item& item) {
return item.item;
}
void set_bare_item_boolean(BareItem& out, bool v) {
out = BareItem(v);
}
void set_bare_item_integer(BareItem& out, int64_t v) {
out = BareItem(v);
}
void set_bare_item_decimal(BareItem& out, double v) {
out = BareItem(v);
}
void set_bare_item_string(BareItem& out, rust::Str v) {
out = BareItem(BareItem::string, base::RustStrToStringView(v));
}
void set_bare_item_token(BareItem& out, rust::Str v) {
out = BareItem(BareItem::token, base::RustStrToStringView(v));
}
void set_bare_item_byte_sequence(BareItem& out, rust::Slice<const uint8_t> v) {
out = BareItem(BareItem::byte_sequence, base::as_string_view(v));
}
namespace {
BareItem& get_or_insert_param(net::structured_headers::Parameters& params,
rust::Str key) {
std::string_view key_view = base::RustStrToStringView(key);
for (auto& param : params) {
if (param.first == key_view) {
return param.second;
}
}
return params.emplace_back(key_view, BareItem()).second;
}
} // namespace
BareItem& get_or_insert_inner_list_param(InnerList& inner_list, rust::Str key) {
return get_or_insert_param(inner_list.params, key);
}
BareItem& get_or_insert_item_param(Item& item, rust::Str key) {
return get_or_insert_param(item.params, key);
}
} // namespace sfv
namespace net::structured_headers {
namespace {
constexpr char kTimeMetricItem[] = "Net.StructuredHeaders.ParseItem.Time";
constexpr char kTimeMetricList[] = "Net.StructuredHeaders.ParseList.Time";
constexpr char kTimeMetricDictionary[] =
"Net.StructuredHeaders.ParseDictionary.Time";
constexpr char kSuccessMetricItem[] = "Net.StructuredHeaders.ParseItem.Success";
constexpr char kSuccessMetricList[] = "Net.StructuredHeaders.ParseList.Success";
constexpr char kSuccessMetricDictionary[] =
"Net.StructuredHeaders.ParseDictionary.Success";
template <typename Parse>
auto ParseAndRecordMetrics(std::string_view time_metric,
std::string_view success_metric,
Parse&& parse) {
const base::TimeTicks start = base::TimeTicks::Now();
auto result = parse();
base::UmaHistogramMicrosecondsTimes(time_metric,
base::TimeTicks::Now() - start);
base::UmaHistogramBoolean(success_metric, !!result);
return result;
}
} // namespace
BASE_FEATURE(kStructuredHeadersInRust, base::FEATURE_DISABLED_BY_DEFAULT);
std::optional<ParameterizedItem> ParseItem(std::string_view str) {
if (base::FeatureList::IsEnabled(kStructuredHeadersInRust)) {
ParameterizedItem item;
bool ok = ParseAndRecordMetrics(kTimeMetricItem, kSuccessMetricItem, [&]() {
return sfv::decode_item(base::StringViewToRustSlice(str), item);
});
if (!ok) {
return std::nullopt;
}
return item;
}
return ParseAndRecordMetrics(kTimeMetricItem, kSuccessMetricItem, [&]() {
return quiche::structured_headers::ParseItem(str);
});
}
std::optional<List> ParseList(std::string_view str) {
if (base::FeatureList::IsEnabled(kStructuredHeadersInRust)) {
List list;
bool ok = ParseAndRecordMetrics(kTimeMetricList, kSuccessMetricList, [&] {
return sfv::decode_list(base::StringViewToRustSlice(str), list);
});
if (!ok) {
return std::nullopt;
}
return list;
}
return ParseAndRecordMetrics(kTimeMetricList, kSuccessMetricList, [&]() {
return quiche::structured_headers::ParseList(str);
});
}
std::optional<Dictionary> ParseDictionary(std::string_view str) {
if (base::FeatureList::IsEnabled(kStructuredHeadersInRust)) {
Dictionary dictionary;
bool ok = ParseAndRecordMetrics(
kTimeMetricDictionary, kSuccessMetricDictionary, [&] {
return sfv::decode_dictionary(base::StringViewToRustSlice(str),
dictionary);
});
if (!ok) {
return std::nullopt;
}
return dictionary;
}
return ParseAndRecordMetrics(
kTimeMetricDictionary, kSuccessMetricDictionary,
[&]() { return quiche::structured_headers::ParseDictionary(str); });
}
} // namespace net::structured_headers