101 Derived<HandlerType>&
104 return static_cast<Derived<HandlerType>&
>(*this);
111 explicit SendLambda(HttpBase& self) : self(self)
115 template <
bool IsRequest,
typename Body,
typename Fields>
117 operator()(http::message<IsRequest, Body, Fields>&& msg)
const
124 auto sp = std::make_shared<http::message<IsRequest, Body, Fields>>(std::move(msg));
131 self.derived().stream(),
133 boost::beast::bind_front_handler(
134 &HttpBase::onWrite, self.derived().shared_from_this(), sp->need_eof()
140 std::shared_ptr<void> res_;
142 std::shared_ptr<AdminVerificationStrategy> adminVerification_;
143 std::shared_ptr<ProxyIpResolver> proxyIpResolver_;
146 boost::beast::flat_buffer buffer_;
147 http::request<http::string_body> req_;
148 std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
149 std::shared_ptr<HandlerType>
const handler_;
150 std::reference_wrapper<data::LedgerCacheInterface const> cache_;
155 httpFail(boost::beast::error_code ec,
char const* what)
174 if (ec == boost::asio::ssl::error::stream_truncated)
177 if (!ec_ && ec != boost::asio::error::operation_aborted) {
179 LOG(perfLog_.info()) <<
tag() <<
": " << what <<
": " << ec.message();
180 boost::beast::get_lowest_layer(derived().stream()).socket().close(ec);
186 std::string
const& ip,
187 std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
188 std::shared_ptr<AdminVerificationStrategy> adminVerification,
189 std::shared_ptr<ProxyIpResolver> proxyIpResolver,
190 std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
191 std::shared_ptr<HandlerType> handler,
192 std::reference_wrapper<data::LedgerCacheInterface const> cache,
193 boost::beast::flat_buffer buffer
197 , adminVerification_(std::move(adminVerification))
198 , proxyIpResolver_(std::move(proxyIpResolver))
199 , buffer_(std::move(buffer))
200 , dosGuard_(dosGuard)
201 , handler_(std::move(handler))
204 LOG(perfLog_.debug()) <<
tag() <<
"http session created";
205 dosGuard_.get().increment(ip);
210 LOG(perfLog_.debug()) <<
tag() <<
"http session closed";
212 dosGuard_.get().decrement(clientIp_);
225 boost::beast::get_lowest_layer(derived().stream()).expires_after(std::chrono::seconds(30));
231 boost::beast::bind_front_handler(&HttpBase::onRead, derived().shared_from_this())
236 onRead(boost::beast::error_code ec, [[maybe_unused]] std::size_t bytesTransferred)
238 if (ec == http::error::end_of_stream)
239 return derived().doClose();
242 return httpFail(ec,
"read");
244 if (req_.method() == http::verb::get and req_.target() ==
"/health")
245 return sender_(httpResponse(http::status::ok,
"text/html", kHEALTH_CHECK_HTML));
247 if (req_.method() == http::verb::get and req_.target() ==
"/cache_state") {
248 if (cache_.get().isFull())
250 httpResponse(http::status::ok,
"text/html", kCACHE_CHECK_LOADED_HTML)
253 return sender_(httpResponse(
254 http::status::service_unavailable,
"text/html", kCACHE_CHECK_NOT_LOADED_HTML
258 if (
auto resolvedIp = proxyIpResolver_->resolveClientIp(clientIp_, req_);
259 resolvedIp != clientIp_) {
260 LOG(log_.info()) <<
tag()
261 <<
"Detected a forwarded request from proxy. Proxy ip: " << clientIp_
262 <<
". Resolved client ip: " << resolvedIp;
263 dosGuard_.get().decrement(clientIp_);
264 clientIp_ = std::move(resolvedIp);
265 dosGuard_.get().increment(clientIp_);
269 ConnectionBase::isAdmin_ = adminVerification_->isAdmin(req_, clientIp_);
271 if (boost::beast::websocket::is_upgrade(req_)) {
272 if (dosGuard_.get().isOk(clientIp_)) {
274 boost::beast::get_lowest_layer(derived().stream()).expires_never();
277 return derived().upgrade();
281 httpResponse(http::status::too_many_requests,
"text/html",
"Too many requests")
285 if (
auto response = util::prometheus::handlePrometheusRequest(req_,
isAdmin());
286 response.has_value())
287 return sender_(std::move(response.value()));
289 if (req_.method() != http::verb::post) {
291 httpResponse(http::status::bad_request,
"text/html",
"Expected a POST request")
295 LOG(log_.info()) <<
tag() <<
"Received request from ip = " << clientIp_;
298 (*handler_)(req_.body(), derived().shared_from_this());
299 }
catch (std::exception
const&) {
300 return sender_(httpResponse(
301 http::status::internal_server_error,
303 boost::json::serialize(
rpc::makeError(rpc::RippledError::rpcINTERNAL))
311 sender_(httpResponse(
312 http::status::service_unavailable,
314 boost::json::serialize(
rpc::makeError(rpc::RippledError::rpcSLOW_DOWN))
324 send(std::string&& msg, http::status status = http::status::ok)
override
326 if (!dosGuard_.get().add(clientIp_, msg.size())) {
327 auto jsonResponse = boost::json::parse(msg).as_object();
328 jsonResponse[
"warning"] =
"load";
329 if (jsonResponse.contains(
"warnings") && jsonResponse[
"warnings"].is_array()) {
330 jsonResponse[
"warnings"].as_array().push_back(
334 jsonResponse[
"warnings"] =
339 msg = boost::json::serialize(jsonResponse);
341 sender_(httpResponse(status,
"application/json", std::move(msg)));
347 ASSERT(
false,
"SubscriptionContext can't be created for a HTTP connection");
352 onWrite(
bool close, boost::beast::error_code ec, std::size_t bytesTransferred)
354 boost::ignore_unused(bytesTransferred);
357 return httpFail(ec,
"write");
362 return derived().doClose();
369 http::response<http::string_body>
370 httpResponse(http::status status, std::string contentType, std::string message)
const
372 http::response<http::string_body> res{status, req_.version()};
373 res.set(http::field::server,
"clio-server-" + util::build::getClioVersionString());
374 res.set(http::field::content_type, contentType);
375 res.keep_alive(req_.keep_alive());
376 res.body() = std::move(message);
377 res.prepare_payload();