// Copyright 2020 The Chromium Authors
// 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/parsed_headers.h"

#include <algorithm>
#include <string>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "build/build_config.h"
#include "net/base/features.h"
#include "net/http/http_cookie_indices.h"
#include "net/http/http_response_headers.h"
#include "net/reporting/reporting_header_parser.h"
#include "net/url_request/clear_site_data.h"
#include "services/network/public/cpp/avail_language_header_parser.h"
#include "services/network/public/cpp/client_hints.h"
#include "services/network/public/cpp/connection_allowlist.h"
#include "services/network/public/cpp/connection_allowlist_parser.h"
#include "services/network/public/cpp/content_language_parser.h"
#include "services/network/public/cpp/content_security_policy/content_security_policy.h"
#include "services/network/public/cpp/cross_origin_embedder_policy_parser.h"
#include "services/network/public/cpp/cross_origin_opener_policy_parser.h"
#include "services/network/public/cpp/declarative_performance_observer_parser.h"
#include "services/network/public/cpp/document_isolation_policy_parser.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/fence_event_reporting_parser.h"
#include "services/network/public/cpp/integrity_policy.h"
#include "services/network/public/cpp/integrity_policy_parser.h"
#include "services/network/public/cpp/link_header_parser.h"
#include "services/network/public/cpp/no_vary_search_header_parser.h"
#include "services/network/public/cpp/origin_agent_cluster_parser.h"
#include "services/network/public/cpp/supports_loading_mode/supports_loading_mode_parser.h"
#include "services/network/public/cpp/timing_allow_origin_parser.h"
#include "services/network/public/cpp/x_frame_options_parser.h"
#include "services/network/public/mojom/supports_loading_mode.mojom.h"

namespace network {

mojom::ParsedHeadersPtr PopulateParsedHeaders(
    const net::HttpResponseHeaders* headers,
    const GURL& url) {
  auto parsed_headers = mojom::ParsedHeaders::New();
  if (!headers)
    return parsed_headers;

  AddContentSecurityPolicyFromHeaders(*headers, url,
                                      &parsed_headers->content_security_policy);

  parsed_headers->allow_csp_from = ParseAllowCSPFromHeader(*headers);

  parsed_headers->cross_origin_embedder_policy =
      ParseCrossOriginEmbedderPolicy(*headers);
  parsed_headers->cross_origin_opener_policy =
      ParseCrossOriginOpenerPolicy(*headers);

  parsed_headers->document_isolation_policy =
      ParseDocumentIsolationPolicy(*headers);

  if (base::FeatureList::IsEnabled(network::features::kConnectionAllowlists)) {
    parsed_headers->connection_allowlists =
        ParseConnectionAllowlistsFromHeaders(*headers, url);
    parsed_headers->allow_connection_allowlist_from =
        ParseAllowConnectionAllowlistFromHeader(*headers);
  }

  if (base::FeatureList::IsEnabled(network::features::kIntegrityPolicyScript)) {
    parsed_headers->integrity_policy = ParseIntegrityPolicyFromHeaders(
        *headers, IntegrityPolicyHeaderType::kEnforce);
    parsed_headers->integrity_policy_report_only =
        ParseIntegrityPolicyFromHeaders(*headers,
                                        IntegrityPolicyHeaderType::kReportOnly);
  }

  const std::optional<std::string> origin_agent_cluster =
      headers->GetNormalizedHeader("Origin-Agent-Cluster");
  parsed_headers->origin_agent_cluster =
      ParseOriginAgentCluster(origin_agent_cluster);

  // If the Clear-Site-Data header would clear client hints, we must not respect
  // any Accept-CH or Critical-CH headers.
  parsed_headers->client_hints_ignored_due_to_clear_site_data_header = false;
  std::string clear_site_data_header =
      headers->GetNormalizedHeader(net::kClearSiteDataHeader)
          .value_or(std::string());
  std::vector<std::string> clear_site_data_types =
      net::ClearSiteDataHeaderContents(clear_site_data_header);
  base::flat_set<std::string> clear_site_data_set(
      std::move(clear_site_data_types));
  if (clear_site_data_set.contains(net::kDatatypeCache) ||
      clear_site_data_set.contains(net::kDatatypeClientHints) ||
      clear_site_data_set.contains(net::kDatatypeCookies) ||
      clear_site_data_set.contains(net::kDatatypeWildcard)) {
    parsed_headers->client_hints_ignored_due_to_clear_site_data_header = true;
  }
  if (!features::ShouldBlockAcceptClientHintsFor(url::Origin::Create(url)) &&
      !parsed_headers->client_hints_ignored_due_to_clear_site_data_header) {
    if (std::optional<std::string> accept_ch =
            headers->GetNormalizedHeader("Accept-CH")) {
      parsed_headers->accept_ch = ParseClientHintsHeader(*accept_ch);
    }

    if (std::optional<std::string> critical_ch =
            headers->GetNormalizedHeader("Critical-CH")) {
      parsed_headers->critical_ch = ParseClientHintsHeader(*critical_ch);
    }
  }

  parsed_headers->xfo = ParseXFrameOptions(*headers);

  parsed_headers->link_headers = ParseLinkHeaders(*headers, url);

  if (std::optional<std::string> timing_allow_origin_value =
          headers->GetNormalizedHeader("Timing-Allow-Origin")) {
    parsed_headers->timing_allow_origin =
        ParseTimingAllowOrigin(*timing_allow_origin_value);
  }

  network::mojom::SupportsLoadingModePtr result =
      network::ParseSupportsLoadingMode(*headers);
  if (!result.is_null()) {
    if (std::ranges::contains(
            result->supported_modes,
            network::mojom::LoadingMode::kCredentialedPrerender)) {
      parsed_headers->supports_loading_mode.push_back(
          network::mojom::LoadingMode::kCredentialedPrerender);
    }

    if (std::ranges::contains(
            result->supported_modes,
            network::mojom::LoadingMode::kPrerenderCrossOriginFrames)) {
      parsed_headers->supports_loading_mode.push_back(
          network::mojom::LoadingMode::kPrerenderCrossOriginFrames);
    }
  }

#if BUILDFLAG(ENABLE_REPORTING)
  if (std::optional<std::string> reporting_endpoints =
          headers->GetNormalizedHeader("Reporting-Endpoints")) {
    parsed_headers->reporting_endpoints =
        net::ParseReportingEndpoints(*reporting_endpoints);
  }
#endif

  if (base::FeatureList::IsEnabled(network::features::kCookieIndicesHeader)) {
    parsed_headers->cookie_indices = net::ParseCookieIndices(*headers);
  }

  if (base::FeatureList::IsEnabled(network::features::kReduceAcceptLanguage) ||
      base::FeatureList::IsEnabled(
          network::features::kReduceAcceptLanguageHTTP)) {
    if (std::optional<std::string> avail_language =
            headers->GetNormalizedHeader("Avail-Language")) {
      parsed_headers->avail_language = ParseAvailLanguage(*avail_language);
    }
    if (std::optional<std::string> content_language =
            headers->GetNormalizedHeader("Content-Language")) {
      parsed_headers->content_language =
          ParseContentLanguages(*content_language);
    }
  }

  // The code here only parses the No-Vary-Search header if it is present.
  parsed_headers->no_vary_search_with_parse_error = ParseNoVarySearch(*headers);

  parsed_headers->allow_cross_origin_event_reporting =
      ParseAllowCrossOriginEventReportingFromHeader(*headers);

  if (std::optional<std::string> performance_observer_header =
          headers->GetNormalizedHeader("Performance-Observer")) {
    parsed_headers->declarative_performance_observer_policy =
        ParseDeclarativePerformanceObserverPolicy(*performance_observer_header);
  }

  if (std::optional<std::string> prefetch_activation_beacon =
          headers->GetNormalizedHeader("on-prefetch-activation")) {
    parsed_headers->prefetch_activation_beacon_endpoint =
        url.Resolve(*prefetch_activation_beacon);
  }

  return parsed_headers;
}

}  // namespace network
