Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[4.0->main] prometheus on different port #989

Merged
merged 11 commits into from
Apr 7, 2023
20 changes: 12 additions & 8 deletions plugins/http_plugin/http_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,18 @@ namespace eosio {
using std::regex;
using boost::asio::ip::tcp;
using std::shared_ptr;

static http_plugin_defaults current_http_plugin_defaults;
static bool verbose_http_errors = false;

void http_plugin::set_defaults(const http_plugin_defaults& config) {
current_http_plugin_defaults = config;
}

std::string http_plugin::get_server_header() {
return current_http_plugin_defaults.server_header;
}

using http_plugin_impl_ptr = std::shared_ptr<class http_plugin_impl>;

class http_plugin_impl : public std::enable_shared_from_this<http_plugin_impl> {
Expand All @@ -47,9 +51,9 @@ namespace eosio {

http_plugin_impl& operator=(const http_plugin_impl&) = delete;
http_plugin_impl& operator=(http_plugin_impl&&) = delete;

std::optional<tcp::endpoint> listen_endpoint;

std::optional<asio::local::stream_protocol::endpoint> unix_endpoint;

shared_ptr<beast_http_listener<plain_session, tcp, tcp_socket_t > > beast_server;
Expand Down Expand Up @@ -272,7 +276,7 @@ namespace eosio {

my->plugin_state->server_header = current_http_plugin_defaults.server_header;


//watch out for the returns above when adding new code here
} FC_LOG_AND_RETHROW()
}
Expand Down Expand Up @@ -309,7 +313,7 @@ namespace eosio {
if(my->unix_endpoint) {
try {
my->create_beast_server(true);

my->beast_unix_server->listen(*my->unix_endpoint);
my->beast_unix_server->start_accept();
} catch ( const fc::exception& e ){
Expand All @@ -335,7 +339,7 @@ namespace eosio {
}
}
}}, appbase::exec_queue::read_only);

} catch (...) {
fc_elog(logger(), "http_plugin startup fails, shutting down");
app().quit();
Expand Down Expand Up @@ -457,9 +461,9 @@ namespace eosio {
fc::microseconds http_plugin::get_max_response_time()const {
return my->plugin_state->max_response_time;
}

size_t http_plugin::get_max_body_size()const {
return my->plugin_state->max_body_size;
}

}
5 changes: 3 additions & 2 deletions plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ namespace eosio {

//must be called before initialize
static void set_defaults(const http_plugin_defaults& config);
static std::string get_server_header();

APPBASE_PLUGIN_REQUIRES()
void set_program_options(options_description&, options_description& cfg) override;
Expand Down Expand Up @@ -122,7 +123,7 @@ namespace eosio {
void register_metrics_listener(chain::plugin_interface::metrics_listener listener);

size_t get_max_body_size()const;

private:
std::shared_ptr<class http_plugin_impl> my;
};
Expand Down Expand Up @@ -176,7 +177,7 @@ namespace eosio {
};

/**
* @brief Used to trim whitespace from body.
* @brief Used to trim whitespace from body.
* Returned string_view valid only for lifetime of body
*/
inline std::string_view make_trimmed_string_view(const std::string& body) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#pragma once

#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>

namespace eosio { namespace rest {

// The majority of the code here are derived from boost source
// libs/beast/example/http/server/async/http_server_async.cpp
// with minimum modification and yet reusable.

namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
template <typename T>
class simple_server {
T* self() { return static_cast<T*>(this); }

void fail(beast::error_code ec, char const* what) { self()->log_error(what, ec.message()); }
// Return a response for the given request.
http::response<http::string_body> handle_request(http::request<http::string_body>&& req) {
auto server_header = self()->server_header();
// Returns a bad request response
auto const bad_request = [&req, &server_header](std::string_view why) {
http::response<http::string_body> res{ http::status::bad_request, req.version() };
res.set(http::field::server, server_header);
res.set(http::field::content_type, "text/plain");
res.keep_alive(req.keep_alive());
res.body() = std::string(why);
res.prepare_payload();
return res;
};

// Returns a not found response
auto const not_found = [&req, &server_header](std::string_view target) {
http::response<http::string_body> res{ http::status::not_found, req.version() };
res.set(http::field::server, server_header);
res.set(http::field::content_type, "text/plain");
res.keep_alive(req.keep_alive());
res.body() = "The resource '" + std::string(target) + "' was not found.";
res.prepare_payload();
return res;
};

// Returns a server error response
auto const server_error = [&req, &server_header](std::string_view what) {
http::response<http::string_body> res{ http::status::internal_server_error, req.version() };
res.set(http::field::server, server_header);
res.set(http::field::content_type, "text/plain");
res.keep_alive(req.keep_alive());
res.body() = "An error occurred: '" + std::string(what) + "'";
res.prepare_payload();
return res;
};

// Make sure we can handle the method
if (!self()->allow_method(req.method()))
return bad_request("Unknown HTTP-method");

// Request path must be absolute and not contain "..".
std::string_view target{req.target().data(), req.target().size()};
if (target.empty() || target[0] != '/' || target.find("..") != std::string_view::npos)
return bad_request("Illegal request-target");

try {
auto res = self()->on_request(std::move(req));
if (!res)
not_found(target);
return *res;
} catch (std::exception& ex) { return server_error(ex.what()); }
}

class session : public std::enable_shared_from_this<session> {
tcp::socket socket_;
boost::asio::io_context::strand strand_;
beast::flat_buffer buffer_;
http::request<http::string_body> req_;
simple_server* server_;
std::shared_ptr<http::response<http::string_body>> res_;

public:
// Take ownership of the stream
session(net::io_context& ioc, tcp::socket&& socket, simple_server* server)
: socket_(std::move(socket)), strand_(ioc), server_(server) {}

// Start the asynchronous operation
void run() { do_read(); }

void do_read() {
// Make the request empty before reading,
// otherwise the operation behavior is undefined.
req_ = {};

// Read a request
http::async_read(
socket_, buffer_, req_,
boost::asio::bind_executor(strand_, [self = this->shared_from_this()](beast::error_code ec,
std::size_t bytes_transferred) {
self->on_read(ec, bytes_transferred);
}));
}

void on_read(beast::error_code ec, std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);

// This means they closed the connection
if (ec == http::error::end_of_stream)
return do_close();

if (ec)
return server_->fail(ec, "read");

// Send the response
send_response(server_->handle_request(std::move(req_)));
}

void send_response(http::response<http::string_body>&& msg) {
// The lifetime of the message has to extend
// for the duration of the async operation so
// we use a shared_ptr to manage it.
res_ = std::make_shared<http::response<http::string_body>>(std::move(msg));

// Write the response
http::async_write(socket_, *res_,
boost::asio::bind_executor(socket_.get_executor(),
[self = this->shared_from_this(), close = res_->need_eof()](
beast::error_code ec, std::size_t bytes_transferred) {
self->on_write(ec, bytes_transferred, close);
}));
}

void on_write(boost::system::error_code ec, std::size_t bytes_transferred, bool close) {
boost::ignore_unused(bytes_transferred);

if (ec)
return server_->fail(ec, "write");

if (close) {
// This means we should close the connection, usually because
// the response indicated the "Connection: close" semantic.
return do_close();
}

// We're done with the response so delete it
res_ = nullptr;

// Read another request
do_read();
}

void do_close() {
// Send a TCP shutdown
beast::error_code ec;
socket_.shutdown(tcp::socket::shutdown_send, ec);

// At this point the connection is closed gracefully
}
};

//------------------------------------------------------------------------------

// Accepts incoming connections and launches the sessions
class listener : public std::enable_shared_from_this<listener> {
net::io_context& ioc_;
tcp::acceptor acceptor_;
tcp::socket socket_;
simple_server* server_;

public:
listener(net::io_context& ioc, tcp::endpoint endpoint, simple_server* server)
: ioc_(ioc), acceptor_(ioc), socket_(ioc), server_(server) {
boost::system::error_code ec;

// Open the acceptor
acceptor_.open(endpoint.protocol(), ec);
if (ec) {
server_->fail(ec, "open");
return;
}

// Allow address reuse
acceptor_.set_option(net::socket_base::reuse_address(true), ec);
if (ec) {
server_->fail(ec, "set_option");
return;
}

// Bind to the server address
acceptor_.bind(endpoint, ec);
if (ec) {
server_->fail(ec, "bind");
return;
}

// Start listening for connections
acceptor_.listen(net::socket_base::max_listen_connections, ec);
if (ec) {
server_->fail(ec, "listen");
return;
}
}

// Start accepting incoming connections
void run() {
if (!acceptor_.is_open())
return;
do_accept();
}

private:
void do_accept() {
acceptor_.async_accept(
socket_, [self = this->shared_from_this()](boost::system::error_code ec) { self->on_accept(ec); });
}

void on_accept(boost::system::error_code ec) {
if (ec) {
server_->fail(ec, "accept");
} else {
// Create the session and run it
std::make_shared<session>(ioc_, std::move(socket_), server_)->run();
}

// Accept another connection
do_accept();
}
};

public:
void run(net::io_context& ioc, tcp::endpoint endpoint) { std::make_shared<listener>(ioc, endpoint, this)->run(); }
};
}} // namespace eosio::rest
Loading