/** @file
 *

  A brief file description

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

#include <deque>

#include "tscore/ink_config.h"
#include "tscore/Filenames.h"
#include "tscore/Tokenizer.h"
#include <cctype>
#include <cstdint>
#include <cstring>
#include "proxy/http/HttpConfig.h"
#include "proxy/hdrs/HTTP.h"
#include "iocore/eventsystem/ConfigProcessor.h"
#include "iocore/eventsystem/UnixSocket.h"
#include "../../iocore/net/P_Net.h"
#include "../../records/P_RecUtils.h"
#include "records/RecHttp.h"
#include "proxy/http/HttpSessionManager.h"

#define HttpEstablishStaticConfigStringAlloc(_ix, _n) \
  RecEstablishStaticConfigString(_ix, _n);            \
  RecRegisterConfigUpdateCb(_n, http_config_cb, NULL)

#define HttpEstablishStaticConfigLongLong(_ix, _n) \
  RecEstablishStaticConfigInt(_ix, _n);            \
  RecRegisterConfigUpdateCb(_n, http_config_cb, NULL)

#define HttpEstablishStaticConfigFloat(_ix, _n) \
  RecEstablishStaticConfigFloat(_ix, _n);       \
  RecRegisterConfigUpdateCb(_n, http_config_cb, NULL)

#define HttpEstablishStaticConfigByte(_ix, _n) \
  RecEstablishStaticConfigByte(_ix, _n);       \
  RecRegisterConfigUpdateCb(_n, http_config_cb, NULL)

namespace
{
DbgCtl dbg_ctl_http_config{"http_config"};

} // end anonymous namespace

class HttpConfigCont : public Continuation
{
public:
  HttpConfigCont();
  int handle_event(int event, void *edata);
};

/// Data item for enumerated type config value.
template <typename T> struct ConfigEnumPair {
  T           _value;
  const char *_key;
};

/// Convert a string to an enumeration value.
/// @a n is the number of entries in the list.
/// @return @c true if the string is found, @c false if not found.
/// If found @a value is set to the corresponding value in @a list.
template <typename T, unsigned N>
static bool
http_config_enum_search(std::string_view key, const ConfigEnumPair<T> (&list)[N], MgmtByte &value)
{
  Dbg(dbg_ctl_http_config, "enum element %.*s", static_cast<int>(key.size()), key.data());
  // We don't expect any of these lists to be more than 10 long, so a linear search is the best choice.
  for (unsigned i = 0; i < N; ++i) {
    if (key.compare(list[i]._key) == 0) {
      value = list[i]._value;
      return true;
    }
  }
  return false;
}

/// Read a string from the configuration and convert it to an enumeration value.
/// @a n is the number of entries in the list.
/// @return @c true if the string is found, @c false if not found.
/// If found @a value is set to the corresponding value in @a list.
template <typename T, unsigned N>
static bool
http_config_enum_read(const char *name, const ConfigEnumPair<T> (&list)[N], MgmtByte &value)
{
  char key_buf[512]; // it's just one key - painful UI if keys are longer than this
  if (auto key{RecGetRecordString(name, key_buf, sizeof(key_buf))}; key) {
    return http_config_enum_search(key.value(), list, value);
  }
  return false;
}

////////////////////////////////////////////////////////////////
//
//  static variables
//
////////////////////////////////////////////////////////////////
/// Session sharing match types.
static const ConfigEnumPair<TSServerSessionSharingMatchType> SessionSharingMatchStrings[] = {
  {TS_SERVER_SESSION_SHARING_MATCH_NONE,     "none"    },
  {TS_SERVER_SESSION_SHARING_MATCH_IP,       "ip"      },
  {TS_SERVER_SESSION_SHARING_MATCH_HOST,     "host"    },
  {TS_SERVER_SESSION_SHARING_MATCH_HOST,     "hostsni" },
  {TS_SERVER_SESSION_SHARING_MATCH_BOTH,     "both"    },
  {TS_SERVER_SESSION_SHARING_MATCH_HOSTONLY, "hostonly"},
  {TS_SERVER_SESSION_SHARING_MATCH_SNI,      "sni"     },
  {TS_SERVER_SESSION_SHARING_MATCH_CERT,     "cert"    }
};

bool
HttpConfig::load_server_session_sharing_match(std::string_view key, MgmtByte &mask)
{
  MgmtByte value = 0;
  mask           = 0;
  // Parse through and build up mask
  size_t start  = 0;
  size_t offset = 0;
  Dbg(dbg_ctl_http_config, "enum mask value %.*s", static_cast<int>(key.length()), key.data());
  do {
    offset = key.find(',', start);
    if (offset == std::string_view::npos) {
      std::string_view one_key = key.substr(start);
      if (!http_config_enum_search(one_key, SessionSharingMatchStrings, value)) {
        return false;
      }
    } else {
      std::string_view one_key = key.substr(start, offset - start);
      if (!http_config_enum_search(one_key, SessionSharingMatchStrings, value)) {
        return false;
      }
      start = offset + 1;
    }
    if (value < TS_SERVER_SESSION_SHARING_MATCH_NONE) {
      mask |= (1 << value);
    } else if (value == TS_SERVER_SESSION_SHARING_MATCH_BOTH) {
      mask |= TS_SERVER_SESSION_SHARING_MATCH_MASK_IP | TS_SERVER_SESSION_SHARING_MATCH_MASK_HOSTONLY |
              TS_SERVER_SESSION_SHARING_MATCH_MASK_HOSTSNISYNC;
    } else if (value == TS_SERVER_SESSION_SHARING_MATCH_HOST) {
      mask |= TS_SERVER_SESSION_SHARING_MATCH_MASK_HOSTONLY | TS_SERVER_SESSION_SHARING_MATCH_MASK_HOSTSNISYNC;
    }
  } while (offset != std::string_view::npos);
  return true;
}

static bool
http_config_enum_mask_read(const char *name, MgmtByte &value)
{
  char key_buf[512]; // it's just one key - painful UI if keys are longer than this
  if (auto key{RecGetRecordString(name, key_buf, sizeof(key_buf))}; key) {
    return HttpConfig::load_server_session_sharing_match(key.value(), value);
  }
  return false;
}

static const ConfigEnumPair<TSServerSessionSharingPoolType> SessionSharingPoolStrings[] = {
  {TS_SERVER_SESSION_SHARING_POOL_GLOBAL,        "global"       },
  {TS_SERVER_SESSION_SHARING_POOL_THREAD,        "thread"       },
  {TS_SERVER_SESSION_SHARING_POOL_HYBRID,        "hybrid"       },
  {TS_SERVER_SESSION_SHARING_POOL_GLOBAL_LOCKED, "global_locked"},
};

int              HttpConfig::m_id = 0;
HttpConfigParams HttpConfig::m_master;

static int             http_config_changes = 1;
static HttpConfigCont *http_config_cont    = nullptr;

HttpConfigCont::HttpConfigCont() : Continuation(new_ProxyMutex())
{
  SET_HANDLER(&HttpConfigCont::handle_event);
}

int
HttpConfigCont::handle_event(int /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
{
  if (ink_atomic_increment(&http_config_changes, -1) == 1) {
    HttpConfig::reconfigure();
  }
  return 0;
}

int
http_config_cb(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData /* data ATS_UNUSED */,
               void * /* cookie ATS_UNUSED */)
{
  ink_atomic_increment(&http_config_changes, 1);

  INK_MEMORY_BARRIER;

  eventProcessor.schedule_in(http_config_cont, HRTIME_SECONDS(1), ET_CALL);
  return 0;
}

// [amc] Not sure which is uglier, this switch or having a micro-function for each var.
// Oh, how I long for when we can use C++eleventy lambdas without compiler problems!
// I think for 5.0 when the BC stuff is yanked, we should probably revert this to independent callbacks.
static int
http_server_session_sharing_cb(const char *name, RecDataT dtype, RecData data, void *cookie)
{
  bool              valid_p = true;
  HttpConfigParams *c       = static_cast<HttpConfigParams *>(cookie);

  if (0 == strcasecmp("proxy.config.http.server_session_sharing.match", name)) {
    MgmtByte &match = c->oride.server_session_sharing_match;
    if (RECD_INT == dtype) {
      match = static_cast<TSServerSessionSharingMatchType>(data.rec_int);
    } else if (RECD_STRING == dtype && HttpConfig::load_server_session_sharing_match(data.rec_string, match)) {
      // empty
    } else {
      valid_p = false;
    }
  } else {
    valid_p = false;
  }

  // Signal an update if valid value arrived.
  if (valid_p) {
    http_config_cb(name, dtype, data, cookie);
  }

  return REC_ERR_OKAY;
}

static int
http_insert_forwarded_cb(const char *name, RecDataT dtype, RecData data, void *cookie)
{
  bool              valid_p = false;
  HttpConfigParams *c       = static_cast<HttpConfigParams *>(cookie);

  if (0 == strcasecmp("proxy.config.http.insert_forwarded", name)) {
    if (RECD_STRING == dtype) {
      swoc::LocalBufferWriter<1024> error;
      HttpForwarded::OptionBitSet   bs = HttpForwarded::optStrToBitset(std::string_view(data.rec_string), error);
      if (!error.size()) {
        c->oride.insert_forwarded = bs;
        valid_p                   = true;
      } else {
        Error("HTTP %.*s", static_cast<int>(error.size()), error.data());
      }
    }
  }

  // Signal an update if valid value arrived.
  if (valid_p) {
    http_config_cb(name, dtype, data, cookie);
  }

  return REC_ERR_OKAY;
}

void
register_stat_callbacks()
{
  http_rsb.background_fill_bytes_aborted     = Metrics::Counter::createPtr("proxy.process.http.background_fill_bytes_aborted");
  http_rsb.background_fill_bytes_completed   = Metrics::Counter::createPtr("proxy.process.http.background_fill_bytes_completed");
  http_rsb.background_fill_current_count     = Metrics::Gauge::createPtr("proxy.process.http.background_fill_current_count");
  http_rsb.background_fill_total_count       = Metrics::Counter::createPtr("proxy.process.http.background_fill_total_count");
  http_rsb.broken_server_connections         = Metrics::Counter::createPtr("proxy.process.http.broken_server_connections");
  http_rsb.cache_deletes                     = Metrics::Counter::createPtr("proxy.process.http.cache_deletes");
  http_rsb.cache_hit_fresh                   = Metrics::Counter::createPtr("proxy.process.http.cache_hit_fresh");
  http_rsb.cache_hit_ims                     = Metrics::Counter::createPtr("proxy.process.http.cache_hit_ims");
  http_rsb.cache_hit_mem_fresh               = Metrics::Counter::createPtr("proxy.process.http.cache_hit_mem_fresh");
  http_rsb.cache_hit_reval                   = Metrics::Counter::createPtr("proxy.process.http.cache_hit_revalidated");
  http_rsb.cache_hit_rww                     = Metrics::Counter::createPtr("proxy.process.http.cache_hit_rww");
  http_rsb.cache_hit_stale_served            = Metrics::Counter::createPtr("proxy.process.http.cache_hit_stale_served");
  http_rsb.cache_lookups                     = Metrics::Counter::createPtr("proxy.process.http.cache_lookups");
  http_rsb.cache_miss_changed                = Metrics::Counter::createPtr("proxy.process.http.cache_miss_changed");
  http_rsb.cache_miss_client_no_cache        = Metrics::Counter::createPtr("proxy.process.http.cache_miss_client_no_cache");
  http_rsb.cache_miss_cold                   = Metrics::Counter::createPtr("proxy.process.http.cache_miss_cold");
  http_rsb.cache_miss_ims                    = Metrics::Counter::createPtr("proxy.process.http.cache_miss_ims");
  http_rsb.cache_miss_uncacheable            = Metrics::Counter::createPtr("proxy.process.http.cache_miss_client_not_cacheable");
  http_rsb.cache_open_read_begin_time        = Metrics::Counter::createPtr("proxy.process.http.milestone.cache_open_read_begin");
  http_rsb.cache_open_read_end_time          = Metrics::Counter::createPtr("proxy.process.http.milestone.cache_open_read_end");
  http_rsb.cache_open_write_adjust_thread    = Metrics::Counter::createPtr("proxy.process.http.cache.open_write.adjust_thread");
  http_rsb.cache_open_write_begin_time       = Metrics::Counter::createPtr("proxy.process.http.milestone.cache_open_write_begin");
  http_rsb.cache_open_write_end_time         = Metrics::Counter::createPtr("proxy.process.http.milestone.cache_open_write_end");
  http_rsb.cache_open_write_fail_count       = Metrics::Counter::createPtr("proxy.process.http.cache_open_write_fail_count");
  http_rsb.cache_read_error                  = Metrics::Counter::createPtr("proxy.process.http.cache_read_error");
  http_rsb.cache_read_errors                 = Metrics::Counter::createPtr("proxy.process.http.cache_read_errors");
  http_rsb.cache_updates                     = Metrics::Counter::createPtr("proxy.process.http.cache_updates");
  http_rsb.cache_write_errors                = Metrics::Counter::createPtr("proxy.process.http.cache_write_errors");
  http_rsb.cache_writes                      = Metrics::Counter::createPtr("proxy.process.http.cache_writes");
  http_rsb.completed_requests                = Metrics::Counter::createPtr("proxy.process.http.completed_requests");
  http_rsb.connect_requests                  = Metrics::Counter::createPtr("proxy.process.http.connect_requests");
  http_rsb.current_active_client_connections = Metrics::Gauge::createPtr("proxy.process.http.current_active_client_connections");
  http_rsb.current_cache_connections         = Metrics::Gauge::createPtr("proxy.process.http.current_cache_connections");
  http_rsb.current_client_connections        = Metrics::Gauge::createPtr("proxy.process.http.current_client_connections");
  http_rsb.current_client_transactions       = Metrics::Gauge::createPtr("proxy.process.http.current_client_transactions");
  http_rsb.current_parent_proxy_connections  = Metrics::Gauge::createPtr("proxy.process.http.current_parent_proxy_connections");
  http_rsb.current_server_connections        = Metrics::Gauge::createPtr("proxy.process.http.current_server_connections");
  http_rsb.current_server_transactions       = Metrics::Gauge::createPtr("proxy.process.http.current_server_transactions");
  http_rsb.delete_requests                   = Metrics::Counter::createPtr("proxy.process.http.delete_requests");
  http_rsb.disallowed_post_100_continue      = Metrics::Counter::createPtr("proxy.process.http.disallowed_post_100_continue");
  http_rsb.dns_lookup_begin_time             = Metrics::Counter::createPtr("proxy.process.http.milestone.dns_lookup_begin");
  http_rsb.dns_lookup_end_time               = Metrics::Counter::createPtr("proxy.process.http.milestone.dns_lookup_end");
  http_rsb.down_server_no_requests           = Metrics::Counter::createPtr("proxy.process.http.down_server.no_requests");
  http_rsb.err_client_abort_count            = Metrics::Counter::createPtr("proxy.process.http.err_client_abort_count");
  http_rsb.err_client_abort_origin_server_bytes =
    Metrics::Counter::createPtr("proxy.process.http.err_client_abort_origin_server_bytes");
  http_rsb.err_client_abort_user_agent_bytes = Metrics::Counter::createPtr("proxy.process.http.err_client_abort_user_agent_bytes");
  http_rsb.err_client_read_error_count       = Metrics::Counter::createPtr("proxy.process.http.err_client_read_error_count");
  http_rsb.err_client_read_error_origin_server_bytes =
    Metrics::Counter::createPtr("proxy.process.http.err_client_read_error_origin_server_bytes");
  http_rsb.err_client_read_error_user_agent_bytes =
    Metrics::Counter::createPtr("proxy.process.http.err_client_read_error_user_agent_bytes");
  http_rsb.err_connect_fail_count = Metrics::Counter::createPtr("proxy.process.http.err_connect_fail_count");
  http_rsb.err_connect_fail_origin_server_bytes =
    Metrics::Counter::createPtr("proxy.process.http.err_connect_fail_origin_server_bytes");
  http_rsb.err_connect_fail_user_agent_bytes = Metrics::Counter::createPtr("proxy.process.http.err_connect_fail_user_agent_bytes");
  http_rsb.extension_method_requests         = Metrics::Counter::createPtr("proxy.process.http.extension_method_requests");
  http_rsb.get_requests                      = Metrics::Counter::createPtr("proxy.process.http.get_requests");
  http_rsb.head_requests                     = Metrics::Counter::createPtr("proxy.process.http.head_requests");
  http_rsb.https_incoming_requests           = Metrics::Counter::createPtr("proxy.process.https.incoming_requests");
  http_rsb.https_total_client_connections    = Metrics::Counter::createPtr("proxy.process.https.total_client_connections");
  http_rsb.incoming_requests                 = Metrics::Counter::createPtr("proxy.process.http.incoming_requests");
  http_rsb.incoming_responses                = Metrics::Counter::createPtr("proxy.process.http.incoming_responses");
  http_rsb.client_request_at_headers_stripped =
    Metrics::Counter::createPtr("proxy.process.http.client_request_at_headers_stripped");
  http_rsb.origin_response_at_headers_stripped =
    Metrics::Counter::createPtr("proxy.process.http.origin_response_at_headers_stripped");
  http_rsb.invalid_client_requests      = Metrics::Counter::createPtr("proxy.process.http.invalid_client_requests");
  http_rsb.misc_count                   = Metrics::Counter::createPtr("proxy.process.http.misc_count");
  http_rsb.misc_origin_server_bytes     = Metrics::Counter::createPtr("proxy.process.http.http_misc_origin_server_bytes");
  http_rsb.misc_user_agent_bytes        = Metrics::Counter::createPtr("proxy.process.http.misc_user_agent_bytes");
  http_rsb.missing_host_hdr             = Metrics::Counter::createPtr("proxy.process.http.missing_host_hdr");
  http_rsb.no_remap_matched             = Metrics::Counter::createPtr("proxy.process.http.no_remap_matched");
  http_rsb.options_requests             = Metrics::Counter::createPtr("proxy.process.http.options_requests");
  http_rsb.origin_body                  = Metrics::Counter::createPtr("proxy.process.http.origin.body");
  http_rsb.origin_close_private         = Metrics::Counter::createPtr("proxy.process.http.origin.close_private");
  http_rsb.origin_connect_adjust_thread = Metrics::Counter::createPtr("proxy.process.http.origin.connect.adjust_thread");
  http_rsb.origin_connections_throttled = Metrics::Counter::createPtr("proxy.process.http.origin_connections_throttled_out");
  http_rsb.origin_make_new              = Metrics::Counter::createPtr("proxy.process.http.origin.make_new");
  http_rsb.origin_no_sharing            = Metrics::Counter::createPtr("proxy.process.http.origin.no_sharing");
  http_rsb.origin_not_found             = Metrics::Counter::createPtr("proxy.process.http.origin.not_found");
  http_rsb.origin_private               = Metrics::Counter::createPtr("proxy.process.http.origin.private");
  http_rsb.origin_raw                   = Metrics::Counter::createPtr("proxy.process.http.origin.raw");
  http_rsb.origin_reuse                 = Metrics::Counter::createPtr("proxy.process.http.origin.reuse");
  http_rsb.origin_reuse_fail            = Metrics::Counter::createPtr("proxy.process.http.origin.reuse_fail");
  http_rsb.origin_server_request_document_total_size =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_request_document_total_size");
  http_rsb.origin_server_request_header_total_size =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_request_header_total_size");
  http_rsb.origin_server_response_document_total_size =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_response_document_total_size");
  http_rsb.origin_server_response_header_total_size =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_response_header_total_size");
  http_rsb.origin_shutdown_cleanup_entry     = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.cleanup_entry");
  http_rsb.origin_shutdown_migration_failure = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.migration_failure");
  http_rsb.origin_shutdown_pool_lock_contention =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.pool_lock_contention");
  http_rsb.origin_shutdown_release_invalid_request =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.release_invalid_request");
  http_rsb.origin_shutdown_release_invalid_response =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.release_invalid_response");
  http_rsb.origin_shutdown_release_misc     = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.release_misc");
  http_rsb.origin_shutdown_release_modified = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.release_modified");
  http_rsb.origin_shutdown_release_no_keep_alive =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.release_no_keep_alive");
  http_rsb.origin_shutdown_release_no_server = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.release_no_server");
  http_rsb.origin_shutdown_release_no_sharing =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.release_no_sharing");
  http_rsb.origin_shutdown_tunnel_abort  = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_abort");
  http_rsb.origin_shutdown_tunnel_client = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_client");
  http_rsb.origin_shutdown_tunnel_server = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_server");
  http_rsb.origin_shutdown_tunnel_server_detach =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_server_detach");
  http_rsb.origin_shutdown_tunnel_server_eos = Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_server_eos");
  http_rsb.origin_shutdown_tunnel_server_no_keep_alive =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_server_no_keep_alive");
  http_rsb.origin_shutdown_tunnel_server_plugin_tunnel =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_server_plugin_tunnel");
  http_rsb.origin_shutdown_tunnel_transform_read =
    Metrics::Counter::createPtr("proxy.process.http.origin_shutdown.tunnel_transform_read");
  http_rsb.outgoing_requests                 = Metrics::Counter::createPtr("proxy.process.http.outgoing_requests");
  http_rsb.parent_count                      = Metrics::Counter::createPtr("proxy.process.http_parent_count");
  http_rsb.parent_proxy_request_total_bytes  = Metrics::Counter::createPtr("proxy.process.http.parent_proxy_request_total_bytes");
  http_rsb.parent_proxy_response_total_bytes = Metrics::Counter::createPtr("proxy.process.http.parent_proxy_response_total_bytes");
  http_rsb.parent_proxy_transaction_time     = Metrics::Counter::createPtr("proxy.process.http.parent_proxy_transaction_time");
  http_rsb.pooled_server_connections         = Metrics::Gauge::createPtr("proxy.process.http.pooled_server_connections");
  http_rsb.post_body_too_large               = Metrics::Counter::createPtr("proxy.process.http.post_body_too_large");
  http_rsb.post_requests                     = Metrics::Counter::createPtr("proxy.process.http.post_requests");
  http_rsb.proxy_loop_detected               = Metrics::Counter::createPtr("proxy.process.http.http_proxy_loop_detected");
  http_rsb.proxy_mh_loop_detected            = Metrics::Counter::createPtr("proxy.process.http.http_proxy_mh_loop_detected");
  http_rsb.purge_requests                    = Metrics::Counter::createPtr("proxy.process.http.purge_requests");
  http_rsb.push_requests                     = Metrics::Counter::createPtr("proxy.process.http.push_requests");
  http_rsb.pushed_document_total_size        = Metrics::Counter::createPtr("proxy.process.http.pushed_document_total_size");
  http_rsb.pushed_response_header_total_size = Metrics::Counter::createPtr("proxy.process.http.pushed_response_header_total_size");
  http_rsb.put_requests                      = Metrics::Counter::createPtr("proxy.process.http.put_requests");
  http_rsb.response_status_000_count         = Metrics::Counter::createPtr("proxy.process.http.000_responses");
  http_rsb.response_status_100_count         = Metrics::Counter::createPtr("proxy.process.http.100_responses");
  http_rsb.response_status_101_count         = Metrics::Counter::createPtr("proxy.process.http.101_responses");
  http_rsb.response_status_1xx_count         = Metrics::Counter::createPtr("proxy.process.http.1xx_responses");
  http_rsb.response_status_200_count         = Metrics::Counter::createPtr("proxy.process.http.200_responses");
  http_rsb.response_status_201_count         = Metrics::Counter::createPtr("proxy.process.http.201_responses");
  http_rsb.response_status_202_count         = Metrics::Counter::createPtr("proxy.process.http.202_responses");
  http_rsb.response_status_203_count         = Metrics::Counter::createPtr("proxy.process.http.203_responses");
  http_rsb.response_status_204_count         = Metrics::Counter::createPtr("proxy.process.http.204_responses");
  http_rsb.response_status_205_count         = Metrics::Counter::createPtr("proxy.process.http.205_responses");
  http_rsb.response_status_206_count         = Metrics::Counter::createPtr("proxy.process.http.206_responses");
  http_rsb.response_status_2xx_count         = Metrics::Counter::createPtr("proxy.process.http.2xx_responses");
  http_rsb.response_status_300_count         = Metrics::Counter::createPtr("proxy.process.http.300_responses");
  http_rsb.response_status_301_count         = Metrics::Counter::createPtr("proxy.process.http.301_responses");
  http_rsb.response_status_302_count         = Metrics::Counter::createPtr("proxy.process.http.302_responses");
  http_rsb.response_status_303_count         = Metrics::Counter::createPtr("proxy.process.http.303_responses");
  http_rsb.response_status_304_count         = Metrics::Counter::createPtr("proxy.process.http.304_responses");
  http_rsb.response_status_305_count         = Metrics::Counter::createPtr("proxy.process.http.305_responses");
  http_rsb.response_status_307_count         = Metrics::Counter::createPtr("proxy.process.http.307_responses");
  http_rsb.response_status_308_count         = Metrics::Counter::createPtr("proxy.process.http.308_responses");
  http_rsb.response_status_3xx_count         = Metrics::Counter::createPtr("proxy.process.http.3xx_responses");
  http_rsb.response_status_400_count         = Metrics::Counter::createPtr("proxy.process.http.400_responses");
  http_rsb.response_status_401_count         = Metrics::Counter::createPtr("proxy.process.http.401_responses");
  http_rsb.response_status_402_count         = Metrics::Counter::createPtr("proxy.process.http.402_responses");
  http_rsb.response_status_403_count         = Metrics::Counter::createPtr("proxy.process.http.403_responses");
  http_rsb.response_status_404_count         = Metrics::Counter::createPtr("proxy.process.http.404_responses");
  http_rsb.response_status_405_count         = Metrics::Counter::createPtr("proxy.process.http.405_responses");
  http_rsb.response_status_406_count         = Metrics::Counter::createPtr("proxy.process.http.406_responses");
  http_rsb.response_status_407_count         = Metrics::Counter::createPtr("proxy.process.http.407_responses");
  http_rsb.response_status_408_count         = Metrics::Counter::createPtr("proxy.process.http.408_responses");
  http_rsb.response_status_409_count         = Metrics::Counter::createPtr("proxy.process.http.409_responses");
  http_rsb.response_status_410_count         = Metrics::Counter::createPtr("proxy.process.http.410_responses");
  http_rsb.response_status_411_count         = Metrics::Counter::createPtr("proxy.process.http.411_responses");
  http_rsb.response_status_412_count         = Metrics::Counter::createPtr("proxy.process.http.412_responses");
  http_rsb.response_status_413_count         = Metrics::Counter::createPtr("proxy.process.http.413_responses");
  http_rsb.response_status_414_count         = Metrics::Counter::createPtr("proxy.process.http.414_responses");
  http_rsb.response_status_415_count         = Metrics::Counter::createPtr("proxy.process.http.415_responses");
  http_rsb.response_status_416_count         = Metrics::Counter::createPtr("proxy.process.http.416_responses");
  http_rsb.response_status_429_count         = Metrics::Counter::createPtr("proxy.process.http.429_responses");
  http_rsb.response_status_4xx_count         = Metrics::Counter::createPtr("proxy.process.http.4xx_responses");
  http_rsb.response_status_500_count         = Metrics::Counter::createPtr("proxy.process.http.500_responses");
  http_rsb.response_status_501_count         = Metrics::Counter::createPtr("proxy.process.http.501_responses");
  http_rsb.response_status_502_count         = Metrics::Counter::createPtr("proxy.process.http.502_responses");
  http_rsb.response_status_503_count         = Metrics::Counter::createPtr("proxy.process.http.503_responses");
  http_rsb.response_status_504_count         = Metrics::Counter::createPtr("proxy.process.http.504_responses");
  http_rsb.response_status_505_count         = Metrics::Counter::createPtr("proxy.process.http.505_responses");
  http_rsb.response_status_5xx_count         = Metrics::Counter::createPtr("proxy.process.http.5xx_responses");
  http_rsb.server_begin_write_time           = Metrics::Counter::createPtr("proxy.process.http.milestone.server_begin_write");
  http_rsb.server_close_time                 = Metrics::Counter::createPtr("proxy.process.http.milestone.server_close");
  http_rsb.server_connect_end_time           = Metrics::Counter::createPtr("proxy.process.http.milestone.server_connect_end");
  http_rsb.server_connect_time               = Metrics::Counter::createPtr("proxy.process.http.milestone.server_connect");
  http_rsb.server_first_connect_time         = Metrics::Counter::createPtr("proxy.process.http.milestone.server_first_connect");
  http_rsb.server_first_read_time            = Metrics::Counter::createPtr("proxy.process.http.milestone.server_first_read");
  http_rsb.server_read_header_done_time      = Metrics::Counter::createPtr("proxy.process.http.milestone.server_read_header_done");
  http_rsb.sm_finish_time                    = Metrics::Counter::createPtr("proxy.process.http.milestone.sm_finish");
  http_rsb.sm_start_time                     = Metrics::Counter::createPtr("proxy.process.http.milestone.sm_start");
  http_rsb.tcp_client_refresh_count          = Metrics::Counter::createPtr("proxy.process.http.tcp_client_refresh_count");
  http_rsb.tcp_client_refresh_origin_server_bytes =
    Metrics::Counter::createPtr("proxy.process.http.tcp_client_refresh_origin_server_bytes");
  http_rsb.tcp_client_refresh_user_agent_bytes =
    Metrics::Counter::createPtr("proxy.process.http.tcp_client_refresh_user_agent_bytes");
  http_rsb.tcp_expired_miss_count = Metrics::Counter::createPtr("proxy.process.http.tcp_expired_miss_count");
  http_rsb.tcp_expired_miss_origin_server_bytes =
    Metrics::Counter::createPtr("proxy.process.http.tcp_expired_miss_origin_server_bytes");
  http_rsb.tcp_expired_miss_user_agent_bytes = Metrics::Counter::createPtr("proxy.process.http.tcp_expired_miss_user_agent_bytes");
  http_rsb.tcp_hit_count                     = Metrics::Counter::createPtr("proxy.process.http.tcp_hit_count");
  http_rsb.tcp_hit_origin_server_bytes       = Metrics::Counter::createPtr("proxy.process.http.tcp_hit_origin_server_bytes");
  http_rsb.tcp_hit_user_agent_bytes          = Metrics::Counter::createPtr("proxy.process.http.tcp_hit_user_agent_bytes");
  http_rsb.tcp_ims_hit_count                 = Metrics::Counter::createPtr("proxy.process.http.tcp_ims_hit_count");
  http_rsb.tcp_ims_hit_origin_server_bytes   = Metrics::Counter::createPtr("proxy.process.http.tcp_ims_hit_origin_server_bytes");
  http_rsb.tcp_ims_hit_user_agent_bytes      = Metrics::Counter::createPtr("proxy.process.http.tcp_ims_hit_user_agent_bytes");
  http_rsb.tcp_ims_miss_count                = Metrics::Counter::createPtr("proxy.process.http.tcp_ims_miss_count");
  http_rsb.tcp_ims_miss_origin_server_bytes  = Metrics::Counter::createPtr("proxy.process.http.tcp_ims_miss_origin_server_bytes");
  http_rsb.tcp_ims_miss_user_agent_bytes     = Metrics::Counter::createPtr("proxy.process.http.tcp_ims_miss_user_agent_bytes");
  http_rsb.tcp_miss_count                    = Metrics::Counter::createPtr("proxy.process.http.tcp_miss_count");
  http_rsb.tcp_miss_origin_server_bytes      = Metrics::Counter::createPtr("proxy.process.http.tcp_miss_origin_server_bytes");
  http_rsb.tcp_miss_user_agent_bytes         = Metrics::Counter::createPtr("proxy.process.http.tcp_miss_user_agent_bytes");
  http_rsb.tcp_refresh_hit_count             = Metrics::Counter::createPtr("proxy.process.http.tcp_refresh_hit_count");
  http_rsb.tcp_refresh_hit_origin_server_bytes =
    Metrics::Counter::createPtr("proxy.process.http.tcp_refresh_hit_origin_server_bytes");
  http_rsb.tcp_refresh_hit_user_agent_bytes = Metrics::Counter::createPtr("proxy.process.http.tcp_refresh_hit_user_agent_bytes");
  http_rsb.tcp_refresh_miss_count           = Metrics::Counter::createPtr("proxy.process.http.tcp_refresh_miss_count");
  http_rsb.tcp_refresh_miss_origin_server_bytes =
    Metrics::Counter::createPtr("proxy.process.http.tcp_refresh_miss_origin_server_bytes");
  http_rsb.tcp_refresh_miss_user_agent_bytes = Metrics::Counter::createPtr("proxy.process.http.tcp_refresh_miss_user_agent_bytes");
  http_rsb.total_client_connections          = Metrics::Counter::createPtr("proxy.process.http.total_client_connections");
  http_rsb.total_client_connections_ipv4     = Metrics::Counter::createPtr("proxy.process.http.total_client_connections_ipv4");
  http_rsb.total_client_connections_ipv6     = Metrics::Counter::createPtr("proxy.process.http.total_client_connections_ipv6");
  http_rsb.total_client_connections_uds      = Metrics::Counter::createPtr("proxy.process.http.total_client_connections_uds");
  http_rsb.total_incoming_connections        = Metrics::Counter::createPtr("proxy.process.http.total_incoming_connections");
  http_rsb.total_parent_marked_down_count    = Metrics::Counter::createPtr("proxy.process.http.total_parent_marked_down_count");
  http_rsb.total_parent_marked_down_timeout  = Metrics::Counter::createPtr("proxy.process.http.total_parent_marked_down_timeout");
  http_rsb.total_parent_proxy_connections    = Metrics::Counter::createPtr("proxy.process.http.total_parent_proxy_connections");
  http_rsb.total_parent_retries              = Metrics::Counter::createPtr("proxy.process.http.total_parent_retries");
  http_rsb.total_parent_retries_exhausted    = Metrics::Counter::createPtr("proxy.process.http.total_parent_retries_exhausted");
  http_rsb.total_parent_switches             = Metrics::Counter::createPtr("proxy.process.http.total_parent_switches");
  http_rsb.total_server_connections          = Metrics::Counter::createPtr("proxy.process.http.total_server_connections");
  http_rsb.total_transactions_time           = Metrics::Counter::createPtr("proxy.process.http.total_transactions_time");
  http_rsb.total_x_redirect                  = Metrics::Counter::createPtr("proxy.process.http.total_x_redirect_count");
  http_rsb.trace_requests                    = Metrics::Counter::createPtr("proxy.process.http.trace_requests");
  http_rsb.tunnel_chunked_throttle           = Metrics::Counter::createPtr("proxy.process.http.tunnel.chunked_throttle");
  http_rsb.tunnel_current_active_connections = Metrics::Gauge::createPtr("proxy.process.tunnel.current_active_connections");
  http_rsb.tunnels                           = Metrics::Counter::createPtr("proxy.process.http.tunnels");
  http_rsb.ua_begin_time                     = Metrics::Counter::createPtr("proxy.process.http.milestone.ua_begin");
  http_rsb.ua_begin_write_time               = Metrics::Counter::createPtr("proxy.process.http.milestone.ua_begin_write");
  http_rsb.ua_close_time                     = Metrics::Counter::createPtr("proxy.process.http.milestone.ua_close");
  http_rsb.ua_counts_errors_aborts           = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.errors.aborts");
  http_rsb.ua_counts_errors_connect_failed =
    Metrics::Counter::createPtr("proxy.process.http.transaction_counts.errors.connect_failed");
  http_rsb.ua_counts_errors_other = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.errors.other");
  http_rsb.ua_counts_errors_possible_aborts =
    Metrics::Counter::createPtr("proxy.process.http.transaction_counts.errors.possible_aborts");
  http_rsb.ua_counts_errors_pre_accept_hangups =
    Metrics::Counter::createPtr("proxy.process.http.transaction_counts.errors.pre_accept_hangups");
  http_rsb.ua_counts_hit_fresh         = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.hit_fresh");
  http_rsb.ua_counts_hit_fresh_process = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.hit_fresh.process");
  http_rsb.ua_counts_hit_reval         = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.hit_revalidated");
  http_rsb.ua_counts_miss_changed      = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.miss_changed");
  http_rsb.ua_counts_miss_client_no_cache =
    Metrics::Counter::createPtr("proxy.process.http.transaction_counts.miss_client_no_cache");
  http_rsb.ua_counts_miss_cold          = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.miss_cold");
  http_rsb.ua_counts_miss_uncacheable   = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.miss_not_cacheable");
  http_rsb.ua_counts_other_unclassified = Metrics::Counter::createPtr("proxy.process.http.transaction_counts.other.unclassified");
  http_rsb.ua_first_read_time           = Metrics::Counter::createPtr("proxy.process.http.milestone.ua_first_read");
  http_rsb.ua_msecs_errors_aborts       = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.errors.aborts");
  http_rsb.ua_msecs_errors_connect_failed =
    Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.errors.connect_failed");
  http_rsb.ua_msecs_errors_other = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.errors.other");
  http_rsb.ua_msecs_errors_possible_aborts =
    Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.errors.possible_aborts");
  http_rsb.ua_msecs_errors_pre_accept_hangups =
    Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.errors.pre_accept_hangups");
  http_rsb.ua_msecs_hit_fresh         = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.hit_fresh");
  http_rsb.ua_msecs_hit_fresh_process = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.hit_fresh.process");
  http_rsb.ua_msecs_hit_reval         = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.hit_revalidated");
  http_rsb.ua_msecs_miss_changed      = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.miss_changed");
  http_rsb.ua_msecs_miss_client_no_cache =
    Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.miss_client_no_cache");
  http_rsb.ua_msecs_miss_cold          = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.miss_cold");
  http_rsb.ua_msecs_miss_uncacheable   = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.miss_not_cacheable");
  http_rsb.ua_msecs_other_unclassified = Metrics::Counter::createPtr("proxy.process.http.transaction_totaltime.other.unclassified");
  http_rsb.ua_read_header_done_time    = Metrics::Counter::createPtr("proxy.process.http.milestone.ua_read_header_done");
  http_rsb.user_agent_request_document_total_size =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_request_document_total_size");
  http_rsb.user_agent_request_header_total_size =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_request_header_total_size");
  http_rsb.user_agent_response_document_total_size =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_response_document_total_size");
  http_rsb.user_agent_response_header_total_size =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_response_header_total_size");
  http_rsb.websocket_current_active_client_connections =
    Metrics::Gauge::createPtr("proxy.process.http.websocket.current_active_client_connections");

  // Speed bucket stats for client and origin
  http_rsb.user_agent_speed_bytes_per_sec_100 =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_100");
  http_rsb.user_agent_speed_bytes_per_sec_1k = Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_1K");
  http_rsb.user_agent_speed_bytes_per_sec_10k =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_10K");
  http_rsb.user_agent_speed_bytes_per_sec_100k =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_100K");
  http_rsb.user_agent_speed_bytes_per_sec_1M = Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_1M");
  http_rsb.user_agent_speed_bytes_per_sec_10M =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_10M");
  http_rsb.user_agent_speed_bytes_per_sec_100M =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_100M");
  http_rsb.user_agent_speed_bytes_per_sec_200M =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_200M");
  http_rsb.user_agent_speed_bytes_per_sec_400M =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_400M");
  http_rsb.user_agent_speed_bytes_per_sec_800M =
    Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_800M");
  http_rsb.user_agent_speed_bytes_per_sec_1G = Metrics::Counter::createPtr("proxy.process.http.user_agent_speed_bytes_per_sec_1G");
  http_rsb.origin_server_speed_bytes_per_sec_100 =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_100");
  http_rsb.origin_server_speed_bytes_per_sec_1k =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_1K");
  http_rsb.origin_server_speed_bytes_per_sec_10k =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_10K");
  http_rsb.origin_server_speed_bytes_per_sec_100k =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_100K");
  http_rsb.origin_server_speed_bytes_per_sec_1M =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_1M");
  http_rsb.origin_server_speed_bytes_per_sec_10M =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_10M");
  http_rsb.origin_server_speed_bytes_per_sec_100M =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_100M");
  http_rsb.origin_server_speed_bytes_per_sec_200M =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_200M");
  http_rsb.origin_server_speed_bytes_per_sec_400M =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_400M");
  http_rsb.origin_server_speed_bytes_per_sec_800M =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_800M");
  http_rsb.origin_server_speed_bytes_per_sec_1G =
    Metrics::Counter::createPtr("proxy.process.http.origin_server_speed_bytes_per_sec_1G");
  http_rsb.cache_compat_key_reads = Metrics::Counter::createPtr("proxy.process.http.cache.compat_key_reads");

  Metrics::Derived::derive({
    // Total bytes of client request body + headers
    {"proxy.process.http.user_agent_total_request_bytes",
     Metrics::MetricType::COUNTER,
     {http_rsb.user_agent_request_document_total_size, http_rsb.user_agent_request_header_total_size}                     },
    // Total bytes of client response body + headers
    {"proxy.process.http.user_agent_total_response_bytes",
     Metrics::MetricType::COUNTER,
     {http_rsb.user_agent_response_document_total_size, http_rsb.user_agent_response_header_total_size}                   },
    // Total bytes of origin server request body + headers
    {"proxy.process.http.origin_server_total_request_bytes",
     Metrics::MetricType::COUNTER,
     {http_rsb.origin_server_request_document_total_size, http_rsb.origin_server_request_header_total_size}               },
    // Total bytes of origin server response body + headers
    {"proxy.process.http.origin_server_total_response_bytes",
     Metrics::MetricType::COUNTER,
     {http_rsb.origin_server_response_document_total_size, http_rsb.origin_server_response_header_total_size}             },
    // Total bytes of client request and response (total traffic to and from clients)
    {"proxy.process.user_agent_total_bytes",
     Metrics::MetricType::COUNTER,
     {"proxy.process.http.user_agent_total_request_bytes", "proxy.process.http.user_agent_total_response_bytes"}          },
    // Total bytes of origin/parent request and response
    {"proxy.process.origin_server_total_bytes",
     Metrics::MetricType::COUNTER,
     {"proxy.process.http.origin_server_total_request_bytes", "proxy.process.http.origin_server_total_response_bytes",
      http_rsb.parent_proxy_request_total_bytes, http_rsb.parent_proxy_response_total_bytes}                              },
    // Total requests which are cache hits
    {"proxy.process.cache_total_hits",
     Metrics::MetricType::COUNTER,
     {http_rsb.cache_hit_fresh, http_rsb.cache_hit_reval, http_rsb.cache_hit_ims, http_rsb.cache_hit_stale_served}        },
    // Total requests which are cache misses
    {"proxy.process.cache_total_misses",
     Metrics::MetricType::COUNTER,
     {http_rsb.cache_miss_cold, http_rsb.cache_miss_changed, http_rsb.cache_miss_client_no_cache, http_rsb.cache_miss_ims,
      http_rsb.cache_miss_uncacheable}                                                                                    },
    // Total of all server connections (sum of origins and parent connections)
    {"proxy.process.current_server_connections",
     Metrics::MetricType::GAUGE,
     {http_rsb.current_server_connections, http_rsb.current_parent_proxy_connections}                                     },
    // Total requests, both hits and misses (this is slightly superfluous, but assures correct percentage calculations)
    {"proxy.process.cache_total_requests",
     Metrics::MetricType::COUNTER,
     {"proxy.process.cache_total_hits", "proxy.process.cache_total_misses"}                                               },
    // Total cache requests bytes which are cache hits
    {"proxy.process.cache_total_hits_bytes",
     Metrics::MetricType::COUNTER,
     {http_rsb.tcp_hit_user_agent_bytes, http_rsb.tcp_refresh_hit_user_agent_bytes, http_rsb.tcp_ims_hit_user_agent_bytes}},
    // Total cache requests bytes which are cache misses
    {"proxy.process.cache_total_misses_bytes",
     Metrics::MetricType::COUNTER,
     {http_rsb.tcp_miss_user_agent_bytes, http_rsb.tcp_expired_miss_user_agent_bytes, http_rsb.tcp_refresh_miss_user_agent_bytes,
      http_rsb.tcp_ims_miss_user_agent_bytes}                                                                             },
    // Total request bytes, both hits and misses
    {"proxy.process.cache_total_bytes",
     Metrics::MetricType::COUNTER,
     {"proxy.process.cache_total_hits_bytes", "proxy.process.cache_total_misses_bytes"}                                   }
  });
}

/**
  Parse list of HTTP status code and return HttpStatusBitset
  - e.g. "204 305 403 404 414 500 501 502 503 504"
 */
static void
parse_http_status_code_list(HttpStatusBitset &set, swoc::TextView status_list)
{
  // Clear target set first
  set.reset();

  // Trim quotes or double quotes if there
  status_list = status_list.trim('"').trim('\'');

  auto is_sep{[](char c) { return isspace(c) || ',' == c || ';' == c; }};

  while (!status_list.ltrim_if(is_sep).empty()) {
    swoc::TextView span;
    swoc::TextView token{status_list.take_prefix_if(is_sep)};
    auto           n = swoc::svtoi(token, &span);
    if (span.size() != token.size()) {
      Error("Invalid status code '%.*s': not a number", static_cast<int>(token.size()), token.data());
    } else if (n <= 0 || n >= HTTP_STATUS_NUMBER) {
      Error("Invalid status code '%.*s': out of range", static_cast<int>(token.size()), token.data());
    } else {
      set[n] = true;
    }
  }
}

// clang-format off
// TODO: find good clang-format setting
const MgmtConverter HttpStatusCodeList::Conv{
  [](const void *data) -> std::string_view {
    const HttpStatusCodeList *list = static_cast<const HttpStatusCodeList *>(data);
    return list->conf_value;
  },
  [](void *data, std::string_view src) -> void {
    HttpStatusCodeList *list = static_cast<HttpStatusCodeList *>(data);
    parse_http_status_code_list(list->_data, src);
  }};
// clang-format on

/////////////////////////////////////////////////////////////
//
// TargetedCacheControlHeaders implementation
//
/////////////////////////////////////////////////////////////

void
TargetedCacheControlHeaders::parse(std::string_view src)
{
  size_t dropped = 0;

  count = 0;
  swoc::TextView config_view{src};

  while (config_view) {
    swoc::TextView header_name = config_view.take_prefix_at(',').trim_if(&isspace);
    if (!header_name.empty()) {
      if (count < MAX_HEADERS) {
        headers[count++] = std::string_view{header_name.data(), header_name.size()};
      } else {
        ++dropped;
      }
    }
  }

  if (dropped > 0) {
    Warning("Ignoring %zu headers for proxy.config.http.cache.targeted_cache_control_headers (maximum is %zu).", dropped,
            MAX_HEADERS);
  }
}

// clang-format off
const MgmtConverter TargetedCacheControlHeaders::Conv{
  [](const void *data) -> std::string_view {
    const TargetedCacheControlHeaders *hdrs = static_cast<const TargetedCacheControlHeaders *>(data);
    return hdrs->conf_value ? hdrs->conf_value : "";
  },
  [](void *data, std::string_view src) -> void {
    TargetedCacheControlHeaders *hdrs = static_cast<TargetedCacheControlHeaders *>(data);
    // Keep conf_value and parse source consistent; headers[] points into src.
    // The caller is responsible for src lifetime for as long as this object is used.
    hdrs->conf_value = const_cast<char *>(src.data());
    hdrs->parse(src);
  }};
// clang-format on

/////////////////////////////////////////////////////////////
//
// ParsedConfigCache implementation
//
/////////////////////////////////////////////////////////////

ParsedConfigCache &
ParsedConfigCache::instance()
{
  static ParsedConfigCache inst;
  return inst;
}

const ParsedConfigCache::ParsedValue &
ParsedConfigCache::lookup(TSOverridableConfigKey key, std::string_view value)
{
  return instance().lookup_impl(key, value);
}

const ParsedConfigCache::ParsedValue &
ParsedConfigCache::lookup_impl(TSOverridableConfigKey key, std::string_view value)
{
  auto cache_key = std::make_pair(key, std::string(value));

  // Fast path: check cache under read lock.
  {
    ts::bravo::shared_lock lock(_mutex);
    // coverity[missing_lock] - ts::bravo::shared_lock properly holds the mutex
    auto it = _cache.find(cache_key);
    if (it != _cache.end()) {
      return it->second;
    }
  }

  // Slow path: parse and insert under write lock.
  std::unique_lock lock(_mutex);

  // Double-check after acquiring write lock.
  auto it = _cache.find(cache_key);
  if (it != _cache.end()) {
    return it->second;
  }

  // Parse and insert.
  auto [inserted_it, success] = _cache.try_emplace(cache_key);
  parse_into(inserted_it->second, key, value);
  return inserted_it->second;
}

void
ParsedConfigCache::parse_into(ParsedValue &result, TSOverridableConfigKey key, std::string_view value)
{
  // Store the string value - the parsed structures may reference this.
  result.conf_value_storage = std::string(value);

  switch (key) {
  case TS_CONFIG_HTTP_HOST_RESOLUTION_PREFERENCE: {
    HostResData host_res_data{};
    parse_host_res_preference(result.conf_value_storage.c_str(), host_res_data.order);
    host_res_data.conf_value = result.conf_value_storage.data();
    result.parsed            = host_res_data;
    break;
  }

  case TS_CONFIG_HTTP_NEGATIVE_CACHING_LIST:
  case TS_CONFIG_HTTP_NEGATIVE_REVALIDATING_LIST: {
    HttpStatusCodeList status_code_list{};
    status_code_list.conf_value = result.conf_value_storage.data();
    HttpStatusCodeList::Conv.store_string(&status_code_list, result.conf_value_storage);
    result.parsed = status_code_list;
    break;
  }

  case TS_CONFIG_HTTP_INSERT_FORWARDED: {
    swoc::LocalBufferWriter<1024> error;
    result.parsed = HttpForwarded::optStrToBitset(result.conf_value_storage, error);
    if (error.size()) {
      Error("HTTP %.*s", static_cast<int>(error.size()), error.data());
    }
    break;
  }

  case TS_CONFIG_HTTP_SERVER_SESSION_SHARING_MATCH: {
    MgmtByte server_session_sharing_match{0};
    HttpConfig::load_server_session_sharing_match(result.conf_value_storage, server_session_sharing_match);
    result.parsed = server_session_sharing_match;
    break;
  }

  case TS_CONFIG_HTTP_CACHE_TARGETED_CACHE_CONTROL_HEADERS: {
    TargetedCacheControlHeaders targeted_headers{};
    TargetedCacheControlHeaders::Conv.store_string(&targeted_headers, result.conf_value_storage);
    result.parsed = targeted_headers;
    break;
  }

  default:
    // No special parsing needed for this config.
    break;
  }
}

/** Template for creating conversions and initialization for @c std::chrono based configuration variables.
 *
 * @tparam V The exact type of the configuration variable.
 *
 * The tricky template code is to enable having a class instance for each configuration variable, instead of for each _type_ of
 * configuration variable. This is required because the callback interface requires functions and so the actual storage must be
 * accessible from that function. *
 */
template <typename V> struct ConfigDuration {
  using self_type = ConfigDuration;
  V *_var; ///< Pointer to the variable to control.

  /** Constructor.
   *
   * @param v The variable to update.
   */
  ConfigDuration(V &v) : _var(&v) {}

  /// Convert to the mgmt (configuration) type.
  static MgmtInt
  to_mgmt(void const *data)
  {
    return static_cast<MgmtInt>(static_cast<V const *>(data)->count());
  }

  /// Convert from the mgmt (configuration) type.
  static void
  from_mgmt(void *data, MgmtInt i)
  {
    *static_cast<V *>(data) = V{i};
  }

  /// The conversion structure, which handles @c MgmtInt.
  static inline const MgmtConverter Conversions{&to_mgmt, &from_mgmt};

  /** Process start up conversion from configuration.
   *
   * @param type The data type in the configuration.
   * @param data The data in the configuration.
   * @param var Pointer to the variable to update.
   * @return @c true if @a data was successfully converted and stored, @c false if not.
   *
   * @note @a var is the target variable because it was explicitly set to be the value of @a _var in @c Enable.
   */
  static bool
  callback(char const *, RecDataT type, RecData data, void *var)
  {
    if (RECD_INT == type) {
      (*self_type::Conversions.store_int)(var, data.rec_int);
      return true;
    }
    return false;
  }

  /** Enable.
   *
   * @param name Name of the configuration variable.
   *
   * This enables both reading from the configuration and handling the callback for dynamic
   * updates of the variable.
   */
  void
  Enable(std::string_view name)
  {
    Enable_Config_Var(name, &self_type::callback, http_config_cb, _var);
  }
};

ConfigDuration HttpDownServerCacheTimeVar{HttpConfig::m_master.oride.down_server_timeout};
// Make the conversions visible to the plugin API. This allows exporting just the conversions
// without having to export the class definition. Again, the compiler doesn't allow doing this
// in one line.
extern MgmtConverter const &HttpDownServerCacheTimeConv;
MgmtConverter const        &HttpDownServerCacheTimeConv = HttpDownServerCacheTimeVar.Conversions;

////////////////////////////////////////////////////////////////
//
//  HttpConfig::startup()
//
////////////////////////////////////////////////////////////////
HttpStatsBlock http_rsb;

void
HttpConfig::startup()
{
  extern void SSLConfigInit(swoc::IPRangeSet * addrs);
  register_stat_callbacks();

  HttpConfigParams &c = m_master;

  http_config_cont = new HttpConfigCont;

  HttpEstablishStaticConfigStringAlloc(c.proxy_hostname, "proxy.config.proxy_name");
  c.proxy_hostname_len = -1;

  if (c.proxy_hostname == nullptr) {
    c.proxy_hostname    = static_cast<char *>(ats_malloc(sizeof(char)));
    c.proxy_hostname[0] = '\0';
  }

  c.inbound  += RecHttpLoadIp("proxy.config.incoming_ip_to_bind");
  c.outbound += RecHttpLoadIp("proxy.config.outgoing_ip_to_bind");
  RecHttpLoadIpAddrsFromConfVar("proxy.config.http.proxy_protocol_allowlist", c.config_proxy_protocol_ip_addrs);
  SSLConfigInit(&c.config_proxy_protocol_ip_addrs);

  HttpEstablishStaticConfigLongLong(c.server_max_connections, "proxy.config.http.server_max_connections");
  HttpEstablishStaticConfigLongLong(c.max_websocket_connections, "proxy.config.http.websocket.max_number_of_connections");
  HttpEstablishStaticConfigByte(c.oride.attach_server_session_to_client, "proxy.config.http.attach_server_session_to_client");
  HttpEstablishStaticConfigLongLong(c.oride.max_proxy_cycles, "proxy.config.http.max_proxy_cycles");

  HttpEstablishStaticConfigLongLong(c.oride.tunnel_activity_check_period, "proxy.config.tunnel.activity_check_period");

  HttpEstablishStaticConfigLongLong(c.oride.default_inactivity_timeout, "proxy.config.net.default_inactivity_timeout");

  HttpEstablishStaticConfigLongLong(c.http_request_line_max_size, "proxy.config.http.request_line_max_size");
  HttpEstablishStaticConfigLongLong(c.http_hdr_field_max_size, "proxy.config.http.header_field_max_size");
  HttpEstablishStaticConfigLongLong(c.pp_hdr_max_size, "proxy.config.proxy_protocol.max_header_size");

  HttpEstablishStaticConfigByte(c.disable_ssl_parenting, "proxy.config.http.parent_proxy.disable_connect_tunneling");
  HttpEstablishStaticConfigByte(c.oride.forward_connect_method, "proxy.config.http.forward_connect_method");

  HttpEstablishStaticConfigByte(c.oride.no_dns_forward_to_parent, "proxy.config.http.no_dns_just_forward_to_parent");
  HttpEstablishStaticConfigByte(c.oride.uncacheable_requests_bypass_parent, "proxy.config.http.uncacheable_requests_bypass_parent");
  HttpEstablishStaticConfigByte(c.oride.doc_in_cache_skip_dns, "proxy.config.http.doc_in_cache_skip_dns");

  HttpEstablishStaticConfigByte(c.no_origin_server_dns, "proxy.config.http.no_origin_server_dns");
  HttpEstablishStaticConfigByte(c.use_client_target_addr, "proxy.config.http.use_client_target_addr");
  HttpEstablishStaticConfigByte(c.use_client_source_port, "proxy.config.http.use_client_source_port");
  HttpEstablishStaticConfigByte(c.oride.maintain_pristine_host_hdr, "proxy.config.url_remap.pristine_host_hdr");

  HttpEstablishStaticConfigByte(c.oride.insert_request_via_string, "proxy.config.http.insert_request_via_str");
  HttpEstablishStaticConfigByte(c.oride.insert_response_via_string, "proxy.config.http.insert_response_via_str");
  HttpEstablishStaticConfigLongLong(c.oride.proxy_response_hsts_max_age, "proxy.config.ssl.hsts_max_age");
  HttpEstablishStaticConfigByte(c.oride.proxy_response_hsts_include_subdomains, "proxy.config.ssl.hsts_include_subdomains");

  HttpEstablishStaticConfigStringAlloc(c.proxy_request_via_string, "proxy.config.http.request_via_str");
  c.proxy_request_via_string_len = -1;
  HttpEstablishStaticConfigStringAlloc(c.proxy_response_via_string, "proxy.config.http.response_via_str");
  c.proxy_response_via_string_len = -1;

  HttpEstablishStaticConfigByte(c.oride.keep_alive_enabled_in, "proxy.config.http.keep_alive_enabled_in");
  HttpEstablishStaticConfigByte(c.oride.keep_alive_enabled_out, "proxy.config.http.keep_alive_enabled_out");
  HttpEstablishStaticConfigByte(c.oride.chunking_enabled, "proxy.config.http.chunking_enabled");
  HttpEstablishStaticConfigLongLong(c.oride.http_chunking_size, "proxy.config.http.chunking.size");
  HttpEstablishStaticConfigByte(c.oride.http_drop_chunked_trailers, "proxy.config.http.drop_chunked_trailers");
  HttpEstablishStaticConfigByte(c.oride.http_strict_chunk_parsing, "proxy.config.http.strict_chunk_parsing");
  HttpEstablishStaticConfigByte(c.oride.flow_control_enabled, "proxy.config.http.flow_control.enabled");
  HttpEstablishStaticConfigLongLong(c.oride.flow_high_water_mark, "proxy.config.http.flow_control.high_water");
  HttpEstablishStaticConfigLongLong(c.oride.flow_low_water_mark, "proxy.config.http.flow_control.low_water");
  HttpEstablishStaticConfigByte(c.oride.post_check_content_length_enabled, "proxy.config.http.post.check.content_length.enabled");
  HttpEstablishStaticConfigByte(c.oride.cache_post_method, "proxy.config.http.cache.post_method");
  HttpEstablishStaticConfigByte(c.oride.request_buffer_enabled, "proxy.config.http.request_buffer_enabled");
  HttpEstablishStaticConfigByte(c.strict_uri_parsing, "proxy.config.http.strict_uri_parsing");

  // [amc] This is a bit of a mess, need to figure out to make this cleaner.
  RecRegisterConfigUpdateCb("proxy.config.http.server_session_sharing.match", &http_server_session_sharing_cb, &c);
  http_config_enum_mask_read("proxy.config.http.server_session_sharing.match", c.oride.server_session_sharing_match);
  HttpEstablishStaticConfigStringAlloc(c.oride.server_session_sharing_match_str, "proxy.config.http.server_session_sharing.match");
  http_config_enum_read("proxy.config.http.server_session_sharing.pool", SessionSharingPoolStrings, c.server_session_sharing_pool);
  httpSessionManager.set_pool_type(c.server_session_sharing_pool);

  RecRegisterConfigUpdateCb("proxy.config.http.insert_forwarded", &http_insert_forwarded_cb, &c);
  {
    char str[512];

    if (auto sv{RecGetRecordString("proxy.config.http.insert_forwarded", str, sizeof(str))}; sv) {
      swoc::LocalBufferWriter<1024> error;
      HttpForwarded::OptionBitSet   bs = HttpForwarded::optStrToBitset(sv.value(), error);
      if (!error.size()) {
        c.oride.insert_forwarded = bs;
      } else {
        Error("HTTP %.*s", static_cast<int>(error.size()), error.data());
      }
    }
  }

  HttpEstablishStaticConfigByte(c.oride.auth_server_session_private, "proxy.config.http.auth_server_session_private");

  HttpEstablishStaticConfigByte(c.oride.keep_alive_post_out, "proxy.config.http.keep_alive_post_out");

  HttpEstablishStaticConfigLongLong(c.oride.keep_alive_no_activity_timeout_in,
                                    "proxy.config.http.keep_alive_no_activity_timeout_in");
  HttpEstablishStaticConfigLongLong(c.oride.keep_alive_no_activity_timeout_out,
                                    "proxy.config.http.keep_alive_no_activity_timeout_out");
  HttpEstablishStaticConfigLongLong(c.oride.transaction_no_activity_timeout_in,
                                    "proxy.config.http.transaction_no_activity_timeout_in");
  HttpEstablishStaticConfigLongLong(c.oride.transaction_no_activity_timeout_out,
                                    "proxy.config.http.transaction_no_activity_timeout_out");
  HttpEstablishStaticConfigLongLong(c.oride.websocket_active_timeout, "proxy.config.websocket.active_timeout");
  HttpEstablishStaticConfigLongLong(c.oride.websocket_inactive_timeout, "proxy.config.websocket.no_activity_timeout");

  HttpEstablishStaticConfigLongLong(c.oride.transaction_active_timeout_in, "proxy.config.http.transaction_active_timeout_in");
  HttpEstablishStaticConfigLongLong(c.oride.transaction_active_timeout_out, "proxy.config.http.transaction_active_timeout_out");
  HttpEstablishStaticConfigLongLong(c.accept_no_activity_timeout, "proxy.config.http.accept_no_activity_timeout");

  HttpEstablishStaticConfigLongLong(c.oride.background_fill_active_timeout, "proxy.config.http.background_fill_active_timeout");
  HttpEstablishStaticConfigFloat(c.oride.background_fill_threshold, "proxy.config.http.background_fill_completed_threshold");

  HttpEstablishStaticConfigLongLong(c.oride.connect_attempts_max_retries, "proxy.config.http.connect_attempts_max_retries");
  HttpEstablishStaticConfigLongLong(c.oride.connect_attempts_max_retries_suspect_server,
                                    "proxy.config.http.connect_attempts_max_retries_suspect_server");
  HttpEstablishStaticConfigLongLong(c.oride.connect_attempts_retry_backoff_base,
                                    "proxy.config.http.connect_attempts_retry_backoff_base");

  HttpEstablishStaticConfigLongLong(c.oride.connect_down_policy, "proxy.config.http.connect.down.policy");

  HttpEstablishStaticConfigLongLong(c.oride.connect_attempts_rr_retries, "proxy.config.http.connect_attempts_rr_retries");
  HttpEstablishStaticConfigLongLong(c.oride.connect_attempts_timeout, "proxy.config.http.connect_attempts_timeout");
  HttpEstablishStaticConfigLongLong(c.oride.parent_connect_attempts, "proxy.config.http.parent_proxy.total_connect_attempts");
  HttpEstablishStaticConfigLongLong(c.oride.parent_retry_time, "proxy.config.http.parent_proxy.retry_time");
  HttpEstablishStaticConfigLongLong(c.oride.parent_fail_threshold, "proxy.config.http.parent_proxy.fail_threshold");
  HttpEstablishStaticConfigLongLong(c.oride.per_parent_connect_attempts,
                                    "proxy.config.http.parent_proxy.per_parent_connect_attempts");
  HttpEstablishStaticConfigByte(c.oride.parent_failures_update_hostdb, "proxy.config.http.parent_proxy.mark_down_hostdb");
  HttpEstablishStaticConfigByte(c.oride.enable_parent_timeout_markdowns,
                                "proxy.config.http.parent_proxy.enable_parent_timeout_markdowns");
  HttpEstablishStaticConfigByte(c.oride.disable_parent_markdowns, "proxy.config.http.parent_proxy.disable_parent_markdowns");

  HttpEstablishStaticConfigLongLong(c.oride.sock_recv_buffer_size_out, "proxy.config.net.sock_recv_buffer_size_out");
  HttpEstablishStaticConfigLongLong(c.oride.sock_send_buffer_size_out, "proxy.config.net.sock_send_buffer_size_out");
  HttpEstablishStaticConfigLongLong(c.oride.sock_option_flag_out, "proxy.config.net.sock_option_flag_out");
  HttpEstablishStaticConfigLongLong(c.oride.sock_packet_mark_out, "proxy.config.net.sock_packet_mark_out");
  HttpEstablishStaticConfigLongLong(c.oride.sock_packet_tos_out, "proxy.config.net.sock_packet_tos_out");
  HttpEstablishStaticConfigLongLong(c.oride.sock_packet_notsent_lowat, "proxy.config.net.sock_notsent_lowat");

  HttpEstablishStaticConfigByte(c.oride.fwd_proxy_auth_to_parent, "proxy.config.http.forward.proxy_auth_to_parent");

  HttpEstablishStaticConfigByte(c.oride.anonymize_remove_from, "proxy.config.http.anonymize_remove_from");
  HttpEstablishStaticConfigByte(c.oride.anonymize_remove_referer, "proxy.config.http.anonymize_remove_referer");
  HttpEstablishStaticConfigByte(c.oride.anonymize_remove_user_agent, "proxy.config.http.anonymize_remove_user_agent");
  HttpEstablishStaticConfigByte(c.oride.anonymize_remove_cookie, "proxy.config.http.anonymize_remove_cookie");
  HttpEstablishStaticConfigByte(c.oride.anonymize_remove_client_ip, "proxy.config.http.anonymize_remove_client_ip");
  HttpEstablishStaticConfigByte(c.oride.anonymize_insert_client_ip, "proxy.config.http.insert_client_ip");
  HttpEstablishStaticConfigStringAlloc(c.anonymize_other_header_list, "proxy.config.http.anonymize_other_header_list");

  HttpEstablishStaticConfigStringAlloc(c.oride.global_user_agent_header, "proxy.config.http.global_user_agent_header");
  c.oride.global_user_agent_header_size = c.oride.global_user_agent_header ? strlen(c.oride.global_user_agent_header) : 0;

  HttpEstablishStaticConfigByte(c.oride.proxy_response_server_enabled, "proxy.config.http.response_server_enabled");
  HttpEstablishStaticConfigStringAlloc(c.oride.proxy_response_server_string, "proxy.config.http.response_server_str");
  c.oride.proxy_response_server_string_len =
    c.oride.proxy_response_server_string ? strlen(c.oride.proxy_response_server_string) : 0;

  HttpEstablishStaticConfigByte(c.oride.insert_squid_x_forwarded_for, "proxy.config.http.insert_squid_x_forwarded_for");

  HttpEstablishStaticConfigLongLong(c.oride.proxy_protocol_out, "proxy.config.http.proxy_protocol_out");

  HttpEstablishStaticConfigByte(c.oride.insert_age_in_response, "proxy.config.http.insert_age_in_response");
  HttpEstablishStaticConfigByte(c.enable_http_stats, "proxy.config.http.enable_http_stats");
  HttpEstablishStaticConfigByte(c.oride.normalize_ae, "proxy.config.http.normalize_ae");

  HttpEstablishStaticConfigLongLong(c.oride.cache_heuristic_min_lifetime, "proxy.config.http.cache.heuristic_min_lifetime");
  HttpEstablishStaticConfigLongLong(c.oride.cache_heuristic_max_lifetime, "proxy.config.http.cache.heuristic_max_lifetime");
  HttpEstablishStaticConfigFloat(c.oride.cache_heuristic_lm_factor, "proxy.config.http.cache.heuristic_lm_factor");

  HttpEstablishStaticConfigLongLong(c.oride.cache_guaranteed_min_lifetime, "proxy.config.http.cache.guaranteed_min_lifetime");
  HttpEstablishStaticConfigLongLong(c.oride.cache_guaranteed_max_lifetime, "proxy.config.http.cache.guaranteed_max_lifetime");

  HttpEstablishStaticConfigLongLong(c.oride.cache_max_stale_age, "proxy.config.http.cache.max_stale_age");

  HttpEstablishStaticConfigByte(c.oride.srv_enabled, "proxy.config.srv_enabled");

  HttpEstablishStaticConfigByte(c.oride.allow_half_open, "proxy.config.http.allow_half_open");

  // Body factory
  HttpEstablishStaticConfigByte(c.oride.response_suppression_mode, "proxy.config.body_factory.response_suppression_mode");

  // open read failure retries
  HttpEstablishStaticConfigLongLong(c.oride.max_cache_open_read_retries, "proxy.config.http.cache.max_open_read_retries");
  HttpEstablishStaticConfigLongLong(c.oride.cache_open_read_retry_time, "proxy.config.http.cache.open_read_retry_time");
  HttpEstablishStaticConfigLongLong(c.oride.cache_generation_number, "proxy.config.http.cache.generation");

  // open write failure retries
  HttpEstablishStaticConfigLongLong(c.oride.max_cache_open_write_retries, "proxy.config.http.cache.max_open_write_retries");
  HttpEstablishStaticConfigLongLong(c.oride.max_cache_open_write_retry_timeout,
                                    "proxy.config.http.cache.max_open_write_retry_timeout");

  HttpEstablishStaticConfigByte(c.oride.cache_http, "proxy.config.http.cache.http");
  HttpEstablishStaticConfigByte(c.oride.cache_ignore_client_no_cache, "proxy.config.http.cache.ignore_client_no_cache");
  HttpEstablishStaticConfigByte(c.oride.cache_ignore_client_cc_max_age, "proxy.config.http.cache.ignore_client_cc_max_age");
  HttpEstablishStaticConfigByte(c.oride.cache_ims_on_client_no_cache, "proxy.config.http.cache.ims_on_client_no_cache");
  HttpEstablishStaticConfigByte(c.oride.cache_ignore_server_no_cache, "proxy.config.http.cache.ignore_server_no_cache");
  HttpEstablishStaticConfigByte(c.oride.cache_responses_to_cookies, "proxy.config.http.cache.cache_responses_to_cookies");

  HttpEstablishStaticConfigByte(c.oride.cache_ignore_auth, "proxy.config.http.cache.ignore_authentication");
  HttpEstablishStaticConfigByte(c.oride.cache_urls_that_look_dynamic, "proxy.config.http.cache.cache_urls_that_look_dynamic");
  HttpEstablishStaticConfigByte(c.oride.cache_ignore_query, "proxy.config.http.cache.ignore_query");

  HttpEstablishStaticConfigByte(c.oride.ignore_accept_mismatch, "proxy.config.http.cache.ignore_accept_mismatch");
  HttpEstablishStaticConfigByte(c.oride.ignore_accept_language_mismatch, "proxy.config.http.cache.ignore_accept_language_mismatch");
  HttpEstablishStaticConfigByte(c.oride.ignore_accept_encoding_mismatch, "proxy.config.http.cache.ignore_accept_encoding_mismatch");
  HttpEstablishStaticConfigByte(c.oride.ignore_accept_charset_mismatch, "proxy.config.http.cache.ignore_accept_charset_mismatch");

  HttpEstablishStaticConfigByte(c.send_100_continue_response, "proxy.config.http.send_100_continue_response");
  HttpEstablishStaticConfigByte(c.disallow_post_100_continue, "proxy.config.http.disallow_post_100_continue");

  HttpEstablishStaticConfigByte(c.oride.cache_open_write_fail_action, "proxy.config.http.cache.open_write_fail_action");

  HttpEstablishStaticConfigByte(c.oride.cache_when_to_revalidate, "proxy.config.http.cache.when_to_revalidate");
  HttpEstablishStaticConfigByte(c.oride.cache_required_headers, "proxy.config.http.cache.required_headers");
  HttpEstablishStaticConfigByte(c.oride.cache_range_lookup, "proxy.config.http.cache.range.lookup");
  HttpEstablishStaticConfigByte(c.oride.cache_range_write, "proxy.config.http.cache.range.write");
  HttpEstablishStaticConfigStringAlloc(c.oride.targeted_cache_control_headers.conf_value,
                                       "proxy.config.http.cache.targeted_cache_control_headers");
  if (c.oride.targeted_cache_control_headers.conf_value) {
    c.oride.targeted_cache_control_headers.parse(c.oride.targeted_cache_control_headers.conf_value);
  }

  HttpEstablishStaticConfigStringAlloc(c.connect_ports_string, "proxy.config.http.connect_ports");

  HttpEstablishStaticConfigLongLong(c.oride.request_hdr_max_size, "proxy.config.http.request_header_max_size");
  HttpEstablishStaticConfigLongLong(c.oride.response_hdr_max_size, "proxy.config.http.response_header_max_size");

  HttpEstablishStaticConfigByte(c.push_method_enabled, "proxy.config.http.push_method_enabled");

  HttpEstablishStaticConfigByte(c.reverse_proxy_enabled, "proxy.config.reverse_proxy.enabled");
  HttpEstablishStaticConfigByte(c.url_remap_required, "proxy.config.url_remap.remap_required");

  HttpEstablishStaticConfigStringAlloc(c.reverse_proxy_no_host_redirect, "proxy.config.header.parse.no_host_url_redirect");
  c.reverse_proxy_no_host_redirect_len = -1;
  HttpEstablishStaticConfigStringAlloc(c.oride.body_factory_template_base, "proxy.config.body_factory.template_base");
  c.oride.body_factory_template_base_len = c.oride.body_factory_template_base ? strlen(c.oride.body_factory_template_base) : 0;
  HttpEstablishStaticConfigLongLong(c.body_factory_response_max_size, "proxy.config.body_factory.response_max_size");
  HttpEstablishStaticConfigByte(c.errors_log_error_pages, "proxy.config.http.errors.log_error_pages");

  HttpEstablishStaticConfigLongLong(c.oride.slow_log_threshold, "proxy.config.http.slow.log.threshold");

  HttpEstablishStaticConfigByte(c.oride.send_http11_requests, "proxy.config.http.send_http11_requests");
  HttpEstablishStaticConfigByte(c.oride.allow_multi_range, "proxy.config.http.allow_multi_range");

  // HTTP Referer Filtering
  HttpEstablishStaticConfigByte(c.referer_filter_enabled, "proxy.config.http.referer_filter");
  HttpEstablishStaticConfigByte(c.referer_format_redirect, "proxy.config.http.referer_format_redirect");

  HttpDownServerCacheTimeVar.Enable("proxy.config.http.down_server.cache_time");

  // Negative caching and revalidation
  HttpEstablishStaticConfigByte(c.oride.negative_caching_enabled, "proxy.config.http.negative_caching_enabled");
  HttpEstablishStaticConfigLongLong(c.oride.negative_caching_lifetime, "proxy.config.http.negative_caching_lifetime");
  HttpEstablishStaticConfigStringAlloc(c.oride.negative_caching_list.conf_value, "proxy.config.http.negative_caching_list");
  HttpEstablishStaticConfigByte(c.oride.negative_revalidating_enabled, "proxy.config.http.negative_revalidating_enabled");
  HttpEstablishStaticConfigLongLong(c.oride.negative_revalidating_lifetime, "proxy.config.http.negative_revalidating_lifetime");
  HttpEstablishStaticConfigStringAlloc(c.oride.negative_revalidating_list.conf_value,
                                       "proxy.config.http.negative_revalidating_list");

  // Buffer size and watermark
  HttpEstablishStaticConfigLongLong(c.oride.default_buffer_size_index, "proxy.config.http.default_buffer_size");
  HttpEstablishStaticConfigLongLong(c.oride.default_buffer_water_mark, "proxy.config.http.default_buffer_water_mark");

  // Plugin VC buffer size and watermark
  HttpEstablishStaticConfigLongLong(c.oride.plugin_vc_default_buffer_index, "proxy.config.plugin.vc.default_buffer_index");
  HttpEstablishStaticConfigLongLong(c.oride.plugin_vc_default_buffer_water_mark,
                                    "proxy.config.plugin.vc.default_buffer_water_mark");

  HttpEstablishStaticConfigLongLong(c.max_post_size, "proxy.config.http.max_post_size");
  HttpEstablishStaticConfigLongLong(c.max_payload_iobuf_index, "proxy.config.payload.io.max_buffer_index");
  HttpEstablishStaticConfigLongLong(c.max_msg_iobuf_index, "proxy.config.msg.io.max_buffer_index");

  // ##############################################################################
  // #
  // # Redirection
  // #
  // # See RecordsConfig definition.
  // #
  // ##############################################################################
  HttpEstablishStaticConfigByte(c.oride.redirect_use_orig_cache_key, "proxy.config.http.redirect_use_orig_cache_key");
  HttpEstablishStaticConfigByte(c.redirection_host_no_port, "proxy.config.http.redirect_host_no_port");
  HttpEstablishStaticConfigLongLong(c.oride.number_of_redirections, "proxy.config.http.number_of_redirections");
  HttpEstablishStaticConfigLongLong(c.post_copy_size, "proxy.config.http.post_copy_size");
  HttpEstablishStaticConfigStringAlloc(c.redirect_actions_string, "proxy.config.http.redirect.actions");
  HttpEstablishStaticConfigByte(c.http_host_sni_policy, "proxy.config.http.host_sni_policy");
  HttpEstablishStaticConfigByte(c.cache_try_compat_key_read, "proxy.config.http.cache.try_compat_key_read");
  HttpEstablishStaticConfigStringAlloc(c.oride.ssl_client_sni_policy, "proxy.config.ssl.client.sni_policy");
  HttpEstablishStaticConfigStringAlloc(c.oride.ssl_client_alpn_protocols, "proxy.config.ssl.client.alpn_protocols");
  HttpEstablishStaticConfigByte(c.scheme_proto_mismatch_policy, "proxy.config.ssl.client.scheme_proto_mismatch_policy");

  ConnectionTracker::config_init(&c.global_connection_tracker_config, &c.oride.connection_tracker_config, http_config_cb);

  MUTEX_TRY_LOCK(lock, http_config_cont->mutex, this_ethread());
  if (!lock.is_locked()) {
    ink_release_assert(0);
  }
  http_config_cont->handleEvent(EVENT_NONE, nullptr);

  return;
}

////////////////////////////////////////////////////////////////
//
//  HttpConfig::reconfigure()
//
////////////////////////////////////////////////////////////////
void
HttpConfig::reconfigure()
{
  auto INT_TO_BOOL = [](RecInt i) -> bool { return i != 0; };

  HttpConfigParams *params;

  params = new HttpConfigParams;

  params->inbound = m_master.inbound;

  params->outbound = m_master.outbound;

  params->proxy_hostname                           = ats_strdup(m_master.proxy_hostname);
  params->proxy_hostname_len                       = (params->proxy_hostname) ? strlen(params->proxy_hostname) : 0;
  params->oride.no_dns_forward_to_parent           = INT_TO_BOOL(m_master.oride.no_dns_forward_to_parent);
  params->oride.uncacheable_requests_bypass_parent = INT_TO_BOOL(m_master.oride.uncacheable_requests_bypass_parent);
  params->no_origin_server_dns                     = INT_TO_BOOL(m_master.no_origin_server_dns);
  params->use_client_target_addr                   = m_master.use_client_target_addr;
  params->use_client_source_port                   = INT_TO_BOOL(m_master.use_client_source_port);
  params->oride.maintain_pristine_host_hdr         = INT_TO_BOOL(m_master.oride.maintain_pristine_host_hdr);

  params->disable_ssl_parenting        = INT_TO_BOOL(m_master.disable_ssl_parenting);
  params->oride.forward_connect_method = INT_TO_BOOL(m_master.oride.forward_connect_method);

  params->server_max_connections                = m_master.server_max_connections;
  params->max_websocket_connections             = m_master.max_websocket_connections;
  params->oride.connection_tracker_config       = m_master.oride.connection_tracker_config;
  params->global_connection_tracker_config      = m_master.global_connection_tracker_config;
  params->oride.attach_server_session_to_client = m_master.oride.attach_server_session_to_client;
  params->oride.max_proxy_cycles                = m_master.oride.max_proxy_cycles;
  params->oride.tunnel_activity_check_period    = m_master.oride.tunnel_activity_check_period;

  params->oride.default_inactivity_timeout = m_master.oride.default_inactivity_timeout;

  params->http_request_line_max_size = m_master.http_request_line_max_size;
  params->http_hdr_field_max_size    = m_master.http_hdr_field_max_size;
  params->pp_hdr_max_size            = m_master.pp_hdr_max_size;

  // A header field name and value are each stored with a uint16_t length, so a
  // single field cannot exceed UINT16_MAX octets. Clamp the configured
  // per-field limit to that ceiling: a larger value can never be honored and an
  // oversized field is rejected at parse time regardless.
  if (params->http_hdr_field_max_size > UINT16_MAX) {
    Note("proxy.config.http.header_field_max_size %" PRId64 " exceeds the %d-octet storage limit; clamping to %d",
         static_cast<int64_t>(params->http_hdr_field_max_size), UINT16_MAX, UINT16_MAX);
    params->http_hdr_field_max_size = UINT16_MAX;
  }

  if (params->oride.connection_tracker_config.server_max > 0 &&
      params->oride.connection_tracker_config.server_max < params->oride.connection_tracker_config.server_min) {
    Warning("'%s' < per_server.min_keep_alive_connections, setting min=max , please correct your %s",
            ConnectionTracker::CONFIG_SERVER_VAR_MAX.data(), ts::filename::RECORDS);
    params->oride.connection_tracker_config.server_min = params->oride.connection_tracker_config.server_max;
  }

  params->oride.insert_request_via_string   = m_master.oride.insert_request_via_string;
  params->oride.insert_response_via_string  = m_master.oride.insert_response_via_string;
  params->proxy_request_via_string          = ats_strdup(m_master.proxy_request_via_string);
  params->proxy_request_via_string_len      = (params->proxy_request_via_string) ? strlen(params->proxy_request_via_string) : 0;
  params->proxy_response_via_string         = ats_strdup(m_master.proxy_response_via_string);
  params->proxy_response_via_string_len     = (params->proxy_response_via_string) ? strlen(params->proxy_response_via_string) : 0;
  params->oride.proxy_response_hsts_max_age = m_master.oride.proxy_response_hsts_max_age;
  params->oride.proxy_response_hsts_include_subdomains = m_master.oride.proxy_response_hsts_include_subdomains;

  params->oride.keep_alive_enabled_in       = INT_TO_BOOL(m_master.oride.keep_alive_enabled_in);
  params->oride.keep_alive_enabled_out      = INT_TO_BOOL(m_master.oride.keep_alive_enabled_out);
  params->oride.chunking_enabled            = INT_TO_BOOL(m_master.oride.chunking_enabled);
  params->oride.http_drop_chunked_trailers  = m_master.oride.http_drop_chunked_trailers;
  params->oride.http_strict_chunk_parsing   = m_master.oride.http_strict_chunk_parsing;
  params->oride.auth_server_session_private = m_master.oride.auth_server_session_private;

  params->oride.http_chunking_size = m_master.oride.http_chunking_size;

  params->oride.post_check_content_length_enabled = INT_TO_BOOL(m_master.oride.post_check_content_length_enabled);
  params->oride.cache_post_method                 = INT_TO_BOOL(m_master.oride.cache_post_method);

  params->oride.request_buffer_enabled = INT_TO_BOOL(m_master.oride.request_buffer_enabled);

  params->oride.flow_control_enabled = INT_TO_BOOL(m_master.oride.flow_control_enabled);
  params->oride.flow_high_water_mark = m_master.oride.flow_high_water_mark;
  params->oride.flow_low_water_mark  = m_master.oride.flow_low_water_mark;
  // If not set (zero) then make values the same.
  if (params->oride.flow_low_water_mark <= 0) {
    params->oride.flow_low_water_mark = params->oride.flow_high_water_mark;
  }
  if (params->oride.flow_high_water_mark <= 0) {
    params->oride.flow_high_water_mark = params->oride.flow_low_water_mark;
  }
  if (params->oride.flow_high_water_mark < params->oride.flow_low_water_mark) {
    Warning("Flow control low water mark is greater than high water mark, flow control disabled");
    params->oride.flow_control_enabled = 0;
    // zero means "hardwired default" when actually used.
    params->oride.flow_high_water_mark = params->oride.flow_low_water_mark = 0;
  }

  params->oride.server_session_sharing_match     = m_master.oride.server_session_sharing_match;
  params->oride.server_session_sharing_match_str = ats_strdup(m_master.oride.server_session_sharing_match_str);
  params->oride.server_min_keep_alive_conns      = m_master.oride.server_min_keep_alive_conns;
  params->server_session_sharing_pool            = m_master.server_session_sharing_pool;
  params->oride.keep_alive_post_out              = m_master.oride.keep_alive_post_out;

  params->oride.keep_alive_no_activity_timeout_in   = m_master.oride.keep_alive_no_activity_timeout_in;
  params->oride.keep_alive_no_activity_timeout_out  = m_master.oride.keep_alive_no_activity_timeout_out;
  params->oride.transaction_no_activity_timeout_in  = m_master.oride.transaction_no_activity_timeout_in;
  params->oride.transaction_no_activity_timeout_out = m_master.oride.transaction_no_activity_timeout_out;
  params->oride.transaction_active_timeout_in       = m_master.oride.transaction_active_timeout_in;
  params->oride.transaction_active_timeout_out      = m_master.oride.transaction_active_timeout_out;
  params->oride.websocket_active_timeout            = m_master.oride.websocket_active_timeout;
  params->oride.websocket_inactive_timeout          = m_master.oride.websocket_inactive_timeout;
  params->accept_no_activity_timeout                = m_master.accept_no_activity_timeout;
  params->oride.background_fill_active_timeout      = m_master.oride.background_fill_active_timeout;
  params->oride.background_fill_threshold           = m_master.oride.background_fill_threshold;

  params->oride.connect_attempts_max_retries                = m_master.oride.connect_attempts_max_retries;
  params->oride.connect_attempts_max_retries_suspect_server = m_master.oride.connect_attempts_max_retries_suspect_server;

  // Deprecation handling for connect_attempts_max_retries_down_server: if the operator explicitly set the deprecated record
  // but did not set the replacement, mirror the value forward so existing configs keep working. If both are set, the new
  // record wins and the deprecated one is ignored. Always warn when the deprecated record is explicitly set. The deprecated
  // record has no bound struct field; fetch its value on demand from RecCore.
  {
    RecSourceT old_src = REC_SOURCE_NULL;
    RecSourceT new_src = REC_SOURCE_NULL;
    RecGetRecordSource("proxy.config.http.connect_attempts_max_retries_down_server", &old_src);
    RecGetRecordSource("proxy.config.http.connect_attempts_max_retries_suspect_server", &new_src);
    const bool old_explicit = (old_src == REC_SOURCE_EXPLICIT || old_src == REC_SOURCE_ENV);
    const bool new_explicit = (new_src == REC_SOURCE_EXPLICIT || new_src == REC_SOURCE_ENV);
    if (old_explicit && !new_explicit) {
      RecInt deprecated_val = RecGetRecordInt("proxy.config.http.connect_attempts_max_retries_down_server")
                                .value_or(params->oride.connect_attempts_max_retries_suspect_server);
      Warning("proxy.config.http.connect_attempts_max_retries_down_server is deprecated; "
              "use proxy.config.http.connect_attempts_max_retries_suspect_server instead. "
              "Using deprecated value %" PRIu64 " for now.",
              deprecated_val);
      params->oride.connect_attempts_max_retries_suspect_server = deprecated_val;
    } else if (old_explicit && new_explicit) {
      Warning("proxy.config.http.connect_attempts_max_retries_down_server is deprecated and is being ignored "
              "in favor of proxy.config.http.connect_attempts_max_retries_suspect_server (%" PRIu64 ").",
              m_master.oride.connect_attempts_max_retries_suspect_server);
    }
  }

  if (m_master.oride.connect_attempts_rr_retries > params->oride.connect_attempts_max_retries) {
    Warning("connect_attempts_rr_retries (%" PRIu64 ") is greater than "
            "connect_attempts_max_retries (%" PRIu64 "), this means requests "
            "will never redispatch to another server",
            m_master.oride.connect_attempts_rr_retries, params->oride.connect_attempts_max_retries);
  }
  if (m_master.oride.connect_attempts_rr_retries > 0 && params->oride.connect_attempts_max_retries_suspect_server == 0) {
    Warning("connect_attempts_max_retries_suspect_server=0 with round-robin enabled leaves no retry budget for recovering "
            "(SUSPECT) origins beyond the initial attempt; "
            "setting proxy.config.http.connect_attempts_max_retries_suspect_server >= 1 is recommended");
  }
  params->oride.connect_attempts_retry_backoff_base = m_master.oride.connect_attempts_retry_backoff_base;

  params->oride.connect_attempts_rr_retries     = m_master.oride.connect_attempts_rr_retries;
  params->oride.connect_attempts_timeout        = m_master.oride.connect_attempts_timeout;
  params->oride.connect_down_policy             = m_master.oride.connect_down_policy;
  params->oride.parent_connect_attempts         = m_master.oride.parent_connect_attempts;
  params->oride.parent_retry_time               = m_master.oride.parent_retry_time;
  params->oride.parent_fail_threshold           = m_master.oride.parent_fail_threshold;
  params->oride.per_parent_connect_attempts     = m_master.oride.per_parent_connect_attempts;
  params->oride.parent_failures_update_hostdb   = m_master.oride.parent_failures_update_hostdb;
  params->oride.no_dns_forward_to_parent        = m_master.oride.no_dns_forward_to_parent;
  params->oride.enable_parent_timeout_markdowns = m_master.oride.enable_parent_timeout_markdowns;
  params->oride.disable_parent_markdowns        = m_master.oride.disable_parent_markdowns;

  params->oride.sock_recv_buffer_size_out = m_master.oride.sock_recv_buffer_size_out;
  params->oride.sock_send_buffer_size_out = m_master.oride.sock_send_buffer_size_out;
  params->oride.sock_packet_mark_out      = m_master.oride.sock_packet_mark_out;
  params->oride.sock_packet_tos_out       = m_master.oride.sock_packet_tos_out;
  params->oride.sock_option_flag_out      = m_master.oride.sock_option_flag_out;
  params->oride.sock_packet_notsent_lowat = m_master.oride.sock_packet_notsent_lowat;

  // Clear the TCP Fast Open option if it is not supported on this host.
  if ((params->oride.sock_option_flag_out & NetVCOptions::SOCK_OPT_TCP_FAST_OPEN) && !UnixSocket::client_fastopen_supported()) {
    Status("disabling unsupported TCP Fast Open flag on proxy.config.net.sock_option_flag_out");
    params->oride.sock_option_flag_out &= ~NetVCOptions::SOCK_OPT_TCP_FAST_OPEN;
  }

  params->oride.fwd_proxy_auth_to_parent = INT_TO_BOOL(m_master.oride.fwd_proxy_auth_to_parent);

  params->oride.anonymize_remove_from       = INT_TO_BOOL(m_master.oride.anonymize_remove_from);
  params->oride.anonymize_remove_referer    = INT_TO_BOOL(m_master.oride.anonymize_remove_referer);
  params->oride.anonymize_remove_user_agent = INT_TO_BOOL(m_master.oride.anonymize_remove_user_agent);
  params->oride.anonymize_remove_cookie     = INT_TO_BOOL(m_master.oride.anonymize_remove_cookie);
  params->oride.anonymize_remove_client_ip  = INT_TO_BOOL(m_master.oride.anonymize_remove_client_ip);
  params->oride.anonymize_insert_client_ip  = m_master.oride.anonymize_insert_client_ip;
  params->anonymize_other_header_list       = ats_strdup(m_master.anonymize_other_header_list);

  params->oride.global_user_agent_header = ats_strdup(m_master.oride.global_user_agent_header);
  params->oride.global_user_agent_header_size =
    params->oride.global_user_agent_header ? strlen(params->oride.global_user_agent_header) : 0;

  params->oride.proxy_response_server_string = ats_strdup(m_master.oride.proxy_response_server_string);
  params->oride.proxy_response_server_string_len =
    params->oride.proxy_response_server_string ? strlen(params->oride.proxy_response_server_string) : 0;
  params->oride.proxy_response_server_enabled = m_master.oride.proxy_response_server_enabled;

  params->oride.insert_squid_x_forwarded_for = INT_TO_BOOL(m_master.oride.insert_squid_x_forwarded_for);
  params->oride.insert_forwarded             = m_master.oride.insert_forwarded;
  params->oride.insert_age_in_response       = INT_TO_BOOL(m_master.oride.insert_age_in_response);
  params->enable_http_stats                  = INT_TO_BOOL(m_master.enable_http_stats);
  params->oride.normalize_ae                 = m_master.oride.normalize_ae;
  params->oride.proxy_protocol_out           = m_master.oride.proxy_protocol_out;

  params->oride.cache_heuristic_min_lifetime = m_master.oride.cache_heuristic_min_lifetime;
  params->oride.cache_heuristic_max_lifetime = m_master.oride.cache_heuristic_max_lifetime;
  params->oride.cache_heuristic_lm_factor    = std::min(std::max(m_master.oride.cache_heuristic_lm_factor, 0.0f), 1.0f);

  params->oride.cache_guaranteed_min_lifetime = m_master.oride.cache_guaranteed_min_lifetime;
  params->oride.cache_guaranteed_max_lifetime = m_master.oride.cache_guaranteed_max_lifetime;

  params->oride.cache_max_stale_age = m_master.oride.cache_max_stale_age;

  params->oride.srv_enabled = m_master.oride.srv_enabled;

  params->oride.allow_half_open = m_master.oride.allow_half_open;

  params->oride.response_suppression_mode = m_master.oride.response_suppression_mode;

  // open read failure retries
  params->oride.max_cache_open_read_retries = m_master.oride.max_cache_open_read_retries;
  params->oride.cache_open_read_retry_time  = m_master.oride.cache_open_read_retry_time;
  params->oride.cache_generation_number     = m_master.oride.cache_generation_number;

  // open write failure retries
  params->oride.max_cache_open_write_retries = m_master.oride.max_cache_open_write_retries;

  params->oride.cache_http                     = INT_TO_BOOL(m_master.oride.cache_http);
  params->oride.cache_ignore_client_no_cache   = INT_TO_BOOL(m_master.oride.cache_ignore_client_no_cache);
  params->oride.cache_ignore_client_cc_max_age = INT_TO_BOOL(m_master.oride.cache_ignore_client_cc_max_age);
  params->oride.cache_ims_on_client_no_cache   = INT_TO_BOOL(m_master.oride.cache_ims_on_client_no_cache);
  params->oride.cache_ignore_server_no_cache   = INT_TO_BOOL(m_master.oride.cache_ignore_server_no_cache);
  params->oride.cache_responses_to_cookies     = m_master.oride.cache_responses_to_cookies;
  params->oride.cache_ignore_auth              = INT_TO_BOOL(m_master.oride.cache_ignore_auth);
  params->oride.cache_urls_that_look_dynamic   = INT_TO_BOOL(m_master.oride.cache_urls_that_look_dynamic);
  params->oride.cache_ignore_query             = INT_TO_BOOL(m_master.oride.cache_ignore_query);

  params->oride.ignore_accept_mismatch          = m_master.oride.ignore_accept_mismatch;
  params->oride.ignore_accept_language_mismatch = m_master.oride.ignore_accept_language_mismatch;
  params->oride.ignore_accept_encoding_mismatch = m_master.oride.ignore_accept_encoding_mismatch;
  params->oride.ignore_accept_charset_mismatch  = m_master.oride.ignore_accept_charset_mismatch;

  params->send_100_continue_response = INT_TO_BOOL(m_master.send_100_continue_response);
  params->disallow_post_100_continue = INT_TO_BOOL(m_master.disallow_post_100_continue);

  params->oride.cache_open_write_fail_action = m_master.oride.cache_open_write_fail_action;
  if (params->oride.cache_open_write_fail_action == static_cast<MgmtByte>(CacheOpenWriteFailAction_t::READ_RETRY) ||
      params->oride.cache_open_write_fail_action ==
        static_cast<MgmtByte>(CacheOpenWriteFailAction_t::READ_RETRY_STALE_ON_REVALIDATE)) {
    if (params->oride.max_cache_open_read_retries <= 0 || params->oride.max_cache_open_write_retries <= 0) {
      Warning("Invalid config, cache_open_write_fail_action (%d), max_cache_open_read_retries (%" PRIu64 "), "
              "max_cache_open_write_retries (%" PRIu64 ")",
              params->oride.cache_open_write_fail_action, params->oride.max_cache_open_read_retries,
              params->oride.max_cache_open_write_retries);
    }
  }

  params->oride.cache_when_to_revalidate = m_master.oride.cache_when_to_revalidate;
  params->max_post_size                  = m_master.max_post_size;
  params->max_payload_iobuf_index        = m_master.max_payload_iobuf_index;
  params->max_msg_iobuf_index            = m_master.max_msg_iobuf_index;

  params->oride.cache_required_headers                    = m_master.oride.cache_required_headers;
  params->oride.cache_range_lookup                        = INT_TO_BOOL(m_master.oride.cache_range_lookup);
  params->oride.cache_range_write                         = INT_TO_BOOL(m_master.oride.cache_range_write);
  params->oride.targeted_cache_control_headers.conf_value = ats_strdup(m_master.oride.targeted_cache_control_headers.conf_value);
  if (params->oride.targeted_cache_control_headers.conf_value) {
    params->oride.targeted_cache_control_headers.parse(params->oride.targeted_cache_control_headers.conf_value);
  }
  params->oride.allow_multi_range = m_master.oride.allow_multi_range;

  params->connect_ports_string = ats_strdup(m_master.connect_ports_string);
  params->connect_ports        = parse_ports_list(params->connect_ports_string);

  params->oride.request_hdr_max_size  = m_master.oride.request_hdr_max_size;
  params->oride.response_hdr_max_size = m_master.oride.response_hdr_max_size;

  params->push_method_enabled = INT_TO_BOOL(m_master.push_method_enabled);

  params->reverse_proxy_enabled            = INT_TO_BOOL(m_master.reverse_proxy_enabled);
  params->url_remap_required               = INT_TO_BOOL(m_master.url_remap_required);
  params->errors_log_error_pages           = INT_TO_BOOL(m_master.errors_log_error_pages);
  params->oride.slow_log_threshold         = m_master.oride.slow_log_threshold;
  params->oride.send_http11_requests       = m_master.oride.send_http11_requests;
  params->oride.doc_in_cache_skip_dns      = INT_TO_BOOL(m_master.oride.doc_in_cache_skip_dns);
  params->oride.default_buffer_size_index  = m_master.oride.default_buffer_size_index;
  params->oride.default_buffer_water_mark  = m_master.oride.default_buffer_water_mark;
  params->oride.body_factory_template_base = ats_strdup(m_master.oride.body_factory_template_base);
  params->oride.body_factory_template_base_len =
    params->oride.body_factory_template_base ? strlen(params->oride.body_factory_template_base) : 0;
  params->body_factory_response_max_size = m_master.body_factory_response_max_size;
  params->reverse_proxy_no_host_redirect = ats_strdup(m_master.reverse_proxy_no_host_redirect);
  params->reverse_proxy_no_host_redirect_len =
    params->reverse_proxy_no_host_redirect ? strlen(params->reverse_proxy_no_host_redirect) : 0;

  params->referer_filter_enabled  = INT_TO_BOOL(m_master.referer_filter_enabled);
  params->referer_format_redirect = INT_TO_BOOL(m_master.referer_format_redirect);

  params->strict_uri_parsing = m_master.strict_uri_parsing;

  params->oride.down_server_timeout = m_master.oride.down_server_timeout;

  params->oride.negative_caching_enabled       = INT_TO_BOOL(m_master.oride.negative_caching_enabled);
  params->oride.negative_caching_lifetime      = m_master.oride.negative_caching_lifetime;
  params->oride.negative_revalidating_enabled  = INT_TO_BOOL(m_master.oride.negative_revalidating_enabled);
  params->oride.negative_revalidating_lifetime = m_master.oride.negative_revalidating_lifetime;

  params->oride.redirect_use_orig_cache_key = INT_TO_BOOL(m_master.oride.redirect_use_orig_cache_key);
  params->redirection_host_no_port          = INT_TO_BOOL(m_master.redirection_host_no_port);
  params->oride.number_of_redirections      = m_master.oride.number_of_redirections;
  params->post_copy_size                    = m_master.post_copy_size;
  if (params->oride.request_buffer_enabled && params->post_copy_size == 0) {
    Warning("proxy.config.http.request_buffer_enabled is set but proxy.config.http.post_copy_size is 0; request buffering "
            "will be disabled");
  }
  params->redirect_actions_string = ats_strdup(m_master.redirect_actions_string);
  params->redirect_actions_map    = parse_redirect_actions(params->redirect_actions_string, params->redirect_actions_self_action);
  params->http_host_sni_policy    = m_master.http_host_sni_policy;
  params->scheme_proto_mismatch_policy = m_master.scheme_proto_mismatch_policy;

  params->oride.ssl_client_sni_policy     = ats_strdup(m_master.oride.ssl_client_sni_policy);
  params->oride.ssl_client_alpn_protocols = ats_strdup(m_master.oride.ssl_client_alpn_protocols);

  params->oride.negative_caching_list.set(m_master.oride.negative_caching_list.conf_value);
  params->oride.negative_revalidating_list.set(m_master.oride.negative_revalidating_list.conf_value);

  params->oride.host_res_data            = m_master.oride.host_res_data;
  params->oride.host_res_data.conf_value = ats_strdup(m_master.oride.host_res_data.conf_value);

  params->oride.plugin_vc_default_buffer_index      = m_master.oride.plugin_vc_default_buffer_index;
  params->oride.plugin_vc_default_buffer_water_mark = m_master.oride.plugin_vc_default_buffer_water_mark;

  params->cache_try_compat_key_read = m_master.cache_try_compat_key_read;

  m_id = configProcessor.set(m_id, params);
}

////////////////////////////////////////////////////////////////
//
//  HttpConfig::acquire()
//
////////////////////////////////////////////////////////////////
HttpConfigParams *
HttpConfig::acquire()
{
  if (m_id != 0) {
    return (HttpConfigParams *)configProcessor.get(m_id);
  } else {
    return nullptr;
  }
}

////////////////////////////////////////////////////////////////
//
//  HttpConfig::release()
//
////////////////////////////////////////////////////////////////
void
HttpConfig::release(HttpConfigParams *params)
{
  configProcessor.release(m_id, params);
}

////////////////////////////////////////////////////////////////
//
//  HttpConfig::parse_ports_list()
//
////////////////////////////////////////////////////////////////
HttpConfigPortRange *
HttpConfig::parse_ports_list(char *ports_string)
{
  HttpConfigPortRange *ports_list = nullptr;

  if (!ports_string) {
    return (nullptr);
  }

  if (strchr(ports_string, '*')) {
    ports_list       = new HttpConfigPortRange;
    ports_list->low  = -1;
    ports_list->high = -1;
    ports_list->next = nullptr;
  } else {
    HttpConfigPortRange *pr, *prev;
    char                *start;
    char                *end;

    pr   = nullptr;
    prev = nullptr;

    start = ports_string;

    while (true) { // eat whitespace
      while ((start[0] != '\0') && ParseRules::is_space(start[0])) {
        start++;
      }

      // locate the end of the next number
      end = start;
      while ((end[0] != '\0') && ParseRules::is_digit(end[0])) {
        end++;
      }

      // if there is no next number we're done
      if (start == end) {
        break;
      }

      pr       = new HttpConfigPortRange;
      pr->low  = atoi(start);
      pr->high = pr->low;
      pr->next = nullptr;

      if (prev) {
        prev->next = pr;
      } else {
        ports_list = pr;
      }
      prev = pr;

      // if the next character after the current port
      //  number is a dash then we are parsing a range
      if (end[0] == '-') {
        start = end + 1;
        while ((start[0] != '\0') && ParseRules::is_space(start[0])) {
          start++;
        }

        end = start;
        while ((end[0] != '\0') && ParseRules::is_digit(end[0])) {
          end++;
        }

        if (start == end) {
          break;
        }

        pr->high = atoi(start);
      }

      start = end;

      ink_release_assert(pr->low <= pr->high);
    }
  }
  return (ports_list);
}

////////////////////////////////////////////////////////////////
//
//  HttpConfig::parse_redirect_actions()
//
////////////////////////////////////////////////////////////////
RedirectEnabled::ActionMap *
HttpConfig::parse_redirect_actions(char *input_string, RedirectEnabled::Action &self_action)
{
  using RedirectEnabled::Action;
  using RedirectEnabled::action_map;
  using RedirectEnabled::address_class_map;
  using RedirectEnabled::AddressClass;

  if (nullptr == input_string) {
    Error("parse_redirect_actions: The configuration value is empty.");
    return nullptr;
  }
  Tokenizer                      configTokens(", ");
  int                            n_rules = configTokens.Initialize(input_string);
  std::map<AddressClass, Action> configMapping;
  for (int i = 0; i < n_rules; i++) {
    const char *rule = configTokens[i];
    Tokenizer   ruleTokens(":");
    int         n_mapping = ruleTokens.Initialize(rule);
    if (2 != n_mapping) {
      Error("parse_redirect_actions: Individual rules must be an address class and an action separated by a colon (:)");
      return nullptr;
    }
    std::string  c_input(ruleTokens[0]), a_input(ruleTokens[1]);
    AddressClass c =
      address_class_map.find(ruleTokens[0]) != address_class_map.end() ? address_class_map[ruleTokens[0]] : AddressClass::INVALID;
    Action a = action_map.find(ruleTokens[1]) != action_map.end() ? action_map[ruleTokens[1]] : Action::INVALID;

    if (AddressClass::INVALID == c) {
      Error("parse_redirect_actions: '%.*s' is not a valid address class", static_cast<int>(c_input.size()), c_input.data());
      return nullptr;
    } else if (Action::INVALID == a) {
      Error("parse_redirect_actions: '%.*s' is not a valid action", static_cast<int>(a_input.size()), a_input.data());
      return nullptr;
    }
    configMapping[c] = a;
  }

  // Ensure the default.
  if (configMapping.end() == configMapping.find(AddressClass::DEFAULT)) {
    configMapping[AddressClass::DEFAULT] = Action::RETURN;
  }

  auto  *ret    = new RedirectEnabled::ActionMap;
  Action action = Action::INVALID;

  // PRIVATE
  action = configMapping.find(AddressClass::PRIVATE) != configMapping.end() ? configMapping[AddressClass::PRIVATE] :
                                                                              configMapping[AddressClass::DEFAULT];
  ret->mark(swoc::IP4Range("10.0.0.0/8"), action);
  ret->mark(swoc::IP4Range("100.64.0.0/10"), action);
  ret->mark(swoc::IP4Range("172.16.0.0/12"), action);
  ret->mark(swoc::IP4Range("192.168.0.0/16"), action);
  ret->mark(swoc::IP6Range("fc00::/7"), action);

  // LOOPBACK
  action = configMapping.find(AddressClass::LOOPBACK) != configMapping.end() ? configMapping[AddressClass::LOOPBACK] :
                                                                               configMapping[AddressClass::DEFAULT];

  ret->mark(swoc::IP4Range("127.0.0.0/8"), action);
  ret->mark(swoc::IP6Addr("::1"), action);

  // MULTICAST
  action = configMapping.find(AddressClass::MULTICAST) != configMapping.end() ? configMapping[AddressClass::MULTICAST] :
                                                                                configMapping[AddressClass::DEFAULT];
  ret->mark(swoc::IP4Range("224.0.0.0/4"), action);
  ret->mark(swoc::IP6Range("ff00::/8"), action);

  // LINKLOCAL
  action = configMapping.find(AddressClass::LINKLOCAL) != configMapping.end() ? configMapping[AddressClass::LINKLOCAL] :
                                                                                configMapping[AddressClass::DEFAULT];
  ret->mark(swoc::IP4Range("169.254.0.0/16"), action);
  ret->mark(swoc::IP6Range("fe80::/10"), action);

  // SELF
  // We must store the self address class separately instead of adding the addresses to our map.
  // The addresses Trafficserver will use depend on configurations that are loaded here, so they are not available yet.
  action      = configMapping.find(AddressClass::SELF) != configMapping.end() ? configMapping[AddressClass::SELF] :
                                                                                configMapping[AddressClass::DEFAULT];
  self_action = action;

  // ROUTABLE
  action = configMapping.find(AddressClass::ROUTABLE) != configMapping.end() ? configMapping[AddressClass::ROUTABLE] :
                                                                               configMapping[AddressClass::DEFAULT];
  // @c fille doesn't change any existing mapping.
  ret->fill(swoc::IP4Range("0/0"), action);
  ret->fill(swoc::IP6Range("::/0"), action);

  return ret;
}
