You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2022/07/13 21:12:51 UTC
[trafficserver] branch master updated: Hostdb Restructure (#8953)
This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new 156528de4 Hostdb Restructure (#8953)
156528de4 is described below
commit 156528de4ce4d6bef2f2d2bc0963dadd9f122eec
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Wed Jul 13 16:12:44 2022 -0500
Hostdb Restructure (#8953)
* Rebase checkpoint for HostDB restructure.
* Post rebase fixup.
* Fix PreWarm
* Fix nexthop test.
* Timing fix for HostDB restructure (#34)
The default duration of time_since_epoch() is
std::chrono::high_resolution_clock::duration, which will not generally
be seconds. hostDB.refcountcache->put expects the epoch count to be a
number of seconds. This explicitly casts to seconds so we get that
expected value.
This also makes some other std::chrono time updates.
Co-authored-by: bneradt <bn...@yahooinc.com>
* Debug_bw updates. And clang-format fix. (#35)
* Debug_bw updates. And clang-format fix.
* If HostDB returns only failed parents, try serving from cache.
Before this change, the parent cache logic would give up if it couldn't
resolve the origin name for a request. This will attempt to retrieve a
cached response if the resolution fails.
Co-authored-by: Brian Neradt <br...@verizonmedia.com>
Co-authored-by: bneradt <bn...@yahooinc.com>
Co-authored-by: Brian Neradt <br...@gmail.com>
---
configs/body_factory/default/Makefile.am | 1 +
configs/body_factory/default/connect#all_dead | 17 +
.../core-architecture/HostDB-Data-Layout.svg | 3 +
.../core-architecture/hostdb.en.rst | 191 +++
doc/developer-guide/core-architecture/index.en.rst | 1 +
doc/uml/host-resolve.plantuml | 24 +
example/plugins/c-api/protocol/TxnSM.c | 12 +-
include/ts/ts.h | 6 +
include/tscore/BufferWriter.h | 4 +-
include/tscore/BufferWriterForward.h | 7 +
include/tscore/Diags.h | 11 +
include/tscore/bwf_std_format.h | 16 +-
include/tscore/ink_time.h | 1 +
include/tscore/ts_file.h | 7 +
iocore/dns/P_SplitDNSProcessor.h | 36 +-
iocore/dns/SRV.h | 1 -
iocore/dns/SplitDNS.cc | 6 +-
iocore/hostdb/HostDB.cc | 1593 +++++++++++---------
iocore/hostdb/I_HostDBProcessor.h | 960 ++++++++----
iocore/hostdb/P_HostDBProcessor.h | 283 +---
plugins/lua/ts_lua_misc.c | 10 +-
proxy/ControlMatcher.h | 8 +-
proxy/ParentSelection.cc | 30 +-
proxy/http/HttpConfig.cc | 4 +-
proxy/http/HttpConfig.h | 4 +-
proxy/http/HttpConnectionCount.cc | 2 +-
proxy/http/HttpSM.cc | 358 ++---
proxy/http/HttpSM.h | 7 +-
proxy/http/HttpTransact.cc | 365 ++---
proxy/http/HttpTransact.h | 77 +-
proxy/http/PreWarmManager.cc | 46 +-
proxy/http/PreWarmManager.h | 4 +-
proxy/http/remap/unit-tests/nexthop_test_stubs.cc | 6 +-
src/traffic_server/InkAPI.cc | 52 +-
src/traffic_server/InkAPITest.cc | 8 +-
src/tscore/unit_tests/test_BufferWriterFormat.cc | 2 +
.../next_hop/strategies_ch2/strategies_ch2.test.py | 2 +
.../proxy_protocol/proxy_serve_stale.test.py | 2 +-
.../tls/tls_verify_override_base.test.py | 4 +-
39 files changed, 2236 insertions(+), 1935 deletions(-)
diff --git a/configs/body_factory/default/Makefile.am b/configs/body_factory/default/Makefile.am
index a24d2e290..69eb6b681 100644
--- a/configs/body_factory/default/Makefile.am
+++ b/configs/body_factory/default/Makefile.am
@@ -28,6 +28,7 @@ dist_bodyfactory_DATA = \
connect\#dns_failed \
connect\#failed_connect \
connect\#hangup \
+ connect\#all_dead \
default \
interception\#no_host \
README \
diff --git a/configs/body_factory/default/connect#all_dead b/configs/body_factory/default/connect#all_dead
new file mode 100644
index 000000000..7e18a6298
--- /dev/null
+++ b/configs/body_factory/default/connect#all_dead
@@ -0,0 +1,17 @@
+<HTML>
+<HEAD>
+<TITLE>No Valid Host</TITLE>
+</HEAD>
+
+<BODY BGCOLOR="white" FGCOLOR="black">
+<H1>No Valid Host</H1>
+<HR>
+
+<FONT FACE="Helvetica,Arial"><B>
+Description: Unable to find a valid target host.
+
+The server was found but all of the addresses are marked dead and so there is
+no valid target address to which to connect. Please try again after a few minutes.
+</B></FONT>
+<HR>
+</BODY>
diff --git a/doc/developer-guide/core-architecture/HostDB-Data-Layout.svg b/doc/developer-guide/core-architecture/HostDB-Data-Layout.svg
new file mode 100644
index 000000000..9c02674a8
--- /dev/null
+++ b/doc/developer-guide/core-architecture/HostDB-Data-Layout.svg
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="741px" height="430px" viewBox="-0.5 -0.5 741 430" content="<mxfile host="Electron" modified="2021-06-15T18:08:38.026Z" agent="5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="xr4FVvgPdRbuqDNpDpa1" version="14.6.13" type="device"><d [...]
\ No newline at end of file
diff --git a/doc/developer-guide/core-architecture/hostdb.en.rst b/doc/developer-guide/core-architecture/hostdb.en.rst
new file mode 100644
index 000000000..33eef3c25
--- /dev/null
+++ b/doc/developer-guide/core-architecture/hostdb.en.rst
@@ -0,0 +1,191 @@
+.. 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:: ../../common.defs
+
+.. highlight:: cpp
+.. default-domain:: cpp
+
+.. _developer-doc-hostdb:
+
+HostDB
+******
+
+HostDB is a cache of DNS results. It is used to increase performance by aggregating address
+resolution across transactions. HostDB also stores state information for specific IP addresses.
+
+Operation
+=========
+
+The primary operation for HostDB is to resolve a fully qualified domain name ("FQDN"). As noted each
+FQDN is associated with a single record. Each record has an array of items. When a resolution
+request is made the database is checked to see if the record is already present. If so, it is
+served. Otherwise a DNS request is made. When the nameserver replies a record is created, added
+to the database, and then returned to the requestor.
+
+Each info tracks several status values for its corresponding upstream. These are
+
+* HTTP version
+* Last failure time
+
+The HTTP version is tracked from responses and provides a mechanism to make intelligent guesses
+about the protocol to use to the upstream.
+
+The last failure time tracks when the last connection failure to the info occurred and doubles as
+a flag, where a value of ``TS_TIME_ZERO`` indicates a live target and any other value indicates a
+dead info.
+
+If an info is marked dead (has a non-zero last failure time) there is a "fail window" during which
+no connections are permitted. After this time the info is considered to be a "zombie". If all infos
+for a record are dead then a specific error message is generated (body factory tag
+"connect#all_dead"). Otherwise if the selected info is a zombie, a request is permitted but the
+zombie is immediately marked dead again, preventing any additional requests until either the fail
+window has passed or the single connection succeeds. A successful connection clears the last file
+time and the info becomes alive.
+
+Runtime Structure
+=================
+
+DNS results are stored in a global hash table as instances of ``HostDBRecord``. Each record stores
+the results of a single query. These records are not updated with new DNS results - instead a new
+record instance is created and replaces the previous instance in the table. The records are
+reference counted so such a replacement doesn't invalidate the old record if the latter is still
+being accessed. Some specific dynamic data is migrated from the old record to the new one, such as
+the failure status of the upstreams in the record.
+
+In each record is a variable length array of items, instances of ``HostDBInfo``, one for each
+IP address in the record. This is called the "round robin" data for historical reasons. For SRV
+records there is an additional storage area in the record that is used to store the SRV names.
+
+.. figure:: HostDB-Data-Layout.svg
+
+The round robin data is accessed by using an offset and count in the base record. For SRV records
+each record has an offset, relative to that ``HostDBInfo`` instance, for its own name in the name
+storage area.
+
+State information for the outbound connection has been moved to a refurbished ``DNSInfo`` class
+named ``ResolveInfo``. As much as possible relevant state information has been moved from the
+``HttpSM`` to this structure. This is intended for future work where the state machine deals only
+with upstream transactions and not sessions.
+
+``ResolveInfo`` may contain a reference to a HostDB record, which preserves the record even if it is
+replaced due to DNS queries in other transactions. The record is not required as the resolution
+information can be supplied directly without DNS or HostDB, e.g. a plugin sets the upstream address
+explicitly. The ``resolved_p`` flag indicates if the current information is valid and ready to be
+used or not. A result of this is there is no longer a specific holder for API provided addresses -
+the interface now puts the address in the ``ResolveInfo`` and marks it as resolved. This prevents
+further DNS / HostDB lookups and the address is used as is.
+
+The upstream port is a bit tricky and should be cleaned up. Currently value in ``srv_port``
+determines the port if set. If not, then the port in ``addr`` is used.
+
+Resolution Style
+----------------
+
+.. cpp:enum:: OS_Addr
+
+ Metadata about the source of the resolved address.'
+
+ .. cpp:enumerator:: TRY_DEFAULT
+
+ Use default resolution. This is the initial state.
+
+ .. cpp:enumerator:: TRY_HOSTDB
+
+ Use HostDB to resolve the target key.
+
+ .. cpp:enumerator:: TRY_CLIENT
+
+ Use the client supplied target address. This is used for transparent connections - the upstream
+ address is obtained from the inbound connection. May fail over to HostDB.
+
+ .. cpp:enumerator:: USE_HOSTDB
+
+ Use HostDB to resolve the target key.
+
+ .. cpp:enumerator:: USE_CLIENT
+
+ Use the client supplied target address.
+
+ .. cpp:enumerator:: USE_API
+
+ Use the address provided via the plugin API.
+
+ The parallel values for using HostDB and the client target address are to control fail over on
+ connection failure. The ``TRY_`` values can fail over to another style, but the ``USE_`` values
+ cannot. This prevents cycles of style changes by having any ``TRY_`` value fail over to a
+ ``USE_`` value, at which point it can no longer change. Note there is no ``TRY_API`` - if a
+ plugin sets the upstream address that is locked in.
+
+Issues
+======
+
+Currently if an upstream is marked down connections are still permitted, the only change is the
+number of retries. This has caused operational problems where dead systems are flooded with requests
+which, despite the timeouts, accumulate in ATS until ATS runs out of memory (there were instances of
+over 800K pending transactions). This also made it hard to bring the upstreams back online. With
+these changes requests to dead upstreams are strongly rate limited and other transactions are
+immediately terminated with a 502 response, protecting both the upstream and ATS.
+
+Future
+======
+
+There is still some work to be done in future PRs.
+
+* The fail window and the zombie window should be separate values. It is quite reasonable to want
+ to configure a very short fail window (possibly 0) with a moderately long zombie window so that
+ probing connections can immediately start going upstream at a low rate.
+
+* Failing an upstream should be more loosely connected to transactions. Currently there is a one
+ to one relationship where failure is defined as the failure of a specific transaction to connect.
+ There are situations where the number of connections attempts for mark a failure is should be
+ larger than the number of retries for a single transaction. For transiently busy upstreams and
+ low latency requests it can be reasonable to tune the per transaction timeout low with no retries
+ but this then risks marking down upstreams that were merely a bit slow at a given moment.
+
+* Parallel DNS requests should be supported. This is for both cross family requests and for split
+ DNS.
+
+* It would be nice to be able to do the probing connections to an upstream using synthetic requests
+ instead of burning actual user requests. What would be needed is a handoff from ATS to the probe
+ to indicate a particular upstream is considered down, at which point active health checks are done
+ until the upstream is once again alive, at which point this is handed off back to ATS.
+
+History
+=======
+
+This version has several major architectural changes from the previous version.
+
+* The data is split into records and info, not handled as a variant of a single data type. This
+ provides a noticeable simplification of the code.
+
+* Single and multiple address results are treated identically - a singleton is simply a multiple
+ of size 1. This yeilds a major simplification of the implementation.
+
+* Connections are throttled to dead upstreams, allowing only a single connection attempt per fail
+ window timing until a connection succeeds.
+
+* Timing information is stored in ``std::chrono`` data types instead of proprietary types.
+
+* State information has been promoted to atomics and updates are immediate rather than scheduled.
+ This also means the data in the state machine is a reference to a shared object, not a local copy.
+ The promotion was necessary to coordinate zombie connections to dead upstreams across transactions.
+
+* The "resolve key" is now a separate data object from the HTTP request. This is a subtle but
+ major change. The effect is requests can be routed to different upstreams without changing
+ the request. Parent selection can be greatly simplified as it become merely a matter of setting
+ the resolve key, rather than having a completely different code path.
diff --git a/doc/developer-guide/core-architecture/index.en.rst b/doc/developer-guide/core-architecture/index.en.rst
index e88e35fb7..97f59712d 100644
--- a/doc/developer-guide/core-architecture/index.en.rst
+++ b/doc/developer-guide/core-architecture/index.en.rst
@@ -26,5 +26,6 @@ Core Architecture
:maxdepth: 1
heap.en
+ hostdb.en
rpc.en
url_rewrite_architecture.en.rst
diff --git a/doc/uml/host-resolve.plantuml b/doc/uml/host-resolve.plantuml
new file mode 100644
index 000000000..f3c6a6091
--- /dev/null
+++ b/doc/uml/host-resolve.plantuml
@@ -0,0 +1,24 @@
+' SPDX-License-Identifier: Apache-2.0
+' Licensed 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.
+
+@startuml
+
+hide empty description
+
+state HttpSM {
+ state do_http_server_open {
+ }
+}
+
+state HandleRequest #cyan
+state CallOSDNSLookup #cyan
+
+CallOSDNSLookup -> OSDNSLookup
+
+@enduml
+
diff --git a/example/plugins/c-api/protocol/TxnSM.c b/example/plugins/c-api/protocol/TxnSM.c
index 8f6ae5416..cb7f00f44 100644
--- a/example/plugins/c-api/protocol/TxnSM.c
+++ b/example/plugins/c-api/protocol/TxnSM.c
@@ -477,8 +477,6 @@ int
state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info)
{
TxnSM *txn_sm = (TxnSM *)TSContDataGet(contp);
- struct sockaddr const *q_server_addr;
- struct sockaddr_in ip_addr;
TSDebug(PLUGIN_NAME, "enter state_dns_lookup");
@@ -489,16 +487,16 @@ state_dns_lookup(TSCont contp, TSEvent event, TSHostLookupResult host_info)
txn_sm->q_pending_action = NULL;
/* Get the server IP from data structure TSHostLookupResult. */
- q_server_addr = TSHostLookupResultAddrGet(host_info);
+ struct sockaddr const *sa = TSHostLookupResultAddrGet(host_info);
/* Connect to the server using its IP. */
set_handler(txn_sm->q_current_handler, (TxnSMHandler)&state_connect_to_server);
TSAssert(txn_sm->q_pending_action == NULL);
- TSAssert(q_server_addr->sa_family == AF_INET); /* NO IPv6 in this plugin */
+ TSAssert(sa->sa_family == AF_INET); /* NO IPv6 in this plugin */
+ struct sockaddr_in *addr = (struct sockaddr_in *)(sa);
- memcpy(&ip_addr, q_server_addr, sizeof(ip_addr));
- ip_addr.sin_port = txn_sm->q_server_port;
- txn_sm->q_pending_action = TSNetConnect(contp, (struct sockaddr const *)&ip_addr);
+ addr->sin_port = txn_sm->q_server_port;
+ txn_sm->q_pending_action = TSNetConnect(contp, sa);
return TS_SUCCESS;
}
diff --git a/include/ts/ts.h b/include/ts/ts.h
index 38f1160ca..66af0983d 100644
--- a/include/ts/ts.h
+++ b/include/ts/ts.h
@@ -1950,7 +1950,13 @@ tsapi TSReturnCode TSPortDescriptorAccept(TSPortDescriptor, TSCont);
/* --------------------------------------------------------------------------
DNS Lookups */
tsapi TSAction TSHostLookup(TSCont contp, const char *hostname, size_t namelen);
+/** Retrieve an address from the host lookup.
+ *
+ * @param lookup_result Result handle passed to event callback.
+ * @return A @c sockaddr with the address if successful, a @c nullptr if not.
+ */
tsapi struct sockaddr const *TSHostLookupResultAddrGet(TSHostLookupResult lookup_result);
+
/* TODO: Eventually, we might want something like this as well, but it requires
support for building the HostDBInfo struct:
tsapi void TSHostLookupResultSet(TSHttpTxn txnp, TSHostLookupResult result);
diff --git a/include/tscore/BufferWriter.h b/include/tscore/BufferWriter.h
index 34e0b6bb5..85d0a74f8 100644
--- a/include/tscore/BufferWriter.h
+++ b/include/tscore/BufferWriter.h
@@ -854,10 +854,10 @@ std::string &
bwprintv(std::string &s, ts::TextView fmt, std::tuple<Args...> const &args)
{
auto len = s.size(); // remember initial size
- size_t n = ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).printv(fmt, std::move(args)).extent();
+ size_t n = ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).printv(fmt, args).extent();
s.resize(n); // always need to resize - if shorter, must clip pre-existing text.
if (n > len) { // dropped data, try again.
- ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).printv(fmt, std::move(args));
+ ts::FixedBufferWriter(const_cast<char *>(s.data()), s.size()).printv(fmt, args);
}
return s;
}
diff --git a/include/tscore/BufferWriterForward.h b/include/tscore/BufferWriterForward.h
index 8da67c60b..7773486b9 100644
--- a/include/tscore/BufferWriterForward.h
+++ b/include/tscore/BufferWriterForward.h
@@ -148,4 +148,11 @@ class BWFormat;
class BufferWriter;
+/// Storage for debug messages.
+/// If @c bwprint is used with this, the storage is reused which minimizes allocations.
+/// E.g.
+/// @code
+
+inline thread_local std::string bw_dbg;
+
} // namespace ts
diff --git a/include/tscore/Diags.h b/include/tscore/Diags.h
index bab84d80b..d87baec41 100644
--- a/include/tscore/Diags.h
+++ b/include/tscore/Diags.h
@@ -188,6 +188,17 @@ is_dbg_ctl_enabled(DbgCtl const &ctl)
} \
} while (false)
+// A BufferWriter version of Debug().
+#define Debug_bw(tag__, fmt, ...) \
+ do { \
+ if (unlikely(diags()->on())) { \
+ static DbgCtl Debug_bw_ctl(tag__); \
+ if (Debug_bw_ctl.ptr()->on) { \
+ DbgPrint(Debug_bw_ctl, "%s", ts::bwprint(ts::bw_dbg, fmt, __VA_ARGS__).c_str()); \
+ } \
+ } \
+ } while (false)
+
// printf-like debug output. First parameter must be tag (C-string literal, or otherwise
// a constexpr returning char const pointer to null-terminated C-string).
//
diff --git a/include/tscore/bwf_std_format.h b/include/tscore/bwf_std_format.h
index e67c858fc..cb060edd1 100644
--- a/include/tscore/bwf_std_format.h
+++ b/include/tscore/bwf_std_format.h
@@ -26,6 +26,7 @@
#include <atomic>
#include <array>
#include <string_view>
+#include <chrono>
#include "tscpp/util/TextView.h"
#include "tscore/BufferWriterForward.h"
@@ -38,6 +39,20 @@ bwformat(ts::BufferWriter &w, ts::BWFSpec const &spec, atomic<T> const &v)
return ts::bwformat(w, spec, v.load());
}
+template <typename Rep, typename Period>
+ts::BufferWriter &
+bwformat(ts::BufferWriter &w, ts::BWFSpec const &spec, chrono::duration<Rep, Period> const &d)
+{
+ return bwformat(w, spec, d.count());
+}
+
+template <typename Clock, typename Duration>
+ts::BufferWriter &
+bwformat(ts::BufferWriter &w, ts::BWFSpec const &spec, chrono::time_point<Clock, Duration> const &t)
+{
+ return bwformat(w, spec, t.time_since_epoch());
+}
+
} // end namespace std
namespace ts
@@ -130,5 +145,4 @@ namespace bwf
BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Errno const &e);
BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Date const &date);
BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::OptionalAffix const &opts);
-
} // namespace ts
diff --git a/include/tscore/ink_time.h b/include/tscore/ink_time.h
index f138bc366..1ac667c85 100644
--- a/include/tscore/ink_time.h
+++ b/include/tscore/ink_time.h
@@ -53,6 +53,7 @@ using ts_hr_time = ts_hr_clock::time_point;
using ts_seconds = std::chrono::seconds;
using ts_milliseconds = std::chrono::milliseconds;
+using ts_nanoseconds = std::chrono::nanoseconds;
/// Equivalent of 0 for @c ts_time. This should be used as the default initializer.
static constexpr ts_time TS_TIME_ZERO;
diff --git a/include/tscore/ts_file.h b/include/tscore/ts_file.h
index c4389e948..8a9eff2be 100644
--- a/include/tscore/ts_file.h
+++ b/include/tscore/ts_file.h
@@ -329,5 +329,12 @@ namespace file
/* ------------------------------------------------------------------- */
} // namespace file
+
+inline BufferWriter &
+bwformat(BufferWriter &w, BWFSpec const &spec, file::path const &path)
+{
+ return bwformat(w, spec, path.string());
+}
+
} // namespace ts
/* ------------------------------------------------------------------- */
diff --git a/iocore/dns/P_SplitDNSProcessor.h b/iocore/dns/P_SplitDNSProcessor.h
index 5fa119e5f..7424ccfdc 100644
--- a/iocore/dns/P_SplitDNSProcessor.h
+++ b/iocore/dns/P_SplitDNSProcessor.h
@@ -79,7 +79,7 @@ struct SplitDNS : public ConfigInfo {
SplitDNS();
~SplitDNS() override;
- void *getDNSRecord(const char *hostname);
+ void *getDNSRecord(ts::TextView hostname);
void findServer(RequestData *rdata, SplitDNSResult *result);
DNS_table *m_DNSSrvrTable = nullptr;
@@ -116,46 +116,34 @@ SplitDNSConfig::isSplitDNSEnabled()
class DNSRequestData : public RequestData
{
public:
- DNSRequestData();
-
- char *get_string() override;
+ DNSRequestData() = default;
+ char *
+ get_string() override
+ {
+ ink_release_assert(!"Do not get a writeable string from a DNS request");
+ };
const char *get_host() override;
sockaddr const *get_ip() override; // unused required virtual method.
sockaddr const *get_client_ip() override; // unused required virtual method.
- const char *m_pHost = nullptr;
+ ts::TextView m_pHost;
};
-/* --------------------------------------------------------------
- DNSRequestData::get_string()
- -------------------------------------------------------------- */
-TS_INLINE
-DNSRequestData::DNSRequestData() {}
-
-/* --------------------------------------------------------------
- DNSRequestData::get_string()
- -------------------------------------------------------------- */
-TS_INLINE char *
-DNSRequestData::get_string()
-{
- return ats_strdup((char *)m_pHost);
-}
-
/* --------------------------------------------------------------
DNSRequestData::get_host()
-------------------------------------------------------------- */
-TS_INLINE const char *
+inline const char *
DNSRequestData::get_host()
{
- return m_pHost;
+ return m_pHost.data();
}
/* --------------------------------------------------------------
DNSRequestData::get_ip()
-------------------------------------------------------------- */
-TS_INLINE sockaddr const *
+inline sockaddr const *
DNSRequestData::get_ip()
{
return nullptr;
@@ -164,7 +152,7 @@ DNSRequestData::get_ip()
/* --------------------------------------------------------------
DNSRequestData::get_client_ip()
-------------------------------------------------------------- */
-TS_INLINE sockaddr const *
+inline sockaddr const *
DNSRequestData::get_client_ip()
{
return nullptr;
diff --git a/iocore/dns/SRV.h b/iocore/dns/SRV.h
index ff75689e7..560223636 100644
--- a/iocore/dns/SRV.h
+++ b/iocore/dns/SRV.h
@@ -25,7 +25,6 @@
#include <vector>
#include "tscore/ink_platform.h"
-#include "I_HostDBProcessor.h"
struct HostDBInfo;
diff --git a/iocore/dns/SplitDNS.cc b/iocore/dns/SplitDNS.cc
index 802e97981..b993f1e7f 100644
--- a/iocore/dns/SplitDNS.cc
+++ b/iocore/dns/SplitDNS.cc
@@ -178,9 +178,9 @@ SplitDNSConfig::print()
SplitDNS::getDNSRecord()
-------------------------------------------------------------- */
void *
-SplitDNS::getDNSRecord(const char *hostname)
+SplitDNS::getDNSRecord(ts::TextView hostname)
{
- Debug("splitdns", "Called SplitDNS::getDNSRecord(%s)", hostname);
+ Debug("splitdns", "Called SplitDNS::getDNSRecord(%.*s)", int(hostname.size()), hostname.data());
DNSRequestData *pRD = DNSReqAllocator.alloc();
pRD->m_pHost = hostname;
@@ -191,7 +191,7 @@ SplitDNS::getDNSRecord(const char *hostname)
DNSReqAllocator.free(pRD);
if (DNS_SRVR_SPECIFIED == res.r) {
- return (void *)&(res.m_rec->m_servers);
+ return &(res.m_rec->m_servers);
}
Debug("splitdns", "Fail to match a valid splitdns rule, fallback to default dns resolver");
diff --git a/iocore/hostdb/HostDB.cc b/iocore/hostdb/HostDB.cc
index 257f35347..0095fd70f 100644
--- a/iocore/hostdb/HostDB.cc
+++ b/iocore/hostdb/HostDB.cc
@@ -26,14 +26,19 @@
#include "P_RefCountCacheSerializer.h"
#include "tscore/I_Layout.h"
#include "Show.h"
-#include "tscore/Tokenizer.h"
+#include "tscore/ts_file.h"
#include "tscore/ink_apidefs.h"
+#include "tscore/bwf_std_format.h"
#include <utility>
#include <vector>
#include <algorithm>
#include <random>
#include <chrono>
+#include <http/HttpConfig.h>
+
+using ts::TextView;
+using std::chrono::duration_cast;
HostDBProcessor hostDBProcessor;
int HostDBProcessor::hostdb_strict_round_robin = 0;
@@ -50,78 +55,216 @@ unsigned int hostdb_ip_stale_interval = HOST_DB_IP_STALE;
unsigned int hostdb_ip_timeout_interval = HOST_DB_IP_TIMEOUT;
unsigned int hostdb_ip_fail_timeout_interval = HOST_DB_IP_FAIL_TIMEOUT;
unsigned int hostdb_serve_stale_but_revalidate = 0;
-unsigned int hostdb_hostfile_check_interval = 86400; // 1 day
-// Epoch timestamp of the current hosts file check.
-ink_time_t hostdb_current_interval = 0;
+static ts_seconds hostdb_hostfile_check_interval{std::chrono::hours(24)};
+// Epoch timestamp of the current hosts file check. This also functions as a
+// cached version of ts_clock::now().
+ts_time hostdb_current_timestamp{TS_TIME_ZERO};
// Epoch timestamp of the last time we actually checked for a hosts file update.
-static ink_time_t hostdb_last_interval = 0;
+static ts_time hostdb_last_timestamp{TS_TIME_ZERO};
// Epoch timestamp when we updated the hosts file last.
-static ink_time_t hostdb_hostfile_update_timestamp = 0;
-static char hostdb_filename[PATH_NAME_MAX] = DEFAULT_HOST_DB_FILENAME;
-int hostdb_max_count = DEFAULT_HOST_DB_SIZE;
-char hostdb_hostfile_path[PATH_NAME_MAX] = "";
-int hostdb_sync_frequency = 0;
-int hostdb_disable_reverse_lookup = 0;
-int hostdb_max_iobuf_index = BUFFER_SIZE_INDEX_32K;
-
-// Verify the generic storage is sufficient to cover all alternate members.
-static_assert(sizeof(HostDBApplicationInfo::allotment) == sizeof(HostDBApplicationInfo),
- "Generic storage for HostDBApplicationInfo is smaller than the union storage.");
+static ts_time hostdb_hostfile_update_timestamp{TS_TIME_ZERO};
+static char hostdb_filename[PATH_NAME_MAX] = DEFAULT_HOST_DB_FILENAME;
+int hostdb_max_count = DEFAULT_HOST_DB_SIZE;
+static ts::file::path hostdb_hostfile_path;
+ts_seconds hostdb_sync_frequency{0};
+int hostdb_disable_reverse_lookup = 0;
+int hostdb_max_iobuf_index = BUFFER_SIZE_INDEX_32K;
ClassAllocator<HostDBContinuation> hostDBContAllocator("hostDBContAllocator");
+namespace
+{
+/** Assign raw storage to an @c IpAddr
+ *
+ * @param ip Destination.
+ * @param af IP family.
+ * @param ptr Raw data for an address of family @a af.
+ */
+void
+ip_addr_set(IpAddr &ip, ///< Target storage.
+ uint8_t af, ///< Address format.
+ void const *ptr ///< Raw address data
+)
+{
+ if (AF_INET6 == af) {
+ ip = *static_cast<in6_addr const *>(ptr);
+ } else if (AF_INET == af) {
+ ip = *static_cast<in_addr_t const *>(ptr);
+ } else {
+ ip.invalidate();
+ }
+}
+
+unsigned int
+HOSTDB_CLIENT_IP_HASH(sockaddr const *lhs, IpAddr const &rhs)
+{
+ unsigned int zret = ~static_cast<unsigned int>(0);
+ if (lhs->sa_family == rhs.family()) {
+ if (rhs.isIp4()) {
+ in_addr_t ip1 = ats_ip4_addr_cast(lhs);
+ in_addr_t ip2 = rhs._addr._ip4;
+ zret = (ip1 >> 16) ^ ip1 ^ ip2 ^ (ip2 >> 16);
+ } else if (rhs.isIp6()) {
+ uint32_t const *ip1 = ats_ip_addr32_cast(lhs);
+ uint32_t const *ip2 = rhs._addr._u32;
+ for (int i = 0; i < 4; ++i, ++ip1, ++ip2) {
+ zret ^= (*ip1 >> 16) ^ *ip1 ^ *ip2 ^ (*ip2 >> 16);
+ }
+ }
+ }
+ return zret & 0xFFFF;
+}
+
+} // namespace
+
+char const *
+name_of(HostDBType t)
+{
+ switch (t) {
+ case HostDBType::UNSPEC:
+ return "*";
+ case HostDBType::ADDR:
+ return "Address";
+ case HostDBType::SRV:
+ return "SRV";
+ case HostDBType::HOST:
+ return "Reverse DNS";
+ }
+ return "";
+}
+
+/** 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, _var);
+ }
+};
+
+ConfigDuration HostDBDownServerCacheTimeVar{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 &HostDBDownServerCacheTimeConv;
+MgmtConverter const &HostDBDownServerCacheTimeConv = HostDBDownServerCacheTimeVar.Conversions;
+
+// Not run time configurable, therefore no support beyond this class needed.
+ConfigDuration HostDBSyncFrequencyVar{hostdb_sync_frequency};
+
+void
+HostDB_Config_Init()
+{
+ HostDBDownServerCacheTimeVar.Enable("proxy.config.http.down_server.cache_time");
+ HostDBSyncFrequencyVar.Enable("proxy.config.cache.hostdb.sync_frequency");
+}
+
// Static configuration information
HostDBCache hostDB;
-void ParseHostFile(const char *path, unsigned int interval);
+void ParseHostFile(ts::file::path const &path, ts_seconds interval);
-char *
-HostDBInfo::srvname(HostDBRoundRobin *rr) const
+auto
+HostDBInfo::assign(sa_family_t af, void const *addr) -> self_type &
{
- if (!is_srv || !data.srv.srv_offset) {
- return nullptr;
- }
- return reinterpret_cast<char *>(rr) + data.srv.srv_offset;
+ type = HostDBType::ADDR;
+ ip_addr_set(data.ip, af, addr);
+ return *this;
}
-static inline bool
-is_addr_valid(uint8_t af, ///< Address family (format of data)
- void *ptr ///< Raw address data (not a sockaddr variant!)
-)
+auto
+HostDBInfo::assign(IpAddr const &addr) -> self_type &
{
- return (AF_INET == af && INADDR_ANY != *(reinterpret_cast<in_addr_t *>(ptr))) ||
- (AF_INET6 == af && !IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast<in6_addr *>(ptr)));
+ type = HostDBType::ADDR;
+ data.ip = addr;
+ return *this;
}
-static inline void
-ip_addr_set(sockaddr *ip, ///< Target storage, sockaddr compliant.
- uint8_t af, ///< Address format.
- void *ptr ///< Raw address data
-)
+auto
+HostDBInfo::assign(SRV const *srv, char const *name) -> self_type &
{
- if (AF_INET6 == af) {
- ats_ip6_set(ip, *static_cast<in6_addr *>(ptr));
- } else if (AF_INET == af) {
- ats_ip4_set(ip, *static_cast<in_addr_t *>(ptr));
- } else {
- ats_ip_invalidate(ip);
- }
+ type = HostDBType::SRV;
+ data.srv.srv_weight = srv->weight;
+ data.srv.srv_priority = srv->priority;
+ data.srv.srv_port = srv->port;
+ data.srv.key = srv->key;
+ data.srv.srv_offset = reinterpret_cast<char const *>(this) - name;
+ return *this;
+}
+
+char const *
+HostDBInfo::srvname() const
+{
+ return data.srv.srv_offset ? reinterpret_cast<char const *>(this) + data.srv.srv_offset : nullptr;
}
-static inline void
-ip_addr_set(IpAddr &ip, ///< Target storage.
- uint8_t af, ///< Address format.
- void *ptr ///< Raw address data
+static inline bool
+is_addr_valid(uint8_t af, ///< Address family (format of data)
+ void *ptr ///< Raw address data (not a sockaddr variant!)
)
{
- if (AF_INET6 == af) {
- ip = *static_cast<in6_addr *>(ptr);
- } else if (AF_INET == af) {
- ip = *static_cast<in_addr_t *>(ptr);
- } else {
- ip.invalidate();
- }
+ return (AF_INET == af && INADDR_ANY != *(reinterpret_cast<in_addr_t *>(ptr))) ||
+ (AF_INET6 == af && !IN6_IS_ADDR_UNSPECIFIED(reinterpret_cast<in6_addr *>(ptr)));
}
inline void
@@ -169,18 +312,12 @@ string_for(HostDBMark mark)
static Action *register_ShowHostDB(Continuation *c, HTTPHdr *h);
HostDBHash &
-HostDBHash::set_host(const char *name, int len)
+HostDBHash::set_host(TextView name)
{
host_name = name;
- host_len = len;
- if (host_name && SplitDNSConfig::isSplitDNSEnabled()) {
- const char *scan;
- // I think this is checking for a hostname that is just an address.
- for (scan = host_name; *scan != '\0' && (ParseRules::is_digit(*scan) || '.' == *scan || ':' == *scan); ++scan) {
- ;
- }
- if ('\0' != *scan) {
+ if (!host_name.empty() && SplitDNSConfig::isSplitDNSEnabled()) {
+ if (TS_SUCCESS != ip.load(host_name)) {
// config is released in the destructor, because we must make sure values we
// get out of it don't evaporate while @a this is still around.
if (!pSD) {
@@ -206,7 +343,7 @@ HostDBHash::refresh()
const char *server_line = dns_server ? dns_server->x_dns_ip_line : nullptr;
uint8_t m = static_cast<uint8_t>(db_mark); // be sure of the type.
- ctx.update(host_name, host_len);
+ ctx.update(host_name.data(), host_name.size());
ctx.update(reinterpret_cast<uint8_t *>(&port), sizeof(port));
ctx.update(&m, sizeof(m));
if (server_line) {
@@ -235,10 +372,7 @@ HostDBHash::~HostDBHash()
}
}
-HostDBCache::HostDBCache()
-{
- hosts_file_ptr = new RefCountedHostsFileMap();
-}
+HostDBCache::HostDBCache() {}
bool
HostDBCache::is_pending_dns_for_hash(const CryptoHash &hash)
@@ -252,6 +386,14 @@ HostDBCache::is_pending_dns_for_hash(const CryptoHash &hash)
return false;
}
+std::shared_ptr<HostFileMap>
+HostDBCache::acquire_host_file()
+{
+ std::shared_lock lock(host_file_mutex);
+ auto zret = host_file;
+ return zret;
+}
+
HostDBCache *
HostDBProcessor::cache()
{
@@ -259,16 +401,16 @@ HostDBProcessor::cache()
}
struct HostDBBackgroundTask : public Continuation {
- int frequency;
- ink_hrtime start_time;
+ ts_seconds frequency;
+ ts_hr_time start_time;
virtual int sync_event(int event, void *edata) = 0;
int wait_event(int event, void *edata);
- HostDBBackgroundTask(int frequency);
+ HostDBBackgroundTask(ts_seconds frequency);
};
-HostDBBackgroundTask::HostDBBackgroundTask(int frequency) : Continuation(new_ProxyMutex()), frequency(frequency), start_time(0)
+HostDBBackgroundTask::HostDBBackgroundTask(ts_seconds frequency) : Continuation(new_ProxyMutex()), frequency(frequency)
{
SET_HANDLER(&HostDBBackgroundTask::sync_event);
}
@@ -276,11 +418,11 @@ HostDBBackgroundTask::HostDBBackgroundTask(int frequency) : Continuation(new_Pro
int
HostDBBackgroundTask::wait_event(int, void *)
{
- ink_hrtime next_sync = HRTIME_SECONDS(this->frequency) - (Thread::get_hrtime() - start_time);
+ auto next_sync = this->frequency - (ts_hr_clock::now() - start_time);
SET_HANDLER(&HostDBBackgroundTask::sync_event);
- if (next_sync > HRTIME_MSECONDS(100)) {
- eventProcessor.schedule_in(this, next_sync, ET_TASK);
+ if (next_sync > ts_milliseconds{100}) {
+ eventProcessor.schedule_in(this, duration_cast<ts_nanoseconds>(next_sync).count(), ET_TASK);
} else {
eventProcessor.schedule_imm(this, ET_TASK);
}
@@ -290,16 +432,16 @@ HostDBBackgroundTask::wait_event(int, void *)
struct HostDBSync : public HostDBBackgroundTask {
std::string storage_path;
std::string full_path;
- HostDBSync(int frequency, const std::string &storage_path, const std::string &full_path)
+ HostDBSync(ts_seconds frequency, const std::string &storage_path, const std::string &full_path)
: HostDBBackgroundTask(frequency), storage_path(std::move(storage_path)), full_path(std::move(full_path)){};
int
sync_event(int, void *) override
{
SET_HANDLER(&HostDBSync::wait_event);
- start_time = Thread::get_hrtime();
+ start_time = ts_hr_clock::now();
- new RefCountCacheSerializer<HostDBInfo>(this, hostDBProcessor.cache()->refcountcache, this->frequency, this->storage_path,
- this->full_path);
+ new RefCountCacheSerializer<HostDBRecord>(this, hostDBProcessor.cache()->refcountcache, this->frequency.count(),
+ this->storage_path, this->full_path);
return EVENT_DONE;
}
};
@@ -327,8 +469,6 @@ HostDBCache::start(int flags)
REC_ReadConfigInteger(hostdb_max_size, "proxy.config.hostdb.max_size");
// number of partitions
REC_ReadConfigInt32(hostdb_partitions, "proxy.config.hostdb.partitions");
- // how often to sync hostdb to disk
- REC_EstablishStaticConfigInt32(hostdb_sync_frequency, "proxy.config.cache.hostdb.sync_frequency");
REC_EstablishStaticConfigInt32(hostdb_max_iobuf_index, "proxy.config.hostdb.io.max_buffer_index");
@@ -337,13 +477,13 @@ HostDBCache::start(int flags)
}
// Setup the ref-counted cache (this must be done regardless of syncing or not).
- this->refcountcache = new RefCountCache<HostDBInfo>(hostdb_partitions, hostdb_max_size, hostdb_max_count, HostDBInfo::version(),
- "proxy.process.hostdb.cache.");
+ this->refcountcache = new RefCountCache<HostDBRecord>(hostdb_partitions, hostdb_max_size, hostdb_max_count, HostDBRecord::Version,
+ "proxy.process.hostdb.cache.");
//
// Load and sync HostDB, if we've asked for it.
//
- if (hostdb_sync_frequency > 0) {
+ if (hostdb_sync_frequency.count() > 0) {
// If proxy.config.hostdb.storage_path is not set, use the local state dir. If it is set to
// a relative path, make it relative to the prefix.
if (storage_path[0] == '\0') {
@@ -366,7 +506,7 @@ HostDBCache::start(int flags)
Debug("hostdb", "Opening %s, partitions=%d storage_size=%" PRIu64 " items=%d", full_path, hostdb_partitions, hostdb_max_size,
hostdb_max_count);
- int load_ret = LoadRefCountCacheFromPath<HostDBInfo>(*this->refcountcache, full_path, HostDBInfo::unmarshall);
+ int load_ret = LoadRefCountCacheFromPath<HostDBRecord>(*this->refcountcache, full_path, HostDBRecord::unmarshall);
if (load_ret != 0) {
Warning("Error loading cache from %s: %d", full_path, load_ret);
}
@@ -411,13 +551,13 @@ HostDBProcessor::start(int, size_t)
REC_EstablishStaticConfigInt32U(hostdb_ip_stale_interval, "proxy.config.hostdb.verify_after");
REC_EstablishStaticConfigInt32U(hostdb_ip_fail_timeout_interval, "proxy.config.hostdb.fail.timeout");
REC_EstablishStaticConfigInt32U(hostdb_serve_stale_but_revalidate, "proxy.config.hostdb.serve_stale_for");
- REC_EstablishStaticConfigInt32U(hostdb_hostfile_check_interval, "proxy.config.hostdb.host_file.interval");
REC_EstablishStaticConfigInt32U(hostdb_round_robin_max_count, "proxy.config.hostdb.round_robin_max_count");
//
- // Set up hostdb_current_interval
+ // Initialize hostdb_current_timestamp which is our cached version of
+ // ts_clock::now().
//
- hostdb_current_interval = ink_time();
+ hostdb_current_timestamp = ts_clock::now();
HostDBContinuation *b = hostDBContAllocator.alloc();
SET_CONTINUATION_HANDLER(b, (HostDBContHandler)&HostDBContinuation::backgroundEvent);
@@ -430,18 +570,14 @@ HostDBProcessor::start(int, size_t)
void
HostDBContinuation::init(HostDBHash const &the_hash, Options const &opt)
{
- hash = the_hash;
- if (hash.host_name) {
+ hash = the_hash;
+ hash.host_name = hash.host_name.prefix(static_cast<int>(sizeof(hash_host_name_store) - 1));
+ if (!hash.host_name.empty()) {
// copy to backing store.
- if (hash.host_len > static_cast<int>(sizeof(hash_host_name_store) - 1)) {
- hash.host_len = sizeof(hash_host_name_store) - 1;
- }
- memcpy(hash_host_name_store, hash.host_name, hash.host_len);
- } else {
- hash.host_len = 0;
+ memcpy(hash_host_name_store, hash.host_name);
}
- hash_host_name_store[hash.host_len] = 0;
- hash.host_name = hash_host_name_store;
+ hash_host_name_store[hash.host_name.size()] = 0;
+ hash.host_name.assign(hash_host_name_store, hash.host_name.size());
host_res_style = opt.host_res_style;
dns_lookup_timeout = opt.timeout;
@@ -460,7 +596,7 @@ HostDBContinuation::refresh_hash()
{
Ptr<ProxyMutex> old_bucket_mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold());
// We're not pending DNS anymore.
- remove_trigger_pending_dns();
+ remove_and_trigger_pending_dns();
hash.refresh();
// Update the mutex if it's from the bucket.
// Some call sites modify this after calling @c init so need to check.
@@ -470,34 +606,22 @@ HostDBContinuation::refresh_hash()
}
static bool
-reply_to_cont(Continuation *cont, HostDBInfo *r, bool is_srv = false)
+reply_to_cont(Continuation *cont, HostDBRecord *r, bool is_srv = false)
{
- if (r == nullptr || r->is_srv != is_srv || r->is_failed()) {
+ if (r == nullptr || r->is_srv() != is_srv || r->is_failed()) {
cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr);
return false;
}
- if (r->reverse_dns) {
- if (!r->hostname()) {
+ if (r->record_type != HostDBType::HOST) {
+ if (!r->name()) {
ink_assert(!"missing hostname");
cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr);
Warning("bogus entry deleted from HostDB: missing hostname");
hostDB.refcountcache->erase(r->key);
return false;
}
- Debug("hostdb", "hostname = %s", r->hostname());
- }
-
- if (!r->is_srv && r->round_robin) {
- if (!r->rr()) {
- ink_assert(!"missing round-robin");
- cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, nullptr);
- Warning("bogus entry deleted from HostDB: missing round-robin");
- hostDB.refcountcache->erase(r->key);
- return false;
- }
- ip_text_buffer ipb;
- Debug("hostdb", "RR of %d with %d good, 1st IP = %s", r->rr()->rrcount, r->rr()->good, ats_ip_ntop(r->ip(), ipb, sizeof ipb));
+ Debug("hostdb", "hostname = %s", r->name());
}
cont->handleEvent(is_srv ? EVENT_SRV_LOOKUP : EVENT_HOST_DB_LOOKUP, r);
@@ -541,74 +665,58 @@ db_mark_for(IpAddr const &ip)
return ip.isIp6() ? HOSTDB_MARK_IPV6 : HOSTDB_MARK_IPV4;
}
-Ptr<HostDBInfo>
+HostDBRecord::Handle
probe(const Ptr<ProxyMutex> &mutex, HostDBHash const &hash, bool ignore_timeout)
{
+ static const Ptr<HostDBRecord> NO_RECORD;
+
// If hostdb is disabled, don't return anything
if (!hostdb_enable) {
- return Ptr<HostDBInfo>();
+ return NO_RECORD;
}
// Otherwise HostDB is enabled, so we'll do our thing
ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding);
uint64_t folded_hash = hash.hash.fold();
- // get the item from cache
- Ptr<HostDBInfo> r = hostDB.refcountcache->get(folded_hash);
+ // get the record from cache
+ Ptr<HostDBRecord> record = hostDB.refcountcache->get(folded_hash);
// If there was nothing in the cache-- this is a miss
- if (r.get() == nullptr) {
- return r;
+ if (record.get() == nullptr) {
+ return record;
}
// If the dns response was failed, and we've hit the failed timeout, lets stop returning it
- if (r->is_failed() && r->is_ip_fail_timeout()) {
- return make_ptr((HostDBInfo *)nullptr);
- // if we aren't ignoring timeouts, and we are past it-- then remove the item
- } else if (!ignore_timeout && r->is_ip_timeout() && !r->serve_stale_but_revalidate()) {
+ if (record->is_failed() && record->is_ip_fail_timeout()) {
+ return NO_RECORD;
+ // if we aren't ignoring timeouts, and we are past it-- then remove the record
+ } else if (!ignore_timeout && record->is_ip_timeout() && !record->serve_stale_but_revalidate()) {
HOSTDB_INCREMENT_DYN_STAT(hostdb_ttl_expires_stat);
- return make_ptr((HostDBInfo *)nullptr);
+ return NO_RECORD;
}
// If the record is stale, but we want to revalidate-- lets start that up
- if ((!ignore_timeout && r->is_ip_stale() && !r->reverse_dns) || (r->is_ip_timeout() && r->serve_stale_but_revalidate())) {
+ if ((!ignore_timeout && record->is_ip_configured_stale() && record->record_type != HostDBType::HOST) ||
+ (record->is_ip_timeout() && record->serve_stale_but_revalidate())) {
HOSTDB_INCREMENT_DYN_STAT(hostdb_total_serve_stale_stat);
if (hostDB.is_pending_dns_for_hash(hash.hash)) {
- Debug("hostdb", "stale %u %u %u, using it and pending to refresh it", r->ip_interval(), r->ip_timestamp,
- r->ip_timeout_interval);
- return r;
- }
- Debug("hostdb", "stale %u %u %u, using it and refreshing it", r->ip_interval(), r->ip_timestamp, r->ip_timeout_interval);
+ Debug("hostdb", "%s",
+ ts::bwprint(ts::bw_dbg, "stale {} {} {}, using with pending refresh", record->ip_age(),
+ record->ip_timestamp.time_since_epoch(), record->ip_timeout_interval)
+ .c_str());
+ return record;
+ }
+ Debug("hostdb", "%s",
+ ts::bwprint(ts::bw_dbg, "stale {} {} {}, using while refresh", record->ip_age(), record->ip_timestamp.time_since_epoch(),
+ record->ip_timeout_interval)
+ .c_str());
HostDBContinuation *c = hostDBContAllocator.alloc();
HostDBContinuation::Options copt;
- copt.host_res_style = host_res_style_for(r->ip());
+ copt.host_res_style = record->af_family == AF_INET6 ? HOST_RES_IPV6_ONLY : HOST_RES_IPV4_ONLY;
c->init(hash, copt);
c->do_dns();
}
- return r;
-}
-
-//
-// Insert a HostDBInfo into the database
-// A null value indicates that the block is empty.
-//
-HostDBInfo *
-HostDBContinuation::insert(unsigned int attl)
-{
- uint64_t folded_hash = hash.hash.fold();
-
- ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(folded_hash)->thread_holding);
-
- HostDBInfo *r = HostDBInfo::alloc();
- r->key = folded_hash;
-
- r->ip_timestamp = hostdb_current_interval;
- r->ip_timeout_interval = std::clamp(attl, 1u, HOST_DB_MAX_TTL);
-
- Debug("hostdb", "inserting for: %.*s: (hash: %" PRIx64 ") now: %u timeout: %u ttl: %u", hash.host_len, hash.host_name,
- folded_hash, r->ip_timestamp, r->ip_timeout_interval, attl);
-
- hostDB.refcountcache->put(folded_hash, r, 0, r->expiry_time());
- return r;
+ return record;
}
//
@@ -659,7 +767,7 @@ HostDBProcessor::getby(Continuation *cont, cb_process_result_pfn cb_process_resu
MUTEX_TRY_LOCK(lock2, bucket_mutex, thread);
if (lock2.is_locked()) {
// If we can get the lock and a level 1 probe succeeds, return
- Ptr<HostDBInfo> r = probe(bucket_mutex, hash, false);
+ HostDBRecord::Handle r = probe(bucket_mutex, hash, false);
if (r) {
// fail, see if we should retry with alternate
if (hash.db_mark != HOSTDB_MARK_SRV && r->is_failed() && hash.host_name) {
@@ -668,10 +776,10 @@ HostDBProcessor::getby(Continuation *cont, cb_process_result_pfn cb_process_resu
if (!loop) {
// No retry -> final result. Return it.
if (hash.db_mark == HOSTDB_MARK_SRV) {
- Debug("hostdb", "immediate SRV answer for %.*s from hostdb", hash.host_len, hash.host_name);
- Debug("dns_srv", "immediate SRV answer for %.*s from hostdb", hash.host_len, hash.host_name);
+ Debug("hostdb", "immediate SRV answer for %.*s from hostdb", int(hash.host_name.size()), hash.host_name.data());
+ Debug("dns_srv", "immediate SRV answer for %.*s from hostdb", int(hash.host_name.size()), hash.host_name.data());
} else if (hash.host_name) {
- Debug("hostdb", "immediate answer for %.*s", hash.host_len, hash.host_name);
+ Debug("hostdb", "immediate answer for %.*s", int(hash.host_name.size()), hash.host_name.data());
} else {
Debug("hostdb", "immediate answer for %s", hash.ip.isValid() ? hash.ip.toString(ipb, sizeof ipb) : "<null>");
}
@@ -689,12 +797,13 @@ HostDBProcessor::getby(Continuation *cont, cb_process_result_pfn cb_process_resu
}
}
if (hash.db_mark == HOSTDB_MARK_SRV) {
- Debug("hostdb", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, hash.host_len, hash.host_name,
- opt.timeout);
- Debug("dns_srv", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, hash.host_len, hash.host_name,
- opt.timeout);
+ Debug("hostdb", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, int(hash.host_name.size()),
+ hash.host_name.data(), opt.timeout);
+ Debug("dns_srv", "delaying (force=%d) SRV answer for %.*s [timeout = %d]", force_dns, int(hash.host_name.size()),
+ hash.host_name.data(), opt.timeout);
} else if (hash.host_name) {
- Debug("hostdb", "delaying (force=%d) answer for %.*s [timeout %d]", force_dns, hash.host_len, hash.host_name, opt.timeout);
+ Debug("hostdb", "delaying (force=%d) answer for %.*s [timeout %d]", force_dns, int(hash.host_name.size()),
+ hash.host_name.data(), opt.timeout);
} else {
Debug("hostdb", "delaying (force=%d) answer for %s [timeout %d]", force_dns,
hash.ip.isValid() ? hash.ip.toString(ipb, sizeof ipb) : "<null>", opt.timeout);
@@ -727,7 +836,7 @@ HostDBProcessor::getbyname_re(Continuation *cont, const char *ahostname, int len
ink_assert(nullptr != ahostname);
// Load the hash data.
- hash.set_host(ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0);
+ hash.set_host({ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0});
// Leave hash.ip invalid
hash.port = 0;
hash.db_mark = db_mark_for(opt.host_res_style);
@@ -744,7 +853,7 @@ HostDBProcessor::getbynameport_re(Continuation *cont, const char *ahostname, int
ink_assert(nullptr != ahostname);
// Load the hash data.
- hash.set_host(ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0);
+ hash.set_host({ahostname, ahostname ? (len ? len : strlen(ahostname)) : 0});
// Leave hash.ip invalid
hash.port = opt.port;
hash.db_mark = db_mark_for(opt.host_res_style);
@@ -783,7 +892,7 @@ HostDBProcessor::getSRVbyname_imm(Continuation *cont, cb_process_result_pfn proc
ink_assert(nullptr != hostname);
- hash.set_host(hostname, len ? len : strlen(hostname));
+ hash.set_host({hostname, len ? len : strlen(hostname)});
// Leave hash.ip invalid
hash.port = 0;
hash.db_mark = HOSTDB_MARK_SRV;
@@ -803,7 +912,7 @@ HostDBProcessor::getbyname_imm(Continuation *cont, cb_process_result_pfn process
ink_assert(nullptr != hostname);
- hash.set_host(hostname, len ? len : strlen(hostname));
+ hash.set_host({hostname, len ? len : strlen(hostname)});
// Leave hash.ip invalid
// TODO: May I rename the wrapper name to getbynameport_imm ? - oknet
// By comparing getbyname_re and getbynameport_re, the hash.port should be 0 if only get hostinfo by name.
@@ -838,150 +947,35 @@ HostDBProcessor::iterate(Continuation *cont)
return &c->action;
}
-static void
-do_setby(HostDBInfo *r, HostDBApplicationInfo *app, const char *hostname, IpAddr const &ip, bool is_srv = false)
-{
- HostDBRoundRobin *rr = r->rr();
-
- if (is_srv && (!r->is_srv || !rr)) {
- return;
- }
-
- if (rr) {
- if (is_srv) {
- uint32_t key = makeHostHash(hostname);
- for (int i = 0; i < rr->rrcount; i++) {
- if (key == rr->info(i).data.srv.key && !strcmp(hostname, rr->info(i).srvname(rr))) {
- Debug("hostdb", "immediate setby for %s", hostname);
- rr->info(i).app.allotment.application1 = app->allotment.application1;
- rr->info(i).app.allotment.application2 = app->allotment.application2;
- return;
- }
- }
- } else {
- for (int i = 0; i < rr->rrcount; i++) {
- if (rr->info(i).ip() == ip) {
- Debug("hostdb", "immediate setby for %s", hostname ? hostname : "<addr>");
- rr->info(i).app.allotment.application1 = app->allotment.application1;
- rr->info(i).app.allotment.application2 = app->allotment.application2;
- return;
- }
- }
- }
- } else {
- if (r->reverse_dns || (!r->round_robin && ip == r->ip())) {
- Debug("hostdb", "immediate setby for %s", hostname ? hostname : "<addr>");
- r->app.allotment.application1 = app->allotment.application1;
- r->app.allotment.application2 = app->allotment.application2;
- }
- }
-}
-
-void
-HostDBProcessor::setby(const char *hostname, int len, sockaddr const *ip, HostDBApplicationInfo *app)
-{
- if (!hostdb_enable) {
- return;
- }
-
- HostDBHash hash;
- hash.set_host(hostname, hostname ? (len ? len : strlen(hostname)) : 0);
- hash.ip.assign(ip);
- hash.port = ip ? ats_ip_port_host_order(ip) : 0;
- hash.db_mark = db_mark_for(ip);
- hash.refresh();
-
- // Attempt to find the result in-line, for level 1 hits
-
- Ptr<ProxyMutex> mutex = hostDB.refcountcache->lock_for_key(hash.hash.fold());
- EThread *thread = this_ethread();
- MUTEX_TRY_LOCK(lock, mutex, thread);
-
- if (lock.is_locked()) {
- Ptr<HostDBInfo> r = probe(mutex, hash, false);
- if (r) {
- do_setby(r.get(), app, hostname, hash.ip);
- }
- return;
- }
- // Create a continuation to do a deeper probe in the background
-
- HostDBContinuation *c = hostDBContAllocator.alloc();
- c->init(hash);
- c->app.allotment.application1 = app->allotment.application1;
- c->app.allotment.application2 = app->allotment.application2;
- SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::setbyEvent);
- thread->schedule_in(c, MUTEX_RETRY_DELAY);
-}
-
-void
-HostDBProcessor::setby_srv(const char *hostname, int len, const char *target, HostDBApplicationInfo *app)
-{
- if (!hostdb_enable || !hostname || !target) {
- return;
- }
-
- HostDBHash hash;
- hash.set_host(hostname, len ? len : strlen(hostname));
- hash.port = 0;
- hash.db_mark = HOSTDB_MARK_SRV;
- hash.refresh();
-
- // Create a continuation to do a deeper probe in the background
-
- HostDBContinuation *c = hostDBContAllocator.alloc();
- c->init(hash);
- ink_strlcpy(c->srv_target_name, target, MAXDNAME);
- c->app.allotment.application1 = app->allotment.application1;
- c->app.allotment.application2 = app->allotment.application2;
- SET_CONTINUATION_HANDLER(c, (HostDBContHandler)&HostDBContinuation::setbyEvent);
- eventProcessor.schedule_imm(c);
-}
-int
-HostDBContinuation::setbyEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
-{
- Ptr<HostDBInfo> r = probe(mutex, hash, false);
-
- if (r) {
- do_setby(r.get(), &app, hash.host_name, hash.ip, is_srv());
- }
-
- hostdb_cont_free(this);
- return EVENT_DONE;
-}
-
// Lookup done, insert into the local table, return data to the
// calling continuation.
// NOTE: if "i" exists it means we already allocated the space etc, just return
//
-HostDBInfo *
-HostDBContinuation::lookup_done(IpAddr const &ip, const char *aname, bool around_robin, unsigned int ttl_seconds, SRVHosts *srv,
- HostDBInfo *r)
+Ptr<HostDBRecord>
+HostDBContinuation::lookup_done(TextView query_name, ts_seconds answer_ttl, SRVHosts *srv, Ptr<HostDBRecord> record)
{
ink_assert(this_ethread() == hostDB.refcountcache->lock_for_key(hash.hash.fold())->thread_holding);
- if (!ip.isValid() || !aname || !aname[0]) {
+ ink_assert(record);
+ if (query_name.empty()) {
if (is_byname()) {
- Debug("hostdb", "lookup_done() failed for '%.*s'", hash.host_len, hash.host_name);
+ Debug("hostdb", "lookup_done() failed for '%.*s'", int(hash.host_name.size()), hash.host_name.data());
} else if (is_srv()) {
- Debug("dns_srv", "SRV failed for '%.*s'", hash.host_len, hash.host_name);
+ Debug("dns_srv", "SRV failed for '%.*s'", int(hash.host_name.size()), hash.host_name.data());
} else {
ip_text_buffer b;
Debug("hostdb", "failed for %s", hash.ip.toString(b, sizeof b));
}
- if (r == nullptr) {
- r = insert(hostdb_ip_fail_timeout_interval);
- } else {
- r->ip_timestamp = hostdb_current_interval;
- r->ip_timeout_interval = std::clamp(hostdb_ip_fail_timeout_interval, 1u, HOST_DB_MAX_TTL);
- }
+ record->ip_timestamp = hostdb_current_timestamp;
+ record->ip_timeout_interval = ts_seconds(std::clamp(hostdb_ip_fail_timeout_interval, 1u, HOST_DB_MAX_TTL));
- r->round_robin = false;
- r->round_robin_elt = false;
- r->is_srv = is_srv();
- r->reverse_dns = !is_byname() && !is_srv();
+ if (is_srv()) {
+ record->record_type = HostDBType::SRV;
+ } else if (!is_byname()) {
+ record->record_type = HostDBType::HOST;
+ }
- r->set_failed();
- return r;
+ record->set_failed();
+ return record;
} else {
switch (hostdb_ttl_mode) {
@@ -990,65 +984,38 @@ HostDBContinuation::lookup_done(IpAddr const &ip, const char *aname, bool around
case TTL_OBEY:
break;
case TTL_IGNORE:
- ttl_seconds = hostdb_ip_timeout_interval;
+ answer_ttl = ts_seconds(hostdb_ip_timeout_interval);
break;
case TTL_MIN:
- if (hostdb_ip_timeout_interval < ttl_seconds) {
- ttl_seconds = hostdb_ip_timeout_interval;
+ if (ts_seconds(hostdb_ip_timeout_interval) < answer_ttl) {
+ answer_ttl = ts_seconds(hostdb_ip_timeout_interval);
}
break;
case TTL_MAX:
- if (hostdb_ip_timeout_interval > ttl_seconds) {
- ttl_seconds = hostdb_ip_timeout_interval;
+ if (ts_seconds(hostdb_ip_timeout_interval) > answer_ttl) {
+ answer_ttl = ts_seconds(hostdb_ip_timeout_interval);
}
break;
}
- HOSTDB_SUM_DYN_STAT(hostdb_ttl_stat, ttl_seconds);
+ HOSTDB_SUM_DYN_STAT(hostdb_ttl_stat, answer_ttl.count());
- if (r == nullptr) {
- r = insert(ttl_seconds);
- } else {
- // update the TTL
- r->ip_timestamp = hostdb_current_interval;
- r->ip_timeout_interval = std::clamp(ttl_seconds, 1u, HOST_DB_MAX_TTL);
- }
+ // update the TTL
+ record->ip_timestamp = hostdb_current_timestamp;
+ record->ip_timeout_interval = std::clamp(answer_ttl, ts_seconds(1), ts_seconds(HOST_DB_MAX_TTL));
- r->round_robin_elt = false; // only true for elements explicitly added as RR elements.
if (is_byname()) {
- ip_text_buffer b;
- Debug("hostdb", "done %s TTL %d", ip.toString(b, sizeof b), ttl_seconds);
- ats_ip_set(r->ip(), ip);
- r->round_robin = around_robin;
- r->reverse_dns = false;
- if (hash.host_name != aname) {
- ink_strlcpy(hash_host_name_store, aname, sizeof(hash_host_name_store));
- }
- r->is_srv = false;
+ Debug_bw("hostdb", "done {} TTL {}", hash.host_name, answer_ttl);
} else if (is_srv()) {
- ink_assert(srv && srv->hosts.size() && srv->hosts.size() <= hostdb_round_robin_max_count && around_robin);
-
- r->data.srv.srv_offset = srv->hosts.size();
- r->reverse_dns = false;
- r->is_srv = true;
- r->round_robin = around_robin;
-
- if (hash.host_name != aname) {
- ink_strlcpy(hash_host_name_store, aname, sizeof(hash_host_name_store));
- }
+ ink_assert(srv && srv->hosts.size() && srv->hosts.size() <= hostdb_round_robin_max_count);
+ record->record_type = HostDBType::SRV;
} else {
- Debug("hostdb", "done '%s' TTL %d", aname, ttl_seconds);
- // TODO: check that this is right, it seems that the 2 hostnames are always the same
- r->data.hostname_offset = r->hostname_offset;
- // TODO: consolidate into a single "item type" field?
- r->round_robin = false;
- r->reverse_dns = true;
- r->is_srv = false;
+ Debug_bw("hostdb", "done {} TTL {}", hash.host_name, answer_ttl);
+ record->record_type = HostDBType::HOST;
}
}
- ink_assert(!r->round_robin || !r->reverse_dns);
- return r;
+ return record;
}
int
@@ -1078,28 +1045,7 @@ HostDBContinuation::dnsPendingEvent(int event, Event *e)
}
}
-// for a new HostDBInfo `r`, "inherit" from the old version of yourself if it exists in `old_rr_data`
-static int
-restore_info(HostDBInfo *r, HostDBInfo *old_r, HostDBInfo &old_info, HostDBRoundRobin *old_rr_data)
-{
- if (old_rr_data) {
- for (int j = 0; j < old_rr_data->rrcount; j++) {
- if (ats_ip_addr_eq(old_rr_data->info(j).ip(), r->ip())) {
- r->app = old_rr_data->info(j).app;
- return true;
- }
- }
- } else if (old_r) {
- if (ats_ip_addr_eq(old_info.ip(), r->ip())) {
- r->app = old_info.app;
- return true;
- }
- }
- return false;
-}
-
// DNS lookup result state
-//
int
HostDBContinuation::dnsEvent(int event, HostEnt *e)
{
@@ -1118,7 +1064,7 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e)
// actual DNS query. If the request rate is high enough this can cause a persistent queue where the
// DNS query is never sent and all requests timeout, even if it was a transient error.
// See issue #8417.
- remove_trigger_pending_dns();
+ remove_and_trigger_pending_dns();
} else {
// "local" signal to give up, usually due this being one of those "other" queries.
// That generally means @a this has already been removed from the queue, but just in case...
@@ -1144,38 +1090,25 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e)
} else {
bool failed = !e || !e->good;
- bool is_rr = false;
pending_action = nullptr;
- if (is_srv()) {
- is_rr = !failed && (e->srv_hosts.hosts.size() > 0);
- } else if (!failed) {
- is_rr = nullptr != e->ent.h_addr_list[1];
- } else {
- }
-
- ttl = failed ? 0 : e->ttl / 60;
- int ttl_seconds = failed ? 0 : e->ttl; // ebalsa: moving to second accuracy
+ ttl = ts_seconds(failed ? 0 : e->ttl);
- Ptr<HostDBInfo> old_r = probe(mutex, hash, false);
+ Ptr<HostDBRecord> old_r = probe(mutex, hash, false);
// If the DNS lookup failed with NXDOMAIN, remove the old record
if (e && e->isNameError() && old_r) {
hostDB.refcountcache->erase(old_r->key);
old_r = nullptr;
Debug("hostdb", "Removing the old record when the DNS lookup failed with NXDOMAIN");
}
- HostDBInfo old_info;
- if (old_r) {
- old_info = *old_r.get();
- }
- HostDBRoundRobin *old_rr_data = old_r ? old_r->rr() : nullptr;
- int valid_records = 0;
- void *first_record = nullptr;
- uint8_t af = e ? e->ent.h_addrtype : AF_UNSPEC; // address family
- // if this is an RR response, we need to find the first record, as well as the
- // total number of records
- if (is_rr) {
- if (is_srv() && !failed) {
+
+ int valid_records = 0;
+ void *first_record = nullptr;
+ sa_family_t af = e ? e->ent.h_addrtype : AF_UNSPEC; // address family
+
+ // Find the first record and total number of records.
+ if (!failed) {
+ if (is_srv()) {
valid_records = e->srv_hosts.hosts.size();
} else {
void *ptr; // tmp for current entry.
@@ -1195,160 +1128,92 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e)
++valid_records;
} else {
- Warning("Zero address removed from round-robin list for '%s'", hash.host_name);
+ Warning("Invalid address removed for '%.*s'", int(hash.host_name.size()), hash.host_name.data());
}
}
if (!first_record) {
failed = true;
- is_rr = false;
}
}
- } else if (!failed) {
- first_record = e->ent.h_addr_list[0];
- } // else first is 0.
-
- IpAddr tip; // temp storage if needed.
+ } // else first is nullptr
// In the event that the lookup failed (SOA response-- for example) we want to use hash.host_name, since it'll be ""
- const char *aname = (failed || strlen(hash.host_name)) ? hash.host_name : e->ent.h_name;
-
- const size_t s_size = strlen(aname) + 1;
- const size_t rrsize = is_rr ? HostDBRoundRobin::size(valid_records, e->srv_hosts.srv_hosts_length) : 0;
- // where in our block of memory we are
- int offset = sizeof(HostDBInfo);
-
- int allocSize = s_size + rrsize; // The extra space we need for the rest of the things
-
- HostDBInfo *r = HostDBInfo::alloc(allocSize);
- Debug("hostdb", "allocating %d bytes for %s with %d RR records at [%p]", allocSize, aname, valid_records, r);
- // set up the record
- r->key = hash.hash.fold(); // always set the key
-
- r->hostname_offset = offset;
- ink_strlcpy(r->perm_hostname(), aname, s_size);
- offset += s_size;
+ TextView query_name = (failed || !hash.host_name.empty()) ? hash.host_name : TextView{e->ent.h_name, strlen(e->ent.h_name)};
+ HostDBRecord::Handle r{HostDBRecord::alloc(query_name, valid_records, failed ? 0 : e->srv_hosts.srv_hosts_length)};
+ r->key = hash.hash.fold(); // always set the key
+ r->af_family = af;
+ r->flags.f.failed_p = failed;
// If the DNS lookup failed (errors such as SERVFAIL, etc.) but we have an old record
// which is okay with being served stale-- lets continue to serve the stale record as long as
// the record is willing to be served.
bool serve_stale = false;
if (failed && old_r && old_r->serve_stale_but_revalidate()) {
- r->free();
- r = old_r.get();
+ r = old_r;
serve_stale = true;
} else if (is_byname()) {
- if (first_record) {
- ip_addr_set(tip, af, first_record);
- }
- r = lookup_done(tip, hash.host_name, is_rr, ttl_seconds, failed ? nullptr : &e->srv_hosts, r);
+ lookup_done(hash.host_name, ttl, failed ? nullptr : &e->srv_hosts, r);
} else if (is_srv()) {
- if (!failed) {
- tip._family = AF_INET; // force the tip valid, or else the srv will fail
- }
- r = lookup_done(tip, /* junk: FIXME: is the code in lookup_done() wrong to NEED this? */
- hash.host_name, /* hostname */
- is_rr, /* is round robin, doesnt matter for SRV since we recheck getCount() inside lookup_done() */
- ttl_seconds, /* ttl in seconds */
- failed ? nullptr : &e->srv_hosts, r);
+ lookup_done(hash.host_name, /* hostname */
+ ttl, /* ttl in seconds */
+ failed ? nullptr : &e->srv_hosts, r);
} else if (failed) {
- r = lookup_done(tip, hash.host_name, false, ttl_seconds, nullptr, r);
+ lookup_done(hash.host_name, ttl, nullptr, r);
} else {
- r = lookup_done(hash.ip, e->ent.h_name, false, ttl_seconds, &e->srv_hosts, r);
+ lookup_done(e->ent.h_name, ttl, &e->srv_hosts, r);
}
- // Conditionally make rr record entries
- if (is_rr) {
- r->app.rr.offset = offset;
- // This will only be set if is_rr
- HostDBRoundRobin *rr_data = static_cast<HostDBRoundRobin *>(r->rr());
- ;
+ if (!failed) { // implies r != old_r
+ auto rr_info = r->rr_info();
+ // Fill in record type specific data.
if (is_srv()) {
- int skip = 0;
- char *pos = reinterpret_cast<char *>(rr_data) + sizeof(HostDBRoundRobin) + valid_records * sizeof(HostDBInfo);
+ char *pos = rr_info.rebind<char>().end();
SRV *q[valid_records];
ink_assert(valid_records <= (int)hostdb_round_robin_max_count);
- // sort
for (int i = 0; i < valid_records; ++i) {
q[i] = &e->srv_hosts.hosts[i];
}
- for (int i = 0; i < valid_records; ++i) {
- for (int ii = i + 1; ii < valid_records; ++ii) {
- if (*q[ii] < *q[i]) {
- SRV *tmp = q[i];
- q[i] = q[ii];
- q[ii] = tmp;
- }
- }
- }
-
- rr_data->good = rr_data->rrcount = valid_records;
- rr_data->current = 0;
- for (int i = 0; i < valid_records; ++i) {
- SRV *t = q[i];
- HostDBInfo &item = rr_data->info(i);
- item.round_robin = 0;
- item.round_robin_elt = 1;
- item.reverse_dns = 0;
- item.is_srv = 1;
- item.data.srv.srv_weight = t->weight;
- item.data.srv.srv_priority = t->priority;
- item.data.srv.srv_port = t->port;
- item.data.srv.key = t->key;
-
- ink_assert((skip + t->host_len) <= e->srv_hosts.srv_hosts_length);
-
- memcpy(pos + skip, t->host, t->host_len);
- item.data.srv.srv_offset = (pos - reinterpret_cast<char *>(rr_data)) + skip;
-
- skip += t->host_len;
-
- item.app.allotment.application1 = 0;
- item.app.allotment.application2 = 0;
- Debug("dns_srv", "inserted SRV RR record [%s] into HostDB with TTL: %d seconds", t->host, ttl_seconds);
- }
-
- // restore
- if (old_rr_data) {
- for (int i = 0; i < rr_data->rrcount; ++i) {
- for (int ii = 0; ii < old_rr_data->rrcount; ++ii) {
- if (rr_data->info(i).data.srv.key == old_rr_data->info(ii).data.srv.key) {
- char *new_host = rr_data->info(i).srvname(rr_data);
- char *old_host = old_rr_data->info(ii).srvname(old_rr_data);
- if (!strcmp(new_host, old_host)) {
- rr_data->info(i).app = old_rr_data->info(ii).app;
- }
+ std::sort(q, q + valid_records, [](SRV *lhs, SRV *rhs) -> bool { return *lhs < *rhs; });
+
+ SRV **cur_srv = q;
+ for (auto &item : rr_info) {
+ auto t = *cur_srv++; // get next SRV record pointer.
+ memcpy(pos, t->host, t->host_len); // Append the name to the overall record.
+ item.assign(t, pos);
+ pos += t->host_len;
+ if (old_r) { // migrate as needed.
+ for (auto &old_item : old_r->rr_info()) {
+ if (item.data.srv.key == old_item.data.srv.key && 0 == strcmp(item.srvname(), old_item.srvname())) {
+ item.migrate_from(old_item);
+ break;
}
}
}
+ // Archetypical example - "%zd" doesn't work on FreeBSD, "%ld" doesn't work on Ubuntu, "%lld" doesn't work on Fedora.
+ Debug_bw("dns_srv", "inserted SRV RR record [{}] into HostDB with TTL: {} seconds", t->host, ttl);
}
} else { // Otherwise this is a regular dns response
- rr_data->good = rr_data->rrcount = valid_records;
- rr_data->current = 0;
- for (int i = 0; i < valid_records; ++i) {
- HostDBInfo &item = rr_data->info(i);
- ip_addr_set(item.ip(), af, e->ent.h_addr_list[i]);
- item.round_robin = 0;
- item.round_robin_elt = 1;
- item.reverse_dns = 0;
- item.is_srv = 0;
- if (!restore_info(&item, old_r.get(), old_info, old_rr_data)) {
- item.app.allotment.application1 = 0;
- item.app.allotment.application2 = 0;
+ unsigned idx = 0;
+ for (auto &item : rr_info) {
+ item.assign(af, e->ent.h_addr_list[idx++]);
+ if (old_r) { // migrate as needed.
+ for (auto &old_item : old_r->rr_info()) {
+ if (item.data.ip == old_item.data.ip) {
+ item.migrate_from(old_item);
+ break;
+ }
+ }
}
}
}
}
- if (!failed && !is_rr && !is_srv()) {
- restore_info(r, old_r.get(), old_info, old_rr_data);
- }
- ink_assert(!r || !r->round_robin || !r->reverse_dns);
- ink_assert(failed || ((r != nullptr) && (!r->round_robin || r->app.rr.offset)));
-
- if (!serve_stale) {
- hostDB.refcountcache->put(hash.hash.fold(), r, allocSize, r->expiry_time());
+ if (!serve_stale) { // implies r != old_r
+ auto const duration_till_revalidate = r->expiry_time().time_since_epoch();
+ auto const seconds_till_revalidate = duration_cast<ts_seconds>(duration_till_revalidate).count();
+ hostDB.refcountcache->put(r->key, r.get(), r->_record_size, seconds_till_revalidate);
} else {
- Warning("Fallback to serving stale record, skip re-update of hostdb for %s", aname);
+ Warning("Fallback to serving stale record, skip re-update of hostdb for %.*s", int(query_name.size()), query_name.data());
}
// try to callback the user
@@ -1373,7 +1238,7 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e)
if (action.continuation->mutex) {
ink_release_assert(action.continuation->mutex == action.mutex);
}
- reply_to_cont(action.continuation, r, is_srv());
+ reply_to_cont(action.continuation, r.get(), is_srv());
}
need_to_reschedule = false;
}
@@ -1390,7 +1255,7 @@ HostDBContinuation::dnsEvent(int event, HostEnt *e)
hostDB.pending_dns_for_hash(hash.hash).remove(this);
// wake up everyone else who is waiting
- remove_trigger_pending_dns();
+ remove_and_trigger_pending_dns();
hostdb_cont_free(this);
@@ -1433,7 +1298,7 @@ HostDBContinuation::iterateEvent(int event, Event *e)
IntrusiveHashMap<RefCountCacheLinkage> &partMap = hostDB.refcountcache->get_partition(current_iterate_pos).get_map();
for (const auto &it : partMap) {
- HostDBInfo *r = static_cast<HostDBInfo *>(it.item.get());
+ auto *r = static_cast<HostDBRecord *>(it.item.get());
if (r && !r->is_failed()) {
action.continuation->handleEvent(EVENT_INTERVAL, static_cast<void *>(r));
}
@@ -1499,7 +1364,7 @@ HostDBContinuation::probeEvent(int /* event ATS_UNUSED */, Event *e)
if (!force_dns) {
// Do the probe
//
- Ptr<HostDBInfo> r = probe(mutex, hash, false);
+ Ptr<HostDBRecord> r = probe(mutex, hash, false);
if (r) {
HOSTDB_INCREMENT_DYN_STAT(hostdb_total_hits_stat);
@@ -1545,7 +1410,7 @@ HostDBContinuation::set_check_pending_dns()
}
void
-HostDBContinuation::remove_trigger_pending_dns()
+HostDBContinuation::remove_and_trigger_pending_dns()
{
Queue<HostDBContinuation> &q = hostDB.pending_dns_for_hash(hash.hash);
q.remove(this);
@@ -1583,31 +1448,42 @@ HostDBContinuation::do_dns()
{
ink_assert(!action.cancelled);
if (is_byname()) {
- Debug("hostdb", "DNS %s", hash.host_name);
+ Debug("hostdb", "DNS %.*s", int(hash.host_name.size()), hash.host_name.data());
IpAddr tip;
if (0 == tip.load(hash.host_name)) {
- // check 127.0.0.1 format // What the heck does that mean? - AMC
+ // Need to consider if this is necessary - could the record in ResolveInfo be left null and
+ // just the resolved address set?
if (action.continuation) {
- HostDBInfo *r = lookup_done(tip, hash.host_name, false, HOST_DB_MAX_TTL, nullptr);
-
- reply_to_cont(action.continuation, r);
+ HostDBRecord::Handle r{HostDBRecord::alloc(hash.host_name, 1)};
+ r->af_family = tip.family();
+ auto &info = r->rr_info()[0];
+ info.assign(tip);
+ // tricksy - @a reply_to_cont must use an intrusive pointer to @a r if it needs to persist
+ // @a r doesn't go out of scope until after this returns. This continuation shares the mutex
+ // of the target continuation therefore this is always dispatched synchronously.
+ reply_to_cont(action.continuation, r.get());
}
hostdb_cont_free(this);
return;
}
- ts::ConstBuffer hname(hash.host_name, hash.host_len);
- Ptr<RefCountedHostsFileMap> current_host_file_map = hostDB.hosts_file_ptr;
- HostsFileMap::iterator find_result = current_host_file_map->hosts_file_map.find(hname);
- if (find_result != current_host_file_map->hosts_file_map.end()) {
- if (action.continuation) {
- // Set the TTL based on how often we stat() the host file
- HostDBInfo *r = lookup_done(IpAddr(find_result->second), hash.host_name, false, hostdb_hostfile_check_interval, nullptr);
- reply_to_cont(action.continuation, r);
+
+ // If looking for an IPv4 or IPv6 address, check the host file.
+ if (hash.db_mark == HOSTDB_MARK_IPV6 || hash.db_mark == HOSTDB_MARK_IPV4) {
+ if (auto static_hosts = hostDB.acquire_host_file(); static_hosts) {
+ if (auto spot = static_hosts->find(hash.host_name); spot != static_hosts->end()) {
+ HostDBRecord::Handle r = (hash.db_mark == HOSTDB_MARK_IPV4) ? spot->second.record_4 : spot->second.record_6;
+ // Set the TTL based on how often we stat() the host file
+ if (r && action.continuation) {
+ r = lookup_done(hash.host_name, hostdb_hostfile_check_interval, nullptr, r);
+ reply_to_cont(action.continuation, r.get());
+ hostdb_cont_free(this);
+ return;
+ }
+ }
}
- hostdb_cont_free(this);
- return;
}
}
+
if (hostdb_lookup_timeout) {
timeout = mutex->thread_holding->schedule_in(this, HRTIME_SECONDS(hostdb_lookup_timeout));
} else {
@@ -1624,7 +1500,7 @@ HostDBContinuation::do_dns()
}
pending_action = dnsProcessor.gethostbyname(this, hash.host_name, opt);
} else if (is_srv()) {
- Debug("dns_srv", "SRV lookup of %s", hash.host_name);
+ Debug("dns_srv", "SRV lookup of %.*s", int(hash.host_name.size()), hash.host_name.data());
pending_action = dnsProcessor.getSRVbyname(this, hash.host_name, opt);
} else {
ip_text_buffer ipb;
@@ -1638,50 +1514,47 @@ HostDBContinuation::do_dns()
//
// Background event
-// Just increment the current_interval. Might do other stuff
-// here, like move records to the current position in the cluster.
-//
+// Increment the hostdb_current_timestamp which funcions as our cached version
+// of ts_clock::now(). Might do other stuff here, like move records to the
+// current position in the cluster.
int
HostDBContinuation::backgroundEvent(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
{
- // No nothing if hosts file checking is not enabled.
- if (hostdb_hostfile_check_interval == 0) {
+ std::string dbg;
+
+ hostdb_current_timestamp = ts_clock::now();
+
+ // Do nothing if hosts file checking is not enabled.
+ if (hostdb_hostfile_check_interval.count() == 0) {
return EVENT_CONT;
}
- hostdb_current_interval = ink_time();
-
- if ((hostdb_current_interval - hostdb_last_interval) > hostdb_hostfile_check_interval) {
+ if ((hostdb_current_timestamp - hostdb_last_timestamp) > hostdb_hostfile_check_interval) {
bool update_p = false; // do we need to reparse the file and update?
- struct stat info;
- char path[sizeof(hostdb_hostfile_path)];
+ char path[PATH_NAME_MAX];
REC_ReadConfigString(path, "proxy.config.hostdb.host_file.path", sizeof(path));
- if (0 != strcasecmp(hostdb_hostfile_path, path)) {
- Debug("hostdb", "Update host file '%s' -> '%s'", (*hostdb_hostfile_path ? hostdb_hostfile_path : "*-none-*"),
- (*path ? path : "*-none-*"));
+ if (0 != strcasecmp(hostdb_hostfile_path.string(), path)) {
+ Debug("hostdb", "%s",
+ ts::bwprint(dbg, R"(Updating hosts file from "{}" to "{}")", hostdb_hostfile_path, ts::bwf::FirstOf(path, "")).c_str());
// path to hostfile changed
- hostdb_hostfile_update_timestamp = 0; // never updated from this file
- if ('\0' != *path) {
- memcpy(hostdb_hostfile_path, path, sizeof(hostdb_hostfile_path));
- } else {
- hostdb_hostfile_path[0] = 0; // mark as not there
- }
- update_p = true;
- } else {
- hostdb_last_interval = hostdb_current_interval;
- if (*hostdb_hostfile_path) {
- if (0 == stat(hostdb_hostfile_path, &info)) {
- if (info.st_mtime > static_cast<time_t>(hostdb_hostfile_update_timestamp)) {
- update_p = true; // same file but it's changed.
- }
- } else {
- Debug("hostdb", "Failed to stat host file '%s'", hostdb_hostfile_path);
+ hostdb_hostfile_update_timestamp = TS_TIME_ZERO; // never updated from this file
+ hostdb_hostfile_path = path;
+ update_p = true;
+ } else if (!hostdb_hostfile_path.empty()) {
+ hostdb_last_timestamp = hostdb_current_timestamp;
+ std::error_code ec;
+ auto stat{ts::file::status(hostdb_hostfile_path, ec)};
+ if (!ec) {
+ if (ts_clock::from_time_t(modification_time(stat)) > hostdb_hostfile_update_timestamp) {
+ update_p = true; // same file but it's changed.
}
+ } else {
+ Debug("hostdb", "%s", ts::bwprint(dbg, R"(Failed to stat host file "{}" - {})", hostdb_hostfile_path, ec).c_str());
}
}
if (update_p) {
- Debug("hostdb", "Updating from host file");
+ Debug("hostdb", "%s", ts::bwprint(dbg, R"(Updating from host file "{}")", hostdb_hostfile_path).c_str());
ParseHostFile(hostdb_hostfile_path, hostdb_hostfile_check_interval);
}
}
@@ -1689,37 +1562,60 @@ HostDBContinuation::backgroundEvent(int /* event ATS_UNUSED */, Event * /* e ATS
return EVENT_CONT;
}
-char *
-HostDBInfo::hostname() const
-{
- if (!reverse_dns) {
- return nullptr;
- }
-
- return (char *)this + data.hostname_offset;
-}
-
-/*
- * The perm_hostname exists for all records not just reverse dns records.
- */
-char *
-HostDBInfo::perm_hostname() const
+HostDBInfo *
+HostDBRecord::select_best_http(ts_time now, ts_seconds fail_window, sockaddr const *hash_addr)
{
- if (hostname_offset == 0) {
- return nullptr;
+ ink_assert(0 < rr_count && rr_count <= hostdb_round_robin_max_count);
+
+ // @a best_any is set to a base candidate, which may be dead.
+ HostDBInfo *best_any = nullptr;
+ // @a best_alive is set when a valid target has been selected and should be used.
+ HostDBInfo *best_alive = nullptr;
+
+ auto info{this->rr_info()};
+
+ if (HostDBProcessor::hostdb_strict_round_robin) {
+ // Always select the next viable target - select failure means no valid targets at all.
+ best_alive = best_any = this->select_next_rr(now, fail_window);
+ Debug("hostdb", "Using strict round robin - index %d", this->index_of(best_alive));
+ } else if (HostDBProcessor::hostdb_timed_round_robin > 0) {
+ auto ctime = rr_ctime.load(); // cache for atomic update.
+ auto ntime = ctime + ts_seconds(HostDBProcessor::hostdb_timed_round_robin);
+ // Check and update RR if it's time - this always yields a valid target if there is one.
+ if (now > ntime && rr_ctime.compare_exchange_strong(ctime, ntime)) {
+ best_alive = best_any = this->select_next_rr(now, fail_window);
+ Debug("hostdb", "Round robin timed interval expired - index %d", this->index_of(best_alive));
+ } else { // pick the current index, which may be dead.
+ best_any = &info[this->rr_idx()];
+ }
+ Debug("hostdb", "Using timed round robin - index %d", this->index_of(best_any));
+ } else {
+ // Walk the entries and find the best (largest) hash.
+ unsigned int best_hash = 0; // any hash is better than this.
+ for (auto &target : info) {
+ unsigned int h = HOSTDB_CLIENT_IP_HASH(hash_addr, target.data.ip);
+ if (best_hash <= h) {
+ best_any = ⌖
+ best_hash = h;
+ }
+ }
+ Debug("hostdb", "Using client affinity - index %d", this->index_of(best_any));
}
- return (char *)this + hostname_offset;
-}
-
-HostDBRoundRobin *
-HostDBInfo::rr()
-{
- if (!round_robin) {
- return nullptr;
+ // If there is a base choice, search for valid target starting there.
+ // Otherwise there is no valid target in the record.
+ if (best_any && !best_alive) {
+ // Starting at the current target, search for a valid one.
+ for (unsigned short i = 0; i < rr_count; i++) {
+ auto target = &info[this->rr_idx(i)];
+ if (target->select(now, fail_window)) {
+ best_alive = target;
+ break;
+ }
+ }
}
- return reinterpret_cast<HostDBRoundRobin *>(reinterpret_cast<char *>(this) + this->app.rr.offset);
+ return best_alive;
}
struct ShowHostDB;
@@ -1785,41 +1681,42 @@ struct ShowHostDB : public ShowCont {
showAllEvent(int event, Event *e)
{
if (event == EVENT_INTERVAL) {
- HostDBInfo *r = reinterpret_cast<HostDBInfo *>(e);
+ auto *r = reinterpret_cast<HostDBRecord *>(e);
if (output_json && records_seen++ > 0) {
CHECK_SHOW(show(",")); // we need to separate records
}
- showOne(r, false, event, e);
- if (r->round_robin) {
- HostDBRoundRobin *rr_data = r->rr();
- if (rr_data) {
- if (!output_json) {
- CHECK_SHOW(show("<table border=1>\n"));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Total", rr_data->rrcount));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Good", rr_data->good));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Current", rr_data->current));
- CHECK_SHOW(show("</table>\n"));
- } else {
- CHECK_SHOW(show(",\"%s\":\"%d\",", "rr_total", rr_data->rrcount));
- CHECK_SHOW(show("\"%s\":\"%d\",", "rr_good", rr_data->good));
- CHECK_SHOW(show("\"%s\":\"%d\",", "rr_current", rr_data->current));
- CHECK_SHOW(show("\"rr_records\":["));
- }
+ auto rr_info{r->rr_info()};
+ if (rr_info.count()) {
+ if (!output_json) {
+ CHECK_SHOW(show("<table border=1>\n"));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Total", r->rr_count));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Current", r->_rr_idx.load()));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Stale", r->is_ip_configured_stale() ? "Yes" : "No"));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Timed-Out", r->is_ip_timeout() ? "Yes" : "No"));
+ CHECK_SHOW(show("</table>\n"));
+ } else {
+ CHECK_SHOW(show(",\"%s\":\"%d\",", "rr_total", r->rr_count));
+ CHECK_SHOW(show("\"%s\":\"%d\",", "rr_current", r->_rr_idx.load()));
+ CHECK_SHOW(show("\"rr_records\":["));
+ }
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "TTL", r->ip_time_remaining()));
- for (int i = 0; i < rr_data->rrcount; i++) {
- showOne(&rr_data->info(i), true, event, e, rr_data);
- if (output_json) {
- CHECK_SHOW(show("}")); // we need to separate records
- if (i < (rr_data->rrcount - 1))
- CHECK_SHOW(show(","));
+ bool need_separator = false;
+ for (auto &item : rr_info) {
+ showOne(&item, r->record_type, event, e);
+ if (output_json) {
+ CHECK_SHOW(show("}")); // we need to separate records
+ if (need_separator) {
+ CHECK_SHOW(show(","));
}
+ need_separator = true;
}
+ }
- if (!output_json) {
- CHECK_SHOW(show("<br />\n<br />\n"));
- } else {
- CHECK_SHOW(show("]"));
- }
+ if (!output_json) {
+ CHECK_SHOW(show("<br />\n<br />\n"));
+ } else {
+ CHECK_SHOW(show("]"));
}
}
@@ -1841,67 +1738,47 @@ struct ShowHostDB : public ShowCont {
}
int
- showOne(HostDBInfo *r, bool rr, int event, Event *e, HostDBRoundRobin *hostdb_rr = nullptr)
+ showOne(HostDBInfo *info, HostDBType record_type, int event, Event *e)
{
ip_text_buffer b;
if (!output_json) {
CHECK_SHOW(show("<table border=1>\n"));
- CHECK_SHOW(show("<tr><td>%s</td><td>%s%s %s</td></tr>\n", "Type", r->round_robin ? "Round-Robin" : "",
- r->reverse_dns ? "Reverse DNS" : "", r->is_srv ? "SRV" : "DNS"));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Type", name_of(record_type)));
- if (r->perm_hostname()) {
- CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Hostname", r->perm_hostname()));
- } else if (rr && r->is_srv && hostdb_rr) {
- CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Hostname", r->srvname(hostdb_rr)));
+ if (HostDBType::SRV == record_type) {
+ CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Hostname", info->srvname()));
}
// Let's display the hash.
- CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "App1", r->app.allotment.application1));
- CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "App2", r->app.allotment.application2));
- CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "LastFailure", r->app.http_data.last_failure));
- if (!rr) {
- CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Stale", r->is_ip_stale() ? "Yes" : "No"));
- CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "Timed-Out", r->is_ip_timeout() ? "Yes" : "No"));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "TTL", r->ip_time_remaining()));
- }
+ CHECK_SHOW(show("<tr><td>%s</td><td>%u</td></tr>\n", "LastFailure", info->last_failure.load().time_since_epoch().count()));
- if (rr && r->is_srv) {
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Weight", r->data.srv.srv_weight));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Priority", r->data.srv.srv_priority));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Port", r->data.srv.srv_port));
- CHECK_SHOW(show("<tr><td>%s</td><td>%x</td></tr>\n", "Key", r->data.srv.key));
- } else if (!r->is_srv) {
- CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "IP", ats_ip_ntop(r->ip(), b, sizeof b)));
+ if (HostDBType::SRV == record_type) {
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Weight", info->data.srv.srv_weight));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Priority", info->data.srv.srv_priority));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Port", info->data.srv.srv_port));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%x</td></tr>\n", "Key", info->data.srv.key));
+ } else {
+ CHECK_SHOW(show("<tr><td>%s</td><td>%s</td></tr>\n", "IP", info->data.ip.toString(b, sizeof b)));
}
CHECK_SHOW(show("</table>\n"));
} else {
CHECK_SHOW(show("{"));
- CHECK_SHOW(show("\"%s\":\"%s%s%s\",", "type", (r->round_robin && !r->is_srv) ? "roundrobin" : "",
- r->reverse_dns ? "reversedns" : "", r->is_srv ? "srv" : "dns"));
+ CHECK_SHOW(show("\"%s\":\"%s\",", "type", name_of(record_type)));
- if (r->perm_hostname()) {
- CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", r->perm_hostname()));
- } else if (rr && r->is_srv && hostdb_rr) {
- CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", r->srvname(hostdb_rr)));
+ if (HostDBType::SRV == record_type) {
+ CHECK_SHOW(show("\"%s\":\"%s\",", "hostname", info->srvname()));
}
- CHECK_SHOW(show("\"%s\":\"%u\",", "app1", r->app.allotment.application1));
- CHECK_SHOW(show("\"%s\":\"%u\",", "app2", r->app.allotment.application2));
- CHECK_SHOW(show("\"%s\":\"%u\",", "lastfailure", r->app.http_data.last_failure));
- if (!rr) {
- CHECK_SHOW(show("\"%s\":\"%s\",", "stale", r->is_ip_stale() ? "yes" : "no"));
- CHECK_SHOW(show("\"%s\":\"%s\",", "timedout", r->is_ip_timeout() ? "yes" : "no"));
- CHECK_SHOW(show("\"%s\":\"%d\",", "ttl", r->ip_time_remaining()));
- }
+ CHECK_SHOW(show("\"%s\":\"%u\",", "lastfailure", info->last_failure.load().time_since_epoch().count()));
- if (rr && r->is_srv) {
- CHECK_SHOW(show("\"%s\":\"%d\",", "weight", r->data.srv.srv_weight));
- CHECK_SHOW(show("\"%s\":\"%d\",", "priority", r->data.srv.srv_priority));
- CHECK_SHOW(show("\"%s\":\"%d\",", "port", r->data.srv.srv_port));
- CHECK_SHOW(show("\"%s\":\"%x\",", "key", r->data.srv.key));
- } else if (!r->is_srv) {
- CHECK_SHOW(show("\"%s\":\"%s\"", "ip", ats_ip_ntop(r->ip(), b, sizeof b)));
+ if (HostDBType::SRV == record_type) {
+ CHECK_SHOW(show("\"%s\":\"%d\",", "weight", info->data.srv.srv_weight));
+ CHECK_SHOW(show("\"%s\":\"%d\",", "priority", info->data.srv.srv_priority));
+ CHECK_SHOW(show("\"%s\":\"%d\",", "port", info->data.srv.srv_port));
+ CHECK_SHOW(show("\"%s\":\"%x\",", "key", info->data.srv.key));
+ } else {
+ CHECK_SHOW(show("\"%s\":\"%s\"", "ip", info->data.ip.toString(b, sizeof b)));
}
}
return EVENT_CONT;
@@ -1910,7 +1787,7 @@ struct ShowHostDB : public ShowCont {
int
showLookupDone(int event, Event *e)
{
- HostDBInfo *r = reinterpret_cast<HostDBInfo *>(e);
+ auto *r = reinterpret_cast<HostDBRecord *>(e);
CHECK_SHOW(begin("HostDB Lookup"));
if (name) {
@@ -1919,19 +1796,15 @@ struct ShowHostDB : public ShowCont {
CHECK_SHOW(show("<H2>%u.%u.%u.%u</H2>\n", PRINT_IP(ip)));
}
if (r) {
- showOne(r, false, event, e);
- if (r->round_robin) {
- HostDBRoundRobin *rr_data = r->rr();
- if (rr_data) {
- CHECK_SHOW(show("<table border=1>\n"));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Total", rr_data->rrcount));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Good", rr_data->good));
- CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Current", rr_data->current));
- CHECK_SHOW(show("</table>\n"));
-
- for (int i = 0; i < rr_data->rrcount; i++) {
- showOne(&rr_data->info(i), true, event, e, rr_data);
- }
+ auto rr_data = r->rr_info();
+ if (rr_data.count()) {
+ CHECK_SHOW(show("<table border=1>\n"));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Total", r->rr_count));
+ CHECK_SHOW(show("<tr><td>%s</td><td>%d</td></tr>\n", "Current", r->_rr_idx.load()));
+ CHECK_SHOW(show("</table>\n"));
+
+ for (auto &item : rr_data) {
+ showOne(&item, r->record_type, event, e);
}
}
} else {
@@ -2027,9 +1900,9 @@ struct HostDBTestReverse : public Continuation {
mainEvent(int event, Event *e)
{
if (event == EVENT_HOST_DB_LOOKUP) {
- HostDBInfo *i = reinterpret_cast<HostDBInfo *>(e);
+ auto *i = reinterpret_cast<HostDBRecord *>(e);
if (i) {
- rprintf(test, "HostDBTestReverse: reversed %s\n", i->hostname());
+ rprintf(test, "HostDBTestReverse: reversed %s\n", i->name());
}
outstanding--;
}
@@ -2149,93 +2022,110 @@ HostDBFileContinuation::destroy()
// We can't allow more than one update to be
// proceeding at a time in any case so we might as well make these
// globals.
-int HostDBFileUpdateActive = 0;
+std::atomic<bool> HostDBFileUpdateActive{false};
-static void
-ParseHostLine(Ptr<RefCountedHostsFileMap> &map, char *l)
+/* Container for temporarily holding data from the host file. For each FQDN there is a vector of IPv4
+ * and IPv6 addresses. These are used to generate the HostDBRecord instances that are stored persistently.
+ */
+using HostAddrMap = std::unordered_map<std::string_view, std::tuple<std::vector<IpAddr>, std::vector<IpAddr>>>;
+
+namespace
{
- Tokenizer elts(" \t");
- int n_elts = elts.Initialize(l, SHARE_TOKS);
+constexpr unsigned IPV4_IDX = 0;
+constexpr unsigned IPV6_IDX = 1;
+} // namespace
+static void
+ParseHostLine(TextView line, HostAddrMap &map)
+{
// Elements should be the address then a list of host names.
+ TextView addr_text = line.take_prefix_if(&isspace);
+ IpAddr addr;
+
// Don't use RecHttpLoadIp because the address *must* be literal.
- IpAddr ip;
- if (n_elts > 1 && 0 == ip.load(elts[0])) {
- for (int i = 1; i < n_elts; ++i) {
- ts::ConstBuffer name(elts[i], strlen(elts[i]));
- // If we don't have an entry already (host files only support single IPs for a given name)
- if (map->hosts_file_map.find(name) == map->hosts_file_map.end()) {
- map->hosts_file_map[name] = ip;
- }
+ if (TS_SUCCESS != addr.load(addr_text)) {
+ return;
+ }
+
+ while (!line.ltrim_if(&isspace).empty()) {
+ TextView name = line.take_prefix_if(&isspace);
+ if (addr.isIp6()) {
+ std::get<IPV6_IDX>(map[name]).push_back(addr);
+ } else if (addr.isIp4()) {
+ std::get<IPV4_IDX>(map[name]).push_back(addr);
}
}
}
void
-ParseHostFile(const char *path, unsigned int hostdb_hostfile_check_interval_parse)
+ParseHostFile(ts::file::path const &path, ts_seconds hostdb_hostfile_check_interval_parse)
{
- Ptr<RefCountedHostsFileMap> parsed_hosts_file_ptr;
+ std::shared_ptr<HostFileMap> map;
// Test and set for update in progress.
- if (0 != ink_atomic_swap(&HostDBFileUpdateActive, 1)) {
+ bool flag = false;
+ if (!HostDBFileUpdateActive.compare_exchange_strong(flag, true)) {
Debug("hostdb", "Skipped load of host file because update already in progress");
return;
}
- Debug("hostdb", "Loading host file '%s'", path);
-
- if (*path) {
- ats_scoped_fd fd(open(path, O_RDONLY));
- if (fd >= 0) {
- struct stat info;
- if (0 == fstat(fd, &info)) {
- // +1 in case no terminating newline
- int64_t size = info.st_size + 1;
-
- parsed_hosts_file_ptr = new RefCountedHostsFileMap;
- parsed_hosts_file_ptr->HostFileText = static_cast<char *>(ats_malloc(size));
- if (parsed_hosts_file_ptr->HostFileText) {
- char *base = parsed_hosts_file_ptr->HostFileText;
- char *limit;
-
- size = read(fd, parsed_hosts_file_ptr->HostFileText, info.st_size);
- limit = parsed_hosts_file_ptr->HostFileText + size;
- *limit = 0;
-
- // We need to get a list of all name/addr pairs so that we can
- // group names for round robin records. Also note that the
- // pairs have pointer back in to the text storage for the file
- // so we need to keep that until we're done with @a pairs.
- while (base < limit) {
- char *spot = strchr(base, '\n');
-
- // terminate the line.
- if (nullptr == spot) {
- spot = limit; // no trailing EOL, grab remaining
- } else {
- *spot = 0;
- }
-
- while (base < spot && isspace(*base)) {
- ++base; // skip leading ws
- }
- if (*base != '#' && base < spot) { // non-empty non-comment line
- ParseHostLine(parsed_hosts_file_ptr, base);
- }
- base = spot + 1;
- }
-
- hostdb_hostfile_update_timestamp = hostdb_current_interval;
+ Debug_bw("hostdb", R"(Loading host file "{}")", path);
+
+ if (!path.empty()) {
+ std::error_code ec;
+ std::string content = ts::file::load(path, ec);
+ if (!ec) {
+ HostAddrMap addr_map;
+ TextView text{content};
+ while (text) {
+ auto line = text.take_prefix_at('\n').ltrim_if(&isspace);
+ if (line.empty() || '#' == *line) {
+ continue;
}
+ ParseHostLine(line, addr_map);
}
+ // @a map should be loaded with all of the data, create the records.
+ map = std::make_shared<HostFileMap>();
+ // Common loading function for creating a record from the address vector.
+ auto loader = [](TextView key, std::vector<IpAddr> const &v) -> HostDBRecord::Handle {
+ HostDBRecord::Handle record{HostDBRecord::alloc(key, v.size())};
+ record->af_family = v.front().family(); // @a v is presumed family homogenous
+ auto rr_info = record->rr_info();
+ auto spot = v.begin();
+ for (auto &item : rr_info) {
+ item.assign(*spot++);
+ }
+ return record;
+ };
+ // Walk the temporary map and create the corresponding records for the persistent map.
+ for (auto const &[key, value] : addr_map) {
+ // Bit of subtlety to be able to search records with a view and not a string - the key
+ // must point at stable memory for the name, which is available in the record itself.
+ // Therefore the lookup for adding the record must be done using a view based in the record.
+ // It doesn't matter if it's the IPv4 or IPv6 record that's used, both are stable and equal
+ // to each other.
+ // IPv4
+ if (auto const &v = std::get<IPV4_IDX>(value); v.size() > 0) {
+ auto r = loader(key, v);
+ (*map)[r->name_view()].record_4 = r;
+ }
+ // IPv6
+ if (auto const &v = std::get<IPV6_IDX>(value); v.size() > 0) {
+ auto r = loader(key, v);
+ (*map)[r->name_view()].record_6 = r;
+ }
+ }
+
+ hostdb_hostfile_update_timestamp = hostdb_current_timestamp;
}
}
// Swap the pointer
- if (parsed_hosts_file_ptr != nullptr) {
- hostDB.hosts_file_ptr = parsed_hosts_file_ptr;
+ if (map) {
+ std::unique_lock lock(hostDB.host_file_mutex);
+ hostDB.host_file = map;
}
// Mark this one as completed, so we can allow another update to happen
- HostDBFileUpdateActive = 0;
+ HostDBFileUpdateActive = false;
}
//
@@ -2258,7 +2148,7 @@ struct HostDBRegressionContinuation : public Continuation {
int i;
int
- mainEvent(int event, HostDBInfo *r)
+ mainEvent(int event, HostDBRecord *r)
{
(void)event;
@@ -2267,27 +2157,15 @@ struct HostDBRegressionContinuation : public Continuation {
}
if (event == EVENT_HOST_DB_LOOKUP) {
--outstanding;
- // since this is a lookup done, data is either hostdbInfo or nullptr
if (r) {
- rprintf(test, "hostdbinfo r=%x\n", r);
- char const *hname = r->perm_hostname();
- if (nullptr == hname) {
- hname = "(null)";
- }
- rprintf(test, "hostdbinfo hostname=%s\n", hname);
- rprintf(test, "hostdbinfo rr %x\n", r->rr());
+ rprintf(test, "HostDBRecord r=%x\n", r);
+ rprintf(test, "HostDBRecord hostname=%s\n", r->name());
// If RR, print all of the enclosed records
- if (r->rr()) {
- rprintf(test, "hostdbinfo good=%d\n", r->rr()->good);
- for (int x = 0; x < r->rr()->good; x++) {
- ip_port_text_buffer ip_buf;
- ats_ip_ntop(r->rr()->info(x).ip(), ip_buf, sizeof(ip_buf));
- rprintf(test, "hostdbinfo RR%d ip=%s\n", x, ip_buf);
- }
- } else { // Otherwise, just the one will do
+ auto rr_info{r->rr_info()};
+ for (int x = 0; x < r->rr_count; ++x) {
ip_port_text_buffer ip_buf;
- ats_ip_ntop(r->ip(), ip_buf, sizeof(ip_buf));
- rprintf(test, "hostdbinfo A ip=%s\n", ip_buf);
+ rr_info[x].data.ip.toString(ip_buf, sizeof(ip_buf));
+ rprintf(test, "hostdbinfo RR%d ip=%s\n", x, ip_buf);
}
++success;
} else {
@@ -2327,9 +2205,9 @@ struct HostDBRegressionContinuation : public Continuation {
static const char *dns_test_hosts[] = {
"www.apple.com", "www.ibm.com", "www.microsoft.com",
- "www.coke.com", // RR record
- "4.2.2.2", // An IP-- since we don't expect resolution
- "127.0.0.1", // loopback since it has some special handling
+ "yahoo.com", // RR record
+ "4.2.2.2", // An IP-- since we don't expect resolution
+ "127.0.0.1", // loopback since it has some special handling
};
REGRESSION_TEST(HostDBProcessor)(RegressionTest *t, int atype, int *pstatus)
@@ -2338,3 +2216,214 @@ REGRESSION_TEST(HostDBProcessor)(RegressionTest *t, int atype, int *pstatus)
}
#endif
+// -----
+void
+HostDBRecord::free()
+{
+ if (_iobuffer_index > 0) {
+ Debug("hostdb", "freeing %d bytes at [%p]", (1 << (7 + _iobuffer_index)), this);
+ ioBufAllocator[_iobuffer_index].free_void(static_cast<void *>(this));
+ }
+}
+
+HostDBRecord *
+HostDBRecord::alloc(TextView query_name, unsigned int rr_count, size_t srv_name_size)
+{
+ const ts::Scalar<8> qn_size = ts::round_up(query_name.size() + 1);
+ const ts::Scalar<8> r_size = ts::round_up(sizeof(self_type) + qn_size + rr_count * sizeof(HostDBInfo) + srv_name_size);
+ int iobuffer_index = iobuffer_size_to_index(r_size, hostdb_max_iobuf_index);
+ ink_release_assert(iobuffer_index >= 0);
+ auto ptr = ioBufAllocator[iobuffer_index].alloc_void();
+ memset(ptr, 0, r_size);
+ auto self = static_cast<self_type *>(ptr);
+ new (self) self_type();
+ self->_iobuffer_index = iobuffer_index;
+ self->_record_size = r_size;
+
+ Debug("hostdb", "allocating %ld bytes for %.*s with %d RR records at [%p]", r_size.value(), int(query_name.size()),
+ query_name.data(), rr_count, self);
+
+ // where in our block of memory we are
+ int offset = sizeof(self_type);
+ memcpy(self->apply_offset<void>(offset), query_name);
+ offset += qn_size;
+ self->rr_offset = offset;
+ self->rr_count = rr_count;
+ // Construct the info instances to a valid state.
+ for (auto &info : self->rr_info()) {
+ new (&info) std::remove_reference_t<decltype(info)>;
+ }
+
+ return self;
+}
+
+HostDBRecord::self_type *
+HostDBRecord::unmarshall(char *buff, unsigned size)
+{
+ if (size < sizeof(self_type)) {
+ return nullptr;
+ }
+ auto src = reinterpret_cast<self_type *>(buff);
+ ink_release_assert(size == src->_record_size);
+ auto ptr = ioBufAllocator[src->_iobuffer_index].alloc_void();
+ auto self = static_cast<self_type *>(ptr);
+ new (self) self_type();
+ auto delta = sizeof(RefCountObj); // skip the VFTP and ref count.
+ memcpy(static_cast<std::byte *>(ptr) + delta, buff + delta, size - delta);
+ return self;
+}
+
+bool
+HostDBRecord::serve_stale_but_revalidate() const
+{
+ // the option is disabled
+ if (hostdb_serve_stale_but_revalidate <= 0) {
+ return false;
+ }
+
+ // ip_timeout_interval == DNS TTL
+ // hostdb_serve_stale_but_revalidate == number of seconds
+ // ip_age() is the number of seconds between now() and when the entry was inserted
+ if ((ip_timeout_interval + ts_seconds(hostdb_serve_stale_but_revalidate)) > ip_age()) {
+ Debug_bw("hostdb", "serving stale entry for {}, TTL: {}, serve_stale_for: {}, age: {} as requested by config", name(),
+ ip_timeout_interval, hostdb_serve_stale_but_revalidate, ip_age());
+ return true;
+ }
+
+ // otherwise, the entry is too old
+ return false;
+}
+
+HostDBInfo *
+HostDBRecord::select_best_srv(char *target, InkRand *rand, ts_time now, ts_seconds fail_window)
+{
+ ink_assert(rr_count <= 0 || static_cast<unsigned int>(rr_count) > hostdb_round_robin_max_count);
+
+ int i = 0;
+ int live_n = 0;
+ uint32_t weight = 0, p = INT32_MAX;
+ HostDBInfo *result = nullptr;
+ auto rr = this->rr_info();
+ // Array of live targets, sized by @a live_n
+ HostDBInfo *live[rr.count()];
+ for (auto &target : rr) {
+ // skip dead upstreams.
+ if (rr[i].is_dead(now, fail_window)) {
+ continue;
+ }
+
+ if (target.data.srv.srv_priority <= p) {
+ p = target.data.srv.srv_priority;
+ weight += target.data.srv.srv_weight;
+ live[live_n++] = ⌖
+ } else {
+ break;
+ }
+ };
+
+ if (live_n == 0 || weight == 0) { // no valid or weighted choice, use strict RR
+ result = this->select_next_rr(now, fail_window);
+ } else {
+ uint32_t xx = rand->random() % weight;
+ for (i = 0; i < live_n - 1 && xx >= live[i]->data.srv.srv_weight; ++i)
+ xx -= live[i]->data.srv.srv_weight;
+
+ result = live[i];
+ }
+
+ if (result) {
+ ink_strlcpy(target, this->name(), MAXDNAME);
+ return result;
+ }
+ return nullptr;
+}
+
+HostDBInfo *
+HostDBRecord::select_next_rr(ts_time now, ts_seconds fail_window)
+{
+ auto rr_info = this->rr_info();
+ for (unsigned idx = 0, limit = rr_info.count(); idx < limit; ++idx) {
+ auto &target = rr_info[this->next_rr()];
+ if (target.select(now, fail_window)) {
+ return ⌖
+ }
+ }
+
+ return nullptr;
+}
+
+unsigned
+HostDBRecord::next_rr()
+{
+ auto raw_idx = ++_rr_idx;
+ // Modulus on an atomic is a bit tricky - need to make sure the value is always decremented by the
+ // modulus even if another thread incremented. Update to modulus value iff the value hasn't been
+ // incremented elsewhere. Eventually the "last" incrementer will do the update.
+ auto idx = raw_idx % rr_count;
+ _rr_idx.compare_exchange_weak(raw_idx, idx);
+ return idx;
+}
+
+HostDBInfo *
+HostDBRecord::find(sockaddr const *addr)
+{
+ for (auto &item : this->rr_info()) {
+ if (item.data.ip == addr) {
+ return &item;
+ }
+ }
+ return nullptr;
+}
+
+bool
+ResolveInfo::resolve_immediate()
+{
+ if (resolved_p) {
+ // nothing - already resolved.
+ } else if (IpAddr tmp; TS_SUCCESS == tmp.load(lookup_name)) {
+ ts::bwprint(ts::bw_dbg, "[resolve_immediate] success - FQDN '{}' is a valid IP address.", lookup_name);
+ Debug("hostdb", "%s", ts::bw_dbg.c_str());
+ addr.assign(tmp);
+ resolved_p = true;
+ }
+ return resolved_p;
+}
+
+bool
+ResolveInfo::set_active(HostDBInfo *info)
+{
+ active = info;
+ if (info) {
+ addr.assign(active->data.ip);
+ resolved_p = true;
+ return true;
+ }
+ resolved_p = false;
+ return false;
+}
+
+bool
+ResolveInfo::select_next_rr()
+{
+ if (active) {
+ if (auto rr_info{this->record->rr_info()}; rr_info.count() > 1) {
+ unsigned limit = active - rr_info.data(), idx = (limit + 1) % rr_info.count();
+ while ((idx = (idx + 1) % rr_info.count()) != limit && !rr_info[idx].is_alive())
+ ;
+ active = &rr_info[idx];
+ return idx != limit; // if the active record was actually changed.
+ }
+ }
+ return false;
+}
+
+bool
+ResolveInfo::set_upstream_address(IpAddr const &ip_addr)
+{
+ if (ip_addr.isValid()) {
+ addr.assign(ip_addr);
+ resolved_p = true;
+ return true;
+ }
+ return false;
+}
diff --git a/iocore/hostdb/I_HostDBProcessor.h b/iocore/hostdb/I_HostDBProcessor.h
index 0be0a81b4..6c34082c1 100644
--- a/iocore/hostdb/I_HostDBProcessor.h
+++ b/iocore/hostdb/I_HostDBProcessor.h
@@ -23,10 +23,14 @@
#pragma once
+#include <chrono>
+#include <atomic>
+
#include "tscore/HashFNV.h"
#include "tscore/ink_time.h"
#include "tscore/CryptoHash.h"
#include "tscore/ink_align.h"
+#include "tscore/ink_inet.h"
#include "tscore/ink_resolver.h"
#include "tscore/HTTPVersion.h"
#include "I_EventSystem.h"
@@ -46,6 +50,7 @@
// Data
//
struct HostDBContinuation;
+struct ResolveInfo;
//
// The host database stores host information, most notably the
@@ -56,7 +61,17 @@ struct HostDBContinuation;
// disk representation to decrease # of seeks.
//
extern int hostdb_enable;
-extern ink_time_t hostdb_current_interval;
+/** Epoch timestamp of the current hosts file check.
+ *
+ * This also functions as a cached version of ts_clock::now(). Since it is
+ * updated in backgroundEvent which runs every second, it should be
+ * approximately accurate to a second.
+ */
+extern ts_time hostdb_current_timestamp;
+
+/** How long before any DNS response is consided stale, regardless of DNS TTL.
+ * This corresponds to proxy.config.hostdb.verify_after.
+ */
extern unsigned int hostdb_ip_stale_interval;
extern unsigned int hostdb_ip_timeout_interval;
extern unsigned int hostdb_ip_fail_timeout_interval;
@@ -84,339 +99,433 @@ makeHostHash(const char *string)
// Types
//
-/** Host information metadata used by various parts of HostDB.
- * It is stored as generic data in the cache.
- *
- * As a @c union only one of the members is valid, Which one depends on context data in the
- * @c HostDBInfo. This data is written literally to disk therefore if any change is made,
- * the @c object_version for the cache must be updated by modifying @c HostDBInfo::version.
- *
- * @see HostDBInfo::version
- */
-union HostDBApplicationInfo {
- /// Generic storage. This is verified to be the size of the union.
- struct application_data_allotment {
- unsigned int application1;
- unsigned int application2;
- } allotment;
-
- //////////////////////////////////////////////////////////
- // http server attributes in the host database //
- // //
- // http_version - one of HTTPVersion //
- // last_failure - UNIX time for the last time //
- // we tried the server & failed //
- // fail_count - Number of times we tried and //
- // and failed to contact the host //
- //////////////////////////////////////////////////////////
- struct http_server_attr {
- uint32_t last_failure;
- HTTPVersion http_version;
- uint8_t fail_count;
- http_server_attr() : http_version() {}
- } http_data;
-
- struct application_data_rr {
- unsigned int offset;
- } rr;
- HostDBApplicationInfo() : http_data() {}
-};
-
-struct HostDBRoundRobin;
+class HostDBRecord;
+/// Information for an SRV record.
struct SRVInfo {
- unsigned int srv_offset : 16;
+ unsigned int srv_offset : 16; ///< Memory offset from @c HostDBInfo to name.
unsigned int srv_weight : 16;
unsigned int srv_priority : 16;
unsigned int srv_port : 16;
unsigned int key;
};
-struct HostDBInfo : public RefCountObj {
- /** Internal IP address data.
- This is at least large enough to hold an IPv6 address.
- */
+/// Type of data stored.
+enum class HostDBType : uint8_t {
+ UNSPEC, ///< No valid data.
+ ADDR, ///< IP address.
+ SRV, ///< SRV record.
+ HOST ///< Hostname (reverse DNS)
+};
+char const *name_of(HostDBType t);
- static HostDBInfo *
- alloc(int size = 0)
- {
- size += sizeof(HostDBInfo);
- int iobuffer_index = iobuffer_size_to_index(size, hostdb_max_iobuf_index);
- ink_release_assert(iobuffer_index >= 0);
- void *ptr = ioBufAllocator[iobuffer_index].alloc_void();
- memset(ptr, 0, size);
- HostDBInfo *ret = new (ptr) HostDBInfo();
- ret->_iobuffer_index = iobuffer_index;
- return ret;
- }
+/** Information about a single target.
+ */
+struct HostDBInfo {
+ using self_type = HostDBInfo; ///< Self reference type.
- void
- free() override
- {
- ink_release_assert(from_alloc());
- Debug("hostdb", "freeing %d bytes at [%p]", (1 << (7 + _iobuffer_index)), this);
- ioBufAllocator[_iobuffer_index].free_void((void *)(this));
- }
+ /// Default constructor.
+ HostDBInfo() = default;
- /// Effectively the @c object_version for cache data.
- /// This is used to indicate incompatible changes in the binary layout of HostDB records.
- /// It must be updated if any such change is made, even if it is functionally equivalent.
- static ts::VersionNumber
- version()
- {
- /// - 1.0 Initial version.
- /// - 1.1 tweak HostDBApplicationInfo::http_data.
- return ts::VersionNumber(1, 1);
- }
+ HostDBInfo &operator=(HostDBInfo const &that);
- static HostDBInfo *
- unmarshall(char *buf, unsigned int size)
- {
- if (size < sizeof(HostDBInfo)) {
- return nullptr;
- }
- HostDBInfo *ret = HostDBInfo::alloc(size - sizeof(HostDBInfo));
- int buf_index = ret->_iobuffer_index;
- memcpy((void *)ret, buf, size);
- // Reset the refcount back to 0, this is a bit ugly-- but I'm not sure we want to expose a method
- // to mess with the refcount, since this is a fairly unique use case
- ret = new (ret) HostDBInfo();
- ret->_iobuffer_index = buf_index;
- return ret;
- }
+ /// Absolute time of when this target failed.
+ /// A value of zero (@c TS_TIME_ZERO ) indicates no failure.
+ ts_time last_fail_time() const;
- // return expiry time (in seconds since epoch)
- ink_time_t
- expiry_time() const
- {
- return ip_timestamp + ip_timeout_interval + hostdb_serve_stale_but_revalidate;
- }
+ /// Target is alive - no known failure.
+ bool is_alive();
- sockaddr *
- ip()
- {
- return &data.ip.sa;
- }
+ /// Target has failed and is still in the blocked time window.
+ bool is_dead(ts_time now, ts_seconds fail_window);
- sockaddr const *
- ip() const
- {
- return &data.ip.sa;
- }
+ /** Select this target.
+ *
+ * @param now Current time.
+ * @param fail_window Failure window.
+ * @return Status of the selection.
+ *
+ * If a zombie is selected the failure time is updated to make it look dead to other threads in a thread safe
+ * manner. The caller should check @c last_fail_time to see if a zombie was selected.
+ */
+ bool select(ts_time now, ts_seconds fail_window);
- char *hostname() const;
- char *perm_hostname() const;
- char *srvname(HostDBRoundRobin *rr) const;
+ /// Check if this info is valid.
+ bool is_valid() const;
- /// Check if this entry is an element of a round robin entry.
- /// If @c true then this entry is part of and was obtained from a round robin root. This is useful if the
- /// address doesn't work - a retry can probably get a new address by doing another lookup and resolving to
- /// a different element of the round robin.
- bool
- is_rr_elt() const
- {
- return 0 != round_robin_elt;
- }
+ /// Mark this info as invalid.
+ void invalidate();
- HostDBRoundRobin *rr();
+ /** Mark the entry as down.
+ *
+ * @param now Time of the failure.
+ * @return @c true if @a this was marked down, @c false if not.
+ *
+ * This can return @c false if the entry is already marked down, in which case the failure time is not updated.
+ */
+ bool mark_down(ts_time now);
- unsigned int
- ip_interval() const
- {
- return (hostdb_current_interval - ip_timestamp) & 0x7FFFFFFF;
- }
+ /** Mark the target as up / alive.
+ *
+ * @return Previous alive state of the target.
+ */
+ bool mark_up();
- int
- ip_time_remaining() const
- {
- return static_cast<int>(ip_timeout_interval) - static_cast<int>(this->ip_interval());
- }
+ char const *srvname() const;
- bool
- is_ip_stale() const
- {
- return ip_timeout_interval >= 2 * hostdb_ip_stale_interval && ip_interval() >= hostdb_ip_stale_interval;
- }
+ /** Migrate data after a DNS update.
+ *
+ * @param that Source item.
+ *
+ * This moves only specific state information, it is not a generic copy.
+ */
+ void migrate_from(self_type const &that);
- bool
- is_ip_timeout() const
- {
- return ip_interval() >= ip_timeout_interval;
- }
+ /// A target is either an IP address or an SRV record.
+ /// The type should be indicated by @c flags.f.is_srv;
+ union {
+ IpAddr ip; ///< IP address / port data.
+ SRVInfo srv; ///< SRV record.
+ } data{IpAddr{}};
+
+ /// Data that migrates after updated DNS records are processed.
+ /// @see migrate_from
+ /// @{
+ /// Last time a failure was recorded.
+ std::atomic<ts_time> last_failure{TS_TIME_ZERO};
+ /// Count of connection failures
+ std::atomic<uint8_t> fail_count{0};
+ /// Expected HTTP version of the target based on earlier transactions.
+ HTTPVersion http_version = HTTP_INVALID;
+ /// @}
+
+ self_type &assign(IpAddr const &addr);
+
+protected:
+ self_type &assign(sa_family_t af, void const *addr);
+ self_type &assign(SRV const *srv, char const *name);
+
+ HostDBType type = HostDBType::UNSPEC; ///< Invalid data.
+
+ friend HostDBContinuation;
+};
- bool
- is_ip_fail_timeout() const
- {
- return ip_interval() >= hostdb_ip_fail_timeout_interval;
+inline HostDBInfo &
+HostDBInfo::operator=(HostDBInfo const &that)
+{
+ if (this != &that) {
+ memcpy(static_cast<void *>(this), static_cast<const void *>(&that), sizeof(*this));
}
+ return *this;
+}
- void
- refresh_ip()
- {
- ip_timestamp = hostdb_current_interval;
- }
+inline ts_time
+HostDBInfo::last_fail_time() const
+{
+ return last_failure;
+}
- bool
- serve_stale_but_revalidate() const
- {
- // the option is disabled
- if (hostdb_serve_stale_but_revalidate <= 0) {
- return false;
- }
+inline bool
+HostDBInfo::is_alive()
+{
+ return this->last_fail_time() == TS_TIME_ZERO;
+}
- // ip_timeout_interval == DNS TTL
- // hostdb_serve_stale_but_revalidate == number of seconds
- // ip_interval() is the number of seconds between now() and when the entry was inserted
- if ((ip_timeout_interval + hostdb_serve_stale_but_revalidate) > ip_interval()) {
- Debug("hostdb", "serving stale entry %d | %d | %d as requested by config", ip_timeout_interval,
- hostdb_serve_stale_but_revalidate, ip_interval());
- return true;
- }
+inline bool
+HostDBInfo::is_dead(ts_time now, ts_seconds fail_window)
+{
+ auto last_fail = this->last_fail_time();
+ return (last_fail != TS_TIME_ZERO) && (last_fail + fail_window < now);
+}
+
+inline bool
+HostDBInfo::mark_up()
+{
+ auto t = last_failure.exchange(TS_TIME_ZERO);
+ return t != TS_TIME_ZERO;
+}
+
+inline bool
+HostDBInfo::mark_down(ts_time now)
+{
+ auto t0{TS_TIME_ZERO};
+ return last_failure.compare_exchange_strong(t0, now);
+}
- // otherwise, the entry is too old
- return false;
+inline bool
+HostDBInfo::select(ts_time now, ts_seconds fail_window)
+{
+ auto t0 = this->last_fail_time();
+ if (t0 == TS_TIME_ZERO) {
+ return true; // it's alive and so is valid for selection.
}
+ // Success means this is a zombie and this thread updated the failure time.
+ return (t0 + fail_window < now) && last_failure.compare_exchange_strong(t0, now);
+}
+
+inline void
+HostDBInfo::migrate_from(HostDBInfo::self_type const &that)
+{
+ this->last_failure = that.last_failure.load();
+ this->http_version = that.http_version;
+}
+
+inline bool
+HostDBInfo::is_valid() const
+{
+ return type != HostDBType::UNSPEC;
+}
+
+inline void
+HostDBInfo::invalidate()
+{
+ type = HostDBType::UNSPEC;
+}
- /*
- * Given the current time `now` and the fail_window, determine if this real is alive
+// ----
+/** Root item for HostDB.
+ * This is the container for HostDB data. It is always an array of @c HostDBInfo instances plus metadata.
+ * All strings are C-strings and therefore don't need a distinct size.
+ *
+ */
+class HostDBRecord : public RefCountObj
+{
+ friend struct HostDBContinuation;
+ friend struct ShowHostDB;
+ using self_type = HostDBRecord;
+
+ /// Size of the IO buffer block owned by @a this.
+ /// If negative @a this is in not allocated memory.
+ int _iobuffer_index{-1};
+ /// Actual size of the data.
+ unsigned _record_size = sizeof(self_type);
+
+public:
+ HostDBRecord() = default;
+ HostDBRecord(self_type const &that) = delete;
+
+ using Handle = Ptr<HostDBRecord>; ///< Shared pointer type to hold an instance.
+
+ /** Allocate an instance from the IOBuffers.
+ *
+ * @param query_name Name of the query for the record.
+ * @param rr_count Number of info instances.
+ * @param srv_name_size Storage for SRV names, if any.
+ * @return An instance sufficient to hold the specified data.
+ *
+ * The query name will stored and initialized, and the info instances initialized.
*/
- bool
- is_alive(ink_time_t now, int32_t fail_window)
- {
- unsigned int last_failure = app.http_data.last_failure;
-
- if (last_failure == 0 || (unsigned int)(now - fail_window) > last_failure) {
- return true;
- } else {
- // Entry is marked down. Make sure some nasty clock skew
- // did not occur. Use the retry time to set an upper bound
- // as to how far in the future we should tolerate bogus last
- // failure times. This sets the upper bound that we would ever
- // consider a server down to 2*down_server_timeout
- if ((unsigned int)(now + fail_window) < last_failure) {
- app.http_data.last_failure = 0;
- return false;
- }
- return false;
- }
- }
+ static self_type *alloc(ts::TextView query_name, unsigned rr_count, size_t srv_name_size = 0);
- bool
- is_failed() const
- {
- return !((is_srv && data.srv.srv_offset) || (reverse_dns && data.hostname_offset) || ats_is_ip(ip()));
- }
+ /// Type of data stored in this record.
+ HostDBType record_type = HostDBType::UNSPEC;
- void
- set_failed()
- {
- if (is_srv) {
- data.srv.srv_offset = 0;
- } else if (reverse_dns) {
- data.hostname_offset = 0;
- } else {
- ats_ip_invalidate(ip());
- }
- }
+ /// IP family of this record.
+ sa_family_t af_family = AF_UNSPEC;
+
+ /// Offset from @a this to the VLA.
+ unsigned short rr_offset = 0;
+ /// Number of @c HostDBInfo instances.
+ unsigned short rr_count = 0;
+
+ /// Timing data for switch records in the RR.
+ std::atomic<ts_time> rr_ctime{TS_TIME_ZERO};
+
+ /// Hash key.
uint64_t key{0};
- // Application specific data. NOTE: We need an integral number of
- // these per block. This structure is 32 bytes. (at 200k hosts =
- // 8 Meg). Which gives us 7 bytes of application information.
- HostDBApplicationInfo app;
+ /// When the DNS response was received.
+ ts_time ip_timestamp;
- union {
- IpEndpoint ip; ///< IP address / port data.
- unsigned int hostname_offset; ///< Some hostname thing.
- SRVInfo srv;
- } data;
+ /// Valid duration of the DNS response data.
+ /// In the code this functions as the TTL in HostDB calcuations, but may not
+ /// be the response's TTL based upon configuration such as
+ /// proxy.config.hostdb.ttl_mode.
+ ts_seconds ip_timeout_interval;
- unsigned int hostname_offset{0}; // always maintain a permanent copy of the hostname for non-rev dns records.
+ /** Atomically advance the round robin index.
+ *
+ * If multiple threads call this simultaneously each thread will get a distinct return value.
+ *
+ * @return The new round robin index.
+ */
+ unsigned next_rr();
+
+ /** Pick the next round robin and update the record atomically.
+ *
+ * @note This may select a zombie server and reserve it for the caller, therefore the caller must
+ * attempt to connect to the selected target if possible.
+ *
+ * @param now Current time to use for aliveness calculations.
+ * @param fail_window Blackout time for dead servers.
+ * @return Status of the updated target.
+ *
+ * If the return value is @c HostDBInfo::Status::DEAD this means all targets are dead and there is
+ * no valid upstream.
+ *
+ * @note Concurrency - this is not done under lock and depends on the caller for correct use.
+ * For strict round robin, it is a feature that every call will get a distinct index. For
+ * timed round robin, the caller must arrange to have only one thread call this per time interval.
+ */
+ HostDBInfo *select_next_rr(ts_time now, ts_seconds fail_window);
- unsigned int ip_timestamp{0};
+ /// Check if this record is of SRV targets.
+ bool is_srv() const;
- unsigned int ip_timeout_interval{0}; // bounded between 1 and HOST_DB_MAX_TTL (0x1FFFFF, 24 days)
+ /** Query name for the record.
+ * @return A C-string.
+ * If this is a @c HOST record, this is the resolved named and the query was based on the IP address.
+ * Otherwise this is the name used in the DNS query.
+ */
+ char const *name() const;
+
+ /** Query name for the record.
+ * @return A view.
+ * If this is a @c HOST record, this is the resolved named and the query was based on the IP address.
+ * Otherwise this is the name used in the DNS query.
+ * @note Although not included in the view, the name is always nul terminated and the string can
+ * be used as a C-string.
+ */
+ ts::TextView name_view() const;
- unsigned int is_srv : 1;
- unsigned int reverse_dns : 1;
+ /// Get the array of info instances.
+ ts::MemSpan<HostDBInfo> rr_info();
- unsigned int round_robin : 1; // This is the root of a round robin block
- unsigned int round_robin_elt : 1; // This is an address in a round robin block
+ /** Find a host record by IP address.
+ *
+ * @param addr Address key.
+ * @return A pointer to the info instance if a match is found, @c nullptr if not.
+ */
+ HostDBInfo *find(sockaddr const *addr);
+
+ /** Select an upstream target.
+ *
+ * @param now Current time.
+ * @param fail_window Dead server blackout time.
+ * @param hash_addr Inbound remote IP address.
+ * @return A selected target, or @c nullptr if there are no valid targets.
+ *
+ * This accounts for the round robin setting. The default is to use "client affinity" in
+ * which case @a hash_addr is as a hash seed to select the target.
+ *
+ * This may select a zombie target, which can be detected by checking the target's last
+ * failure time. If it is not @c TS_TIME_ZERO the target is a zombie. Other transactions will
+ * be blocked from selecting that target until @a fail_window time has passed.
+ *
+ * In cases other than strict round robin, a base target is selected. If valid, that is returned,
+ * but if not then the targets in this record are searched until a valid one is found. The result
+ * is this can be called to select a target for failover when a previous target fails.
+ */
+ HostDBInfo *select_best_http(ts_time now, ts_seconds fail_window, sockaddr const *hash_addr);
+ HostDBInfo *select_best_srv(char *target, InkRand *rand, ts_time now, ts_seconds fail_window);
- HostDBInfo() : _iobuffer_index{-1} {}
+ bool is_failed() const;
- HostDBInfo(HostDBInfo const &src) : RefCountObj()
- {
- memcpy(static_cast<void *>(this), static_cast<const void *>(&src), sizeof(*this));
- _iobuffer_index = -1;
- }
+ void set_failed();
- HostDBInfo &
- operator=(HostDBInfo const &src)
- {
- if (this != &src) {
- int iob_idx = _iobuffer_index;
- memcpy(static_cast<void *>(this), static_cast<const void *>(&src), sizeof(*this));
- _iobuffer_index = iob_idx;
- }
- return *this;
- }
+ /// @return The time point when the item expires.
+ ts_time expiry_time() const;
- bool
- from_alloc() const
- {
- return _iobuffer_index >= 0;
- }
+ /// @return The age of the DNS response.
+ ts_seconds ip_age() const;
-private:
- // The value of this will be -1 for objects that are not created by the alloc() static member function.
- int _iobuffer_index;
-};
+ /// @return How long before the DNS response becomes stale (i.e., exceeds @a
+ /// ip_timeout_interval from the time of the response).
+ ts_seconds ip_time_remaining() const;
+
+ /// @return Whether the age of the DNS response exceeds @a the user's
+ /// configured proxy.config.hostdb.verify_after value.
+ bool is_ip_configured_stale() const;
+
+ /** Whether we have exceeded the DNS response's TTL (i.e., whether the DNS
+ * response is stale). */
+ bool is_ip_timeout() const;
+
+ bool is_ip_fail_timeout() const;
+
+ void refresh_ip();
-struct HostDBRoundRobin {
- /** Total number (to compute space used). */
- short rrcount = 0;
+ /** Whether the DNS response can still be used per
+ * proxy.config.hostdb.serve_stale_for configuration.
+ *
+ * @return False if serve_stale_for is not configured or if the DNS
+ * response's age is less than TTL + the serve_stale_for value. Note that
+ * this function will return true for DNS responses whose age is less than
+ * TTL (i.e., for responses that are not yet stale).
+ */
+ bool serve_stale_but_revalidate() const;
+
+ /// Deallocate @a this.
+ void free() override;
- /** Number which have not failed a connect. */
- short good = 0;
+ /** The current round robin index.
+ *
+ * @return The current index.
+ *
+ * @note The internal index may be out of range due to concurrency constraints - this insures the
+ * returned valu is in range.
+ */
+ unsigned short rr_idx() const;
- unsigned short current = 0;
- ink_time_t timed_rr_ctime = 0;
+ /** Offset from the current round robin index.
+ *
+ * @param delta Distance from the current index.
+ * @return The effective index.
+ */
+ unsigned short rr_idx(unsigned short delta) const;
- // This is the equivalent of a variable length array, we can't use a VLA because
- // HostDBInfo is a non-POD type-- so this is the best we can do.
- HostDBInfo &
- info(short n)
+ /// The index of @a target in this record.
+ int index_of(HostDBInfo const *target) const;
+
+ /** Allocation and initialize an instance from a serialized buffer.
+ *
+ * @param buff Serialization data.
+ * @param size Size of @a buff.
+ * @return An instance initialized from @a buff.
+ */
+ static self_type *unmarshall(char *buff, unsigned size);
+
+ /// Database version.
+ static constexpr ts::VersionNumber Version{3, 0};
+
+protected:
+ /// Current active info.
+ /// @note This value may be out of range due to the difficulty of synchronization, therefore
+ /// must always be taken modulus @c rr_count when used. Use the @c rr_idx() method unless
+ /// raw access is required.
+ std::atomic<unsigned short> _rr_idx = 0;
+
+ /** Access an internal object at @a offset.
+ *
+ * @tparam T Type of object.
+ * @param offset Offset of object.
+ * @return A pointer to the object of type @a T.
+ *
+ * @a offset is applied to @a this record and the result cast to a pointer to @a T.
+ *
+ * @note @a offset based at @a this.
+ */
+ template <typename T>
+ T *
+ apply_offset(unsigned offset)
{
- ink_assert(n < rrcount && n >= 0);
- return *((HostDBInfo *)((char *)this + sizeof(HostDBRoundRobin)) + n);
+ return reinterpret_cast<T *>(reinterpret_cast<char *>(this) + offset);
}
- // Return the allocation size of a HostDBRoundRobin struct suitable for storing
- // "count" HostDBInfo records.
- static unsigned
- size(unsigned count, unsigned srv_len = 0)
+ template <typename T>
+ T const *
+ apply_offset(unsigned offset) const
{
- ink_assert(count > 0);
- return INK_ALIGN((sizeof(HostDBRoundRobin) + (count * sizeof(HostDBInfo)) + srv_len), 8);
+ return reinterpret_cast<T const *>(reinterpret_cast<char const *>(this) + offset);
}
- /** Find the index of @a addr in member @a info.
- @return The index if found, -1 if not found.
- */
- int index_of(sockaddr const *addr);
- HostDBInfo *find_ip(sockaddr const *addr);
- // Find the srv target
- HostDBInfo *find_target(const char *target);
- /** Select the next entry after @a addr.
- @note If @a addr isn't an address in the round robin nothing is updated.
- @return The selected entry or @c nullptr if @a addr wasn't present.
- */
- HostDBInfo *select_next(sockaddr const *addr);
- HostDBInfo *select_best_http(sockaddr const *client_ip, ink_time_t now, int32_t fail_window);
- HostDBInfo *select_best_srv(char *target, InkRand *rand, ink_time_t now, int32_t fail_window);
- HostDBRoundRobin() {}
+ union {
+ uint16_t all;
+ struct {
+ unsigned failed_p : 1; ///< DNS error.
+ } f;
+ } flags{0};
};
struct HostDBCache;
@@ -424,10 +533,123 @@ struct HostDBHash;
// Prototype for inline completion function or
// getbyname_imm()
-typedef void (Continuation::*cb_process_result_pfn)(HostDBInfo *r);
+using cb_process_result_pfn = void (Continuation::*)(HostDBRecord *r);
Action *iterate(Continuation *cont);
+/** Information for doing host resolution for a request.
+ *
+ * This is effectively a state object for a request attempting to connect upstream. Information about its attempt
+ * that are local to the request are kept here, while shared data is accessed via the @c HostDBInfo pointers.
+ *
+ * A primitive version of the IP address generator concept.
+ */
+struct ResolveInfo {
+ using self_type = ResolveInfo; ///< Self reference type.
+
+ /// Not quite sure what this is for.
+ enum UpstreamResolveStyle { UNDEFINED_LOOKUP, ORIGIN_SERVER, PARENT_PROXY, HOST_NONE };
+
+ /** Origin server address source selection.
+
+ If config says to use CTA (client target addr) state is TRY_CLIENT, otherwise it
+ remains the default. If the connect fails then we switch to a USE. We go to USE_HOSTDB if (1)
+ the HostDB lookup is successful and (2) some address other than the CTA is available to try.
+ Otherwise we keep retrying on the CTA (USE_CLIENT) up to the max retry value. In essence we
+ try to treat the CTA as if it were another RR value in the HostDB record.
+ */
+ enum class OS_Addr {
+ TRY_DEFAULT, ///< Initial state, use what config says.
+ TRY_HOSTDB, ///< Try HostDB data.
+ TRY_CLIENT, ///< Try client target addr.
+ USE_HOSTDB, ///< Force use of HostDB target address.
+ USE_CLIENT, ///< Force client target addr.
+ USE_API ///< Use the API provided address.
+ };
+
+ ResolveInfo() = default;
+ ~ResolveInfo() = default;
+
+ /// Keep a reference to the base HostDB object, so it doesn't get GC'd.
+ Ptr<HostDBRecord> record;
+ HostDBInfo *active = nullptr; ///< Active host record.
+
+ /// Working address. The meaning / source of the value depends on other elements.
+ /// This is the "resolved" address if @a resolved_p is @c true.
+ IpEndpoint addr;
+
+ int attempts = 0; ///< Number of connection attempts.
+
+ char const *lookup_name = nullptr;
+ char srv_hostname[MAXDNAME] = {0};
+ const sockaddr *inbound_remote_addr = nullptr; ///< Remote address of inbound client - used for hashing.
+ in_port_t srv_port = 0; ///< Port from SRV lookup or API call.
+
+ OS_Addr os_addr_style = OS_Addr::TRY_DEFAULT;
+ HostResStyle host_res_style = HOST_RES_IPV4;
+ UpstreamResolveStyle looking_up = UNDEFINED_LOOKUP;
+
+ HTTPVersion http_version = HTTP_INVALID;
+
+ bool resolved_p = false; ///< If there is a valid, resolved address in @a addr.
+
+ /// Flag for @a addr being set externally.
+ // bool api_addr_set_p = false;
+
+ /*** Set to true by default. If use_client_target_address is set
+ * to 1, this value will be set to false if the client address is
+ * not in the DNS pool */
+ bool cta_validated_p = true;
+
+ bool set_active(HostDBInfo *info);
+
+ bool set_active(sockaddr const *s);
+
+ bool set_active(std::nullptr_t);
+
+ /** Force a resolved address.
+ *
+ * @param sa Address to use for the upstream.
+ * @return @c true if successful, @c false if error.
+ *
+ * This fails if @a sa isn't a valid IP address.
+ */
+ bool set_upstream_address(const sockaddr *sa);
+
+ bool set_upstream_address(IpAddr const &ip_addr);
+
+ void set_upstream_port(in_port_t port);
+
+ /** Check and (if possible) immediately resolve the upstream address without consulting the HostDB.
+ * The cases where this is successful are
+ * - The address is already resolved (@a resolved_p is @c true).
+ * - The upstream was set explicitly.
+ * - The hostname is a valid IP address.
+ *
+ * @return @c true if the upstream address was resolved, @c false if not.
+ */
+ bool resolve_immediate();
+
+ /** Mark the active target as dead.
+ *
+ * @param now Time of failure.
+ * @return @c true if the server was marked as dead, @c false if not.
+ *
+ */
+ bool mark_active_server_dead(ts_time now);
+
+ /** Mark the active target as alive.
+ *
+ * @return @c true if the target changed state.
+ */
+ bool mark_active_server_alive();
+
+ /// Select / resolve to the next RR entry for the record.
+ bool select_next_rr();
+
+ bool is_srv() const;
+};
+
/** The Host Database access interface. */
struct HostDBProcessor : public Processor {
friend struct HostDBSync;
@@ -486,29 +708,6 @@ struct HostDBProcessor : public Processor {
/** Lookup Hostinfo by addr */
Action *getbyaddr_re(Continuation *cont, sockaddr const *aip);
- /** Set the application information (fire-and-forget). */
- void
- setbyname_appinfo(char *hostname, int len, int port, HostDBApplicationInfo *app)
- {
- sockaddr_in addr;
- ats_ip4_set(&addr, INADDR_ANY, port);
- setby(hostname, len, ats_ip_sa_cast(&addr), app);
- }
-
- void
- setbyaddr_appinfo(sockaddr const *addr, HostDBApplicationInfo *app)
- {
- this->setby(nullptr, 0, addr, app);
- }
-
- void
- setbyaddr_appinfo(in_addr_t ip, HostDBApplicationInfo *app)
- {
- sockaddr_in addr;
- ats_ip4_set(&addr, ip);
- this->setby(nullptr, 0, ats_ip_sa_cast(&addr), app);
- }
-
/** Configuration. */
static int hostdb_strict_round_robin;
static int hostdb_timed_round_robin;
@@ -524,21 +723,154 @@ struct HostDBProcessor : public Processor {
private:
Action *getby(Continuation *cont, cb_process_result_pfn cb_process_result, HostDBHash &hash, Options const &opt);
+};
-public:
- /** Set something.
- @a aip can carry address and / or port information. If setting just
- by a port value, the address should be set to INADDR_ANY which is of
- type IPv4.
- */
- void setby(const char *hostname, ///< Hostname.
- int len, ///< Length of hostname.
- sockaddr const *aip, ///< Address and/or port.
- HostDBApplicationInfo *app ///< I don't know.
- );
+inline bool
+HostDBRecord::is_srv() const
+{
+ return HostDBType::SRV == record_type;
+}
- void setby_srv(const char *hostname, int len, const char *target, HostDBApplicationInfo *app);
-};
+inline char const *
+HostDBRecord::name() const
+{
+ return this->apply_offset<char const>(sizeof(self_type));
+}
+
+inline ts::TextView
+HostDBRecord::name_view() const
+{
+ return {this->name(), ts::TextView::npos};
+}
+
+inline ts_time
+HostDBRecord::expiry_time() const
+{
+ return ip_timestamp + ip_timeout_interval + ts_seconds(hostdb_serve_stale_but_revalidate);
+}
+
+inline ts_seconds
+HostDBRecord::ip_age() const
+{
+ static constexpr ts_seconds ZERO{0};
+ static constexpr ts_seconds MAX{0x7FFFFFFF};
+ return std::clamp(std::chrono::duration_cast<ts_seconds>(hostdb_current_timestamp - ip_timestamp), ZERO, MAX);
+}
+
+inline ts_seconds
+HostDBRecord::ip_time_remaining() const
+{
+ static constexpr ts_seconds ZERO{0};
+ static constexpr ts_seconds MAX{0x7FFFFFFF};
+ return std::clamp(std::chrono::duration_cast<ts_seconds>(ip_timeout_interval - this->ip_age()), ZERO, MAX);
+}
+
+inline bool
+HostDBRecord::is_ip_configured_stale() const
+{
+ return (
+ ((ip_timeout_interval >= ts_seconds(2 * hostdb_ip_stale_interval)) && (ip_age() >= ts_seconds(hostdb_ip_stale_interval))));
+}
+
+inline bool
+HostDBRecord::is_ip_timeout() const
+{
+ return ip_age() >= ip_timeout_interval;
+}
+
+inline bool
+HostDBRecord::is_ip_fail_timeout() const
+{
+ return ip_age() >= ts_seconds(hostdb_ip_fail_timeout_interval);
+}
+
+inline void
+HostDBRecord::refresh_ip()
+{
+ ip_timestamp = hostdb_current_timestamp;
+}
+
+inline ts::MemSpan<HostDBInfo>
+HostDBRecord::rr_info()
+{
+ return {this->apply_offset<HostDBInfo>(rr_offset), rr_count};
+}
+
+inline bool
+HostDBRecord::is_failed() const
+{
+ return flags.f.failed_p;
+}
+
+inline void
+HostDBRecord::set_failed()
+{
+ flags.f.failed_p = true;
+}
+
+inline unsigned short
+HostDBRecord::rr_idx() const
+{
+ return _rr_idx % rr_count;
+}
+
+inline unsigned short
+HostDBRecord::rr_idx(unsigned short delta) const
+{
+ return (_rr_idx + delta) % rr_count;
+}
+
+inline int
+HostDBRecord::index_of(HostDBInfo const *target) const
+{
+ return target ? target - this->apply_offset<HostDBInfo>(rr_offset) : -1;
+}
+
+// --
+
+inline bool
+ResolveInfo::set_active(sockaddr const *s)
+{
+ return this->set_active(record->find(s));
+}
+
+inline bool
+ResolveInfo::mark_active_server_alive()
+{
+ return active->mark_up();
+}
+
+inline bool
+ResolveInfo::mark_active_server_dead(ts_time now)
+{
+ return active != nullptr && active->mark_down(now);
+}
+
+inline bool ResolveInfo::set_active(std::nullptr_t)
+{
+ active = nullptr;
+ resolved_p = false;
+ return false;
+}
+
+inline bool
+ResolveInfo::set_upstream_address(sockaddr const *sa)
+{
+ return resolved_p = addr.assign(sa).isValid();
+}
+
+inline void
+ResolveInfo::set_upstream_port(in_port_t port)
+{
+ srv_port = port;
+}
+
+inline bool
+ResolveInfo::is_srv() const
+{
+ return record && record->is_srv();
+}
+// ---
void run_HostDBTest();
diff --git a/iocore/hostdb/P_HostDBProcessor.h b/iocore/hostdb/P_HostDBProcessor.h
index 7a05013c7..3ebd270a8 100644
--- a/iocore/hostdb/P_HostDBProcessor.h
+++ b/iocore/hostdb/P_HostDBProcessor.h
@@ -27,6 +27,8 @@
#pragma once
+#include <shared_mutex>
+
#include "I_HostDBProcessor.h"
#include "tscore/TsBuffer.h"
@@ -51,7 +53,7 @@ extern int hostdb_ttl_mode;
extern int hostdb_srv_enabled;
// extern int hostdb_timestamp;
-extern int hostdb_sync_frequency;
+extern ts_seconds hostdb_sync_frequency;
extern int hostdb_disable_reverse_lookup;
// Static configuration information
@@ -74,26 +76,6 @@ enum HostDBMark {
*/
extern const char *string_for(HostDBMark mark);
-inline unsigned int
-HOSTDB_CLIENT_IP_HASH(sockaddr const *lhs, sockaddr const *rhs)
-{
- unsigned int zret = ~static_cast<unsigned int>(0);
- if (ats_ip_are_compatible(lhs, rhs)) {
- if (ats_is_ip4(lhs)) {
- in_addr_t ip1 = ats_ip4_addr_cast(lhs);
- in_addr_t ip2 = ats_ip4_addr_cast(rhs);
- zret = (ip1 >> 16) ^ ip1 ^ ip2 ^ (ip2 >> 16);
- } else if (ats_is_ip6(lhs)) {
- uint32_t const *ip1 = ats_ip_addr32_cast(lhs);
- uint32_t const *ip2 = ats_ip_addr32_cast(rhs);
- for (int i = 0; i < 4; ++i, ++ip1, ++ip2) {
- zret ^= (*ip1 >> 16) ^ *ip1 ^ *ip2 ^ (*ip2 >> 16);
- }
- }
- }
- return zret & 0xFFFF;
-}
-
//
// Constants
//
@@ -170,21 +152,12 @@ extern RecRawStatBlock *hostdb_rsb;
#define HOSTDB_DECREMENT_THREAD_DYN_STAT(_s, _t) RecIncrRawStatSum(hostdb_rsb, _t, (int)_s, -1);
-struct CmpConstBuffferCaseInsensitive {
- bool
- operator()(ts::ConstBuffer a, ts::ConstBuffer b) const
- {
- return ptr_len_casecmp(a._ptr, a._size, b._ptr, b._size) < 0;
- }
+struct HostFileRecord {
+ HostDBRecord::Handle record_4;
+ HostDBRecord::Handle record_6;
};
-// Our own typedef for the host file mapping
-typedef std::map<ts::ConstBuffer, IpAddr, CmpConstBuffferCaseInsensitive> HostsFileMap;
-// A to hold a ref-counted map
-struct RefCountedHostsFileMap : public RefCountObj {
- HostsFileMap hosts_file_map;
- ats_scoped_str HostFileText;
-};
+using HostFileMap = std::unordered_map<ts::TextView, HostFileRecord, std::hash<std::string_view>>;
//
// HostDBCache (Private)
@@ -192,9 +165,11 @@ struct RefCountedHostsFileMap : public RefCountObj {
struct HostDBCache {
int start(int flags = 0);
// Map to contain all of the host file overrides, initialize it to empty
- Ptr<RefCountedHostsFileMap> hosts_file_ptr;
+ std::shared_ptr<HostFileMap> host_file;
+ std::shared_mutex host_file_mutex;
+
// TODO: make ATS call a close() method or something on shutdown (it does nothing of the sort today)
- RefCountCache<HostDBInfo> *refcountcache = nullptr;
+ RefCountCache<HostDBRecord> *refcountcache = nullptr;
// TODO configurable number of items in the cache
Queue<HostDBContinuation, Continuation::Link_link> *pending_dns = nullptr;
@@ -202,189 +177,9 @@ struct HostDBCache {
Queue<HostDBContinuation, Continuation::Link_link> *remoteHostDBQueue = nullptr;
HostDBCache();
bool is_pending_dns_for_hash(const CryptoHash &hash);
-};
-
-inline int
-HostDBRoundRobin::index_of(sockaddr const *ip)
-{
- bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
- (unsigned int)good > hostdb_round_robin_max_count);
- if (bad) {
- ink_assert(!"bad round robin size");
- return -1;
- }
-
- for (int i = 0; i < good; i++) {
- if (ats_ip_addr_eq(ip, info(i).ip())) {
- return i;
- }
- }
-
- return -1;
-}
-
-inline HostDBInfo *
-HostDBRoundRobin::find_ip(sockaddr const *ip)
-{
- int idx = this->index_of(ip);
- return idx < 0 ? nullptr : &info(idx);
-}
-
-inline HostDBInfo *
-HostDBRoundRobin::select_next(sockaddr const *ip)
-{
- HostDBInfo *zret = nullptr;
- if (good > 1) {
- int idx = this->index_of(ip);
- if (idx >= 0) {
- idx = (idx + 1) % good;
- zret = &info(idx);
- }
- }
- return zret;
-}
-
-inline HostDBInfo *
-HostDBRoundRobin::find_target(const char *target)
-{
- bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
- (unsigned int)good > hostdb_round_robin_max_count);
- if (bad) {
- ink_assert(!"bad round robin size");
- return nullptr;
- }
-
- uint32_t key = makeHostHash(target);
- for (int i = 0; i < good; i++) {
- if (info(i).data.srv.key == key && !strcmp(target, info(i).srvname(this)))
- return &info(i);
- }
- return nullptr;
-}
-
-inline HostDBInfo *
-HostDBRoundRobin::select_best_http(sockaddr const *client_ip, ink_time_t now, int32_t fail_window)
-{
- bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
- (unsigned int)good > hostdb_round_robin_max_count);
-
- if (bad) {
- ink_assert(!"bad round robin size");
- return nullptr;
- }
-
- int best_any = 0;
- int best_up = -1;
-
- // Basic round robin, increment current and mod with how many we have
- if (HostDBProcessor::hostdb_strict_round_robin) {
- Debug("hostdb", "Using strict round robin");
- // Check that the host we selected is alive
- for (int i = 0; i < good; i++) {
- best_any = current++ % good;
- if (info(best_any).is_alive(now, fail_window)) {
- best_up = best_any;
- break;
- }
- }
- } else if (HostDBProcessor::hostdb_timed_round_robin > 0) {
- Debug("hostdb", "Using timed round-robin for HTTP");
- if ((now - timed_rr_ctime) > HostDBProcessor::hostdb_timed_round_robin) {
- Debug("hostdb", "Timed interval expired.. rotating");
- ++current;
- timed_rr_ctime = now;
- }
- for (int i = 0; i < good; i++) {
- best_any = (current + i) % good;
- if (info(best_any).is_alive(now, fail_window)) {
- best_up = best_any;
- break;
- }
- }
- Debug("hostdb", "Using %d for best_up", best_up);
- } else {
- Debug("hostdb", "Using default round robin");
- unsigned int best_hash_any = 0;
- unsigned int best_hash_up = 0;
- for (int i = 0; i < good; i++) {
- sockaddr const *ip = info(i).ip();
- unsigned int h = HOSTDB_CLIENT_IP_HASH(client_ip, ip);
- if (best_hash_any <= h) {
- best_any = i;
- best_hash_any = h;
- }
- if (info(i).is_alive(now, fail_window)) {
- if (best_hash_up <= h) {
- best_up = i;
- best_hash_up = h;
- }
- }
- }
- }
-
- if (best_up != -1) {
- ink_assert(best_up >= 0 && best_up < good);
- return &info(best_up);
- } else {
- ink_assert(best_any >= 0 && best_any < good);
- return &info(best_any);
- }
-}
-
-inline HostDBInfo *
-HostDBRoundRobin::select_best_srv(char *target, InkRand *rand, ink_time_t now, int32_t fail_window)
-{
- bool bad = (rrcount <= 0 || (unsigned int)rrcount > hostdb_round_robin_max_count || good <= 0 ||
- (unsigned int)good > hostdb_round_robin_max_count);
-
- if (bad) {
- ink_assert(!"bad round robin size");
- return nullptr;
- }
-
-#ifdef DEBUG
- for (int i = 1; i < good; ++i) {
- ink_assert(info(i).data.srv.srv_priority >= info(i - 1).data.srv.srv_priority);
- }
-#endif
-
- int i = 0;
- int len = 0;
- uint32_t weight = 0, p = INT32_MAX;
- HostDBInfo *result = nullptr;
- HostDBInfo *infos[good];
- do {
- // if the real isn't alive-- exclude it from selection
- if (!info(i).is_alive(now, fail_window)) {
- continue;
- }
-
- if (info(i).data.srv.srv_priority <= p) {
- p = info(i).data.srv.srv_priority;
- weight += info(i).data.srv.srv_weight;
- infos[len++] = &info(i);
- } else
- break;
- } while (++i < good);
-
- if (len == 0) { // all failed
- result = &info(current++ % good);
- } else if (weight == 0) { // srv weight is 0
- result = infos[current++ % len];
- } else {
- uint32_t xx = rand->random() % weight;
- for (i = 0; i < len - 1 && xx >= infos[i]->data.srv.srv_weight; ++i)
- xx -= infos[i]->data.srv.srv_weight;
-
- result = infos[i];
- }
- if (result) {
- ink_strlcpy(target, result->srvname(this), MAXDNAME);
- return result;
- }
- return nullptr;
-}
+ std::shared_ptr<HostFileMap> acquire_host_file();
+};
//
// Types
@@ -398,10 +193,9 @@ struct HostDBHash {
CryptoHash hash; ///< The hash value.
- const char *host_name = nullptr; ///< Host name.
- int host_len = 0; ///< Length of @a _host_name
- IpAddr ip; ///< IP address.
- in_port_t port = 0; ///< IP port (host order).
+ ts::TextView host_name; ///< Name of the host for the query.
+ IpAddr ip; ///< IP address.
+ in_port_t port = 0; ///< IP port (host order).
/// DNS server. Not strictly part of the hash data but
/// it's both used by @c HostDBContinuation and provides access to
/// hash data. It's just handier to store it here for both uses.
@@ -418,20 +212,23 @@ struct HostDBHash {
/** Assign a hostname.
This updates the split DNS data as well.
*/
- self &set_host(const char *name, int len);
+ self &set_host(ts::TextView name);
+ self &
+ set_host(char const *name)
+ {
+ return this->set_host(ts::TextView{name, strlen(name)});
+ }
};
//
// Handles a HostDB lookup request
//
-struct HostDBContinuation;
-typedef int (HostDBContinuation::*HostDBContHandler)(int, void *);
+using HostDBContHandler = int (HostDBContinuation::*)(int, void *);
struct HostDBContinuation : public Continuation {
Action action;
HostDBHash hash;
- // IpEndpoint ip;
- unsigned int ttl = 0;
+ ts_seconds ttl{0};
// HostDBMark db_mark; ///< Target type.
/// Original IP address family style. Note this will disagree with
/// @a hash.db_mark when doing a retry on an alternate family. The retry
@@ -440,9 +237,8 @@ struct HostDBContinuation : public Continuation {
int dns_lookup_timeout = DEFAULT_OPTIONS.timeout;
Event *timeout = nullptr;
Continuation *from_cont = nullptr;
- HostDBApplicationInfo app;
- int probe_depth = 0;
- size_t current_iterate_pos = 0;
+ int probe_depth = 0;
+ size_t current_iterate_pos = 0;
// char name[MAXDNAME];
// int namelen;
char hash_host_name_store[MAXDNAME + 1]; // used as backing store for @a hash
@@ -452,7 +248,6 @@ struct HostDBContinuation : public Continuation {
unsigned int missing : 1;
unsigned int force_dns : 1;
- unsigned int round_robin : 1;
int probeEvent(int event, Event *e);
int iterateEvent(int event, Event *e);
@@ -475,19 +270,23 @@ struct HostDBContinuation : public Continuation {
{
return hash.db_mark == HOSTDB_MARK_SRV;
}
- HostDBInfo *lookup_done(IpAddr const &ip, const char *aname, bool round_robin, unsigned int attl, SRVHosts *s = nullptr,
- HostDBInfo *r = nullptr);
+
+ Ptr<HostDBRecord>
+ lookup_done(const char *query_name, ts_seconds answer_ttl, SRVHosts *s = nullptr, Ptr<HostDBRecord> record = Ptr<HostDBRecord>{})
+ {
+ return this->lookup_done(ts::TextView{query_name, strlen(query_name)}, answer_ttl, s, record);
+ }
+
+ Ptr<HostDBRecord> lookup_done(ts::TextView query_name, ts_seconds answer_ttl, SRVHosts *s = nullptr,
+ Ptr<HostDBRecord> record = Ptr<HostDBRecord>{});
+
int key_partition();
- void remove_trigger_pending_dns();
+ void remove_and_trigger_pending_dns();
int set_check_pending_dns();
- HostDBInfo *insert(unsigned int attl);
-
/** Optional values for @c init.
*/
struct Options {
- typedef Options self; ///< Self reference type.
-
int timeout = 0; ///< Timeout value. Default 0
HostResStyle host_res_style = HOST_RES_NONE; ///< IP address family fallback. Default @c HOST_RES_NONE
bool force_dns = false; ///< Force DNS lookup. Default @c false
@@ -500,7 +299,7 @@ struct HostDBContinuation : public Continuation {
int make_get_message(char *buf, int len);
int make_put_message(HostDBInfo *r, Continuation *c, char *buf, int len);
- HostDBContinuation() : missing(false), force_dns(DEFAULT_OPTIONS.force_dns), round_robin(false)
+ HostDBContinuation() : missing(false), force_dns(DEFAULT_OPTIONS.force_dns)
{
ink_zero(hash_host_name_store);
ink_zero(hash.hash);
@@ -514,12 +313,6 @@ master_hash(CryptoHash const &hash)
return static_cast<int>(hash[1] >> 32);
}
-inline bool
-is_dotted_form_hostname(const char *c)
-{
- return -1 != (int)ink_inet_addr(c);
-}
-
inline Queue<HostDBContinuation> &
HostDBCache::pending_dns_for_hash(const CryptoHash &hash)
{
diff --git a/plugins/lua/ts_lua_misc.c b/plugins/lua/ts_lua_misc.c
index 8859a17ba..f8848f045 100644
--- a/plugins/lua/ts_lua_misc.c
+++ b/plugins/lua/ts_lua_misc.c
@@ -517,12 +517,14 @@ ts_lua_host_lookup_handler(TSCont contp, TSEvent event, void *edata)
} else if (!edata) {
lua_pushnil(L);
} else {
- TSHostLookupResult result = (TSHostLookupResult)edata;
- struct sockaddr const *addr = TSHostLookupResultAddrGet(result);
+ TSHostLookupResult record = (TSHostLookupResult)edata;
+ struct sockaddr const *addr = TSHostLookupResultAddrGet(record);
if (addr->sa_family == AF_INET) {
- inet_ntop(AF_INET, (const void *)&((struct sockaddr_in *)addr)->sin_addr, cip, sizeof(cip));
+ inet_ntop(AF_INET, &((struct sockaddr_in const *)addr)->sin_addr, cip, sizeof(cip));
+ } else if (addr->sa_family == AF_INET6) {
+ inet_ntop(AF_INET6, &((struct sockaddr_in6 const *)addr)->sin6_addr, cip, sizeof(cip));
} else {
- inet_ntop(AF_INET6, (const void *)&((struct sockaddr_in6 *)addr)->sin6_addr, cip, sizeof(cip));
+ cip[0] = 0;
}
lua_pushstring(L, cip);
}
diff --git a/proxy/ControlMatcher.h b/proxy/ControlMatcher.h
index 6c1d36244..6365ee0ed 100644
--- a/proxy/ControlMatcher.h
+++ b/proxy/ControlMatcher.h
@@ -144,10 +144,10 @@ public:
ink_zero(dest_ip);
}
- HTTPHdr *hdr = nullptr;
- char *hostname_str = nullptr;
- HttpApiInfo *api_info = nullptr;
- time_t xact_start = 0;
+ HTTPHdr *hdr = nullptr;
+ char const *hostname_str = nullptr;
+ HttpApiInfo *api_info = nullptr;
+ time_t xact_start = 0;
IpEndpoint src_ip;
IpEndpoint dest_ip;
uint16_t incoming_port = 0;
diff --git a/proxy/ParentSelection.cc b/proxy/ParentSelection.cc
index 0f495f1af..9d5546e2e 100644
--- a/proxy/ParentSelection.cc
+++ b/proxy/ParentSelection.cc
@@ -1068,21 +1068,21 @@ EXCLUSIVE_REGRESSION_TEST(PARENTSELECTION)(RegressionTest * /* t ATS_UNUSED */,
params = new ParentConfigParams(ParentTable); \
} while (0)
-#define REINIT \
- do { \
- if (request != NULL) { \
- delete request->hdr; \
- ats_free(request->hostname_str); \
- delete request->api_info; \
- } \
- delete request; \
- delete result; \
- request = new HttpRequestData(); \
- result = new ParentResult(); \
- if (!result || !request) { \
- (void)printf("Allocation failed\n"); \
- return; \
- } \
+#define REINIT \
+ do { \
+ if (request != nullptr) { \
+ delete request->hdr; \
+ /* ats_free(request->hostname_str); */ \
+ delete request->api_info; \
+ } \
+ delete request; \
+ delete result; \
+ request = new HttpRequestData(); \
+ result = new ParentResult(); \
+ if (!result || !request) { \
+ (void)printf("Allocation failed\n"); \
+ return; \
+ } \
} while (0)
#define ST(x) \
diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc
index d7798dbb1..7fe46cef7 100644
--- a/proxy/http/HttpConfig.cc
+++ b/proxy/http/HttpConfig.cc
@@ -35,6 +35,8 @@
#include <records/I_RecHttp.h>
#include "HttpSessionManager.h"
+extern void HostDB_Config_Init();
+
#define HttpEstablishStaticConfigStringAlloc(_ix, _n) \
REC_EstablishStaticConfigStringAlloc(_ix, _n); \
REC_RegisterConfigUpdateFunc(_n, http_config_cb, NULL)
@@ -1372,7 +1374,7 @@ HttpConfig::startup()
HttpEstablishStaticConfigByte(c.referer_filter_enabled, "proxy.config.http.referer_filter");
HttpEstablishStaticConfigByte(c.referer_format_redirect, "proxy.config.http.referer_format_redirect");
- HttpEstablishStaticConfigLongLong(c.oride.down_server_timeout, "proxy.config.http.down_server.cache_time");
+ HostDB_Config_Init();
// Negative caching and revalidation
HttpEstablishStaticConfigByte(c.oride.negative_caching_enabled, "proxy.config.http.negative_caching_enabled");
diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h
index 03c4904a0..cc6b3e2e9 100644
--- a/proxy/http/HttpConfig.h
+++ b/proxy/http/HttpConfig.h
@@ -40,6 +40,8 @@
#include <cctype>
#include <string_view>
+#include <chrono>
+
#include "tscore/ink_platform.h"
#include "tscore/ink_inet.h"
#include "tscore/ink_resolver.h"
@@ -688,7 +690,7 @@ struct OverridableHttpConfigParams {
MgmtByte enable_parent_timeout_markdowns = 0;
MgmtByte disable_parent_markdowns = 0;
- MgmtInt down_server_timeout = 300;
+ ts_seconds down_server_timeout{300};
MgmtInt client_abort_threshold = 1000;
// open read failure retries.
diff --git a/proxy/http/HttpConnectionCount.cc b/proxy/http/HttpConnectionCount.cc
index cd64e8d38..009ea8f33 100644
--- a/proxy/http/HttpConnectionCount.cc
+++ b/proxy/http/HttpConnectionCount.cc
@@ -31,7 +31,7 @@
using namespace std::literals;
-extern int http_config_cb(const char *, RecDataT, RecData, void *);
+extern void Enable_Config_Var(std::string_view const &name, bool (*cb)(const char *, RecDataT, RecData, void *), void *cookie);
OutboundConnTrack::Imp OutboundConnTrack::_imp;
diff --git a/proxy/http/HttpSM.cc b/proxy/http/HttpSM.cc
index ded644801..813b6032c 100644
--- a/proxy/http/HttpSM.cc
+++ b/proxy/http/HttpSM.cc
@@ -1867,7 +1867,7 @@ HttpSM::state_http_server_open(int event, void *data)
NetVConnection *netvc = static_cast<NetVConnection *>(data);
UnixNetVConnection *vc = static_cast<UnixNetVConnection *>(data);
PoolableSession *new_session = this->create_server_session(netvc);
- if (t_state.current.request_to == HttpTransact::PARENT_PROXY) {
+ if (t_state.current.request_to == ResolveInfo::PARENT_PROXY) {
new_session->to_parent_proxy = true;
HTTP_INCREMENT_DYN_STAT(http_current_parent_proxy_connections_stat);
HTTP_INCREMENT_DYN_STAT(http_total_parent_proxy_connections_stat);
@@ -2230,34 +2230,28 @@ HttpSM::state_send_server_request_header(int event, void *data)
}
void
-HttpSM::process_srv_info(HostDBInfo *r)
+HttpSM::process_srv_info(HostDBRecord *record)
{
SMDebug("dns_srv", "beginning process_srv_info");
- t_state.hostdb_entry = Ptr<HostDBInfo>(r);
+ t_state.dns_info.record = record;
/* we didn't get any SRV records, continue w normal lookup */
- if (!r || !r->is_srv || !r->round_robin) {
- t_state.dns_info.srv_hostname[0] = '\0';
- t_state.dns_info.srv_lookup_success = false;
- t_state.my_txn_conf().srv_enabled = false;
+ if (!record || !record->is_srv()) {
+ t_state.dns_info.srv_hostname[0] = '\0';
+ t_state.dns_info.resolved_p = false;
+ t_state.my_txn_conf().srv_enabled = false;
SMDebug("dns_srv", "No SRV records were available, continuing to lookup %s", t_state.dns_info.lookup_name);
} else {
- HostDBRoundRobin *rr = r->rr();
- HostDBInfo *srv = nullptr;
- if (rr) {
- srv = rr->select_best_srv(t_state.dns_info.srv_hostname, &mutex->thread_holding->generator, ink_local_time(),
- static_cast<int>(t_state.txn_conf->down_server_timeout));
- }
+ HostDBInfo *srv = record->select_best_srv(t_state.dns_info.srv_hostname, &mutex->thread_holding->generator, ts_clock::now(),
+ t_state.txn_conf->down_server_timeout);
if (!srv) {
- t_state.dns_info.srv_lookup_success = false;
- t_state.dns_info.srv_hostname[0] = '\0';
- t_state.my_txn_conf().srv_enabled = false;
+ // t_state.dns_info.srv_lookup_success = false;
+ t_state.dns_info.srv_hostname[0] = '\0';
+ t_state.my_txn_conf().srv_enabled = false;
SMDebug("dns_srv", "SRV records empty for %s", t_state.dns_info.lookup_name);
} else {
- t_state.dns_info.srv_lookup_success = true;
- t_state.dns_info.srv_port = srv->data.srv.srv_port;
- t_state.dns_info.srv_app = srv->app;
- // t_state.dns_info.single_srv = (rr->good == 1);
+ t_state.dns_info.resolved_p = false;
+ t_state.dns_info.srv_port = srv->data.srv.srv_port;
ink_assert(srv->data.srv.key == makeHostHash(t_state.dns_info.srv_hostname));
SMDebug("dns_srv", "select SRV records %s", t_state.dns_info.srv_hostname);
}
@@ -2266,87 +2260,43 @@ HttpSM::process_srv_info(HostDBInfo *r)
}
void
-HttpSM::process_hostdb_info(HostDBInfo *r)
+HttpSM::process_hostdb_info(HostDBRecord *record)
{
- // Increment the refcount to our item, since we are pointing at it
- t_state.hostdb_entry = Ptr<HostDBInfo>(r);
+ t_state.dns_info.record = record; // protect record.
+
+ bool use_client_addr = t_state.http_config_param->use_client_target_addr == 1 && t_state.client_info.is_transparent &&
+ t_state.dns_info.os_addr_style == ResolveInfo::OS_Addr::TRY_DEFAULT;
+
+ t_state.dns_info.set_active(nullptr);
- sockaddr const *client_addr = nullptr;
- bool use_client_addr = t_state.http_config_param->use_client_target_addr == 1 && t_state.client_info.is_transparent &&
- t_state.dns_info.os_addr_style == HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_DEFAULT;
if (use_client_addr) {
NetVConnection *vc = ua_txn ? ua_txn->get_netvc() : nullptr;
if (vc) {
- client_addr = vc->get_local_addr();
- // Regardless of whether the client address matches the DNS record or not,
- // we want to use that address. Therefore, we copy over the client address
- // info and skip the assignment from the DNS cache
- ats_ip_copy(t_state.host_db_info.ip(), client_addr);
- t_state.dns_info.os_addr_style = HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT;
- t_state.dns_info.lookup_success = true;
- // Leave ret unassigned, so we don't overwrite the host_db_info
- } else {
- use_client_addr = false;
+ t_state.dns_info.set_upstream_address(vc->get_local_addr());
+ t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::TRY_CLIENT;
}
}
- if (r && !r->is_failed()) {
- ink_time_t now = ink_local_time();
- HostDBInfo *ret = nullptr;
- t_state.dns_info.lookup_success = true;
- t_state.dns_info.lookup_validated = true;
-
- HostDBRoundRobin *rr = r->round_robin ? r->rr() : nullptr;
- if (rr) {
+ if (record && !record->is_failed()) {
+ t_state.dns_info.inbound_remote_addr = &t_state.client_info.src_addr.sa;
+ if (!use_client_addr) {
+ t_state.dns_info.set_active(
+ record->select_best_http(ts_clock::now(), t_state.txn_conf->down_server_timeout, t_state.dns_info.inbound_remote_addr));
+ } else {
// if use_client_target_addr is set, make sure the client addr is in the results pool
- if (use_client_addr && rr->find_ip(client_addr) == nullptr) {
+ t_state.dns_info.cta_validated_p = true;
+ t_state.dns_info.record = record; // Cache this but do not make it active.
+ if (record->find(t_state.dns_info.addr) == nullptr) {
SMDebug("http", "use_client_target_addr == 1. Client specified address is not in the pool, not validated.");
- t_state.dns_info.lookup_validated = false;
- } else {
- // Since the time elapsed between current time and client_request_time
- // may be very large, we cannot use client_request_time to approximate
- // current time when calling select_best_http().
- ret = rr->select_best_http(&t_state.client_info.src_addr.sa, now, static_cast<int>(t_state.txn_conf->down_server_timeout));
- // set the srv target`s last_failure
- if (t_state.dns_info.srv_lookup_success) {
- uint32_t last_failure = 0xFFFFFFFF;
- for (int i = 0; i < rr->rrcount && last_failure != 0; ++i) {
- if (last_failure > rr->info(i).app.http_data.last_failure) {
- last_failure = rr->info(i).app.http_data.last_failure;
- }
- }
-
- if (last_failure != 0 && static_cast<uint32_t>(now - t_state.txn_conf->down_server_timeout) < last_failure) {
- HostDBApplicationInfo app;
- app.allotment.application1 = 0;
- app.allotment.application2 = 0;
- app.http_data.last_failure = last_failure;
- hostDBProcessor.setby_srv(t_state.dns_info.lookup_name, 0, t_state.dns_info.srv_hostname, &app);
- }
- }
- }
- } else {
- if (use_client_addr && !ats_ip_addr_eq(client_addr, &r->data.ip.sa)) {
- SMDebug("http", "use_client_target_addr == 1. Comparing single addresses failed, not validated.");
- t_state.dns_info.lookup_validated = false;
- } else {
- ret = r;
+ t_state.dns_info.cta_validated_p = false;
}
}
- if (ret) {
- t_state.host_db_info = *ret;
- ink_release_assert(!t_state.host_db_info.reverse_dns);
- ink_release_assert(ats_is_ip(t_state.host_db_info.ip()));
- }
} else {
SMDebug("http", "DNS lookup failed for '%s'", t_state.dns_info.lookup_name);
+ }
- if (!use_client_addr) {
- t_state.dns_info.lookup_success = false;
- }
- t_state.host_db_info.app.allotment.application1 = 0;
- t_state.host_db_info.app.allotment.application2 = 0;
- ink_assert(!t_state.host_db_info.round_robin);
+ if (!t_state.dns_info.resolved_p) {
+ SMDebug("http", "[%" PRId64 "] resolution failed for '%s'", sm_id, t_state.dns_info.lookup_name);
}
milestones[TS_MILESTONE_DNS_LOOKUP_END] = Thread::get_hrtime();
@@ -2359,6 +2309,13 @@ HttpSM::process_hostdb_info(HostDBInfo *r)
}
}
+int
+HttpSM::state_pre_resolve(int event, void *data)
+{
+ STATE_ENTER(&HttpSM::state_hostdb_lookup, event);
+ return 0;
+}
+
//////////////////////////////////////////////////////////////////////////////
//
// HttpSM::state_hostdb_lookup()
@@ -2377,16 +2334,16 @@ HttpSM::state_hostdb_lookup(int event, void *data)
switch (event) {
case EVENT_HOST_DB_LOOKUP:
pending_action = nullptr;
- process_hostdb_info(static_cast<HostDBInfo *>(data));
+ process_hostdb_info(static_cast<HostDBRecord *>(data));
call_transact_and_set_next_state(nullptr);
break;
case EVENT_SRV_LOOKUP: {
pending_action = nullptr;
- process_srv_info(static_cast<HostDBInfo *>(data));
+ process_srv_info(static_cast<HostDBRecord *>(data));
- char *host_name = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_hostname : t_state.dns_info.lookup_name;
+ char const *host_name = t_state.dns_info.is_srv() ? t_state.dns_info.record->name() : t_state.dns_info.lookup_name;
HostDBProcessor::Options opt;
- opt.port = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_port : t_state.server_info.dst_addr.host_order_port();
+ opt.port = t_state.dns_info.is_srv() ? t_state.dns_info.srv_port : t_state.server_info.dst_addr.host_order_port();
opt.flags = (t_state.cache_info.directives.does_client_permit_dns_storing) ? HostDBProcessor::HOSTDB_DO_NOT_FORCE_DNS :
HostDBProcessor::HOSTDB_FORCE_DNS_RELOAD;
opt.timeout = (t_state.api_txn_dns_timeout_value != -1) ? t_state.api_txn_dns_timeout_value : 0;
@@ -2421,7 +2378,7 @@ HttpSM::state_hostdb_reverse_lookup(int event, void *data)
case EVENT_HOST_DB_LOOKUP:
pending_action = nullptr;
if (data) {
- t_state.request_data.hostname_str = (static_cast<HostDBInfo *>(data))->hostname();
+ t_state.request_data.hostname_str = (static_cast<HostDBRecord *>(data))->name();
} else {
SMDebug("http", "reverse DNS lookup failed for '%s'", t_state.dns_info.lookup_name);
}
@@ -2443,27 +2400,15 @@ int
HttpSM::state_mark_os_down(int event, void *data)
{
STATE_ENTER(&HttpSM::state_mark_os_down, event);
- HostDBInfo *mark_down = nullptr;
if (event == EVENT_HOST_DB_LOOKUP && data) {
- HostDBInfo *r = static_cast<HostDBInfo *>(data);
-
- if (r->round_robin) {
- // Look for the entry we need mark down in the round robin
- ink_assert(t_state.current.server != nullptr);
- ink_assert(t_state.current.request_to == HttpTransact::ORIGIN_SERVER);
- if (t_state.current.server) {
- mark_down = r->rr()->find_ip(&t_state.current.server->dst_addr.sa);
- }
- } else {
- // No longer a round robin, check to see if our address is the same
- if (ats_ip_addr_eq(t_state.host_db_info.ip(), r->ip())) {
- mark_down = r;
- }
- }
+ auto r = static_cast<HostDBRecord *>(data);
- if (mark_down) {
- mark_host_failure(mark_down, t_state.request_sent_time);
+ // Look for the entry we need mark down in the round robin
+ ink_assert(t_state.current.server != nullptr);
+ ink_assert(t_state.dns_info.looking_up == ResolveInfo::ORIGIN_SERVER);
+ if (auto *info = r->find(&t_state.dns_info.addr.sa); info != nullptr) {
+ info->mark_down(ts_clock::now());
}
}
// We either found our entry or we did not. Either way find
@@ -4260,8 +4205,8 @@ HttpSM::do_hostdb_lookup()
}
pending_action = hostDBProcessor.getSRVbyname_imm(this, (cb_process_result_pfn)&HttpSM::process_srv_info, d, 0, opt);
if (pending_action.empty()) {
- char *host_name = t_state.dns_info.srv_lookup_success ? t_state.dns_info.srv_hostname : t_state.dns_info.lookup_name;
- opt.port = t_state.dns_info.srv_lookup_success ?
+ char const *host_name = t_state.dns_info.resolved_p ? t_state.dns_info.srv_hostname : t_state.dns_info.lookup_name;
+ opt.port = t_state.dns_info.resolved_p ?
t_state.dns_info.srv_port :
t_state.server_info.dst_addr.isValid() ? t_state.server_info.dst_addr.host_order_port() :
t_state.hdr_info.client_request.port_get();
@@ -4347,18 +4292,10 @@ HttpSM::track_connect_fail() const
void
HttpSM::do_hostdb_update_if_necessary()
{
- int issue_update = 0;
-
- if (t_state.current.server == nullptr || plugin_tunnel_type != HTTP_NO_PLUGIN_TUNNEL) {
+ if (t_state.current.server == nullptr || plugin_tunnel_type != HTTP_NO_PLUGIN_TUNNEL || t_state.dns_info.active == nullptr) {
// No server, so update is not necessary
return;
}
- // If we failed back over to the origin server, we don't have our
- // hostdb information anymore which means we shouldn't update the hostdb
- if (!ats_ip_addr_eq(&t_state.current.server->dst_addr.sa, t_state.host_db_info.ip())) {
- SMDebug("http", "skipping hostdb update due to server failover");
- return;
- }
if (t_state.updated_server_version != HTTP_INVALID) {
// we may have incorrectly assumed that the hostdb had the wrong version of
@@ -4368,39 +4305,25 @@ HttpSM::do_hostdb_update_if_necessary()
//
// This test therefore just issues the update only if the hostdb version is
// in fact different from the version we want the value to be updated to.
- if (t_state.host_db_info.app.http_data.http_version != t_state.updated_server_version) {
- t_state.host_db_info.app.http_data.http_version = t_state.updated_server_version;
- issue_update |= 1;
- }
-
- t_state.updated_server_version = HTTP_INVALID;
+ t_state.updated_server_version = HTTP_INVALID;
+ t_state.dns_info.active->http_version = t_state.updated_server_version;
}
+
// Check to see if we need to report or clear a connection failure
if (track_connect_fail()) {
- issue_update |= 1;
- mark_host_failure(&t_state.host_db_info, t_state.client_request_time);
+ this->mark_host_failure(&t_state.dns_info, ts_clock::from_time_t(t_state.client_request_time));
} else {
- if (t_state.host_db_info.app.http_data.last_failure != 0) {
- t_state.host_db_info.app.http_data.last_failure = 0;
- t_state.host_db_info.app.http_data.fail_count = 0;
- issue_update |= 1;
- char addrbuf[INET6_ADDRPORTSTRLEN];
- SMDebug("http", "hostdb update marking IP: %s as up",
- ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
- }
-
- if (t_state.dns_info.srv_lookup_success && t_state.dns_info.srv_app.http_data.last_failure != 0) {
- t_state.dns_info.srv_app.http_data.last_failure = 0;
- hostDBProcessor.setby_srv(t_state.dns_info.lookup_name, 0, t_state.dns_info.srv_hostname, &t_state.dns_info.srv_app);
- SMDebug("http", "hostdb update marking SRV: %s as up", t_state.dns_info.srv_hostname);
+ if (t_state.dns_info.mark_active_server_alive()) {
+ if (t_state.dns_info.record->is_srv()) {
+ SMDebug("http", "[%" PRId64 "] hostdb update marking SRV: %s as up", sm_id, t_state.dns_info.record->name());
+ } else {
+ char addrbuf[INET6_ADDRPORTSTRLEN];
+ SMDebug("http", "[%" PRId64 "] hostdb update marking IP: %s as up", sm_id,
+ ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
+ }
}
}
- if (issue_update) {
- hostDBProcessor.setby(t_state.current.server->name, strlen(t_state.current.server->name), &t_state.current.server->dst_addr.sa,
- &t_state.host_db_info.app);
- }
-
char addrbuf[INET6_ADDRPORTSTRLEN];
SMDebug("http", "server info = %s", ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
return;
@@ -4898,7 +4821,7 @@ HttpSM::send_origin_throttled_response()
// if the request is to a parent proxy, do not reset
// t_state.current.attempts so that another parent or
// NextHop may be tried.
- if (t_state.current.request_to != HttpTransact::PARENT_PROXY) {
+ if (t_state.dns_info.looking_up != ResolveInfo::PARENT_PROXY) {
t_state.current.attempts = t_state.txn_conf->connect_attempts_max_retries;
}
t_state.current.state = HttpTransact::OUTBOUND_CONGESTION;
@@ -5537,42 +5460,44 @@ HttpSM::do_transform_open()
}
void
-HttpSM::mark_host_failure(HostDBInfo *info, time_t time_down)
+HttpSM::mark_host_failure(ResolveInfo *info, ts_time time_down)
{
char addrbuf[INET6_ADDRPORTSTRLEN];
- if (time_down) {
- // Increment the fail_count
- ++info->app.http_data.fail_count;
- if (info->app.http_data.fail_count >= t_state.txn_conf->connect_attempts_rr_retries) {
- if (info->app.http_data.last_failure == 0) {
- char *url_str = t_state.hdr_info.client_request.url_string_get(&t_state.arena, nullptr);
- int host_len;
- const char *host_name_ptr = t_state.unmapped_url.host_get(&host_len);
- std::string_view host_name{host_name_ptr, size_t(host_len)};
- ts::bwprint(error_bw_buffer, "CONNECT: {::s} connecting to {} for host='{}' url='{}' marking down",
- ts::bwf::Errno(t_state.current.server->connect_result), t_state.current.server->dst_addr, host_name,
- ts::bwf::FirstOf(url_str, "<none>"));
- Log::error("%s", error_bw_buffer.c_str());
-
- if (url_str) {
- t_state.arena.str_free(url_str);
+ if (info->active) {
+ if (time_down != TS_TIME_ZERO) {
+ // Increment the fail_count
+ if (++info->active->fail_count >= t_state.txn_conf->connect_attempts_rr_retries) {
+ if (info->active) {
+ if (info->active->last_failure.load() == TS_TIME_ZERO) {
+ char *url_str = t_state.hdr_info.client_request.url_string_get(&t_state.arena, nullptr);
+ int host_len;
+ const char *host_name_ptr = t_state.unmapped_url.host_get(&host_len);
+ std::string_view host_name{host_name_ptr, size_t(host_len)};
+ ts::bwprint(error_bw_buffer, "CONNECT : {::s} connecting to {} for host='{}' url='{}' marking down",
+ ts::bwf::Errno(t_state.current.server->connect_result), t_state.current.server->dst_addr, host_name,
+ ts::bwf::FirstOf(url_str, "<none>"));
+ Log::error("%s", error_bw_buffer.c_str());
+
+ if (url_str) {
+ t_state.arena.str_free(url_str);
+ }
+ }
+ info->active->last_failure = time_down;
+ SMDebug("http", "hostdb update marking IP: %s as down",
+ ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
+ } else {
+ SMDebug("http", "hostdb increment IP failcount %s to %d",
+ ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)), info->active->fail_count.load());
}
+ } else { // Clear the failure
+ info->active->fail_count = 0;
+ info->active->last_failure = time_down;
}
- info->app.http_data.last_failure = time_down;
- SMDebug("http", "hostdb update marking IP: %s as down",
- ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
- } else {
- SMDebug("http", "hostdb increment IP failcount %s to %d",
- ats_ip_nptop(&t_state.current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)), info->app.http_data.fail_count);
}
- } else { // Clear the failure
- info->app.http_data.fail_count = 0;
- info->app.http_data.last_failure = time_down;
}
-
#ifdef DEBUG
- ink_assert(ink_local_time() + t_state.txn_conf->down_server_timeout > time_down);
+ ink_assert(std::chrono::system_clock::now() + t_state.txn_conf->down_server_timeout > time_down);
#endif
}
@@ -5839,6 +5764,7 @@ HttpSM::handle_server_setup_error(int event, void *data)
}
}
+ [[maybe_unused]] UnixNetVConnection *dbg_vc = nullptr;
switch (event) {
case VC_EVENT_EOS:
t_state.current.state = HttpTransact::CONNECTION_CLOSED;
@@ -7589,63 +7515,47 @@ HttpSM::set_next_state()
}
case HttpTransact::SM_ACTION_DNS_LOOKUP: {
- sockaddr const *addr;
-
- if (t_state.api_server_addr_set) {
- /* If the API has set the server address before the OS DNS lookup
- * then we can skip the lookup
- */
- ip_text_buffer ipb;
- SMDebug("dns", "Skipping DNS lookup for API supplied target %s.",
- ats_ip_ntop(&t_state.server_info.dst_addr, ipb, sizeof(ipb)));
- // this seems wasteful as we will just copy it right back
- ats_ip_copy(t_state.host_db_info.ip(), &t_state.server_info.dst_addr);
- t_state.dns_info.lookup_success = true;
- call_transact_and_set_next_state(nullptr);
- break;
- } else if (0 == ats_ip_pton(t_state.dns_info.lookup_name, t_state.host_db_info.ip()) &&
- ats_is_ip_loopback(t_state.host_db_info.ip())) {
- // If it's 127.0.0.1 or ::1 don't bother with hostdb
- SMDebug("dns", "Skipping DNS lookup for %s because it's loopback", t_state.dns_info.lookup_name);
- t_state.dns_info.lookup_success = true;
- call_transact_and_set_next_state(nullptr);
- break;
- } else if (t_state.http_config_param->use_client_target_addr == 2 && !t_state.url_remap_success &&
- t_state.parent_result.result != PARENT_SPECIFIED && t_state.client_info.is_transparent &&
- t_state.dns_info.os_addr_style == HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_DEFAULT &&
- ats_is_ip(addr = ua_txn->get_netvc()->get_local_addr())) {
- /* If the connection is client side transparent and the URL
- * was not remapped/directed to parent proxy, we can use the
- * client destination IP address instead of doing a DNS
- * lookup. This is controlled by the 'use_client_target_addr'
- * configuration parameter.
+ if (sockaddr const *addr; t_state.http_config_param->use_client_target_addr == 2 && // no CTA verification
+ !t_state.url_remap_success && // wasn't remapped
+ t_state.parent_result.result != PARENT_SPECIFIED && // no parent.
+ t_state.client_info.is_transparent && // inbound transparent
+ t_state.dns_info.os_addr_style == ResolveInfo::OS_Addr::TRY_DEFAULT && // haven't tried anything yet.
+ ats_is_ip(addr = ua_txn->get_netvc()->get_local_addr())) // valid inbound remote address
+ {
+ /* If the connection is client side transparent and the URL was not remapped/directed to
+ * parent proxy, we can use the client destination IP address instead of doing a DNS lookup.
+ * This is controlled by the 'use_client_target_addr' configuration parameter.
*/
if (is_debug_tag_set("dns")) {
ip_text_buffer ipb;
SMDebug("dns", "Skipping DNS lookup for client supplied target %s.", ats_ip_ntop(addr, ipb, sizeof(ipb)));
}
- ats_ip_copy(t_state.host_db_info.ip(), addr);
- t_state.host_db_info.app.http_data.http_version = t_state.hdr_info.client_request.version_get();
- t_state.dns_info.lookup_success = true;
- // cache this result so we don't have to unreliably duplicate the
- // logic later if the connect fails.
- t_state.dns_info.os_addr_style = HttpTransact::DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT;
+ t_state.dns_info.set_upstream_address(addr);
+
+ // Make a note the CTA is being used - don't do this case again.
+ t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::TRY_CLIENT;
+
+ if (t_state.hdr_info.client_request.version_get() == HTTPVersion(0, 9)) {
+ t_state.dns_info.http_version = HTTP_0_9;
+ } else if (t_state.hdr_info.client_request.version_get() == HTTPVersion(1, 0)) {
+ t_state.dns_info.http_version = HTTP_1_0;
+ } else {
+ t_state.dns_info.http_version = HTTP_1_1;
+ }
+
call_transact_and_set_next_state(nullptr);
break;
- } else if (t_state.parent_result.result == PARENT_UNDEFINED && t_state.dns_info.lookup_success) {
- // Already set, and we don't have a parent proxy to lookup
- ink_assert(ats_is_ip(t_state.host_db_info.ip()));
- SMDebug("dns", "Skipping DNS lookup, provided by plugin");
+ } else if (t_state.dns_info.looking_up == ResolveInfo::ORIGIN_SERVER && t_state.http_config_param->no_dns_forward_to_parent &&
+ t_state.parent_result.result != PARENT_UNDEFINED) {
+ t_state.dns_info.resolved_p = true; // seems dangerous - where's the IP address?
call_transact_and_set_next_state(nullptr);
break;
- } else if (t_state.dns_info.looking_up == HttpTransact::ORIGIN_SERVER && t_state.http_config_param->no_dns_forward_to_parent &&
- t_state.parent_result.result != PARENT_UNDEFINED) {
- t_state.dns_info.lookup_success = true;
+ } else if (t_state.dns_info.resolve_immediate()) {
call_transact_and_set_next_state(nullptr);
break;
}
-
+ // else have to do DNS.
HTTP_SM_SET_DEFAULT_HANDLER(&HttpSM::state_hostdb_lookup);
// We need to close the previous attempt
@@ -7673,7 +7583,7 @@ HttpSM::set_next_state()
}
}
- ink_assert(t_state.dns_info.looking_up != HttpTransact::UNDEFINED_LOOKUP);
+ ink_assert(t_state.dns_info.looking_up != ResolveInfo::UNDEFINED_LOOKUP);
do_hostdb_lookup();
break;
}
@@ -7858,7 +7768,7 @@ HttpSM::set_next_state()
case HttpTransact::SM_ACTION_ORIGIN_SERVER_RR_MARK_DOWN: {
HTTP_SM_SET_DEFAULT_HANDLER(&HttpSM::state_mark_os_down);
- ink_assert(t_state.dns_info.looking_up == HttpTransact::ORIGIN_SERVER);
+ ink_assert(t_state.dns_info.looking_up == ResolveInfo::ORIGIN_SERVER);
// TODO: This might not be optimal (or perhaps even correct), but it will
// effectively mark the host as down. What's odd is that state_mark_os_down
@@ -8126,13 +8036,13 @@ HttpSM::redirect_request(const char *arg_redirect_url, const int arg_redirect_le
t_state.response_received_time = 0;
t_state.next_action = HttpTransact::SM_ACTION_REDIRECT_READ;
// we have a new OS and need to have DNS lookup the new OS
- t_state.dns_info.lookup_success = false;
- t_state.force_dns = false;
+ t_state.dns_info.resolved_p = false;
+ t_state.force_dns = false;
t_state.server_info.clear();
t_state.parent_info.clear();
// Must reset whether the InkAPI has set the destination address
- t_state.api_server_addr_set = false;
+ // t_state.dns_info.api_addr_set_p = false;
if (t_state.txn_conf->cache_http) {
t_state.cache_info.object_read = nullptr;
diff --git a/proxy/http/HttpSM.h b/proxy/http/HttpSM.h
index 0d66cab71..5726f4130 100644
--- a/proxy/http/HttpSM.h
+++ b/proxy/http/HttpSM.h
@@ -261,8 +261,8 @@ public:
// Handles the setting of all state necessary before
// calling transact to process the hostdb lookup
// A NULL 'r' argument indicates the hostdb lookup failed
- void process_hostdb_info(HostDBInfo *r);
- void process_srv_info(HostDBInfo *r);
+ void process_hostdb_info(HostDBRecord *record);
+ void process_srv_info(HostDBRecord *record);
// Called by transact. Synchronous.
VConnection *do_transform_open();
@@ -405,6 +405,7 @@ protected:
int state_read_client_request_header(int event, void *data);
int state_watch_for_client_abort(int event, void *data);
int state_read_push_response_header(int event, void *data);
+ int state_pre_resolve(int event, void *data);
int state_hostdb_lookup(int event, void *data);
int state_hostdb_reverse_lookup(int event, void *data);
int state_mark_os_down(int event, void *data);
@@ -472,7 +473,7 @@ protected:
void handle_server_setup_error(int event, void *data);
void handle_http_server_open();
void handle_post_failure();
- void mark_host_failure(HostDBInfo *info, time_t time_down);
+ void mark_host_failure(ResolveInfo *info, ts_time time_down);
void release_server_session(bool serve_from_cache = false);
void set_ua_abort(HttpTransact::AbortState_t ua_abort, int event);
int write_header_into_buffer(HTTPHdr *h, MIOBuffer *b);
diff --git a/proxy/http/HttpTransact.cc b/proxy/http/HttpTransact.cc
index 3dc23eddf..9f660d420 100644
--- a/proxy/http/HttpTransact.cc
+++ b/proxy/http/HttpTransact.cc
@@ -368,7 +368,7 @@ HttpTransact::is_response_valid(State *s, HTTPHdr *incoming_response)
inline static ParentRetry_t
response_is_retryable(HttpTransact::State *s, HTTPStatus response_code)
{
- if (!HttpTransact::is_response_valid(s, &s->hdr_info.server_response) || s->current.request_to != HttpTransact::PARENT_PROXY) {
+ if (!HttpTransact::is_response_valid(s, &s->hdr_info.server_response) || s->current.request_to != ResolveInfo::PARENT_PROXY) {
return PARENT_RETRY_NONE;
}
if (s->response_action.handled) {
@@ -487,8 +487,8 @@ update_cache_control_information_from_config(HttpTransact::State *s)
bool
HttpTransact::is_server_negative_cached(State *s)
{
- if (s->host_db_info.app.http_data.last_failure != 0 &&
- s->host_db_info.app.http_data.last_failure + s->txn_conf->down_server_timeout > s->client_request_time) {
+ if (s->dns_info.active && s->dns_info.active->last_fail_time() != TS_TIME_ZERO &&
+ s->dns_info.active->last_fail_time() + s->txn_conf->down_server_timeout > ts_clock::from_time_t(s->client_request_time)) {
return true;
} else {
// Make sure some nasty clock skew has not happened
@@ -496,9 +496,10 @@ HttpTransact::is_server_negative_cached(State *s)
// future we should tolerate bogus last failure times. This sets
// the upper bound to the time that we would ever consider a server
// down to 2*down_server_timeout
- if (s->client_request_time + s->txn_conf->down_server_timeout < s->host_db_info.app.http_data.last_failure) {
- s->host_db_info.app.http_data.last_failure = 0;
- s->host_db_info.app.http_data.fail_count = 0;
+ if (s->dns_info.active &&
+ ts_clock::from_time_t(s->client_request_time) + s->txn_conf->down_server_timeout < s->dns_info.active->last_fail_time()) {
+ s->dns_info.active->last_failure = TS_TIME_ZERO;
+ s->dns_info.active->fail_count = 0;
ink_assert(!"extreme clock skew");
return true;
}
@@ -507,8 +508,8 @@ HttpTransact::is_server_negative_cached(State *s)
}
inline static void
-update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAttributes *from, HttpTransact::LookingUp_t who,
- int attempts)
+update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAttributes *from,
+ ResolveInfo::UpstreamResolveStyle who, int attempts)
{
into->request_to = who;
into->server = from;
@@ -516,7 +517,7 @@ update_current_info(HttpTransact::CurrentInfo *into, HttpTransact::ConnectionAtt
}
inline static void
-update_dns_info(HttpTransact::DNSLookupInfo *dns, HttpTransact::CurrentInfo *from)
+update_dns_info(ResolveInfo *dns, HttpTransact::CurrentInfo *from)
{
dns->looking_up = from->request_to;
dns->lookup_name = from->server->name;
@@ -558,7 +559,7 @@ is_negative_caching_appropriate(HttpTransact::State *s)
}
}
-inline static HttpTransact::LookingUp_t
+inline static ResolveInfo::UpstreamResolveStyle
find_server_and_update_current_info(HttpTransact::State *s)
{
int host_len;
@@ -639,16 +640,16 @@ find_server_and_update_current_info(HttpTransact::State *s)
switch (s->parent_result.result) {
case PARENT_SPECIFIED:
s->parent_info.name = s->arena.str_store(s->parent_result.hostname, strlen(s->parent_result.hostname));
- update_current_info(&s->current, &s->parent_info, HttpTransact::PARENT_PROXY, (s->current.attempts)++);
+ update_current_info(&s->current, &s->parent_info, ResolveInfo::PARENT_PROXY, (s->current.attempts)++);
update_dns_info(&s->dns_info, &s->current);
- ink_assert(s->dns_info.looking_up == HttpTransact::PARENT_PROXY);
+ ink_assert(s->dns_info.looking_up == ResolveInfo::PARENT_PROXY);
s->next_hop_scheme = URL_WKSIDX_HTTP;
- return HttpTransact::PARENT_PROXY;
+ return ResolveInfo::PARENT_PROXY;
case PARENT_FAIL:
// No more parents - need to return an error message
- s->current.request_to = HttpTransact::HOST_NONE;
- return HttpTransact::HOST_NONE;
+ s->current.request_to = ResolveInfo::HOST_NONE;
+ return ResolveInfo::HOST_NONE;
case PARENT_DIRECT:
// if the configuration does not allow the origin to be dns'd
@@ -656,15 +657,15 @@ find_server_and_update_current_info(HttpTransact::State *s)
if (s->http_config_param->no_dns_forward_to_parent) {
Warning("no available parents and the config proxy.config.http.no_dns_just_forward_to_parent, prevents origin lookups.");
s->parent_result.result = PARENT_FAIL;
- return HttpTransact::HOST_NONE;
+ return ResolveInfo::HOST_NONE;
}
/* fall through */
default:
- update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, (s->current.attempts)++);
+ update_current_info(&s->current, &s->server_info, ResolveInfo::ORIGIN_SERVER, (s->current.attempts)++);
update_dns_info(&s->dns_info, &s->current);
- ink_assert(s->dns_info.looking_up == HttpTransact::ORIGIN_SERVER);
+ ink_assert(s->dns_info.looking_up == ResolveInfo::ORIGIN_SERVER);
s->next_hop_scheme = s->scheme;
- return HttpTransact::ORIGIN_SERVER;
+ return ResolveInfo::ORIGIN_SERVER;
}
}
@@ -764,7 +765,7 @@ does_method_effect_cache(int method)
inline static HttpTransact::StateMachineAction_t
how_to_open_connection(HttpTransact::State *s)
{
- ink_assert((s->pending_work == nullptr) || (s->current.request_to == HttpTransact::PARENT_PROXY));
+ ink_assert((s->pending_work == nullptr) || (s->current.request_to == ResolveInfo::PARENT_PROXY));
// Originally we returned which type of server to open
// Now, however, we may want to issue a cache
@@ -1675,13 +1676,13 @@ HttpTransact::setup_plugin_request_intercept(State *s)
s->scheme = s->next_hop_scheme = URL_WKSIDX_HTTP;
// Set up a "fake" server entry
- update_current_info(&s->current, &s->server_info, HttpTransact::ORIGIN_SERVER, 0);
+ update_current_info(&s->current, &s->server_info, ResolveInfo::ORIGIN_SERVER, 0);
// Also "fake" the info we'd normally get from
// hostDB
- s->server_info.http_version = HTTP_1_0;
- s->server_info.keep_alive = HTTP_NO_KEEPALIVE;
- s->host_db_info.app.http_data.http_version = HTTP_1_0;
+ s->server_info.http_version = HTTP_1_0;
+ s->server_info.keep_alive = HTTP_NO_KEEPALIVE;
+ s->server_info.http_version = HTTP_1_0;
s->server_info.dst_addr.setToAnyAddr(AF_INET); // must set an address or we can't set the port.
s->server_info.dst_addr.network_order_port() = htons(s->hdr_info.client_request.port_get()); // this is the info that matters.
@@ -1783,19 +1784,25 @@ HttpTransact::PPDNSLookup(State *s)
{
TxnDebug("http_trans", "Entering HttpTransact::PPDNSLookup");
- ink_assert(s->dns_info.looking_up == PARENT_PROXY);
- if (!s->dns_info.lookup_success) {
+ ink_assert(s->dns_info.looking_up == ResolveInfo::PARENT_PROXY);
+ if (!s->dns_info.resolved_p) {
// Mark parent as down due to resolving failure
markParentDown(s);
// DNS lookup of parent failed, find next parent or o.s.
- if (find_server_and_update_current_info(s) == HttpTransact::HOST_NONE) {
- ink_assert(s->current.request_to == HOST_NONE);
+ if (find_server_and_update_current_info(s) == ResolveInfo::HOST_NONE) {
+ if (is_cache_hit(s->cache_lookup_result) && is_stale_cache_response_returnable(s)) {
+ s->source = SOURCE_CACHE;
+ TxnDebug("http_trans", "All parents are down, serving stale doc to client");
+ build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
+ return;
+ }
+ ink_assert(s->current.request_to == ResolveInfo::HOST_NONE);
handle_parent_died(s);
return;
}
if (!s->current.server->dst_addr.isValid()) {
- if (s->current.request_to == PARENT_PROXY) {
+ if (s->current.request_to == ResolveInfo::PARENT_PROXY) {
TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookupAPICall);
} else if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) {
// We ran out of parents but parent configuration allows us to go to Origin Server directly
@@ -1803,16 +1810,16 @@ HttpTransact::PPDNSLookup(State *s)
return;
} else {
// We could be out of parents here if all the parents failed DNS lookup
- ink_assert(s->current.request_to == HOST_NONE);
+ ink_assert(s->current.request_to == ResolveInfo::HOST_NONE);
handle_parent_died(s);
}
return;
}
} else {
// lookup succeeded, open connection to p.p.
- ats_ip_copy(&s->parent_info.dst_addr, s->host_db_info.ip());
+ ats_ip_copy(&s->parent_info.dst_addr, s->dns_info.addr);
s->parent_info.dst_addr.network_order_port() = htons(s->parent_result.port);
- get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, &s->host_db_info);
+ get_ka_info_from_host_db(s, &s->parent_info, &s->client_info, s->dns_info.active);
char addrbuf[INET6_ADDRSTRLEN];
TxnDebug("http_trans", "DNS lookup for successful IP: %s", ats_ip_ntop(&s->parent_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
@@ -1835,65 +1842,6 @@ HttpTransact::PPDNSLookup(State *s)
s->next_action = how_to_open_connection(s);
}
-///////////////////////////////////////////////////////////////////////////////
-//
-// Name : ReDNSRoundRobin
-// Description: Called after we fail to contact part of a round-robin
-// robin server set and we found a another ip address.
-//
-// Details :
-//
-//
-//
-// Possible Next States From Here:
-// - HttpTransact::ORIGIN_SERVER_RAW_OPEN;
-// - HttpTransact::ORIGIN_SERVER_OPEN;
-// - HttpTransact::PROXY_INTERNAL_CACHE_NOOP;
-//
-///////////////////////////////////////////////////////////////////////////////
-void
-HttpTransact::ReDNSRoundRobin(State *s)
-{
- ink_assert(s->current.server == &s->server_info);
- ink_assert(s->current.server->had_connect_fail());
-
- if (s->dns_info.lookup_success) {
- // We using a new server now so clear the connection
- // failure mark
- s->current.server->clear_connect_fail();
-
- // Our ReDNS of the server succeeded so update the necessary
- // information and try again. Need to preserve the current port value if possible.
- in_port_t server_port = s->current.server->dst_addr.host_order_port();
- // Temporary check to make sure the port preservation can be depended upon. That should be the case
- // because we get here only after trying a connection. Remove for 6.2.
- ink_assert(s->current.server->dst_addr.isValid() && 0 != server_port);
-
- ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip());
- s->server_info.dst_addr.network_order_port() = htons(server_port);
- ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr);
- get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
-
- char addrbuf[INET6_ADDRSTRLEN];
- TxnDebug("http_trans", "DNS lookup for O.S. successful IP: %s",
- ats_ip_ntop(&s->server_info.dst_addr.sa, addrbuf, sizeof(addrbuf)));
-
- s->next_action = how_to_open_connection(s);
- } else {
- // Our ReDNS failed so output the DNS failure error message
- // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL
- build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", Dns_error_body);
- s->cache_info.action = CACHE_DO_NO_ACTION;
- s->next_action = SM_ACTION_SEND_ERROR_CACHE_NOOP;
- // s->next_action = PROXY_INTERNAL_CACHE_NOOP;
- char *url_str = s->hdr_info.client_request.url_string_get(&s->arena, nullptr);
- ts::bwprint(error_bw_buffer, "DNS Error: looking up {}", ts::bwf::FirstOf(url_str, "<none>"));
- Log::error("%s", error_bw_buffer.c_str());
- }
-
- return;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Name : OSDNSLookup
// Description: called after the DNS lookup of origin server name
@@ -1923,29 +1871,22 @@ HttpTransact::ReDNSRoundRobin(State *s)
void
HttpTransact::OSDNSLookup(State *s)
{
- ink_assert(s->dns_info.looking_up == ORIGIN_SERVER);
+ ink_assert(s->dns_info.looking_up == ResolveInfo::UpstreamResolveStyle::ORIGIN_SERVER);
TxnDebug("http_trans", "Entering HttpTransact::OSDNSLookup");
- // It's never valid to connect *to* INADDR_ANY, so let's reject the request now.
- if (ats_is_ip_any(s->host_db_info.ip())) {
- TxnDebug("http_trans", "Invalid request IP: INADDR_ANY");
- build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Bad Destination Address", "request#syntax_error");
- SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
- TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
- }
-
- if (!s->dns_info.lookup_success) {
- if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
- /*
- * Transparent case: We tried to connect to client target address, failed and tried to use a different addr
- * No HostDB data, just keep on with the CTA.
+ if (!s->dns_info.resolved_p) {
+ if (ResolveInfo::OS_Addr::TRY_HOSTDB == s->dns_info.os_addr_style) {
+ /* Transparent case: We tried to connect to client target address, failed and tried to use a different addr
+ * but that failed to resolve therefore keep on with the CTA.
*/
- s->dns_info.lookup_success = true;
- s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
+ s->dns_info.addr.assign(s->state_machine->ua_txn->get_netvc()->get_local_addr()); // fetch CTA
+ s->dns_info.resolved_p = true;
+ s->dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_CLIENT;
TxnDebug("http_seq", "DNS lookup unsuccessful, using client target address");
} else {
TxnDebug("http_seq", "DNS Lookup unsuccessful");
+ char const *log_msg;
// Even with unsuccessful DNS lookup, return stale object from cache if applicable
if (is_cache_hit(s->cache_lookup_result) && is_stale_cache_response_returnable(s)) {
@@ -1956,10 +1897,16 @@ HttpTransact::OSDNSLookup(State *s)
}
// output the DNS failure error message
SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
- // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL
- build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", "connect#dns_failed");
+ if (!s->dns_info.record || s->dns_info.record->is_failed()) {
+ // Set to internal server error so later logging will pick up SQUID_LOG_ERR_DNS_FAIL
+ build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "Cannot find server.", "connect#dns_failed");
+ log_msg = "looking up";
+ } else {
+ build_error_response(s, HTTP_STATUS_INTERNAL_SERVER_ERROR, "No valid server.", "connect#all_dead");
+ log_msg = "no valid server";
+ }
char *url_str = s->hdr_info.client_request.url_string_get(&s->arena, nullptr);
- ts::bwprint(error_bw_buffer, "DNS Error: looking up {}", ts::bwf::FirstOf(url_str, "<none>"));
+ ts::bwprint(error_bw_buffer, "DNS Error: {} {}", log_msg, ts::bwf::FirstOf(url_str, "<none>"));
Log::error("%s", error_bw_buffer.c_str());
// s->cache_info.action = CACHE_DO_NO_ACTION;
TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
@@ -1968,43 +1915,43 @@ HttpTransact::OSDNSLookup(State *s)
}
// The dns lookup succeeded
- ink_assert(s->dns_info.lookup_success);
+ ink_assert(s->dns_info.resolved_p);
TxnDebug("http_seq", "DNS Lookup successful");
+ // It's never valid to connect *to* INADDR_ANY, so let's reject the request now.
+ if (ats_is_ip_any(s->dns_info.addr)) {
+ TxnDebug("http_trans", "[OSDNSLookup] Invalid request IP: INADDR_ANY");
+ build_error_response(s, HTTP_STATUS_BAD_REQUEST, "Bad Destination Address", "request#syntax_error");
+ SET_VIA_STRING(VIA_DETAIL_TUNNEL, VIA_DETAIL_TUNNEL_NO_FORWARD);
+ TRANSACT_RETURN(SM_ACTION_SEND_ERROR_CACHE_NOOP, nullptr);
+ }
+
// For the transparent case, nail down the kind of address we are really using
- if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB == s->dns_info.os_addr_style) {
+ if (ResolveInfo::OS_Addr::TRY_HOSTDB == s->dns_info.os_addr_style) {
// We've backed off from a client supplied address and found some
// HostDB addresses. We use those if they're different from the CTA.
// In all cases we now commit to client or HostDB for our source.
- if (s->host_db_info.round_robin) {
- HostDBInfo *cta = s->host_db_info.rr()->select_next(&s->current.server->dst_addr.sa);
- if (cta) {
- // found another addr, lock in host DB.
- s->host_db_info = *cta;
- s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB;
- } else {
- // nothing else there, continue with CTA.
- s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
- }
- } else if (ats_ip_addr_eq(s->host_db_info.ip(), &s->server_info.dst_addr.sa)) {
- s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
+ if (s->dns_info.set_active(&s->current.server->dst_addr.sa) && s->dns_info.select_next_rr()) {
+ s->dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_HOSTDB;
} else {
- s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB;
+ // nothing else there, continue with CTA.
+ s->dns_info.set_active(nullptr);
+ s->dns_info.set_upstream_address(&s->current.server->dst_addr.sa);
+ s->dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_CLIENT;
}
}
- // Check to see if can fullfill expect requests based on the cached
- // update some state variables with hostdb information that has
- // been provided.
- ats_ip_copy(&s->server_info.dst_addr, s->host_db_info.ip());
+ s->server_info.dst_addr.assign(s->dns_info.addr);
// If the SRV response has a port number, we should honor it. Otherwise we do the port defined in remap
- if (s->dns_info.srv_lookup_success) {
+ if (s->dns_info.resolved_p && s->dns_info.srv_port) {
s->server_info.dst_addr.network_order_port() = htons(s->dns_info.srv_port);
- } else if (!s->api_server_addr_set) {
+ } else if (s->dns_info.os_addr_style == ResolveInfo::OS_Addr::USE_API && 0 != ats_ip_port_cast(s->dns_info.addr)) {
+ // Nothing - port set via API and already copied over.
+ } else {
s->server_info.dst_addr.network_order_port() = htons(s->hdr_info.client_request.port_get()); // now we can set the port.
}
ats_ip_copy(&s->request_data.dest_ip, &s->server_info.dst_addr);
- get_ka_info_from_host_db(s, &s->server_info, &s->client_info, &s->host_db_info);
+ get_ka_info_from_host_db(s, &s->server_info, &s->client_info, s->dns_info.active);
char addrbuf[INET6_ADDRSTRLEN];
TxnDebug("http_trans", "DNS lookup for O.S. successful IP: %s",
@@ -2013,14 +1960,17 @@ HttpTransact::OSDNSLookup(State *s)
if (s->redirect_info.redirect_in_process) {
// If dns lookup was not successful, the code below will handle the error.
RedirectEnabled::Action action = RedirectEnabled::Action::INVALID;
- if (true == Machine::instance()->is_self(s->host_db_info.ip())) {
+ if (true == Machine::instance()->is_self(&s->dns_info.addr.sa)) {
action = s->http_config_param->redirect_actions_self_action;
+ TxnDebug("http_trans", "[OSDNSLookup] Self action - %d.", int(action));
} else {
// Make sure the return value from contains is big enough for a void*.
intptr_t x{intptr_t(RedirectEnabled::Action::INVALID)};
ink_release_assert(s->http_config_param->redirect_actions_map != nullptr);
- ink_release_assert(s->http_config_param->redirect_actions_map->contains(s->host_db_info.ip(), reinterpret_cast<void **>(&x)));
+ ink_release_assert(s->http_config_param->redirect_actions_map->contains(s->dns_info.addr, reinterpret_cast<void **>(&x)));
action = static_cast<RedirectEnabled::Action>(x);
+ TxnDebug("http_trans", "[OSDNSLookup] Mapped action - %d for family %d.", int(action),
+ int(s->dns_info.active->data.ip.family()));
}
if (action == RedirectEnabled::Action::FOLLOW) {
@@ -2055,10 +2005,11 @@ HttpTransact::OSDNSLookup(State *s)
// After SM_ACTION_DNS_LOOKUP, goto the saved action/state ORIGIN_SERVER_(RAW_)OPEN.
// Should we skip the StartAccessControl()? why?
- if (DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT == s->dns_info.os_addr_style ||
- DNSLookupInfo::OS_Addr::OS_ADDR_USE_HOSTDB == s->dns_info.os_addr_style) {
- // we've come back after already trying the server to get a better address
- // and finished with all backtracking - return to trying the server.
+ if (ResolveInfo::OS_Addr::USE_CLIENT == s->dns_info.os_addr_style ||
+ ResolveInfo::OS_Addr::USE_HOSTDB == s->dns_info.os_addr_style) {
+ // we've come back after already trying the server to get a better address,
+ // or we're locked on a plugin supplied address.
+ // therefore no more backtracking - return to trying the server.
TRANSACT_RETURN(how_to_open_connection(s), HttpTransact::HandleResponse);
} else if (s->dns_info.lookup_name[0] <= '9' && s->dns_info.lookup_name[0] >= '0' && s->parent_params->parent_table->hostMatch &&
!s->http_config_param->no_dns_forward_to_parent) {
@@ -2233,14 +2184,14 @@ HttpTransact::LookupSkipOpenServer(State *s)
// to a parent proxy or to the origin server.
find_server_and_update_current_info(s);
- if (s->current.request_to == PARENT_PROXY) {
+ if (s->current.request_to == ResolveInfo::PARENT_PROXY) {
TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookupAPICall);
} else if (s->parent_result.result == PARENT_FAIL) {
handle_parent_died(s);
return;
}
- ink_assert(s->current.request_to == ORIGIN_SERVER);
+ ink_assert(s->current.request_to == ResolveInfo::ORIGIN_SERVER);
// ink_assert(s->current.server->ip != 0);
build_request(s, &s->hdr_info.client_request, &s->hdr_info.server_request, s->current.server->http_version);
@@ -2892,18 +2843,18 @@ HttpTransact::HandleCacheOpenReadHit(State *s)
// scheme & 2) If we skip down parents, every page
// we serve is potentially stale
//
- if (s->current.request_to == ORIGIN_SERVER && is_server_negative_cached(s) && response_returnable == true &&
+ if (s->current.request_to == ResolveInfo::ORIGIN_SERVER && is_server_negative_cached(s) && response_returnable == true &&
is_stale_cache_response_returnable(s) == true) {
server_up = false;
- update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0);
+ update_current_info(&s->current, nullptr, ResolveInfo::UNDEFINED_LOOKUP, 0);
TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document");
}
// a parent lookup could come back as PARENT_FAIL if in parent.config, go_direct == false and
// there are no available parents (all down).
- else if (s->current.request_to == HOST_NONE && s->parent_result.result == PARENT_FAIL) {
+ else if (s->current.request_to == ResolveInfo::HOST_NONE && s->parent_result.result == PARENT_FAIL) {
if (response_returnable == true && is_stale_cache_response_returnable(s) == true) {
server_up = false;
- update_current_info(&s->current, nullptr, UNDEFINED_LOOKUP, 0);
+ update_current_info(&s->current, nullptr, ResolveInfo::UNDEFINED_LOOKUP, 0);
TxnDebug("http_trans", "CacheOpenReadHit - server_down, returning stale document");
} else {
handle_parent_died(s);
@@ -2926,14 +2877,14 @@ HttpTransact::HandleCacheOpenReadHit(State *s)
// through. The request will fail because of the
// missing ip but we won't take down the system
//
- if (s->current.request_to == PARENT_PROXY) {
+ if (s->current.request_to == ResolveInfo::PARENT_PROXY) {
// Set ourselves up to handle pending revalidate issues
// after the PP DNS lookup
ink_assert(s->pending_work == nullptr);
s->pending_work = issue_revalidate;
TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookupAPICall);
- } else if (s->current.request_to == ORIGIN_SERVER) {
+ } else if (s->current.request_to == ResolveInfo::ORIGIN_SERVER) {
return CallOSDNSLookup(s);
} else {
handle_parent_died(s);
@@ -3352,12 +3303,12 @@ HttpTransact::HandleCacheOpenReadMiss(State *s)
return;
}
if (!s->current.server->dst_addr.isValid()) {
- ink_release_assert(s->parent_result.result == PARENT_DIRECT || s->current.request_to == PARENT_PROXY ||
+ ink_release_assert(s->parent_result.result == PARENT_DIRECT || s->current.request_to == ResolveInfo::PARENT_PROXY ||
s->http_config_param->no_dns_forward_to_parent != 0);
if (s->parent_result.result == PARENT_DIRECT && s->http_config_param->no_dns_forward_to_parent != 1) {
return CallOSDNSLookup(s);
}
- if (s->current.request_to == PARENT_PROXY) {
+ if (s->current.request_to == ResolveInfo::PARENT_PROXY) {
TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, HttpTransact::PPDNSLookupAPICall);
} else {
handle_parent_died(s);
@@ -3466,7 +3417,7 @@ HttpTransact::HandleResponse(State *s)
HTTP_INCREMENT_DYN_STAT(http_incoming_responses_stat);
- ink_release_assert(s->current.request_to != UNDEFINED_LOOKUP);
+ ink_release_assert(s->current.request_to != ResolveInfo::UNDEFINED_LOOKUP);
if (s->cache_info.action != CACHE_DO_WRITE) {
ink_release_assert(s->cache_info.action != CACHE_DO_LOOKUP);
ink_release_assert(s->cache_info.action != CACHE_DO_SERVE);
@@ -3483,10 +3434,10 @@ HttpTransact::HandleResponse(State *s)
}
switch (s->current.request_to) {
- case PARENT_PROXY:
+ case ResolveInfo::PARENT_PROXY:
handle_response_from_parent(s);
break;
- case ORIGIN_SERVER:
+ case ResolveInfo::ORIGIN_SERVER:
handle_response_from_server(s);
break;
default:
@@ -3610,7 +3561,7 @@ HttpTransact::HandleStatPage(State *s)
void
HttpTransact::handle_response_from_parent(State *s)
{
- LookingUp_t next_lookup = UNDEFINED_LOOKUP;
+ auto next_lookup = ResolveInfo::UNDEFINED_LOOKUP;
TxnDebug("http_trans", "(hrfp)");
HTTP_RELEASE_ASSERT(s->current.server == &s->parent_info);
@@ -3717,7 +3668,7 @@ HttpTransact::handle_response_from_parent(State *s)
markParentDown(s);
}
s->parent_result.result = PARENT_FAIL;
- next_lookup = HOST_NONE;
+ next_lookup = ResolveInfo::HOST_NONE;
}
break;
}
@@ -3725,17 +3676,17 @@ HttpTransact::handle_response_from_parent(State *s)
// We have either tried to find a new parent or failed over to the
// origin server
switch (next_lookup) {
- case PARENT_PROXY:
- ink_assert(s->current.request_to == PARENT_PROXY);
+ case ResolveInfo::PARENT_PROXY:
+ ink_assert(s->current.request_to == ResolveInfo::PARENT_PROXY);
TRANSACT_RETURN(SM_ACTION_DNS_LOOKUP, PPDNSLookupAPICall);
break;
- case ORIGIN_SERVER:
+ case ResolveInfo::ORIGIN_SERVER:
// Next lookup is Origin Server, try DNS for Origin Server
return CallOSDNSLookup(s);
break;
- case HOST_NONE:
+ case ResolveInfo::HOST_NONE:
// Check if content can be served from cache
- s->current.request_to = PARENT_PROXY;
+ s->current.request_to = ResolveInfo::PARENT_PROXY;
handle_server_connection_not_open(s);
break;
default:
@@ -3809,38 +3760,25 @@ HttpTransact::handle_response_from_server(State *s)
if (is_request_retryable(s) && s->current.attempts < max_connect_retries) {
// If this is a round robin DNS entry & we're tried configured
// number of times, we should try another node
- if (DNSLookupInfo::OS_Addr::OS_ADDR_TRY_CLIENT == s->dns_info.os_addr_style) {
- // attempt was based on client supplied server address. Try again
- // using HostDB.
+ if (ResolveInfo::OS_Addr::TRY_CLIENT == s->dns_info.os_addr_style) {
+ // attempt was based on client supplied server address. Try again using HostDB.
// Allow DNS attempt
- s->dns_info.lookup_success = false;
+ s->dns_info.resolved_p = false;
// See if we can get data from HostDB for this.
- s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_TRY_HOSTDB;
+ s->dns_info.os_addr_style = ResolveInfo::OS_Addr::TRY_HOSTDB;
// Force host resolution to have the same family as the client.
// Because this is a transparent connection, we can't switch address
// families - that is locked in by the client source address.
ats_force_order_by_family(&s->current.server->dst_addr.sa, s->my_txn_conf().host_res_data.order);
return CallOSDNSLookup(s);
- } else if ((s->dns_info.srv_lookup_success || s->host_db_info.is_rr_elt()) &&
- (s->txn_conf->connect_attempts_rr_retries > 0) &&
- ((s->current.attempts + 1) % s->txn_conf->connect_attempts_rr_retries == 0)) {
- delete_server_rr_entry(s, max_connect_retries);
- return;
} else {
+ if ((s->txn_conf->connect_attempts_rr_retries > 0) &&
+ ((s->current.attempts + 1) % s->txn_conf->connect_attempts_rr_retries == 0)) {
+ s->dns_info.select_next_rr();
+ }
retry_server_connection_not_open(s, s->current.state, max_connect_retries);
TxnDebug("http_trans", "Error. Retrying...");
s->next_action = how_to_open_connection(s);
-
- if (s->api_server_addr_set) {
- // If the plugin set a server address, back up to the OS_DNS hook
- // to let it try another one. Force OS_ADDR_USE_CLIENT so that
- // in OSDNSLoopkup, we back up to how_to_open_connections which
- // will tell HttpSM to connect the origin server.
-
- s->dns_info.os_addr_style = DNSLookupInfo::OS_Addr::OS_ADDR_USE_CLIENT;
- TRANSACT_RETURN(SM_ACTION_API_OS_DNS, OSDNSLookup);
- }
- return;
}
} else {
error_log_connection_failure(s, s->current.state);
@@ -3863,35 +3801,6 @@ HttpTransact::handle_response_from_server(State *s)
return;
}
-///////////////////////////////////////////////////////////////////////////////
-// Name : delete_server_rr_entry
-// Description:
-//
-// Details :
-//
-// connection to server failed mark down the server round robin entry
-//
-//
-// Possible Next States From Here:
-//
-///////////////////////////////////////////////////////////////////////////////
-void
-HttpTransact::delete_server_rr_entry(State *s, int max_retries)
-{
- char addrbuf[INET6_ADDRSTRLEN];
-
- TxnDebug("http_trans", "[%d] failed to connect to %s", s->current.attempts,
- ats_ip_ntop(&s->current.server->dst_addr.sa, addrbuf, sizeof(addrbuf)));
- TxnDebug("http_trans", "marking rr entry down and finding next one");
- ink_assert(s->current.server->had_connect_fail());
- ink_assert(s->current.request_to == ORIGIN_SERVER);
- ink_assert(s->current.server == &s->server_info);
- update_dns_info(&s->dns_info, &s->current);
- s->current.attempts++;
- TxnDebug("http_trans", "attempts now: %d, max: %d", s->current.attempts, max_retries);
- TRANSACT_RETURN(SM_ACTION_ORIGIN_SERVER_RR_MARK_DOWN, ReDNSRoundRobin);
-}
-
void
HttpTransact::error_log_connection_failure(State *s, ServerState_t conn_state)
{
@@ -4022,10 +3931,10 @@ HttpTransact::handle_server_connection_not_open(State *s)
build_response_from_cache(s, HTTP_WARNING_CODE_REVALIDATION_FAILED);
} else {
switch (s->current.request_to) {
- case PARENT_PROXY:
+ case ResolveInfo::PARENT_PROXY:
handle_parent_died(s);
break;
- case ORIGIN_SERVER:
+ case ResolveInfo::ORIGIN_SERVER:
handle_server_died(s);
break;
default:
@@ -4061,7 +3970,7 @@ HttpTransact::handle_forward_server_connection_open(State *s)
ink_release_assert(s->current.state == CONNECTION_ALIVE);
HTTPVersion real_version = s->state_machine->get_server_version(s->hdr_info.server_response);
- if (real_version != s->host_db_info.app.http_data.http_version) {
+ if (real_version != s->dns_info.http_version) {
// Need to update the hostdb
s->updated_server_version = real_version;
TxnDebug("http_trans", "Update hostdb history of server HTTP version 0x%x", s->updated_server_version.get_flat_version());
@@ -5291,22 +5200,22 @@ HttpTransact::get_ka_info_from_host_db(State *s, ConnectionAttributes *server_in
break;
}
- if (force_http11 == true || (http11_if_hostdb == true && host_db_info->app.http_data.http_version == HTTP_1_1)) {
+ if (force_http11 == true || (http11_if_hostdb == true && host_db_info->http_version == HTTP_1_1)) {
server_info->http_version = HTTP_1_1;
server_info->keep_alive = HTTP_KEEPALIVE;
- } else if (host_db_info->app.http_data.http_version == HTTP_1_0) {
+ } else if (host_db_info->http_version == HTTP_1_0) {
server_info->http_version = HTTP_1_0;
server_info->keep_alive = HTTP_KEEPALIVE;
- } else if (host_db_info->app.http_data.http_version == HTTP_0_9) {
+ } else if (host_db_info->http_version == HTTP_0_9) {
server_info->http_version = HTTP_0_9;
server_info->keep_alive = HTTP_NO_KEEPALIVE;
} else {
//////////////////////////////////////////////
// not set yet for this host. set defaults. //
//////////////////////////////////////////////
- server_info->http_version = HTTP_1_0;
- server_info->keep_alive = HTTP_KEEPALIVE;
- host_db_info->app.http_data.http_version = HTTP_1_0;
+ server_info->http_version = HTTP_1_0;
+ server_info->keep_alive = HTTP_KEEPALIVE;
+ host_db_info->http_version = HTTP_1_0;
}
/////////////////////////////
@@ -5852,7 +5761,7 @@ HttpTransact::initialize_state_variables_from_request(State *s, HTTPHdr *obsolet
// the expanded host for cache lookup, and //
// the host ip for reverse proxy. //
/////////////////////////////////////////////
- s->dns_info.looking_up = ORIGIN_SERVER;
+ s->dns_info.looking_up = ResolveInfo::ORIGIN_SERVER;
s->dns_info.lookup_name = s->server_info.name;
}
@@ -6248,7 +6157,7 @@ HttpTransact::is_response_cacheable(State *s, HTTPHdr *request, HTTPHdr *respons
// host addresses, do not allow cache. This may cause DNS cache poisoning
// of other trafficserver clients. The flag is set in the
// process_host_db_info method
- if (!s->dns_info.lookup_validated && s->client_info.is_transparent) {
+ if (!s->dns_info.cta_validated_p && s->client_info.is_transparent) {
TxnDebug("http_trans", "Lookup not validated. Possible DNS cache poison. Don't cache");
return false;
}
@@ -6638,7 +6547,7 @@ HttpTransact::will_this_request_self_loop(State *s)
////////////////////////////////////////
// check if we are about to self loop //
////////////////////////////////////////
- if (s->dns_info.lookup_success) {
+ if (s->dns_info.active) {
TxnDebug("http_transact", "max_proxy_cycles = %d", max_proxy_cycles);
if (max_proxy_cycles == 0) {
in_port_t dst_port = s->hdr_info.client_request.url_get()->port_get(); // going to this port.
@@ -6646,13 +6555,13 @@ HttpTransact::will_this_request_self_loop(State *s)
// It's a loop if connecting to the same port as it already connected to the proxy and
// it's a proxy address or the same address it already connected to.
TxnDebug("http_transact", "dst_port = %d local_port = %d", dst_port, local_port);
- if (dst_port == local_port && (ats_ip_addr_eq(s->host_db_info.ip(), &Machine::instance()->ip.sa) ||
- ats_ip_addr_eq(s->host_db_info.ip(), s->client_info.dst_addr))) {
+ if (dst_port == local_port && ((s->dns_info.active->data.ip == &Machine::instance()->ip.sa) ||
+ (s->dns_info.active->data.ip == s->client_info.dst_addr))) {
switch (s->dns_info.looking_up) {
- case ORIGIN_SERVER:
+ case ResolveInfo::ORIGIN_SERVER:
TxnDebug("http_transact", "host ip and port same as local ip and port - bailing");
break;
- case PARENT_PROXY:
+ case ResolveInfo::PARENT_PROXY:
TxnDebug("http_transact", "parent proxy ip and port same as local ip and port - bailing");
break;
default:
@@ -6883,7 +6792,7 @@ HttpTransact::handle_request_keep_alive_headers(State *s, HTTPVersion ver, HTTPH
case KA_CONNECTION:
ink_assert(s->current.server->keep_alive != HTTP_NO_KEEPALIVE);
if (ver == HTTP_1_0) {
- if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) {
+ if (s->current.request_to == ResolveInfo::PARENT_PROXY && parent_is_proxy(s)) {
heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "keep-alive", 10);
} else {
heads->value_set(MIME_FIELD_CONNECTION, MIME_LEN_CONNECTION, "keep-alive", 10);
@@ -6897,7 +6806,7 @@ HttpTransact::handle_request_keep_alive_headers(State *s, HTTPVersion ver, HTTPH
if (s->current.server->keep_alive != HTTP_NO_KEEPALIVE || (ver == HTTP_1_1)) {
/* Had keep-alive */
s->current.server->keep_alive = HTTP_NO_KEEPALIVE;
- if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) {
+ if (s->current.request_to == ResolveInfo::PARENT_PROXY && parent_is_proxy(s)) {
heads->value_set(MIME_FIELD_PROXY_CONNECTION, MIME_LEN_PROXY_CONNECTION, "close", 5);
} else {
ProxyTransaction *svr = s->state_machine->get_server_txn();
@@ -7809,7 +7718,7 @@ HttpTransact::build_request(State *s, HTTPHdr *base_request, HTTPHdr *outgoing_r
if (outgoing_request->method_get_wksidx() == HTTP_WKSIDX_CONNECT) {
// CONNECT method requires a target in the URL, so always force it from the Host header.
outgoing_request->set_url_target_from_host_field();
- } else if (s->current.request_to == PARENT_PROXY && parent_is_proxy(s)) {
+ } else if (s->current.request_to == ResolveInfo::PARENT_PROXY && parent_is_proxy(s)) {
// If we have a parent proxy set the URL target field.
if (!outgoing_request->is_target_in_url()) {
TxnDebug("http_trans", "adding target to URL for parent proxy");
@@ -8836,7 +8745,7 @@ HttpTransact::update_size_and_time_stats(State *s, ink_hrtime total_time, ink_hr
HTTP_SUM_DYN_STAT(http_user_agent_response_document_total_size_stat, user_agent_response_body_size);
// proxy stats
- if (s->current.request_to == HttpTransact::PARENT_PROXY) {
+ if (s->current.request_to == ResolveInfo::PARENT_PROXY) {
HTTP_SUM_DYN_STAT(http_parent_proxy_request_total_bytes_stat,
origin_server_request_header_size + origin_server_request_body_size);
HTTP_SUM_DYN_STAT(http_parent_proxy_response_total_bytes_stat,
diff --git a/proxy/http/HttpTransact.h b/proxy/http/HttpTransact.h
index 7e7fc71ea..e9700f831 100644
--- a/proxy/http/HttpTransact.h
+++ b/proxy/http/HttpTransact.h
@@ -282,14 +282,6 @@ public:
HTTP_TRANSACT_MAGIC_SEPARATOR = 0x12345678
};
- enum LookingUp_t {
- ORIGIN_SERVER,
- UNDEFINED_LOOKUP,
- PARENT_PROXY,
- INCOMING_ROUTER,
- HOST_NONE,
- };
-
enum ProxyMode_t {
UNDEFINED_MODE,
GENERIC_PROXY,
@@ -574,57 +566,19 @@ public:
};
typedef struct _CurrentInfo {
- ProxyMode_t mode = UNDEFINED_MODE;
- LookingUp_t request_to = UNDEFINED_LOOKUP;
- ConnectionAttributes *server = nullptr;
- ink_time_t now = 0;
- ServerState_t state = STATE_UNDEFINED;
- unsigned attempts = 0;
- unsigned simple_retry_attempts = 0;
- unsigned unavailable_server_retry_attempts = 0;
- ParentRetry_t retry_type = PARENT_RETRY_NONE;
+ ProxyMode_t mode = UNDEFINED_MODE;
+ ResolveInfo::UpstreamResolveStyle request_to = ResolveInfo::UNDEFINED_LOOKUP;
+ ConnectionAttributes *server = nullptr;
+ ink_time_t now = 0;
+ ServerState_t state = STATE_UNDEFINED;
+ unsigned attempts = 0;
+ unsigned simple_retry_attempts = 0;
+ unsigned unavailable_server_retry_attempts = 0;
+ ParentRetry_t retry_type = PARENT_RETRY_NONE;
_CurrentInfo() {}
} CurrentInfo;
- typedef struct _DNSLookupInfo {
- /** Origin server address source selection.
-
- If config says to use CTA (client target addr) state is
- OS_ADDR_TRY_CLIENT, otherwise it remains the default. If the
- connect fails then we switch to a USE. We go to USE_HOSTDB if
- (1) the HostDB lookup is successful and (2) some address other
- than the CTA is available to try. Otherwise we keep retrying
- on the CTA (USE_CLIENT) up to the max retry value. In essence
- we try to treat the CTA as if it were another RR value in the
- HostDB record.
- */
- enum class OS_Addr {
- OS_ADDR_TRY_DEFAULT, ///< Initial state, use what config says.
- OS_ADDR_TRY_HOSTDB, ///< Try HostDB data.
- OS_ADDR_TRY_CLIENT, ///< Try client target addr.
- OS_ADDR_USE_HOSTDB, ///< Force use of HostDB target address.
- OS_ADDR_USE_CLIENT ///< Use client target addr, no fallback.
- };
-
- OS_Addr os_addr_style = OS_Addr::OS_ADDR_TRY_DEFAULT;
-
- bool lookup_success = false;
- char *lookup_name = nullptr;
- char srv_hostname[MAXDNAME] = {0};
- LookingUp_t looking_up = UNDEFINED_LOOKUP;
- bool srv_lookup_success = false;
- short srv_port = 0;
- HostDBApplicationInfo srv_app;
-
- /*** Set to true by default. If use_client_target_address is set
- * to 1, this value will be set to false if the client address is
- * not in the DNS pool */
- bool lookup_validated = true;
-
- _DNSLookupInfo() {}
- } DNSLookupInfo;
-
// Conversion handling for DNS host resolution type.
static const MgmtConverter HOST_RES_CONV;
@@ -672,7 +626,7 @@ public:
HttpConfigParams *http_config_param = nullptr;
CacheLookupInfo cache_info;
- DNSLookupInfo dns_info;
+ ResolveInfo dns_info;
RedirectInfo redirect_info;
OutboundConnTrack::TxnState outbound_conn_track_state;
HTTPVersion updated_server_version = HTTP_INVALID;
@@ -724,8 +678,6 @@ public:
int orig_scheme = scheme; // pre-mapped scheme
int method = 0;
int cause_of_death_errno = -UNKNOWN_INTERNAL_ERROR; // in
- Ptr<HostDBInfo> hostdb_entry; // Pointer to the entry we are referencing in hostdb-- to keep our ref
- HostDBInfo host_db_info; // in
ink_time_t client_request_time = UNDEFINED_TIME; // internal
ink_time_t request_sent_time = UNDEFINED_TIME; // internal
@@ -774,7 +726,6 @@ public:
bool api_server_request_body_set = false;
bool api_req_cacheable = false;
bool api_resp_cacheable = false;
- bool api_server_addr_set = false;
UpdateCachedObject_t api_update_cached_object = UPDATE_CACHED_OBJECT_NONE;
StateMachineAction_t saved_update_next_action = SM_ACTION_UNDEFINED;
CacheAction_t saved_update_cache_action = CACHE_DO_UNDEFINED;
@@ -822,6 +773,7 @@ public:
init()
{
parent_params = ParentConfig::acquire();
+ new (&dns_info) decltype(dns_info); // reset to default state.
}
// Constructor
@@ -848,7 +800,8 @@ public:
via_string[VIA_DETAIL_SERVER_DESCRIPTOR] = VIA_DETAIL_SERVER_DESCRIPTOR_STRING;
via_string[MAX_VIA_INDICES] = '\0';
- memset((void *)&host_db_info, 0, sizeof(host_db_info));
+ // memset(user_args, 0, sizeof(user_args));
+ // memset((void *)&host_db_info, 0, sizeof(host_db_info));
}
void
@@ -878,7 +831,7 @@ public:
url_map.clear();
arena.reset();
unmapped_url.clear();
- hostdb_entry.clear();
+ dns_info.~ResolveInfo();
outbound_conn_track_state.clear();
delete[] ranges;
@@ -922,6 +875,7 @@ public:
if (e != EIO) {
this->cause_of_death_errno = e;
}
+ Debug("http", "Setting upstream connection failure %d to %d", e, this->current.server->connect_result);
}
private:
@@ -953,7 +907,6 @@ public:
static void CallOSDNSLookup(State *s);
static void OSDNSLookup(State *s);
- static void ReDNSRoundRobin(State *s);
static void PPDNSLookup(State *s);
static void PPDNSLookupAPICall(State *s);
static void OriginServerRawOpen(State *s);
diff --git a/proxy/http/PreWarmManager.cc b/proxy/http/PreWarmManager.cc
index 930d71ed7..ffddf5d93 100644
--- a/proxy/http/PreWarmManager.cc
+++ b/proxy/http/PreWarmManager.cc
@@ -27,6 +27,7 @@
#include "HttpConfig.h"
#include "P_SSLSNI.h"
+#include "tscore/ink_time.h"
#include "tscpp/util/PostScript.h"
#include <algorithm>
@@ -41,8 +42,8 @@ namespace
{
using namespace std::literals;
-constexpr int DOWN_SERVER_TIMEOUT = 300;
-constexpr size_t STAT_NAME_BUF_LEN = 1024;
+constexpr ts_seconds DOWN_SERVER_TIMEOUT = 300s;
+constexpr size_t STAT_NAME_BUF_LEN = 1024;
constexpr std::string_view SRV_TUNNEL_TCP = "_tunnel._tcp."sv;
constexpr std::string_view CLIENT_SNI_POLICY_SERVER_NAME = "server_name"sv;
@@ -265,22 +266,29 @@ PreWarmSM::state_init(int event, void *data)
int
PreWarmSM::state_dns_lookup(int event, void *data)
{
- HostDBInfo *info = static_cast<HostDBInfo *>(data);
+ HostDBRecord *record = static_cast<HostDBRecord *>(data);
switch (event) {
case EVENT_HOST_DB_LOOKUP: {
_pending_action = nullptr;
- if (info == nullptr || info->is_failed()) {
+ if (record == nullptr || record->is_failed()) {
PreWarmSMVDebug("hostdb lookup is failed");
retry();
return EVENT_DONE;
}
+ HostDBInfo *info = record->select_next_rr(ts_clock::now(), DOWN_SERVER_TIMEOUT);
+
+ if (info == nullptr) {
+ PreWarmSMVDebug("hostdb lookup found no entry");
+ retry();
+ return EVENT_DONE;
+ }
IpEndpoint addr;
+ addr.assign(info->data.ip);
- ats_ip_copy(addr, info->ip());
addr.network_order_port() = htons(_dst->port);
if (is_debug_tag_set("v_prewarm_sm")) {
@@ -302,23 +310,19 @@ PreWarmSM::state_dns_lookup(int event, void *data)
_pending_action = nullptr;
std::string_view hostname;
- if (info == nullptr || !info->is_srv || !info->round_robin) {
+ if (record == nullptr || !record->is_srv()) {
// no SRV record, fallback to default lookup
hostname = _dst->host;
} else {
- HostDBRoundRobin *rr = info->rr();
- HostDBInfo *srv = nullptr;
- if (rr) {
- char srv_hostname[MAXDNAME] = {0};
-
- ink_hrtime now = Thread::get_hrtime();
- srv = rr->select_best_srv(srv_hostname, &mutex->thread_holding->generator, ink_hrtime_to_sec(now), DOWN_SERVER_TIMEOUT);
- hostname = std::string_view(srv_hostname);
-
- if (srv == nullptr) {
- // lookup SRV record failed, fallback to default lookup
- hostname = _dst->host;
- }
+ char srv_hostname[MAXDNAME] = {0};
+
+ hostname = std::string_view(srv_hostname);
+
+ HostDBInfo *info =
+ record->select_best_srv(srv_hostname, &mutex->thread_holding->generator, ts_clock::now(), DOWN_SERVER_TIMEOUT);
+ if (info == nullptr) {
+ // lookup SRV record failed, fallback to default lookup
+ hostname = _dst->host;
}
}
@@ -508,7 +512,7 @@ PreWarmSM::is_inactive_timeout_expired(ink_hrtime now)
}
void
-PreWarmSM::process_hostdb_info(HostDBInfo *r)
+PreWarmSM::process_hostdb_info(HostDBRecord *r)
{
ink_release_assert(this->handler == &PreWarmSM::state_dns_lookup);
@@ -516,7 +520,7 @@ PreWarmSM::process_hostdb_info(HostDBInfo *r)
}
void
-PreWarmSM::process_srv_info(HostDBInfo *r)
+PreWarmSM::process_srv_info(HostDBRecord *r)
{
ink_release_assert(this->handler == &PreWarmSM::state_dns_lookup);
diff --git a/proxy/http/PreWarmManager.h b/proxy/http/PreWarmManager.h
index e7da819c2..ea3506365 100644
--- a/proxy/http/PreWarmManager.h
+++ b/proxy/http/PreWarmManager.h
@@ -192,8 +192,8 @@ public:
bool is_inactive_timeout_expired(ink_hrtime now);
// HostDB inline completion functions
- void process_hostdb_info(HostDBInfo *r);
- void process_srv_info(HostDBInfo *r);
+ void process_hostdb_info(HostDBRecord *r);
+ void process_srv_info(HostDBRecord *r);
private:
enum class Milestone {
diff --git a/proxy/http/remap/unit-tests/nexthop_test_stubs.cc b/proxy/http/remap/unit-tests/nexthop_test_stubs.cc
index b72183b21..905651261 100644
--- a/proxy/http/remap/unit-tests/nexthop_test_stubs.cc
+++ b/proxy/http/remap/unit-tests/nexthop_test_stubs.cc
@@ -86,7 +86,6 @@ br_destroy(HttpSM &sm)
}
delete h->hdr;
delete h->api_info;
- ats_free(h->hostname_str);
}
void
@@ -102,10 +101,7 @@ build_request(int64_t sm_id, HttpSM *sm, sockaddr_in *ip, const char *os_hostnam
}
sm->t_state.request_data.hdr = new HTTPHdr();
sm->t_state.request_data.hdr->create(HTTP_TYPE_REQUEST, myHeap);
-
- ats_free(sm->t_state.request_data.hostname_str);
-
- sm->t_state.request_data.hostname_str = ats_strdup(os_hostname);
+ sm->t_state.request_data.hostname_str = sm->t_state.arena.str_store(os_hostname, strlen(os_hostname));
sm->t_state.request_data.xact_start = time(nullptr);
ink_zero(sm->t_state.request_data.src_ip);
ink_zero(sm->t_state.request_data.dest_ip);
diff --git a/src/traffic_server/InkAPI.cc b/src/traffic_server/InkAPI.cc
index 8743e1701..1b19095e7 100644
--- a/src/traffic_server/InkAPI.cc
+++ b/src/traffic_server/InkAPI.cc
@@ -5893,12 +5893,11 @@ TSHttpTxnServerAddrSet(TSHttpTxn txnp, struct sockaddr const *addr)
sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
HttpSM *sm = reinterpret_cast<HttpSM *>(txnp);
- if (ats_ip_copy(&sm->t_state.server_info.dst_addr.sa, addr)) {
- sm->t_state.api_server_addr_set = true;
+ if (sm->t_state.dns_info.set_upstream_address(addr)) {
+ sm->t_state.dns_info.os_addr_style = ResolveInfo::OS_Addr::USE_API;
return TS_SUCCESS;
- } else {
- return TS_ERROR;
}
+ return TS_ERROR;
}
void
@@ -7595,6 +7594,30 @@ TSNetAcceptNamedProtocol(TSCont contp, const char *protocol)
}
/* DNS Lookups */
+/// Context structure for the lookup callback to the plugin.
+struct TSResolveInfo {
+ IpEndpoint addr; ///< Lookup result.
+ HostDBRecord *record = nullptr; ///< Record for the FQDN.
+};
+
+int
+TSHostLookupTrampoline(TSCont contp, TSEvent ev, void *data)
+{
+ auto c = reinterpret_cast<INKContInternal *>(contp);
+ // Set up the local context.
+ TSResolveInfo ri;
+ ri.record = static_cast<HostDBRecord *>(data);
+ if (ri.record) {
+ ri.record->rr_info()[0].data.ip.toSockAddr(ri.addr);
+ }
+ auto *target = reinterpret_cast<INKContInternal *>(c->mdata);
+ // Deliver the message.
+ target->handleEvent(ev, &ri);
+ // Cleanup.
+ c->destroy();
+ return TS_SUCCESS;
+};
+
TSAction
TSHostLookup(TSCont contp, const char *hostname, size_t namelen)
{
@@ -7604,16 +7627,23 @@ TSHostLookup(TSCont contp, const char *hostname, size_t namelen)
FORCE_PLUGIN_SCOPED_MUTEX(contp);
- INKContInternal *i = (INKContInternal *)contp;
- return (TSAction)hostDBProcessor.getbyname_re(i, hostname, namelen);
+ // There is no place to store the actual sockaddr to which a pointer should be returned.
+ // therefore an intermediate continuation is created to intercept the reply from HostDB.
+ // Its handler can create the required sockaddr context on the stack and then forward
+ // the event to the plugin continuation. The sockaddr cannot be placed in the HostDB
+ // record because that is a shared object.
+ auto bouncer = INKContAllocator.alloc();
+ bouncer->init(&TSHostLookupTrampoline, reinterpret_cast<TSMutex>(reinterpret_cast<INKContInternal *>(contp)->mutex.get()));
+ bouncer->mdata = contp;
+ return (TSAction)hostDBProcessor.getbyname_re(bouncer, hostname, namelen);
}
sockaddr const *
TSHostLookupResultAddrGet(TSHostLookupResult lookup_result)
{
sdk_assert(sdk_sanity_check_hostlookup_structure(lookup_result) == TS_SUCCESS);
- HostDBInfo *di = reinterpret_cast<HostDBInfo *>(lookup_result);
- return di->ip();
+ auto ri{reinterpret_cast<TSResolveInfo *>(lookup_result)};
+ return ri->addr.isValid() ? &ri->addr.sa : nullptr;
}
/*
@@ -8662,6 +8692,9 @@ _memberp_to_generic(MgmtFloat *ptr, MgmtConverter const *&conv) -> typename std:
static void *
_conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overridableHttpConfig, MgmtConverter const *&conv)
{
+ // External converters.
+ extern MgmtConverter const &HostDBDownServerCacheTimeConv;
+
void *ret = nullptr;
conv = nullptr;
@@ -8820,7 +8853,8 @@ _conf_to_memberp(TSOverridableConfigKey conf, OverridableHttpConfigParams *overr
ret = _memberp_to_generic(&overridableHttpConfig->post_connect_attempts_timeout, conv);
break;
case TS_CONFIG_HTTP_DOWN_SERVER_CACHE_TIME:
- ret = _memberp_to_generic(&overridableHttpConfig->down_server_timeout, conv);
+ conv = &HostDBDownServerCacheTimeConv;
+ ret = &overridableHttpConfig->down_server_timeout;
break;
case TS_CONFIG_HTTP_DOWN_SERVER_ABORT_THRESHOLD:
ret = _memberp_to_generic(&overridableHttpConfig->client_abort_threshold, conv);
diff --git a/src/traffic_server/InkAPITest.cc b/src/traffic_server/InkAPITest.cc
index f6b070082..96526d9fa 100644
--- a/src/traffic_server/InkAPITest.cc
+++ b/src/traffic_server/InkAPITest.cc
@@ -8512,14 +8512,14 @@ EXCLUSIVE_REGRESSION_TEST(SDK_API_TSHttpConnectIntercept)(RegressionTest *test,
/* ip and log do not matter as it is used for logging only */
sockaddr_in addr;
ats_ip4_set(&addr, 1, 1);
- data->vc = TSHttpConnect(ats_ip_sa_cast(&addr));
+ data->vc = TSHttpConnectWithPluginId(ats_ip_sa_cast(&addr), "TSHttpConnectIntercept", 1);
if (TSVConnClosedGet(data->vc)) {
SDK_RPRINT(data->test, "TSHttpConnect", "TestCase 1", TC_FAIL, "Connect reported as closed immediately after open");
}
synclient_txn_send_request_to_vc(data->browser, data->request, data->vc);
/* Wait until transaction is done */
- TSContScheduleOnPool(cont_test, 25, TS_THREAD_POOL_NET);
+ TSContScheduleOnPool(cont_test, 100, TS_THREAD_POOL_NET);
return;
}
@@ -8558,12 +8558,12 @@ EXCLUSIVE_REGRESSION_TEST(SDK_API_TSHttpConnectServerIntercept)(RegressionTest *
/* ip and log do not matter as it is used for logging only */
sockaddr_in addr;
ats_ip4_set(&addr, 2, 2);
- data->vc = TSHttpConnect(ats_ip_sa_cast(&addr));
+ data->vc = TSHttpConnectWithPluginId(ats_ip_sa_cast(&addr), "TSHttpConnectServerIntercept", 1);
synclient_txn_send_request_to_vc(data->browser, data->request, data->vc);
/* Wait until transaction is done */
- TSContScheduleOnPool(cont_test, 25, TS_THREAD_POOL_NET);
+ TSContScheduleOnPool(cont_test, 100, TS_THREAD_POOL_NET);
return;
}
diff --git a/src/tscore/unit_tests/test_BufferWriterFormat.cc b/src/tscore/unit_tests/test_BufferWriterFormat.cc
index 66004f97d..76d97080e 100644
--- a/src/tscore/unit_tests/test_BufferWriterFormat.cc
+++ b/src/tscore/unit_tests/test_BufferWriterFormat.cc
@@ -284,9 +284,11 @@ TEST_CASE("bwstring", "[bwprint][bwstring]")
ts::bwprint(s, fmt, 99999, text);
REQUIRE(s == "99999 -- e99a18c428cb38d5f260853678922e03");
+ REQUIRE(strlen(s.c_str()) == text.size() + 9);
ts::bwprint(s, "{} .. |{:,20}|", 32767, text);
REQUIRE(s == "32767 .. |e99a18c428cb38d5f260|");
+ REQUIRE(strlen(s.c_str()) == 31);
ts::LocalBufferWriter<128> bw;
char buff[128];
diff --git a/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py b/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py
index 9d7f97652..0ad0c4986 100644
--- a/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py
+++ b/tests/gold_tests/next_hop/strategies_ch2/strategies_ch2.test.py
@@ -74,6 +74,7 @@ ts.Disk.records_config.update({
'proxy.config.http.uncacheable_requests_bypass_parent': 0,
'proxy.config.http.no_dns_just_forward_to_parent': 1,
'proxy.config.http.parent_proxy.mark_down_hostdb': 0,
+ 'proxy.config.http.down_server.cache_time': 1,
'proxy.config.http.parent_proxy.self_detect': 0,
})
@@ -90,6 +91,7 @@ for i in range(num_nh):
# The health check URL does not seem to be used currently.
# s.AddLine(f" health_check_url: http://next_hop{i}:{ts_nh[i].Variables.port}")
s.AddLine(f" weight: 1.0")
+
s.AddLines([
"strategies:",
" - strategy: the-strategy",
diff --git a/tests/gold_tests/proxy_protocol/proxy_serve_stale.test.py b/tests/gold_tests/proxy_protocol/proxy_serve_stale.test.py
index e012d394f..e799884c6 100644
--- a/tests/gold_tests/proxy_protocol/proxy_serve_stale.test.py
+++ b/tests/gold_tests/proxy_protocol/proxy_serve_stale.test.py
@@ -47,7 +47,7 @@ class ProxyServeStaleTest:
'proxy.config.http.cache.max_stale_age': 10,
'proxy.config.http.parent_proxy.self_detect': 0,
'proxy.config.diags.debug.enabled': 1,
- 'proxy.config.diags.debug.tags': 'http|dns|parent_proxy',
+ 'proxy.config.diags.debug.tags': 'cache|http|dns|hostdb|parent_proxy',
})
self.ts_child.Disk.parent_config.AddLine(
f'dest_domain=. parent="{self.ts_parent_hostname}" round_robin=consistent_hash go_direct=false'
diff --git a/tests/gold_tests/tls/tls_verify_override_base.test.py b/tests/gold_tests/tls/tls_verify_override_base.test.py
index 0495ec54b..00027f230 100644
--- a/tests/gold_tests/tls/tls_verify_override_base.test.py
+++ b/tests/gold_tests/tls/tls_verify_override_base.test.py
@@ -113,7 +113,9 @@ ts.Disk.records_config.update({
'proxy.config.url_remap.pristine_host_hdr': 1,
'proxy.config.exec_thread.autoconfig.scale': 1.0,
'proxy.config.dns.nameservers': '127.0.0.1:{0}'.format(dns.Variables.Port),
- 'proxy.config.dns.resolv_conf': 'NULL'
+ 'proxy.config.dns.resolv_conf': 'NULL',
+ 'proxy.config.ssl.client.verify.server.policy': 'PERMISSIVE',
+ 'proxy.config.http.connect.dead.policy': 1, # Don't count TLS failures for dead upstream.
})
dns.addRecords(records={"foo.com.": ["127.0.0.1"]})