1 #pragma once 2 #include "logging.hpp" 3 #include "nlohmann/json.hpp" 4 5 #include <boost/beast/http/message.hpp> 6 #include <boost/beast/http/string_body.hpp> 7 #include <utils/hex_utils.hpp> 8 9 #include <optional> 10 #include <string> 11 #include <string_view> 12 13 namespace crow 14 { 15 16 template <typename Adaptor, typename Handler> 17 class Connection; 18 19 struct Response 20 { 21 template <typename Adaptor, typename Handler> 22 friend class crow::Connection; 23 using response_type = 24 boost::beast::http::response<boost::beast::http::string_body>; 25 26 std::optional<response_type> stringResponse; 27 28 nlohmann::json jsonValue; 29 30 void addHeader(const std::string_view key, const std::string_view value) 31 { 32 stringResponse->set(key, value); 33 } 34 35 void addHeader(boost::beast::http::field key, std::string_view value) 36 { 37 stringResponse->set(key, value); 38 } 39 40 Response() : stringResponse(response_type{}) 41 {} 42 43 ~Response() = default; 44 45 Response(const Response&) = delete; 46 Response(Response&&) = delete; 47 48 Response& operator=(const Response& r) = delete; 49 50 Response& operator=(Response&& r) noexcept 51 { 52 BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this 53 << "; other: " << &r; 54 if (this == &r) 55 { 56 return *this; 57 } 58 stringResponse = std::move(r.stringResponse); 59 r.stringResponse.emplace(response_type{}); 60 jsonValue = std::move(r.jsonValue); 61 completed = r.completed; 62 completeRequestHandler = std::move(r.completeRequestHandler); 63 isAliveHelper = std::move(r.isAliveHelper); 64 r.completeRequestHandler = nullptr; 65 r.isAliveHelper = nullptr; 66 return *this; 67 } 68 69 void result(boost::beast::http::status v) 70 { 71 stringResponse->result(v); 72 } 73 74 boost::beast::http::status result() 75 { 76 return stringResponse->result(); 77 } 78 79 unsigned resultInt() const 80 { 81 return stringResponse->result_int(); 82 } 83 84 std::string_view reason() 85 { 86 return stringResponse->reason(); 87 } 88 89 bool isCompleted() const noexcept 90 { 91 return completed; 92 } 93 94 std::string& body() 95 { 96 return stringResponse->body(); 97 } 98 99 void keepAlive(bool k) 100 { 101 stringResponse->keep_alive(k); 102 } 103 104 bool keepAlive() 105 { 106 return stringResponse->keep_alive(); 107 } 108 109 void preparePayload() 110 { 111 stringResponse->prepare_payload(); 112 } 113 114 void clear() 115 { 116 BMCWEB_LOG_DEBUG << this << " Clearing response containers"; 117 stringResponse.emplace(response_type{}); 118 jsonValue.clear(); 119 completed = false; 120 } 121 122 void write(std::string_view bodyPart) 123 { 124 stringResponse->body() += std::string(bodyPart); 125 } 126 127 void end() 128 { 129 // Only set etag if this request succeeded 130 if (result() == boost::beast::http::status::ok) 131 { 132 // and the json response isn't empty 133 if (!jsonValue.empty()) 134 { 135 size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 136 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\""; 137 addHeader(boost::beast::http::field::etag, hexVal); 138 } 139 } 140 if (completed) 141 { 142 BMCWEB_LOG_ERROR << this << " Response was ended twice"; 143 return; 144 } 145 completed = true; 146 BMCWEB_LOG_DEBUG << this << " calling completion handler"; 147 if (completeRequestHandler) 148 { 149 BMCWEB_LOG_DEBUG << this << " completion handler was valid"; 150 completeRequestHandler(*this); 151 } 152 } 153 154 bool isAlive() 155 { 156 return isAliveHelper && isAliveHelper(); 157 } 158 159 void setCompleteRequestHandler(std::function<void(Response&)>&& handler) 160 { 161 BMCWEB_LOG_DEBUG << this << " setting completion handler"; 162 completeRequestHandler = std::move(handler); 163 } 164 165 std::function<void(Response&)> releaseCompleteRequestHandler() 166 { 167 BMCWEB_LOG_DEBUG << this << " releasing completion handler" 168 << static_cast<bool>(completeRequestHandler); 169 std::function<void(Response&)> ret = completeRequestHandler; 170 completeRequestHandler = nullptr; 171 return ret; 172 } 173 174 void setIsAliveHelper(std::function<bool()>&& handler) 175 { 176 isAliveHelper = std::move(handler); 177 } 178 179 std::function<bool()> releaseIsAliveHelper() 180 { 181 std::function<bool()> ret = std::move(isAliveHelper); 182 isAliveHelper = nullptr; 183 return ret; 184 } 185 186 private: 187 bool completed = false; 188 std::function<void(Response&)> completeRequestHandler; 189 std::function<bool()> isAliveHelper; 190 191 // In case of a JSON object, set the Content-Type header 192 void jsonMode() 193 { 194 addHeader("Content-Type", "application/json"); 195 } 196 }; 197 } // namespace crow 198