| // 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_ENABLED_BY_DEFAULT); | |
| namespace { | |
| std::optional<ParameterizedItem> ParseItemInternal(std::string_view str, | |
| bool strict) { | |
| if (base::FeatureList::IsEnabled(kStructuredHeadersInRust)) { | |
| ParameterizedItem item; | |
| bool ok = ParseAndRecordMetrics(kTimeMetricItem, kSuccessMetricItem, [&]() { | |
| return sfv::decode_item(base::StringViewToRustSlice(str), item, strict); | |
| }); | |
| if (!ok) { | |
| return std::nullopt; | |
| } | |
| return item; | |
| } | |
| return ParseAndRecordMetrics(kTimeMetricItem, kSuccessMetricItem, [&]() { | |
| return quiche::structured_headers::ParseItem(str, strict); | |
| }); | |
| } | |
| std::optional<List> ParseListInternal(std::string_view str, bool strict) { | |
| if (base::FeatureList::IsEnabled(kStructuredHeadersInRust)) { | |
| List list; | |
| bool ok = ParseAndRecordMetrics(kTimeMetricList, kSuccessMetricList, [&] { | |
| return sfv::decode_list(base::StringViewToRustSlice(str), list, strict); | |
| }); | |
| if (!ok) { | |
| return std::nullopt; | |
| } | |
| return list; | |
| } | |
| return ParseAndRecordMetrics(kTimeMetricList, kSuccessMetricList, [&]() { | |
| return quiche::structured_headers::ParseList(str, strict); | |
| }); | |
| } | |
| std::optional<Dictionary> ParseDictionaryInternal(std::string_view str, | |
| bool strict) { | |
| if (base::FeatureList::IsEnabled(kStructuredHeadersInRust)) { | |
| Dictionary dictionary; | |
| bool ok = ParseAndRecordMetrics( | |
| kTimeMetricDictionary, kSuccessMetricDictionary, [&] { | |
| return sfv::decode_dictionary(base::StringViewToRustSlice(str), | |
| dictionary, strict); | |
| }); | |
| if (!ok) { | |
| return std::nullopt; | |
| } | |
| return dictionary; | |
| } | |
| return ParseAndRecordMetrics( | |
| kTimeMetricDictionary, kSuccessMetricDictionary, [&]() { | |
| return quiche::structured_headers::ParseDictionary(str, strict); | |
| }); | |
| } | |
| } // namespace | |
| std::optional<ParameterizedItem> ParseItem(std::string_view str) { | |
| return ParseItemInternal(str, /*strict=*/false); | |
| } | |
| std::optional<ParameterizedItem> ParseItemStrict(std::string_view str) { | |
| return ParseItemInternal(str, /*strict=*/true); | |
| } | |
| std::optional<List> ParseList(std::string_view str) { | |
| return ParseListInternal(str, /*strict=*/false); | |
| } | |
| std::optional<List> ParseListStrict(std::string_view str) { | |
| return ParseListInternal(str, /*strict=*/true); | |
| } | |
| std::optional<Dictionary> ParseDictionary(std::string_view str) { | |
| return ParseDictionaryInternal(str, /*strict=*/false); | |
| } | |
| std::optional<Dictionary> ParseDictionaryStrict(std::string_view str) { | |
| return ParseDictionaryInternal(str, /*strict=*/true); | |
| } | |
| } // namespace net::structured_headers |