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(Response&& res) noexcept : 44 stringResponse(std::move(res.stringResponse)), completed(res.completed) 45 { 46 jsonValue = std::move(res.jsonValue); 47 // See note in operator= move handler for why this is needed. 48 if (!res.completed) 49 { 50 completeRequestHandler = std::move(res.completeRequestHandler); 51 res.completeRequestHandler = nullptr; 52 } 53 isAliveHelper = res.isAliveHelper; 54 res.isAliveHelper = nullptr; 55 } 56 57 ~Response() = default; 58 59 Response(const Response&) = delete; 60 61 Response& operator=(const Response& r) = delete; 62 63 Response& operator=(Response&& r) noexcept 64 { 65 BMCWEB_LOG_DEBUG << "Moving response containers; this: " << this 66 << "; other: " << &r; 67 if (this == &r) 68 { 69 return *this; 70 } 71 stringResponse = std::move(r.stringResponse); 72 r.stringResponse.emplace(response_type{}); 73 jsonValue = std::move(r.jsonValue); 74 75 // Only need to move completion handler if not already completed 76 // Note, there are cases where we might move out of a Response object 77 // while in a completion handler for that response object. This check 78 // is intended to prevent destructing the functor we are currently 79 // executing from in that case. 80 if (!r.completed) 81 { 82 completeRequestHandler = std::move(r.completeRequestHandler); 83 r.completeRequestHandler = nullptr; 84 } 85 else 86 { 87 completeRequestHandler = nullptr; 88 } 89 completed = r.completed; 90 isAliveHelper = std::move(r.isAliveHelper); 91 r.isAliveHelper = nullptr; 92 return *this; 93 } 94 95 void result(unsigned v) 96 { 97 stringResponse->result(v); 98 } 99 100 void result(boost::beast::http::status v) 101 { 102 stringResponse->result(v); 103 } 104 105 boost::beast::http::status result() const 106 { 107 return stringResponse->result(); 108 } 109 110 unsigned resultInt() const 111 { 112 return stringResponse->result_int(); 113 } 114 115 std::string_view reason() const 116 { 117 return stringResponse->reason(); 118 } 119 120 bool isCompleted() const noexcept 121 { 122 return completed; 123 } 124 125 std::string& body() 126 { 127 return stringResponse->body(); 128 } 129 130 std::string_view getHeaderValue(std::string_view key) const 131 { 132 return stringResponse->base()[key]; 133 } 134 135 void keepAlive(bool k) 136 { 137 stringResponse->keep_alive(k); 138 } 139 140 bool keepAlive() const 141 { 142 return stringResponse->keep_alive(); 143 } 144 145 void preparePayload() 146 { 147 stringResponse->prepare_payload(); 148 } 149 150 void clear() 151 { 152 BMCWEB_LOG_DEBUG << this << " Clearing response containers"; 153 stringResponse.emplace(response_type{}); 154 jsonValue.clear(); 155 completed = false; 156 } 157 158 void write(std::string_view bodyPart) 159 { 160 stringResponse->body() += std::string(bodyPart); 161 } 162 163 void end() 164 { 165 // Only set etag if this request succeeded 166 if (result() == boost::beast::http::status::ok) 167 { 168 // and the json response isn't empty 169 if (!jsonValue.empty()) 170 { 171 size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 172 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\""; 173 addHeader(boost::beast::http::field::etag, hexVal); 174 } 175 } 176 if (completed) 177 { 178 BMCWEB_LOG_ERROR << this << " Response was ended twice"; 179 return; 180 } 181 completed = true; 182 BMCWEB_LOG_DEBUG << this << " calling completion handler"; 183 if (completeRequestHandler) 184 { 185 BMCWEB_LOG_DEBUG << this << " completion handler was valid"; 186 completeRequestHandler(*this); 187 } 188 } 189 190 bool isAlive() const 191 { 192 return isAliveHelper && isAliveHelper(); 193 } 194 195 void setCompleteRequestHandler(std::function<void(Response&)>&& handler) 196 { 197 BMCWEB_LOG_DEBUG << this << " setting completion handler"; 198 completeRequestHandler = std::move(handler); 199 200 // Now that we have a new completion handler attached, we're no longer 201 // complete 202 completed = false; 203 } 204 205 std::function<void(Response&)> releaseCompleteRequestHandler() 206 { 207 BMCWEB_LOG_DEBUG << this << " releasing completion handler" 208 << static_cast<bool>(completeRequestHandler); 209 std::function<void(Response&)> ret = completeRequestHandler; 210 completeRequestHandler = nullptr; 211 completed = true; 212 return ret; 213 } 214 215 void setIsAliveHelper(std::function<bool()>&& handler) 216 { 217 isAliveHelper = std::move(handler); 218 } 219 220 std::function<bool()> releaseIsAliveHelper() 221 { 222 std::function<bool()> ret = std::move(isAliveHelper); 223 isAliveHelper = nullptr; 224 return ret; 225 } 226 227 private: 228 bool completed = false; 229 std::function<void(Response&)> completeRequestHandler; 230 std::function<bool()> isAliveHelper; 231 }; 232 } // namespace crow 233