| /** |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| Copyright 2019, Oath Inc. |
| */ |
| |
| #include <swoc/TextView.h> |
| #include <swoc/Errata.h> |
| #include <swoc/BufferWriter.h> |
| #include <swoc/bwf_base.h> |
| |
| #include "txn_box/common.h" |
| #include "txn_box/Config.h" |
| #include "txn_box/Context.h" |
| #include "txn_box/Directive.h" |
| #include "txn_box/Comparison.h" |
| |
| #include "txn_box/yaml_util.h" |
| #include "txn_box/ts_util.h" |
| |
| using swoc::BufferWriter; |
| using swoc::Errata; |
| using swoc::Rv; |
| using swoc::TextView; |
| namespace bwf = swoc::bwf; |
| using namespace swoc::literals; |
| |
| /* ------------------------------------------------------------------------------------ */ |
| Feature |
| Generic::extract() const |
| { |
| return NIL_FEATURE; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| class Do_ua_req_url_host : public Directive |
| { |
| using super_type = Directive; |
| using self_type = Do_ua_req_url_host; |
| |
| public: |
| static const std::string KEY; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_ua_req_url_host(Expr &&expr); |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Feature expression. |
| }; |
| |
| const std::string Do_ua_req_url_host::KEY{"ua-req-url-host"}; |
| const HookMask Do_ua_req_url_host::HOOKS = MaskFor(Hook::PREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP); |
| |
| Do_ua_req_url_host::Do_ua_req_url_host(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_url_host::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| if (auto url{hdr.url()}; url.is_valid()) { |
| auto value = ctx.extract(_expr); |
| if (auto host = std::get_if<IndexFor(STRING)>(&value); nullptr != host) { |
| url.host_set(*host); |
| } |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_url_host::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), STRING); |
| } |
| return Handle(new self_type{std::move(expr)}); |
| } |
| |
| // --- |
| |
| class Do_proxy_req_url_host : public Directive |
| { |
| using super_type = Directive; |
| using self_type = Do_proxy_req_url_host; |
| |
| public: |
| static const std::string KEY; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_proxy_req_url_host(Expr &&expr); |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature expression. |
| }; |
| |
| const std::string Do_proxy_req_url_host::KEY{"proxy-req-url-host"}; |
| const HookMask Do_proxy_req_url_host::HOOKS{MaskFor({Hook::PREQ})}; |
| |
| Do_proxy_req_url_host::Do_proxy_req_url_host(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_proxy_req_url_host::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| if (auto url{hdr.url()}; url.is_valid()) { |
| auto value = ctx.extract(_expr); |
| if (auto host = std::get_if<IndexFor(STRING)>(&value); nullptr != host) { |
| url.host_set(*host); |
| } |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_url_host::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, |
| swoc::TextView const &, YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), STRING); |
| } |
| return Handle(new self_type{std::move(expr)}); |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| |
| class Do_ua_req_url_port : public Directive |
| { |
| using super_type = Directive; |
| using self_type = Do_ua_req_url_port; |
| |
| public: |
| static const std::string KEY; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_ua_req_url_port(Expr &&expr); |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Feature expression. |
| }; |
| |
| const std::string Do_ua_req_url_port::KEY{"ua-req-url-port"}; |
| const HookMask Do_ua_req_url_port::HOOKS{MaskFor({Hook::CREQ, Hook::PREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_url_port::Do_ua_req_url_port(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_url_port::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| if (auto url{hdr.url()}; url.is_valid()) { |
| auto port = ctx.extract(_expr).as_integer(-1); |
| if (0 < port && port < std::numeric_limits<in_port_t>::max()) { |
| url.port_set(port); |
| } |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_url_port::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(INTEGER)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), INTEGER); |
| } |
| return Handle(new self_type{std::move(expr)}); |
| } |
| |
| // --- |
| |
| class Do_proxy_req_url_port : public Directive |
| { |
| using super_type = Directive; |
| using self_type = Do_proxy_req_url_port; |
| |
| public: |
| static const std::string KEY; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_proxy_req_url_port(Expr &&expr); |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Feature expression. |
| }; |
| |
| const std::string Do_proxy_req_url_port::KEY{"proxy-req-url-port"}; |
| const HookMask Do_proxy_req_url_port::HOOKS{MaskFor(Hook::PREQ)}; |
| |
| Do_proxy_req_url_port::Do_proxy_req_url_port(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_proxy_req_url_port::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| if (auto url{hdr.url()}; url.is_valid()) { |
| auto port = ctx.extract(_expr).as_integer(-1); |
| if (0 < port && port < std::numeric_limits<in_port_t>::max()) { |
| url.port_set(port); |
| } |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_url_port::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, |
| swoc::TextView const &, YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(INTEGER)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), INTEGER); |
| } |
| return Handle(new self_type{std::move(expr)}); |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| namespace |
| { |
| // @return @c true if @a loc is syntactically a valid location and break out the pieces. |
| bool |
| Loc_String_Parse(TextView const &loc, TextView &host_token, int &port) |
| { |
| TextView port_token, rest; |
| if (swoc::IPEndpoint::tokenize(loc, &host_token, &port_token, &rest) && rest.size() == 0) { |
| if (port_token.empty()) { |
| port = 0; |
| return true; |
| } |
| |
| if (auto n = svtou(port_token, &rest); |
| rest.size() == port_token.size() && 0 < n && n <= std::numeric_limits<in_port_t>::max()) { |
| port = n; |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /// Set the location in a URL, accepting either a string or a tuple of <host, port>. |
| void |
| URL_Loc_Set(Context &ctx, Expr &expr, ts::URL &url) |
| { |
| auto value = ctx.extract(expr); |
| TextView host_token; |
| int port = -1; // if still -1 after parsing, the parsing failed. |
| if (auto loc = std::get_if<IndexFor(STRING)>(&value); nullptr != loc) { |
| // split the string to get the pieces. |
| Loc_String_Parse(*loc, host_token, port); |
| } else if (auto t = std::get_if<IndexFor(TUPLE)>(&value); nullptr != t) { |
| // Must be host name, then port. |
| if (t->size() > 0) { |
| if (auto &f0 = (*t)[0]; f0.index() == IndexFor(STRING)) { |
| host_token = std::get<IndexFor(STRING)>(f0); |
| if (t->size() > 1) { |
| auto &f1 = (*t)[1]; |
| if (is_empty(f1)) { |
| port = 0; // clear port. |
| } else { |
| port = f1.as_integer(-1); // try as integer, fail if not convertible. |
| } |
| } |
| } |
| } |
| } |
| if (0 <= port && port < std::numeric_limits<in_port_t>::max()) { |
| url.host_set(host_token); |
| url.port_set(port); // if @a port is 0 then it will be removed from the URL. |
| } |
| } |
| |
| /// Set the location in the Host field and URL (if needed). |
| void |
| Req_Loc_Set(Context &ctx, Expr &expr, ts::HttpRequest &req) |
| { |
| auto value = ctx.extract(expr); |
| TextView host_token; |
| int port = -1; // if still -1 after parsing, the parsing failed. |
| if (auto loc = std::get_if<IndexFor(STRING)>(&value); nullptr != loc && Loc_String_Parse(*loc, host_token, port)) { |
| req.field_obtain(ts::HTTP_FIELD_HOST).assign(*loc); |
| } else if (auto t = std::get_if<IndexFor(TUPLE)>(&value); nullptr != t) { |
| if (t->size() > 0) { // host name, then port. |
| if (auto &f0 = (*t)[0]; f0.index() == IndexFor(STRING)) { |
| host_token = std::get<IndexFor(STRING)>(f0); |
| if (t->size() > 1) { |
| auto &f1 = (*t)[1]; |
| if (is_empty(f1)) { |
| port = 0; // clear port. |
| } else { |
| port = f1.as_integer(-1); // try as integer, fail if not convertible. |
| } |
| } else { |
| port = 0; // no port element, clear port. |
| } |
| auto buffer = ctx.transient_buffer(host_token.size() + 1 + std::numeric_limits<in_port_t>::digits10); |
| swoc::FixedBufferWriter w{buffer}; |
| w.write(host_token); |
| if (port > 0) { |
| w.write(':'); |
| bwformat(w, swoc::bwf::Spec::DEFAULT, port); |
| } |
| req.field_obtain(ts::HTTP_FIELD_HOST).assign(w.view()); |
| ctx.transient_discard(); |
| } |
| } |
| } |
| |
| // If the field was set, set the URL to match if it has a host. |
| if (port >= 0) { |
| if (auto url{req.url()}; url.is_valid() && !url.host().empty()) { |
| url.host_set(host_token); |
| url.port_set(port); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| class Do_ua_req_url_loc : public Directive |
| { |
| using super_type = Directive; |
| using self_type = Do_ua_req_url_loc; |
| |
| public: |
| static inline const std::string KEY = "ua-req-url-loc"; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_ua_req_url_loc(Expr &&expr); |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Feature expression. |
| }; |
| |
| const HookMask Do_ua_req_url_loc::HOOKS = MaskFor(Hook::PREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP); |
| |
| Do_ua_req_url_loc::Do_ua_req_url_loc(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_url_loc::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| if (auto url{hdr.url()}; url.is_valid()) { |
| URL_Loc_Set(ctx, _expr, url); |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_url_loc::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy({STRING, TUPLE})) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {} or a {} of 2 elements.)", KEY, drtv_node.Mark(), STRING, |
| TUPLE); |
| } |
| return Handle(new self_type{std::move(expr)}); |
| } |
| |
| // --- |
| |
| class Do_proxy_req_url_loc : public Directive |
| { |
| using super_type = Directive; |
| using self_type = Do_proxy_req_url_loc; |
| |
| public: |
| static inline const std::string KEY = "proxy-req-url-loc"; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_proxy_req_url_loc(Expr &&expr); |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Feature expression. |
| }; |
| |
| const HookMask Do_proxy_req_url_loc::HOOKS = MaskFor(Hook::PREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP); |
| |
| Do_proxy_req_url_loc::Do_proxy_req_url_loc(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_proxy_req_url_loc::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| if (auto url{hdr.url()}; url.is_valid()) { |
| URL_Loc_Set(ctx, _expr, url); |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_url_loc::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy({STRING, TUPLE})) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {} or a {} of 2 elements.)", KEY, drtv_node.Mark(), STRING, |
| TUPLE); |
| } |
| return Handle(new self_type{std::move(expr)}); |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the host for the request. |
| * This updates both the URL and the "Host" field, if appropriate. |
| */ |
| class Do_ua_req_host : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_ua_req_host; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"ua-req-host"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_ua_req_host(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature. |
| }; |
| |
| const HookMask Do_ua_req_host::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_host::Do_ua_req_host(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_host::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| auto value = ctx.extract(_expr); |
| if (auto host = std::get_if<IndexFor(STRING)>(&value); nullptr != host) { |
| hdr.host_set(*host); |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_host::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), STRING); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the port for the user agent request. |
| * This updates both the URL and the "Host" field, if appropriate. |
| */ |
| class Do_ua_req_port : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_ua_req_port; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"ua-req-port"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_ua_req_port(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature. |
| }; |
| |
| const HookMask Do_ua_req_port::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_port::Do_ua_req_port(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_port::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| auto value = ctx.extract(_expr); |
| if (auto port = value.as_integer(-1); port >= 0) { |
| hdr.port_set(port); |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_port::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(INTEGER)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), INTEGER); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| |
| // --- |
| |
| /** Set the port for the proxy request. |
| * This updates both the URL and the "Host" field, if appropriate. |
| */ |
| class Do_proxy_req_port : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_proxy_req_port; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"proxy-req-port"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_proxy_req_port(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature. |
| }; |
| |
| const HookMask Do_proxy_req_port::HOOKS{MaskFor(Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP)}; |
| |
| Do_proxy_req_port::Do_proxy_req_port(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_proxy_req_port::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| auto value = ctx.extract(_expr); |
| if (auto port = value.as_integer(-1); port >= 0) { |
| hdr.port_set(port); |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_port::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(INTEGER)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), INTEGER); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the host for the request. |
| * This updates both the URL and the "Host" field, if appropriate. |
| */ |
| class Do_proxy_req_host : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_proxy_req_host; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"proxy-req-host"}; ///< Directive name. |
| static inline const HookMask HOOKS{MaskFor(Hook::PREQ)}; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a fmt. |
| * |
| * @param fmt Feature for host. |
| */ |
| explicit Do_proxy_req_host(Expr &&fmt); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _fmt; ///< Host feature. |
| }; |
| |
| Do_proxy_req_host::Do_proxy_req_host(Expr &&fmt) : _fmt(std::move(fmt)) {} |
| |
| Errata |
| Do_proxy_req_host::invoke(Context &ctx) |
| { |
| TextView host{std::get<IndexFor(STRING)>(ctx.extract(_fmt))}; |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| hdr.host_set(host); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_host::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a string.)", KEY, drtv_node.Mark()); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the location for the user agent request. |
| * This updates both the URL and the "Host" field, if appropriate. |
| */ |
| class Do_ua_req_loc : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_ua_req_loc; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"ua-req-loc"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_ua_req_loc(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature. |
| }; |
| |
| const HookMask Do_ua_req_loc::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_loc::Do_ua_req_loc(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_loc::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| Req_Loc_Set(ctx, _expr, hdr); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_loc::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy({STRING, TUPLE})) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {} or a {}.)", KEY, drtv_node.Mark(), STRING, TUPLE); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the location for the proxy request. |
| * This updates both the URL and the "Host" field, if appropriate. |
| */ |
| class Do_proxy_req_loc : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_proxy_req_loc; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"proxy-req-loc"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression. |
| */ |
| explicit Do_proxy_req_loc(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature. |
| }; |
| |
| const HookMask Do_proxy_req_loc::HOOKS{MaskFor({Hook::PREQ})}; |
| |
| Do_proxy_req_loc::Do_proxy_req_loc(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_proxy_req_loc::invoke(Context &ctx) |
| { |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| Req_Loc_Set(ctx, _expr, hdr); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_loc::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy({STRING, TUPLE})) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {} or a {}.)", KEY, drtv_node.Mark(), STRING, TUPLE); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the scheme for the inbound request. |
| */ |
| class Do_ua_req_scheme : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_ua_req_scheme; ///< Self reference type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a fmt. |
| * |
| * @param fmt Feature for host. |
| */ |
| explicit Do_ua_req_scheme(Expr &&fmt); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Scheme expression. |
| }; |
| |
| const std::string Do_ua_req_scheme::KEY{"ua-req-scheme"}; |
| const HookMask Do_ua_req_scheme::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_scheme::Do_ua_req_scheme(Expr &&fmt) : _expr(std::move(fmt)) {} |
| |
| Errata |
| Do_ua_req_scheme::invoke(Context &ctx) |
| { |
| TextView text{std::get<IndexFor(STRING)>(ctx.extract(_expr))}; |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| hdr.url().scheme_set(text); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_scheme::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), STRING); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the URL for the inbound request. |
| */ |
| class Do_ua_req_url : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_ua_req_url; ///< Self reference type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a fmt. |
| * |
| * @param fmt Feature for host. |
| */ |
| explicit Do_ua_req_url(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< URL expression. |
| }; |
| |
| const std::string Do_ua_req_url::KEY{"ua-req-url"}; |
| const HookMask Do_ua_req_url::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_url::Do_ua_req_url(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_url::invoke(Context &ctx) |
| { |
| TextView text{std::get<IndexFor(STRING)>(ctx.extract(_expr))}; |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| hdr.url_set(text); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_url::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), STRING); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the scheme for the outbound request. |
| */ |
| class Do_proxy_req_scheme : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_proxy_req_scheme; ///< Self reference type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a fmt. |
| * |
| * @param fmt Feature for host. |
| */ |
| explicit Do_proxy_req_scheme(Expr &&fmt); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _fmt; ///< Host feature. |
| }; |
| |
| const std::string Do_proxy_req_scheme::KEY{"proxy-req-scheme"}; |
| const HookMask Do_proxy_req_scheme::HOOKS{MaskFor({Hook::PREQ})}; |
| |
| Do_proxy_req_scheme::Do_proxy_req_scheme(Expr &&fmt) : _fmt(std::move(fmt)) {} |
| |
| Errata |
| Do_proxy_req_scheme::invoke(Context &ctx) |
| { |
| TextView host{std::get<IndexFor(STRING)>(ctx.extract(_fmt))}; |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| hdr.url().scheme_set(host); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_scheme::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), STRING); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the URL for the outbound request. |
| */ |
| class Do_proxy_req_url : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_proxy_req_url; ///< Self reference type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression for the URL. |
| */ |
| explicit Do_proxy_req_url(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< URL expression. |
| }; |
| |
| const std::string Do_proxy_req_url::KEY{"proxy-req-url"}; |
| const HookMask Do_proxy_req_url::HOOKS{MaskFor({Hook::PREQ})}; |
| |
| Do_proxy_req_url::Do_proxy_req_url(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_proxy_req_url::invoke(Context &ctx) |
| { |
| TextView text{std::get<IndexFor(STRING)>(ctx.extract(_expr))}; |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| hdr.url_set(text); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_url::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a {}.)", KEY, drtv_node.Mark(), STRING); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| class Do_did_remap : public Directive |
| { |
| using self_type = Do_did_remap; |
| |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature expression.. |
| * |
| * @param expr Feature expression for the URL. |
| */ |
| explicit Do_did_remap(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Boolean to set whether remap was done. |
| }; |
| |
| const std::string Do_did_remap::KEY{"did-remap"}; |
| const HookMask Do_did_remap::HOOKS{MaskFor(Hook::REMAP)}; |
| |
| Do_did_remap::Do_did_remap(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_did_remap::invoke(Context &ctx) |
| { |
| auto f = ctx.extract(_expr); |
| ctx._remap_status = f.as_bool() ? TSREMAP_DID_REMAP : TSREMAP_NO_REMAP; |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_did_remap::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| // Default, with no value, is @c true. |
| if (key_value.IsNull()) { |
| return Handle{new self_type(Expr(true))}; |
| } |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing value of "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(BOOLEAN)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be convertible to a {}.)", KEY, drtv_node.Mark(), BOOLEAN); |
| } |
| return Handle{new self_type{std::move(expr)}}; |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /** Do the remap. |
| */ |
| class Do_apply_remap_rule : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_apply_remap_rule; ///< Self reference type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| }; |
| |
| const std::string Do_apply_remap_rule::KEY{"apply-remap-rule"}; |
| const HookMask Do_apply_remap_rule::HOOKS{MaskFor(Hook::REMAP)}; |
| |
| Errata |
| Do_apply_remap_rule::invoke(Context &ctx) |
| { |
| ctx._remap_status = TSREMAP_DID_REMAP; |
| // This is complex because the internal logic is as well. A bit fragile, but this is |
| // really only useful as a backwards compatibility fix for pre ATS 9 and should eventually |
| // be removed. |
| // Copy over the host and port. |
| ts::URL replacement_url{ctx._remap_info->requestBufp, ctx._remap_info->mapToUrl}; |
| ts::URL target_url{ctx._remap_info->requestBufp, ctx._remap_info->mapFromUrl}; |
| ts::URL request_url{ctx._remap_info->requestBufp, ctx._remap_info->requestUrl}; |
| |
| in_port_t port = replacement_url.port(); |
| // decanonicalize the port - may need to dig in and see if it was explicitly set. |
| if ((port == 80 && replacement_url.scheme() == ts::URL_SCHEME_HTTP) || |
| (port == 443 && replacement_url.scheme() == ts::URL_SCHEME_HTTPS)) { |
| port = 0; |
| } |
| request_url.port_set(port); |
| request_url.host_set(replacement_url.host()); |
| if (ts::HttpRequest{ctx._remap_info->requestBufp, ctx._remap_info->requestHdrp}.method() != "CONNECT"_tv) { |
| request_url.scheme_set(replacement_url.scheme()); |
| // Update the path as needed. |
| auto replacement_path{replacement_url.path()}; |
| auto target_path{target_url.path()}; |
| auto request_path{request_url.path()}; |
| |
| // Need to do better - see if Context can provide an ArenaWriter? |
| swoc::LocalBufferWriter<(1 << 16) - 1> url_w; |
| url_w.write(replacement_path); |
| if (request_path.size() > target_path.size()) { |
| // Always slash separate the replacement from the remnant of the incoming request path. |
| if (url_w.size() && url_w.view()[url_w.size() - 1] != '/') { |
| url_w.write('/'); |
| } |
| // Already have the separating slash, trim it from the target path. |
| url_w.write(request_path.substr(target_path.size()).ltrim('/')); |
| } |
| request_url.path_set(TextView{url_w.view()}.ltrim('/')); |
| }; |
| |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_apply_remap_rule::load(Config &, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &, YAML::Node) |
| { |
| return Handle(new self_type); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the path for the request. |
| */ |
| class Do_ua_req_path : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_ua_req_path; ///< Self reference type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a expr. |
| * |
| * @param expr Feature for host. |
| */ |
| explicit Do_ua_req_path(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature. |
| }; |
| |
| const std::string Do_ua_req_path::KEY{"ua-req-path"}; |
| const HookMask Do_ua_req_path::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_path::Do_ua_req_path(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_path::invoke(Context &ctx) |
| { |
| auto value{ctx.extract(_expr)}; |
| if (auto text = std::get_if<IndexFor(STRING)>(&value); text) { |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| hdr.url().path_set(*text); |
| } |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_path::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a string.)", KEY, drtv_node.Mark()); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the fragment for the request. |
| */ |
| class Do_ua_req_fragment : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_ua_req_fragment; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"ua-req-fragment"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a fmt. |
| * |
| * @param expr Feature for host. |
| */ |
| explicit Do_ua_req_fragment(Expr &&expr); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Host feature. |
| }; |
| |
| const HookMask Do_ua_req_fragment::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Do_ua_req_fragment::Do_ua_req_fragment(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_ua_req_fragment::invoke(Context &ctx) |
| { |
| TextView text{std::get<IndexFor(STRING)>(ctx.extract(_expr))}; |
| if (auto hdr{ctx.ua_req_hdr()}; hdr.is_valid()) { |
| hdr.url().fragment_set(text); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_ua_req_fragment::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (expr.is_null()) { |
| expr = Feature{FeatureView::Literal(""_tv)}; |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a string.)", KEY, drtv_node.Mark()); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the path for the request. |
| */ |
| class Do_proxy_req_path : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_proxy_req_path; ///< Self reference type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a fmt. |
| * |
| * @param fmt Feature for host. |
| */ |
| explicit Do_proxy_req_path(Expr &&fmt); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _fmt; ///< Host feature. |
| }; |
| |
| const std::string Do_proxy_req_path::KEY{"proxy-req-path"}; |
| const HookMask Do_proxy_req_path::HOOKS{MaskFor({Hook::PREQ})}; |
| |
| Do_proxy_req_path::Do_proxy_req_path(Expr &&fmt) : _fmt(std::move(fmt)) {} |
| |
| Errata |
| Do_proxy_req_path::invoke(Context &ctx) |
| { |
| TextView host{std::get<IndexFor(STRING)>(ctx.extract(_fmt))}; |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| hdr.url().path_set(host); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_path::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a string.)", KEY, drtv_node.Mark()); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /** Set the fragment for the request. |
| */ |
| class Do_proxy_req_fragment : public Directive |
| { |
| using super_type = Directive; ///< Parent type. |
| using self_type = Do_proxy_req_fragment; ///< Self reference type. |
| public: |
| static inline const std::string KEY{"proxy-req-fragment"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /** Construct with feature extractor @a fmt. |
| * |
| * @param fmt Feature for host. |
| */ |
| explicit Do_proxy_req_fragment(Expr &&fmt); |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _fmt; ///< Host feature. |
| }; |
| |
| const HookMask Do_proxy_req_fragment::HOOKS{MaskFor({Hook::PREQ})}; |
| |
| Do_proxy_req_fragment::Do_proxy_req_fragment(Expr &&fmt) : _fmt(std::move(fmt)) {} |
| |
| Errata |
| Do_proxy_req_fragment::invoke(Context &ctx) |
| { |
| TextView text{std::get<IndexFor(STRING)>(ctx.extract(_fmt))}; |
| if (auto hdr{ctx.proxy_req_hdr()}; hdr.is_valid()) { |
| hdr.url().fragment_set(text); |
| } |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_proxy_req_fragment::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, |
| swoc::TextView const &, YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing "{}" directive at {}.)", KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(Value for "{}" directive at {} must be a string.)", KEY, drtv_node.Mark()); |
| } |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| class FieldDirective : public Directive |
| { |
| using self_type = FieldDirective; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| protected: |
| TextView _name; ///< Field name. |
| Expr _expr; ///< Value for field. |
| |
| /** Base constructor. |
| * |
| * @param name Name of the field. |
| * @param expr Value to assign to the field. |
| */ |
| FieldDirective(TextView const &name, Expr &&expr); |
| |
| /** Load from configuration. |
| * |
| * @param cfg Configuration. |
| * @param maker Subclass maker. |
| * @param key Name of the key identifying the directive. |
| * @param name Field name (directive argumnet). |
| * @param key_value Value of the node for @a key. |
| * @return An instance of the directive for @a key, or errors. |
| */ |
| static Rv<Handle> load(Config &cfg, std::function<Handle(TextView const &name, Expr &&fmt)> const &maker, TextView const &key, |
| TextView const &name, YAML::Node key_value); |
| |
| /** Invoke the directive on an HTTP header. |
| * |
| * @param ctx Runtime context. |
| * @param hdr HTTP header. |
| * @return Errors, if any. |
| */ |
| Errata invoke_on_hdr(Context &ctx, ts::HttpHeader &&hdr); |
| |
| /** Get the directive name (key). |
| * |
| * @return The directive key. |
| * |
| * Used by subclasses to provide the key for diagnostics. |
| */ |
| virtual swoc::TextView key() const = 0; |
| |
| template <typename T> |
| void |
| apply(Context &, ts::HttpHeader &&, T const &) |
| { |
| } |
| |
| /// Visitor to perform the assignment. |
| struct Apply { |
| Context &_ctx; ///< Runtime context. |
| ts::HttpHeader &_hdr; ///< HTTP Header to modify. |
| ts::HttpField _field; ///< Working field. |
| TextView const &_name; ///< Field name. |
| |
| Apply(Context &ctx, ts::HttpHeader &hdr, TextView const &name) : _ctx(ctx), _hdr(hdr), _field(hdr.field(name)), _name(name) {} |
| |
| /// Clear all duplicate fields past @a _field. |
| void |
| clear_dups() |
| { |
| if (_field.is_valid()) { |
| for (auto nf = _field.next_dup(); nf.is_valid();) { |
| nf.destroy(); |
| } |
| } |
| } |
| |
| /// Assigned @a text to a single field, creating as needed. |
| /// @return @c true if a field value was changed, @c false if the current value is @a text. |
| void |
| assign(TextView const &text) |
| { |
| if (_field.is_valid()) { |
| if (_field.value() != text) { |
| _field.assign(text); |
| } |
| } else { |
| _hdr.field_create(_name).assign(text); |
| } |
| } |
| |
| /// Nil / NULL means destroy the field. |
| void |
| operator()(feature_type_for<NIL>) |
| { |
| if (_field.is_valid()) { |
| this->clear_dups(); |
| _field.destroy(); |
| } |
| } |
| |
| /// Assign the string, clear out any dups. |
| void |
| operator()(feature_type_for<STRING> &text) |
| { |
| this->assign(text); |
| this->clear_dups(); |
| } |
| |
| /// Assign the tuple elements to duplicate fields. |
| void |
| operator()(feature_type_for<TUPLE> &t) |
| { |
| for (auto const &tf : t) { |
| auto text{std::get<STRING>(tf.join(_ctx, ", "_tv))}; |
| // skip to next equal field, destroying mismatched fields. |
| // once @a _field becomes invalid, it remains in that state. |
| while (_field.is_valid() && _field.value() != text) { |
| auto tmp = _field.next_dup(); |
| _field.destroy(); |
| _field = std::move(tmp); |
| } |
| this->assign(text); |
| _field = _field.next_dup(); // does nothing if @a _field is invalid. |
| } |
| if (_field.is_valid()) { |
| this->clear_dups(); // Any remaining fields need to be cleaned up. |
| _field.destroy(); |
| } |
| } |
| |
| // Other types, convert to string |
| template <typename T> |
| auto |
| operator()(T &&t) -> EnableForFeatureTypes<T, void> |
| { |
| this->assign(_ctx.render_transient([&t](BufferWriter &w) { bwformat(w, bwf::Spec::DEFAULT, t); })); |
| this->clear_dups(); |
| } |
| }; |
| }; |
| |
| FieldDirective::FieldDirective(TextView const &name, Expr &&expr) : _name(name), _expr(std::move(expr)) {} |
| |
| Errata |
| FieldDirective::invoke_on_hdr(Context &ctx, ts::HttpHeader &&hdr) |
| { |
| if (hdr.is_valid()) { |
| auto value{ctx.extract(this->_expr)}; |
| std::visit(Apply(ctx, hdr, _name), value); |
| return {}; |
| } |
| return Errata(S_ERROR, R"(Failed to assign field value due to invalid HTTP header.)"); |
| } |
| |
| auto |
| FieldDirective::load(Config &cfg, std::function<Handle(TextView const &, Expr &&)> const &maker, TextView const &key, |
| TextView const &arg, YAML::Node key_value) -> Rv<Handle> |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing value for "{}".)", key); |
| return std::move(errata); |
| } |
| |
| auto expr_type = expr.result_type(); |
| if (!expr_type.has_value()) { |
| return Errata(S_ERROR, R"(Directive "{}" must have a value.)", key); |
| } |
| return maker(cfg.localize(arg), std::move(expr)); |
| } |
| |
| // -- Implementations -- |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set transaction level debuggging for this transaction. |
| class Do_txn_debug : public Directive |
| { |
| using self_type = Do_txn_debug; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static inline const std::string KEY{"txn-debug"}; ///< Directive name. |
| |
| /// Valid hooks for directive. |
| static inline const HookMask HOOKS{ |
| MaskFor({Hook::TXN_START, Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, Hook::PRE_REMAP, Hook::POST_REMAP, Hook::REMAP})}; |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML configuration. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Whether transaction debug is enabled or disabled. |
| |
| Do_txn_debug(Expr &&msg); |
| }; |
| |
| Do_txn_debug::Do_txn_debug(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_txn_debug::invoke(Context &ctx) |
| { |
| auto f = ctx.extract(_expr); |
| ctx._txn.enable_debug(f.as_bool()); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_txn_debug::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing message at {} for "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return std::move(errata); |
| } |
| return {Handle{new self_type{std::move(expr)}}}; |
| } |
| |
| // -- |
| class Do_ua_req_field : public FieldDirective |
| { |
| using self_type = Do_ua_req_field; |
| using super_type = FieldDirective; |
| |
| public: |
| static const std::string KEY; ///< Directive key. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| using super_type::invoke; |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| using super_type::super_type; // Inherit super_type constructors. |
| TextView |
| key() const override |
| { |
| return KEY; |
| } |
| }; |
| |
| const std::string Do_ua_req_field::KEY{"ua-req-field"}; |
| const HookMask Do_ua_req_field::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Errata |
| Do_ua_req_field::invoke(Context &ctx) |
| { |
| return this->invoke_on_hdr(ctx, ctx.ua_req_hdr()); |
| } |
| |
| Rv<Directive::Handle> |
| Do_ua_req_field::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &arg, |
| YAML::Node key_value) |
| { |
| return super_type::load( |
| cfg, [](TextView const &name, Expr &&fmt) -> Handle { return Handle(new self_type(name, std::move(fmt))); }, KEY, arg, |
| key_value); |
| } |
| |
| // -- |
| class Do_proxy_req_field : public FieldDirective |
| { |
| using self_type = Do_proxy_req_field; |
| using super_type = FieldDirective; |
| |
| public: |
| static const std::string KEY; ///< Directive key. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| using super_type::super_type; // Inherit super_type constructors. |
| TextView |
| key() const override |
| { |
| return KEY; |
| } |
| }; |
| |
| const std::string Do_proxy_req_field::KEY{"proxy-req-field"}; |
| const HookMask Do_proxy_req_field::HOOKS{MaskFor({Hook::PREQ})}; |
| |
| Errata |
| Do_proxy_req_field::invoke(Context &ctx) |
| { |
| return this->invoke_on_hdr(ctx, ctx.proxy_req_hdr()); |
| } |
| |
| Rv<Directive::Handle> |
| Do_proxy_req_field::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &arg, |
| YAML::Node key_value) |
| { |
| return super_type::load( |
| cfg, [](TextView const &name, Expr &&fmt) -> Handle { return Handle(new self_type(name, std::move(fmt))); }, KEY, arg, |
| key_value); |
| } |
| |
| // -- |
| class Do_proxy_rsp_field : public FieldDirective |
| { |
| using self_type = Do_proxy_rsp_field; |
| using super_type = FieldDirective; |
| |
| public: |
| static const std::string KEY; ///< Directive key. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| using super_type::super_type; // Inherit super_type constructors. |
| TextView |
| key() const override |
| { |
| return KEY; |
| } |
| }; |
| |
| const std::string Do_proxy_rsp_field::KEY{"proxy-rsp-field"}; |
| const HookMask Do_proxy_rsp_field::HOOKS{MaskFor(Hook::PRSP)}; |
| |
| Errata |
| Do_proxy_rsp_field::invoke(Context &ctx) |
| { |
| return this->invoke_on_hdr(ctx, ctx.proxy_rsp_hdr()); |
| } |
| |
| Rv<Directive::Handle> |
| Do_proxy_rsp_field::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &arg, |
| YAML::Node key_value) |
| { |
| return super_type::load( |
| cfg, [](TextView const &name, Expr &&fmt) -> Handle { return Handle(new self_type(name, std::move(fmt))); }, KEY, arg, |
| key_value); |
| } |
| |
| // -- |
| class Do_upstream_rsp_field : public FieldDirective |
| { |
| using self_type = Do_upstream_rsp_field; |
| using super_type = FieldDirective; |
| |
| public: |
| static const std::string KEY; ///< Directive key. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| using super_type::super_type; // Inherit super_type constructors. |
| TextView |
| key() const override |
| { |
| return KEY; |
| } |
| }; |
| |
| const std::string Do_upstream_rsp_field::KEY{"upstream-rsp-field"}; |
| const HookMask Do_upstream_rsp_field::HOOKS{MaskFor(Hook::URSP)}; |
| |
| Errata |
| Do_upstream_rsp_field::invoke(Context &ctx) |
| { |
| return this->invoke_on_hdr(ctx, ctx.upstream_rsp_hdr()); |
| } |
| |
| Rv<Directive::Handle> |
| Do_upstream_rsp_field::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &arg, |
| YAML::Node key_value) |
| { |
| return super_type::load( |
| cfg, [](TextView const &name, Expr &&fmt) -> Handle { return Handle(new self_type(name, std::move(fmt))); }, KEY, arg, |
| key_value); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set upstream response status code. |
| class Do_upstream_rsp_status : public Directive |
| { |
| using self_type = Do_upstream_rsp_status; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Return status. |
| |
| Do_upstream_rsp_status() = default; |
| }; |
| |
| const std::string Do_upstream_rsp_status::KEY{"upstream-rsp-status"}; |
| const HookMask Do_upstream_rsp_status::HOOKS{MaskFor({Hook::URSP})}; |
| |
| Errata |
| Do_upstream_rsp_status::invoke(Context &ctx) |
| { |
| int status = TS_HTTP_STATUS_NONE; |
| Feature value = ctx.extract(_expr); |
| auto vtype = value.value_type(); |
| if (INTEGER == vtype) { |
| status = std::get<IndexFor(INTEGER)>(value); |
| } else if (TUPLE == vtype) { |
| auto t = std::get<IndexFor(TUPLE)>(value); |
| if (0 < t.count() && t.count() <= 2) { |
| if (t[0].value_type() != INTEGER) { |
| return Errata(S_ERROR, R"(Tuple for "{}" must be an integer and a string.)", KEY); |
| } |
| status = std::get<IndexFor(INTEGER)>(t[0]); |
| if (t.count() == 2) { |
| if (t[1].value_type() != STRING) { |
| return Errata(S_ERROR, R"(Tuple for "{}" must be an integer and a string.)", KEY); |
| } |
| ctx._txn.ursp_hdr().reason_set(std::get<IndexFor(STRING)>(t[1])); |
| } |
| } else { |
| return Errata(S_ERROR, R"(Tuple for "{}" has {} elements, instead of there required 1 or 2.)", KEY, t.size()); |
| } |
| } |
| if (100 <= status && status <= 599) { |
| ctx._txn.ursp_hdr().status_set(static_cast<TSHttpStatus>(status)); |
| } else { |
| return Errata(S_ERROR, R"(Status value {} out of range 100..599 for "{}".)", status, KEY); |
| } |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_upstream_rsp_status::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, |
| swoc::TextView const &, YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| auto self = new self_type; |
| Handle handle(self); |
| |
| auto expr_type = expr.result_type(); |
| if (!expr_type.can_satisfy({INTEGER, TUPLE})) { |
| return Errata(S_ERROR, R"(Value for "{}" at {} is not an integer or tuple as required.)", KEY, drtv_node.Mark()); |
| } |
| self->_expr = std::move(expr); |
| return handle; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set upstream response reason phrase. |
| class Do_upstream_reason : public Directive |
| { |
| using self_type = Do_upstream_reason; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML configuration. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| TSHttpStatus _status = TS_HTTP_STATUS_NONE; ///< Return status is literal, 0 => extract at runtime. |
| Expr _fmt; ///< Reason phrase. |
| |
| Do_upstream_reason() = default; |
| }; |
| |
| const std::string Do_upstream_reason::KEY{"upstream-reason"}; |
| const HookMask Do_upstream_reason::HOOKS{MaskFor({Hook::URSP})}; |
| |
| Errata |
| Do_upstream_reason::invoke(Context &ctx) |
| { |
| auto value = ctx.extract(_fmt); |
| if (STRING != value.value_type()) { |
| return Errata(S_ERROR, R"(Value for "{}" is not a string.)", KEY); |
| } |
| ctx._txn.ursp_hdr().reason_set(std::get<IndexFor(STRING)>(value)); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_upstream_reason::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(The value for "{}" must be a string.)", KEY, drtv_node.Mark()); |
| } |
| auto self = new self_type; |
| Handle handle(self); |
| |
| self->_fmt = std::move(expr); |
| |
| return handle; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set proxy response status code. |
| class Do_proxy_rsp_status : public Directive |
| { |
| using self_type = Do_proxy_rsp_status; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML configuration. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Return status. |
| |
| Do_proxy_rsp_status() = default; |
| }; |
| |
| const std::string Do_proxy_rsp_status::KEY{"proxy-rsp-status"}; |
| const HookMask Do_proxy_rsp_status::HOOKS{MaskFor({Hook::PRSP})}; |
| |
| Errata |
| Do_proxy_rsp_status::invoke(Context &ctx) |
| { |
| int status = TS_HTTP_STATUS_NONE; |
| Feature value = ctx.extract(_expr); |
| auto vtype = value.value_type(); |
| if (INTEGER == vtype) { |
| status = std::get<IndexFor(INTEGER)>(value); |
| } else if (TUPLE == vtype) { |
| auto t = std::get<IndexFor(TUPLE)>(value); |
| if (0 < t.count() && t.count() <= 2) { |
| if (t[0].value_type() != INTEGER) { |
| return Errata(S_ERROR, R"(Tuple for "{}" must be an integer and a string.)", KEY); |
| } |
| status = std::get<IndexFor(INTEGER)>(t[0]); |
| if (t.count() == 2) { |
| if (t[1].value_type() != STRING) { |
| return Errata(S_ERROR, R"(Tuple for "{}" must be an integer and a string.)", KEY); |
| } |
| ctx._txn.prsp_hdr().reason_set(std::get<IndexFor(STRING)>(t[1])); |
| } |
| } else { |
| return Errata(S_ERROR, R"(Tuple for "{}" has {} elements, instead of there required 1 or 2.)", KEY, t.size()); |
| } |
| } |
| if (100 <= status && status <= 599) { |
| ctx._txn.prsp_hdr().status_set(static_cast<TSHttpStatus>(status)); |
| } else { |
| return Errata(S_ERROR, R"(Status value {} out of range 100..599 for "{}".)", status, KEY); |
| } |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_proxy_rsp_status::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| auto self = new self_type; |
| Handle handle(self); |
| |
| auto expr_type = expr.result_type(); |
| if (!expr_type.can_satisfy(MaskFor({INTEGER, TUPLE}))) { |
| return Errata(S_ERROR, R"(Value for "{}" at {} is not an integer or tuple as required.)", KEY, drtv_node.Mark()); |
| } |
| self->_expr = std::move(expr); |
| return handle; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set proxy response reason phrase. |
| class Do_proxy_rsp_reason : public Directive |
| { |
| using self_type = Do_proxy_rsp_reason; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static inline const std::string KEY{"proxy-rsp-reason"}; ///< Directive name. |
| static inline const HookMask HOOKS{MaskFor({Hook::PRSP})}; ///< Valid hooks for directive. |
| |
| /** Invoke directive. |
| * |
| * @param ctx Transaction context. |
| * @return Errors, if any. |
| */ |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML configuration. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| TSHttpStatus _status = TS_HTTP_STATUS_NONE; ///< Return status is literal, 0 => extract at runtime. |
| Expr _expr; ///< Reason phrase. |
| |
| Do_proxy_rsp_reason() = default; |
| }; |
| |
| Errata |
| Do_proxy_rsp_reason::invoke(Context &ctx) |
| { |
| auto value = ctx.extract(_expr); |
| if (STRING != value.value_type()) { |
| return Errata(S_ERROR, R"(Value for "{}" is not a string.)", KEY); |
| } |
| ctx._txn.prsp_hdr().reason_set(std::get<IndexFor(STRING)>(value)); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_proxy_rsp_reason::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(The value for "{}" must be a string.)", KEY, drtv_node.Mark()); |
| } |
| auto self = new self_type; |
| Handle handle(self); |
| |
| self->_expr = std::move(expr); |
| |
| return handle; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set proxy response (error) body. |
| class Do_proxy_rsp_body : public Directive |
| { |
| using self_type = Do_proxy_rsp_body; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML configuration. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Body content. |
| |
| Do_proxy_rsp_body() = default; |
| }; |
| |
| const std::string Do_proxy_rsp_body::KEY{"proxy-rsp-body"}; |
| const HookMask Do_proxy_rsp_body::HOOKS{MaskFor({Hook::PRSP})}; |
| |
| Errata |
| Do_proxy_rsp_body::invoke(Context &ctx) |
| { |
| TextView body, mime{"text/html"}; |
| auto value = ctx.extract(_expr); |
| if (STRING == value.value_type()) { |
| body = std::get<IndexFor(STRING)>(value); |
| } else if (auto tp = std::get_if<IndexFor(TUPLE)>(&value); tp) { |
| if (tp->count() == 2) { |
| if (auto ptr = std::get_if<IndexFor(STRING)>(&(*tp)[0]); ptr) { |
| body = *ptr; |
| } |
| if (auto ptr = std::get_if<IndexFor(STRING)>(&(*tp)[1]); ptr) { |
| mime = *ptr; |
| } |
| } else { |
| return Errata(S_ERROR, R"(Value for "{}" is not a list of length 2.)", KEY); |
| } |
| } else { |
| return Errata(S_ERROR, R"(Value for "{}" is not a string nor a list.)", KEY); |
| } |
| ctx._txn.error_body_set(body, mime); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_proxy_rsp_body::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy({STRING, ActiveType::TupleOf(STRING)})) { |
| return Errata(S_ERROR, R"(The value for "{}" must be a string or a list of two strings.)", KEY, drtv_node.Mark()); |
| } |
| auto self = new self_type; |
| Handle handle(self); |
| |
| self->_expr = std::move(expr); |
| |
| return handle; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Replace the upstream response body with a feature. |
| class Do_upstream_rsp_body : public Directive |
| { |
| using self_type = Do_upstream_rsp_body; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML configuration. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Body content. |
| |
| Do_upstream_rsp_body(Expr &&expr) : _expr(std::move(expr)) {} |
| }; |
| |
| const std::string Do_upstream_rsp_body::KEY{"upstream-rsp-body"}; |
| const HookMask Do_upstream_rsp_body::HOOKS{MaskFor({Hook::URSP})}; |
| |
| Errata |
| Do_upstream_rsp_body::invoke(Context &ctx) |
| { |
| /// State data for the transform continuation. |
| /// @internal Due to ugliness in the plugin API where the final event for the @c Continuation |
| /// can arrive after the transaction is destroyed, the @c IOBuffer needs to get cleaned up at |
| /// transaction termination, not the final transform event. Therefore the destructor here does |
| /// the cleanup, so that it can be marked for cleanup in the @c Context. |
| struct State { |
| TextView _view; ///< Source view for body. |
| TSIOBuffer _tsio_buff = nullptr; ///< Buffer used to write body. |
| TSCont _cont = nullptr; ///< Transform continuation with which State is associated. |
| /// Clean up the @c IOBuffer. |
| ~State() |
| { |
| if (_cont != nullptr) { |
| TSContDataSet(_cont, nullptr); |
| } |
| if (_tsio_buff) { |
| TSIOBufferDestroy(_tsio_buff); |
| } |
| } |
| }; |
| |
| auto static transform = [](TSCont contp, TSEvent ev_code, void *) -> int { |
| if (TSVConnClosedGet(contp)) { |
| // IOBuffer is cleaned up at transaction close, not here. |
| if (auto state = static_cast<State *>(TSContDataGet(contp)); state) { |
| state->_cont = nullptr; |
| TSContDataSet(contp, nullptr); |
| } |
| TSContDestroy(contp); |
| return 0; |
| } |
| |
| auto in_vio = TSVConnWriteVIOGet(contp); |
| switch (ev_code) { |
| case TS_EVENT_ERROR: |
| TSContCall(TSVIOContGet(in_vio), TS_EVENT_ERROR, in_vio); |
| break; |
| case TS_EVENT_VCONN_WRITE_COMPLETE: |
| TSVConnShutdown(TSTransformOutputVConnGet(contp), 0, 1); |
| break; |
| default: |
| // Consume all input data. |
| auto in_todo = TSVIONTodoGet(in_vio); |
| auto in_reader = TSVIOReaderGet(in_vio); |
| if (in_reader && in_todo) { |
| auto avail = TSIOBufferReaderAvail(in_reader); |
| in_todo = std::min(in_todo, avail); |
| if (in_todo > 0) { |
| TSIOBufferReaderConsume(in_reader, in_todo); |
| TSVIONDoneSet(in_vio, TSVIONDoneGet(in_vio) + in_todo); |
| TSContCall(TSVIOContGet(in_vio), |
| (TSVIONTodoGet(in_vio) <= 0) ? TS_EVENT_VCONN_WRITE_COMPLETE : TS_EVENT_VCONN_WRITE_READY, in_vio); |
| } |
| // If the buffer isn't already there, create it and write out the view. |
| if (auto state = static_cast<State *>(TSContDataGet(contp)); state && !state->_tsio_buff) { |
| auto out_vconn = TSTransformOutputVConnGet(contp); |
| state->_tsio_buff = TSIOBufferCreate(); |
| TSIOBufferWrite(state->_tsio_buff, state->_view.data(), state->_view.size()); |
| auto out_vio = TSVConnWrite(out_vconn, contp, TSIOBufferReaderAlloc(state->_tsio_buff), state->_view.size()); |
| TSVIOReenable(out_vio); |
| } |
| } |
| break; |
| } |
| |
| return 0; |
| }; |
| |
| auto value = ctx.extract(_expr); |
| auto vtype = value.value_type(); |
| TextView *content = nullptr; |
| TextView content_type = "text/html"; |
| if (STRING == vtype) { |
| content = &std::get<IndexFor(STRING)>(value); |
| } else if (TUPLE == vtype) { |
| auto t = std::get<IndexFor(TUPLE)>(value); |
| if (t.size() > 0) { |
| if (STRING == t[0].value_type()) { |
| content = &std::get<IndexFor(STRING)>(t[0]); |
| if (t.size() > 1 && STRING == t[1].value_type()) { |
| content_type = std::get<IndexFor(STRING)>(t[1]); |
| } |
| } |
| } |
| } |
| |
| if (content) { |
| // The view contents are in the transaction data, but the view in the feature is not. |
| // Put a copy in the transform @a state. |
| auto state = ctx.make<State>(); |
| ctx.mark_for_cleanup(state); |
| auto cont = TSTransformCreate(transform, ctx._txn); |
| state->_cont = cont; |
| state->_view = *content; |
| TSContDataSet(cont, state); |
| TSHttpTxnHookAdd(ctx._txn, TS_HTTP_RESPONSE_TRANSFORM_HOOK, cont); |
| ctx._txn.ursp_hdr().field_obtain("Content-Type"_tv).assign(content_type); |
| } |
| |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_upstream_rsp_body::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| if (!expr.result_type().can_satisfy(STRING)) { |
| return Errata(S_ERROR, R"(The value for "{}" must be a string.)", KEY, drtv_node.Mark()); |
| } |
| |
| return Handle(new self_type(std::move(expr))); |
| } |
| // --- |
| /// Immediate proxy reply. |
| class Do_proxy_reply : public Directive |
| { |
| using self_type = Do_proxy_reply; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| |
| /// Per context information. |
| /// This is what is stored in the span @c CfgInfo::_ctx_span |
| struct CtxInfo { |
| TextView _reason; ///< Status reason string. |
| }; |
| |
| public: |
| inline static const std::string KEY = "proxy-reply"; ///< Directive name. |
| inline static const std::string STATUS_KEY = "status"; ///< Key for status value. |
| inline static const std::string REASON_KEY = "reason"; ///< Key for reason value. |
| inline static const std::string BODY_KEY = "body"; ///< Key for body. |
| |
| static inline const HookMask HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP})}; ///< Valid hooks for directive. |
| |
| /// Need to do fixups on a later hook. |
| static constexpr Hook FIXUP_HOOK = Hook::PRSP; |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| static Errata cfg_init(Config &cfg, CfgStaticData const *); |
| |
| protected: |
| using index_type = FeatureGroup::index_type; |
| |
| FeatureGroup _fg; ///< Support cross references among the keys. |
| |
| int _status = 0; ///< Return status is literal, 0 => extract at runtime. |
| index_type _status_idx; ///< Return status. |
| index_type _reason_idx; ///< Status reason text. |
| index_type _body_idx; ///< Body content of respons. |
| /// Bounce from fixup hook directive back to @a this. |
| Directive::Handle _fixup{new LambdaDirective([this](Context &ctx) -> Errata { return this->fixup(ctx); })}; |
| |
| Errata load_status(); |
| |
| /// Do post invocation fixup. |
| Errata fixup(Context &ctx); |
| }; |
| |
| Errata |
| Do_proxy_reply::cfg_init(Config &cfg, CfgStaticData const *) |
| { |
| cfg.reserve_slot(FIXUP_HOOK); // needed to fix up "Location" field in proxy response. |
| return {}; |
| } |
| |
| Errata |
| Do_proxy_reply::invoke(Context &ctx) |
| { |
| auto ctx_info = ctx.obtain_named_object<CtxInfo>(KEY); |
| |
| // Is a fix up hook required to set the reason correctly? |
| bool need_hook_p = false; |
| |
| // Finalize the reason and stash it in context storage. |
| if (_reason_idx != FeatureGroup::INVALID_IDX) { |
| Feature reason = _fg.extract(ctx, _reason_idx); |
| if (reason.index() == IndexFor(STRING)) { |
| ctx.commit(reason); |
| need_hook_p = ctx_info->_reason.empty(); // hook needed if this is first to set reason. |
| ctx_info->_reason = std::get<IndexFor(STRING)>(reason); |
| } |
| } |
| |
| // Set the status to prevent the upstream request. |
| if (_status) { |
| ctx._txn.status_set(static_cast<TSHttpStatus>(_status)); |
| } else { |
| auto &&[status, errata]{_fg.extract(ctx, _status_idx).as_integer(-1)}; |
| if (100 <= status && status <= 599) { |
| ctx._txn.status_set(static_cast<TSHttpStatus>(status)); |
| } |
| } |
| |
| // Set the body. |
| if (_body_idx != FeatureGroup::INVALID_IDX) { |
| auto body{_fg.extract(ctx, _body_idx)}; |
| ctx._txn.error_body_set(std::get<IndexFor(STRING)>(body), "text/html"); |
| } |
| |
| // Arrange for fixup to get invoked. |
| if (need_hook_p) { |
| ctx.on_hook_do(FIXUP_HOOK, _fixup.get()); |
| } |
| return {}; |
| } |
| |
| Errata |
| Do_proxy_reply::fixup(Context &ctx) |
| { |
| if (auto ctx_info = ctx.named_object<CtxInfo>(KEY); ctx_info) { |
| if (!ctx_info->_reason.empty()) { |
| auto hdr{ctx.proxy_rsp_hdr()}; |
| hdr.reason_set(ctx_info->_reason); |
| } |
| } |
| return {}; |
| } |
| |
| Errata |
| Do_proxy_reply::load_status() |
| { |
| _status_idx = _fg.index_of(STATUS_KEY); |
| |
| FeatureGroup::ExprInfo &info = _fg[_status_idx]; |
| |
| if (info._expr.is_literal()) { |
| auto &&[status, errata]{std::get<Feature>(info._expr._raw).as_integer(-1)}; |
| if (!errata.is_ok()) { |
| errata.note("While load key '{}' for directive '{}'", STATUS_KEY, KEY); |
| return std::move(errata); |
| } |
| if (!(100 <= status && status <= 599)) { |
| return Errata(S_ERROR, R"(Value for '{}' key in {} directive is not a positive integer 100..599 as required.)", STATUS_KEY, |
| KEY); |
| } |
| _status = status; |
| } else if (!info._expr.result_type().can_satisfy(MaskFor(STRING, INTEGER))) { |
| return Errata(S_ERROR, R"({} is not an integer nor string as required.)", STATUS_KEY); |
| } |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_proxy_reply::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| Handle handle{new self_type}; |
| Errata errata; |
| auto self = static_cast<self_type *>(handle.get()); |
| if (key_value.IsScalar()) { |
| errata = self->_fg.load_as_scalar(cfg, key_value, STATUS_KEY); |
| } else if (key_value.IsSequence()) { |
| errata = self->_fg.load_as_tuple(cfg, key_value, |
| { |
| {STATUS_KEY, FeatureGroup::REQUIRED}, |
| {REASON_KEY} |
| }); |
| } else if (key_value.IsMap()) { |
| errata = self->_fg.load(cfg, key_value, |
| { |
| {STATUS_KEY, FeatureGroup::REQUIRED}, |
| {REASON_KEY}, |
| {BODY_KEY} |
| }); |
| } else { |
| return Errata(S_ERROR, R"(Value for "{}" key at {} is must be a scalar, a list, or a map and is not.)", KEY, key_value.Mark()); |
| } |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing value at {} in "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {{}, std::move(errata)}; |
| } |
| |
| self->_reason_idx = self->_fg.index_of(REASON_KEY); |
| self->_body_idx = self->_fg.index_of(BODY_KEY); |
| errata.note(self->load_status()); |
| |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing value at {} in "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {{}, std::move(errata)}; |
| } |
| |
| return {std::move(handle), {}}; |
| } |
| // --- |
| /* ------------------------------------------------------------------------------------ */ |
| class Do_remap_redirect : public Directive |
| { |
| using self_type = Do_remap_redirect; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static inline const HookMask HOOKS{MaskFor(Hook::REMAP)}; ///< Valid hooks for directive. |
| static inline const std::string KEY = "remap-redirect"; ///< Directive name. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| }; |
| |
| Rv<Directive::Handle> |
| Do_remap_redirect::load(Config &, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &, YAML::Node) |
| { |
| return Handle{new self_type}; |
| } |
| |
| Errata |
| Do_remap_redirect::invoke(Context &ctx) |
| { |
| if (ctx._remap_info) { |
| ctx._remap_info->redirect = 1; |
| ctx._remap_status = TSREMAP_DID_REMAP; |
| } |
| return {}; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Redirect. |
| /// Although this could technically be done "by hand", it's common enough to justify |
| /// a specific directive. |
| class Do_redirect : public Directive |
| { |
| using self_type = Do_redirect; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| |
| /// Per context information, used for fix up on proxy response hook. |
| /// -- doc Do_redirect::CtxInfo |
| struct CtxInfo { |
| TextView _location; ///< Redirect target location. |
| TextView _reason; ///< Status text. |
| }; |
| |
| public: |
| inline static const std::string KEY = "redirect"; ///< Directive name. |
| inline static const std::string STATUS_KEY = "status"; ///< Key for status value. |
| inline static const std::string REASON_KEY = "reason"; ///< Key for reason value. |
| inline static const std::string LOCATION_KEY = "location"; ///< Key for location value. |
| inline static const std::string BODY_KEY = "body"; ///< Key for body. |
| |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| /// Need to do fixups on a later hook. |
| static constexpr Hook FIXUP_HOOK = Hook::PRSP; |
| /// Status code to use if not specified. |
| static const int DEFAULT_STATUS = TS_HTTP_STATUS_MOVED_PERMANENTLY; |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| /// Configuration level initialization. |
| static Errata cfg_init(Config &cfg, CfgStaticData const *rtti); |
| |
| protected: |
| using index_type = FeatureGroup::index_type; |
| |
| FeatureGroup _fg; ///< Support cross references among the keys. |
| |
| int _status = 0; ///< Return status is literal, 0 => extract at runtime. |
| index_type _status_idx; ///< Return status. |
| index_type _reason_idx; ///< Status reason text. |
| index_type _location_idx; ///< Location field value. |
| index_type _body_idx; ///< Body content of respons. |
| /// Bounce from fixup hook directive back to @a this. |
| Directive::Handle _set_location{new LambdaDirective([this](Context &ctx) -> Errata { return this->fixup(ctx); })}; |
| |
| Errata load_status(); |
| |
| /// Do post invocation fixup. |
| Errata fixup(Context &ctx); |
| }; |
| |
| const HookMask Do_redirect::HOOKS{MaskFor(Hook::PRE_REMAP, Hook::REMAP)}; |
| |
| Errata |
| Do_redirect::cfg_init(Config &cfg, CfgStaticData const *) |
| { |
| cfg.reserve_slot(FIXUP_HOOK); // needed to fix up "Location" field in proxy response. |
| return {}; |
| } |
| // -- doc Do_redirect::cfg_init |
| |
| Errata |
| Do_redirect::invoke(Context &ctx) |
| { |
| auto ctx_info = ctx.obtain_named_object<CtxInfo>(KEY); |
| |
| // If the Location view is empty, it hasn't been set and therefore the clean up hook |
| // hasn't been set either, so need to do that. |
| bool need_hook_p = ctx_info->_location.empty(); |
| |
| // Finalize the location and stash it in context storage. |
| Feature location = _fg.extract(ctx, _location_idx); |
| if (location.index() == IndexFor(STRING)) { |
| ctx.commit(location); |
| ctx_info->_location = std::get<IndexFor(STRING)>(location); |
| } else { |
| return Errata(S_ERROR, "{} directive - '{}' was not a string as required.", KEY, LOCATION_KEY); |
| } |
| |
| // Set the status to prevent the upstream request. |
| if (_status) { |
| ctx._txn.status_set(static_cast<TSHttpStatus>(_status)); |
| } else { |
| Feature value = _fg.extract(ctx, _status_idx); |
| auto &&[status, errata]{value.as_integer(DEFAULT_STATUS)}; |
| if (!(100 <= status && status <= 599)) { |
| status = DEFAULT_STATUS; |
| } |
| ctx._txn.status_set(static_cast<TSHttpStatus>(status)); |
| } |
| // Set the reason. |
| if (_reason_idx != FeatureGroup::INVALID_IDX) { |
| auto reason{_fg.extract(ctx, _reason_idx)}; |
| if (reason.index() == IndexFor(STRING)) { |
| ctx.commit(reason); |
| ctx_info->_reason = std::get<IndexFor(STRING)>(reason); |
| } |
| } |
| // Set the body. |
| if (_body_idx != FeatureGroup::INVALID_IDX) { |
| auto body{_fg.extract(ctx, _body_idx)}; |
| ctx._txn.error_body_set(std::get<IndexFor(STRING)>(body), "text/html"); |
| } |
| // Arrange for fixup to get invoked. |
| if (need_hook_p) { |
| ctx.on_hook_do(FIXUP_HOOK, _set_location.get()); |
| } |
| return {}; |
| } |
| |
| Errata |
| Do_redirect::fixup(Context &ctx) |
| { |
| if (auto ctx_info = ctx.named_object<CtxInfo>(KEY); ctx_info) { |
| auto hdr{ctx.proxy_rsp_hdr()}; |
| |
| auto field{hdr.field_obtain(ts::HTTP_FIELD_LOCATION)}; |
| field.assign(ctx_info->_location); |
| |
| if (!ctx_info->_reason.empty()) { |
| hdr.reason_set(ctx_info->_reason); |
| } |
| } |
| return {}; |
| } |
| |
| Errata |
| Do_redirect::load_status() |
| { |
| _status_idx = _fg.index_of(STATUS_KEY); |
| |
| if (_status_idx == FeatureGroup::INVALID_IDX) { // not present, use default value. |
| _status = DEFAULT_STATUS; |
| return {}; |
| } |
| |
| FeatureGroup::ExprInfo &info = _fg[_status_idx]; |
| |
| if (info._expr.is_literal()) { |
| auto &&[status, errata]{std::get<Feature>(info._expr._raw).as_integer(DEFAULT_STATUS)}; |
| _status = status; |
| if (!errata.is_ok()) { |
| errata.note("While load key '{}' for directive '{}'", STATUS_KEY, KEY); |
| return std::move(errata); |
| } |
| if (!(100 <= status && status <= 599)) { |
| return Errata(S_ERROR, R"(Value for '{}' key in {} directive is not a positive integer 100..599 as required.)", STATUS_KEY, |
| KEY); |
| } |
| } else { |
| auto rtype = info._expr.result_type(); |
| if (rtype != STRING && rtype != INTEGER) { |
| return Errata(S_ERROR, R"({} is not an integer nor string as required.)", STATUS_KEY); |
| } |
| } |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_redirect::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| Handle handle{new self_type}; |
| Errata errata; |
| auto self = static_cast<self_type *>(handle.get()); |
| if (key_value.IsScalar()) { |
| errata = self->_fg.load_as_scalar(cfg, key_value, LOCATION_KEY); |
| } else if (key_value.IsSequence()) { |
| errata = self->_fg.load_as_tuple(cfg, key_value, |
| { |
| {STATUS_KEY, FeatureGroup::REQUIRED}, |
| {LOCATION_KEY, FeatureGroup::REQUIRED} |
| }); |
| } else if (key_value.IsMap()) { |
| errata = self->_fg.load(cfg, key_value, |
| { |
| {LOCATION_KEY, FeatureGroup::REQUIRED}, |
| {STATUS_KEY}, |
| {REASON_KEY}, |
| {BODY_KEY} |
| }); |
| } else { |
| return Errata(S_ERROR, R"(Value for "{}" key at {} is must be a scalar, a list, or a map and is not.)", KEY, key_value.Mark()); |
| } |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing value at {} in "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {{}, std::move(errata)}; |
| } |
| |
| self->_reason_idx = self->_fg.index_of(REASON_KEY); |
| self->_body_idx = self->_fg.index_of(BODY_KEY); |
| self->_location_idx = self->_fg.index_of(LOCATION_KEY); |
| errata.note(self->load_status()); |
| |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing value at {} in "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {{}, std::move(errata)}; |
| } |
| |
| return {std::move(handle), {}}; |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Send a debug message. |
| class Do_debug : public Directive |
| { |
| using self_type = Do_debug; |
| using super_type = Directive; |
| |
| public: |
| static const std::string KEY; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _tag; |
| Expr _msg; |
| |
| Do_debug(Expr &&tag, Expr &&msg); |
| }; |
| |
| const std::string Do_debug::KEY{"debug"}; |
| const HookMask Do_debug::HOOKS{MaskFor({Hook::POST_LOAD, Hook::TXN_START, Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, |
| Hook::PRE_REMAP, Hook::POST_REMAP, Hook::REMAP})}; |
| |
| Do_debug::Do_debug(Expr &&tag, Expr &&msg) : _tag(std::move(tag)), _msg(std::move(msg)) {} |
| |
| Errata |
| Do_debug::invoke(Context &ctx) |
| { |
| [[maybe_unused]] TextView tag = ctx.extract_view(_tag, {Context::EX_COMMIT, Context::EX_C_STR}); |
| TextView msg = ctx.extract_view(_msg); |
| TS_DBG("%.*s", static_cast<int>(msg.size()), msg.data()); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_debug::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| if (key_value.IsScalar()) { |
| auto &&[msg_fmt, msg_errata] = cfg.parse_expr(key_value); |
| if (!msg_errata.is_ok()) { |
| msg_errata.note(R"(While parsing message at {} for "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {{}, std::move(msg_errata)}; |
| } |
| return {Handle{new self_type{Expr{Config::PLUGIN_TAG}, std::move(msg_fmt)}}, {}}; |
| } else if (key_value.IsSequence()) { |
| if (key_value.size() > 2) { |
| return Errata(S_ERROR, R"(Value for "{}" key at {} is not a list of two strings as required.)", KEY, key_value.Mark()); |
| } else if (key_value.size() < 1) { |
| return Errata(S_ERROR, R"(The list value for "{}" key at {} does not have at least one string as required.)", KEY, |
| key_value.Mark()); |
| } |
| auto &&[tag_expr, tag_errata] = cfg.parse_expr(key_value[0]); |
| if (!tag_errata.is_ok()) { |
| tag_errata.note(R"(While parsing tag at {} for "{}" directive at {}.)", key_value[0].Mark(), KEY, drtv_node.Mark()); |
| return std::move(tag_errata); |
| } |
| auto &&[msg_expr, msg_errata] = cfg.parse_expr(key_value[1]); |
| if (!tag_errata.is_ok()) { |
| tag_errata.note(R"(While parsing message at {} for "{}" directive at {}.)", key_value[1].Mark(), KEY, drtv_node.Mark()); |
| return std::move(tag_errata); |
| } |
| return Handle(new self_type(std::move(tag_expr), std::move(msg_expr))); |
| } |
| return Errata(S_ERROR, R"(Value for "{}" key at {} is not a string or a list of strings as required.)", KEY, key_value.Mark()); |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /// Log an Error message. |
| class Do_error : public Directive |
| { |
| using self_type = Do_error; |
| using super_type = Directive; |
| |
| public: |
| static inline const std::string KEY{"error"}; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _msg; |
| |
| Do_error(Expr &&msg); |
| }; |
| |
| const HookMask Do_error::HOOKS{MaskFor({Hook::POST_LOAD, Hook::TXN_START, Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, |
| Hook::PRE_REMAP, Hook::POST_REMAP, Hook::REMAP})}; |
| |
| Do_error::Do_error(Expr &&msg) : _msg(std::move(msg)) {} |
| |
| Errata |
| Do_error::invoke(Context &ctx) |
| { |
| TextView msg = ctx.extract_view(_msg); |
| ts::Log_Error(msg); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_error::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[msg_fmt, msg_errata] = cfg.parse_expr(key_value); |
| if (!msg_errata.is_ok()) { |
| msg_errata.note(R"(While parsing message at {} for "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {{}, std::move(msg_errata)}; |
| } |
| return {Handle{new self_type{std::move(msg_fmt)}}}; |
| } |
| |
| /// Log an notify message. |
| class Do_note : public Directive |
| { |
| using self_type = Do_note; ///< Self reference type. |
| using super_type = Directive; ///< Super type. |
| public: |
| static inline const std::string KEY{"note"}; ///< Name of directive. |
| /// Valid hooks for this directive. |
| static inline const HookMask HOOKS{MaskFor(Hook::POST_LOAD, Hook::TXN_START, Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, |
| Hook::PRE_REMAP, Hook::POST_REMAP, Hook::REMAP)}; |
| |
| /// Runtime invocation. |
| Errata invoke(Context &ctx) override; |
| |
| /// Load from configuration. |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _msg; ///< Message to log. |
| |
| /** Constructor. |
| * |
| * @param msg Parsed feature expression for message. |
| */ |
| Do_note(Expr &&msg); |
| }; |
| // -- doc end Do_note |
| |
| Do_note::Do_note(Expr &&msg) : _msg(std::move(msg)) {} |
| |
| // -- doc note::invoke |
| Errata |
| Do_note::invoke(Context &ctx) |
| { |
| TextView msg = ctx.extract_view(_msg); |
| ts::Log_Note(msg); |
| return {}; |
| } |
| // -- doc note::invoke |
| |
| // -- doc note::load |
| Rv<Directive::Handle> |
| Do_note::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[msg_fmt, msg_errata] = cfg.parse_expr(key_value); |
| if (!msg_errata.is_ok()) { |
| msg_errata.note(R"(While parsing message at {} for "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return std::move(msg_errata); |
| } |
| return Handle{new self_type{std::move(msg_fmt)}}; |
| } |
| // -- doc note::load |
| |
| /// Log an warning message. |
| class Do_warning : public Directive |
| { |
| using self_type = Do_warning; |
| using super_type = Directive; |
| |
| public: |
| static inline const std::string KEY{"warning"}; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _msg; |
| |
| Do_warning(Expr &&msg); |
| }; |
| |
| const HookMask Do_warning::HOOKS{MaskFor({Hook::POST_LOAD, Hook::TXN_START, Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, |
| Hook::PRE_REMAP, Hook::POST_REMAP, Hook::REMAP})}; |
| |
| Do_warning::Do_warning(Expr &&msg) : _msg(std::move(msg)) {} |
| |
| Errata |
| Do_warning::invoke(Context &ctx) |
| { |
| TextView msg = ctx.extract_view(_msg); |
| ts::Log_Warning(msg); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_warning::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[msg_fmt, msg_errata] = cfg.parse_expr(key_value); |
| if (!msg_errata.is_ok()) { |
| msg_errata.note(R"(While parsing message at {} for "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {{}, std::move(msg_errata)}; |
| } |
| return {Handle{new self_type{std::move(msg_fmt)}}}; |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set the cache key. |
| class Do_cache_key : public Directive |
| { |
| using self_type = Do_cache_key; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _fmt; ///< Cache key. |
| |
| Do_cache_key(Expr &&fmt) : _fmt(std::move(fmt)) {} |
| }; |
| |
| const std::string Do_cache_key::KEY{"cache-key"}; |
| const HookMask Do_cache_key::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| Errata |
| Do_cache_key::invoke(Context &ctx) |
| { |
| auto value = ctx.extract(_fmt); |
| ctx._txn.cache_key_assign(std::get<IndexFor(STRING)>(value)); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_cache_key::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[fmt, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| |
| return Handle(new self_type(std::move(fmt))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set a transaction configuration variable override. |
| class Do_txn_conf : public Directive |
| { |
| using self_type = Do_txn_conf; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Value for override. |
| ts::TxnConfigVar *_var = nullptr; |
| |
| Do_txn_conf(Expr &&fmt, ts::TxnConfigVar *var) : _expr(std::move(fmt)), _var(var) {} |
| }; |
| |
| const std::string Do_txn_conf::KEY{"txn-conf"}; |
| const HookMask Do_txn_conf::HOOKS{ |
| MaskFor({Hook::TXN_START, Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP, Hook::PREQ})}; |
| |
| Errata |
| Do_txn_conf::invoke(Context &ctx) |
| { |
| auto value = ctx.extract(_expr); |
| if (value.index() == IndexFor(INTEGER)) { |
| ctx._txn.override_assign(*_var, std::get<IndexFor(INTEGER)>(value)); |
| } else if (value.index() == IndexFor(BOOLEAN)) { |
| ctx._txn.override_assign(*_var, std::get<IndexFor(BOOLEAN)>(value) ? 1L : 0L); |
| } else if (value.index() == IndexFor(STRING)) { |
| // Unfortunately although the interface doesn't appear to require C strings, in practice some of |
| // the string overridables do (such as client cert file path). |
| auto str = ctx.localize_as_c_str(std::get<IndexFor(STRING)>(value)); |
| ctx._txn.override_assign(*_var, str); |
| } else if (value.index() == IndexFor(FLOAT)) { |
| ctx._txn.override_assign(*_var, std::get<IndexFor(FLOAT)>(value)); |
| } |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_txn_conf::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &arg, |
| YAML::Node key_value) |
| { |
| auto txn_var = ts::HttpTxn::find_override(arg); |
| if (!txn_var) { |
| return Errata(S_ERROR, R"("{}" is not recognized as an overridable transaction configuration variable.)", arg); |
| } |
| if (txn_var->type() != TS_RECORDDATATYPE_INT && txn_var->type() != TS_RECORDDATATYPE_STRING && |
| txn_var->type() != TS_RECORDDATATYPE_FLOAT) { |
| return Errata(S_ERROR, R"("{}" is of type "{}" which is not currently supported.)", arg, |
| ts::TSRecordDataTypeNames[txn_var->type()]); |
| } |
| auto &&[fmt, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| |
| return Handle(new self_type(std::move(fmt), txn_var)); |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set the address for the upstream. |
| class Do_upstream_addr : public Directive |
| { |
| using self_type = Do_upstream_addr; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static inline const std::string KEY{"upstream-addr"}; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Address. |
| ts::TxnConfigVar *_var = nullptr; |
| |
| Do_upstream_addr(Expr &&expr) : _expr(std::move(expr)) {} |
| }; |
| |
| const HookMask Do_upstream_addr::HOOKS{MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP, Hook::PREQ})}; |
| |
| Errata |
| Do_upstream_addr::invoke(Context &ctx) |
| { |
| auto value = ctx.extract(_expr); |
| if (value.index() == IndexFor(IP_ADDR)) { |
| ctx._txn.set_upstream_addr(std::get<IndexFor(IP_ADDR)>(value)); |
| } |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_upstream_addr::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| |
| if (!expr.result_type().can_satisfy(IP_ADDR)) { |
| return Errata(S_ERROR, R"(Value for "{}" must be an IP address.)"); |
| } |
| |
| return Handle(new self_type(std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Set a transaction local variable. |
| class Do_var : public Directive |
| { |
| using self_type = Do_var; ///< Self reference type. |
| using super_type = Directive; ///< Parent type. |
| public: |
| static const std::string KEY; ///< Directive name. |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; ///< Runtime activation. |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| TextView _name; ///< Variable name. |
| Expr _value; ///< Value for variable. |
| |
| Do_var(TextView const &arg, Expr &&value) : _name(arg), _value(std::move(value)) {} |
| }; |
| |
| const std::string Do_var::KEY{"var"}; |
| const HookMask Do_var::HOOKS{ |
| MaskFor({Hook::CREQ, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP, Hook::PREQ, Hook::URSP, Hook::PRSP})}; |
| |
| Errata |
| Do_var::invoke(Context &ctx) |
| { |
| ctx.store_txn_var(_name, ctx.extract(_value)); |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_var::load(Config &cfg, CfgStaticData const *, YAML::Node, swoc::TextView const &, swoc::TextView const &arg, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata]{cfg.parse_expr(key_value)}; |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| |
| return Handle(new self_type(cfg.localize(arg), std::move(expr))); |
| } |
| /* ------------------------------------------------------------------------------------ */ |
| /// Internal transaction error control |
| class Do_txn_error : public Directive |
| { |
| using self_type = Do_txn_error; |
| using super_type = Directive; |
| |
| public: |
| static inline const std::string KEY{"txn-error"}; |
| /// Valid hooks for directive. |
| static inline const HookMask HOOKS{ |
| MaskFor({Hook::TXN_START, Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, Hook::PRE_REMAP, Hook::POST_REMAP, Hook::REMAP})}; |
| |
| Errata invoke(Context &ctx) override; |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; |
| |
| Do_txn_error(Expr &&msg); |
| }; |
| |
| Do_txn_error::Do_txn_error(Expr &&expr) : _expr(std::move(expr)) {} |
| |
| Errata |
| Do_txn_error::invoke(Context &ctx) |
| { |
| ctx._global_status = ctx.extract(_expr).as_bool() ? TS_EVENT_HTTP_ERROR : TS_EVENT_HTTP_CONTINUE; |
| return {}; |
| } |
| |
| Rv<Directive::Handle> |
| Do_txn_error::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| auto &&[expr, errata] = cfg.parse_expr(key_value); |
| if (!errata.is_ok()) { |
| errata.note(R"(While parsing message at {} for "{}" directive at {}.)", key_value.Mark(), KEY, drtv_node.Mark()); |
| return {std::move(errata)}; |
| } |
| return {Handle{new self_type{std::move(expr)}}}; |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| /** @c with directive. |
| * |
| * This is a core directive that has lots of special properties. |
| */ |
| class Do_with : public Directive |
| { |
| using super_type = Directive; |
| using self_type = Do_with; |
| |
| public: |
| static const std::string KEY; |
| static const std::string SELECT_KEY; |
| static const std::string FOR_EACH_KEY; |
| static const std::string CONTINUE_KEY; |
| static const HookMask HOOKS; ///< Valid hooks for directive. |
| |
| Errata invoke(Context &ctx) override; |
| |
| /** Load from YAML node. |
| * |
| * @param cfg Configuration data. |
| * @param rtti Configuration level static data for this directive. |
| * @param drtv_node Node containing the directive. |
| * @param name Name from key node tag. |
| * @param arg Arg from key node tag. |
| * @param key_value Value for directive @a KEY |
| * @return A directive, or errors on failure. |
| */ |
| static Rv<Handle> load(Config &cfg, CfgStaticData const *rtti, YAML::Node drtv_node, swoc::TextView const &name, |
| swoc::TextView const &arg, YAML::Node key_value); |
| |
| protected: |
| Expr _expr; ///< Feature expression |
| Directive::Handle _do; ///< Explicit actions. |
| |
| union { |
| uint32_t all = 0; |
| struct { |
| unsigned for_each_p : 1; ///< Direct action is per tuple element. |
| unsigned continue_p : 1; ///< Continue with directives after this - want default to be 0. |
| } f; |
| } _opt; |
| |
| /// A single case in the select. |
| struct Case { |
| Comparison::Handle _cmp; ///< Comparison to perform. |
| Directive::Handle _do; ///< Directives to execute. |
| }; |
| using CaseGroup = std::vector<Case>; |
| CaseGroup _cases; ///< List of cases for the select. |
| |
| Do_with() = default; |
| |
| Errata load_case(Config &cfg, YAML::Node node); |
| }; |
| |
| const std::string Do_with::KEY{"with"}; |
| const std::string Do_with::SELECT_KEY{"select"}; |
| const std::string Do_with::FOR_EACH_KEY{"for-each"}; |
| const std::string Do_with::CONTINUE_KEY{"continue"}; |
| |
| const HookMask Do_with::HOOKS{MaskFor({Hook::POST_LOAD, Hook::TXN_START, Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, |
| Hook::PRE_REMAP, Hook::POST_REMAP, Hook::REMAP})}; |
| |
| Errata |
| Do_with::invoke(Context &ctx) |
| { |
| Feature feature{ctx.extract(_expr)}; |
| ctx.commit(feature); |
| Feature save{ctx._active}; |
| ctx._active = feature; |
| |
| if (_do) { |
| if (_opt.f.for_each_p) { |
| ctx._active_ext = feature; |
| while (!is_nil(feature)) { |
| ctx._active = car(feature); |
| ctx.mark_terminal(false); |
| _do->invoke(ctx); |
| cdr(feature); |
| } |
| clear(feature); |
| ctx._active_ext = NIL_FEATURE; |
| // Iteration can potentially modify the extracted feature value, so if there are comparisons |
| // reset the feature. |
| if (!_cases.empty()) { |
| ctx._active = feature = ctx.extract(_expr); |
| } |
| } else { |
| ctx.mark_terminal(false); |
| _do->invoke(ctx); |
| } |
| } |
| |
| ctx.mark_terminal(false); // default is continue on. |
| for (auto const &c : _cases) { |
| if (!c._cmp || (*c._cmp)(ctx, feature)) { |
| if (c._do) { |
| c._do->invoke(ctx); |
| } |
| ctx.mark_terminal(!_opt.f.continue_p); // successful compare, mark terminal. |
| break; |
| } |
| } |
| // Need to restore to previous state if nothing matched. |
| clear(ctx._active); |
| ctx._active = save; |
| return {}; |
| } |
| |
| swoc::Rv<Directive::Handle> |
| Do_with::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| // Need to parse this first, so the feature type can be determined. |
| auto &&[expr, errata] = cfg.parse_expr(key_value); |
| |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| |
| auto *self = new self_type; |
| Handle handle(self); // for return, and cleanup in case of error. |
| self->_expr = std::move(expr); |
| auto f_scope = cfg.feature_scope(self->_expr.result_type()); |
| |
| YAML::Node select_node{drtv_node[SELECT_KEY]}; |
| if (select_node) { |
| if (select_node.IsMap()) { |
| errata = self->load_case(cfg, select_node); |
| if (!errata.is_ok()) { |
| return std::move(errata); |
| } |
| } else if (select_node.IsSequence()) { |
| for (YAML::Node child : select_node) { |
| errata = (self->load_case(cfg, child)); |
| if (!errata.is_ok()) { |
| errata.note(R"(While loading "{}" directive at {} in "{}" at {}.)", KEY, drtv_node.Mark(), SELECT_KEY, |
| select_node.Mark()); |
| return std::move(errata); |
| } |
| } |
| } else { |
| return Errata(S_ERROR, R"(The value for "{}" at {} in "{}" directive at {} is not a list or object.")", SELECT_KEY, |
| select_node.Mark(), KEY, drtv_node.Mark()); |
| } |
| } |
| |
| YAML::Node continue_node{drtv_node[CONTINUE_KEY]}; |
| if (continue_node) { |
| self->_opt.f.continue_p = true; |
| } |
| |
| YAML::Node do_node{drtv_node[DO_KEY]}; |
| YAML::Node for_each_node{drtv_node[FOR_EACH_KEY]}; |
| if (do_node && for_each_node) { |
| return Errata(S_ERROR, R"("{}" directive cannot have both "{}" and "{}" as keys - {}.)", DO_KEY, FOR_EACH_KEY, |
| drtv_node.Mark()); |
| } else if (do_node) { |
| auto &&[do_handle, errata]{cfg.parse_directive(do_node)}; |
| if (errata.is_ok()) { |
| self->_do = std::move(do_handle); |
| } else { |
| errata.note(R"(While parsing "{}" key at {} in selection case at {}.)", DO_KEY, do_node.Mark(), drtv_node.Mark()); |
| return std::move(errata); |
| } |
| } else if (for_each_node) { |
| auto &&[fe_handle, errata]{cfg.parse_directive(for_each_node)}; |
| if (errata.is_ok()) { |
| self->_do = std::move(fe_handle); |
| self->_opt.f.for_each_p = true; |
| } else { |
| errata.note(R"(While parsing "{}" key at {} in selection case at {}.)", FOR_EACH_KEY, for_each_node.Mark(), drtv_node.Mark()); |
| return std::move(errata); |
| } |
| } |
| return handle; |
| } |
| |
| Errata |
| Do_with::load_case(Config &cfg, YAML::Node node) |
| { |
| if (node.IsMap()) { |
| Case c; |
| YAML::Node do_node{node[DO_KEY]}; |
| // It's allowed to have no comparison, which is either an empty map or only a DO key. |
| // In that case the comparison always matches. |
| if (node.size() > 1 || (node.size() == 1 && !do_node)) { |
| auto f_scope = cfg.feature_scope(_expr.result_type()); |
| auto &&[cmp_handle, cmp_errata]{Comparison::load(cfg, node)}; |
| if (cmp_errata.is_ok()) { |
| c._cmp = std::move(cmp_handle); |
| } else { |
| return std::move(cmp_errata); |
| } |
| } |
| |
| if (do_node) { |
| auto c_scope = cfg.capture_scope((c._cmp ? c._cmp->rxp_group_count() : 0), node.Mark().line); |
| auto &&[handle, errata]{cfg.parse_directive(do_node)}; |
| if (errata.is_ok()) { |
| c._do = std::move(handle); |
| } else { |
| errata.note(R"(While parsing "{}" key at {} in selection case at {}.)", DO_KEY, do_node.Mark(), node.Mark()); |
| return std::move(errata); |
| } |
| } else { |
| c._do.reset(new NilDirective); |
| } |
| // Everything is fine, update the case load and return. |
| _cases.emplace_back(std::move(c)); |
| return {}; |
| } |
| return Errata(S_ERROR, R"(The value at {} for "{}" is not an object as required.")", node.Mark(), SELECT_KEY); |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| const std::string When::KEY{"when"}; |
| const HookMask When::HOOKS{ |
| MaskFor({Hook::CREQ, Hook::PREQ, Hook::URSP, Hook::PRSP, Hook::PRE_REMAP, Hook::REMAP, Hook::POST_REMAP})}; |
| |
| When::When(Hook hook_idx, Directive::Handle &&directive) : _hook(hook_idx), _directive(std::move(directive)) {} |
| |
| // Put the internal directive in the directive array for the specified hook. |
| Errata |
| When::invoke(Context &ctx) |
| { |
| return ctx.on_hook_do(_hook, _directive.get()); |
| } |
| |
| swoc::Rv<Directive::Handle> |
| When::load(Config &cfg, CfgStaticData const *, YAML::Node drtv_node, swoc::TextView const &, swoc::TextView const &, |
| YAML::Node key_value) |
| { |
| Errata zret; |
| if (Hook hook{HookName[key_value.Scalar()]}; hook != Hook::INVALID) { |
| if (YAML::Node do_node{drtv_node[DO_KEY]}; do_node) { |
| auto save = cfg._hook; |
| cfg._hook = hook; |
| auto &&[do_handle, do_errata]{cfg.parse_directive(do_node)}; |
| cfg._hook = save; |
| if (do_errata.is_ok()) { |
| cfg.reserve_slot(hook); |
| return {Handle{new self_type{hook, std::move(do_handle)}}, {}}; |
| } else { |
| zret.note(do_errata); |
| zret.note(R"(Failed to load directive in "{}" at {} in "{}" directive at {}.)", DO_KEY, do_node.Mark(), KEY, |
| key_value.Mark()); |
| } |
| } else { |
| zret.note(R"(The required "{}" key was not found in the "{}" directive at {}.")", DO_KEY, KEY, drtv_node.Mark()); |
| } |
| } else { |
| zret.note(R"(Invalid hook name "{}" in "{}" directive at {}.)", key_value.Scalar(), When::KEY, key_value.Mark()); |
| } |
| return {{}, std::move(zret)}; |
| } |
| |
| /* ------------------------------------------------------------------------------------ */ |
| |
| namespace |
| { |
| [[maybe_unused]] bool INITIALIZED = []() -> bool { |
| Config::define<When>(); |
| Config::define<Do_with>(); |
| |
| Config::define<Do_ua_req_field>(); |
| Config::define<Do_ua_req_url>(); |
| Config::define<Do_ua_req_url>("ua-url-host"_tv); // alias |
| Config::define<Do_ua_req_url_host>(); |
| Config::define<Do_ua_req_url_port>(); |
| Config::define<Do_ua_req_url_loc>(); |
| Config::define<Do_ua_req_scheme>(); |
| Config::define<Do_ua_req_host>(); |
| Config::define<Do_ua_req_port>(); |
| Config::define<Do_ua_req_loc>(); |
| Config::define<Do_ua_req_path>(); |
| Config::define<Do_ua_req_fragment>(); |
| |
| Config::define<Do_proxy_req_field>(); |
| Config::define<Do_proxy_req_url>(); |
| Config::define<Do_proxy_req_url_host>(); |
| Config::define<Do_proxy_req_url_port>(); |
| Config::define<Do_proxy_req_url_loc>(); |
| Config::define<Do_proxy_req_host>(); |
| Config::define<Do_proxy_req_port>(); |
| Config::define<Do_proxy_req_loc>(); |
| Config::define<Do_proxy_req_scheme>(); |
| Config::define<Do_proxy_req_path>(); |
| Config::define<Do_proxy_req_fragment>(); |
| |
| Config::define<Do_upstream_rsp_field>(); |
| Config::define<Do_upstream_rsp_status>(); |
| Config::define<Do_upstream_reason>(); |
| |
| Config::define<Do_upstream_addr>(); |
| |
| Config::define<Do_proxy_rsp_field>(); |
| Config::define<Do_proxy_rsp_status>(); |
| Config::define<Do_proxy_rsp_reason>(); |
| Config::define<Do_proxy_rsp_body>(); |
| |
| Config::define<Do_upstream_rsp_body>(); |
| |
| Config::define<Do_cache_key>(); |
| Config::define<Do_txn_conf>(); |
| Config::define<Do_redirect>(); |
| Config::define<Do_remap_redirect>(); |
| Config::define<Do_proxy_reply>(); |
| Config::define<Do_debug>(); |
| Config::define<Do_note>(); |
| Config::define<Do_warning>(); |
| Config::define<Do_error>(); |
| Config::define<Do_txn_error>(); |
| Config::define<Do_txn_debug>(); |
| Config::define<Do_var>(); |
| |
| Config::define<Do_apply_remap_rule>(); |
| Config::define<Do_did_remap>(); |
| |
| return true; |
| }(); |
| } // namespace |