| #include "http_response.hpp" | |
| #include <chrono> // NOLINT | |
| #include <cstddef> | |
| #include <functional> | |
| #include <optional> | |
| #include <string> | |
| #include <utility> | |
| #include "absl/strings/match.h" | |
| #include "boost/algorithm/string/case_conv.hpp" // NOLINT | |
| #include "logging.hpp" | |
| #include "hex_utils.hpp" | |
| #include <nlohmann/json.hpp> | |
| #include "managed_store.hpp" | |
| namespace crow { | |
| Response& Response::operator=(Response&& r) noexcept { | |
| BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this | |
| << "; other: " << &r; | |
| if (this == &r) { | |
| return *this; | |
| } | |
| stringResponse = std::move(r.stringResponse); | |
| r.stringResponse.emplace(response_type{}); | |
| jsonValue = std::move(r.jsonValue); | |
| for (const auto& [marker, timestamp] : r.markers) { | |
| markers.emplace(marker, timestamp); | |
| } | |
| // Only need to move completion handler if not already completed | |
| // Note, there are cases where we might move out of a Response object | |
| // while in a completion handler for that response object. This check | |
| // is intended to prevent destructing the functor we are currently | |
| // executing from in that case. | |
| if (!r.completed) { | |
| completeRequestHandler = std::move(r.completeRequestHandler); | |
| r.completeRequestHandler = nullptr; | |
| } else { | |
| completeRequestHandler = nullptr; | |
| } | |
| completed = r.completed; | |
| isAliveHelper = std::move(r.isAliveHelper); | |
| r.isAliveHelper = nullptr; | |
| return *this; | |
| } | |
| void Response::clear() { | |
| BMCWEB_LOG_DEBUG << this << " Clearing response containers"; | |
| stringResponse.emplace(response_type{}); | |
| jsonValue.clear(); | |
| completed = false; | |
| expectedHash = std::nullopt; | |
| } | |
| std::string Response::computeEtag() const { | |
| // return empty string if we don't have a string response | |
| if (!stringResponse.has_value()) { | |
| return ""; | |
| } | |
| // Only set etag if this request succeeded | |
| if (result() != boost::beast::http::status::ok) { | |
| return ""; | |
| } | |
| // and the json response isn't empty | |
| if (jsonValue.empty()) { | |
| return ""; | |
| } | |
| size_t hashval = std::hash<nlohmann::json>{}(jsonValue); | |
| return "\"" + intToHexString(hashval, 8) + "\""; | |
| } | |
| void Response::setHashAndHandleNotModified() { | |
| // Can only hash if we have content that's valid | |
| if (jsonValue.empty() || result() != boost::beast::http::status::ok) { | |
| return; | |
| } | |
| size_t hashval = std::hash<nlohmann::json>{}(jsonValue); | |
| std::string hex_val = "\"" + intToHexString(hashval, 8) + "\""; | |
| addHeader(boost::beast::http::field::etag, hex_val); | |
| if (expectedHash && hex_val == *expectedHash) { | |
| jsonValue.clear(); | |
| result(boost::beast::http::status::not_modified); | |
| } | |
| } | |
| void Response::end() { | |
| std::string etag = computeEtag(); | |
| if (!etag.empty()) { | |
| addHeader(boost::beast::http::field::etag, etag); | |
| } | |
| if (completed) { | |
| BMCWEB_LOG_ERROR << this << " Response was ended twice"; | |
| return; | |
| } | |
| completed = true; | |
| BMCWEB_LOG_DEBUG << this << " calling completion handler"; | |
| if (completeRequestHandler) { | |
| BMCWEB_LOG_DEBUG << this << " completion handler was valid"; | |
| completeRequestHandler(*this); | |
| } | |
| } | |
| void Response::setCompleteRequestHandler( | |
| std::function<void(Response&)>&& handler) { | |
| completeRequestHandler = std::move(handler); | |
| BMCWEB_LOG_DEBUG << this << " setting completion handler" | |
| << &completeRequestHandler; | |
| // Now that we have a new completion handler attached, we're no longer | |
| // complete | |
| completed = false; | |
| } | |
| std::function<void(Response&)> Response::releaseCompleteRequestHandler() { | |
| BMCWEB_LOG_DEBUG << this << " releasing completion handler" | |
| << &completeRequestHandler; | |
| std::function<void(Response&)> ret = completeRequestHandler; | |
| completeRequestHandler = nullptr; | |
| completed = true; | |
| return ret; | |
| } | |
| void Response::addTimetrace(const std::string& marker) { | |
| if (managedStore::GetManagedObjectStore()->getConfig().timetrace) { | |
| std::chrono::high_resolution_clock::time_point now = | |
| std::chrono::high_resolution_clock::now(); | |
| auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( | |
| now.time_since_epoch()) | |
| .count(); | |
| markers[marker] = ms; | |
| } | |
| } | |
| void Response::startTimetrace(const std::string& block) { | |
| addTimetrace("START_" + block); | |
| } | |
| void Response::endTimetrace(const std::string& block) { | |
| addTimetrace("END_" + block); | |
| } | |
| void Response::requestStartTime(const std::string& uri) { | |
| addTimetrace("REQUEST_" + uri); | |
| } | |
| void Response::requestEndTime() { addTimetrace("REQUESTEND"); } | |
| nlohmann::json Response::timetraceToJson() { | |
| nlohmann::json json_obj; | |
| if (managedStore::GetManagedObjectStore()->getConfig().timetrace) { | |
| nlohmann::json& hops = json_obj["hops"]; | |
| hops = nlohmann::json::array(); | |
| for (const auto& [marker, timestamp] : markers) { | |
| if (absl::StrContains(marker, "START_")) { | |
| continue; | |
| } | |
| // process END_ | |
| size_t pos = marker.find("END_"); | |
| if (pos != std::string::npos) { | |
| // find the start and calculate the diff | |
| std::string block = marker.substr(pos + 4); | |
| auto start_time = markers["START_" + block]; | |
| auto duration = timestamp - start_time; | |
| nlohmann::json::object_t hop; | |
| hop["id"] = boost::algorithm::to_lower_copy(block); | |
| hop["time_ms"] = duration; | |
| hops.emplace_back(std::move(hop)); | |
| } else if (absl::StrContains(marker, "REQUEST_")) { | |
| size_t uri_pos = marker.find("REQUEST_"); | |
| std::string uri = marker.substr(uri_pos + 8); | |
| json_obj["uri"] = uri; | |
| json_obj["starttime"] = timestamp; | |
| } else if (absl::StrContains(marker, "REQUESTEND")) { | |
| json_obj["endtime"] = timestamp; | |
| } | |
| } | |
| } | |
| return json_obj; | |
| } | |
| void Response::updateTraceInManagedStore() { | |
| if (managedStore::GetManagedObjectStore()->getConfig().timetrace) { | |
| nlohmann::json json_obj = timetraceToJson(); | |
| managedStore::GetManagedObjectStore()->storeTimeTrace(json_obj); | |
| } | |
| } | |
| } // namespace crow |