text: // Copyright 2017 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "services/network/public/cpp/client_hints.h"
  
  #include <utility>
  #include <vector>
  
  #include "base/cxx17_backports.h"
  #include "base/no_destructor.h"
  #include "base/strings/string_number_conversions.h"
  #include "base/strings/string_tokenizer.h"
  #include "base/strings/string_util.h"
  #include "net/http/structured_headers.h"
  #include "third_party/abseil-cpp/absl/types/optional.h"
  
  namespace network {
  
  ClientHintToNameMap MakeClientHintToNameMap() {
    return {
        {network::mojom::WebClientHintsType::kDeviceMemory_DEPRECATED,
         "device-memory"},
        {network::mojom::WebClientHintsType::kDpr_DEPRECATED, "dpr"},
        {network::mojom::WebClientHintsType::kResourceWidth_DEPRECATED, "width"},
        {network::mojom::WebClientHintsType::kViewportWidth_DEPRECATED,
         "viewport-width"},
        {network::mojom::WebClientHintsType::kRtt_DEPRECATED, "rtt"},
        {network::mojom::WebClientHintsType::kDownlink_DEPRECATED, "downlink"},
        {network::mojom::WebClientHintsType::kEct_DEPRECATED, "ect"},
        {network::mojom::WebClientHintsType::kUA, "sec-ch-ua"},
        {network::mojom::WebClientHintsType::kUAArch, "sec-ch-ua-arch"},
        {network::mojom::WebClientHintsType::kUAPlatform, "sec-ch-ua-platform"},
        {network::mojom::WebClientHintsType::kUAModel, "sec-ch-ua-model"},
        {network::mojom::WebClientHintsType::kUAMobile, "sec-ch-ua-mobile"},
        {network::mojom::WebClientHintsType::kUAFullVersion,
         "sec-ch-ua-full-version"},
        {network::mojom::WebClientHintsType::kUAPlatformVersion,
         "sec-ch-ua-platform-version"},
        {network::mojom::WebClientHintsType::kPrefersColorScheme,
         "sec-ch-prefers-color-scheme"},
        {network::mojom::WebClientHintsType::kUABitness, "sec-ch-ua-bitness"},
        {network::mojom::WebClientHintsType::kUAReduced, "sec-ch-ua-reduced"},
        {network::mojom::WebClientHintsType::kViewportHeight,
         "sec-ch-viewport-height"},
        {network::mojom::WebClientHintsType::kDeviceMemory,
         "sec-ch-device-memory"},
        {network::mojom::WebClientHintsType::kDpr, "sec-ch-dpr"},
        {network::mojom::WebClientHintsType::kResourceWidth, "sec-ch-width"},
        {network::mojom::WebClientHintsType::kViewportWidth,
         "sec-ch-viewport-width"},
    };
  }
  
  const ClientHintToNameMap& GetClientHintToNameMap() {
    static const base::NoDestructor<ClientHintToNameMap> map(
        MakeClientHintToNameMap());
    return *map;
  }
  
  namespace {
  
  struct ClientHintNameCompator {
    bool operator()(const std::string& lhs, const std::string& rhs) const {
      return base::CompareCaseInsensitiveASCII(lhs, rhs) < 0;
    }
  };
  
  using DecodeMap = base::flat_map<std::string,
                                   network::mojom::WebClientHintsType,
                                   ClientHintNameCompator>;
  
  DecodeMap MakeDecodeMap() {
    DecodeMap result;
    for (const auto& elem : network::GetClientHintToNameMap()) {
      const auto& type = elem.first;
      const auto& header = elem.second;
      result.insert(std::make_pair(header, type));
    }
    return result;
  }
  
  const DecodeMap& GetDecodeMap() {
    static const base::NoDestructor<DecodeMap> decode_map(MakeDecodeMap());
    return *decode_map;
  }
  
  }  // namespace
  
  absl::optional<std::vector<network::mojom::WebClientHintsType>>
  ParseClientHintsHeader(const std::string& header) {
    // Accept-CH is an sh-list of tokens; see:
    // https://httpwg.org/http-extensions/client-hints.html#rfc.section.3.1
    absl::optional<net::structured_headers::List> maybe_list =
        net::structured_headers::ParseList(header);
    if (!maybe_list.has_value())
      return absl::nullopt;
  
    // Standard validation rules: we want a list of tokens, so this better
    // only have tokens (but params are OK!)
    for (const auto& list_item : maybe_list.value()) {
      // Make sure not a nested list.
      if (list_item.member.size() != 1u)
        return absl::nullopt;
      if (!list_item.member[0].item.is_token())
        return absl::nullopt;
    }
  
    std::vector<network::mojom::WebClientHintsType> result;
  
    // Now convert those to actual hint enums.
    const DecodeMap& decode_map = GetDecodeMap();
    for (const auto& list_item : maybe_list.value()) {
      const std::string& token_value = list_item.member[0].item.GetString();
      auto iter = decode_map.find(token_value);
      if (iter != decode_map.end())
        result.push_back(iter->second);
    }  // for list_item
    return absl::make_optional(std::move(result));
  }
  
  base::TimeDelta ParseAcceptCHLifetime(const std::string& header) {
    int64_t persist_duration_seconds = 0;
    if (!base::StringToInt64(header, &persist_duration_seconds) ||
        persist_duration_seconds <= 0)
      return base::TimeDelta();
  
    return base::Seconds(persist_duration_seconds);
  }
  
  }  // namespace network
  