#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { using Endpoints = std::unordered_map; /** * A multi-protocol server. * * This server maintains multiple configured listening ports, * with each listening port allows for multiple protocols including * HTTP, HTTP/S, WebSocket, Secure WebSocket, and the Peer protocol. */ class Server { public: /** * Destroy the server. * The server is closed if it is not already closed. This call * blocks until the server has stopped. */ virtual ~Server() = default; /** * Returns the Journal associated with the server. */ virtual beast::Journal journal() = 0; /** * Set the listening port settings. * This may only be called once. */ virtual Endpoints ports(std::vector const& v) = 0; /** * Close the server. * The close is performed asynchronously. The handler will be notified * when the server has stopped. The server is considered stopped when * there are no pending I/O completion handlers and all connections * have closed. * Thread safety: * Safe to call concurrently from any thread. */ virtual void close() = 0; }; template class ServerImpl : public Server { private: using clock_type = std::chrono::system_clock; static constexpr auto kHistorySize = 100; Handler& handler_; beast::Journal const j_; boost::asio::io_context& ioContext_; boost::asio::strand strand_; std::optional> work_; std::mutex m_; std::vector ports_; std::vector>> list_; int high_ = 0; std::array hist_{}; IOList ios_; public: ServerImpl(Handler& handler, boost::asio::io_context& ioContext, beast::Journal journal); ~ServerImpl() override; beast::Journal journal() override { return j_; } Endpoints ports(std::vector const& ports) override; void close() override; IOList& ios() { return ios_; } boost::asio::io_context& getIoContext() { return ioContext_; } bool closed(); private: static int ceilLog2(unsigned long long x); }; template ServerImpl::ServerImpl( Handler& handler, boost::asio::io_context& ioContext, beast::Journal journal) : handler_(handler) , j_(journal) , ioContext_(ioContext) , strand_(boost::asio::make_strand(ioContext_)) , work_(std::in_place, boost::asio::make_work_guard(ioContext_)) { } template ServerImpl::~ServerImpl() { // Handler::onStopped will not be called work_ = std::nullopt; ios_.close(); ios_.join(); } template Endpoints ServerImpl::ports(std::vector const& ports) { if (closed()) Throw("ports() on closed Server"); ports_.reserve(ports.size()); Endpoints eps; eps.reserve(ports.size()); for (auto const& port : ports) { ports_.push_back(port); auto& internalPort = ports_.back(); if (auto sp = ios_.emplace>(handler_, ioContext_, internalPort, j_)) { list_.push_back(sp); auto ep = sp->getEndpoint(); if (internalPort.port == 0u) internalPort.port = ep.port(); eps.emplace(port.name, std::move(ep)); sp->run(); } } return eps; } template void ServerImpl::close() { ios_.close([&] { work_ = std::nullopt; handler_.onStopped(*this); }); } template bool ServerImpl::closed() { return ios_.closed(); } } // namespace xrpl