LCOV - code coverage report
Current view: top level - model - Transformation.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.6 % 523 474
Test Date: 2026-04-17 17:21:26 Functions: 75.0 % 4 3

            Line data    Source code
       1              : /*
       2              :  ___________________________________________
       3              : |    _     ___                        _     |
       4              : |   | |   |__ \                      | |    |
       5              : |   | |__    ) |__ _  __ _  ___ _ __ | |_   |
       6              : |   | '_ \  / // _` |/ _` |/ _ \ '_ \| __|  |  HTTP/2 AGENT FOR MOCK TESTING
       7              : |   | | | |/ /| (_| | (_| |  __/ | | | |_   |  Version 0.0.z
       8              : |   |_| |_|____\__,_|\__, |\___|_| |_|\__|  |  https://github.com/testillano/h2agent
       9              : |                     __/ |                 |
      10              : |                    |___/                  |
      11              : |___________________________________________|
      12              : 
      13              : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
      14              : SPDX-License-Identifier: MIT
      15              : Copyright (c) 2021 Eduardo Ramos
      16              : 
      17              : Permission is hereby  granted, free of charge, to any  person obtaining a copy
      18              : of this software and associated  documentation files (the "Software"), to deal
      19              : in the Software  without restriction, including without  limitation the rights
      20              : to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
      21              : copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
      22              : furnished to do so, subject to the following conditions:
      23              : 
      24              : The above copyright notice and this permission notice shall be included in all
      25              : copies or substantial portions of the Software.
      26              : 
      27              : THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
      28              : IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
      29              : FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
      30              : AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
      31              : LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      32              : OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
      33              : SOFTWARE.
      34              : */
      35              : 
      36              : 
      37              : #include <ert/tracing/Logger.hpp>
      38              : 
      39              : #include <Transformation.hpp>
      40              : #include <functions.hpp>
      41              : 
      42              : #include <sstream>
      43              : #include <algorithm>
      44              : 
      45              : namespace h2agent
      46              : {
      47              : namespace model
      48              : {
      49              : 
      50              : namespace {
      51              : 
      52            0 : std::string validPrefixes(const std::vector<std::string> &prefixes) {
      53            0 :     std::string result;
      54            0 :     for (size_t i = 0; i < prefixes.size(); ++i) {
      55            0 :         if (i) result += ", ";
      56            0 :         result += prefixes[i];
      57              :     }
      58            0 :     return result;
      59            0 : }
      60              : 
      61              : const std::vector<std::string> VALID_SOURCE_PREFIXES = {
      62              :     "request", "response", "eraser", "math", "random", "randomset",
      63              :     "timestamp", "strftime", "recvseq", "sendseq",
      64              :     "var", "vault", "value", "serverEvent", "clientEvent",
      65              :     "inState", "txtFile", "binFile", "command"
      66              : };
      67              : 
      68              : const std::vector<std::string> VALID_TARGET_PREFIXES = {
      69              :     "response", "request", "var", "vault", "outState",
      70              :     "txtFile", "binFile", "udpSocket", "serverEvent", "clientEvent",
      71              :     "clientProvision", "break"
      72              : };
      73              : 
      74              : } // anonymous namespace
      75              : 
      76          835 : void Transformation::collectVariablePatterns(const std::string &str, std::map<std::string, std::string> &patterns) {
      77              : 
      78          835 :     static std::regex re("@\\{[^\\{\\}]*\\}", std::regex::optimize); // @{[^{}]*} with curly braces escaped
      79              :     // or: R"(@\{[^\{\}]*\})"
      80              : 
      81          835 :     std::string::const_iterator it(str.cbegin());
      82          835 :     std::smatch matches;
      83          835 :     std::string pattern;
      84          835 :     patterns.clear();
      85          848 :     while (std::regex_search(it, str.cend(), matches, re)) {
      86           13 :         it = matches.suffix().first;
      87           13 :         pattern = matches[0];
      88           13 :         patterns[pattern] = pattern.substr(2, pattern.size()-3); // @{foo} -> foo
      89              :     }
      90          835 : }
      91              : 
      92          277 : bool Transformation::load(const nlohmann::json &j) {
      93              : 
      94          277 :     bool collectFilterPatterns = false;
      95              : 
      96              :     // Mandatory
      97          277 :     auto source_it = j.find("source");
      98          277 :     std::string sourceSpec = *source_it;
      99              : 
     100          277 :     auto target_it = j.find("target");
     101          277 :     std::string targetSpec = *target_it;
     102              : 
     103              :     // Optional
     104          277 :     auto it = j.find("filter");
     105          277 :     filter_ = "";
     106          277 :     has_filter_ = false;
     107          277 :     if (it != j.end()) {
     108           71 :         has_filter_ = true;
     109              : 
     110              :         // [filter_type_]
     111              :         //   RegexCapture        regular expression literal -> [filter_rgx_]
     112              :         //   RegexReplace        regular expression literal (rgx) -> [filter_rgx_]  /  replace format (fmt) -> [filter_]
     113              :         //   Append              suffix value -> [filter_]
     114              :         //   Prepend             prefix value -> [filter_]
     115              :         //   Sum                 amount -> [filter_i_/filter_u_/filter_f_/filter_number_type_]
     116              :         //   Multiply            amount -> [filter_i_/filter_u_/filter_f_/filter_number_type_]
     117              :         //   ConditionVar        variable name -> [filter_]
     118              :         //   EqualTo             value -> [filter_]
     119              :         //   DifferentFrom       value -> [filter_]
     120              :         //   JsonConstraint      value -> [filter_object_]
     121              :         //   SchemaId            value -> [filter_]
     122              :         //   Split               size -> [filter_i_], count -> [filter_u_], sep -> [filter_], filler -> [filter_filler_], numeric -> [filter_number_type_]
     123              :         //   BaseConvert          in -> [filter_i_], out -> [filter_u_], capital -> [filter_number_type_]
     124              :         //   Strptime            fmt -> [filter_], unit -> [filter_number_type_] (0:s,1:ms,2:us,3:ns)
     125              :         //   Strftime            fmt -> [filter_], unit -> [filter_number_type_] (0:s,1:ms,2:us,3:ns)
     126              :         //   Size                (parameterless, string filter)
     127              : 
     128              :         // Parameterless string filters:
     129           71 :         if (it->is_string() && it->get<std::string>() == "Size") {
     130            3 :             filter_type_ = FilterType::Size;
     131              :         }
     132              :         else {
     133              : 
     134           68 :         auto f_it = it->find("RegexCapture");
     135              : 
     136              :         try {
     137           68 :             if (f_it != it->end()) {
     138            6 :                 filter_ = *f_it;
     139            6 :                 filter_rgx_.assign(filter_, std::regex::optimize);
     140            4 :                 filter_type_ = FilterType::RegexCapture;
     141              :             }
     142           62 :             else if ((f_it = it->find("RegexReplace")) != it->end()) {
     143            2 :                 filter_rgx_.assign(std::string(*(f_it->find("rgx"))), std::regex::optimize);
     144            2 :                 filter_ = *(f_it->find("fmt"));
     145            2 :                 filter_type_ = FilterType::RegexReplace;
     146              :             }
     147           60 :             else if ((f_it = it->find("Append")) != it->end()) {
     148            2 :                 filter_ = *f_it;
     149            2 :                 filter_type_ = FilterType::Append;
     150            2 :                 collectFilterPatterns = true;
     151              :             }
     152           58 :             else if ((f_it = it->find("Prepend")) != it->end()) {
     153            2 :                 filter_ = *f_it;
     154            2 :                 filter_type_ = FilterType::Prepend;
     155            2 :                 collectFilterPatterns = true;
     156              :             }
     157           56 :             else if ((f_it = it->find("Sum")) != it->end()) {
     158            6 :                 if (f_it->is_number_unsigned()) { // first unsigned, because positive would be integer
     159            4 :                     filter_u_ = *f_it;
     160            4 :                     filter_number_type_ = 1 ;
     161              :                 }
     162            2 :                 else if (f_it->is_number_integer()) {
     163            1 :                     filter_i_ = *f_it;
     164            1 :                     filter_number_type_ = 0 ;
     165              :                 }
     166            1 :                 else if (f_it->is_number_float()) {
     167            1 :                     filter_f_ = *f_it;
     168            1 :                     filter_number_type_ = 2 ;
     169              :                 }
     170            6 :                 filter_type_ = FilterType::Sum;
     171              :             }
     172           50 :             else if ((f_it = it->find("Multiply")) != it->end()) {
     173            3 :                 if (f_it->is_number_unsigned()) { // first unsigned, because positive would be integer
     174            1 :                     filter_u_ = *f_it;
     175            1 :                     filter_number_type_ = 1 ;
     176              :                 }
     177            2 :                 else if (f_it->is_number_integer()) {
     178            1 :                     filter_i_ = *f_it;
     179            1 :                     filter_number_type_ = 0 ;
     180              :                 }
     181            1 :                 else if (f_it->is_number_float()) {
     182            1 :                     filter_f_ = *f_it;
     183            1 :                     filter_number_type_ = 2 ;
     184              :                 }
     185            3 :                 filter_type_ = FilterType::Multiply;
     186              :             }
     187           47 :             else if ((f_it = it->find("ConditionVar")) != it->end()) {
     188            6 :                 filter_ = *f_it;
     189            6 :                 filter_type_ = FilterType::ConditionVar;
     190              :             }
     191           41 :             else if ((f_it = it->find("EqualTo")) != it->end()) {
     192            6 :                 filter_ = *f_it;
     193            6 :                 filter_type_ = FilterType::EqualTo;
     194            6 :                 collectFilterPatterns = true;
     195              :             }
     196           35 :             else if ((f_it = it->find("DifferentFrom")) != it->end()) {
     197            3 :                 filter_ = *f_it;
     198            3 :                 filter_type_ = FilterType::DifferentFrom;
     199            3 :                 collectFilterPatterns = true;
     200              :             }
     201           32 :             else if ((f_it = it->find("JsonConstraint")) != it->end()) {
     202            3 :                 filter_object_ = *f_it;
     203            3 :                 filter_type_ = FilterType::JsonConstraint;
     204              :             }
     205           29 :             else if ((f_it = it->find("SchemaId")) != it->end()) {
     206            2 :                 filter_ = *f_it;
     207            2 :                 filter_type_ = FilterType::SchemaId;
     208              :             }
     209           27 :             else if ((f_it = it->find("Split")) != it->end()) {
     210            6 :                 auto size_it = f_it->find("size");
     211            6 :                 auto count_it = f_it->find("count");
     212            6 :                 auto sep_it = f_it->find("sep");
     213            6 :                 auto filler_it = f_it->find("filler");
     214            6 :                 auto numeric_it = f_it->find("numeric");
     215            6 :                 filter_i_ = (size_it != f_it->end()) ? size_it->get<std::int64_t>() : 2;
     216            6 :                 filter_u_ = (count_it != f_it->end()) ? count_it->get<std::uint64_t>() : 4;
     217           11 :                 filter_ = (sep_it != f_it->end()) ? sep_it->get<std::string>() : ".";
     218           11 :                 filter_filler_ = (filler_it != f_it->end()) ? filler_it->get<std::string>() : "0";
     219            6 :                 filter_number_type_ = (numeric_it != f_it->end() && numeric_it->get<bool>()) ? 1 : 0;
     220            6 :                 filter_type_ = FilterType::Split;
     221              :             }
     222           21 :             else if ((f_it = it->find("BaseConvert")) != it->end()) {
     223            6 :                 filter_i_ = f_it->find("in")->get<std::int64_t>();
     224            6 :                 filter_u_ = f_it->find("out")->get<std::uint64_t>();
     225            6 :                 auto capital_it = f_it->find("capital");
     226            6 :                 filter_number_type_ = (capital_it != f_it->end() && capital_it->get<bool>()) ? 1 : 0;
     227            6 :                 filter_type_ = FilterType::BaseConvert;
     228              :             }
     229           15 :             else if ((f_it = it->find("Strptime")) != it->end()) {
     230            4 :                 filter_ = f_it->find("fmt")->get<std::string>();
     231            7 :                 std::string unit = (f_it->find("unit") != f_it->end()) ? f_it->find("unit")->get<std::string>() : "s";
     232            4 :                 filter_number_type_ = (unit == "ms") ? 1 : (unit == "us") ? 2 : (unit == "ns") ? 3 : 0;
     233            4 :                 filter_type_ = FilterType::FStrptime;
     234            4 :             }
     235           11 :             else if ((f_it = it->find("Strftime")) != it->end()) {
     236            4 :                 filter_ = f_it->find("fmt")->get<std::string>();
     237            7 :                 std::string unit = (f_it->find("unit") != f_it->end()) ? f_it->find("unit")->get<std::string>() : "s";
     238            4 :                 filter_number_type_ = (unit == "ms") ? 1 : (unit == "us") ? 2 : (unit == "ns") ? 3 : 0;
     239            4 :                 filter_type_ = FilterType::FStrftime;
     240            4 :             }
     241            7 :             else if ((f_it = it->find("RegexKey")) != it->end()) {
     242            7 :                 filter_ = *f_it;
     243            7 :                 filter_rgx_.assign(filter_, std::regex::optimize);
     244            7 :                 filter_type_ = FilterType::RegexKey;
     245              :             }
     246              :         }
     247            2 :         catch (std::regex_error &e) {
     248            2 :             ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
     249            2 :             return false;
     250            2 :         }
     251              :         } // else (object filters)
     252              :     }
     253              : 
     254              :     // Interpret source/target:
     255              : 
     256              :     // SOURCE (enum SourceType { RequestUri = 0, RequestUriPath, RequestUriParam, RequestBody, ResponseBody, RequestHeader, Eraser,
     257              :     //                           Math, Random, RandomSet, Timestamp, Strftime, Recvseq, SVar, SGVar, Value, ServerEvent, InState };)
     258          275 :     source_ = ""; // empty by default (-), as many cases are only work modes and no parameters(+) are included in their transformation configuration
     259              : 
     260              :     // Source specifications:
     261              :     // - request.uri: whole `url-decoded` request *URI* (path together with possible query parameters). This is the unmodified original *URI*, not necessarily the same as the classification *URI*.
     262              :     // - request.uri.path: `url-decoded` request *URI* path part.
     263              :     // + request.uri.param.<name>: request URI specific parameter `<name>`.
     264              :     // - request.body: request body document.
     265              :     // + request.body./<node1>/../<nodeN>: request body node path.
     266              :     // + request.header.<hname>: request header component (i.e. *content-type*).
     267              :     // - eraser: this is used to indicate that the *target* specified (next section) must be removed or reset.
     268              :     // + math.`<expression>`: this source is based in Arash Partow's exprtk math library compilation.
     269              :     // + random.<min>.<max>: integer number in range `[min, max]`. Negatives allowed, i.e.: `"-3.+4"`.
     270              :     // + timestamp.<unit>: UNIX epoch time in `s` (seconds), `ms` (milliseconds), `us` (microseconds) or `ns` (nanoseconds).
     271              :     // + strftime.<format>: current date/time formatted by [strftime](https://www.cplusplus.com/reference/ctime/strftime/).
     272              :     // - recvseq: sequence id increased for every mock reception (starts on *1* when the *h2agent* is started).
     273              :     // + var.<id>: general purpose variable.
     274              :     // + vault.<id>: general purpose vault entry.
     275              :     // - value.<value>: free string value. Even convertible types are allowed, for example: integer string, unsigned integer string, float number string, boolean string (true if non-empty string), will be converted to the target type.
     276              :     // - inState: current processing state.
     277              :     // + serverEvent.`<server event address in query parameters format>`: access server context indexed by request *method* (`requestMethod`), *URI* (`requestUri`), events *number* (`eventNumber`) and events number *path* (`eventPath`).
     278              :     // + txtFile.`<path>`: reads text content from file with the path provided.
     279              :     // + binFile.`<path>`: reads binary content from file with the path provided.
     280              :     // + command.`<command>`: executes command on process shell and captures the standard output.
     281              : 
     282              :     // Regex needed:
     283          275 :     static std::regex requestUriParam("^request.uri.param.(.*)", std::regex::optimize); // no need to escape dots as this is validated in schema
     284          275 :     static std::regex requestBodyNode("^request.body.(.*)", std::regex::optimize);
     285          275 :     static std::regex responseBodyNode("^response.body.(.*)", std::regex::optimize);
     286          275 :     static std::regex requestHeader("^request.header.(.*)", std::regex::optimize);
     287          275 :     static std::regex math("^math.(.*)", std::regex::optimize);
     288          275 :     static std::regex random("^random\\.([-+]{0,1}[0-9]+)\\.([-+]{0,1}[0-9]+)$", std::regex::optimize); // no need to validate min/max as it was done at schema
     289          275 :     static std::regex randomSet("^randomset.(.*)", std::regex::optimize);
     290          275 :     static std::regex timestamp("^timestamp.(.*)", std::regex::optimize); // no need to validate s/ms/us/ns as it was done at schema
     291          275 :     static std::regex strftime("^strftime.(.*)", std::regex::optimize); // free format, errors captured
     292          275 :     static std::regex varId("^var.(.*)", std::regex::optimize);
     293          275 :     static std::regex gvarJsonObject("^vault\\.([^.]+)\\.json\\.object(?:\\.(/.+))?$", std::regex::optimize);
     294          275 :     static std::regex gvarJsonJsonString("^vault\\.([^.]+)\\.json\\.jsonstring(?:\\.(/.+))?$", std::regex::optimize);
     295          275 :     static std::regex gvarId("^vault\\.([^.]+)(?:\\.(/.+))?$", std::regex::optimize);
     296          275 :     static std::regex value("^value.([.\\s\\S]*)", std::regex::optimize); // added support for special characters: \n \t \r
     297          275 :     static std::regex serverEvent("^serverEvent.(.*)", std::regex::optimize);
     298          275 :     static std::regex clientEvent("^clientEvent.(.*)", std::regex::optimize);
     299          275 :     static std::regex responseHeader_source("^response.header.(.*)", std::regex::optimize);
     300          275 :     static std::regex txtFile("^txtFile.(.*)", std::regex::optimize);
     301          275 :     static std::regex binFile("^binFile.(.*)", std::regex::optimize);
     302          275 :     static std::regex command("^command.(.*)", std::regex::optimize);
     303              : 
     304          275 :     std::smatch matches; // to capture regex group(s)
     305              :     // BE CAREFUL!: https://stackoverflow.com/a/51709911/2576671
     306              :     // In this case, it is not a problem, as we store the match from sourceSpec or targetSpec before changing them.
     307              : 
     308              :     // no need to try (controlled regex)
     309              :     //try {
     310          275 :     if (sourceSpec == "request.uri") {
     311            5 :         source_type_ = SourceType::RequestUri;
     312              :     }
     313          270 :     else if (sourceSpec == "request.uri.path") {
     314            3 :         source_type_ = SourceType::RequestUriPath;
     315              :     }
     316          267 :     else if (std::regex_match(sourceSpec, matches, requestUriParam)) { // parameter name
     317            2 :         source_ = matches.str(1);
     318            2 :         source_type_ = SourceType::RequestUriParam;
     319              :     }
     320          265 :     else if (sourceSpec == "request.body") { // whole document
     321           16 :         source_type_ = SourceType::RequestBody;
     322              :     }
     323          249 :     else if (std::regex_match(sourceSpec, matches, requestBodyNode)) { // nlohmann::json_pointer path
     324            7 :         source_ = matches.str(1);
     325            7 :         source_type_ = SourceType::RequestBody;
     326              :     }
     327          242 :     else if (sourceSpec == "response.body") { // whole document
     328            2 :         source_type_ = SourceType::ResponseBody;
     329              :     }
     330          240 :     else if (std::regex_match(sourceSpec, matches, responseBodyNode)) { // nlohmann::json_pointer path
     331            2 :         source_ = matches.str(1);
     332            2 :         source_type_ = SourceType::ResponseBody;
     333              :     }
     334              :     // IMPORTANT: literal "request.headers"/"response.headers" must precede the regex "request.header.(.*)"
     335              :     // because the dot in the regex also matches 's', causing "request.headers" to be misclassified as RequestHeader.
     336          238 :     else if (sourceSpec == "request.headers") {
     337            2 :         source_type_ = SourceType::RequestHeaders;
     338              :     }
     339          236 :     else if (sourceSpec == "response.headers") {
     340            1 :         source_type_ = SourceType::ResponseHeaders;
     341              :     }
     342          235 :     else if (std::regex_match(sourceSpec, matches, requestHeader)) { // header name
     343            2 :         source_ = matches.str(1);
     344            2 :         source_type_ = SourceType::RequestHeader;
     345              :     }
     346          233 :     else if (sourceSpec == "eraser") {
     347           13 :         source_type_ = SourceType::Eraser;
     348              :     }
     349          220 :     else if (std::regex_match(sourceSpec, matches, math)) { // math expression, i.e. "2*sqrt(2)"
     350            4 :         source_ = matches.str(1);
     351            4 :         source_type_ = SourceType::Math;
     352              :     }
     353          216 :     else if (std::regex_match(sourceSpec, matches, random)) { // range "<min>.<max>", i.e.: "-3.8", "0.100", "-15.+2", etc. These go to -> [source_i1_] and [source_i2_]
     354            8 :         source_i1_ = stoi(matches.str(1));
     355            8 :         source_i2_ = stoi(matches.str(2));
     356            8 :         source_type_ = SourceType::Random;
     357              :     }
     358          208 :     else if (std::regex_match(sourceSpec, matches, randomSet)) { // random set given by tokenized pipe-separated list of values
     359            1 :         source_ = matches.str(1) + "|"; // add pipe, just to allow getting empty part after trailing pipe ("foo|" => 'foo' ''), because the algorithm below ignores latest pipe and its trailing token.
     360            1 :         static std::regex pipedRgx(R"(\|)", std::regex::optimize);
     361            2 :         source_tokenized_ = std::vector<std::string>(
     362            4 :                                 std::sregex_token_iterator{begin(source_), end(source_), pipedRgx, -1},
     363            2 :                                 std::sregex_token_iterator{}
     364            1 :                             );
     365            1 :         source_type_ = SourceType::RandomSet;
     366            1 :         source_.pop_back(); // remove added pipe
     367              :     }
     368          207 :     else if (std::regex_match(sourceSpec, matches, timestamp)) { // unit (s: seconds, ms: milliseconds, us: microseconds, ns: nanoseconds)
     369            5 :         source_ = matches.str(1);
     370            5 :         source_type_ = SourceType::Timestamp;
     371              :     }
     372          202 :     else if (std::regex_match(sourceSpec, matches, strftime)) { // current date/time formatted by as described in https://www.cplusplus.com/reference/ctime/strftime/
     373            1 :         source_ = matches.str(1);
     374            1 :         source_type_ = SourceType::Strftime;
     375              :     }
     376          201 :     else if (sourceSpec == "recvseq") {
     377            2 :         source_type_ = SourceType::Recvseq;
     378              :     }
     379          199 :     else if (sourceSpec == "sendseq") {
     380            0 :         source_type_ = SourceType::Sendseq;
     381              :     }
     382          199 :     else if (std::regex_match(sourceSpec, matches, varId)) { // variable id
     383           30 :         source_ = matches.str(1);
     384           30 :         source_type_ = SourceType::SVar;
     385              :     }
     386          169 :     else if (std::regex_match(sourceSpec, matches, gvarId)) { // vault entry id
     387           34 :         source_ = matches.str(1); // key name
     388           34 :         if (matches[2].matched) source2_ = matches.str(2); // optional json path
     389           34 :         source_type_ = SourceType::SGVar;
     390              :     }
     391          135 :     else if (std::regex_match(sourceSpec, matches, value)) { // value content
     392          111 :         source_ = matches.str(1);
     393          111 :         source_type_ = SourceType::Value;
     394              :     }
     395           24 :     else if (std::regex_match(sourceSpec, matches, serverEvent)) { // value content
     396            5 :         source_ = matches.str(1); // i.e. requestMethod=GET&requestUri=/app/v1/foo/bar%3Fid%3D5%26name%3Dtest&eventNumber=3&eventPath=/requestBody
     397            5 :         source_type_ = SourceType::ServerEvent;
     398            5 :         std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(source_);
     399            5 :         std::map<std::string, std::string>::const_iterator it;
     400           30 :         for (auto const & qp: {
     401              :         "requestMethod", "requestUri", "eventNumber", "eventPath", "recvseq"
     402           35 :     }) { // tokenized vector order
     403           25 :             it = qmap.find(qp);
     404           49 :             source_tokenized_.push_back((it != qmap.end()) ? it->second:"");
     405              :         }
     406            5 :     }
     407           19 :     else if (std::regex_match(sourceSpec, matches, clientEvent)) { // client event data
     408            4 :         source_ = matches.str(1); // i.e. clientEndpointId=myEndpoint&requestMethod=GET&requestUri=/app/v1/foo/bar&eventNumber=3&eventPath=/responseBody
     409            4 :         source_type_ = SourceType::ClientEvent;
     410            4 :         std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(source_);
     411            4 :         std::map<std::string, std::string>::const_iterator it;
     412           28 :         for (auto const & qp: {
     413              :         "clientEndpointId", "requestMethod", "requestUri", "eventNumber", "eventPath", "sendseq"
     414           32 :     }) { // tokenized vector order
     415           24 :             it = qmap.find(qp);
     416           40 :             source_tokenized_.push_back((it != qmap.end()) ? it->second:"");
     417              :         }
     418            4 :     }
     419           15 :     else if (sourceSpec == "response.statusCode") {
     420            2 :         source_type_ = SourceType::ResponseStatusCode;
     421              :     }
     422           13 :     else if (std::regex_match(sourceSpec, matches, responseHeader_source)) { // response header name
     423            1 :         source_ = matches.str(1);
     424            1 :         source_type_ = SourceType::ResponseHeader;
     425              :     }
     426           12 :     else if (sourceSpec == "inState") {
     427            4 :         source_type_ = SourceType::InState;
     428              :     }
     429            8 :     else if (std::regex_match(sourceSpec, matches, txtFile)) { // path file
     430            3 :         source_ = matches.str(1);
     431            3 :         source_type_ = SourceType::STxtFile;
     432              :     }
     433            5 :     else if (std::regex_match(sourceSpec, matches, binFile)) { // path file
     434            2 :         source_ = matches.str(1);
     435            2 :         source_type_ = SourceType::SBinFile;
     436              :     }
     437            3 :     else if (std::regex_match(sourceSpec, matches, command)) { // command string
     438            3 :         source_ = matches.str(1);
     439            3 :         source_type_ = SourceType::Command;
     440              :     }
     441              :     else {
     442            0 :         std::string hint = validPrefixes(VALID_SOURCE_PREFIXES);
     443            0 :         ert::tracing::Logger::error(ert::tracing::Logger::asString("Cannot identify source type for: '%s'. Valid prefixes: %s", sourceSpec.c_str(), hint.c_str()), ERT_FILE_LOCATION);
     444            0 :         return false;
     445            0 :     }
     446              : 
     447              :     // TARGET (enum TargetType { ResponseBodyString = 0, ..., ResponseHeader_t, ResponseStatusCode_t, ..., RequestHeader, RequestDelayMs, RequestTimeoutMs, ClientEventToPurge };)
     448          275 :     target_ = ""; // empty by default (-), as many cases are only work modes and no parameters(+) are included in their transformation configuration
     449          275 :     target2_ = ""; // same
     450              : 
     451              :     // Target specifications:
     452              :     // SERVER MODE
     453              :     // - response.body.string *[string]*: response body storing expected string processed.
     454              :     // - response.body.hexstring *[string]*: response body storing expected string processed from hexadecimal representation, for example `0x8001` (prefix `0x` is optional).
     455              :     // - response.body.json.string *[string]*: response body document storing expected string.
     456              :     // - response.body.json.integer *[number]*: response body document storing expected integer.
     457              :     // - response.body.json.unsigned *[unsigned number]*: response body document storing expected unsigned integer.
     458              :     // - response.body.json.float *[float number]*: response body document storing expected float number.
     459              :     // - response.body.json.boolean *[boolean]*: response body document storing expected booolean.
     460              :     // - response.body.json.object *[json object]*: response body document storing expected object.
     461              :     // - response.body.json.jsonstring *[json string]*: response body document storing expected object, extracted from json-parsed string, as root node.
     462              :     // + response.body.json.string./<n1>/../<nN> *[string]*: response body node path storing expected string.
     463              :     // + response.body.json.integer./<n1>/../<nN> *[number]*: response body node path storing expected integer.
     464              :     // + response.body.json.unsigned./<n1>/../<nN> *[unsigned number]*: response body node path storing expected unsigned integer.
     465              :     // + response.body.json.float./<n1>/../<nN> *[float number]*: response body node path storing expected float number.
     466              :     // + response.body.json.boolean./<n1>/../<nN> *[boolean]*: response body node path storing expected booblean.
     467              :     // + response.body.json.object./<n1>/../<nN> *[json object]*: response body node path storing expected object.
     468              :     // + response.body.json.jsonstring./<n1>/../<nN> *[json string]*: response body node path storing expected object, extracted from json-parsed string, under provided path.
     469              :     // + response.header.<hname> *[string (or number as string)]*: response header component (i.e. *location*).
     470              :     // - response.statusCode *[unsigned integer]*: response status code.
     471              :     // - response.delayMs *[unsigned integer]*: simulated delay to respond.
     472              :     // + var.<id> *[string (or number as string)]*: general purpose variable.
     473              :     // + vault.<id> *[string (or number as string)]*: general purpose vault entry.
     474              :     // - outState *[string (or number as string)]*: next processing state. This overrides the default provisioned one.
     475              :     // + outState.`[POST|GET|PUT|DELETE|HEAD][.<uri>]` *[string (or number as string)]*: next processing state for specific method (virtual server data will be created if needed: this way we could modify the flow for other methods different than the one which is managing the current provision). This target **admits variables substitution** in the `uri` part.
     476              :     // + txtFile.`<path>` *[string]*: dumps source (as string) over text file with the path provided.
     477              :     // + binFile.`<path>` *[string]*: dumps source (as string) over binary file with the path provided.
     478              :     // + udpSocket.`<path>[|<milliseconds delay>]` *[string]*: sends source (as string) towards the UDP unix socket with the path provided.
     479              :     // + serverEvent.`<server event address in query parameters format>`: this target is always used in conjunction with `eraser`.
     480              :     // - break *[string]*: when non-empty string is transferred, the transformations list is interrupted. Empty string (or undefined source) ignores the action.
     481              :     //
     482              :     // CLIENT MODE ADDITIONAL TARGET TYPES:
     483              :     // - request.body.string *[string]*: request body storing expected string processed.
     484              :     // - request.body.hexstring *[string]*: request body storing expected string processed from hexadecimal representation, for example `0x8001` (prefix `0x` is optional).
     485              :     // - request.body.json.string *[string]*: request body document storing expected string.
     486              :     // - request.body.json.integer *[number]*: request body document storing expected integer.
     487              :     // - request.body.json.unsigned *[unsigned number]*: request body document storing expected unsigned integer.
     488              :     // - request.body.json.float *[float number]*: request body document storing expected float number.
     489              :     // - request.body.json.boolean *[boolean]*: request body document storing expected booolean.
     490              :     // - request.body.json.object *[json object]*: request body document storing expected object.
     491              :     // - request.body.json.jsonstring *[json string]*: request body document storing expected object, extracted from json-parsed string, as root node.
     492              :     // + request.body.json.string./<n1>/../<nN> *[string]*: request body node path storing expected string.
     493              :     // + request.body.json.integer./<n1>/../<nN> *[number]*: request body node path storing expected integer.
     494              :     // + request.body.json.unsigned./<n1>/../<nN> *[unsigned number]*: request body node path storing expected unsigned integer.
     495              :     // + request.body.json.float./<n1>/../<nN> *[float number]*: request body node path storing expected float number.
     496              :     // + request.body.json.boolean./<n1>/../<nN> *[boolean]*: request body node path storing expected booblean.
     497              :     // + request.body.json.object./<n1>/../<nN> *[json object]*: request body node path storing expected object.
     498              :     // + request.body.json.jsonstring./<n1>/../<nN> *[json string]*: request body node path storing expected object, extracted from json-parsed string, under provided path.
     499              : 
     500              :     // Regex needed:
     501              :     // SERVER MODE:
     502          275 :     static std::regex responseBodyJson_StringNode("^response.body.json.string.(.*)", std::regex::optimize);
     503          275 :     static std::regex responseBodyJson_IntegerNode("^response.body.json.integer.(.*)", std::regex::optimize);
     504          275 :     static std::regex responseBodyJson_UnsignedNode("^response.body.json.unsigned.(.*)", std::regex::optimize);
     505          275 :     static std::regex responseBodyJson_FloatNode("^response.body.json.float.(.*)", std::regex::optimize);
     506          275 :     static std::regex responseBodyJson_BooleanNode("^response.body.json.boolean.(.*)", std::regex::optimize);
     507          275 :     static std::regex responseBodyJson_ObjectNode("^response.body.json.object.(.*)", std::regex::optimize);
     508          275 :     static std::regex responseBodyJson_JsonStringNode("^response.body.json.jsonstring.(.*)", std::regex::optimize);
     509          275 :     static std::regex responseHeader("^response.header.(.*)", std::regex::optimize);
     510          275 :     static std::regex outStateMethodUri("^outState.(POST|GET|PUT|DELETE|HEAD)(\\..+)?", std::regex::optimize);
     511              : 
     512              :     // CLIENT MODE:
     513          275 :     static std::regex requestBodyJson_StringNode("^request.body.json.string.(.*)", std::regex::optimize);
     514          275 :     static std::regex requestBodyJson_IntegerNode("^request.body.json.integer.(.*)", std::regex::optimize);
     515          275 :     static std::regex requestBodyJson_UnsignedNode("^request.body.json.unsigned.(.*)", std::regex::optimize);
     516          275 :     static std::regex requestBodyJson_FloatNode("^request.body.json.float.(.*)", std::regex::optimize);
     517          275 :     static std::regex requestBodyJson_BooleanNode("^request.body.json.boolean.(.*)", std::regex::optimize);
     518          275 :     static std::regex requestBodyJson_ObjectNode("^request.body.json.object.(.*)", std::regex::optimize);
     519          275 :     static std::regex requestBodyJson_JsonStringNode("^request.body.json.jsonstring.(.*)", std::regex::optimize);
     520              : 
     521              :     // Only target
     522          275 :     static std::regex requestHeader_target("^request.header.(.*)", std::regex::optimize);
     523          275 :     static std::regex udpSocket("^udpSocket.(.*)", std::regex::optimize);
     524          275 :     static std::regex clientProvision("^clientProvision\\.([^.]+)(?:\\.(.+))?$", std::regex::optimize);
     525              : 
     526              :     // no need to try (controlled regex)
     527              :     //try {
     528              :     // SERVER_MODE
     529          275 :     if (targetSpec == "response.body.string") {
     530           94 :         target_type_ = TargetType::ResponseBodyString;
     531              :     }
     532          181 :     else if (targetSpec == "response.body.hexstring") {
     533            1 :         target_type_ = TargetType::ResponseBodyHexString;
     534              :     }
     535          180 :     else if (targetSpec == "response.body.json.string") { // whole document
     536            2 :         target_type_ = TargetType::ResponseBodyJson_String;
     537              :     }
     538          178 :     else if (std::regex_match(targetSpec, matches, responseBodyJson_StringNode)) { // nlohmann::json_pointer path
     539           32 :         target_ = matches.str(1);
     540           32 :         target_type_ = TargetType::ResponseBodyJson_String;
     541              :     }
     542          146 :     else if (targetSpec == "response.body.json.integer") { // whole document
     543            1 :         target_type_ = TargetType::ResponseBodyJson_Integer;
     544              :     }
     545          145 :     else if (std::regex_match(targetSpec, matches, responseBodyJson_IntegerNode)) { // nlohmann::json_pointer path
     546           11 :         target_ = matches.str(1);
     547           11 :         target_type_ = TargetType::ResponseBodyJson_Integer;
     548              :     }
     549          134 :     else if (targetSpec == "response.body.json.unsigned") { // whole document
     550            1 :         target_type_ = TargetType::ResponseBodyJson_Unsigned;
     551              :     }
     552          133 :     else if (std::regex_match(targetSpec, matches, responseBodyJson_UnsignedNode)) { // nlohmann::json_pointer path
     553            4 :         target_ = matches.str(1);
     554            4 :         target_type_ = TargetType::ResponseBodyJson_Unsigned;
     555              :     }
     556          129 :     else if (targetSpec == "response.body.json.float") { // whole document
     557            1 :         target_type_ = TargetType::ResponseBodyJson_Float;
     558              :     }
     559          128 :     else if (std::regex_match(targetSpec, matches, responseBodyJson_FloatNode)) { // nlohmann::json_pointer path
     560            1 :         target_ = matches.str(1);
     561            1 :         target_type_ = TargetType::ResponseBodyJson_Float;
     562              :     }
     563          127 :     else if (targetSpec == "response.body.json.boolean") { // whole document
     564            1 :         target_type_ = TargetType::ResponseBodyJson_Boolean;
     565              :     }
     566          126 :     else if (std::regex_match(targetSpec, matches, responseBodyJson_BooleanNode)) { // nlohmann::json_pointer path
     567            1 :         target_ = matches.str(1);
     568            1 :         target_type_ = TargetType::ResponseBodyJson_Boolean;
     569              :     }
     570          125 :     else if (targetSpec == "response.body.json.object") { // whole document
     571            7 :         target_type_ = TargetType::ResponseBodyJson_Object;
     572              :     }
     573          118 :     else if (std::regex_match(targetSpec, matches, responseBodyJson_ObjectNode)) { // nlohmann::json_pointer path
     574           12 :         target_ = matches.str(1);
     575           12 :         target_type_ = TargetType::ResponseBodyJson_Object;
     576              :     }
     577          106 :     else if (targetSpec == "response.body.json.jsonstring") { // whole document
     578            2 :         target_type_ = TargetType::ResponseBodyJson_JsonString;
     579              :     }
     580          104 :     else if (std::regex_match(targetSpec, matches, responseBodyJson_JsonStringNode)) { // nlohmann::json_pointer path
     581            1 :         target_ = matches.str(1);
     582            1 :         target_type_ = TargetType::ResponseBodyJson_JsonString;
     583              :     }
     584              :     // CLIENT MODE
     585          103 :     else if (targetSpec == "request.body.string") {
     586            0 :         target_type_ = TargetType::RequestBodyString;
     587              :     }
     588          103 :     else if (targetSpec == "request.body.hexstring") {
     589            0 :         target_type_ = TargetType::RequestBodyHexString;
     590              :     }
     591          103 :     else if (targetSpec == "request.body.json.string") { // whole document
     592            0 :         target_type_ = TargetType::RequestBodyJson_String;
     593              :     }
     594          103 :     else if (std::regex_match(targetSpec, matches, requestBodyJson_StringNode)) { // nlohmann::json_pointer path
     595            1 :         target_ = matches.str(1);
     596            1 :         target_type_ = TargetType::RequestBodyJson_String;
     597              :     }
     598          102 :     else if (targetSpec == "request.body.json.integer") { // whole document
     599            0 :         target_type_ = TargetType::RequestBodyJson_Integer;
     600              :     }
     601          102 :     else if (std::regex_match(targetSpec, matches, requestBodyJson_IntegerNode)) { // nlohmann::json_pointer path
     602            1 :         target_ = matches.str(1);
     603            1 :         target_type_ = TargetType::RequestBodyJson_Integer;
     604              :     }
     605          101 :     else if (targetSpec == "request.body.json.unsigned") { // whole document
     606            0 :         target_type_ = TargetType::RequestBodyJson_Unsigned;
     607              :     }
     608          101 :     else if (std::regex_match(targetSpec, matches, requestBodyJson_UnsignedNode)) { // nlohmann::json_pointer path
     609            0 :         target_ = matches.str(1);
     610            0 :         target_type_ = TargetType::RequestBodyJson_Unsigned;
     611              :     }
     612          101 :     else if (targetSpec == "request.body.json.float") { // whole document
     613            0 :         target_type_ = TargetType::RequestBodyJson_Float;
     614              :     }
     615          101 :     else if (std::regex_match(targetSpec, matches, requestBodyJson_FloatNode)) { // nlohmann::json_pointer path
     616            0 :         target_ = matches.str(1);
     617            0 :         target_type_ = TargetType::RequestBodyJson_Float;
     618              :     }
     619          101 :     else if (targetSpec == "request.body.json.boolean") { // whole document
     620            0 :         target_type_ = TargetType::RequestBodyJson_Boolean;
     621              :     }
     622          101 :     else if (std::regex_match(targetSpec, matches, requestBodyJson_BooleanNode)) { // nlohmann::json_pointer path
     623            0 :         target_ = matches.str(1);
     624            0 :         target_type_ = TargetType::RequestBodyJson_Boolean;
     625              :     }
     626          101 :     else if (targetSpec == "request.body.json.object") { // whole document
     627            0 :         target_type_ = TargetType::RequestBodyJson_Object;
     628              :     }
     629          101 :     else if (std::regex_match(targetSpec, matches, requestBodyJson_ObjectNode)) { // nlohmann::json_pointer path
     630            0 :         target_ = matches.str(1);
     631            0 :         target_type_ = TargetType::RequestBodyJson_Object;
     632              :     }
     633          101 :     else if (targetSpec == "request.body.json.jsonstring") { // whole document
     634            0 :         target_type_ = TargetType::RequestBodyJson_JsonString;
     635              :     }
     636          101 :     else if (std::regex_match(targetSpec, matches, requestBodyJson_JsonStringNode)) { // nlohmann::json_pointer path
     637            0 :         target_ = matches.str(1);
     638            0 :         target_type_ = TargetType::RequestBodyJson_JsonString;
     639              :     }
     640              : 
     641              :     // CLIENT MODE ADDITIONAL TARGET TYPES:
     642          101 :     else if (std::regex_match(targetSpec, matches, requestHeader_target)) { // request header name
     643            2 :         target_ = matches.str(1);
     644            2 :         target_type_ = TargetType::RequestHeader_t;
     645              :     }
     646           99 :     else if (targetSpec == "request.delayMs") {
     647            4 :         target_type_ = TargetType::RequestDelayMs;
     648              :     }
     649           95 :     else if (targetSpec == "request.timeoutMs") {
     650            1 :         target_type_ = TargetType::RequestTimeoutMs;
     651              :     }
     652           94 :     else if (targetSpec == "request.uri") {
     653           11 :         target_type_ = TargetType::RequestUri_t;
     654              :     }
     655           83 :     else if (targetSpec == "request.method") {
     656            1 :         target_type_ = TargetType::RequestMethod_t;
     657              :     }
     658              : 
     659           82 :     else if (std::regex_match(targetSpec, matches, responseHeader)) { // header name
     660            1 :         target_ = matches.str(1);
     661            1 :         target_type_ = TargetType::ResponseHeader_t;
     662              :     }
     663           81 :     else if (targetSpec == "response.statusCode") {
     664            6 :         target_type_ = TargetType::ResponseStatusCode_t;
     665              :     }
     666           75 :     else if (targetSpec == "response.delayMs") {
     667            1 :         target_type_ = TargetType::ResponseDelayMs;
     668              :     }
     669           74 :     else if (std::regex_match(targetSpec, matches, varId)) { // variable id
     670           21 :         target_ = matches.str(1);
     671           21 :         target_type_ = TargetType::TVar;
     672              :     }
     673           53 :     else if (std::regex_match(targetSpec, matches, gvarJsonObject)) { // vault entry json object
     674            3 :         target_ = matches.str(1); // key name
     675            3 :         if (matches[2].matched) target2_ = matches.str(2); // optional json path
     676            3 :         target_type_ = TargetType::TGVarJson_Object;
     677              :     }
     678           50 :     else if (std::regex_match(targetSpec, matches, gvarJsonJsonString)) { // vault entry json-parsed string
     679            4 :         target_ = matches.str(1); // key name
     680            4 :         if (matches[2].matched) target2_ = matches.str(2); // optional json path
     681            4 :         target_type_ = TargetType::TGVarJson_JsonString;
     682              :     }
     683           46 :     else if (std::regex_match(targetSpec, matches, gvarId)) { // vault entry id
     684           18 :         target_ = matches.str(1); // key name
     685           18 :         if (matches[2].matched) target2_ = matches.str(2); // optional json path
     686           18 :         target_type_ = TargetType::TGVar;
     687              :     }
     688           28 :     else if (targetSpec == "outState") {
     689            6 :         target_type_ = TargetType::OutState;
     690              :     }
     691           22 :     else if (std::regex_match(targetSpec, matches, outStateMethodUri)) { // method
     692            2 :         target_ = matches.str(1); // <method>
     693            2 :         target2_ = matches.str(2); // .<uri>
     694            2 :         if (!target2_.empty()) {
     695            2 :             target2_ = target2_.substr(1); // remove the initial dot to store the uri
     696              :         }
     697            2 :         target_type_ = TargetType::OutState;
     698              :     }
     699           20 :     else if (std::regex_match(targetSpec, matches, txtFile)) { // path file
     700            3 :         target_ = matches.str(1);
     701            3 :         target_type_ = TargetType::TTxtFile;
     702              :     }
     703           17 :     else if (std::regex_match(targetSpec, matches, binFile)) { // path file
     704            2 :         target_ = matches.str(1);
     705            2 :         target_type_ = TargetType::TBinFile;
     706              :     }
     707           15 :     else if (std::regex_match(targetSpec, matches, udpSocket)) { // path file
     708            1 :         target_ = matches.str(1);
     709            1 :         target_type_ = TargetType::UDPSocket;
     710              :     }
     711           14 :     else if (std::regex_match(targetSpec, matches, serverEvent)) { // value content
     712            4 :         target_ = matches.str(1); // i.e. requestMethod=GET&requestUri=/app/v1/foo/bar%3Fid%3D5%26name%3Dtest&eventNumber=3
     713            4 :         target_type_ = TargetType::ServerEventToPurge;
     714            4 :         std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(target_);
     715            4 :         std::map<std::string, std::string>::const_iterator it;
     716           20 :         for (auto const & qp: {
     717              :         "requestMethod", "requestUri", "eventNumber", "recvseq"
     718           24 :     }) { // tokenized vector order
     719           16 :             it = qmap.find(qp);
     720           32 :             target_tokenized_.push_back((it != qmap.end()) ? it->second:"");
     721              :         }
     722            4 :     }
     723           10 :     else if (std::regex_match(targetSpec, matches, clientEvent)) { // client event purge
     724            1 :         target_ = matches.str(1); // i.e. clientEndpointId=myEndpoint&requestMethod=GET&requestUri=/app/v1/foo/bar&eventNumber=3
     725            1 :         target_type_ = TargetType::ClientEventToPurge;
     726            1 :         std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(target_);
     727            1 :         std::map<std::string, std::string>::const_iterator it;
     728            6 :         for (auto const & qp: {
     729              :         "clientEndpointId", "requestMethod", "requestUri", "eventNumber", "sendseq"
     730            7 :     }) { // tokenized vector order
     731            5 :             it = qmap.find(qp);
     732           15 :             target_tokenized_.push_back((it != qmap.end()) ? it->second:"");
     733              :         }
     734            1 :     }
     735            9 :     else if (targetSpec == "break") {
     736            4 :         target_type_ = TargetType::Break;
     737              :     }
     738            5 :     else if (std::regex_match(targetSpec, matches, clientProvision)) { // clientProvision.<id>.<inState>
     739            5 :         target_ = matches.str(1);
     740            5 :         target2_ = matches.str(2);
     741            5 :         if (target2_.empty()) {
     742            1 :             ert::tracing::Logger::error(ert::tracing::Logger::asString("Missing inState in clientProvision target: '%s'. Use 'clientProvision.<id>.<inState>'", targetSpec.c_str()), ERT_FILE_LOCATION);
     743            1 :             return false;
     744              :         }
     745            4 :         target_type_ = TargetType::ClientProvision_t;
     746              :     }
     747              :     else {
     748            0 :         std::string hint = validPrefixes(VALID_TARGET_PREFIXES);
     749            0 :         ert::tracing::Logger::error(ert::tracing::Logger::asString("Cannot identify target type for: '%s'. Valid prefixes: %s", targetSpec.c_str(), hint.c_str()), ERT_FILE_LOCATION);
     750            0 :         return false;
     751            0 :     }
     752              : 
     753              :     //LOGDEBUG(ert::tracing::Logger::debug(asString(), ERT_FILE_LOCATION));
     754              : 
     755              :     // Variable patterns:
     756          274 :     collectVariablePatterns(source_, source_patterns_);
     757          274 :     if (collectFilterPatterns) collectVariablePatterns(filter_, filter_patterns_); // protected to avoid possible gathering of false patterns (i.e. complex regexp's)
     758          274 :     collectVariablePatterns(target_, target_patterns_);
     759          274 :     collectVariablePatterns(target2_, target2_patterns_);
     760              : 
     761              :     // onFilterFail:
     762          274 :     auto off_it = j.find("onFilterFail");
     763          274 :     if (off_it != j.end() && off_it->is_array()) {
     764           10 :         for (const auto &item : *off_it) {
     765            5 :             auto t = std::make_shared<Transformation>();
     766            5 :             if (!t->load(item)) return false;
     767            5 :             on_filter_fail_.push_back(std::move(t));
     768            5 :         }
     769              :     }
     770              : 
     771          274 :     return true;
     772          277 : }
     773              : 
     774           12 : std::string Transformation::asString() const {
     775              : 
     776           12 :     std::stringstream ss;
     777              : 
     778              : 
     779              :     // SOURCE
     780           12 :     ss << "SourceType: " << SourceTypeAsText(source_type_);
     781           12 :     if (source_type_ != SourceType::RequestUri && source_type_ != SourceType::RequestUriPath && source_type_ != SourceType::Eraser && source_type_ != SourceType::Recvseq && source_type_ != SourceType::Sendseq && source_type_ != SourceType::InState && source_type_ != SourceType::ResponseStatusCode) {
     782           10 :         ss << " | source_: " << source_;
     783              : 
     784           10 :         if (source_type_ == SourceType::RequestBody || source_type_ == SourceType::ResponseBody) {
     785            1 :             ss << " (empty: whole, path: node)";
     786              :         }
     787            9 :         else if (source_type_ == SourceType::Random) {
     788            1 :             ss << " | source_i1_: " << source_i1_ << " (Random min)" << " | source_i2_: " << source_i2_ << " (Random max)";
     789              :         }
     790            8 :         else if (source_type_ == SourceType::RandomSet || source_type_ == SourceType::ServerEvent || source_type_ == SourceType::ClientEvent) {
     791            0 :             ss << " | source_tokenized_:";
     792            0 :             for(auto it: source_tokenized_) {
     793            0 :                 ss << " '" << it << "'";
     794            0 :             }
     795            0 :         }
     796            8 :         else if (source_type_ == SourceType::STxtFile || source_type_ == SourceType::SBinFile) {
     797            1 :             ss << " (path file)";
     798              :         }
     799            7 :         else if (source_type_ == SourceType::Command) {
     800            1 :             ss << " (shell command expression)";
     801              :         }
     802              : 
     803           10 :         if (!source_patterns_.empty()) {
     804            1 :             ss << " | source variables:";
     805            2 :             for (auto it = source_patterns_.begin(); it != source_patterns_.end(); it ++) {
     806            1 :                 ss << " " << it->second;
     807              :             }
     808              :         }
     809              :     }
     810              : 
     811              :     // TARGET
     812           12 :     ss << " | TargetType: " << TargetTypeAsText(target_type_);
     813           12 :     if (target_type_ != TargetType::ResponseStatusCode_t &&
     814           12 :             target_type_ != TargetType::ResponseDelayMs &&
     815           12 :             target_type_ != TargetType::ResponseBodyString &&
     816            4 :             target_type_ != TargetType::ResponseBodyHexString &&
     817            4 :             target_type_ != TargetType::RequestDelayMs &&
     818            4 :             target_type_ != TargetType::RequestTimeoutMs &&
     819            4 :             target_type_ != TargetType::RequestBodyString &&
     820            4 :             target_type_ != TargetType::RequestBodyHexString ) {
     821              : 
     822            4 :         ss << " | target_: " << target_;
     823              : 
     824            4 :         if (target_type_ == TargetType::ResponseBodyJson_String || target_type_ == TargetType::ResponseBodyJson_Integer || target_type_ == TargetType::ResponseBodyJson_Unsigned || target_type_ == TargetType::ResponseBodyJson_Float || target_type_ == TargetType::ResponseBodyJson_Boolean || target_type_ == TargetType::ResponseBodyJson_Object ||
     825            3 :                 target_type_ == TargetType::RequestBodyJson_String || target_type_ == TargetType::RequestBodyJson_Integer || target_type_ == TargetType::RequestBodyJson_Unsigned || target_type_ == TargetType::RequestBodyJson_Float || target_type_ == TargetType::RequestBodyJson_Boolean || target_type_ == TargetType::RequestBodyJson_Object) {
     826            1 :             ss << " (empty: whole, path: node)";
     827              :         }
     828            3 :         else if (target_type_ == TargetType::OutState) {
     829            1 :             ss << " (empty: current method, method: another)" << " | target2_: " << target2_ << "(empty: current uri, uri: another)";
     830            1 :             if (!target2_patterns_.empty()) {
     831            1 :                 ss << " | target2 variables:";
     832            2 :                 for (auto it = target2_patterns_.begin(); it != target2_patterns_.end(); it ++) {
     833            1 :                     ss << " " << it->second;
     834              :                 }
     835              :             }
     836              :         }
     837            2 :         else if (target_type_ == TargetType::TTxtFile || target_type_ == TargetType::TBinFile) {
     838            1 :             ss << " (path file)";
     839              :         }
     840            1 :         else if (target_type_ == TargetType::ClientProvision_t && !target2_.empty()) {
     841            0 :             ss << " | target2_: " << target2_ << " (explicit inState)";
     842            0 :             if (!target2_patterns_.empty()) {
     843            0 :                 ss << " | target2 variables:";
     844            0 :                 for (auto it = target2_patterns_.begin(); it != target2_patterns_.end(); it ++) {
     845            0 :                     ss << " " << it->second;
     846              :                 }
     847              :             }
     848              :         }
     849            1 :         else if (target_type_ == TargetType::UDPSocket) {
     850            0 :             ss << " (<path file>[|<write delay ms>])";
     851              :         }
     852              : 
     853            4 :         if (!target_patterns_.empty()) {
     854            1 :             ss << " | target variables:";
     855            2 :             for (auto it = target_patterns_.begin(); it != target_patterns_.end(); it ++) {
     856            1 :                 ss << " " << it->second;
     857              :             }
     858              :         }
     859              :     }
     860              : 
     861              :     // FILTER
     862           12 :     if (has_filter_) {
     863            5 :         ss << " | FilterType: " << FilterTypeAsText(filter_type_);
     864            5 :         if (filter_type_ != FilterType::Sum && filter_type_ != FilterType::Multiply && filter_type_ != FilterType::Split && filter_type_ != FilterType::BaseConvert && filter_type_ != FilterType::FStrptime && filter_type_ != FilterType::FStrftime && filter_type_ != FilterType::Size) {
     865              :             /*<< " | filter_rgx_: ?"*/
     866            3 :             ss << " | filter_ " << filter_;
     867              : 
     868            3 :             if (filter_type_ == FilterType::RegexReplace) {
     869            1 :                 ss << " (fmt)";
     870              :             }
     871            2 :             else if (filter_type_ == FilterType::RegexCapture) {
     872            1 :                 ss << " (literal, although not actually needed, but useful to access & print on traces)";
     873              :             }
     874              :         }
     875            2 :         else if (filter_type_ == FilterType::Split) {
     876            1 :             ss << " | size: " << filter_i_ << " | count: " << filter_u_ << " | sep: '" << filter_ << "' | filler: '" << filter_filler_ << "' | numeric: " << (filter_number_type_ ? "true" : "false");
     877              :         }
     878            1 :         else if (filter_type_ == FilterType::BaseConvert) {
     879            0 :             ss << " | in: " << filter_i_ << " | out: " << filter_u_ << " | capital: " << (filter_number_type_ ? "true" : "false");
     880              :         }
     881            1 :         else if (filter_type_ == FilterType::FStrptime || filter_type_ == FilterType::FStrftime) {
     882              :             static const char* units[] = {"s", "ms", "us", "ns"};
     883            0 :             ss << " | fmt: '" << filter_ << "' | unit: " << units[filter_number_type_];
     884            0 :         }
     885              :         else {
     886            1 :             ss << " | filter_number_type_: " << filter_number_type_ << " (0: integer, 1: unsigned, 2: float)"
     887            1 :                << " | filter_i_: " << filter_i_ << " | filter_u_: " << filter_u_ << " | filter_f_: " << filter_f_;
     888              :         }
     889              : 
     890            5 :         if (!filter_patterns_.empty()) {
     891            1 :             ss << " | filter variables:";
     892            2 :             for (auto it = filter_patterns_.begin(); it != filter_patterns_.end(); it ++) {
     893            1 :                 ss << " " << it->second;
     894              :             }
     895              :         }
     896              :     }
     897              : 
     898           24 :     return ss.str();
     899           12 : }
     900              : 
     901              : 
     902              : }
     903              : }
     904              : 
        

Generated by: LCOV version 2.0-1