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(boost::beast::http::status v) 96 { 97 stringResponse->result(v); 98 } 99 100 boost::beast::http::status result() const 101 { 102 return stringResponse->result(); 103 } 104 105 unsigned resultInt() const 106 { 107 return stringResponse->result_int(); 108 } 109 110 std::string_view reason() const 111 { 112 return stringResponse->reason(); 113 } 114 115 bool isCompleted() const noexcept 116 { 117 return completed; 118 } 119 120 std::string& body() 121 { 122 return stringResponse->body(); 123 } 124 125 std::string_view getHeaderValue(std::string_view key) const 126 { 127 return stringResponse->base()[key]; 128 } 129 130 void keepAlive(bool k) 131 { 132 stringResponse->keep_alive(k); 133 } 134 135 bool keepAlive() const 136 { 137 return stringResponse->keep_alive(); 138 } 139 140 void preparePayload() 141 { 142 stringResponse->prepare_payload(); 143 } 144 145 void clear() 146 { 147 BMCWEB_LOG_DEBUG << this << " Clearing response containers"; 148 stringResponse.emplace(response_type{}); 149 jsonValue.clear(); 150 completed = false; 151 } 152 153 void write(std::string_view bodyPart) 154 { 155 stringResponse->body() += std::string(bodyPart); 156 } 157 158 void end() 159 { 160 // Only set etag if this request succeeded 161 if (result() == boost::beast::http::status::ok) 162 { 163 // and the json response isn't empty 164 if (!jsonValue.empty()) 165 { 166 size_t hashval = std::hash<nlohmann::json>{}(jsonValue); 167 std::string hexVal = "\"" + intToHexString(hashval, 8) + "\""; 168 addHeader(boost::beast::http::field::etag, hexVal); 169 } 170 } 171 if (completed) 172 { 173 BMCWEB_LOG_ERROR << this << " Response was ended twice"; 174 return; 175 } 176 completed = true; 177 BMCWEB_LOG_DEBUG << this << " calling completion handler"; 178 if (completeRequestHandler) 179 { 180 BMCWEB_LOG_DEBUG << this << " completion handler was valid"; 181 completeRequestHandler(*this); 182 } 183 } 184 185 bool isAlive() const 186 { 187 return isAliveHelper && isAliveHelper(); 188 } 189 190 void setCompleteRequestHandler(std::function<void(Response&)>&& handler) 191 { 192 BMCWEB_LOG_DEBUG << this << " setting completion handler"; 193 completeRequestHandler = std::move(handler); 194 195 // Now that we have a new completion handler attached, we're no longer 196 // complete 197 completed = false; 198 } 199 200 std::function<void(Response&)> releaseCompleteRequestHandler() 201 { 202 BMCWEB_LOG_DEBUG << this << " releasing completion handler" 203 << static_cast<bool>(completeRequestHandler); 204 std::function<void(Response&)> ret = completeRequestHandler; 205 completeRequestHandler = nullptr; 206 completed = true; 207 return ret; 208 } 209 210 void setIsAliveHelper(std::function<bool()>&& handler) 211 { 212 isAliveHelper = std::move(handler); 213 } 214 215 std::function<bool()> releaseIsAliveHelper() 216 { 217 std::function<bool()> ret = std::move(isAliveHelper); 218 isAliveHelper = nullptr; 219 return ret; 220 } 221 222 private: 223 bool completed = false; 224 std::function<void(Response&)> completeRequestHandler; 225 std::function<bool()> isAliveHelper; 226 }; 227 } // namespace crow 228