src/cripts/Bundles/HRWBridge.cc (271 lines of code) (raw):

/* 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 cripts::string_view &id); ~ID() override = default; cripts::string_view value(cripts::Context *context) override; private: Type _type = Type::none; }; ID::ID(const cripts::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 { CFatal("[Cripts::Headers] Unknown HRWBridge ID type: %s.", id.data()); } } cripts::string_view ID::value(cripts::Context *context) { switch (_type) { case Type::REQUEST: _value = cripts::UUID::Request::_get(context); break; case Type::PROCESS: _value = cripts::UUID::Process::_get(context); break; case Type::UNIQUE: _value = cripts::UUID::Unique::_get(context); break; default: _value = ""; break; } return _value; } ///////////////////////////////////////////////////////////////////////////// // Bridge for the IP 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 cripts::string_view &ip); ~IP() override = default; cripts::string_view value(cripts::Context *context) override; private: Type _type = Type::none; }; IP::IP(const cripts::string_view &type) : super_type(type) { if (type == "CLIENT") { _type = Type::CLIENT; } else if (type == "INBOUND") { _type = Type::INBOUND; } else if (type == "SERVER") { _type = Type::SERVER; } else if (type == "OUTBOUND") { _type = Type::INBOUND; } else { CFatal("[Cripts::Headers] Unknown HRWBridge IP type: %s.", type.data()); } } cripts::string_view IP::value(cripts::Context *context) { switch (_type) { case Type::CLIENT: { auto ip = cripts::Client::Connection::Get().IP(); _value = ip.string(); } break; case Type::INBOUND: { auto ip = cripts::Client::Connection::Get().LocalIP(); _value = ip.string(); } break; case Type::SERVER: { auto ip = cripts::Server::Connection::Get().IP(); _value = ip.string(); } break; case Type::OUTBOUND: { auto ip = cripts::Server::Connection::Get().LocalIP(); _value = ip.string(); } break; default: _value = ""; break; } return _value; } ///////////////////////////////////////////////////////////////////////////// // Bridge for the CIDR class, this only deals with the client IP class CIDR : public detail::HRWBridge { using self_type = CIDR; using super_type = detail::HRWBridge; public: CIDR(const self_type &) = delete; void operator=(const self_type &) = delete; CIDR(cripts::string_view &cidr); ~CIDR() override = default; cripts::string_view value(cripts::Context *context) override; private: unsigned int _ipv4_cidr = 32; unsigned int _ipv6_cidr = 128; }; CIDR::CIDR(cripts::string_view &cidr) : super_type(cidr) { auto ipv4 = cidr.split_prefix_at(','); CAssert(ipv4 != cidr); // No ' found auto result = std::from_chars(ipv4.data(), ipv4.data() + ipv4.size(), _ipv4_cidr); if (result.ec != std::errc()) { CFatal("[Cripts::Headers] Invalid IPv4 CIDR parameters: %s.", cidr.data()); } result = std::from_chars(cidr.data(), cidr.data() + cidr.size(), _ipv6_cidr); if (result.ec != std::errc()) { CFatal("[Cripts::Headers] Invalid IPv6 CIDR parameters: %s.", cidr.data()); } } cripts::string_view CIDR::value(cripts::Context *context) { auto ip = cripts::Client::Connection::Get().IP(); _value = ip.string(_ipv4_cidr, _ipv6_cidr); 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 cripts::string_view &) = delete; URL(Type utype, const cripts::string_view &comp); ~URL() override = default; cripts::string_view value(cripts::Context *context) override; private: cripts::string_view _getComponent(cripts::Url &url); Type _type = Type::none; Component _comp = Component::none; }; cripts::string_view URL::_getComponent(cripts::Url &url) { switch (_comp) { case Component::HOST: return url.host.GetSV(); break; case Component::PATH: return url.path; break; case Component::PORT: _value = cripts::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: CFatal("[Cripts::Headers] Invalid URL component in HRWBridge."); break; } return ""; // Should never happen } URL::URL(Type utype, const cripts::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 { CFatal("[Cripts::Headers] Invalid URL component in HRWBridge."); } } cripts::string_view URL::value(cripts::Context *context) { switch (_type) { case Type::CLIENT: { borrow url = cripts::Client::URL::Get(); return _getComponent(url); } break; case Type::REMAP_FROM: { borrow url = cripts::Remap::From::URL::Get(); return _getComponent(url); } break; case Type::REMAP_TO: { borrow url = cripts::Remap::To::URL::Get(); return _getComponent(url); } break; case Type::PRISTINE: { borrow url = cripts::Pristine::URL::Get(); return _getComponent(url); } break; case Type::CACHE: { borrow url = cripts::Cache::URL::Get(); return _getComponent(url); } break; case Type::PARENT: { borrow url = cripts::Parent::URL::Get(); return _getComponent(url); } break; default: CFatal("[Cripts::Headers] Invalid URL type in HRWBridge."); break; } return _value; } } // namespace detail detail::HRWBridge * cripts::Bundle::Headers::BridgeFactory(const cripts::string &source) { cripts::string_view str = source; str.trim_if([](char c) { return std::isspace(c) || c == '"' || c == '\''; }); 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); } else if (key == "CIDR") { return new detail::CIDR(str); } 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); }