LCOV - code coverage report
Current view: top level - model - AdminServerProvision.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 98.9 % 737 729
Test Date: 2026-03-07 03:59:50 Functions: 100.0 % 9 9

            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              : #include <sstream>
      37              : #include <chrono>
      38              : #include <sys/time.h>
      39              : #include <ctime>
      40              : #include <time.h>       /* time_t, struct tm, time, localtime, strftime */
      41              : #include <string>
      42              : #include <algorithm>
      43              : //#include <fcntl.h> // non-blocking fgets call
      44              : 
      45              : #include <nlohmann/json.hpp>
      46              : #include <arashpartow/exprtk.hpp>
      47              : 
      48              : #include <ert/tracing/Logger.hpp>
      49              : #include <ert/http2comm/Http.hpp>
      50              : 
      51              : #include <AdminServerProvision.hpp>
      52              : #include <MockServerData.hpp>
      53              : #include <MockClientData.hpp>
      54              : #include <Configuration.hpp>
      55              : #include <GlobalVariable.hpp>
      56              : #include <FileManager.hpp>
      57              : #include <SocketManager.hpp>
      58              : #include <AdminData.hpp>
      59              : 
      60              : #include <functions.hpp>
      61              : 
      62              : 
      63              : typedef exprtk::expression<double>   expression_t;
      64              : typedef exprtk::parser<double>       parser_t;
      65              : 
      66              : namespace h2agent
      67              : {
      68              : namespace model
      69              : {
      70              : 
      71          246 : AdminServerProvision::AdminServerProvision() : in_state_(DEFAULT_ADMIN_PROVISION_STATE),
      72          246 :     out_state_(DEFAULT_ADMIN_PROVISION_STATE),
      73          369 :     response_delay_ms_(0), mock_server_events_data_(nullptr), mock_client_events_data_(nullptr) {;}
      74              : 
      75              : 
      76          213 : std::shared_ptr<h2agent::model::AdminSchema> AdminServerProvision::getRequestSchema() {
      77              : 
      78          213 :     if(request_schema_id_.empty()) return nullptr;
      79              : 
      80            7 :     if (admin_data_->getSchemaData().size() != 0) { // the only way to destroy schema references, is to clean whole schema data
      81            6 :         if (request_schema_) return request_schema_; // provision cache
      82            2 :         request_schema_ = admin_data_->getSchemaData().find(request_schema_id_);
      83              :     }
      84              : 
      85            3 :     LOGWARNING(
      86              :         if (!request_schema_) ert::tracing::Logger::warning(ert::tracing::Logger::asString("Missing schema '%s' referenced in provision for incoming message: VALIDATION will be IGNORED", request_schema_id_.c_str()), ERT_FILE_LOCATION);
      87              :     );
      88              : 
      89            3 :     return request_schema_;
      90              : }
      91              : 
      92          106 : std::shared_ptr<h2agent::model::AdminSchema> AdminServerProvision::getResponseSchema() {
      93              : 
      94          106 :     if(response_schema_id_.empty()) return nullptr;
      95              : 
      96            3 :     if (admin_data_->getSchemaData().size() != 0) { // the only way to destroy schema references, is to clean whole schema data
      97            2 :         if (response_schema_) return response_schema_; // provision cache
      98            1 :         response_schema_ = admin_data_->getSchemaData().find(response_schema_id_);
      99              :     }
     100              : 
     101            2 :     LOGWARNING(
     102              :         if (!response_schema_) ert::tracing::Logger::warning(ert::tracing::Logger::asString("Missing schema '%s' referenced in provision for outgoing message: VALIDATION will be IGNORED", response_schema_id_.c_str()), ERT_FILE_LOCATION);
     103              :     );
     104              : 
     105            2 :     return response_schema_;
     106              : }
     107              : 
     108          136 : bool AdminServerProvision::processSources(std::shared_ptr<Transformation> transformation,
     109              :         TypeConverter& sourceVault,
     110              :         std::map<std::string, std::string>& variables, /* Command generates "rc" */
     111              :         const std::string &requestUri,
     112              :         const std::string &requestUriPath,
     113              :         const std::map<std::string, std::string> &requestQueryParametersMap,
     114              :         const DataPart &requestBodyDataPart,
     115              :         const nghttp2::asio_http2::header_map &requestHeaders,
     116              :         bool &eraser,
     117              :         std::uint64_t generalUniqueServerSequence,
     118              :         bool usesResponseBodyAsTransformationJsonTarget, const nlohmann::json &responseBodyJson) const {
     119              : 
     120          136 :     switch (transformation->getSourceType()) {
     121            4 :     case Transformation::SourceType::RequestUri:
     122              :     {
     123            4 :         sourceVault.setString(requestUri);
     124            4 :         break;
     125              :     }
     126            2 :     case Transformation::SourceType::RequestUriPath:
     127              :     {
     128            2 :         sourceVault.setString(requestUriPath);
     129            2 :         break;
     130              :     }
     131            2 :     case Transformation::SourceType::RequestUriParam:
     132              :     {
     133            2 :         auto iter = requestQueryParametersMap.find(transformation->getSource());
     134            2 :         if (iter != requestQueryParametersMap.end()) sourceVault.setString(iter->second);
     135              :         else {
     136            1 :             LOGDEBUG(
     137              :                 std::string msg = ert::tracing::Logger::asString("Unable to extract query parameter '%s' in transformation item", transformation->getSource().c_str());
     138              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     139              :             );
     140            1 :             return false;
     141              :         }
     142            1 :         break;
     143              :     }
     144           16 :     case Transformation::SourceType::RequestBody:
     145              :     {
     146           16 :         if (requestBodyDataPart.isJson()) {
     147           13 :             std::string path = transformation->getSource(); // document path (empty or not to be whole or node)
     148           13 :             replaceVariables(path, transformation->getSourcePatterns(), variables, global_variable_);
     149           13 :             if (!sourceVault.setObject(requestBodyDataPart.getJson(), path)) {
     150            1 :                 LOGDEBUG(
     151              :                     std::string msg = ert::tracing::Logger::asString("Unable to extract path '%s' from request body (it is null) in transformation item", transformation->getSource().c_str());
     152              :                     ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     153              :                 );
     154            1 :                 return false;
     155              :             }
     156           13 :         }
     157              :         else {
     158            3 :             sourceVault.setString(requestBodyDataPart.str());
     159              :         }
     160           15 :         break;
     161              :     }
     162            3 :     case Transformation::SourceType::ResponseBody:
     163              :     {
     164            3 :         std::string path = transformation->getSource(); // document path (empty or not to be whole or node)
     165            3 :         replaceVariables(path, transformation->getSourcePatterns(), variables, global_variable_);
     166            3 :         if (!sourceVault.setObject(usesResponseBodyAsTransformationJsonTarget ? responseBodyJson:getResponseBody(), path)) {
     167            1 :             LOGDEBUG(
     168              :                 std::string msg = ert::tracing::Logger::asString("Unable to extract path '%s' from response body (it is null) in transformation item", transformation->getSource().c_str());
     169              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     170              :             );
     171            1 :             return false;
     172              :         }
     173            2 :         break;
     174            3 :     }
     175            2 :     case Transformation::SourceType::RequestHeader:
     176              :     {
     177            2 :         auto iter = requestHeaders.find(transformation->getSource());
     178            2 :         if (iter != requestHeaders.end()) sourceVault.setString(iter->second.value);
     179              :         else {
     180            1 :             LOGDEBUG(
     181              :                 std::string msg = ert::tracing::Logger::asString("Unable to extract request header '%s' in transformation item", transformation->getSource().c_str());
     182              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     183              :             );
     184            1 :             return false;
     185              :         }
     186            1 :         break;
     187              :     }
     188            8 :     case Transformation::SourceType::Eraser:
     189              :     {
     190            8 :         sourceVault.setString(""); // with other than response body nodes, it acts like setting empty string
     191            8 :         eraser = true;
     192            8 :         break;
     193              :     }
     194            3 :     case Transformation::SourceType::Math:
     195              :     {
     196            3 :         std::string expressionString = transformation->getSource();
     197            3 :         replaceVariables(expressionString, transformation->getSourcePatterns(), variables, global_variable_);
     198              : 
     199              :         /*
     200              :            We don't use builtin variables as we can parse h2agent ones which is easier to implement:
     201              : 
     202              :            typedef exprtk::symbol_table<double> symbol_table_t;
     203              :            symbol_table_t symbol_table;
     204              :            double x = 2.0;
     205              :            symbol_table.add_variable("x",x);
     206              :            expression.register_symbol_table(symbol_table);
     207              :            parser.compile("3*x",expression);
     208              :            std::cout << expression.value() << std::endl; // 3*2
     209              :         */
     210              : 
     211            3 :         expression_t   expression;
     212            3 :         parser_t       parser;
     213            3 :         parser.compile(expressionString, expression);
     214              : 
     215            3 :         double result = expression.value(); // if the result has decimals, set as float. If not, set as integer:
     216            3 :         if (result == (int)result) sourceVault.setInteger(expression.value());
     217            1 :         else sourceVault.setFloat(expression.value());
     218            3 :         break;
     219            3 :     }
     220            2 :     case Transformation::SourceType::Random:
     221              :     {
     222            2 :         int range = transformation->getSourceI2() - transformation->getSourceI1() + 1;
     223            2 :         sourceVault.setInteger(transformation->getSourceI1() + (rand() % range));
     224            2 :         break;
     225              :     }
     226            1 :     case Transformation::SourceType::RandomSet:
     227              :     {
     228            1 :         sourceVault.setStringReplacingVariables(transformation->getSourceTokenized()[rand () % transformation->getSourceTokenized().size()], transformation->getSourcePatterns(), variables, global_variable_); // replace variables if they exist
     229            1 :         break;
     230              :     }
     231            4 :     case Transformation::SourceType::Timestamp:
     232              :     {
     233            4 :         if (transformation->getSource() == "s") {
     234            1 :             sourceVault.setInteger(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count());
     235              :         }
     236            3 :         else if (transformation->getSource() == "ms") {
     237            1 :             sourceVault.setInteger(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
     238              :         }
     239            2 :         else if (transformation->getSource() == "us") {
     240            1 :             sourceVault.setInteger(std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
     241              :         }
     242            1 :         else if (transformation->getSource() == "ns") {
     243            1 :             sourceVault.setInteger(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
     244              :         }
     245            4 :         break;
     246              :     }
     247            1 :     case Transformation::SourceType::Strftime:
     248              :     {
     249            1 :         std::time_t unixTime = 0;
     250            1 :         std::time (&unixTime);
     251            1 :         char buffer[100] = {0};
     252            1 :         /*size_t size = */strftime(buffer, sizeof(buffer), transformation->getSource().c_str(), localtime(&unixTime));
     253              :         //if (size > 1) { // convert TZ offset to RFC3339 format
     254              :         //    char minute[] = { buffer[size-2], buffer[size-1], '\0' };
     255              :         //    sprintf(buffer + size - 2, ":%s", minute);
     256              :         //}
     257              : 
     258            2 :         sourceVault.setStringReplacingVariables(std::string(buffer), transformation->getSourcePatterns(), variables, global_variable_); // replace variables if they exist
     259            1 :         break;
     260              :     }
     261            2 :     case Transformation::SourceType::Recvseq:
     262              :     {
     263            2 :         sourceVault.setUnsigned(generalUniqueServerSequence);
     264            2 :         break;
     265              :     }
     266           16 :     case Transformation::SourceType::SVar:
     267              :     {
     268           16 :         std::string varname = transformation->getSource();
     269           16 :         replaceVariables(varname, transformation->getSourcePatterns(), variables, global_variable_);
     270           16 :         auto iter = variables.find(varname);
     271           16 :         if (iter != variables.end()) sourceVault.setString(iter->second);
     272              :         else {
     273            5 :             LOGDEBUG(
     274              :                 std::string msg = ert::tracing::Logger::asString("Unable to extract source variable '%s' in transformation item", varname.c_str());
     275              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     276              :             );
     277            5 :             return false;
     278              :         }
     279           11 :         break;
     280           16 :     }
     281            9 :     case Transformation::SourceType::SGVar:
     282              :     {
     283            9 :         std::string varname = transformation->getSource();
     284            9 :         replaceVariables(varname, transformation->getSourcePatterns(), variables, global_variable_);
     285            9 :         std::string globalVariableValue{};
     286            9 :         bool exists = global_variable_->tryGet(varname, globalVariableValue);
     287            9 :         if (exists) sourceVault.setString(globalVariableValue);
     288              :         else {
     289            2 :             LOGDEBUG(
     290              :                 std::string msg = ert::tracing::Logger::asString("Unable to extract source global variable '%s' in transformation item", varname.c_str());
     291              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     292              :             );
     293            2 :             return false;
     294              :         }
     295            7 :         break;
     296           18 :     }
     297           46 :     case Transformation::SourceType::Value:
     298              :     {
     299           46 :         sourceVault.setStringReplacingVariables(transformation->getSource(), transformation->getSourcePatterns(), variables, global_variable_); // replace variables if they exist
     300           46 :         break;
     301              :     }
     302            3 :     case Transformation::SourceType::ServerEvent:
     303              :     {
     304              :         // transformation->getSourceTokenized() is a vector:
     305              :         //
     306              :         // requestMethod: index 0
     307              :         // requestUri:    index 1
     308              :         // eventNumber:   index 2
     309              :         // eventPath:     index 3
     310            3 :         std::string event_method = transformation->getSourceTokenized()[0];
     311            3 :         replaceVariables(event_method, transformation->getSourcePatterns(), variables, global_variable_);
     312            3 :         std::string event_uri = transformation->getSourceTokenized()[1];
     313            3 :         replaceVariables(event_uri, transformation->getSourcePatterns(), variables, global_variable_);
     314            3 :         std::string event_number = transformation->getSourceTokenized()[2];
     315            3 :         replaceVariables(event_number, transformation->getSourcePatterns(), variables, global_variable_);
     316            3 :         std::string event_path = transformation->getSourceTokenized()[3];
     317            3 :         replaceVariables(event_path, transformation->getSourcePatterns(), variables, global_variable_);
     318              : 
     319              :         // Now, access the server data for the former selection values:
     320            3 :         nlohmann::json object;
     321            3 :         EventKey ekey(event_method, event_uri, event_number);
     322            3 :         auto mockServerRequest = mock_server_events_data_->getEvent(ekey);
     323            3 :         if (!mockServerRequest) {
     324            1 :             LOGDEBUG(
     325              :                 std::string msg = ert::tracing::Logger::asString("Unable to extract server event for variable '%s' in transformation item", transformation->getSource().c_str());
     326              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     327              :             );
     328            1 :             return false;
     329              :         }
     330              : 
     331            6 :         if (!sourceVault.setObject(mockServerRequest->getJson(), event_path /* document path (empty or not to be whole 'requests number' or node) */)) {
     332            1 :             LOGDEBUG(
     333              :                 std::string msg = ert::tracing::Logger::asString("Unexpected error extracting server event for variable '%s' in transformation item", transformation->getSource().c_str());
     334              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     335              :             );
     336            1 :             return false;
     337              :         }
     338              : 
     339            1 :         LOGDEBUG(
     340              :             std::string msg = ert::tracing::Logger::asString("Extracted object from server event: %s", sourceVault.asString().c_str());
     341              :             ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     342              :         );
     343            1 :         break;
     344           21 :     }
     345            3 :     case Transformation::SourceType::InState:
     346              :     {
     347            3 :         sourceVault.setString(getInState());
     348            3 :         break;
     349              :     }
     350            2 :     case Transformation::SourceType::STxtFile:
     351              :     {
     352            2 :         std::string path = transformation->getSource();
     353            2 :         replaceVariables(path, transformation->getSourcePatterns(), variables, global_variable_);
     354              : 
     355            2 :         std::string content;
     356            2 :         file_manager_->read(path, content, true/*text*/);
     357            2 :         sourceVault.setString(std::move(content));
     358            2 :         break;
     359            2 :     }
     360            2 :     case Transformation::SourceType::SBinFile:
     361              :     {
     362            2 :         std::string path = transformation->getSource();
     363            2 :         replaceVariables(path, transformation->getSourcePatterns(), variables, global_variable_);
     364              : 
     365            2 :         std::string content;
     366            2 :         file_manager_->read(path, content, false/*binary*/);
     367            2 :         sourceVault.setString(std::move(content));
     368            2 :         break;
     369            2 :     }
     370            2 :     case Transformation::SourceType::Command:
     371              :     {
     372            2 :         std::string command = transformation->getSource();
     373            2 :         replaceVariables(command, transformation->getSourcePatterns(), variables, global_variable_);
     374              : 
     375              :         static char buffer[256];
     376            2 :         std::string output{};
     377              : 
     378            2 :         FILE *fp = popen(command.c_str(), "r");
     379            2 :         variables["rc"] = "-1"; // rare case where fp could be NULL
     380            2 :         if (fp) {
     381              :             /* This makes asyncronous the command execution, but we will have broken pipe and cannot capture anything.
     382              :             // fgets is blocking (https://stackoverflow.com/questions/6055702/using-fgets-as-non-blocking-function-c/6055774#6055774)
     383              :             int fd = fileno(fp);
     384              :             int flags = fcntl(fd, F_GETFL, 0);
     385              :             flags |= O_NONBLOCK;
     386              :             fcntl(fd, F_SETFL, flags);
     387              :             */
     388              : 
     389            3 :             while(fgets(buffer, sizeof(buffer), fp))
     390              :             {
     391            1 :                 output += buffer;
     392              :             }
     393            6 :             variables["rc"] = std::to_string(WEXITSTATUS(/* status = */pclose(fp))); // rc = status >>= 8; // divide by 256
     394              :         }
     395              : 
     396            2 :         sourceVault.setString(std::move(output));
     397            2 :         break;
     398            2 :     }
     399            3 :     case Transformation::SourceType::ClientEvent:
     400              :     {
     401              :         // transformation->getSourceTokenized() is a vector:
     402              :         //
     403              :         // clientEndpointId: index 0
     404              :         // requestMethod:    index 1
     405              :         // requestUri:       index 2
     406              :         // eventNumber:      index 3
     407              :         // eventPath:        index 4
     408            3 :         std::string event_endpoint = transformation->getSourceTokenized()[0];
     409            3 :         replaceVariables(event_endpoint, transformation->getSourcePatterns(), variables, global_variable_);
     410            3 :         std::string event_method = transformation->getSourceTokenized()[1];
     411            3 :         replaceVariables(event_method, transformation->getSourcePatterns(), variables, global_variable_);
     412            3 :         std::string event_uri = transformation->getSourceTokenized()[2];
     413            3 :         replaceVariables(event_uri, transformation->getSourcePatterns(), variables, global_variable_);
     414            3 :         std::string event_number = transformation->getSourceTokenized()[3];
     415            3 :         replaceVariables(event_number, transformation->getSourcePatterns(), variables, global_variable_);
     416            3 :         std::string event_path = transformation->getSourceTokenized()[4];
     417            3 :         replaceVariables(event_path, transformation->getSourcePatterns(), variables, global_variable_);
     418              : 
     419            3 :         DataKey dkey(event_endpoint, event_method, event_uri);
     420            3 :         EventKey ekey(dkey, event_number);
     421            3 :         auto mockClientRequest = mock_client_events_data_->getEvent(ekey);
     422            3 :         if (!mockClientRequest) {
     423            1 :             LOGDEBUG(
     424              :                 std::string msg = ert::tracing::Logger::asString("Unable to extract client event for variable '%s' in transformation item", transformation->getSource().c_str());
     425              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     426              :             );
     427            1 :             return false;
     428              :         }
     429              : 
     430            6 :         if (!sourceVault.setObject(mockClientRequest->getJson(), event_path)) {
     431            1 :             LOGDEBUG(
     432              :                 std::string msg = ert::tracing::Logger::asString("Unexpected error extracting client event for variable '%s' in transformation item", transformation->getSource().c_str());
     433              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     434              :             );
     435            1 :             return false;
     436              :         }
     437              : 
     438            1 :         LOGDEBUG(
     439              :             std::string msg = ert::tracing::Logger::asString("Extracted object from client event: %s", sourceVault.asString().c_str());
     440              :             ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     441              :         );
     442            1 :         break;
     443           24 :     }
     444              :     // Not applicable in server context:
     445            0 :     case Transformation::SourceType::Sendseq:
     446              :     case Transformation::SourceType::Seq:
     447              :     case Transformation::SourceType::ResponseHeader:
     448              :     case Transformation::SourceType::ResponseStatusCode:
     449            0 :         return false;
     450              :     }
     451              : 
     452              : 
     453          121 :     return true;
     454              : }
     455              : 
     456           25 : bool AdminServerProvision::processFilters(std::shared_ptr<Transformation> transformation,
     457              :         TypeConverter& sourceVault,
     458              :         const std::map<std::string, std::string>& variables,
     459              :         std::smatch &matches,
     460              :         std::string &source) const
     461              : {
     462           25 :     bool success = false;
     463           25 :     std::string targetS;
     464           25 :     std::int64_t targetI = 0;
     465           25 :     std::uint64_t targetU = 0;
     466           25 :     double targetF = 0;
     467              : 
     468              :     // all the filters except Sum/Multiply, require a string target
     469           25 :     if (transformation->getFilterType() != Transformation::FilterType::Sum && transformation->getFilterType() != Transformation::FilterType::Multiply) {
     470           19 :         source = sourceVault.getString(success);
     471           19 :         if (!success) return false;
     472              :     }
     473              : 
     474              :     // All our regex are built with 'std::regex::optimize' so they are already validated and regex functions cannot throw exception:
     475              :     //try { // std::regex exceptions
     476           25 :     switch (transformation->getFilterType()) {
     477            3 :     case Transformation::FilterType::RegexCapture:
     478              :     {
     479            3 :         if (std::regex_match(source, matches, transformation->getFilterRegex()) && matches.size() >=1) {
     480            2 :             targetS = matches.str(0);
     481            2 :             sourceVault.setString(targetS);
     482            2 :             LOGDEBUG(
     483              :                 std::stringstream ss;
     484              :                 ss << "Regex matches: Size = " << matches.size();
     485              :             for(size_t i=0; i < matches.size(); i++) {
     486              :             ss << " | [" << i << "] = " << matches.str(i);
     487              :             }
     488              :             ert::tracing::Logger::debug(ss.str(), ERT_FILE_LOCATION);
     489              :             );
     490              :         }
     491              :         else {
     492            1 :             LOGDEBUG(
     493              :                 std::string msg = ert::tracing::Logger::asString("Unable to match '%s' againt regex capture '%s' in transformation item", source.c_str(), transformation->getFilter().c_str());
     494              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
     495              :             );
     496            1 :             return false;
     497              :         }
     498            2 :         break;
     499              :     }
     500            1 :     case Transformation::FilterType::RegexReplace:
     501              :     {
     502            1 :         targetS = std::regex_replace (source, transformation->getFilterRegex(), transformation->getFilter() /* fmt */);
     503            1 :         sourceVault.setString(targetS);
     504            1 :         break;
     505              :     }
     506            1 :     case Transformation::FilterType::Append:
     507              :     {
     508            1 :         std::string filter = transformation->getFilter();
     509            1 :         replaceVariables(filter, transformation->getFilterPatterns(), variables, global_variable_);
     510              : 
     511            1 :         targetS = source + filter;
     512            1 :         sourceVault.setString(targetS);
     513            1 :         break;
     514            1 :     }
     515            1 :     case Transformation::FilterType::Prepend:
     516              :     {
     517            1 :         std::string filter = transformation->getFilter();
     518            1 :         replaceVariables(filter, transformation->getFilterPatterns(), variables, global_variable_);
     519              : 
     520            1 :         targetS = filter + source;
     521            1 :         sourceVault.setString(targetS);
     522            1 :         break;
     523            1 :     }
     524            3 :     case Transformation::FilterType::Sum:
     525              :     {
     526            3 :         switch (transformation->getFilterNumberType()) {
     527            1 :         case 0: /* integer */
     528              :         {
     529            1 :             targetI = sourceVault.getInteger(success);
     530            1 :             if (success) targetI += transformation->getFilterI();
     531              :             //else return false; // should not happen (protected by schema)
     532            1 :             sourceVault.setInteger(targetI);
     533            1 :             break;
     534              :         }
     535            1 :         case 1: /* unsigned */
     536              :         {
     537            1 :             targetU = sourceVault.getUnsigned(success);
     538            1 :             if (success) targetU += transformation->getFilterU();
     539              :             //else return false; // should not happen (protected by schema)
     540            1 :             sourceVault.setUnsigned(targetU);
     541            1 :             break;
     542              :         }
     543            1 :         case 2: /* double */
     544              :         {
     545            1 :             targetF = sourceVault.getFloat(success);
     546            1 :             if (success) targetF += transformation->getFilterF();
     547              :             //else return false; // should not happen (protected by schema)
     548            1 :             sourceVault.setFloat(targetF);
     549            1 :             break;
     550              :         }
     551              :         }
     552            3 :         break;
     553              :     }
     554            3 :     case Transformation::FilterType::Multiply:
     555              :     {
     556            3 :         switch (transformation->getFilterNumberType()) {
     557            1 :         case 0: /* integer */
     558              :         {
     559            1 :             targetI = sourceVault.getInteger(success);
     560            1 :             if (success) targetI *= transformation->getFilterI();
     561              :             //else return false; // should not happen (protected by schema)
     562            1 :             sourceVault.setInteger(targetI);
     563            1 :             break;
     564              :         }
     565            1 :         case 1: /* unsigned */
     566              :         {
     567            1 :             targetU = sourceVault.getUnsigned(success);
     568            1 :             if (success) targetU *= transformation->getFilterU();
     569              :             //else return false; // should not happen (protected by schema)
     570            1 :             sourceVault.setUnsigned(targetU);
     571            1 :             break;
     572              :         }
     573            1 :         case 2: /* double */
     574              :         {
     575            1 :             targetF = sourceVault.getFloat(success);
     576            1 :             if (success) targetF *= transformation->getFilterF();
     577              :             //else return false; // should not happen (protected by schema)
     578            1 :             sourceVault.setFloat(targetF);
     579            1 :             break;
     580              :         }
     581              :         }
     582            3 :         break;
     583              :     }
     584            4 :     case Transformation::FilterType::ConditionVar: // TODO: if condition is false, source storage could be omitted to improve performance
     585              :     {
     586              :         // Get variable value for the variable name 'transformation->getFilter()':
     587            4 :         std::string varname = transformation->getFilter();
     588            4 :         bool reverse = (transformation->getFilter()[0] == '!');
     589            4 :         if (reverse) {
     590            2 :             varname.erase(0,1);
     591              :         }
     592            4 :         auto iter = variables.find(varname);
     593            4 :         bool varFound = (iter != variables.end());
     594            4 :         std::string varvalue{};
     595            4 :         if (varFound) {
     596            1 :             varvalue = iter->second;
     597            1 :             LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Variable '%s' found (local)", varname.c_str()), ERT_FILE_LOCATION));
     598              :         }
     599              :         else {
     600            3 :             varFound = global_variable_->tryGet(varname, varvalue);
     601            3 :             LOGDEBUG(if (varFound) ert::tracing::Logger::debug(ert::tracing::Logger::asString("Variable '%s' found (global)", varname.c_str()), ERT_FILE_LOCATION));
     602              :         }
     603              : 
     604            4 :         bool conditionVar = (varFound && !(varvalue.empty()));
     605            4 :         LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Variable value: '%s'", (varFound ? varvalue.c_str():"<undefined>")), ERT_FILE_LOCATION));
     606              : 
     607            4 :         if ((reverse && !conditionVar)||(!reverse && conditionVar)) {
     608            3 :             LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("%sConditionVar is true", (reverse ? "!":"")), ERT_FILE_LOCATION));
     609            3 :             sourceVault.setString(source);
     610              :         }
     611              :         else {
     612            1 :             LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("%sConditionVar is false", (reverse ? "!":"")), ERT_FILE_LOCATION));
     613            1 :             return false;
     614              :         }
     615            3 :         break;
     616            8 :     }
     617            2 :     case Transformation::FilterType::EqualTo:
     618              :     {
     619            2 :         std::string filter = transformation->getFilter();
     620            2 :         replaceVariables(filter, transformation->getFilterPatterns(), variables, global_variable_);
     621              : 
     622              :         // Get value for the comparison 'transformation->getFilter()':
     623            2 :         if (source == filter) {
     624            1 :             LOGDEBUG(ert::tracing::Logger::debug("EqualTo is true", ERT_FILE_LOCATION));
     625            1 :             sourceVault.setString(source);
     626              :         }
     627              :         else {
     628            1 :             LOGDEBUG(ert::tracing::Logger::debug("EqualTo is false", ERT_FILE_LOCATION));
     629            1 :             return false;
     630              :         }
     631            1 :         break;
     632            2 :     }
     633            2 :     case Transformation::FilterType::DifferentFrom:
     634              :     {
     635            2 :         std::string filter = transformation->getFilter();
     636            2 :         replaceVariables(filter, transformation->getFilterPatterns(), variables, global_variable_);
     637              : 
     638              :         // Get value for the comparison 'transformation->getFilter()':
     639            2 :         if (source != filter) {
     640            1 :             LOGDEBUG(ert::tracing::Logger::debug("DifferentFrom is true", ERT_FILE_LOCATION));
     641            1 :             sourceVault.setString(source);
     642              :         }
     643              :         else {
     644            1 :             LOGDEBUG(ert::tracing::Logger::debug("DifferentFrom is false", ERT_FILE_LOCATION));
     645            1 :             return false;
     646              :         }
     647            1 :         break;
     648            2 :     }
     649            3 :     case Transformation::FilterType::JsonConstraint:
     650              :     {
     651            3 :         nlohmann::json sobj = sourceVault.getObject(success);
     652              :         // should not happen (protected by schema)
     653              :         //if (!success) {
     654              :         //    LOGDEBUG(ert::tracing::Logger::debug("Source provided for JsonConstraint filter must be a valid json object", ERT_FILE_LOCATION));
     655              :         //    return false;
     656              :         //}
     657            3 :         std::string failReport;
     658            3 :         if (h2agent::model::jsonConstraint(sobj, transformation->getFilterObject(), failReport)) {
     659            2 :             sourceVault.setString("1");
     660              :         }
     661              :         else {
     662            2 :             sourceVault.setString(failReport);
     663              :         }
     664            3 :         break;
     665            3 :     }
     666            2 :     case Transformation::FilterType::SchemaId:
     667              :     {
     668            2 :         nlohmann::json sobj = sourceVault.getObject(success);
     669              :         // should not happen (protected by schema)
     670              :         //if (!success) {
     671              :         //    LOGDEBUG(ert::tracing::Logger::debug("Source provided for SchemaId filter must be a valid json object", ERT_FILE_LOCATION));
     672              :         //    return false;
     673              :         //}
     674            2 :         std::string failReport;
     675            2 :         auto schema = admin_data_->getSchemaData().find(transformation->getFilter()); // TODO: find a way to cache this (set the schema into transformation: but clean schemas should be detected to avoid corruption)
     676            2 :         if (schema) {
     677            2 :             if (schema->validate(sobj, failReport)) {
     678            2 :                 sourceVault.setString("1");
     679              :             }
     680              :             else {
     681            1 :                 sourceVault.setString(failReport);
     682              :             }
     683              :         }
     684              :         else {
     685            0 :             ert::tracing::Logger::warning(ert::tracing::Logger::asString("Missing schema '%s' referenced in transformation item: VALIDATION will be IGNORED", transformation->getFilter().c_str()), ERT_FILE_LOCATION);
     686              :         }
     687            2 :         break;
     688            2 :     }
     689              :     }
     690              :     //}
     691              :     //catch (std::exception& e)
     692              :     //{
     693              :     //    ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
     694              :     //}
     695              : 
     696              : 
     697           21 :     return true;
     698           25 : }
     699              : 
     700          117 : bool AdminServerProvision::processTargets(std::shared_ptr<Transformation> transformation,
     701              :         TypeConverter &sourceVault,
     702              :         std::map<std::string, std::string>& variables,
     703              :         const std::smatch &matches,
     704              :         bool eraser,
     705              :         bool hasFilter,
     706              :         unsigned int &responseStatusCode,
     707              :         nlohmann::json &responseBodyJson,
     708              :         std::string &responseBodyAsString,
     709              :         nghttp2::asio_http2::header_map &responseHeaders,
     710              :         unsigned int &responseDelayMs,
     711              :         std::string &outState,
     712              :         std::string &outStateMethod,
     713              :         std::string &outStateUri,
     714              :         std::vector<std::pair<std::string, std::string>> &clientProvisionTriggers,
     715              :         bool &breakCondition) const
     716              : {
     717          117 :     bool success = false;
     718          117 :     std::string targetS;
     719          117 :     std::int64_t targetI = 0;
     720          117 :     std::uint64_t targetU = 0;
     721          117 :     double targetF = 0;
     722          117 :     bool boolean = false;
     723          117 :     nlohmann::json obj;
     724              : 
     725              : 
     726              :     try { // nlohmann::json exceptions
     727              : 
     728          117 :         std::string target = transformation->getTarget();
     729          117 :         std::string target2 = transformation->getTarget2(); // foreign outState URI
     730              : 
     731          117 :         replaceVariables(target, transformation->getTargetPatterns(), variables, global_variable_);
     732          117 :         if (!target2.empty()) {
     733            1 :             replaceVariables(target2, transformation->getTarget2Patterns(), variables, global_variable_);
     734              :         }
     735              : 
     736          117 :         switch (transformation->getTargetType()) {
     737           44 :         case Transformation::TargetType::ResponseBodyString:
     738              :         {
     739              :             // extraction
     740           44 :             targetS = sourceVault.getString(success);
     741           44 :             if (!success) return false;
     742              :             // assignment
     743           44 :             responseBodyAsString = targetS;
     744           44 :             break;
     745              :         }
     746            1 :         case Transformation::TargetType::ResponseBodyHexString:
     747              :         {
     748              :             // extraction
     749            1 :             targetS = sourceVault.getString(success);
     750            1 :             if (!success) return false;
     751              :             // assignment
     752            1 :             if (!h2agent::model::fromHexString(targetS, responseBodyAsString)) return false;
     753            1 :             break;
     754              :         }
     755           12 :         case Transformation::TargetType::ResponseBodyJson_String:
     756              :         {
     757              :             // extraction
     758           12 :             targetS = sourceVault.getString(success);
     759           12 :             if (!success) return false;
     760              :             // assignment
     761           12 :             nlohmann::json::json_pointer j_ptr(target);
     762           11 :             responseBodyJson[j_ptr] = targetS;
     763           11 :             break;
     764           11 :         }
     765            2 :         case Transformation::TargetType::ResponseBodyJson_Integer:
     766              :         {
     767              :             // extraction
     768            2 :             targetI = sourceVault.getInteger(success);
     769            2 :             if (!success) return false;
     770              :             // assignment
     771            2 :             nlohmann::json::json_pointer j_ptr(target);
     772            2 :             responseBodyJson[j_ptr] = targetI;
     773            2 :             break;
     774            2 :         }
     775            2 :         case Transformation::TargetType::ResponseBodyJson_Unsigned:
     776              :         {
     777              :             // extraction
     778            2 :             targetU = sourceVault.getUnsigned(success);
     779            2 :             if (!success) return false;
     780              :             // assignment
     781            2 :             nlohmann::json::json_pointer j_ptr(target);
     782            2 :             responseBodyJson[j_ptr] = targetU;
     783            2 :             break;
     784            2 :         }
     785            2 :         case Transformation::TargetType::ResponseBodyJson_Float:
     786              :         {
     787              :             // extraction
     788            2 :             targetF = sourceVault.getFloat(success);
     789            2 :             if (!success) return false;
     790              :             // assignment
     791            2 :             nlohmann::json::json_pointer j_ptr(target);
     792            2 :             responseBodyJson[j_ptr] = targetF;
     793            2 :             break;
     794            2 :         }
     795            2 :         case Transformation::TargetType::ResponseBodyJson_Boolean:
     796              :         {
     797              :             // extraction
     798            2 :             boolean = sourceVault.getBoolean(success);
     799            2 :             if (!success) return false;
     800              :             // assignment
     801            2 :             nlohmann::json::json_pointer j_ptr(target);
     802            2 :             responseBodyJson[j_ptr] = boolean;
     803            2 :             break;
     804            2 :         }
     805           14 :         case Transformation::TargetType::ResponseBodyJson_Object:
     806              :         {
     807              : 
     808           14 :             if (eraser) {
     809            2 :                 LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Eraser source into json path '%s'", target.c_str()), ERT_FILE_LOCATION));
     810            2 :                 if (target.empty()) {
     811            1 :                     responseBodyJson.erase(responseBodyJson.begin(), responseBodyJson.end());
     812            1 :                     return false;
     813              :                 }
     814              : 
     815              :                 //erase() DOES NOT SUPPORT JSON POINTERS:
     816              :                 //nlohmann::json::json_pointer j_ptr(target);
     817              :                 //responseBodyJson.erase(j_ptr);
     818              :                 //
     819              :                 // For a path '/a/b/c' we must access to /a/b and then erase "c":
     820            1 :                 size_t lastSlashPos = target.find_last_of("/");
     821              :                 // lastSlashPos will never be std::string::npos here
     822            1 :                 std::string parentPath = target.substr(0, lastSlashPos);
     823            1 :                 std::string childKey = "";
     824            1 :                 if (lastSlashPos + 1 < target.size()) childKey = target.substr(lastSlashPos + 1, target.size());
     825            1 :                 nlohmann::json::json_pointer j_ptr(parentPath);
     826            1 :                 responseBodyJson[j_ptr].erase(childKey);
     827            1 :                 return false;
     828            1 :             }
     829              : 
     830              :             // extraction will be object if possible, falling back to the rest of formats with this priority: string, integer, unsigned, float, boolean
     831              :             // assignment for valid extraction
     832           12 :             nlohmann::json::json_pointer j_ptr(target);
     833              : 
     834              :             // Native types for SOURCES:
     835              :             //
     836              :             // [string] request.uri, request.uri.path, request.header, randomset, strftime, var, globalVar, value, txtFile, binFile, command
     837              :             // [object] request.body, response.body, serverEvent  (when target is also object, it could be promoted to string, unsigned, integer, float or boolean).
     838              :             // [integer] random, timestamp
     839              :             // [unsigned] recvseq
     840              :             // [float] math.*
     841              :             // [boolean] NONE
     842              :             // So, depending on the target, the corresponding getter (getInteger, getString, etc.) will be used: WE DO NOT WANT FORCE CONVERSIONS:
     843              :             // Note that there is not sources with boolean as native type, so boolean getter is never reached (so commented to avoid UT coverage fault).
     844              :             //
     845           12 :             switch (sourceVault.getNativeType()) {
     846            2 :             case  TypeConverter::NativeType::Object:
     847            2 :                 obj = sourceVault.getObject(success);
     848            2 :                 if (success) {
     849            2 :                     if (target.empty()) {
     850            1 :                         responseBodyJson.merge_patch(obj); // merge origin by default for target response.body.json.object
     851              :                     }
     852              :                     else {
     853            1 :                         responseBodyJson[j_ptr] = obj;
     854              :                     }
     855              :                 }
     856            2 :                 break;
     857              : 
     858            2 :             case  TypeConverter::NativeType::String:
     859            2 :                 targetS = sourceVault.getString(success);
     860            2 :                 if (success) responseBodyJson[j_ptr] = targetS;
     861            2 :                 break;
     862              : 
     863            5 :             case  TypeConverter::NativeType::Integer:
     864            5 :                 targetI = sourceVault.getInteger(success);
     865            5 :                 if (success) responseBodyJson[j_ptr] = targetI;
     866            5 :                 break;
     867              : 
     868            1 :             case  TypeConverter::NativeType::Unsigned:
     869            1 :                 targetU = sourceVault.getUnsigned(success);
     870            1 :                 if (success) responseBodyJson[j_ptr] = targetU;
     871            1 :                 break;
     872              : 
     873            1 :             case  TypeConverter::NativeType::Float:
     874            1 :                 targetF = sourceVault.getFloat(success);
     875            1 :                 if (success) responseBodyJson[j_ptr] = targetF;
     876            1 :                 break;
     877              : 
     878              :             // Not reached at the moment:
     879            1 :             case  TypeConverter::NativeType::Boolean:
     880            1 :                 boolean = sourceVault.getBoolean(success);
     881            1 :                 if (success) responseBodyJson[j_ptr] = boolean;
     882            1 :                 break;
     883              :             }
     884           12 :             break;
     885           12 :         }
     886            3 :         case Transformation::TargetType::ResponseBodyJson_JsonString:
     887              :         {
     888              : 
     889              :             // assignment for valid extraction
     890            3 :             nlohmann::json::json_pointer j_ptr(target);
     891              : 
     892              :             // extraction
     893            3 :             targetS = sourceVault.getString(success);
     894            3 :             if (!success) return false;
     895            3 :             if (!h2agent::model::parseJsonContent(targetS, obj))
     896            1 :                 return false;
     897              : 
     898              :             // assignment
     899            2 :             if (target.empty()) {
     900            1 :                 responseBodyJson.merge_patch(obj); // merge origin by default for target response.body.json.object
     901              :             }
     902              :             else {
     903            1 :                 responseBodyJson[j_ptr] = obj;
     904              :             }
     905            2 :             break;
     906            3 :         }
     907            1 :         case Transformation::TargetType::ResponseHeader_t:
     908              :         {
     909              :             // extraction
     910            1 :             targetS = sourceVault.getString(success);
     911            1 :             if (!success) return false;
     912              :             // assignment
     913            1 :             responseHeaders.emplace(target, nghttp2::asio_http2::header_value{targetS});
     914            1 :             break;
     915              :         }
     916            5 :         case Transformation::TargetType::ResponseStatusCode_t:
     917              :         {
     918              :             // extraction
     919            5 :             targetU = sourceVault.getUnsigned(success);
     920            5 :             if (!success) return false;
     921              :             // assignment
     922            5 :             responseStatusCode = targetU;
     923            5 :             break;
     924              :         }
     925            1 :         case Transformation::TargetType::ResponseDelayMs:
     926              :         {
     927              :             // extraction
     928            1 :             targetU = sourceVault.getUnsigned(success);
     929            1 :             if (!success) return false;
     930              :             // assignment
     931            1 :             responseDelayMs = targetU;
     932            1 :             break;
     933              :         }
     934            9 :         case Transformation::TargetType::TVar:
     935              :         {
     936            9 :             if (hasFilter && transformation->getFilterType() == Transformation::FilterType::RegexCapture) {
     937            1 :                 std::string varname;
     938            1 :                 if (matches.size() >=1) { // this protection shouldn't be needed as it would be continued above on RegexCapture matching...
     939            1 :                     variables[target] = matches.str(0); // variable "as is" stores the entire match
     940            4 :                     for(size_t i=1; i < matches.size(); i++) {
     941            3 :                         varname = target;
     942            3 :                         varname += ".";
     943            3 :                         varname += std::to_string(i);
     944            3 :                         variables[varname] = matches.str(i);
     945            3 :                         LOGDEBUG(
     946              :                             std::stringstream ss;
     947              :                             ss << "Variable '" << varname << "' takes value '" << matches.str(i) << "'";
     948              :                             ert::tracing::Logger::debug(ss.str(), ERT_FILE_LOCATION);
     949              :                         );
     950              :                     }
     951              :                 }
     952            1 :             }
     953              :             else {
     954              :                 // extraction
     955            8 :                 targetS = sourceVault.getString(success);
     956            8 :                 if (!success) return false;
     957              : 
     958            8 :                 if (hasFilter) {
     959            4 :                     if(transformation->getFilterType() == Transformation::FilterType::JsonConstraint) {
     960            1 :                         if (targetS != "1") { // this is a fail report
     961            1 :                             variables[target + ".fail"] = targetS;
     962            1 :                             targetS = "";
     963              :                         }
     964              :                     }
     965            3 :                     else if (transformation->getFilterType() == Transformation::FilterType::SchemaId) {
     966            0 :                         if (targetS != "1") { // this is a fail report
     967            0 :                             variables[target + ".fail"] = targetS;
     968            0 :                             targetS = "";
     969              :                         }
     970              :                     }
     971              :                 }
     972              : 
     973              :                 // assignment
     974            8 :                 variables[target] = targetS;
     975              :             }
     976            9 :             break;
     977              :         }
     978            4 :         case Transformation::TargetType::TGVar:
     979              :         {
     980            4 :             if (eraser) {
     981              :                 bool exists;
     982            1 :                 global_variable_->remove(target, exists);
     983            1 :                 LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Eraser source into global variable '%s' (%s)", target.c_str(), exists ? "removed":"missing"), ERT_FILE_LOCATION));
     984              :             }
     985            3 :             else if (hasFilter && transformation->getFilterType() == Transformation::FilterType::RegexCapture) {
     986            1 :                 std::string varname;
     987            1 :                 if (matches.size() >=1) { // this protection shouldn't be needed as it would be continued above on RegexCapture matching...
     988            1 :                     global_variable_->load(target, matches.str(0)); // variable "as is" stores the entire match
     989            4 :                     for(size_t i=1; i < matches.size(); i++) {
     990            3 :                         varname = target;
     991            3 :                         varname += ".";
     992            3 :                         varname += std::to_string(i);
     993            3 :                         global_variable_->load(varname, matches.str(i));
     994            3 :                         LOGDEBUG(
     995              :                             std::stringstream ss;
     996              :                             ss << "Variable '" << varname << "' takes value '" << matches.str(i) << "'";
     997              :                             ert::tracing::Logger::debug(ss.str(), ERT_FILE_LOCATION);
     998              :                         );
     999              :                     }
    1000              :                 }
    1001            1 :             }
    1002              :             else {
    1003              :                 // extraction
    1004            2 :                 targetS = sourceVault.getString(success);
    1005            2 :                 if (!success) return false;
    1006              :                 // assignment
    1007            2 :                 global_variable_->load(target, targetS);
    1008              :             }
    1009            4 :             break;
    1010              :         }
    1011            2 :         case Transformation::TargetType::OutState:
    1012              :         {
    1013              :             // extraction
    1014            2 :             targetS = sourceVault.getString(success);
    1015            2 :             if (!success) return false;
    1016              :             // assignments
    1017            2 :             outState = targetS;
    1018            2 :             outStateMethod = target; // empty on regular usage
    1019            2 :             outStateUri = target2; // empty on regular usage
    1020            2 :             break;
    1021              :         }
    1022            2 :         case Transformation::TargetType::TTxtFile:
    1023              :         {
    1024              :             // extraction
    1025            2 :             targetS = sourceVault.getString(success);
    1026            2 :             if (!success) return false;
    1027              : 
    1028            2 :             if (eraser) {
    1029            1 :                 LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Eraser source into text file '%s'", target.c_str()), ERT_FILE_LOCATION));
    1030            1 :                 file_manager_->empty(target/*path*/);
    1031              :             }
    1032              :             else {
    1033              :                 // assignments
    1034            1 :                 bool longTerm =(transformation->getTargetPatterns().empty()); // path is considered fixed (long term files), instead of arbitrary and dynamic (short term files)
    1035              :                 // even if @{varname} is missing (empty value) we consider the intention to allow force short term
    1036              :                 // files type.
    1037            1 :                 file_manager_->write(target/*path*/, targetS/*data*/, true/*text*/, (longTerm ? configuration_->getLongTermFilesCloseDelayUsecs():configuration_->getShortTermFilesCloseDelayUsecs()));
    1038              :             }
    1039            2 :             break;
    1040              :         }
    1041            2 :         case Transformation::TargetType::TBinFile:
    1042              :         {
    1043              :             // extraction
    1044            2 :             targetS = sourceVault.getString(success);
    1045            2 :             if (!success) return false;
    1046              : 
    1047            2 :             if (eraser) {
    1048            1 :                 LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Eraser source into binary file '%s'", target.c_str()), ERT_FILE_LOCATION));
    1049            1 :                 file_manager_->empty(target/*path*/);
    1050              :             }
    1051              :             else {
    1052              :                 // assignments
    1053            1 :                 bool longTerm =(transformation->getTargetPatterns().empty()); // path is considered fixed (long term files), instead of arbitrary and dynamic (short term files)
    1054              :                 // even if @{varname} is missing (empty value) we consider the intention to allow force short term
    1055              :                 // files type.
    1056            1 :                 file_manager_->write(target/*path*/, targetS/*data*/, false/*binary*/, (longTerm ? configuration_->getLongTermFilesCloseDelayUsecs():configuration_->getShortTermFilesCloseDelayUsecs()));
    1057              :             }
    1058            2 :             break;
    1059              :         }
    1060            1 :         case Transformation::TargetType::UDPSocket:
    1061              :         {
    1062              :             // extraction
    1063            1 :             targetS = sourceVault.getString(success);
    1064            1 :             if (!success) return false;
    1065              : 
    1066              :             // assignments
    1067              :             // Possible delay provided in 'target': <path>|<delay>
    1068            1 :             std::string path = target;
    1069            1 :             size_t lastDotPos = target.find_last_of("|");
    1070            1 :             unsigned int delayMs = atoi(target.substr(lastDotPos + 1).c_str());
    1071            1 :             path = target.substr(0, lastDotPos);
    1072              : 
    1073            1 :             LOGDEBUG(
    1074              :                 std::string msg = ert::tracing::Logger::asString("UDPSocket '%s' target, delayed %u milliseconds, in transformation item", path.c_str(), delayMs);
    1075              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
    1076              :             );
    1077              : 
    1078            1 :             socket_manager_->write(path, targetS/*data*/, delayMs * 1000 /* usecs */);
    1079            1 :             break;
    1080            1 :         }
    1081            3 :         case Transformation::TargetType::ServerEventToPurge:
    1082              :         {
    1083            3 :             if (!eraser) {
    1084            1 :                 LOGDEBUG(ert::tracing::Logger::debug("'ServerEventToPurge' target type only works with 'eraser' source type. This transformation will be ignored.", ERT_FILE_LOCATION));
    1085            2 :                 return false;
    1086              :             }
    1087              :             // transformation->getTargetTokenized() is a vector:
    1088              :             //
    1089              :             // requestMethod: index 0
    1090              :             // requestUri:    index 1
    1091              :             // eventNumber:   index 2
    1092            2 :             std::string event_method = transformation->getTargetTokenized()[0];
    1093            2 :             replaceVariables(event_method, transformation->getTargetPatterns(), variables, global_variable_);
    1094            2 :             std::string event_uri = transformation->getTargetTokenized()[1];
    1095            2 :             replaceVariables(event_uri, transformation->getTargetPatterns(), variables, global_variable_);
    1096            2 :             std::string event_number = transformation->getTargetTokenized()[2];
    1097            2 :             replaceVariables(event_number, transformation->getTargetPatterns(), variables, global_variable_);
    1098              : 
    1099            2 :             bool serverDataDeleted = false;
    1100            2 :             EventKey ekey(event_method, event_uri, event_number);
    1101            2 :             bool success = mock_server_events_data_->clear(serverDataDeleted, ekey);
    1102              : 
    1103            2 :             if (!success) {
    1104            1 :                 LOGDEBUG(
    1105              :                     std::string msg = ert::tracing::Logger::asString("Unexpected error while removing server data event '%s' in transformation item", transformation->getTarget().c_str());
    1106              :                     ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
    1107              :                 );
    1108            1 :                 return false;
    1109              :             }
    1110              : 
    1111            1 :             LOGDEBUG(
    1112              :                 std::string msg = ert::tracing::Logger::asString("Server event '%s' removal result: %s", transformation->getTarget().c_str(), (serverDataDeleted ? "SUCCESS":"NOTHING REMOVED"));
    1113              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
    1114              :             );
    1115            1 :             break;
    1116            8 :         }
    1117            3 :         case Transformation::TargetType::ClientProvision_t:
    1118              :         {
    1119            3 :             std::string inState = DEFAULT_ADMIN_PROVISION_STATE; // "initial" by default
    1120            3 :             if (!eraser) {
    1121            2 :                 targetS = sourceVault.getString(success);
    1122            2 :                 if (success && !targetS.empty()) inState = targetS;
    1123              :             }
    1124            3 :             std::string clientProvisionId = transformation->getTarget();
    1125            3 :             replaceVariables(clientProvisionId, transformation->getTargetPatterns(), variables, global_variable_);
    1126            3 :             replaceVariables(inState, transformation->getSourcePatterns(), variables, global_variable_);
    1127            3 :             clientProvisionTriggers.emplace_back(clientProvisionId, inState);
    1128            3 :             LOGDEBUG(
    1129              :                 std::string msg = ert::tracing::Logger::asString("Scheduled client provision trigger: id='%s', inState='%s'", clientProvisionId.c_str(), inState.c_str());
    1130              :                 ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
    1131              :             );
    1132            3 :             break;
    1133            3 :         }
    1134            2 :         case Transformation::TargetType::Break:
    1135              :         {
    1136              :             // extraction
    1137            2 :             targetS = sourceVault.getString(success);
    1138            2 :             if (!success) return false;
    1139              :             // assignments
    1140            2 :             if (targetS.empty()) {
    1141            1 :                 LOGDEBUG(ert::tracing::Logger::debug("Break action ignored (empty string as source provided)", ERT_FILE_LOCATION));
    1142            1 :                 return false;
    1143              :             }
    1144              : 
    1145            1 :             breakCondition = true;
    1146            1 :             LOGDEBUG(ert::tracing::Logger::debug("Break action triggered: ignoring remaining transformation items", ERT_FILE_LOCATION));
    1147            1 :             return false;
    1148              :             break;
    1149              :         }
    1150              :         // this won't happen due to schema for server target types:
    1151            0 :         case Transformation::TargetType::RequestBodyString:
    1152              :         case Transformation::TargetType::RequestBodyHexString:
    1153              :         case Transformation::TargetType::RequestBodyJson_String:
    1154              :         case Transformation::TargetType::RequestBodyJson_Integer:
    1155              :         case Transformation::TargetType::RequestBodyJson_Unsigned:
    1156              :         case Transformation::TargetType::RequestBodyJson_Float:
    1157              :         case Transformation::TargetType::RequestBodyJson_Boolean:
    1158              :         case Transformation::TargetType::RequestBodyJson_Object:
    1159              :         case Transformation::TargetType::RequestBodyJson_JsonString:
    1160              :         case Transformation::TargetType::RequestHeader_t:
    1161              :         case Transformation::TargetType::RequestDelayMs:
    1162              :         case Transformation::TargetType::RequestTimeoutMs:
    1163              :         case Transformation::TargetType::ClientEventToPurge:
    1164              :         case Transformation::TargetType::RequestUri_t:
    1165              :         case Transformation::TargetType::RequestMethod_t:
    1166            0 :             break;
    1167              :         }
    1168          125 :     }
    1169            1 :     catch (std::exception& e)
    1170              :     {
    1171            1 :         ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
    1172            1 :     }
    1173              : 
    1174              : 
    1175          110 :     return true;
    1176          117 : }
    1177              : 
    1178          105 : void AdminServerProvision::transform( const std::string &requestUri,
    1179              :                                       const std::string &requestUriPath,
    1180              :                                       const std::map<std::string, std::string> &requestQueryParametersMap,
    1181              :                                       DataPart &requestBodyDataPart,
    1182              :                                       const nghttp2::asio_http2::header_map &requestHeaders,
    1183              :                                       std::uint64_t generalUniqueServerSequence,
    1184              : 
    1185              :                                       /* OUTPUT PARAMETERS WHICH ALREADY HAVE DEFAULT VALUES BEFORE TRANSFORMATIONS: */
    1186              :                                       unsigned int &responseStatusCode,
    1187              :                                       nghttp2::asio_http2::header_map &responseHeaders,
    1188              :                                       std::string &responseBody,
    1189              :                                       unsigned int &responseDelayMs,
    1190              :                                       std::string &outState,
    1191              :                                       std::string &outStateMethod,
    1192              :                                       std::string &outStateUri,
    1193              :                                       std::vector<std::pair<std::string, std::string>> &clientProvisionTriggers,
    1194              :                                       std::map<std::string, std::string> &variables
    1195              :                                     )
    1196              : {
    1197              :     // Default values without transformations:
    1198          105 :     responseStatusCode = getResponseCode();
    1199          105 :     responseHeaders = getResponseHeaders();
    1200          105 :     responseDelayMs = getResponseDelayMilliseconds();
    1201          105 :     outState = getOutState(); // prepare next request state, with URI path before transformed with matching algorithms
    1202          105 :     outStateMethod = "";
    1203          105 :     outStateUri = "";
    1204              : 
    1205              :     // Check if the request body must be decoded:
    1206          105 :     bool mustDecodeRequestBody = false;
    1207          105 :     if (getRequestSchema()) {
    1208            2 :         mustDecodeRequestBody = true;
    1209              :     }
    1210              :     else {
    1211          222 :         for (const auto &t : transformations_) {
    1212          135 :             if (t->getSourceType() == Transformation::SourceType::RequestBody) {
    1213           16 :                 if (!requestBodyDataPart.str().empty()) {
    1214           15 :                     mustDecodeRequestBody = true;
    1215              :                 }
    1216              :                 else {
    1217            1 :                     LOGINFORMATIONAL(ert::tracing::Logger::informational("Empty request body received: some transformations will be ignored", ERT_FILE_LOCATION));
    1218              :                 }
    1219           16 :                 break;
    1220              :             }
    1221              :         }
    1222              :     }
    1223          105 :     if (mustDecodeRequestBody) {
    1224           17 :         requestBodyDataPart.decode(requestHeaders);
    1225              :     }
    1226              : 
    1227              :     // Request schema validation (normally used to validate native json received, but can also be used to validate the agent json representation (multipart, text, etc.)):
    1228          105 :     if (getRequestSchema()) {
    1229            2 :         std::string error{};
    1230            2 :         if (!getRequestSchema()->validate(requestBodyDataPart.getJson(), error)) {
    1231            1 :             responseStatusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
    1232            1 :             return; // INTERRUPT TRANSFORMATIONS
    1233              :         }
    1234            2 :     }
    1235              : 
    1236              :     // Find out if response body will need to be cloned (this is true if any transformation uses it as target):
    1237          104 :     bool usesResponseBodyAsTransformationJsonTarget = false;
    1238          198 :     for (const auto &t : transformations_) {
    1239          239 :         if (t->getTargetType() == Transformation::TargetType::ResponseBodyJson_String ||
    1240          230 :                 t->getTargetType() == Transformation::TargetType::ResponseBodyJson_Integer ||
    1241          226 :                 t->getTargetType() == Transformation::TargetType::ResponseBodyJson_Unsigned ||
    1242          222 :                 t->getTargetType() == Transformation::TargetType::ResponseBodyJson_Float ||
    1243          218 :                 t->getTargetType() == Transformation::TargetType::ResponseBodyJson_Boolean ||
    1244          347 :                 t->getTargetType() == Transformation::TargetType::ResponseBodyJson_Object ||
    1245           97 :                 t->getTargetType() == Transformation::TargetType::ResponseBodyJson_JsonString) {
    1246           29 :             usesResponseBodyAsTransformationJsonTarget = true;
    1247           29 :             break;
    1248              :         }
    1249              :     }
    1250              : 
    1251          104 :     nlohmann::json responseBodyJson;
    1252          104 :     if (usesResponseBodyAsTransformationJsonTarget) {
    1253           29 :         responseBodyJson = getResponseBody();   // clone provision response body to manipulate this copy and finally we will dump() it over 'responseBody':
    1254              :         // if(usesResponseBodyAsTransformationJsonTarget) responseBody = responseBodyJson.dump(); <--- place this after transformations (*)
    1255              :     }
    1256              :     else {
    1257           75 :         responseBody = getResponseBodyAsString(); // this could be overwritten by targets ResponseBodyString or ResponseBodyHexString
    1258              :     }
    1259              : 
    1260              :     // Type converter:
    1261          104 :     TypeConverter sourceVault{};
    1262              : 
    1263              :     // Apply transformations sequentially
    1264          104 :     bool breakCondition = false;
    1265          240 :     for (const auto &transformation : transformations_) {
    1266              : 
    1267          137 :         if (breakCondition) break;
    1268              : 
    1269          136 :         bool eraser = false;
    1270              : 
    1271          136 :         LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Processing transformation item: %s", transformation->asString().c_str()), ERT_FILE_LOCATION));
    1272              : 
    1273              :         // SOURCES: RequestUri, RequestUriPath, RequestUriParam, RequestBody, ResponseBody, RequestHeader, Eraser, Math, Random, Timestamp, Strftime, Recvseq, SVar, SGvar, Value, ServerEvent, InState
    1274          136 :         if (!processSources(transformation, sourceVault, variables, requestUri, requestUriPath, requestQueryParametersMap, requestBodyDataPart, requestHeaders, eraser, generalUniqueServerSequence, usesResponseBodyAsTransformationJsonTarget, responseBodyJson)) {
    1275           15 :             LOGDEBUG(ert::tracing::Logger::debug("Transformation item skipped on source", ERT_FILE_LOCATION));
    1276           26 :             continue;
    1277              :         }
    1278              : 
    1279          121 :         std::smatch matches; // BE CAREFUL!: https://stackoverflow.com/a/51709911/2576671
    1280              :         // So, we can't use 'matches' as container because source may change: BUT, using that source exclusively, it will work (*)
    1281          121 :         std::string source; // Now, this never will be out of scope, and 'matches' will be valid.
    1282              : 
    1283              :         // FILTERS: RegexCapture, RegexReplace, Append, Prepend, Sum, Multiply, ConditionVar, EqualTo, DifferentFrom, JsonConstraint, SchemaId
    1284          121 :         bool hasFilter = transformation->hasFilter();
    1285          121 :         if (hasFilter) {
    1286           25 :             if (eraser || !processFilters(transformation, sourceVault, variables, matches, source)) {
    1287            4 :                 LOGDEBUG(ert::tracing::Logger::debug("Transformation item skipped on filter", ERT_FILE_LOCATION));
    1288            4 :                 if (eraser) LOGWARNING(ert::tracing::Logger::warning("Filter is not allowed when using 'eraser' source type. Transformation will be ignored.", ERT_FILE_LOCATION));
    1289            4 :                 continue;
    1290              :             }
    1291              :         }
    1292              : 
    1293              :         // TARGETS: ResponseBodyString, ResponseBodyHexString, ResponseBodyJson_String, ResponseBodyJson_Integer, ResponseBodyJson_Unsigned, ResponseBodyJson_Float, ResponseBodyJson_Boolean, ResponseBodyJson_Object, ResponseBodyJson_JsonString, ResponseHeader, ResponseStatusCode, ResponseDelayMs, TVar, TGVar, OutState, TTxtFile, TBinFile, UDPSocket, ServerEventToPurge, Break
    1294          117 :         if (!processTargets(transformation, sourceVault, variables, matches, eraser, hasFilter, responseStatusCode, responseBodyJson, responseBody, responseHeaders, responseDelayMs, outState, outStateMethod, outStateUri, clientProvisionTriggers, breakCondition)) {
    1295            7 :             LOGDEBUG(ert::tracing::Logger::debug("Transformation item skipped on target", ERT_FILE_LOCATION));
    1296            7 :             continue;
    1297              :         }
    1298              : 
    1299          132 :     }
    1300              : 
    1301              :     // (*) Regenerate final responseBody after transformations:
    1302          104 :     if(usesResponseBodyAsTransformationJsonTarget && !responseBodyJson.empty()) {
    1303              :         try {
    1304           28 :             responseBody = responseBodyJson.dump(); // this may arise type error, for example in case of trying to set json field value with binary data:
    1305              :             // When having a provision transformation from 'request.body' to 'response.body.json.string./whatever':
    1306              :             // echo -en '\x80\x01' | curl --http2-prior-knowledge -i -H 'content-type:application/octet-stream' -X GET "<traffic url>/uri" --data-binary @-
    1307              :             //
    1308              :             // This is not valid and must be protected. The user should use another kind of target to store binary.
    1309              :         }
    1310            1 :         catch (const std::exception& e)
    1311              :         {
    1312            1 :             ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
    1313            1 :         }
    1314              :     }
    1315              : 
    1316              :     // Response schema validation (not supported for response body created by non-json targets, to simplify the fact to parse need on ResponseBodyString/ResponseBodyHexString):
    1317          104 :     if (getResponseSchema()) {
    1318            1 :         std::string error{};
    1319            1 :         if (!getResponseSchema()->validate(usesResponseBodyAsTransformationJsonTarget ? responseBodyJson:getResponseBody(), error)) {
    1320            1 :             responseStatusCode = ert::http2comm::ResponseCode::INTERNAL_SERVER_ERROR; // 500: built response will be anyway sent although status code is overwritten with internal server error.
    1321              :         }
    1322            1 :     }
    1323          104 : }
    1324              : 
    1325          123 : bool AdminServerProvision::load(const nlohmann::json &j, bool regexMatchingConfigured) {
    1326              : 
    1327              :     // Store whole document (useful for GET operation)
    1328          123 :     json_ = j;
    1329              : 
    1330              :     // Mandatory
    1331          123 :     auto requestMethod_it = j.find("requestMethod");
    1332          123 :     request_method_ = *requestMethod_it;
    1333              : 
    1334          123 :     auto it = j.find("responseCode");
    1335          123 :     response_code_ = *it;
    1336              : 
    1337              :     // Optional
    1338          123 :     it = j.find("requestUri");
    1339          123 :     if (it != j.end() && it->is_string()) {
    1340          123 :         request_uri_ = *it;
    1341              :     }
    1342              : 
    1343          123 :     it = j.find("inState");
    1344          123 :     if (it != j.end() && it->is_string()) {
    1345            3 :         in_state_ = *it;
    1346            3 :         if (in_state_.empty()) in_state_ = DEFAULT_ADMIN_PROVISION_STATE;
    1347              :     }
    1348              : 
    1349          123 :     it = j.find("outState");
    1350          123 :     if (it != j.end() && it->is_string()) {
    1351            2 :         out_state_ = *it;
    1352            2 :         if (out_state_.empty()) out_state_ = DEFAULT_ADMIN_PROVISION_STATE;
    1353              :     }
    1354              : 
    1355          123 :     it = j.find("requestSchemaId");
    1356          123 :     if (it != j.end() && it->is_string()) {
    1357            7 :         request_schema_id_ = *it;
    1358            7 :         if (request_schema_id_.empty()) {
    1359            1 :             ert::tracing::Logger::error("Invalid empty request schema identifier", ERT_FILE_LOCATION);
    1360            1 :             return false;
    1361              :         }
    1362              :     }
    1363              : 
    1364          122 :     it = j.find("responseSchemaId");
    1365          122 :     if (it != j.end() && it->is_string()) {
    1366            7 :         response_schema_id_ = *it;
    1367            7 :         if (response_schema_id_.empty()) {
    1368            1 :             ert::tracing::Logger::error("Invalid empty response schema identifier", ERT_FILE_LOCATION);
    1369            1 :             return false;
    1370              :         }
    1371              :     }
    1372              : 
    1373          121 :     it = j.find("responseHeaders");
    1374          121 :     if (it != j.end() && it->is_object()) {
    1375          339 :         for (auto& [key, val] : it->items())
    1376          339 :             response_headers_.emplace(key, nghttp2::asio_http2::header_value{val});
    1377              :     }
    1378              : 
    1379          121 :     it = j.find("responseBody");
    1380          121 :     if (it != j.end()) {
    1381          113 :         if (it->is_object() || it->is_array()) {
    1382          107 :             response_body_ = *it;
    1383          107 :             response_body_string_ = response_body_.dump(); // valid as cache for static responses (not updated with transformations)
    1384              :         }
    1385            6 :         else if (it->is_string()) {
    1386            1 :             response_body_string_ = *it;
    1387              :         }
    1388            5 :         else if (it->is_number_integer() || it->is_number_unsigned()) {
    1389              :             //response_body_integer_ = *it;
    1390            2 :             int number = *it;
    1391            2 :             response_body_string_ = std::to_string(number);
    1392              :         }
    1393            3 :         else if (it->is_number_float()) {
    1394              :             //response_body_number_ = *it;
    1395            1 :             response_body_string_ = std::to_string(double(*it));
    1396              :         }
    1397            2 :         else if (it->is_boolean()) {
    1398              :             //response_body_boolean_ = *it;
    1399            1 :             response_body_string_ = ((bool)(*it) ? "true":"false");
    1400              :         }
    1401            1 :         else if (it->is_null()) {
    1402              :             //response_body_null_ = true;
    1403            1 :             response_body_string_ = "null";
    1404              :         }
    1405              :     }
    1406              : 
    1407          121 :     it = j.find("responseDelayMs");
    1408          121 :     if (it != j.end() && it->is_number()) {
    1409          108 :         response_delay_ms_ = *it;
    1410              :     }
    1411              : 
    1412          121 :     auto transform_it = j.find("transform");
    1413          121 :     if (transform_it != j.end()) {
    1414          247 :         for (auto it : *transform_it) { // "it" is of type json::reference and has no key() member
    1415          145 :             loadTransformation(it);
    1416          145 :         }
    1417              :     }
    1418              : 
    1419              :     // Store key:
    1420          121 :     h2agent::model::calculateStringKey(key_, in_state_, request_method_, request_uri_);
    1421              : 
    1422          121 :     if (regexMatchingConfigured) {
    1423              :         // Precompile regex with key, only for 'RegexMatching' algorithm:
    1424              :         try {
    1425            6 :             LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Assigning regex: %s", key_.c_str()), ERT_FILE_LOCATION));
    1426            6 :             regex_.assign(key_, std::regex::optimize);
    1427              :         }
    1428            2 :         catch (std::regex_error &e) {
    1429            4 :             ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
    1430            2 :             ert::tracing::Logger::error("Invalid regular expression (detected when joining 'inState' and/or 'requestUri' from provision) for current 'RegexMatching' server matching algorithm", ERT_FILE_LOCATION);
    1431            2 :             return false;
    1432            2 :         }
    1433              :     }
    1434              : 
    1435          119 :     return true;
    1436              : }
    1437              : 
    1438          145 : void AdminServerProvision::loadTransformation(const nlohmann::json &j) {
    1439              : 
    1440          145 :     LOGDEBUG(
    1441              :         std::string msg = ert::tracing::Logger::asString("Loading transformation item: %s", j.dump().c_str()); // avoid newlines in traces (dump(n) pretty print)
    1442              :         ert::tracing::Logger::debug(msg, ERT_FILE_LOCATION);
    1443              :     );
    1444              : 
    1445              :     // Transformation object to fill:
    1446          145 :     auto transformation = std::make_shared<Transformation>();
    1447              : 
    1448          145 :     if (transformation->load(j)) {
    1449          143 :         transformations_.push_back(transformation);
    1450          143 :         LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Loaded transformation item: %s", transformation->asString().c_str()), ERT_FILE_LOCATION));
    1451              :     }
    1452              :     else {
    1453            4 :         ert::tracing::Logger::error("Discarded transform item due to incoherent data", ERT_FILE_LOCATION);
    1454              :     }
    1455          145 : }
    1456              : 
    1457              : 
    1458              : }
    1459              : }
    1460              : 
        

Generated by: LCOV version 2.0-1