LCOV - code coverage report
Current view: top level - http2 - MyAdminHttp2Server.cpp (source / functions) Coverage Total Hit
Test: final-coverage.info Lines: 31.3 % 594 186
Test Date: 2025-12-03 20:05:55 Functions: 78.3 % 23 18

            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 <boost/optional.hpp>
      37              : 
      38              : #include <sstream>
      39              : #include <errno.h>
      40              : 
      41              : #include <nlohmann/json.hpp>
      42              : 
      43              : #include <ert/tracing/Logger.hpp>
      44              : #include <ert/http2comm/Http.hpp>
      45              : #include <ert/http2comm/Http2Headers.hpp>
      46              : 
      47              : #include <MyAdminHttp2Server.hpp>
      48              : #include <MyTrafficHttp2Server.hpp>
      49              : //#include <MyTrafficHttp2Client.hpp>
      50              : 
      51              : #include <AdminData.hpp>
      52              : #include <MockServerData.hpp>
      53              : #include <Configuration.hpp>
      54              : #include <GlobalVariable.hpp>
      55              : #include <FileManager.hpp>
      56              : #include <SocketManager.hpp>
      57              : #include <functions.hpp>
      58              : 
      59              : 
      60              : namespace h2agent
      61              : {
      62              : namespace http2
      63              : {
      64              : 
      65           29 : bool statusCodeOK(int statusCode) {
      66           29 :     return (statusCode >= ert::http2comm::ResponseCode::OK && statusCode < ert::http2comm::ResponseCode::MULTIPLE_CHOICES); // [200,300)
      67              : }
      68              : 
      69           49 : MyAdminHttp2Server::MyAdminHttp2Server(const std::string &name, size_t workerThreads):
      70           49 :     ert::http2comm::Http2Server(name, workerThreads, workerThreads, nullptr) {
      71              : 
      72           49 :     admin_data_ = new model::AdminData();
      73           49 :     common_resources_.AdminDataPtr = admin_data_; // it would be dirty to assign this outside like Configuration or other common resources
      74              : 
      75              :     // Client data storage
      76           49 :     client_data_ = true;
      77           49 :     client_data_key_history_ = true;
      78           49 :     purge_execution_ = true;
      79           49 : }
      80              : 
      81           60 : MyAdminHttp2Server::~MyAdminHttp2Server()
      82              : {
      83           30 :     delete (admin_data_);
      84           60 : }
      85              : 
      86           48 : bool MyAdminHttp2Server::checkMethodIsAllowed(
      87              :     const nghttp2::asio_http2::server::request& req,
      88              :     std::vector<std::string>& allowedMethods)
      89              : {
      90          240 :     allowedMethods = {"POST", "GET", "DELETE", "PUT"};
      91           48 :     return (req.method() == "POST" || req.method() == "GET" || req.method() == "DELETE" || req.method() == "PUT");
      92          144 : }
      93              : 
      94           48 : bool MyAdminHttp2Server::checkMethodIsImplemented(
      95              :     const nghttp2::asio_http2::server::request& req)
      96              : {
      97           48 :     return (req.method() == "POST" || req.method() == "GET" || req.method() == "DELETE" || req.method() == "PUT");
      98              : }
      99              : 
     100              : 
     101           48 : bool MyAdminHttp2Server::checkHeaders(const
     102              :                                       nghttp2::asio_http2::server::request& req)
     103              : {
     104              :     // Don't check headers for GET and DELETE:
     105           48 :     if (req.method() == "GET" || req.method() == "DELETE" || req.method() == "PUT") {
     106           23 :         return true;
     107              :     }
     108              : 
     109           50 :     auto ctype     = req.header().find("content-type");
     110           50 :     auto clength   = req.header().find("content-length");
     111           25 :     auto ctype_end = req.header().end();
     112              : 
     113           25 :     LOGDEBUG(
     114              :         ert::tracing::Logger::debug(
     115              :             ert::tracing::Logger::asString(
     116              :                 "[ReceivedRequest] Headers: content-type = %s; content-length = %s",
     117              :                 (ctype != ctype_end) ? ctype->second.value.c_str() : "(absent)",
     118              :                 (clength != ctype_end) ? clength->second.value.c_str() : "(absent)"), ERT_FILE_LOCATION));
     119              : 
     120           25 :     if (ctype != ctype_end)
     121              :     {
     122           25 :         return (ctype->second.value == "application/json");
     123              :     }
     124              : 
     125            0 :     return (clength != ctype_end && clength->second.value != "0");
     126              : }
     127              : 
     128              : 
     129           46 : std::string MyAdminHttp2Server::getPathSuffix(const std::string &uriPath) const
     130              : {
     131           46 :     std::string result;
     132              : 
     133           46 :     size_t apiPathSize = getApiPath().size(); // /admin/v1
     134           46 :     size_t uriPathSize = uriPath.size(); // /admin/v1<suffix>
     135              :     //LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("apiPathSize %d uriPathSize %d", apiPathSize, uriPathSize),  ERT_FILE_LOCATION));
     136              : 
     137              :     // Special case
     138           46 :     if (uriPathSize <= apiPathSize) return result; // indeed, it should not be lesser, as API & VERSION is already checked
     139              : 
     140           44 :     result = uriPath.substr(apiPathSize + 1);
     141              : 
     142           44 :     if (result.back() == '/') result.pop_back(); // normalize by mean removing last slash (if exists)
     143              : 
     144           44 :     return result;
     145              : } // LCOV_EXCL_LINE
     146              : 
     147              : /*
     148              : #include <iomanip>
     149              : 
     150              : void MyAdminHttp2Server::buildJsonResponse(bool result, const std::string &response, std::string &jsonResponse) const
     151              : {
     152              :     std::stringstream ss;
     153              :     ss << R"({ "result":")" << (result ? "true":"false") << R"(", "response": )" << std::quoted(response) << R"( })";
     154              :     jsonResponse = ss.str();
     155              :     LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("jsonResponse %s", jsonResponse.c_str()), ERT_FILE_LOCATION));
     156              : }
     157              : 
     158              : THIS WAS REPLACED TEMPORARILY BY A SLIGHTLY LESS EFFICIENT VERSION, TO AVOID VALGRIND COMPLAIN:
     159              : */
     160              : 
     161           23 : std::string MyAdminHttp2Server::buildJsonResponse(bool responseResult, const std::string &responseBody) const
     162              : {
     163           23 :     std::string result;
     164           23 :     result = R"({ "result":")";
     165           23 :     result += (responseResult ? "true":"false");
     166           23 :     result += R"(", "response": )";
     167           23 :     result += R"(")";
     168           23 :     result += responseBody;
     169           23 :     result += R"(" })";
     170           23 :     LOGDEBUG(ert::tracing::Logger::debug(ert::tracing::Logger::asString("Json Response %s", result.c_str()), ERT_FILE_LOCATION));
     171              : 
     172           23 :     return result;
     173              : } // LCOV_EXCL_LINE
     174              : 
     175            2 : void MyAdminHttp2Server::receiveNOOP(unsigned int& statusCode, nghttp2::asio_http2::header_map& headers, std::string &responseBody) const
     176              : {
     177            2 :     LOGDEBUG(ert::tracing::Logger::debug("receiveNOOP()",  ERT_FILE_LOCATION));
     178              :     // Response document:
     179              :     // {
     180              :     //   "result":"<true or false>",
     181              :     //   "response":"<additional information>"
     182              :     // }
     183            2 :     responseBody = buildJsonResponse(false, "no operation provided");
     184            4 :     headers.emplace("content-type", nghttp2::asio_http2::header_value{"application/json"});
     185            2 :     statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     186            2 : }
     187              : 
     188           11 : int MyAdminHttp2Server::serverMatching(const nlohmann::json &configurationObject, std::string& log) const
     189              : {
     190           11 :     log = "server-matching operation; ";
     191              : 
     192           11 :     h2agent::model::AdminServerMatchingData::LoadResult loadResult = getAdminData()->loadServerMatching(configurationObject);
     193           11 :     int result = ((loadResult == h2agent::model::AdminServerMatchingData::Success) ? ert::http2comm::ResponseCode::CREATED:ert::http2comm::ResponseCode::BAD_REQUEST); // 201 or 400
     194              : 
     195           11 :     if (loadResult == h2agent::model::AdminServerMatchingData::Success) {
     196            8 :         log += "valid schema and matching data received";
     197              : 
     198              :         // Warn in case previous server provisions exists:
     199            8 :         if (getAdminData()->getServerProvisionData().size() != 0)
     200            3 :             LOGWARNING(
     201              :             if (getAdminData()->getServerProvisionData().size() != 0) {
     202              :             ert::tracing::Logger::warning("There are current server provisions: remove/update them to avoid unexpected behavior (matching must be configured firstly !)", ERT_FILE_LOCATION);
     203              :             }
     204              :         );
     205              :     }
     206            3 :     else if (loadResult == h2agent::model::AdminServerMatchingData::BadSchema) {
     207            2 :         log += "invalid schema";
     208              :     }
     209            1 :     else if (loadResult == h2agent::model::AdminServerMatchingData::BadContent) {
     210            1 :         log += "invalid matching data received";
     211              :     }
     212              : 
     213           11 :     return result;
     214              : }
     215              : 
     216           14 : int MyAdminHttp2Server::serverProvision(const nlohmann::json &configurationObject, std::string& log) const
     217              : {
     218           14 :     log = "server-provision operation; ";
     219              : 
     220           14 :     h2agent::model::AdminServerProvisionData::LoadResult loadResult = getAdminData()->loadServerProvision(configurationObject, common_resources_);
     221           14 :     int result = ((loadResult == h2agent::model::AdminServerProvisionData::Success) ? ert::http2comm::ResponseCode::CREATED:ert::http2comm::ResponseCode::BAD_REQUEST); // 201 or 400
     222              : 
     223           14 :     bool isArray = configurationObject.is_array();
     224           14 :     if (loadResult == h2agent::model::AdminServerProvisionData::Success) {
     225           11 :         log += (isArray ? "valid schemas and server provisions data received":"valid schema and server provision data received");
     226              :     }
     227            3 :     else if (loadResult == h2agent::model::AdminServerProvisionData::BadSchema) {
     228            3 :         log += (isArray ? "detected one invalid schema":"invalid schema");
     229              :     }
     230            0 :     else if (loadResult == h2agent::model::AdminServerProvisionData::BadContent) {
     231            0 :         log += (isArray ? "detected one invalid server provision data received":"invalid server provision data received");
     232              :     }
     233              : 
     234           14 :     return result;
     235              : }
     236              : 
     237            6 : int MyAdminHttp2Server::clientEndpoint(const nlohmann::json &configurationObject, std::string& log) const
     238              : {
     239            6 :     log = "client-endpoint operation; ";
     240              : 
     241            6 :     h2agent::model::AdminClientEndpointData::LoadResult loadResult = getAdminData()->loadClientEndpoint(configurationObject, common_resources_);
     242            6 :     int result = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     243            6 :     if (loadResult == h2agent::model::AdminClientEndpointData::Success) {
     244            2 :         result = ert::http2comm::ResponseCode::CREATED; // 201
     245              :     }
     246            4 :     else if (loadResult == h2agent::model::AdminClientEndpointData::Accepted) {
     247            0 :         result = ert::http2comm::ResponseCode::ACCEPTED; // 202
     248              :     }
     249              : 
     250            6 :     bool isArray = configurationObject.is_array();
     251            6 :     if (loadResult == h2agent::model::AdminClientEndpointData::Success || loadResult == h2agent::model::AdminClientEndpointData::Accepted) {
     252            2 :         log += (isArray ? "valid schemas and client endpoints data received":"valid schema and client endpoint data received");
     253              :     }
     254            4 :     else if (loadResult == h2agent::model::AdminClientEndpointData::BadSchema) {
     255            2 :         log += (isArray ? "detected one invalid schema":"invalid schema");
     256              :     }
     257            2 :     else if (loadResult == h2agent::model::AdminClientEndpointData::BadContent) {
     258            2 :         log += (isArray ? "detected one invalid client endpoint data received":"invalid client endpoint data received");
     259              :     }
     260              : 
     261            6 :     return result;
     262              : }
     263              : 
     264            0 : int MyAdminHttp2Server::clientProvision(const nlohmann::json &configurationObject, std::string& log) const
     265              : {
     266            0 :     log = "client-provision operation; ";
     267              : 
     268            0 :     h2agent::model::AdminClientProvisionData::LoadResult loadResult = getAdminData()->loadClientProvision(configurationObject, common_resources_);
     269            0 :     int result = ((loadResult == h2agent::model::AdminClientProvisionData::Success) ? 201:400);
     270              : 
     271            0 :     bool isArray = configurationObject.is_array();
     272            0 :     if (loadResult == h2agent::model::AdminClientProvisionData::Success) {
     273            0 :         log += (isArray ? "valid schemas and client provisions data received":"valid schema and client provision data received");
     274              :     }
     275            0 :     else if (loadResult == h2agent::model::AdminClientProvisionData::BadSchema) {
     276            0 :         log += (isArray ? "detected one invalid schema":"invalid schema");
     277              :     }
     278            0 :     else if (loadResult == h2agent::model::AdminClientProvisionData::BadContent) {
     279            0 :         log += (isArray ? "detected one invalid client provision data received":"invalid client provision data received");
     280              :     }
     281              : 
     282            0 :     return result;
     283              : }
     284              : 
     285            8 : int MyAdminHttp2Server::globalVariable(const nlohmann::json &configurationObject, std::string& log) const
     286              : {
     287            8 :     log = "global-variable operation; ";
     288              : 
     289            8 :     int result = getGlobalVariable()->loadJson(configurationObject) ? ert::http2comm::ResponseCode::CREATED:ert::http2comm::ResponseCode::BAD_REQUEST; // 201 or 400
     290            8 :     log += (statusCodeOK(result) ? "valid schema and global variables received":"invalid schema");
     291              : 
     292            8 :     return result;
     293              : }
     294              : 
     295            5 : int MyAdminHttp2Server::schema(const nlohmann::json &configurationObject, std::string& log) const
     296              : {
     297            5 :     log = "schema operation; ";
     298              : 
     299            5 :     h2agent::model::AdminSchemaData::LoadResult loadResult = getAdminData()->loadSchema(configurationObject);
     300            5 :     int result = ((loadResult == h2agent::model::AdminSchemaData::Success) ? ert::http2comm::ResponseCode::CREATED:ert::http2comm::ResponseCode::BAD_REQUEST); // 201 or 400
     301              : 
     302            5 :     bool isArray = configurationObject.is_array();
     303            5 :     if (loadResult == h2agent::model::AdminSchemaData::Success) {
     304            4 :         log += (isArray ? "valid schemas and schemas data received":"valid schema and schema data received");
     305              :     }
     306            1 :     else if (loadResult == h2agent::model::AdminSchemaData::BadSchema) {
     307            1 :         log += (isArray ? "detected one invalid schema":"invalid schema");
     308              :     }
     309            0 :     else if (loadResult == h2agent::model::AdminSchemaData::BadContent) {
     310            0 :         log += (isArray ? "detected one invalid schema data received":"invalid schema data received");
     311              :     }
     312              : 
     313            5 :     return result;
     314              : }
     315              : 
     316           21 : void MyAdminHttp2Server::receivePOST(const std::string &pathSuffix, const std::string& requestBody, unsigned int& statusCode, nghttp2::asio_http2::header_map& headers, std::string &responseBody) const
     317              : {
     318           21 :     LOGDEBUG(ert::tracing::Logger::debug("receivePOST()",  ERT_FILE_LOCATION));
     319           21 :     LOGDEBUG(ert::tracing::Logger::debug("Json body received (admin interface)", ERT_FILE_LOCATION));
     320              : 
     321           21 :     std::string jsonResponse_response;
     322              : 
     323              :     // All responses are json content:
     324           42 :     headers.emplace("content-type", nghttp2::asio_http2::header_value{"application/json"});
     325              : 
     326              :     // Admin schema validation:
     327           21 :     nlohmann::json requestJson;
     328           21 :     bool success = h2agent::model::parseJsonContent(requestBody, requestJson);
     329              : 
     330           21 :     if (success) {
     331           21 :         if (pathSuffix == "server-matching") {
     332            7 :             statusCode = serverMatching(requestJson, jsonResponse_response);
     333              :         }
     334           14 :         else if (pathSuffix == "server-provision") {
     335           10 :             statusCode = serverProvision(requestJson, jsonResponse_response);
     336              :         }
     337            4 :         else if (pathSuffix == "client-endpoint") {
     338            0 :             statusCode = clientEndpoint(requestJson, jsonResponse_response);
     339              :         }
     340            4 :         else if (pathSuffix == "client-provision") {
     341            0 :             statusCode = clientProvision(requestJson, jsonResponse_response);
     342              :         }
     343            4 :         else if (pathSuffix == "schema") {
     344            2 :             statusCode = schema(requestJson, jsonResponse_response);
     345              :         }
     346            2 :         else if (pathSuffix == "global-variable") {
     347            2 :             statusCode = globalVariable(requestJson, jsonResponse_response);
     348              :         }
     349              :         else {
     350            0 :             statusCode = ert::http2comm::ResponseCode::NOT_IMPLEMENTED; // 501
     351            0 :             jsonResponse_response = "unsupported operation";
     352              :         }
     353              :     }
     354              :     else
     355              :     {
     356              :         // Response data:
     357            0 :         statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     358            0 :         jsonResponse_response = "failed to parse json from body request";
     359              :     }
     360              : 
     361              :     // Build json response body:
     362           21 :     responseBody = buildJsonResponse(statusCodeOK(statusCode), jsonResponse_response);
     363           21 : }
     364              : 
     365           23 : void MyAdminHttp2Server::receiveGET(const std::string &uri, const std::string &pathSuffix, const std::string &queryParams, unsigned int& statusCode, nghttp2::asio_http2::header_map& headers, std::string &responseBody) const
     366              : {
     367           23 :     LOGDEBUG(ert::tracing::Logger::debug("receiveGET()",  ERT_FILE_LOCATION));
     368              : 
     369              :     // All responses, except for 'logging', are json content:
     370           23 :     bool jsonContent = true;
     371              : 
     372              :     // composed path suffixes
     373           23 :     std::smatch matches;
     374           23 :     static std::regex clientProvisionId("^client-provision/(.*)", std::regex::optimize);
     375              : 
     376              : 
     377           23 :     if (pathSuffix == "server-matching/schema") {
     378              :         // Add the $id field dynamically (full URI including scheme/host)
     379            0 :         nlohmann::json jsonSchema = getAdminData()->getServerMatchingData().getSchema().getJson();
     380            0 :         jsonSchema["$id"] = uri;
     381            0 :         responseBody = jsonSchema.dump();
     382            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     383            0 :     }
     384           23 :     else if (pathSuffix == "server-provision/schema") {
     385              :         // Add the $id field dynamically (full URI including scheme/host)
     386            0 :         nlohmann::json jsonSchema = getAdminData()->getServerProvisionData().getSchema().getJson();
     387            0 :         jsonSchema["$id"] = uri;
     388            0 :         responseBody = jsonSchema.dump();
     389            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     390            0 :     }
     391           23 :     else if (pathSuffix == "client-endpoint/schema") {
     392              :         // Add the $id field dynamically (full URI including scheme/host)
     393            0 :         nlohmann::json jsonSchema = getAdminData()->getClientEndpointData().getSchema().getJson();
     394            0 :         jsonSchema["$id"] = uri;
     395            0 :         responseBody = jsonSchema.dump();
     396            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     397            0 :     }
     398           23 :     else if (pathSuffix == "client-provision/schema") {
     399              :         // Add the $id field dynamically (full URI including scheme/host)
     400            0 :         nlohmann::json jsonSchema = getAdminData()->getClientProvisionData().getSchema().getJson();
     401            0 :         jsonSchema["$id"] = uri;
     402            0 :         responseBody = jsonSchema.dump();
     403            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     404            0 :     }
     405           23 :     else if (pathSuffix == "schema/schema") {
     406              :         // Add the $id field dynamically (full URI including scheme/host)
     407            0 :         nlohmann::json jsonSchema = getAdminData()->getSchemaData().getSchema().getJson();
     408            0 :         jsonSchema["$id"] = uri;
     409            0 :         responseBody = jsonSchema.dump();
     410            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     411            0 :     }
     412           23 :     else if (pathSuffix == "server-data/summary") {
     413            0 :         std::string maxKeys = "";
     414            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     415            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     416            0 :             auto it = qmap.find("maxKeys");
     417            0 :             if (it != qmap.end()) maxKeys = it->second;
     418            0 :         }
     419              : 
     420            0 :         responseBody = getMockServerData()->summary(maxKeys);
     421            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     422            0 :     }
     423           23 :     else if (pathSuffix == "client-data/summary") {
     424            0 :         std::string maxKeys = "";
     425            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     426            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     427            0 :             auto it = qmap.find("maxKeys");
     428            0 :             if (it != qmap.end()) maxKeys = it->second;
     429            0 :         }
     430              : 
     431            0 :         responseBody = getMockClientData()->summary(maxKeys);
     432            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     433            0 :     }
     434           23 :     else if (pathSuffix == "global-variable") {
     435            4 :         std::string name = "";
     436            4 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     437            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     438            0 :             auto it = qmap.find("name");
     439            0 :             if (it != qmap.end()) name = it->second;
     440            0 :             if (name.empty()) {
     441            0 :                 statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     442            0 :                 responseBody = "";
     443              :             }
     444            0 :         }
     445            4 :         if (statusCode != ert::http2comm::ResponseCode::BAD_REQUEST) { // 400
     446            4 :             if (name.empty()) {
     447            4 :                 responseBody = getGlobalVariable()->asJsonString();
     448            4 :                 statusCode = ((responseBody == "{}") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     449              :             }
     450              :             else {
     451            0 :                 bool exists = getGlobalVariable()->tryGet(name, responseBody);
     452            0 :                 statusCode = (exists ? ert::http2comm::ResponseCode::OK:ert::http2comm::ResponseCode::NO_CONTENT); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     453              :             }
     454              :         }
     455            4 :     }
     456           19 :     else if (pathSuffix == "global-variable/schema") {
     457              :         // Add the $id field dynamically (full URI including scheme/host)
     458            0 :         nlohmann::json jsonSchema = getGlobalVariable()->getSchema().getJson();
     459            0 :         jsonSchema["$id"] = uri;
     460            0 :         responseBody = jsonSchema.dump();
     461            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     462            0 :     }
     463           19 :     else if (pathSuffix == "server-matching") {
     464            7 :         responseBody = getAdminData()->getServerMatchingData().getJson().dump();
     465            7 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     466              :     }
     467           12 :     else if (pathSuffix == "server-provision") {
     468            4 :         bool ordered = (getAdminData()->getServerMatchingData().getAlgorithm() == h2agent::model::AdminServerMatchingData::RegexMatching);
     469            4 :         responseBody = getAdminData()->getServerProvisionData().asJsonString(ordered);
     470            4 :         statusCode = ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     471              :     }
     472            8 :     else if (pathSuffix == "server-provision/unused") {
     473            4 :         bool ordered = (getAdminData()->getServerMatchingData().getAlgorithm() == h2agent::model::AdminServerMatchingData::RegexMatching);
     474            4 :         responseBody = getAdminData()->getServerProvisionData().asJsonString(ordered, true /*unused*/);
     475            4 :         statusCode = ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     476              :     }
     477            4 :     else if (pathSuffix == "client-endpoint") {
     478            0 :         responseBody = getAdminData()->getClientEndpointData().asJsonString();
     479            0 :         statusCode = ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     480              :     }
     481            4 :     else if (pathSuffix == "client-provision") {
     482            0 :         responseBody = getAdminData()->getClientProvisionData().asJsonString();
     483            0 :         statusCode = ((responseBody == "[]") ? 204:200); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     484              :     }
     485            4 :     else if (pathSuffix == "client-provision/unused") {
     486            0 :         responseBody = getAdminData()->getClientProvisionData().asJsonString(true /*unused*/);
     487            0 :         statusCode = ((responseBody == "[]") ? 204:200); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     488              :     }
     489            4 :     else if (std::regex_match(pathSuffix, matches, clientProvisionId)) { // client-provision/<client provision id>
     490            0 :         triggerClientOperation(matches.str(1), queryParams, statusCode);
     491            0 :         bool result = statusCodeOK(statusCode);
     492            0 :         responseBody = buildJsonResponse(result, (result ? "operation processed":"operation failed"));
     493              :     }
     494            4 :     else if (pathSuffix == "schema") {
     495            4 :         responseBody = getAdminData()->getSchemaData().asJsonString();
     496            4 :         statusCode = ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK); // response body will be emptied by nghttp2 when status code is 204 (No Content)
     497              :     }
     498            0 :     else if (pathSuffix == "server-data") {
     499            0 :         std::string requestMethod = "";
     500            0 :         std::string requestUri = "";
     501            0 :         std::string eventNumber = "";
     502            0 :         std::string eventPath = "";
     503            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     504            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     505            0 :             auto it = qmap.find("requestMethod");
     506            0 :             if (it != qmap.end()) requestMethod = it->second;
     507            0 :             it = qmap.find("requestUri");
     508            0 :             if (it != qmap.end()) requestUri = it->second;
     509            0 :             it = qmap.find("eventNumber");
     510            0 :             if (it != qmap.end()) eventNumber = it->second;
     511            0 :             it = qmap.find("eventPath");
     512            0 :             if (it != qmap.end()) eventPath = it->second;
     513            0 :         }
     514              : 
     515            0 :         bool validQuery = false;
     516              :         try { // dump could throw exception if something weird is done (binary data with non-binary content-type)
     517            0 :             h2agent::model::EventLocationKey elkey(requestMethod, requestUri, eventNumber, eventPath);
     518            0 :             responseBody = getMockServerData()->asJsonString(elkey, validQuery);
     519            0 :         }
     520            0 :         catch (const std::exception& e)
     521              :         {
     522              :             //validQuery = false; // will be ert::http2comm::ResponseCode::OK (200) with empty result (corner case)
     523            0 :             ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
     524            0 :         }
     525            0 :         statusCode = validQuery ? ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK):ert::http2comm::ResponseCode::BAD_REQUEST; // response body will be emptied by nghttp2 when status code is 204 (No Content)
     526            0 :     }
     527            0 :     else if (pathSuffix == "client-data") {
     528            0 :         std::string clientEndpointId = "";
     529            0 :         std::string requestMethod = "";
     530            0 :         std::string requestUri = "";
     531            0 :         std::string eventNumber = "";
     532            0 :         std::string eventPath = "";
     533            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     534            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     535            0 :             auto it = qmap.find("clientEndpointId");
     536            0 :             if (it != qmap.end()) clientEndpointId = it->second;
     537            0 :             it = qmap.find("requestMethod");
     538            0 :             if (it != qmap.end()) requestMethod = it->second;
     539            0 :             it = qmap.find("requestUri");
     540            0 :             if (it != qmap.end()) requestUri = it->second;
     541            0 :             it = qmap.find("eventNumber");
     542            0 :             if (it != qmap.end()) eventNumber = it->second;
     543            0 :             it = qmap.find("eventPath");
     544            0 :             if (it != qmap.end()) eventPath = it->second;
     545            0 :         }
     546              : 
     547            0 :         bool validQuery = false;
     548              :         try { // dump could throw exception if something weird is done (binary data with non-binary content-type)
     549            0 :             h2agent::model::EventLocationKey elkey(clientEndpointId, requestMethod, requestUri, eventNumber, eventPath);
     550            0 :             responseBody = getMockClientData()->asJsonString(elkey, validQuery);
     551            0 :         }
     552            0 :         catch (const std::exception& e)
     553              :         {
     554              :             //validQuery = false; // will be ert::http2comm::ResponseCode::OK (200) with empty result (corner case)
     555            0 :             ert::tracing::Logger::error(e.what(), ERT_FILE_LOCATION);
     556            0 :         }
     557            0 :         statusCode = validQuery ? ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK):ert::http2comm::ResponseCode::BAD_REQUEST; // response body will be emptied by nghttp2 when status code is 204 (No Content)
     558            0 :     }
     559            0 :     else if (pathSuffix == "configuration") {
     560            0 :         responseBody = getConfiguration()->asJsonString();
     561            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     562              :     }
     563            0 :     else if (pathSuffix == "server/configuration") {
     564            0 :         responseBody = getHttp2Server()->configurationAsJsonString();
     565            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     566              :     }
     567            0 :     else if (pathSuffix == "server-data/configuration") {
     568            0 :         responseBody = getHttp2Server()->dataConfigurationAsJsonString();
     569            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     570              :     }
     571            0 :     else if (pathSuffix == "client-data/configuration") {
     572            0 :         responseBody = clientDataConfigurationAsJsonString();
     573            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     574              :     }
     575            0 :     else if (pathSuffix == "files/configuration") {
     576            0 :         responseBody = getFileManager()->configurationAsJsonString();
     577            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     578              :     }
     579            0 :     else if (pathSuffix == "files") {
     580            0 :         responseBody = getFileManager()->asJsonString();
     581            0 :         statusCode = ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK); // 204 or 200
     582              :     }
     583            0 :     else if (pathSuffix == "udp-sockets") {
     584            0 :         responseBody = getSocketManager()->asJsonString();
     585            0 :         statusCode = ((responseBody == "[]") ? ert::http2comm::ResponseCode::NO_CONTENT:ert::http2comm::ResponseCode::OK); // 204 or 200
     586              :     }
     587            0 :     else if (pathSuffix == "logging") {
     588            0 :         responseBody = ert::tracing::Logger::levelAsString(ert::tracing::Logger::getLevel());
     589            0 :         headers.emplace("content-type", nghttp2::asio_http2::header_value{"text/html"});
     590            0 :         jsonContent = false;
     591            0 :         statusCode = ert::http2comm::ResponseCode::OK; // 200
     592              :     }
     593              :     else {
     594            0 :         statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     595            0 :         responseBody = buildJsonResponse(false, std::string("invalid operation '") + pathSuffix + std::string("'"));
     596              :     }
     597              : 
     598           69 :     if (jsonContent) headers.emplace("content-type", nghttp2::asio_http2::header_value{"application/json"});
     599           23 : }
     600              : 
     601            0 : void MyAdminHttp2Server::receiveDELETE(const std::string &pathSuffix, const std::string &queryParams, unsigned int& statusCode) const
     602              : {
     603            0 :     LOGDEBUG(ert::tracing::Logger::debug("receiveDELETE()",  ERT_FILE_LOCATION));
     604              : 
     605            0 :     if (pathSuffix == "server-provision") {
     606            0 :         statusCode = (getAdminData()->clearServerProvisions() ? ert::http2comm::ResponseCode::OK:ert::http2comm::ResponseCode::NO_CONTENT);  // 200 or 204
     607              :     }
     608            0 :     else if (pathSuffix == "client-endpoint") {
     609            0 :         statusCode = (getAdminData()->clearClientEndpoints() ? ert::http2comm::ResponseCode::OK:ert::http2comm::ResponseCode::NO_CONTENT);  // 200 or 204
     610              :     }
     611            0 :     else if (pathSuffix == "client-provision") {
     612            0 :         statusCode = (getAdminData()->clearClientProvisions() ? 200:204);
     613              :     }
     614            0 :     else if (pathSuffix == "schema") {
     615            0 :         statusCode = (getAdminData()->clearSchemas() ? ert::http2comm::ResponseCode::OK:ert::http2comm::ResponseCode::NO_CONTENT);  // 200 or 204
     616              :     }
     617            0 :     else if (pathSuffix == "server-data") {
     618            0 :         bool serverDataDeleted = false;
     619            0 :         std::string requestMethod = "";
     620            0 :         std::string requestUri = "";
     621            0 :         std::string eventNumber = "";
     622            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     623            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     624            0 :             auto it = qmap.find("requestMethod");
     625            0 :             if (it != qmap.end()) requestMethod = it->second;
     626            0 :             it = qmap.find("requestUri");
     627            0 :             if (it != qmap.end()) requestUri = it->second;
     628            0 :             it = qmap.find("eventNumber");
     629            0 :             if (it != qmap.end()) eventNumber = it->second;
     630            0 :         }
     631              : 
     632            0 :         h2agent::model::EventKey ekey(requestMethod, requestUri, eventNumber);
     633            0 :         bool success = getMockServerData()->clear(serverDataDeleted, ekey);
     634              : 
     635            0 :         statusCode = (success ? (serverDataDeleted ? ert::http2comm::ResponseCode::OK /*200*/:ert::http2comm::ResponseCode::NO_CONTENT /*204*/):ert::http2comm::ResponseCode::BAD_REQUEST /*400*/);
     636            0 :     }
     637            0 :     else if (pathSuffix == "client-data") {
     638            0 :         bool clientDataDeleted = false;
     639            0 :         std::string clientEndpointId = "";
     640            0 :         std::string requestMethod = "";
     641            0 :         std::string requestUri = "";
     642            0 :         std::string eventNumber = "";
     643            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     644            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     645            0 :             auto it = qmap.find("clientEndpointId");
     646            0 :             if (it != qmap.end()) clientEndpointId = it->second;
     647            0 :             it = qmap.find("requestMethod");
     648            0 :             if (it != qmap.end()) requestMethod = it->second;
     649            0 :             it = qmap.find("requestUri");
     650            0 :             if (it != qmap.end()) requestUri = it->second;
     651            0 :             it = qmap.find("eventNumber");
     652            0 :             if (it != qmap.end()) eventNumber = it->second;
     653            0 :         }
     654              : 
     655            0 :         h2agent::model::EventKey ekey(clientEndpointId, requestMethod, requestUri, eventNumber);
     656            0 :         bool success = getMockClientData()->clear(clientDataDeleted, ekey);
     657              : 
     658            0 :         statusCode = (success ? (clientDataDeleted ? ert::http2comm::ResponseCode::OK /*200*/:ert::http2comm::ResponseCode::NO_CONTENT /*204*/):ert::http2comm::ResponseCode::BAD_REQUEST /*400*/);
     659            0 :     }
     660            0 :     else if (pathSuffix == "global-variable") {
     661            0 :         bool globalVariableDeleted = false;
     662            0 :         std::string name = "";
     663            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     664            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     665            0 :             auto it = qmap.find("name");
     666            0 :             if (it != qmap.end()) name = it->second;
     667            0 :             if (name.empty()) {
     668            0 :                 statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     669              :             }
     670            0 :         }
     671            0 :         if (statusCode != ert::http2comm::ResponseCode::BAD_REQUEST) { // 400
     672            0 :             if (name.empty()) {
     673            0 :                 statusCode = (getGlobalVariable()->clear() ? ert::http2comm::ResponseCode::OK:ert::http2comm::ResponseCode::NO_CONTENT); // 204
     674              :             }
     675              :             else {
     676              :                 bool exists;
     677            0 :                 getGlobalVariable()->remove(name, exists);
     678            0 :                 statusCode = (exists ? ert::http2comm::ResponseCode::OK:ert::http2comm::ResponseCode::NO_CONTENT); // 200 or 204
     679              :             }
     680              :         }
     681            0 :     }
     682              :     else {
     683            0 :         statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     684              :     }
     685            0 : }
     686              : 
     687            0 : void MyAdminHttp2Server::receivePUT(const std::string &pathSuffix, const std::string &queryParams, unsigned int& statusCode)
     688              : {
     689            0 :     LOGDEBUG(ert::tracing::Logger::debug("receivePUT()",  ERT_FILE_LOCATION));
     690              : 
     691            0 :     bool success = false;
     692              : 
     693            0 :     if (pathSuffix == "logging") {
     694            0 :         std::string level = "?";
     695            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     696            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     697            0 :             auto it = qmap.find("level");
     698            0 :             if (it != qmap.end()) level = it->second;
     699            0 :         }
     700              : 
     701            0 :         std::string previousLevel = ert::tracing::Logger::levelAsString(ert::tracing::Logger::getLevel());
     702            0 :         if (level != "?") {
     703            0 :             success = ert::tracing::Logger::setLevel(level);
     704              :         }
     705              : 
     706              :         //LOGWARNING(
     707            0 :         if (success) {
     708            0 :             if (level != previousLevel)
     709            0 :                 ert::tracing::Logger::warning(ert::tracing::Logger::asString("Log level changed: %s -> %s", previousLevel.c_str(), level.c_str()), ERT_FILE_LOCATION);
     710              :             else
     711            0 :                 ert::tracing::Logger::warning(ert::tracing::Logger::asString("Log level unchanged (already %s)", previousLevel.c_str()), ERT_FILE_LOCATION);
     712              :         }
     713              :         else {
     714            0 :             ert::tracing::Logger::error(ert::tracing::Logger::asString("Invalid log level provided (%s). Keeping current (%s)", level.c_str(), previousLevel.c_str()), ERT_FILE_LOCATION);
     715              :         }
     716              :         //);
     717            0 :     }
     718            0 :     else if (pathSuffix == "server/configuration") {
     719            0 :         std::string receiveRequestBody;
     720            0 :         std::string preReserveRequestBody;
     721              : 
     722            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     723            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     724            0 :             auto it = qmap.find("receiveRequestBody");
     725            0 :             if (it != qmap.end()) receiveRequestBody = it->second;
     726            0 :             it = qmap.find("preReserveRequestBody");
     727            0 :             if (it != qmap.end()) preReserveRequestBody = it->second;
     728            0 :         }
     729              : 
     730            0 :         bool b_receiveRequestBody = (receiveRequestBody == "true");
     731            0 :         bool b_preReserveRequestBody = (preReserveRequestBody == "true");
     732              : 
     733            0 :         success = (!receiveRequestBody.empty() || !preReserveRequestBody.empty());
     734              : 
     735            0 :         if (!receiveRequestBody.empty()) {
     736            0 :             success = (receiveRequestBody == "true" || receiveRequestBody == "false");
     737            0 :             if (success) {
     738            0 :                 getHttp2Server()->setReceiveRequestBody(b_receiveRequestBody);
     739            0 :                 LOGWARNING(ert::tracing::Logger::warning(ert::tracing::Logger::asString("Traffic server request body reception: %s", b_receiveRequestBody ? "processed":"ignored"), ERT_FILE_LOCATION));
     740              :             }
     741              :         }
     742              : 
     743            0 :         if (success && !preReserveRequestBody.empty()) {
     744            0 :             success = (preReserveRequestBody == "true" || preReserveRequestBody == "false");
     745            0 :             if (success) {
     746            0 :                 getHttp2Server()->setPreReserveRequestBody(b_preReserveRequestBody);
     747            0 :                 LOGWARNING(ert::tracing::Logger::warning(ert::tracing::Logger::asString("Traffic server dynamic request body allocation: %s", b_preReserveRequestBody ? "false":"true"), ERT_FILE_LOCATION));
     748              :             }
     749              :         }
     750            0 :     }
     751            0 :     else if (pathSuffix == "server-data/configuration" || pathSuffix == "client-data/configuration") {
     752              : 
     753            0 :         std::string discard;
     754            0 :         std::string discardKeyHistory;
     755            0 :         std::string disablePurge;
     756              : 
     757            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     758            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     759            0 :             auto it = qmap.find("discard");
     760            0 :             if (it != qmap.end()) discard = it->second;
     761            0 :             it = qmap.find("discardKeyHistory");
     762            0 :             if (it != qmap.end()) discardKeyHistory = it->second;
     763            0 :             it = qmap.find("disablePurge");
     764            0 :             if (it != qmap.end()) disablePurge = it->second;
     765            0 :         }
     766              : 
     767            0 :         bool b_discard = (discard == "true");
     768            0 :         bool b_discardKeyHistory = (discardKeyHistory == "true");
     769            0 :         bool b_disablePurge = (disablePurge == "true");
     770              : 
     771            0 :         success = (!discard.empty() || !discardKeyHistory.empty() || !disablePurge.empty());
     772              : 
     773            0 :         if (success) {
     774            0 :             if (!discard.empty() && !discardKeyHistory.empty())
     775            0 :                 success = !(b_discard && !b_discardKeyHistory); // it has no sense to try to keep history if whole data is discarded
     776              :         }
     777              : 
     778            0 :         bool serverMode = (pathSuffix == "server-data/configuration"); // true: server mode, false: client mode
     779            0 :         const char *mode = (serverMode ? "server":"client");
     780              : 
     781            0 :         if (success) {
     782            0 :             if (!discard.empty()) {
     783            0 :                 if (serverMode) getHttp2Server()->discardData(b_discard);
     784            0 :                 else discardClientData(b_discard);
     785            0 :                 LOGWARNING(ert::tracing::Logger::warning(ert::tracing::Logger::asString("Discard %s-data: %s", mode, b_discard ? "true":"false"), ERT_FILE_LOCATION));
     786              :             }
     787            0 :             if (!discardKeyHistory.empty()) {
     788            0 :                 if (serverMode) getHttp2Server()->discardDataKeyHistory(b_discardKeyHistory);
     789            0 :                 else discardClientDataKeyHistory(b_discardKeyHistory);
     790            0 :                 LOGWARNING(ert::tracing::Logger::warning(ert::tracing::Logger::asString("Discard %s-data key history: %s", mode, b_discardKeyHistory ? "true":"false"), ERT_FILE_LOCATION));
     791              :             }
     792            0 :             if (!disablePurge.empty()) {
     793            0 :                 if (serverMode) getHttp2Server()->disablePurge(b_disablePurge);
     794            0 :                 else disableClientPurge(b_disablePurge);
     795            0 :                 LOGWARNING(
     796              :                     ert::tracing::Logger::warning(ert::tracing::Logger::asString("Disable %s purge execution: %s", mode, b_disablePurge ? "true":"false"), ERT_FILE_LOCATION);
     797              :                     if (!b_disablePurge && b_discardKeyHistory)
     798              :                     ert::tracing::Logger::warning(ert::tracing::Logger::asString("Purge execution will be limited as history is discarded for %s data", mode), ERT_FILE_LOCATION);
     799              :                     if (!b_disablePurge && b_discard)
     800              :                         ert::tracing::Logger::warning(ert::tracing::Logger::asString("Purge execution has no sense as no events will be stored at %s storage", mode), ERT_FILE_LOCATION);
     801              :                     );
     802              :             }
     803              :         }
     804              :         else {
     805            0 :             ert::tracing::Logger::error(ert::tracing::Logger::asString("Cannot keep requests history if %s data storage is discarded", mode), ERT_FILE_LOCATION);
     806              :         }
     807            0 :     }
     808            0 :     else if (pathSuffix == "files/configuration") {
     809            0 :         std::string readCache;
     810              : 
     811            0 :         if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     812            0 :             std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     813            0 :             auto it = qmap.find("readCache");
     814            0 :             if (it != qmap.end()) readCache = it->second;
     815              : 
     816            0 :             success = (readCache == "true" || readCache == "false");
     817            0 :         }
     818              : 
     819            0 :         if (success) {
     820            0 :             bool b_readCache = (readCache == "true");
     821            0 :             getFileManager()->enableReadCache(b_readCache);
     822            0 :             LOGWARNING(ert::tracing::Logger::warning(ert::tracing::Logger::asString("File read cache: %s", b_readCache ? "true":"false"), ERT_FILE_LOCATION));
     823              :         }
     824            0 :     }
     825              : 
     826            0 :     statusCode = success ? ert::http2comm::ResponseCode::OK:ert::http2comm::ResponseCode::BAD_REQUEST; // 200 or 400
     827            0 : }
     828              : 
     829           46 : void MyAdminHttp2Server::receive(const std::uint64_t &receptionId,
     830              :                                  const nghttp2::asio_http2::server::request&
     831              :                                  req,
     832              :                                  const std::string &requestBody,
     833              :                                  const std::chrono::microseconds &receptionTimestampUs,
     834              :                                  unsigned int& statusCode, nghttp2::asio_http2::header_map& headers,
     835              :                                  std::string& responseBody, unsigned int &responseDelayMs)
     836              : {
     837           46 :     LOGDEBUG(ert::tracing::Logger::debug("receive()",  ERT_FILE_LOCATION));
     838              : 
     839              :     // see uri_ref struct (https://nghttp2.org/documentation/asio_http2.h.html#asio-http2-h)
     840           46 :     std::string method = req.method();
     841              :     //std::string uriPath = req.uri().raw_path; // percent-encoded
     842           46 :     std::string uriPath = req.uri().path; // decoded
     843           46 :     std::string uriQuery = req.uri().raw_query; // parameter values may be percent-encoded
     844              : 
     845              :     // Get path suffix normalized:
     846           46 :     std::string pathSuffix = getPathSuffix(uriPath);
     847           46 :     bool noPathSuffix = pathSuffix.empty();
     848           46 :     LOGDEBUG(
     849              :     if (noPathSuffix) {
     850              :     ert::tracing::Logger::debug("URI Path Suffix: <null>", ERT_FILE_LOCATION);
     851              :     }
     852              :     else {
     853              :         std::stringstream ss;
     854              :         ss << "ADMIN REQUEST RECEIVED | Method: " << method
     855              :            << " | Headers: " << ert::http2comm::headersAsString(req.header())
     856              :            << " | Uri: " << req.uri().scheme << "://" << req.uri().host << uriPath;
     857              :         if (!uriQuery.empty()) {
     858              :             ss << " | Query Params: " << uriQuery;
     859              :         }
     860              :         if (!requestBody.empty()) {
     861              :             std::string requestBodyWithoutNewlines = requestBody; // administrative interface receives json bodies in POST requests, so we normalize for logging
     862              :             requestBodyWithoutNewlines.erase(std::remove(requestBodyWithoutNewlines.begin(), requestBodyWithoutNewlines.end(), '\n'), requestBodyWithoutNewlines.end());
     863              :             ss << " | Body: " << requestBodyWithoutNewlines;
     864              :         }
     865              :         ert::tracing::Logger::debug(ss.str(), ERT_FILE_LOCATION);
     866              :     }
     867              :     );
     868              : 
     869              :     // Defaults
     870           46 :     responseBody.clear();
     871              : 
     872              :     // No operation provided:
     873           46 :     if (noPathSuffix) {
     874            2 :         receiveNOOP(statusCode, headers, responseBody);
     875            2 :         return;
     876              :     }
     877              : 
     878              :     // Methods supported:
     879           44 :     if (method == "DELETE") {
     880            0 :         receiveDELETE(pathSuffix, uriQuery, statusCode);
     881            0 :         headers.clear();
     882            0 :         return;
     883              :     }
     884           44 :     else if (method == "GET") {
     885           46 :         receiveGET(req.uri().scheme + std::string("://") + req.uri().host + uriPath /* schema $id */, pathSuffix, uriQuery, statusCode, headers, responseBody);
     886           23 :         return;
     887              :     }
     888           21 :     else if (method == "POST") {
     889           21 :         receivePOST(pathSuffix, requestBody, statusCode, headers, responseBody);
     890           21 :         return;
     891              :     }
     892            0 :     else if (method == "PUT") {
     893            0 :         receivePUT(pathSuffix, uriQuery, statusCode);
     894            0 :         headers.clear();
     895            0 :         return;
     896              :     }
     897          184 : }
     898              : 
     899            0 : void MyAdminHttp2Server::triggerClientOperation(const std::string &clientProvisionId, const std::string &queryParams, unsigned int& statusCode) const {
     900              : 
     901            0 :     std::string inState = DEFAULT_ADMIN_PROVISION_STATE; // administrative operation triggers "initial" provisions by default
     902            0 :     std::string sequenceBegin = "";
     903            0 :     std::string sequenceEnd = "";
     904            0 :     std::string rps = "";
     905            0 :     std::string repeat = "";
     906              : 
     907            0 :     if (!queryParams.empty()) { // https://stackoverflow.com/questions/978061/http-get-with-request-body#:~:text=Yes.,semantic%20meaning%20to%20the%20request.
     908            0 :         std::map<std::string, std::string> qmap = h2agent::model::extractQueryParameters(queryParams);
     909            0 :         auto it = qmap.find("inState");
     910            0 :         if (it != qmap.end()) inState = it->second;
     911            0 :         it = qmap.find("sequenceBegin");
     912            0 :         if (it != qmap.end()) sequenceBegin = it->second;
     913            0 :         it = qmap.find("sequenceEnd");
     914            0 :         if (it != qmap.end()) sequenceEnd = it->second;
     915            0 :         it = qmap.find("rps");
     916            0 :         if (it != qmap.end()) rps = it->second;
     917            0 :         it = qmap.find("repeat");
     918            0 :         if (it != qmap.end()) repeat = it->second;
     919            0 :     }
     920              : 
     921              :     // Admin provision:
     922            0 :     const h2agent::model::AdminClientProvisionData & provisionData = getAdminData()->getClientProvisionData();
     923            0 :     std::shared_ptr<h2agent::model::AdminClientProvision> provision = provisionData.find(inState, clientProvisionId);
     924              : 
     925            0 :     if (!provision) {
     926            0 :         statusCode = ert::http2comm::ResponseCode::NOT_FOUND; // 404
     927            0 :         return;
     928              :     }
     929              : 
     930            0 :     statusCode = ert::http2comm::ResponseCode::OK; // 200
     931            0 :     if (!sequenceBegin.empty() || !sequenceEnd.empty() || !rps.empty() || !repeat.empty()) {
     932            0 :         if (provision->updateTriggering(sequenceBegin, sequenceEnd, rps, repeat)) {
     933            0 :             statusCode = ert::http2comm::ResponseCode::ACCEPTED; // 202; "sender" operates asynchronously
     934              :         }
     935              :         else {
     936            0 :             statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     937            0 :             return;
     938              :         }
     939              :     }
     940              : 
     941              :     // Process provision (before sending)
     942            0 :     provision->employ(); // set provision as employed:
     943            0 :     std::string requestMethod{};
     944            0 :     std::string requestUri{};
     945            0 :     std::string requestBody{};
     946            0 :     nghttp2::asio_http2::header_map requestHeaders;
     947              : 
     948            0 :     std::string outState{};
     949            0 :     unsigned int requestDelayMs{};
     950            0 :     unsigned int requestTimeoutMs{};
     951              : 
     952            0 :     std::string error{}; // error detail (empty when all is OK)
     953              : 
     954            0 :     provision->transform(requestMethod, requestUri, requestBody, requestHeaders, outState, requestDelayMs, requestTimeoutMs, error);
     955            0 :     LOGDEBUG(
     956              :         ert::tracing::Logger::debug(ert::tracing::Logger::asString("Request method: %s", requestMethod.c_str()), ERT_FILE_LOCATION);
     957              :         ert::tracing::Logger::debug(ert::tracing::Logger::asString("Request uri: %s", requestUri.c_str()), ERT_FILE_LOCATION);
     958              :         ert::tracing::Logger::debug(ert::tracing::Logger::asString("Request body: %s", requestBody.c_str()), ERT_FILE_LOCATION);
     959              :     );
     960              : 
     961            0 :     if (error.empty()) {
     962            0 :         const h2agent::model::AdminClientEndpointData & clientEndpointData = getAdminData()->getClientEndpointData();
     963            0 :         std::shared_ptr<h2agent::model::AdminClientEndpoint> clientEndpoint(nullptr);
     964            0 :         clientEndpoint = clientEndpointData.find(provision->getClientEndpointId());
     965            0 :         if (clientEndpoint) {
     966            0 :             if (clientEndpoint->getPermit()) {
     967            0 :                 clientEndpoint->connect();
     968            0 :                 ert::http2comm::Http2Client::response response = clientEndpoint->getClient()->send(requestMethod, requestUri, requestBody, requestHeaders, std::chrono::milliseconds(requestTimeoutMs));
     969            0 :                 clientEndpoint->getClient()->incrementGeneralUniqueClientSequence();
     970              : 
     971              :                 // Store event:
     972            0 :                 if (client_data_) {
     973            0 :                     h2agent::model::DataKey dataKey(provision->getClientEndpointId(), requestMethod, requestUri);
     974            0 :                     h2agent::model::DataPart responseBodyDataPart(response.body);
     975            0 :                     getMockClientData()->loadEvent(dataKey, provision->getClientProvisionId(), inState, outState, response.sendingUs, response.receptionUs, response.statusCode, requestHeaders, response.headers, requestBody, responseBodyDataPart, clientEndpoint->getGeneralUniqueClientSequence(), provision->getSeq(), requestDelayMs, requestTimeoutMs, client_data_key_history_ /* history enabled */);
     976            0 :                 }
     977            0 :             }
     978              :             else {
     979            0 :                 error = "Referenced client endpoint is disabled (not permitted)";
     980              :             }
     981              :         }
     982              :         else {
     983            0 :             error = "Referenced client endpoint is not provisioned";
     984              :         }
     985            0 :     }
     986              :     else {
     987            0 :         statusCode = ert::http2comm::ResponseCode::BAD_REQUEST; // 400
     988              :     }
     989            0 : }
     990              : 
     991            0 : std::string MyAdminHttp2Server::clientDataConfigurationAsJsonString() const {
     992            0 :     nlohmann::json result;
     993              : 
     994            0 :     result["storeEvents"] = client_data_;
     995            0 :     result["storeEventsKeyHistory"] = client_data_key_history_;
     996            0 :     result["purgeExecution"] = purge_execution_;
     997              : 
     998            0 :     return result.dump();
     999            0 : }
    1000              : 
    1001              : }
    1002              : }
        

Generated by: LCOV version 2.0-1