86 return static_cast<Derived<HandlerType>&
>(*this);
93 explicit SendLambda(HttpBase& self) : self(self)
97 template <
bool IsRequest,
typename Body,
typename Fields>
99 operator()(http::message<IsRequest, Body, Fields>&& msg)
const
106 auto sp = std::make_shared<http::message<IsRequest, Body, Fields>>(std::move(msg));
113 self.derived().stream(),
115 boost::beast::bind_front_handler(
116 &HttpBase::onWrite, self.derived().shared_from_this(), sp->need_eof()
122 std::shared_ptr<void> res_;
124 std::shared_ptr<AdminVerificationStrategy> adminVerification_;
125 std::shared_ptr<ProxyIpResolver> proxyIpResolver_;
126 bool isProxyConnection_ =
false;
129 boost::beast::flat_buffer buffer_;
130 http::request<http::string_body> req_;
131 std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard_;
132 std::shared_ptr<HandlerType>
const handler_;
133 std::reference_wrapper<data::LedgerCacheInterface const> cache_;
138 httpFail(boost::beast::error_code ec,
char const* what)
157 if (ec == boost::asio::ssl::error::stream_truncated)
160 if (!ec_ && ec != boost::asio::error::operation_aborted) {
162 LOG(perfLog_.info()) <<
tag() <<
": " << what <<
": " << ec.message();
163 boost::beast::get_lowest_layer(derived().stream()).socket().close(ec);
169 std::string
const& ip,
170 std::reference_wrapper<util::TagDecoratorFactory const> tagFactory,
171 std::shared_ptr<AdminVerificationStrategy> adminVerification,
172 std::shared_ptr<ProxyIpResolver> proxyIpResolver,
173 std::reference_wrapper<dosguard::DOSGuardInterface> dosGuard,
174 std::shared_ptr<HandlerType> handler,
175 std::reference_wrapper<data::LedgerCacheInterface const> cache,
176 boost::beast::flat_buffer buffer
180 , adminVerification_(std::move(adminVerification))
181 , proxyIpResolver_(std::move(proxyIpResolver))
182 , buffer_(std::move(buffer))
183 , dosGuard_(dosGuard)
184 , handler_(std::move(handler))
187 LOG(perfLog_.debug()) <<
tag() <<
"http session created";
188 dosGuard_.get().increment(ip);
193 LOG(perfLog_.debug()) <<
tag() <<
"http session closed";
195 dosGuard_.get().decrement(clientIp_);
208 boost::beast::get_lowest_layer(derived().stream()).expires_after(std::chrono::seconds(30));
214 boost::beast::bind_front_handler(&HttpBase::onRead, derived().shared_from_this())
219 onRead(boost::beast::error_code ec, [[maybe_unused]] std::size_t bytesTransferred)
221 if (ec == http::error::end_of_stream)
222 return derived().doClose();
225 return httpFail(ec,
"read");
227 auto const updateClientIp = [&](std::string newIp) {
228 if (newIp == clientIp_)
230 LOG(log_.info()) <<
tag()
231 <<
"Detected a forwarded request from proxy. Resolved client ip: "
233 dosGuard_.get().decrement(clientIp_);
234 clientIp_ = std::move(newIp);
235 dosGuard_.get().increment(clientIp_);
238 if (isProxyConnection_) {
240 updateClientIp(std::move(*resolvedIp));
242 auto resolvedIp = proxyIpResolver_->resolveClientIp(clientIp_, req_);
243 resolvedIp.has_value()
245 updateClientIp(std::move(*resolvedIp));
246 isProxyConnection_ =
true;
249 if (req_.method() == http::verb::get and req_.target() ==
"/health")
250 return sender_(httpResponse(http::status::ok,
"text/html", kHealthCheckHtml));
252 if (req_.method() == http::verb::get and req_.target() ==
"/cache_state") {
253 if (cache_.get().isFull()) {
254 return sender_(httpResponse(http::status::ok,
"text/html", kCacheCheckLoadedHtml));
257 return sender_(httpResponse(
258 http::status::service_unavailable,
"text/html", kCacheCheckNotLoadedHtml
263 ConnectionBase::isAdmin_ = adminVerification_->isAdmin(req_, clientIp_);
265 if (boost::beast::websocket::is_upgrade(req_)) {
266 if (dosGuard_.get().isOk(clientIp_)) {
268 boost::beast::get_lowest_layer(derived().stream()).expires_never();
271 return derived().upgrade();
275 httpResponse(http::status::too_many_requests,
"text/html",
"Too many requests")
279 if (
auto response = util::prometheus::handlePrometheusRequest(req_,
isAdmin());
280 response.has_value())
281 return sender_(std::move(response.value()));
283 if (req_.method() != http::verb::post) {
285 httpResponse(http::status::bad_request,
"text/html",
"Expected a POST request")
289 LOG(log_.info()) <<
tag() <<
"Received request from ip = " << clientIp_;
292 (*handler_)(req_.body(), derived().shared_from_this());
293 }
catch (std::exception
const&) {
294 return sender_(httpResponse(
295 http::status::internal_server_error,
297 boost::json::serialize(
rpc::makeError(rpc::RippledError::RpcInternal))
305 sender_(httpResponse(
306 http::status::service_unavailable,
308 boost::json::serialize(
rpc::makeError(rpc::RippledError::RpcSlowDown))
320 send(std::string&& msg, http::status status = http::status::ok)
override
322 if (!dosGuard_.get().add(clientIp_, msg.size())) {
325 msg = boost::json::serialize(*warned);
328 sender_(httpResponse(status,
"application/json", std::move(msg)));
334 ASSERT(
false,
"SubscriptionContext can't be created for a HTTP connection");
339 onWrite(
bool close, boost::beast::error_code ec, std::size_t bytesTransferred)
341 boost::ignore_unused(bytesTransferred);
344 return httpFail(ec,
"write");
349 return derived().doClose();
356 [[nodiscard]] http::response<http::string_body>
357 httpResponse(http::status status, std::string contentType, std::string message)
const
359 http::response<http::string_body> res{status, req_.version()};
360 res.set(http::field::server,
"clio-server-" + util::build::getClioVersionString());
361 res.set(http::field::content_type, contentType);
362 res.keep_alive(req_.keep_alive());
363 res.body() = std::move(message);
364 res.prepare_payload();