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 68022cc Via: Enable detail level in VIA protocol stack.
68022cc is described below
commit 68022cc31af1d0451802e50206be3b9349390503
Author: Alan M. Carroll <[email protected]>
AuthorDate: Thu May 25 22:41:32 2017 -0500
Via: Enable detail level in VIA protocol stack.
---
doc/admin-guide/files/records.config.en.rst | 49 +++++++
.../api/functions/TSClientProtocolStack.en.rst | 2 +
lib/records/I_RecCore.h | 1 +
lib/records/RecCore.cc | 19 +++
lib/ts/apidefs.h.in | 9 ++
mgmt/RecordsConfig.cc | 9 +-
plugins/experimental/ts_lua/ts_lua_http_config.c | 4 +
proxy/InkAPI.cc | 28 ++++
proxy/InkAPITest.cc | 2 +
proxy/http/HttpConfig.cc | 141 +++++++++++++++++++--
proxy/http/HttpConfig.h | 25 ++++
proxy/http/HttpProxyAPIEnums.h | 27 ++++
proxy/http/HttpServerSession.h | 14 ++
proxy/http/HttpTransactHeaders.cc | 88 ++++++++++---
tests/gold_tests/headers/via-compact.gold | 4 +
.../headers/{via.test.py => via-compact.test.py} | 16 ++-
tests/gold_tests/headers/via.gold | 2 +
tests/gold_tests/headers/via.test.py | 28 +++-
18 files changed, 428 insertions(+), 40 deletions(-)
diff --git a/doc/admin-guide/files/records.config.en.rst
b/doc/admin-guide/files/records.config.en.rst
index efb3492..372fd46 100644
--- a/doc/admin-guide/files/records.config.en.rst
+++ b/doc/admin-guide/files/records.config.en.rst
@@ -867,6 +867,29 @@ ip-resolve
Set the server and version string in the ``Via`` request header to the
origin server which is inserted when the value of
:ts:cv:`proxy.config.http.insert_request_via_str` is not ``0``. Note that the
actual default value is defined with ``"ApacheTrafficServer/" PACKAGE_VERSION``
in a C++ source code, and you must write such as ``ApacheTrafficServer/6.0.0``
if you really set a value with the version in :file:`records.config` file. If
you want to hide the version, you can set this value [...]
+.. ts:cv:: CONFIG proxy.config.http.request_via_transport STRING compact
+ :reloadable:
+ :overridable:
+
+ The ``Via`` string set by |TS| on an upstream request to the origin server
can contain a
+ description of the transport protocols used by the user agent to
+ connect to |TS|. This configuration controls the level of detail for the
``Via`` string placed in
+ the outbound request.
+
+ ======= =================================================================
+ Value Effect
+ ======= =================================================================
+ None No details about the transport protocol.
+ Compact Only the top level protocol plus a TLS indicator.
+ Full The entire protocol stack of :ref:`protocol_tags <protocol_tags>`
+ as a dash separated list.
+ ======= =================================================================
+
+ The full protocol stack will be a list of :ref:`protocol tags
<protocol_tags>` separated by a '-' character. The set of tags will vary but
not the number of white space separated tokens in the ``Via`` header. The tags
are ordered left to right from top most protocol to lowest level protocol.
+
+ The compact form always starts with ``http``. If the connection is TLS an
``s`` is added. The
+ HTTP version is a ``/`` followed by one of ``1.0``, ``1.1``, ``2``. E.g.
``http/1.1`` for just HTTP, or ``https/2`` for HTTP/2 over TLS.
+
.. ts:cv:: CONFIG proxy.config.http.insert_response_via_str INT 0
:reloadable:
:overridable:
@@ -892,6 +915,32 @@ ip-resolve
Set the server and version string in the ``Via`` response header to the
client which is inserted when the value of
:ts:cv:`proxy.config.http.insert_response_via_str` is not ``0``. Note that the
actual default value is defined with ``"ApacheTrafficServer/" PACKAGE_VERSION``
in a C++ source code, and you must write such as ``ApacheTrafficServer/6.0.0``
if you really set a value with the version in :file:`records.config` file. If
you want to hide the version, you can set this value to ` [...]
+.. ts:cv:: CONFIG proxy.config.http.response_via_transport STRING compact
+ :reloadable:
+ :overridable:
+
+ The ``Via`` string set by |TS| in the response to the client can contain a
description of the
+ transport protocols used by |TS| to connect to the origin server. This
configuration controls
+ the level of detail for the ``Via`` string placed in the response.
+
+ ======= =================================================================
+ Value Effect
+ ======= =================================================================
+ None No details about the transport protocol.
+ Compact Only the top level protocol plus a TLS indicator.
+ Full The entire protocol stack of :ref:`protocol_tags <protocol_tags>`
+ as a dash separated list.
+ ======= =================================================================
+
+ The full protocol stack will be a list of :ref:`protocol tags
<protocol_tags>` separated by a '-'
+ character. The set of tags will vary but not the number of white space
separated tokens in the
+ ``Via`` header. The tags are ordered left to right from top most protocol
to lowest level
+ protocol. Note this list may not have more detail even when set to ``Full``
if |TS| does not
+ connect upstream (e.g., the request is a cache hit).
+
+ The compact form always starts with ``http``. If the connection is TLS an
``s`` is added. The
+ HTTP version is a ``/`` followed by one of ``1.0``, ``1.1``, ``2``. E.g.
``http/1.1`` for just HTTP, or ``https/2`` for HTTP/2 over TLS.
+
.. ts:cv:: CONFIG proxy.config.http.send_100_continue_response INT 0
:reloadable:
diff --git a/doc/developer-guide/api/functions/TSClientProtocolStack.en.rst
b/doc/developer-guide/api/functions/TSClientProtocolStack.en.rst
index d84ca03..d5953f1 100644
--- a/doc/developer-guide/api/functions/TSClientProtocolStack.en.rst
+++ b/doc/developer-guide/api/functions/TSClientProtocolStack.en.rst
@@ -77,6 +77,8 @@ matched with an anchor prefix search, as with debug tags. For
instance if :arg:`
will match "tls/1.2" or "tls/1.3". This makes checking for TLS or IP more
convenient. If more precision
is required the entire protocol stack can be retrieved and processed more
thoroughly.
+.. _protocol_tags:
+
The protocol tags defined by |TS|.
=========== =========
diff --git a/lib/records/I_RecCore.h b/lib/records/I_RecCore.h
index da904c7..358ffd9 100644
--- a/lib/records/I_RecCore.h
+++ b/lib/records/I_RecCore.h
@@ -176,6 +176,7 @@ int RecGetRecordBool(const char *name, RecBool *rec_byte,
bool lock = true);
typedef void (*RecLookupCallback)(const RecRecord *, void *);
int RecLookupRecord(const char *name, RecLookupCallback callback, void *data,
bool lock = true);
+int RecLookupRecord(const char *name, std::function<void(const RecRecord *)>
&&f, bool lock = true);
int RecLookupMatchingRecords(unsigned rec_type, const char *match,
RecLookupCallback callback, void *data, bool lock = true);
int RecGetRecordType(const char *name, RecT *rec_type, bool lock = true);
diff --git a/lib/records/RecCore.cc b/lib/records/RecCore.cc
index ed8632e..4ef1652 100644
--- a/lib/records/RecCore.cc
+++ b/lib/records/RecCore.cc
@@ -484,6 +484,25 @@ RecLookupRecord(const char *name, void (*callback)(const
RecRecord *, void *), v
return err;
}
+namespace
+{
+// Shim to enable using functors for @c RecLookupRecord.
+// This function is passed to @c RecLookupRecord with a @a data argument that
is a pointer to the functor.
+// When this is called, cast @a data back to the functor and invoke it.
+void
+RecLookupRecordForward(const RecRecord *r, void *data)
+{
+ (*static_cast<std::function<void(const RecRecord *)> *>(data))(r);
+}
+}
+
+/// Overload that takes a functor.
+int
+RecLookupRecord(const char *name, std::function<void(const RecRecord *)>
&&callback, bool lock)
+{
+ return RecLookupRecord(name, &RecLookupRecordForward, &callback, lock);
+}
+
int
RecLookupMatchingRecords(unsigned rec_type, const char *match, void
(*callback)(const RecRecord *, void *), void *data, bool lock)
{
diff --git a/lib/ts/apidefs.h.in b/lib/ts/apidefs.h.in
index ac0a802..6e2888e 100644
--- a/lib/ts/apidefs.h.in
+++ b/lib/ts/apidefs.h.in
@@ -567,6 +567,13 @@ typedef enum {
TS_SERVER_SESSION_SHARING_POOL_GLOBAL,
TS_SERVER_SESSION_SHARING_POOL_THREAD,
} TSServerSessionSharingPoolType;
+
+/// The verbosity of the transport protocol stack in a VIA header.
+typedef enum {
+ TS_VIA_TRANSPORT_NONE = 0,
+ TS_VIA_TRANSPORT_COMPACT = 1,
+ TS_VIA_TRANSPORT_FULL = 2
+} TSViaTransportVerbosity;
#endif
/* librecords types */
@@ -751,6 +758,8 @@ typedef enum {
TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_LANGUAGE_MISMATCH,
TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_ENCODING_MISMATCH,
TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_CHARSET_MISMATCH,
+ TS_CONFIG_HTTP_REQUEST_VIA_TRANSPORT,
+ TS_CONFIG_HTTP_RESPONSE_VIA_TRANSPORT,
TS_CONFIG_LAST_ENTRY
} TSOverridableConfigKey;
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index 762029a..f7d331a 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -365,12 +365,17 @@ static const RecordElement RecordsConfig[] =
// # verbose via string
// #
// # 0 - no extra info added to string
- // # 1 - all extra information added
- // # 2 - some extra info added
+ // # 1 - minimal information.
+ // # 2 - more information
+ // # 3 - maximum information.
{RECT_CONFIG, "proxy.config.http.request_via_str", RECD_STRING,
"ApacheTrafficServer/" PACKAGE_VERSION, RECU_DYNAMIC, RR_NULL, RECC_NULL,
nullptr, RECA_NULL}
,
{RECT_CONFIG, "proxy.config.http.response_via_str", RECD_STRING,
"ApacheTrafficServer/" PACKAGE_VERSION, RECU_DYNAMIC, RR_NULL, RECC_NULL,
nullptr, RECA_NULL}
,
+ {RECT_CONFIG, "proxy.config.http.request_via_transport", RECD_STRING,
"compact", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
+ ,
+ {RECT_CONFIG, "proxy.config.http.response_via_transport", RECD_STRING,
"compact", RECU_DYNAMIC, RR_NULL, RECC_NULL, nullptr, RECA_NULL}
+ ,
{RECT_CONFIG, "proxy.config.http.response_server_enabled", RECD_INT, "1",
RECU_DYNAMIC, RR_NULL, RECC_NULL, "[0-2]", RECA_NULL}
,
{RECT_CONFIG, "proxy.config.http.response_server_str", RECD_STRING, "ATS/"
PACKAGE_VERSION, RECU_DYNAMIC, RR_NULL, RECC_NULL, ".*", RECA_NULL}
diff --git a/plugins/experimental/ts_lua/ts_lua_http_config.c
b/plugins/experimental/ts_lua/ts_lua_http_config.c
index 00d1e04..36d0dcd 100644
--- a/plugins/experimental/ts_lua/ts_lua_http_config.c
+++ b/plugins/experimental/ts_lua/ts_lua_http_config.c
@@ -128,6 +128,8 @@ typedef enum {
TS_LUA_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_LANGUAGE_MISMATCH =
TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_LANGUAGE_MISMATCH,
TS_LUA_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_ENCODING_MISMATCH =
TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_ENCODING_MISMATCH,
TS_LUA_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_CHARSET_MISMATCH =
TS_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_CHARSET_MISMATCH,
+ TS_LUA_CONFIG_HTTP_REQUEST_VIA_TRANSPORT =
TS_CONFIG_HTTP_REQUEST_VIA_TRANSPORT,
+ TS_LUA_CONFIG_HTTP_RESPONSE_VIA_TRANSPORT =
TS_CONFIG_HTTP_RESPONSE_VIA_TRANSPORT,
TS_LUA_CONFIG_LAST_ENTRY =
TS_CONFIG_LAST_ENTRY,
} TSLuaOverridableConfigKey;
@@ -248,6 +250,8 @@ ts_lua_var_item ts_lua_http_config_vars[] = {
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_LANGUAGE_MISMATCH),
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_ENCODING_MISMATCH),
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_CACHE_IGNORE_ACCEPT_CHARSET_MISMATCH),
+ TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_REQUEST_VIA_TRANSPORT),
+ TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_HTTP_RESPONSE_VIA_TRANSPORT),
TS_LUA_MAKE_VAR_ITEM(TS_LUA_CONFIG_LAST_ENTRY),
};
diff --git a/proxy/InkAPI.cc b/proxy/InkAPI.cc
index 33436b4..93ab100 100644
--- a/proxy/InkAPI.cc
+++ b/proxy/InkAPI.cc
@@ -7882,6 +7882,12 @@ _conf_to_memberp(TSOverridableConfigKey conf,
OverridableHttpConfigParams *overr
case TS_CONFIG_HTTP_INSERT_RESPONSE_VIA_STR:
ret =
_memberp_to_generic(&overridableHttpConfig->insert_response_via_string, typep);
break;
+ case TS_CONFIG_HTTP_REQUEST_VIA_TRANSPORT:
+ ret = _memberp_to_generic(&overridableHttpConfig->request_via_transport,
typep);
+ break;
+ case TS_CONFIG_HTTP_RESPONSE_VIA_TRANSPORT:
+ ret = _memberp_to_generic(&overridableHttpConfig->response_via_transport,
typep);
+ break;
case TS_CONFIG_HTTP_CACHE_HEURISTIC_MIN_LIFETIME:
ret =
_memberp_to_generic(&overridableHttpConfig->cache_heuristic_min_lifetime,
typep);
break;
@@ -8210,6 +8216,9 @@ TSHttpTxnConfigFloatGet(TSHttpTxn txnp,
TSOverridableConfigKey conf, TSMgmtFloat
TSReturnCode
TSHttpTxnConfigStringSet(TSHttpTxn txnp, TSOverridableConfigKey conf, const
char *value, int length)
{
+ extern MgmtByteEnumConversion ViaTransportVerbosityConversion;
+ RecEnumData data;
+
sdk_assert(sdk_sanity_check_txn(txnp) == TS_SUCCESS);
if (length == -1) {
@@ -8257,6 +8266,17 @@ TSHttpTxnConfigStringSet(TSHttpTxn txnp,
TSOverridableConfigKey conf, const char
if (value && length > 0) {
s->t_state.txn_conf->client_cert_filepath = const_cast<char *>(value);
}
+ case TS_CONFIG_HTTP_RESPONSE_VIA_TRANSPORT:
+ data._s.setView(value, length);
+ if
(ViaTransportVerbosityConversion(s->t_state.txn_conf->response_via_transport,
"proxy.config.http.response_via_tranport",
+ RECD_STRING, data)) {
+ }
+ break;
+ case TS_CONFIG_HTTP_REQUEST_VIA_TRANSPORT:
+ data._s.setView(value, length);
+ if
(ViaTransportVerbosityConversion(s->t_state.txn_conf->request_via_transport,
"proxy.config.http.request_via_tranport",
+ RECD_STRING, data)) {
+ }
default:
return TS_ERROR;
break;
@@ -8466,6 +8486,11 @@ TSHttpTxnConfigFind(const char *name, int length,
TSOverridableConfigKey *conf,
cnf = TS_CONFIG_HTTP_DOC_IN_CACHE_SKIP_DNS;
}
break;
+ case 't':
+ if (!strncmp(name, "proxy.config.http.request_via_transport", length)) {
+ cnf = TS_CONFIG_HTTP_REQUEST_VIA_TRANSPORT;
+ typ = TS_RECORDDATATYPE_STRING;
+ }
}
break;
@@ -8506,6 +8531,9 @@ TSHttpTxnConfigFind(const char *name, int length,
TSOverridableConfigKey *conf,
case 't':
if (!strncmp(name, "proxy.config.http.keep_alive_enabled_out", length)) {
cnf = TS_CONFIG_HTTP_KEEP_ALIVE_ENABLED_OUT;
+ } else if (!strncmp(name, "proxy.config.http.response_via_transport",
length)) {
+ cnf = TS_CONFIG_HTTP_RESPONSE_VIA_TRANSPORT;
+ typ = TS_RECORDDATATYPE_STRING;
}
break;
}
diff --git a/proxy/InkAPITest.cc b/proxy/InkAPITest.cc
index dcd246e..8d5d20f 100644
--- a/proxy/InkAPITest.cc
+++ b/proxy/InkAPITest.cc
@@ -7608,6 +7608,8 @@ const char *SDK_Overridable_Configs[TS_CONFIG_LAST_ENTRY]
= {
"proxy.config.http.cache.ignore_accept_language_mismatch",
"proxy.config.http.cache.ignore_accept_encoding_mismatch",
"proxy.config.http.cache.ignore_accept_charset_mismatch",
+ "proxy.config.http.request_via_transport",
+ "proxy.config.http.response_via_transport",
};
REGRESSION_TEST(SDK_API_OVERRIDABLE_CONFIGS)(RegressionTest *test, int /*
atype ATS_UNUSED */, int *pstatus)
{
diff --git a/proxy/http/HttpConfig.cc b/proxy/http/HttpConfig.cc
index 52b8674..dfe6d53 100644
--- a/proxy/http/HttpConfig.cc
+++ b/proxy/http/HttpConfig.cc
@@ -31,6 +31,8 @@
#include "P_Net.h"
#include "P_RecUtils.h"
#include <records/I_RecHttp.h>
+#include <ts/MemView.h>
+#include <list>
#define HttpEstablishStaticConfigStringAlloc(_ix, _n) \
REC_EstablishStaticConfigStringAlloc(_ix, _n); \
@@ -65,16 +67,18 @@ public:
/// Data item for enumerated type config value.
template <typename T> struct ConfigEnumPair {
T _value;
- const char *_key;
+ ts::StringView _key;
+
+ ConfigEnumPair(T v, char const *k) : _value(v), _key(k) {}
};
/// Convert a string to an enumeration value.
/// @a n is the number of entries in the list.
/// @return @c true if the string is found, @c false if not found.
/// If found @a value is set to the corresponding value in @a list.
-template <typename T, unsigned N>
+template <typename E, unsigned N>
static bool
-http_config_enum_search(const char *key, const ConfigEnumPair<T> (&list)[N],
MgmtByte &value)
+http_config_enum_search(ts::StringView key, const ConfigEnumPair<E>
(&list)[N], MgmtByte &value)
{
// We don't expect any of these lists to be more than 10 long, so a linear
search is the best choice.
for (unsigned i = 0; i < N; ++i) {
@@ -86,6 +90,13 @@ http_config_enum_search(const char *key, const
ConfigEnumPair<T> (&list)[N], Mgm
return false;
}
+template <typename E, unsigned N>
+static bool
+http_config_enum_search(const char *key, const ConfigEnumPair<E> (&list)[N],
MgmtByte &value)
+{
+ return http_config_enum_search(ts::StringView(key), list, value);
+}
+
/// Read a string from the configuration and convert it to an enumeration
value.
/// @a n is the number of entries in the list.
/// @return @c true if the string is found, @c false if not found.
@@ -101,6 +112,100 @@ http_config_enum_read(const char *name, const
ConfigEnumPair<T> (&list)[N], Mgmt
return false;
}
+/** Create a functor that converts record data to a @c MgmtByte based on a set
of enum pairs.
+ The functor takes a refernce to the byte and the record data and updates
the byte if valid.
+ @return @c true if the data was valid and @a b update. @c false otherwise.
+ */
+template <typename E, int N>
+MgmtByteEnumConversion
+CreateMgmtByteEnumConversion(const ConfigEnumPair<E> (&pairs)[N])
+{
+ return [pairs](MgmtByte &b, const char *name, RecDataT type, RecEnumData
data) -> bool {
+ MgmtByte value;
+ bool valid_p = false;
+ if (RECD_INT == type) {
+ value = data._i;
+ valid_p = std::numeric_limits<E>::min() <= value && value <=
std::numeric_limits<E>::max();
+ if (!valid_p)
+ Warning("Configuration update for '%s' failed - %d is not in
[%d..%d]", name, static_cast<int>(data._i),
+ static_cast<int>(std::numeric_limits<E>::min()),
static_cast<int>(std::numeric_limits<E>::max()));
+ } else if (RECD_STRING == type) {
+ valid_p = http_config_enum_search(data._s, pairs, value);
+ if (!valid_p) {
+ char buff[1024];
+ char *s = buff;
+ char *limit = buff + sizeof(buff);
+ for (auto const &p : pairs) {
+ if (s + p._key.size() + 2 < limit) {
+ if (s != buff)
+ *s++ = ',';
+ memcpy(s, p._key.ptr(), p._key.size());
+ s += p._key.size();
+ *s = 0;
+ } else {
+ break; // out of space, give up.
+ }
+ }
+ Warning("Configuration update for '%s' failed - '%.*s' is not one of
(%s)", name, static_cast<int>(data._s.size()),
+ data._s.ptr(), buff);
+ }
+ } else {
+ Warning("Configuration update for '%s' failed - type %d - expected
STRING or INT", name, static_cast<int>(type));
+ }
+ if (valid_p)
+ b = value;
+ return valid_p;
+ };
+}
+
+// forward declare for the enum update.
+static int http_config_cb(const char *, RecDataT, RecData, void *);
+
+namespace
+{
+/// Type of function called from the generic HTTP config update callback.
+/// It validates the updated data and if valid, updates the data and returns
@c true.
+using UpdateFunc = std::function<bool(const char *name, RecDataT type, RecData
data)>;
+
+/** Establish an update callback for an enum value in the same manner as the
base types.
+
+ This uses the generic update callback, passing it an update functor
specialized for this
+ type. When the generic update (@c http_config_cb) is called it will be
passed a pointer to the
+ specialized update functor, which it will then invoke. If that succeeds,
then the data was valid
+ and the override member was updated just as for the base type callbacks.
+
+ A conversion functor must be supplied. See @c MgmtByteEnumConversion.
+ */
+void
+HttpEstablishStaticConfigEnumByte(MgmtByte &b, const char *name,
MgmtByteEnumConversion &convertor)
+{
+ // Memory management troubles. Because the callback system isn't really C++
it doesn't handle
+ // cleaning up the callback data. That must be done here by stuffing the
functor into a list and
+ // letting the list destructor clean up. This is not strictly needed as
these objects have process
+ // lifetime and we could just "leak", but the ASAN guys will yell at me if I
do that.
+ static std::list<UpdateFunc> f_list;
+
+ // Create the actual update function which updates the value in the master
overide global.
+ f_list.emplace_front([&b, &convertor](const char *name, RecDataT type,
RecData data) -> bool {
+ return convertor(b, name, type, RecEnumData(type, data));
+ });
+ // Get a direct reference to it to capture in the initial update lambda.
+ UpdateFunc &f = *(f_list.begin());
+
+ // No standard config link because it's not a standard type.
+ // Just use the generic callback with the updater.
+ REC_RegisterConfigUpdateFunc(name, http_config_cb, &f);
+ // Need to do this the first time around to initialize. Find the record and
whatever
+ // data it has and pass that as if it were an update.
+ RecLookupRecord(name, [&f](const RecRecord *r) -> void { f(r->name,
r->data_type, r->data); });
+}
+}
+
+////////////////////////////////////////////////////////////////
+//
+// static variables
+//
+////////////////////////////////////////////////////////////////
/// Session sharing match types.
static const ConfigEnumPair<TSServerSessionSharingMatchType>
SessionSharingMatchStrings[] = {
{TS_SERVER_SESSION_SHARING_MATCH_NONE, "none"},
@@ -112,17 +217,18 @@ static const
ConfigEnumPair<TSServerSessionSharingPoolType> SessionSharingPoolSt
{TS_SERVER_SESSION_SHARING_POOL_GLOBAL, "global"},
{TS_SERVER_SESSION_SHARING_POOL_THREAD, "thread"}};
-////////////////////////////////////////////////////////////////
-//
-// static variables
-//
-////////////////////////////////////////////////////////////////
+static const ConfigEnumPair<TSViaTransportVerbosity>
ViaTransportVerbosityStrings[] = {{TS_VIA_TRANSPORT_NONE, "none"},
+
{TS_VIA_TRANSPORT_COMPACT, "compact"},
+
{TS_VIA_TRANSPORT_FULL, "full"}};
+
int HttpConfig::m_id = 0;
HttpConfigParams HttpConfig::m_master;
static volatile int http_config_changes = 1;
static HttpConfigCont *http_config_cont = nullptr;
+MgmtByteEnumConversion
ViaTransportVerbosityConversion{CreateMgmtByteEnumConversion(ViaTransportVerbosityStrings)};
+
HttpConfigCont::HttpConfigCont() : Continuation(new_ProxyMutex())
{
SET_HANDLER(&HttpConfigCont::handle_event);
@@ -138,14 +244,22 @@ HttpConfigCont::handle_event(int /* event ATS_UNUSED */,
void * /* edata ATS_UNU
}
static int
-http_config_cb(const char * /* name ATS_UNUSED */, RecDataT /* data_type
ATS_UNUSED */, RecData /* data ATS_UNUSED */,
- void * /* cookie ATS_UNUSED */)
+http_config_cb(const char *name, RecDataT data_type, RecData data, void
*cookie)
{
+ // A cookie means a specialized update function for this particular config
value.
+ // Indirect through this because the config update logic only takes function
pointers
+ // not generic functors. The update function verifies it's worth doing an
update.
+ if (cookie) {
+ if (!((*static_cast<UpdateFunc *>(cookie))(name, data_type, data)))
+ return 0; // invalid data, don't update config.
+ }
+
ink_atomic_increment((int *)&http_config_changes, 1);
INK_MEMORY_BARRIER;
eventProcessor.schedule_in(http_config_cont, HRTIME_SECONDS(1), ET_CALL);
+
return 0;
}
@@ -938,6 +1052,11 @@ HttpConfig::startup()
c.oride.server_session_sharing_match);
http_config_enum_read("proxy.config.http.server_session_sharing.pool",
SessionSharingPoolStrings, c.server_session_sharing_pool);
+ HttpEstablishStaticConfigEnumByte(c.oride.request_via_transport,
"proxy.config.http.request_via_transport",
+ ViaTransportVerbosityConversion);
+ HttpEstablishStaticConfigEnumByte(c.oride.response_via_transport,
"proxy.config.http.response_via_transport",
+ ViaTransportVerbosityConversion);
+
HttpEstablishStaticConfigByte(c.oride.auth_server_session_private,
"proxy.config.http.auth_server_session_private");
HttpEstablishStaticConfigByte(c.oride.keep_alive_post_out,
"proxy.config.http.keep_alive_post_out");
@@ -1178,6 +1297,8 @@ HttpConfig::reconfigure()
params->oride.insert_request_via_string =
m_master.oride.insert_request_via_string;
params->oride.insert_response_via_string =
m_master.oride.insert_response_via_string;
+ params->oride.request_via_transport =
m_master.oride.request_via_transport;
+ params->oride.response_via_transport =
m_master.oride.response_via_transport;
params->proxy_request_via_string =
ats_strdup(m_master.proxy_request_via_string);
params->proxy_request_via_string_len =
(params->proxy_request_via_string) ? strlen(params->proxy_request_via_string) :
0;
params->proxy_response_via_string =
ats_strdup(m_master.proxy_response_via_string);
diff --git a/proxy/http/HttpConfig.h b/proxy/http/HttpConfig.h
index 303db6a..b52f831 100644
--- a/proxy/http/HttpConfig.h
+++ b/proxy/http/HttpConfig.h
@@ -47,6 +47,7 @@
#include "HttpProxyAPIEnums.h"
#include "ProxyConfig.h"
#include "P_RecProcess.h"
+#include <functional>
/* Instead of enumerating the stats in DynamicStats.h, each module needs
to enumerate its stats separately and register them with librecords
@@ -554,6 +555,8 @@ struct OverridableHttpConfigParams {
MgmtByte insert_request_via_string;
MgmtByte insert_response_via_string;
+ MgmtByte request_via_transport = TS_VIA_TRANSPORT_COMPACT; ///< Verbosity
of the transport via entry for a request.
+ MgmtByte response_via_transport = TS_VIA_TRANSPORT_COMPACT; ///< Verbosity
of the transport via entry for a response.
//////////////////////
// DOC IN CACHE NO DNS//
@@ -888,4 +891,26 @@ inline HttpConfigParams::~HttpConfigParams()
delete connect_ports;
}
}
+
+/// A variant of RecData that contains a StringView. This is necessary because
the TS API passes in configuration
+/// strings by pointer&length.
+union RecEnumData {
+ int _i;
+ ts::StringView _s;
+
+ RecEnumData() : _i(0) {}
+ RecEnumData(RecDataT t, RecData d)
+ {
+ if (t == RECD_INT)
+ _i = d.rec_int;
+ else if (t == RECD_STRING)
+ _s = ts::StringView{d.rec_string};
+ else
+ _i = 0;
+ }
+};
+/// Type of function called from the generic HTTP config update callback.
+/// It validates the updated data and if valid, updates the data and returns
@c true.
+using MgmtByteEnumConversion = std::function<bool(MgmtByte &b, const char
*name, RecDataT type, RecEnumData data)>;
+
#endif /* #ifndef _HttpConfig_h_ */
diff --git a/proxy/http/HttpProxyAPIEnums.h b/proxy/http/HttpProxyAPIEnums.h
index f8ff3b4..ab88b7b 100644
--- a/proxy/http/HttpProxyAPIEnums.h
+++ b/proxy/http/HttpProxyAPIEnums.h
@@ -30,6 +30,10 @@
#ifndef _HTTP_PROXY_API_ENUMS_H_
#define _HTTP_PROXY_API_ENUMS_H_
+#ifdef __cplusplus
+#include <limits>
+#endif
+
/// Server session sharing values - match
typedef enum {
TS_SERVER_SESSION_SHARING_MATCH_NONE,
@@ -44,4 +48,27 @@ typedef enum {
TS_SERVER_SESSION_SHARING_POOL_THREAD,
} TSServerSessionSharingPoolType;
+/// The verbosity of the transport protocol stack in a VIA header.
+typedef enum { TS_VIA_TRANSPORT_NONE = 0, TS_VIA_TRANSPORT_COMPACT = 1,
TS_VIA_TRANSPORT_FULL = 2 } TSViaTransportVerbosity;
+
+#ifdef __cplusplus
+namespace std
+{
+template <> class numeric_limits<TSViaTransportVerbosity> : public
numeric_limits<uint8_t>
+{
+public:
+ static TSViaTransportVerbosity
+ min()
+ {
+ return TS_VIA_TRANSPORT_NONE;
+ }
+ static TSViaTransportVerbosity
+ max()
+ {
+ return TS_VIA_TRANSPORT_FULL;
+ }
+};
+}
+#endif
+
#endif // _HTTP_PROXY_API_ENUMS_H_
diff --git a/proxy/http/HttpServerSession.h b/proxy/http/HttpServerSession.h
index 5400d4e..f110ed3 100644
--- a/proxy/http/HttpServerSession.h
+++ b/proxy/http/HttpServerSession.h
@@ -180,6 +180,20 @@ public:
// an asyncronous cancel on NT
MIOBuffer *read_buffer;
+ virtual int
+ populate_protocol(ts::StringView *result, int size) const
+ {
+ auto vc = this->get_netvc();
+ return vc ? vc->populate_protocol(result, size) : 0;
+ }
+
+ virtual const char *
+ protocol_contains(ts::StringView tag_prefix) const
+ {
+ auto vc = this->get_netvc();
+ return vc ? vc->protocol_contains(tag_prefix) : nullptr;
+ }
+
private:
HttpServerSession(HttpServerSession &);
diff --git a/proxy/http/HttpTransactHeaders.cc
b/proxy/http/HttpTransactHeaders.cc
index 7bcf31f..3f7f583 100644
--- a/proxy/http/HttpTransactHeaders.cc
+++ b/proxy/http/HttpTransactHeaders.cc
@@ -683,26 +683,84 @@
HttpTransactHeaders::insert_server_header_in_response(const char *server_tag, in
}
}
-/// Look up the protocol stack and write it to the @a via_string.
+/// write the protocol stack to the @a via_string.
size_t
-write_client_protocol_stack(HttpTransact::State *s, char *via_string, size_t
len)
+write_via_protocol_stack(char *via_string, size_t len, TSViaTransportVerbosity
detail, ts::StringView *proto_buf, int nproto)
{
- std::array<ts::StringView, 10> proto_buf; // 10 seems like a reasonable
number of protos to print
- int retval =
s->state_machine->populate_client_protocol(proto_buf.data(), proto_buf.size());
- char *via = via_string;
- char *limit = via_string + len;
- ts::StringView *v = proto_buf.data();
- for (int i = 0; i < retval && (via + v->size() + 1) < limit; ++i, ++v) {
- if (i) {
- *via++ = '-';
+ char *via = via_string; // keep original pointer for size computation
later.
+ char *limit = via_string + len;
+ static constexpr ts::StringView tls_prefix{"tls/", ts::StringView::literal};
+
+ if (nproto <= 0 || via == nullptr || len <= 0) {
+ // nothing
+ } else if (detail == TS_VIA_TRANSPORT_FULL) {
+ ts::StringView *v = proto_buf;
+ for (int i = 0; i < nproto && (via + v->size() + 1) < limit; ++i, ++v) {
+ if (i)
+ *via++ = '-';
+ memcpy(via, v->ptr(), v->size());
+ via += v->size();
+ }
+ *via++ = ' ';
+ } else if (detail == TS_VIA_TRANSPORT_COMPACT) {
+ ts::StringView *proto_end = proto_buf + nproto;
+ bool http_1_0_p = std::find(proto_buf, proto_end,
IP_PROTO_TAG_HTTP_1_0) != proto_end;
+ bool http_1_1_p = std::find(proto_buf, proto_end,
IP_PROTO_TAG_HTTP_1_1) != proto_end;
+
+ if ((http_1_0_p || http_1_1_p) && via + 10 < limit) {
+ bool tls_p = std::find_if(proto_buf, proto_end, [](ts::StringView tag) {
return tls_prefix.isPrefixOf(tag); }) != proto_end;
+ bool http_2_p = std::find(proto_buf, proto_end, IP_PROTO_TAG_HTTP_2_0)
!= proto_end;
+
+ memcpy(via, "http", 4);
+ via += 4;
+ if (tls_p)
+ *via++ = 's';
+ *via++ = '/';
+ if (http_2_p) {
+ *via++ = '2';
+ } else if (http_1_0_p) {
+ memcpy(via, "1.0", 3);
+ via += 3;
+ } else if (http_1_1_p) {
+ memcpy(via, "1.1", 3);
+ via += 3;
+ }
+ *via++ = ' ';
}
- memcpy(via, v->ptr(), v->size());
- via += v->size();
}
- *via++ = ' ';
return via - via_string;
}
+/// Look up the protocol stack and write it to the @a via_string.
+size_t
+write_client_protocol_stack(char *via_string, size_t len, HttpTransact::State
*s)
+{
+ std::array<ts::StringView, 10> proto_buf; // 10 seems like a reasonable
number of protos to print
+ int n = s->state_machine->populate_client_protocol(proto_buf.data(),
proto_buf.size());
+ return write_via_protocol_stack(via_string, len,
static_cast<TSViaTransportVerbosity>(s->txn_conf->request_via_transport),
+ proto_buf.data(), n);
+}
+
+/// Look up the protocol stack and write it to the @a via_string.
+size_t
+write_upstream_protocol_stack(char *via_string, size_t len,
HttpTransact::State *s, HTTPHdr *header)
+{
+ int n = 0;
+ int offset = 0; // # of proto_buf slots reserved
for protocol data above the netvc
+ std::array<ts::StringView, 10> proto_buf; // 10 seems like a reasonable
number of protos to print
+
+ // Should suffice - if we're adding a VIA, it's HTTP and only 1.0 and 1.1
are supported outbound.
+ proto_buf[offset] = HTTP_MINOR(header->version_get().m_version) == 0 ?
IP_PROTO_TAG_HTTP_1_0 : IP_PROTO_TAG_HTTP_1_1;
+ ++offset;
+
+ auto ss = s->state_machine->get_server_session();
+ if (ss) {
+ n += ss->populate_protocol(proto_buf.data() + offset, proto_buf.size() -
offset);
+ }
+ return write_via_protocol_stack(via_string, len,
static_cast<TSViaTransportVerbosity>(s->txn_conf->response_via_transport),
+ proto_buf.data(), n + offset);
+}
+
///////////////////////////////////////////////////////////////////////////////
// Name : insert_via_header_in_request
// Description: takes in existing via_string and inserts it in header
@@ -760,7 +818,7 @@
HttpTransactHeaders::insert_via_header_in_request(HttpTransact::State *s, HTTPHd
char *incoming_via = s->via_string;
- via_string += write_client_protocol_stack(s, via_string,
sizeof(new_via_string) - (via_string - new_via_string));
+ via_string += write_client_protocol_stack(via_string, sizeof(new_via_string)
- (via_string - new_via_string), s);
via_string += nstrcpy(via_string, s->http_config_param->proxy_hostname);
*via_string++ = '[';
@@ -827,7 +885,7 @@
HttpTransactHeaders::insert_via_header_in_response(HttpTransact::State *s, HTTPH
char *incoming_via = s->via_string;
- via_string += write_client_protocol_stack(s, via_string,
sizeof(new_via_string) - (via_string - new_via_string));
+ via_string += write_upstream_protocol_stack(via_string,
sizeof(new_via_string) - (via_string - new_via_string), s, header);
via_string += nstrcpy(via_string, s->http_config_param->proxy_hostname);
*via_string++ = ' ';
*via_string++ = '(';
diff --git a/tests/gold_tests/headers/via-compact.gold
b/tests/gold_tests/headers/via-compact.gold
new file mode 100644
index 0000000..cc5518b
--- /dev/null
+++ b/tests/gold_tests/headers/via-compact.gold
@@ -0,0 +1,4 @@
+Via: http/1.1
+Via: http/1.0
+Via: https/2
+Via: https/1.1
diff --git a/tests/gold_tests/headers/via.test.py
b/tests/gold_tests/headers/via-compact.test.py
similarity index 81%
copy from tests/gold_tests/headers/via.test.py
copy to tests/gold_tests/headers/via-compact.test.py
index db70d79..c448789 100644
--- a/tests/gold_tests/headers/via.test.py
+++ b/tests/gold_tests/headers/via-compact.test.py
@@ -44,7 +44,8 @@ def RequireCurlFeature(tag):
return False
Test.SkipUnless(
- Condition.Condition(lambda : RequireCurlFeature('http2'), "Via test
requires a curl that supports HTTP/2")
+ Condition.Condition(lambda : RequireCurlFeature('http2'), "Via test
requires a curl that supports HTTP/2"),
+ Condition.Condition(lambda : RequireCurlFeature('IPv6'), "Via test
requires a curl that supports IPv6")
)
Test.ContinueOnFail=True
@@ -67,9 +68,10 @@ ts.Variables.ssl_port = 4443
ts.Disk.records_config.update({
'proxy.config.http.insert_request_via_str' : 1,
'proxy.config.http.insert_response_via_str' : 1,
+ 'proxy.config.http.request_via_transport' : 'compact',
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path':
'{0}'.format(ts.Variables.SSLDir),
- 'proxy.config.http.server_ports': '{0}
{1}:proto=http2;http:ssl'.format(ts.Variables.port,ts.Variables.ssl_port),
+ 'proxy.config.http.server_ports': 'ipv4:{0}
ipv4:{1}:proto=http2;http:ssl ipv6:{0}
ipv6:{1}:proto=http2;http:ssl'.format(ts.Variables.port,ts.Variables.ssl_port),
})
ts.Disk.remap_config.AddLine(
@@ -85,7 +87,7 @@ ts.Disk.ssl_multicert_config.AddLine(
# Set up to check the output after the tests have run.
via_log_id = Test.Disk.File("via.log")
-via_log_id.Content = "via.gold"
+via_log_id.Content = "via-compact.gold"
# Ask the OS if the port is ready for connect()
def CheckPort(Port) :
@@ -98,7 +100,7 @@ tr.Processes.Default.StartBefore(server,
ready=CheckPort(server.Variables.Port))
# Delay on readiness of our ssl ports
tr.Processes.Default.StartBefore(Test.Processes.ts,
ready=CheckPort(ts.Variables.ssl_port))
-tr.Processes.Default.Command='curl --verbose --http1.1 --proxy localhost:{}
http://www.example.com'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --http1.1 --proxy
localhost:{} http://www.example.com'.format(ts.Variables.port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
@@ -106,7 +108,7 @@ tr.StillRunningAfter=ts
# HTTP 1.0
tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --verbose --http1.0 --proxy localhost:{}
http://www.example.com'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --http1.0 --proxy
localhost:{} http://www.example.com'.format(ts.Variables.port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
@@ -114,7 +116,7 @@ tr.StillRunningAfter=ts
# HTTP 2
tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --verbose --insecure --header "Host:
www.example.com" https://localhost:{}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --insecure --header "Host:
www.example.com" https://localhost:{}'.format(ts.Variables.ssl_port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
@@ -122,7 +124,7 @@ tr.StillRunningAfter=ts
# TLS
tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --verbose --http1.1 --insecure --header
"Host: www.example.com" https://localhost:{}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --http1.1 --insecure
--header "Host: www.example.com"
https://localhost:{}'.format(ts.Variables.ssl_port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
diff --git a/tests/gold_tests/headers/via.gold
b/tests/gold_tests/headers/via.gold
index c2eadda..9f12e56 100644
--- a/tests/gold_tests/headers/via.gold
+++ b/tests/gold_tests/headers/via.gold
@@ -2,3 +2,5 @@ Via: http/1.1-tcp-ipv4
Via: http/1.0-tcp-ipv4
Via: http/1.1-h2-tls/1.2-tcp-ipv4
Via: http/1.1-tls/1.2-tcp-ipv4
+Via: http/1.1-tcp-ipv6
+Via: http/1.1-tls/1.2-tcp-ipv6
diff --git a/tests/gold_tests/headers/via.test.py
b/tests/gold_tests/headers/via.test.py
index db70d79..00eef1f 100644
--- a/tests/gold_tests/headers/via.test.py
+++ b/tests/gold_tests/headers/via.test.py
@@ -44,7 +44,8 @@ def RequireCurlFeature(tag):
return False
Test.SkipUnless(
- Condition.Condition(lambda : RequireCurlFeature('http2'), "Via test
requires a curl that supports HTTP/2")
+ Condition.Condition(lambda : RequireCurlFeature('http2'), "Via test
requires a curl that supports HTTP/2"),
+ Condition.Condition(lambda : RequireCurlFeature('IPv6'), "Via test
requires a curl that supports IPv6")
)
Test.ContinueOnFail=True
@@ -67,9 +68,10 @@ ts.Variables.ssl_port = 4443
ts.Disk.records_config.update({
'proxy.config.http.insert_request_via_str' : 1,
'proxy.config.http.insert_response_via_str' : 1,
+ 'proxy.config.http.request_via_transport' : 'full',
'proxy.config.ssl.server.cert.path': '{0}'.format(ts.Variables.SSLDir),
'proxy.config.ssl.server.private_key.path':
'{0}'.format(ts.Variables.SSLDir),
- 'proxy.config.http.server_ports': '{0}
{1}:proto=http2;http:ssl'.format(ts.Variables.port,ts.Variables.ssl_port),
+ 'proxy.config.http.server_ports': 'ipv4:{0}
ipv4:{1}:proto=http2;http:ssl ipv6:{0}
ipv6:{1}:proto=http2;http:ssl'.format(ts.Variables.port,ts.Variables.ssl_port),
})
ts.Disk.remap_config.AddLine(
@@ -98,7 +100,7 @@ tr.Processes.Default.StartBefore(server,
ready=CheckPort(server.Variables.Port))
# Delay on readiness of our ssl ports
tr.Processes.Default.StartBefore(Test.Processes.ts,
ready=CheckPort(ts.Variables.ssl_port))
-tr.Processes.Default.Command='curl --verbose --http1.1 --proxy localhost:{}
http://www.example.com'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --http1.1 --proxy
localhost:{} http://www.example.com'.format(ts.Variables.port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
@@ -106,7 +108,7 @@ tr.StillRunningAfter=ts
# HTTP 1.0
tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --verbose --http1.0 --proxy localhost:{}
http://www.example.com'.format(ts.Variables.port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --http1.0 --proxy
localhost:{} http://www.example.com'.format(ts.Variables.port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
@@ -114,7 +116,7 @@ tr.StillRunningAfter=ts
# HTTP 2
tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --verbose --insecure --header "Host:
www.example.com" https://localhost:{}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --insecure --header "Host:
www.example.com" https://localhost:{}'.format(ts.Variables.ssl_port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
@@ -122,7 +124,21 @@ tr.StillRunningAfter=ts
# TLS
tr=Test.AddTestRun()
-tr.Processes.Default.Command='curl --verbose --http1.1 --insecure --header
"Host: www.example.com" https://localhost:{}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.Command='curl --verbose --ipv4 --http1.1 --insecure
--header "Host: www.example.com"
https://localhost:{}'.format(ts.Variables.ssl_port)
+tr.Processes.Default.ReturnCode=0
+
+tr.StillRunningAfter=server
+tr.StillRunningAfter=ts
+
+# IPv6
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --verbose --ipv6 --http1.1 --proxy
localhost:{} http://www.example.com'.format(ts.Variables.port)
+tr.Processes.Default.ReturnCode=0
+tr.StillRunningAfter=server
+tr.StillRunningAfter=ts
+
+tr=Test.AddTestRun()
+tr.Processes.Default.Command='curl --verbose --ipv6 --http1.1 --insecure
--header "Host: www.example.com"
https://localhost:{}'.format(ts.Variables.ssl_port)
tr.Processes.Default.ReturnCode=0
tr.StillRunningAfter=server
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].