#pragma once #include #include #include "configuration.hpp" #include "curl.hpp" #include "json.hpp" #include "sign.hpp" #include #include namespace rublon { template < typename HttpHandler = CURL > class CoreHandler : public CoreHandlerInterface< CoreHandler< HttpHandler > > { std::string secretKey; std::string url; std::pmr::string xRublonSignature(std::pmr::memory_resource & mr, std::string_view body) const { return {signData(body, secretKey.c_str()).data(), &mr}; } void signRequest(std::pmr::monotonic_buffer_resource & mr, Request & request) const { request.headers["X-Rublon-Signature"] = xRublonSignature(mr, request.body); } bool responseSigned(const Response & response) const { const auto & xRubResp = response.headers.at("x-rublon-signature"); const auto & sign = signData(response.body, secretKey); const bool signatureMatch = xRubResp == sign.data(); if(not signatureMatch) log(LogLevel::Error, "Signature mismatch %s != %s ", xRubResp.c_str(), sign.data()); return signatureMatch; } protected: HttpHandler http{}; public: CoreHandler(const Configuration & config) : secretKey{config.parameters.secretKey}, url{config.parameters.apiServer}, http{[]() { return Response{}; }} {} tl::expected< Response, Error > validateSignature(const Response & response) const { if(not responseSigned(response)) { log(LogLevel::Error, "CoreHandlerError::BadSigature"); return tl::unexpected{CoreHandlerError{CoreHandlerError::BadSigature}}; } return response; } tl::expected< Document, Error > validateResponse(const Response & response) const { RapidJSONPMRStackAlloc< 4 * 1024 > alloc{}; Document resp{&alloc}; resp.Parse(response.body.c_str()); if(resp.HasParseError() or not resp.HasMember("result")) { log(LogLevel::Error, "rublon Core responded with broken data"); return tl::unexpected{CoreHandlerError{CoreHandlerError::BrokenData}}; } if(resp["result"].HasMember("exception")) { const auto & exception = resp["result"]["exception"].GetString(); log(LogLevel::Error, "rublon Core exception %s", exception); return tl::unexpected{CoreHandlerError{CoreHandlerError::CoreException, exception}}; } return resp; } tl::expected< Document, Error > request(std::string_view path, const Document & body) const { const auto validateSignature = [this](const auto & arg) { return this->validateSignature(arg); }; const auto validateResponse = [this](const auto & arg) { return this->validateResponse(arg); }; RapidJSONPMRStackAlloc< 1 * 1024 > alloc{}; StringBuffer jsonStr{&alloc}; Writer writer{jsonStr, &alloc}; body.Accept(writer); std::byte _buffer[2 * 1024]; std::pmr::monotonic_buffer_resource mr{_buffer, sizeof(_buffer)}; Request request{mr}; request.headers["Content-Type"] = "application/json"; request.headers["Accept"] = "application/json"; request.body = jsonStr.GetString(); signRequest(mr, request); std::pmr::string uri{url + path.data(), &mr}; return http.request(uri, request) .and_then(validateSignature) .and_then(validateResponse) .or_else([](const Error & e) -> tl::expected< Document, Error > { return tl::unexpected{e}; }); } }; } // namespace rublon