This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 023d250a011a8205ad02c02fe090a9f93435e94a Author: Leif Hedstrom <[email protected]> AuthorDate: Wed Jun 5 10:52:47 2024 -0600 Adds a bridge betwen Cripts and HRW operators (#11417) * Adds a bridge betwen Cripts and HRW operators * Cleanup of Cripts URLs as needed for HRW Bridges * Move the connBase class into detail:: * Fix the validate() functionality to be sane (cherry picked from commit 7e1339d3069ecf1378ff9b06a057bd42f7f32d85) --- doc/developer-guide/cripts/cripts-bundles.en.rst | 31 ++ .../cripts/cripts-connections.en.rst | 22 +- doc/developer-guide/cripts/cripts-headers.en.rst | 4 +- doc/developer-guide/cripts/cripts-misc.en.rst | 29 +- doc/developer-guide/cripts/cripts-urls.en.rst | 18 +- include/cripts/Bundles/Headers.hpp | 121 ++++++++ include/cripts/Connections.hpp | 49 +++- include/cripts/Context.hpp | 4 + include/cripts/Epilogue.hpp | 2 +- include/cripts/Urls.hpp | 151 ++++++++-- src/cripts/Bundles/Common.cc | 6 +- src/cripts/Bundles/HRWBridge.cc | 320 +++++++++++++++++++++ src/cripts/Bundles/Headers.cc | 172 +++++++++++ src/cripts/Bundles/LogsMetrics.cc | 2 +- src/cripts/CMakeLists.txt | 5 +- src/cripts/Connections.cc | 4 +- src/cripts/Geo.cc | 16 +- src/cripts/Urls.cc | 132 ++++++--- 18 files changed, 981 insertions(+), 107 deletions(-) diff --git a/doc/developer-guide/cripts/cripts-bundles.en.rst b/doc/developer-guide/cripts/cripts-bundles.en.rst index d0230c22dc..ad77b03746 100644 --- a/doc/developer-guide/cripts/cripts-bundles.en.rst +++ b/doc/developer-guide/cripts/cripts-bundles.en.rst @@ -41,6 +41,7 @@ Bundle Description ============================ ==================================================================== ``Bundle::Common`` For DSCP and an overridable Cache-Control header. ``Bundle::LogsMetrics`` Log sampling, TCPInfo and per-remap metrics. +``Bundle::Headers`` For removing or adding headers ============================ ==================================================================== This example shows how a Cript would enable both of these bundles with all features: @@ -69,3 +70,33 @@ This example shows how a Cript would enable both of these bundles with all featu The bundles are not enabled by default. You have to explicitly activate them in your Cript, with the appropriate include directives. This is because the list of Bundles may grow over time, as well as the build system allowing for custom bundles locally. + + +.. _cripts-bundles-headers: + +Headers +======= + +Even though adding or removing headers in Cripts is very straight forward, we've added the ``Bundle::Headers`` +for not only convenience, but also for easier integration and migratino with existing configurations. There +are two main functions in this bundle: + +- ``rm_headers()``: Add a header to the request or response. +- ``add_headers()``: Remove a header from the request or response. + +The ``rm_headers()`` function takes a list of headers to remove, while the ``add_headers()`` function takes a list +of key-value pairs to add. The header values here are strings, but they can also be strings with the special +operators from the ``header_rewrite`` plugin. For example: + +.. code-block:: cpp + + #include <cripts/Preamble.hpp> + #include <cripts/Bundles/Headers.hpp> + + do_create_instance() + { + Bundle::Headers::activate().rm_headers({"X-Header1", "X-Header2"}) + .add_headers({{"X-Header3", "value3"}, + {"X-Header4", "%{FROM-URL:PATH}"}, + {"X-Header5", "%{ID:UNIQUE}"} }); + } diff --git a/doc/developer-guide/cripts/cripts-connections.en.rst b/doc/developer-guide/cripts/cripts-connections.en.rst index 272904fc7b..c10b9d4f34 100644 --- a/doc/developer-guide/cripts/cripts-connections.en.rst +++ b/doc/developer-guide/cripts/cripts-connections.en.rst @@ -52,7 +52,26 @@ Connection Object Description ======================= ========================================================================= As usual, the ``Server::Connection`` object is only available assuming that the request -is a forward proxy request. On cache misses, there is no such connection. +is a forward proxy request, and you borrow it with the ``get()`` method. On cache misses, +there is no such connection. + +.. _cripts-connections-methods: + +Connection Methods +================== + +The connection objects provides a set of methods, used to access some internals details of the +connections. These are: + +======================= ========================================================================= +Method Description +======================= ========================================================================= +``count()`` The number of transactions processed on the connection so far. +``ip()`` The IP address of the connection. +``localIP()`` The server (ATS) IP address of the connection. +``isInternal()`` Returns ``true`` or ``false`` if the connection is internal to ATS. +``socket()`` Returns the raw socket structure for the connection (use with care). +======================= ========================================================================= .. _cripts-connections-variables: @@ -64,7 +83,6 @@ Both connection objects provide a number of variables that can be accessed. Thes ======================= ========================================================================= Variable Description ======================= ========================================================================= -``count`` The number of transactions processed on the connection so far. ``tcpinfo`` A number of TCPinfo related fields (see below). ``geo`` If available (compile time) acess to Geo-IP data (see below). ``congestion`` Configure the congestion algorithm used on the socket. diff --git a/doc/developer-guide/cripts/cripts-headers.en.rst b/doc/developer-guide/cripts/cripts-headers.en.rst index 4e952eaf10..8f07f9f85e 100644 --- a/doc/developer-guide/cripts/cripts-headers.en.rst +++ b/doc/developer-guide/cripts/cripts-headers.en.rst @@ -68,6 +68,8 @@ A header can also be removed by using the ``erase`` method, which is a little mo req.erase("X-Foo"); +.. note:: There is also a Cripts Bundle for headers, see :ref:`Bundles <cripts-bundles-headers>`. + .. _cripts-headers-iterators: Iterators @@ -161,5 +163,3 @@ fresh or stale. Example usage of the cache status: // Do something } } - -.. diff --git a/doc/developer-guide/cripts/cripts-misc.en.rst b/doc/developer-guide/cripts/cripts-misc.en.rst index d259cec275..ad4702c5cf 100644 --- a/doc/developer-guide/cripts/cripts-misc.en.rst +++ b/doc/developer-guide/cripts/cripts-misc.en.rst @@ -36,7 +36,7 @@ Errors ====== Often it's useful to be able to abort a client transaction prematurely, and -return an error to the client. Cripts provides a few convenience functions for +return an error to the client. Cripts provid making this easy. .. note:: @@ -207,3 +207,30 @@ by the ``File::Line::Reader`` object. Some examples: string secret = File::Line::Reader(p2); CDebug("Read secret = {}", secret); } + +.. _cripts-misc-uuid: + +UUID +==== + +Cripts supports generating a few different UUID (Universally Unique Identifier), for +different purposes. The ``UUID`` object provides the following functions: + +========================= ======================================================================= +Function Description +========================= ======================================================================= +``UUID::Process`` Returns a UUID for the running process (changes on ATS startup). +``UUID::Unique`` Returns a completely unique UUID for the server and transacion. +``UUID::Request`` Returns a unique id for this request. +========================= ======================================================================= + +Using the ``UUID`` object is simple, via the ``get()`` method. Here's an example: + +.. code-block:: cpp + + do_remap() + { + static borrow req = Client::Request::get(); + + resp["X-UUID"] = UUID::Unique::get(); + } diff --git a/doc/developer-guide/cripts/cripts-urls.en.rst b/doc/developer-guide/cripts/cripts-urls.en.rst index 022d898c52..cdf11b4f0e 100644 --- a/doc/developer-guide/cripts/cripts-urls.en.rst +++ b/doc/developer-guide/cripts/cripts-urls.en.rst @@ -36,14 +36,16 @@ must always be borrewed. The pattern for this is as follows: There are four types of URLs that can be borrowed: -=================== ============================================================================= -URL Object Description -=================== ============================================================================= -``Client::URL`` The client request URL, as it currently stands (may be modified). -``Pristine:URL`` The pristine client request URL, as it was coming into the ATS server. -``Parent::URL`` The outgoing server URL, as sent to the next server (parent or origin). -``Cache::URL`` The cache URL, which will be used for the cache lookups. -=================== ============================================================================= +===================== ============================================================================= +URL Object Description +===================== ============================================================================= +``Client::URL`` The client request URL, as it currently stands (may be modified). +``Pristine:URL`` The pristine client request URL, as it was coming into the ATS server. +``Parent::URL`` The outgoing server URL, as sent to the next server (parent or origin). +``Cache::URL`` The cache URL, which will be used for the cache lookups. +``Remap::From::URL`` The remap ``from`` URL, from :file:`remap.config`. +``Remap::To::URL`` The remap ``to`` URL, from :file:`remap.config`. +===================== ============================================================================= These URLs all have the same methods and properties, but they are used in different hooks and have different meanings. The ``Client::URL`` is the most commonly used URL, diff --git a/include/cripts/Bundles/Headers.hpp b/include/cripts/Bundles/Headers.hpp new file mode 100644 index 0000000000..db65e42f1b --- /dev/null +++ b/include/cripts/Bundles/Headers.hpp @@ -0,0 +1,121 @@ +/* + 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. +*/ + +// Bundle::Headers::activate().rm_headers("Client::Request", ["X-ATS-Request-ID", "X-ATS-Request-Start", "X-ATS-Request-End"]) +// .set_headers("Client::Response", {{"X-Foo", "bar", {"X-Fie", "fum"}}); +#pragma once + +#include <vector> + +#include "cripts/Bundle.hpp" + +namespace detail +{ +class HRWBridge +{ + using self_type = HRWBridge; + +public: + HRWBridge(const self_type &) = delete; + void operator=(const self_type &) = delete; + + HRWBridge() = delete; + HRWBridge(const Cript::string_view &str) : _value(str) {} + + virtual ~HRWBridge() = default; + + virtual Cript::string_view + value(Cript::Context *context) + { + return _value; + } + +protected: + Cript::string _value; + +}; // class HRWBridge + +// We support 4 different type of header operations, so isolate the common code +class HeadersType +{ + using self_type = HeadersType; + +public: + HeadersType() = default; + HeadersType(const self_type &) = delete; + void operator=(const self_type &) = delete; + + ~HeadersType() + { + for (auto &header : set_headers) { + delete header.second; + } + } + + std::vector<Cript::string> rm_headers; + std::vector<std::pair<Cript::string, detail::HRWBridge *>> set_headers; +}; +} // namespace detail + +namespace Bundle +{ +class Headers : public Cript::Bundle::Base +{ + using super_type = Cript::Bundle::Base; + using self_type = Headers; + +public: + using super_type::Base; + + // This is the factory to create an instance of this bundle + static self_type & + _activate(Cript::Instance &inst) + { + auto *entry = new self_type(); + + inst.addBundle(entry); + + return *entry; + } + + const Cript::string & + name() const override + { + return _name; + } + + static detail::HRWBridge *bridgeFactory(const Cript::string &source); + + self_type &rm_headers(const Cript::string_view target, const std::vector<Cript::string> &headers); + self_type &set_headers(const Cript::string_view target, const std::vector<std::pair<Cript::string, Cript::string>> &headers); + + void doRemap(Cript::Context *context) override; + void doSendResponse(Cript::Context *context) override; + void doSendRequest(Cript::Context *context) override; + void doReadResponse(Cript::Context *context) override; + +private: + static const Cript::string _name; + + detail::HeadersType _client_request; + detail::HeadersType _client_response; + detail::HeadersType _server_request; + detail::HeadersType _server_response; +}; + +} // namespace Bundle diff --git a/include/cripts/Connections.hpp b/include/cripts/Connections.hpp index 46c6ae1be7..c3bb85d508 100644 --- a/include/cripts/Connections.hpp +++ b/include/cripts/Connections.hpp @@ -64,6 +64,9 @@ private: } // namespace Cript +namespace detail +{ + class ConnBase { using self_type = ConnBase; @@ -335,15 +338,17 @@ public: return TSHttpTxnIsInternal(_state->txnp); } - [[nodiscard]] virtual int count() const = 0; - virtual void setDscp(int val) = 0; - virtual void setMark(int val) = 0; - Dscp dscp; - Congestion congestion; - TcpInfo tcpinfo; - Geo geo; - Pacing pacing; - Mark mark; + [[nodiscard]] virtual Cript::IP localIP() const = 0; + [[nodiscard]] virtual int count() const = 0; + virtual void setDscp(int val) = 0; + virtual void setMark(int val) = 0; + + Dscp dscp; + Congestion congestion; + TcpInfo tcpinfo; + Geo geo; + Pacing pacing; + Mark mark; Cript::string_view string(unsigned ipv4_cidr = 32, unsigned ipv6_cidr = 128); @@ -355,12 +360,14 @@ protected: }; // End class ConnBase +} // namespace detail + namespace Client { -class Connection : public ConnBase +class Connection : public detail::ConnBase { - using super_type = ConnBase; - using self_type = Connection; + using pe = detail::ConnBase; + using self_type = Connection; public: Connection() = default; @@ -384,16 +391,22 @@ public: TSHttpTxnClientPacketMarkSet(_state->txnp, val); } + [[nodiscard]] Cript::IP + localIP() const override + { + return Cript::IP(TSHttpTxnIncomingAddrGet(_state->txnp)); + } + }; // End class Client::Connection } // namespace Client namespace Server { -class Connection : public ConnBase +class Connection : public detail::ConnBase { - using super_type = ConnBase; - using self_type = Connection; + using pe = detail::ConnBase; + using self_type = Connection; public: Connection() = default; @@ -416,6 +429,12 @@ public: TSHttpTxnServerPacketMarkSet(_state->txnp, val); } + [[nodiscard]] Cript::IP + localIP() const override + { + return Cript::IP(TSHttpTxnOutgoingAddrGet(_state->txnp)); + } + }; // End class Server::Connection } // namespace Server diff --git a/include/cripts/Context.hpp b/include/cripts/Context.hpp index b873101ab0..915569e5b1 100644 --- a/include/cripts/Context.hpp +++ b/include/cripts/Context.hpp @@ -121,6 +121,8 @@ private: friend class Client::Response; friend class Client::Connection; friend class Client::URL; + friend class Remap::From::URL; + friend class Remap::To::URL; friend class Server::Request; friend class Server::Response; friend class Server::Connection; @@ -135,6 +137,8 @@ private: Client::Request _client_req_header; Client::Connection _client_conn; Client::URL _client_url; + Remap::From::URL _remap_from_url; + Remap::To::URL _remap_to_url; Pristine::URL _pristine_url; Server::Response _server_resp_header; Server::Request _server_req_header; diff --git a/include/cripts/Epilogue.hpp b/include/cripts/Epilogue.hpp index 2f02c2d845..0a710a5b99 100644 --- a/include/cripts/Epilogue.hpp +++ b/include/cripts/Epilogue.hpp @@ -408,7 +408,7 @@ TSRemapNewInstance(int argc, char *argv[], void **ih, char * /* errbuf ATS_UNUSE for (auto &bundle : inst->bundles) { // Collect all the callbacks needed from all bundles, and validate them - if (!bundle->validate(errors)) { + if (bundle->validate(errors)) { inst->needCallback(bundle->callbacks()); } } diff --git a/include/cripts/Urls.hpp b/include/cripts/Urls.hpp index 1a16d624c3..fbd4dbd3af 100644 --- a/include/cripts/Urls.hpp +++ b/include/cripts/Urls.hpp @@ -17,11 +17,6 @@ */ #pragma once -namespace Cript -{ -class Context; -} - #include <cstring> #include <iostream> #include <sstream> @@ -33,6 +28,10 @@ class Context; #include "ts/remap.h" #include "ts/ts.h" +namespace Cript +{ +class Context; + class Url { class Component @@ -86,6 +85,24 @@ class Url // This is not ideal, but best way I can think of for now to mixin the Cript::string_view mixin class // Remember to add things here when added to the Lulu.hpp file for the mixin class... :/ + [[nodiscard]] constexpr Cript::string_view + substr(Cript::string_view::size_type pos = 0, Cript::string_view::size_type count = Cript::string_view::npos) const + { + return _data.substr(pos, count); + } + + void + remove_prefix(Cript::string_view::size_type n) + { + _data.remove_prefix(n); + } + + void + remove_suffix(Cript::string_view::size_type n) + { + _data.remove_suffix(n); + } + Cript::string_view & ltrim(char c) { @@ -140,6 +157,24 @@ class Url return _data.starts_with(prefix); } + [[nodiscard]] constexpr Cript::string_view::size_type + find(Cript::string_view const substr, Cript::string_view::size_type pos = 0) const + { + return _data.find(substr, pos); + } + + [[nodiscard]] constexpr Cript::string_view::size_type + rfind(Cript::string_view const substr, Cript::string_view::size_type pos = 0) const + { + return _data.rfind(substr, pos); + } + + [[nodiscard]] constexpr bool + contains(Cript::string_view const substr) const + { + return (_data.find(substr) != _data.npos); + } + protected: mutable Cript::string_view _data; Url *_owner = nullptr; @@ -542,12 +577,14 @@ protected: }; // End class Url +} // namespace Cript + // The Pristine URL is immutable, we should "delete" the operator= methods namespace Pristine { -class URL : public Url +class URL : public Cript::Url { - using super_type = Url; + using super_type = Cript::Url; using self_type = URL; public: @@ -564,9 +601,9 @@ public: namespace Client { -class URL : public Url +class URL : public Cript::Url { - using super_type = Url; + using super_type = Cript::Url; using self_type = URL; public: @@ -590,11 +627,71 @@ private: } // namespace Client +namespace Remap +{ +namespace From +{ + class URL : public Cript::Url + { + using super_type = Cript::Url; + using self_type = URL; + + public: + URL() = default; + URL(const URL &) = delete; + void operator=(const URL &) = delete; + + // We must not release the bufp etc. since it comes from the RRI structure + void + reset() override + { + } + + static URL &_get(Cript::Context *context); + bool _update(Cript::Context *context); + + private: + void _initialize(Cript::Context *context); + + }; // End class Client::URL + +} // namespace From + +namespace To +{ + class URL : public Cript::Url + { + using super_type = Cript::Url; + using self_type = URL; + + public: + URL() = default; + URL(const URL &) = delete; + void operator=(const URL &) = delete; + + // We must not release the bufp etc. since it comes from the RRI structure + void + reset() override + { + } + + static URL &_get(Cript::Context *context); + bool _update(Cript::Context *context); + + private: + void _initialize(Cript::Context *context); + + }; // End class Client::URL + +} // namespace To + +} // namespace Remap + namespace Cache { -class URL : public Url // ToDo: This can maybe be a subclass of Client::URL ? +class URL : public Cript::Url // ToDo: This can maybe be a subclass of Client::URL ? { - using super_type = Url; + using super_type = Cript::Url; using self_type = URL; public: @@ -621,9 +718,9 @@ private: namespace Parent { -class URL : public Url +class URL : public Cript::Url { - using super_type = Url; + using super_type = Cript::Url; using self_type = URL; public: @@ -651,7 +748,7 @@ private: // Formatters for {fmt} namespace fmt { -template <> struct formatter<Url::Scheme> { +template <> struct formatter<Cript::Url::Scheme> { constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { @@ -660,13 +757,13 @@ template <> struct formatter<Url::Scheme> { template <typename FormatContext> auto - format(Url::Scheme &scheme, FormatContext &ctx) -> decltype(ctx.out()) + format(Cript::Url::Scheme &scheme, FormatContext &ctx) -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", scheme.getSV()); } }; -template <> struct formatter<Url::Host> { +template <> struct formatter<Cript::Url::Host> { constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { @@ -675,13 +772,13 @@ template <> struct formatter<Url::Host> { template <typename FormatContext> auto - format(Url::Host &host, FormatContext &ctx) -> decltype(ctx.out()) + format(Cript::Url::Host &host, FormatContext &ctx) -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", host.getSV()); } }; -template <> struct formatter<Url::Port> { +template <> struct formatter<Cript::Url::Port> { constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { @@ -690,13 +787,13 @@ template <> struct formatter<Url::Port> { template <typename FormatContext> auto - format(Url::Port &port, FormatContext &ctx) -> decltype(ctx.out()) + format(Cript::Url::Port &port, FormatContext &ctx) -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", integer(port)); } }; -template <> struct formatter<Url::Path::String> { +template <> struct formatter<Cript::Url::Path::String> { constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { @@ -705,13 +802,13 @@ template <> struct formatter<Url::Path::String> { template <typename FormatContext> auto - format(Url::Path::String &path, FormatContext &ctx) -> decltype(ctx.out()) + format(Cript::Url::Path::String &path, FormatContext &ctx) -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", path.getSV()); } }; -template <> struct formatter<Url::Path> { +template <> struct formatter<Cript::Url::Path> { constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { @@ -720,13 +817,13 @@ template <> struct formatter<Url::Path> { template <typename FormatContext> auto - format(Url::Path &path, FormatContext &ctx) -> decltype(ctx.out()) + format(Cript::Url::Path &path, FormatContext &ctx) -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", path.getSV()); } }; -template <> struct formatter<Url::Query::Parameter> { +template <> struct formatter<Cript::Url::Query::Parameter> { constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { @@ -735,13 +832,13 @@ template <> struct formatter<Url::Query::Parameter> { template <typename FormatContext> auto - format(Url::Query::Parameter ¶m, FormatContext &ctx) -> decltype(ctx.out()) + format(Cript::Url::Query::Parameter ¶m, FormatContext &ctx) -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", param.getSV()); } }; -template <> struct formatter<Url::Query> { +template <> struct formatter<Cript::Url::Query> { constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { @@ -750,7 +847,7 @@ template <> struct formatter<Url::Query> { template <typename FormatContext> auto - format(Url::Query &query, FormatContext &ctx) -> decltype(ctx.out()) + format(Cript::Url::Query &query, FormatContext &ctx) -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", query.getSV()); } diff --git a/src/cripts/Bundles/Common.cc b/src/cripts/Bundles/Common.cc index 495efaf480..96eb8dfcbb 100644 --- a/src/cripts/Bundles/Common.cc +++ b/src/cripts/Bundles/Common.cc @@ -27,15 +27,15 @@ const Cript::string Common::_name = "Bundle::Common"; bool Common::validate(std::vector<Cript::Bundle::Error> &errors) const { - bool failed = false; + bool good = true; // The .dscp() can only be 0 - 63 if (_dscp < 0 || _dscp > 63) { errors.emplace_back("dscp must be between 0 and 63", name(), "dscp"); - failed = true; + good = false; } - return failed; + return good; } void diff --git a/src/cripts/Bundles/HRWBridge.cc b/src/cripts/Bundles/HRWBridge.cc new file mode 100644 index 0000000000..0f0f3ccd4b --- /dev/null +++ b/src/cripts/Bundles/HRWBridge.cc @@ -0,0 +1,320 @@ +/* + 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 "cripts/Preamble.hpp" +#include "cripts/Bundles/Headers.hpp" + +#include <cctype> + +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////// +// Bridge for the ID class +class ID : public detail::HRWBridge +{ + using self_type = ID; + using super_type = detail::HRWBridge; + + enum class Type : uint8_t { none, REQUEST, PROCESS, UNIQUE }; + +public: + ID(const self_type &) = delete; + void operator=(const self_type &) = delete; + + ID(const Cript::string_view &id); + ~ID() override = default; + + Cript::string_view value(Cript::Context *context) override; + +private: + Type _type = Type::none; +}; + +ID::ID(const Cript::string_view &id) : super_type(id) +{ + if (id == "REQUEST") { + _type = Type::REQUEST; + } else if (id == "PROCESS") { + _type = Type::PROCESS; + } else if (id == "UNIQUE") { + _type = Type::UNIQUE; + } else { + TSReleaseAssert(!"Invalid ID type in HRWBridge"); + } +} + +Cript::string_view +ID::value(Cript::Context *context) +{ + switch (_type) { + case Type::REQUEST: + _value = UUID::Request::_get(context); + break; + case Type::PROCESS: + _value = UUID::Process::_get(context); + break; + case Type::UNIQUE: + _value = UUID::Unique::_get(context); + break; + default: + _value = ""; + break; + } + + return _value; +} + +///////////////////////////////////////////////////////////////////////////// +// Bridge for the ID class +class IP : public detail::HRWBridge +{ + using self_type = IP; + using super_type = detail::HRWBridge; + + enum class Type : uint8_t { none, CLIENT, INBOUND, SERVER, OUTBOUND }; + +public: + IP(const self_type &) = delete; + void operator=(const self_type &) = delete; + + IP(const Cript::string_view &iP); + ~IP() override = default; + + Cript::string_view value(Cript::Context *context) override; + +private: + Type _type = Type::none; +}; + +IP::IP(const Cript::string_view &ip) : super_type(ip) +{ + if (ip == "CLIENT") { + _type = Type::CLIENT; + } else if (ip == "INBOUND") { + _type = Type::INBOUND; + } else if (ip == "SERVER") { + _type = Type::SERVER; + } else if (ip == "OUTBOUND") { + _type = Type::INBOUND; + } else { + TSReleaseAssert(!"Invalid IP type in HRWBridge"); + } +} + +Cript::string_view +IP::value(Cript::Context *context) +{ + switch (_type) { + case Type::CLIENT: { + auto ip = Client::Connection::get().ip(); + _value = ip.string(); + } break; + case Type::INBOUND: { + auto ip = Client::Connection::get().localIP(); + _value = ip.string(); + } break; + case Type::SERVER: { + auto ip = Server::Connection::get().ip(); + _value = ip.string(); + } break; + case Type::OUTBOUND: { + auto ip = Server::Connection::get().localIP(); + _value = ip.string(); + } break; + default: + _value = ""; + break; + } + + return _value; +} + +///////////////////////////////////////////////////////////////////////////// +// Bridge for all URLs +class URL : public detail::HRWBridge +{ + using self_type = URL; + using super_type = detail::HRWBridge; + + enum class Component : uint8_t { none, HOST, PATH, PORT, QUERY, SCHEME, URL }; + +public: + enum class Type : uint8_t { none, CLIENT, REMAP_FROM, REMAP_TO, PRISTINE, CACHE, PARENT }; + + URL(const self_type &) = delete; + URL operator=(const self_type &) = delete; + + URL(const Cript::string_view &) = delete; + URL(Type utype, const Cript::string_view &comp); + ~URL() override = default; + + Cript::string_view value(Cript::Context *context) override; + +private: + Cript::string_view _getComponent(Cript::Url &url); + + Type _type = Type::none; + Component _comp = Component::none; +}; + +Cript::string_view +URL::_getComponent(Cript::Url &url) +{ + switch (_comp) { + case Component::HOST: + return url.host.getSV(); + break; + + case Component::PATH: + return url.path; + break; + + case Component::PORT: + _value = Cript::string(std::to_string(url.port)); + break; + + case Component::QUERY: + return url.query; + break; + + case Component::SCHEME: + return url.scheme; + break; + + case Component::URL: + return ""; + // return url.url; + break; + + default: + TSReleaseAssert(!"Invalid URL component in HRWBridge"); + break; + } + + return ""; // Should never happen +} + +URL::URL(Type utype, const Cript::string_view &comp) : super_type("") +{ + _type = utype; + + if (comp == "HOST") { + _comp = Component::HOST; + } else if (comp == "PATH") { + _comp = Component::PATH; + } else if (comp == "PORT") { + _comp = Component::PORT; + } else if (comp == "QUERY") { + _comp = Component::QUERY; + } else if (comp == "SCHEME") { + _comp = Component::SCHEME; + } else if (comp == "URL") { + _comp = Component::URL; + } else { + TSReleaseAssert(!"Invalid URL component in HRWBridge"); + } +} + +Cript::string_view +URL::value(Cript::Context *context) +{ + switch (_type) { + case Type::CLIENT: { + borrow url = Client::URL::get(); + + return _getComponent(url); + } break; + + case Type::REMAP_FROM: { + borrow url = Remap::From::URL::get(); + + return _getComponent(url); + } break; + + case Type::REMAP_TO: { + borrow url = Remap::To::URL::get(); + + return _getComponent(url); + } break; + + case Type::PRISTINE: { + borrow url = Pristine::URL::get(); + + return _getComponent(url); + } break; + + case Type::CACHE: { + borrow url = Cache::URL::get(); + + return _getComponent(url); + } break; + + case Type::PARENT: { + borrow url = Parent::URL::get(); + + return _getComponent(url); + } break; + + default: + TSReleaseAssert(!"Invalid URL type in HRWBridge"); + break; + } + + return _value; +} + +} // namespace detail + +detail::HRWBridge * +Bundle::Headers::bridgeFactory(const Cript::string &source) +{ + Cript::string_view str = source; + + str.trim_if(&isspace); + + if (str.starts_with("%{") && str.ends_with("}")) { + str.remove_prefix_at('{'); + str.remove_suffix_at('}'); + + auto key = str.take_prefix_at(':'); + + if (key == "ID") { + return new detail::ID(str); + } else if (key == "IP") { + return new detail::IP(str); + // ToDo: We need CIDR:x,y support here, on the IP:CLIENT + } else if (key == "FROM-URL") { + return new detail::URL(detail::URL::Type::REMAP_FROM, str); + } else if (key == "TO-URL") { + return new detail::URL(detail::URL::Type::REMAP_TO, str); + } else if (key == "CLIENT-URL") { + return new detail::URL(detail::URL::Type::CLIENT, str); + } else if (key == "CACHE-URL") { + return new detail::URL(detail::URL::Type::CACHE, str); + } else if (key == "PRISTINE-URL") { + return new detail::URL(detail::URL::Type::PRISTINE, str); + } else if (key == "NEXT-HOP") { + return new detail::URL(detail::URL::Type::PARENT, str); + // ToDo: Need proper support for URL: type here, which is a bit more complex (context sensitive) + } else { + TSError("[Cripts::Headers] Unknown HRWBridge key: %s.", key.data()); + } + } + // Always return the "raw" string if we don't have something special to do + return new detail::HRWBridge(source); +} diff --git a/src/cripts/Bundles/Headers.cc b/src/cripts/Bundles/Headers.cc new file mode 100644 index 0000000000..595f0e5128 --- /dev/null +++ b/src/cripts/Bundles/Headers.cc @@ -0,0 +1,172 @@ +/* + 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 "cripts/Lulu.hpp" +#include "cripts/Preamble.hpp" +#include "cripts/Bundles/Headers.hpp" + +namespace +{ +enum HeaderTargets : uint8_t { + NONE, + CLIENT_REQUEST, + CLIENT_RESPONSE, + SERVER_REQUEST, + SERVER_RESPONSE, +}; + +HeaderTargets +header_target(const Cript::string_view &target) +{ + if (target == "Client::Request") { + return CLIENT_REQUEST; + } else if (target == "Client::Response") { + return CLIENT_RESPONSE; + } else if (target == "Server::Request") { + return SERVER_REQUEST; + } else if (target == "Server::Response") { + return SERVER_RESPONSE; + } + + return NONE; +} + +} // namespace + +namespace Bundle +{ +const Cript::string Headers::_name = "Bundle::Headers"; + +Headers::self_type & +Headers::rm_headers(const Cript::string_view target, const std::vector<Cript::string> &headers) +{ + switch (header_target(target)) { + case CLIENT_REQUEST: + _client_request.rm_headers = headers; + needCallback(Cript::Callbacks::DO_REMAP); + break; + case CLIENT_RESPONSE: + _client_response.rm_headers = headers; + needCallback(Cript::Callbacks::DO_SEND_RESPONSE); + break; + case SERVER_REQUEST: + _server_request.rm_headers = headers; + needCallback(Cript::Callbacks::DO_SEND_REQUEST); + break; + case SERVER_RESPONSE: + _server_response.rm_headers = headers; + needCallback(Cript::Callbacks::DO_READ_RESPONSE); + break; + default: + TSReleaseAssert(!"Invalid target for rm_headers()"); + } + + return *this; +} + +Headers::self_type & +Headers::set_headers(const Cript::string_view target, const std::vector<std::pair<Cript::string, Cript::string>> &headers) +{ + std::vector<std::pair<Cript::string, detail::HRWBridge *>> hdrs; + + hdrs.reserve(headers.size()); + for (const auto &hdr : headers) { + hdrs.emplace_back(hdr.first, Headers::bridgeFactory(hdr.second)); + } + + switch (header_target(target)) { + case CLIENT_REQUEST: + _client_request.set_headers = hdrs; + needCallback(Cript::Callbacks::DO_REMAP); + break; + case CLIENT_RESPONSE: + _client_response.set_headers = hdrs; + needCallback(Cript::Callbacks::DO_SEND_RESPONSE); + break; + case SERVER_REQUEST: + _server_request.set_headers = hdrs; + needCallback(Cript::Callbacks::DO_SEND_REQUEST); + break; + case SERVER_RESPONSE: + _server_response.set_headers = hdrs; + needCallback(Cript::Callbacks::DO_READ_RESPONSE); + break; + default: + TSReleaseAssert(!"Invalid target for set_headers()"); + } + + return *this; +} + +void +Headers::doRemap(Cript::Context *context) +{ + borrow req = Client::Request::get(); + + for (auto &header : _client_request.rm_headers) { + req[header] = ""; + } + + for (auto &header : _client_request.set_headers) { + req[header.first] = header.second->value(context); + } +} + +void +Headers::doSendResponse(Cript::Context *context) +{ + borrow resp = Client::Response::get(); + + for (auto &header : _client_response.rm_headers) { + resp[header] = ""; + } + + for (auto &header : _client_response.set_headers) { + resp[header.first] = header.second->value(context); + } +} + +void +Headers::doSendRequest(Cript::Context *context) +{ + borrow req = Server::Request::get(); + + for (auto &header : _server_request.rm_headers) { + req[header] = ""; + } + + for (auto &header : _server_request.set_headers) { + req[header.first] = header.second->value(context); + } +} + +void +Headers::doReadResponse(Cript::Context *context) +{ + borrow resp = Server::Response::get(); + + for (auto &header : _server_response.rm_headers) { + resp[header] = ""; + } + + for (auto &header : _server_response.set_headers) { + resp[header.first] = header.second->value(context); + } +} + +} // namespace Bundle diff --git a/src/cripts/Bundles/LogsMetrics.cc b/src/cripts/Bundles/LogsMetrics.cc index 1a40319312..4e8c8af130 100644 --- a/src/cripts/Bundles/LogsMetrics.cc +++ b/src/cripts/Bundles/LogsMetrics.cc @@ -26,7 +26,7 @@ const Cript::string LogsMetrics::_name = "Bundle::LogsMetrics"; namespace { - enum { + enum : uint8_t { PROPSTAT_CACHE_MISS = 0, // TS_CACHE_LOOKUP_MISS == 0 PROPSTAT_CACHE_HIT_STALE, // TS_CACHE_LOOKUP_HIT_STALE == 1 PROPSTAT_CACHE_HIT_FRESH, // TS_CACHE_LOOKUP_HIT_FRESH == 2 diff --git a/src/cripts/CMakeLists.txt b/src/cripts/CMakeLists.txt index 06e8bb1d84..034b1c1559 100644 --- a/src/cripts/CMakeLists.txt +++ b/src/cripts/CMakeLists.txt @@ -47,8 +47,9 @@ set(CRIPTS_PUBLIC_HEADERS ${PROJECT_SOURCE_DIR}/include/cripts/Configs.hpp ) -set(CRIPTS_BUNDLE_HEADERS ${PROJECT_SOURCE_DIR}/include/cripts/Bundles/Common.hpp - ${PROJECT_SOURCE_DIR}/include/cripts/Bundles/LogsMetrics.hpp +set(CRIPTS_BUNDLE_HEADERS + ${PROJECT_SOURCE_DIR}/include/cripts/Bundles/Common.hpp ${PROJECT_SOURCE_DIR}/include/cripts/Bundles/Headers.hpp + ${PROJECT_SOURCE_DIR}/include/cripts/Bundles/LogsMetrics.hpp ) add_library(cripts SHARED ${CPP_FILES}) diff --git a/src/cripts/Connections.cc b/src/cripts/Connections.cc index bf9a72e8f6..8b7bbd735a 100644 --- a/src/cripts/Connections.cc +++ b/src/cripts/Connections.cc @@ -139,7 +139,7 @@ Cript::IP::sample(double rate, uint32_t seed, unsigned ipv4_cidr, unsigned ipv6_ } void -ConnBase::TcpInfo::initialize() +detail::ConnBase::TcpInfo::initialize() { #if defined(TCP_INFO) && defined(HAVE_STRUCT_TCP_INFO) if (!_ready) { @@ -159,7 +159,7 @@ ConnBase::TcpInfo::initialize() } Cript::string_view -ConnBase::TcpInfo::log() +detail::ConnBase::TcpInfo::log() { initialize(); // We intentionally do not use the old tcpinfo that may be stored, since we may diff --git a/src/cripts/Geo.cc b/src/cripts/Geo.cc index bf4256797a..706699a73d 100644 --- a/src/cripts/Geo.cc +++ b/src/cripts/Geo.cc @@ -89,13 +89,13 @@ get_geo_string(const sockaddr *addr, Qualifiers q) } Cript::string -ConnBase::Geo::ASN() const +detail::ConnBase::Geo::ASN() const { return get_geo_string(this->_owner->socket(), GEO_QUAL_ASN); } Cript::string -ConnBase::Geo::ASNName() const +detail::ConnBase::Geo::ASNName() const { Cript::string ret; ret = get_geo_string(this->_owner->socket(), GEO_QUAL_ASN_NAME); @@ -104,7 +104,7 @@ ConnBase::Geo::ASNName() const } Cript::string -ConnBase::Geo::Country() const +detail::ConnBase::Geo::Country() const { Cript::string ret; ret = get_geo_string(this->_owner->socket(), GEO_QUAL_COUNTRY); @@ -113,7 +113,7 @@ ConnBase::Geo::Country() const } Cript::string -ConnBase::Geo::CountryCode() const +detail::ConnBase::Geo::CountryCode() const { Cript::string ret; ret = get_geo_string(this->_owner->socket(), GEO_QUAL_COUNTRY_ISO); @@ -124,25 +124,25 @@ ConnBase::Geo::CountryCode() const #else Cript::string -ConnBase::Geo::ASN() const +detail::ConnBase::Geo::ASN() const { return "(unavailable)"; } Cript::string -ConnBase::Geo::ASNName() const +detail::ConnBase::Geo::ASNName() const { return "(unavailable)"; } Cript::string -ConnBase::Geo::Country() const +detail::ConnBase::Geo::Country() const { return "(unavailable)"; } Cript::string -ConnBase::Geo::CountryCode() const +detail::ConnBase::Geo::CountryCode() const { return "(unavailable)"; } diff --git a/src/cripts/Urls.cc b/src/cripts/Urls.cc index ebfca52c74..61066a6b30 100644 --- a/src/cripts/Urls.cc +++ b/src/cripts/Urls.cc @@ -20,13 +20,13 @@ #include "cripts/Preamble.hpp" std::vector<Cript::string_view> -Url::Component::split(const char delim) +Cript::Url::Component::split(const char delim) { return Cript::splitter(getSV(), delim); } Cript::string_view -Url::Scheme::getSV() +Cript::Url::Scheme::getSV() { if (_owner && _data.empty()) { const char *value = nullptr; @@ -39,8 +39,8 @@ Url::Scheme::getSV() return _data; } -Url::Scheme -Url::Scheme::operator=(Cript::string_view scheme) +Cript::Url::Scheme +Cript::Url::Scheme::operator=(Cript::string_view scheme) { TSUrlSchemeSet(_owner->_bufp, _owner->_urlp, scheme.data(), scheme.size()); _owner->_modified = true; @@ -51,7 +51,7 @@ Url::Scheme::operator=(Cript::string_view scheme) } Cript::string_view -Url::Host::getSV() +Cript::Url::Host::getSV() { if (_owner && _data.empty()) { const char *value = nullptr; @@ -65,8 +65,8 @@ Url::Host::getSV() return _data; } -Url::Host -Url::Host::operator=(Cript::string_view host) +Cript::Url::Host +Cript::Url::Host::operator=(Cript::string_view host) { TSUrlHostSet(_owner->_bufp, _owner->_urlp, host.data(), host.size()); _owner->_modified = true; @@ -76,7 +76,7 @@ Url::Host::operator=(Cript::string_view host) return *this; } -Url::Port::operator integer() // This should not be explicit +Cript::Url::Port::operator integer() // This should not be explicit { if (_owner && _port < 0) { _port = TSUrlPortGet(_owner->_bufp, _owner->_urlp); @@ -85,8 +85,8 @@ Url::Port::operator integer() // This should not be explicit return _port; } -Url::Port -Url::Port::operator=(int port) +Cript::Url::Port +Cript::Url::Port::operator=(int port) { TSUrlPortSet(_owner->_bufp, _owner->_urlp, port); _owner->_modified = true; @@ -96,7 +96,7 @@ Url::Port::operator=(int port) } Cript::string_view -Url::Path::getSV() +Cript::Url::Path::getSV() { if (_segments.size() > 0) { std::ostringstream path; @@ -122,8 +122,8 @@ Url::Path::getSV() return _data; } -Url::Path::String -Url::Path::operator[](Segments::size_type ix) +Cript::Url::Path::String +Cript::Url::Path::operator[](Segments::size_type ix) { Url::Path::String ret; @@ -135,8 +135,8 @@ Url::Path::operator[](Segments::size_type ix) return ret; // RVO } -Url::Path -Url::Path::operator=(Cript::string_view path) +Cript::Url::Path +Cript::Url::Path::operator=(Cript::string_view path) { TSUrlPathSet(_owner->_bufp, _owner->_urlp, path.data(), path.size()); _owner->_modified = true; @@ -147,7 +147,7 @@ Url::Path::operator=(Cript::string_view path) } Cript::string -Url::Path::operator+=(Cript::string_view add) +Cript::Url::Path::operator+=(Cript::string_view add) { Cript::string str; @@ -159,8 +159,9 @@ Url::Path::operator+=(Cript::string_view add) return str; // RVO } -Url::Path::String & -Url::Path::String::operator=(const Cript::string_view str) + +Cript::Url::Path::String & +Cript::Url::Path::String::operator=(const Cript::string_view str) { _owner->_size -= _owner->_segments[_ix].size(); _owner->_segments[_ix] = str; @@ -171,7 +172,7 @@ Url::Path::String::operator=(const Cript::string_view str) } void -Url::Path::reset() +Cript::Url::Path::reset() { Component::reset(); @@ -182,7 +183,7 @@ Url::Path::reset() } void -Url::Path::push(Cript::string_view val) +Cript::Url::Path::push(Cript::string_view val) { _parser(); _modified = true; @@ -190,7 +191,7 @@ Url::Path::push(Cript::string_view val) } void -Url::Path::insert(Segments::size_type ix, Cript::string_view val) +Cript::Url::Path::insert(Segments::size_type ix, Cript::string_view val) { _parser(); _modified = true; @@ -198,15 +199,15 @@ Url::Path::insert(Segments::size_type ix, Cript::string_view val) } void -Url::Path::_parser() +Cript::Url::Path::_parser() { if (_segments.size() == 0) { _segments = split('/'); } } -Url::Query::Parameter & -Url::Query::Parameter::operator=(const Cript::string_view str) +Cript::Url::Query::Parameter & +Cript::Url::Query::Parameter::operator=(const Cript::string_view str) { auto iter = _owner->_hashed.find(_name); @@ -222,7 +223,7 @@ Url::Query::Parameter::operator=(const Cript::string_view str) } Cript::string_view -Url::Query::getSV() +Cript::Url::Query::getSV() { if (_ordered.size() > 0) { _storage.clear(); @@ -264,8 +265,8 @@ Url::Query::getSV() return _data; } -Url::Query -Url::Query::operator=(Cript::string_view query) +Cript::Url::Query +Cript::Url::Query::operator=(Cript::string_view query) { TSUrlHttpQuerySet(_owner->_bufp, _owner->_urlp, query.data(), query.size()); _owner->_modified = true; @@ -276,7 +277,7 @@ Url::Query::operator=(Cript::string_view query) } Cript::string -Url::Query::operator+=(Cript::string_view add) +Cript::Url::Query::operator+=(Cript::string_view add) { Cript::string str; @@ -289,8 +290,8 @@ Url::Query::operator+=(Cript::string_view add) return str; // RVO } -Url::Query::Parameter -Url::Query::operator[](Cript::string_view param) +Cript::Url::Query::Parameter +Cript::Url::Query::operator[](Cript::string_view param) { // Make sure the hash and vector are populated _parser(); @@ -308,7 +309,7 @@ Url::Query::operator[](Cript::string_view param) } void -Url::Query::erase(Cript::string_view param) +Cript::Url::Query::erase(Cript::string_view param) { // Make sure the hash and vector are populated _parser(); @@ -332,7 +333,7 @@ Url::Query::erase(Cript::string_view param) } void -Url::Query::erase(std::initializer_list<Cript::string_view> list, bool keep) +Cript::Url::Query::erase(std::initializer_list<Cript::string_view> list, bool keep) { if (keep) { // Make sure the hash and vector are populated @@ -363,7 +364,7 @@ Url::Query::erase(std::initializer_list<Cript::string_view> list, bool keep) } void -Url::Query::reset() +Cript::Url::Query::reset() { Component::reset(); @@ -375,7 +376,7 @@ Url::Query::reset() } void -Url::Query::_parser() +Cript::Url::Query::_parser() { if (_ordered.size() == 0) { for (const auto sv : split('&')) { @@ -394,7 +395,7 @@ Url::Query::_parser() } Cript::string -Url::url() const +Cript::Url::url() const { Cript::string ret; @@ -457,6 +458,46 @@ Client::URL::_update(Cript::Context *context) return _modified; } +void +Remap::From::URL::_initialize(Cript::Context *context) +{ + Url::_initialize(&context->state); + + _bufp = context->rri->requestBufp; + _hdr_loc = context->rri->requestHdrp; + _urlp = context->rri->mapFromUrl; +} + +Remap::From::URL & +Remap::From::URL::_get(Cript::Context *context) +{ + if (!context->_remap_from_url.initialized()) { + context->_remap_from_url._initialize(context); + } + + return context->_remap_from_url; +} + +void +Remap::To::URL::_initialize(Cript::Context *context) +{ + Url::_initialize(&context->state); + + _bufp = context->rri->requestBufp; + _hdr_loc = context->rri->requestHdrp; + _urlp = context->rri->mapToUrl; +} + +Remap::To::URL & +Remap::To::URL::_get(Cript::Context *context) +{ + if (!context->_remap_to_url.initialized()) { + context->_remap_to_url._initialize(context); + } + + return context->_remap_to_url; +} + Cache::URL & Cache::URL::_get(Cript::Context *context) { @@ -521,3 +562,24 @@ Cache::URL::_update(Cript::Context *context) return false; } + +// ToDo: This may need more work, to understand which hooks the parent URL is actually available +Parent::URL & +Parent::URL::_get(Cript::Context *context) +{ + if (!context->_cache_url.initialized()) { + Parent::URL *url = &context->_parent_url; + Client::Request &req = Client::Request::_get(context); // Repurpose / create the shared request object + + if (TSUrlCreate(req.bufp(), &url->_urlp) == TS_SUCCESS) { + TSAssert(context->state.txnp); + if (TSHttpTxnParentSelectionUrlGet(context->state.txnp, req.bufp(), url->_urlp) == TS_SUCCESS) { + url->_initialize(&context->state, &req); + } + } else { + context->state.error.fail(); + } + } + + return context->_parent_url; +}
