diff options
author | Roland Reichwein <mail@reichwein.it> | 2023-01-12 15:30:07 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2023-01-12 15:30:07 +0100 |
commit | 00ed7df1a09cad8862f2c586347f4f55c99681e5 (patch) | |
tree | e24ef2699affc7630ea42e728e62df7c6686f714 /https.cpp | |
parent | 3cb78411178f8458f889975799060e0bb866d2cf (diff) |
Consolidate HTTP+HTTPS via CRTP
Diffstat (limited to 'https.cpp')
-rw-r--r-- | https.cpp | 818 |
1 files changed, 355 insertions, 463 deletions
@@ -6,8 +6,6 @@ #include "response.h" #include "websocket.h" -#include "libreichwein/file.h" - #include <openssl/ssl.h> #include <openssl/crypto.h> @@ -46,526 +44,420 @@ using namespace Reichwein; namespace { -//------------------------------------------------------------------------------ - // Handles an HTTP server connection -class session : public std::enable_shared_from_this<session> +template<class Derived> +class session { +private: + Derived& derived() + { + return static_cast<Derived&>(*this); + } + boost::asio::io_context& ioc_; - beast::ssl_stream<beast::tcp_stream> stream_; beast::flat_buffer buffer_; - Server& m_server; + Server& server_; std::optional<http::request_parser<http::string_body>> parser_; // need to reset parser every time, no other mechanism currently request_type req_; - std::shared_ptr<response_type> res_; // std::shared_ptr<void> + std::shared_ptr<response_type> res_; void handle_request() { - beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(300)); // timeout on write by server much longer than read timeout from client - auto sp = std::make_shared<response_type>(response::generate_response(req_, m_server)); + beast::get_lowest_layer(derived().stream()).expires_after(std::chrono::seconds(300)); // timeout on write by server much longer than read timeout from client + auto sp = std::make_shared<response_type>(response::generate_response(req_, server_)); res_ = sp; // Write the response http::async_write( - stream_, + derived().stream(), *sp, beast::bind_front_handler( &session::on_write, - shared_from_this(), + derived().shared_from_this(), sp->need_eof())); } void handle_websocket() { - beast::get_lowest_layer(stream_).expires_never(); - make_websocket_session(ioc_, std::move(stream_), response::get_websocket_address(req_, m_server), parser_->release()); + beast::get_lowest_layer(derived().stream()).expires_never(); + make_websocket_session(ioc_, std::move(derived().stream()), response::get_websocket_address(req_, server_), parser_->release()); } public: - // Take ownership of the socket - explicit - session( - boost::asio::io_context& ioc, - tcp::socket&& socket, - ssl::context& ctx, - Server& server): - ioc_(ioc), - stream_(std::move(socket), ctx), - m_server(server) - { - } - - // Start the asynchronous operation - void - run() - { - // We need to be executing within a strand to perform async operations - // on the I/O objects in this session. - net::dispatch( - stream_.get_executor(), - beast::bind_front_handler( - &session::on_run, - shared_from_this())); - } - - void - on_run() - { - // Set the timeout. - beast::get_lowest_layer(stream_).expires_after( - std::chrono::seconds(30)); - - // Perform the SSL handshake - stream_.async_handshake( - ssl::stream_base::server, - beast::bind_front_handler( - &session::on_handshake, - shared_from_this())); - } - - void - on_handshake( - beast::error_code ec - ) - { - if(ec) - return fail(ec, "https handshake"); - - do_read(); - } - - void - do_read() - { - // Make the request empty before reading, - // otherwise the operation behavior is undefined. - req_ = {}; - - // this is the way to reset the parser. it's necessary. - // https://github.com/boostorg/beast/issues/927 - parser_.emplace(); - parser_->body_limit(1000000000); // 1GB limit - - // Set the timeout. - beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); - - // Read a request - http::async_read(stream_, buffer_, *parser_, - beast::bind_front_handler( - &session::on_read, - shared_from_this())); - } - - 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 == http::error::partial_message) - return; // ignore - - if (ec) - return fail(ec, "https read"); - - req_ = parser_->get(); - - if (websocket::is_upgrade(req_)) - { - handle_websocket(); - return; - } - - // Send the response - handle_request(); - } - - void - on_write( - bool close, - beast::error_code ec, - std::size_t bytes_transferred - ) - { - boost::ignore_unused(bytes_transferred); - - if(ec) - return fail(ec, "https 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() - { - // Set the timeout. - beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); - - // Perform the SSL shutdown - stream_.async_shutdown( - beast::bind_front_handler( - &session::on_shutdown, - shared_from_this())); - } - - void - on_shutdown(beast::error_code ec) - { - if(ec) - return fail(ec, "https shutdown"); - - // At this point the connection is closed gracefully - } -}; + explicit + session( + boost::asio::io_context& ioc, + Server& server): + ioc_(ioc), + server_(server) + { + } -//------------------------------------------------------------------------------ + // Start the asynchronous operation + void + run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. + net::dispatch( + derived().stream().get_executor(), + beast::bind_front_handler( + &Derived::on_run, + derived().shared_from_this())); + } -// Accepts incoming connections and launches the sessions -class listener : public std::enable_shared_from_this<listener> -{ - net::io_context& ioc_; - ssl::context& ctx_; - tcp::acceptor acceptor_; - ::Server& m_server; + void + do_read() + { + // Make the request empty before reading, + // otherwise the operation behavior is undefined. + req_ = {}; + + // this is the way to reset the parser. it's necessary. + // https://github.com/boostorg/beast/issues/927 + parser_.emplace(); + parser_->body_limit(1000000000); // 1GB limit -public: - listener( - net::io_context& ioc, - ssl::context& ctx, - tcp::endpoint endpoint, - Server& server) : - ioc_(ioc), - ctx_(ctx), - acceptor_(ioc), - m_server(server) - { - beast::error_code ec; - - // Open the acceptor - acceptor_.open(endpoint.protocol(), ec); - if(ec) - { - fail(ec, "https open"); - return; - } - - // Allow address reuse - acceptor_.set_option(net::socket_base::reuse_address(true), ec); - if(ec) - { - fail(ec, "https set_option"); - return; - } - - // Bind to the server address - acceptor_.bind(endpoint, ec); - if(ec) - { - fail(ec, "https bind"); - return; - } - - // Start listening for connections - acceptor_.listen( - net::socket_base::max_listen_connections, ec); - if(ec) - { - fail(ec, "https listen"); - return; - } - } - - // Start accepting incoming connections - void - run() - { - do_accept(); - } + // Set the timeout. + beast::get_lowest_layer(derived().stream()).expires_after(std::chrono::seconds(30)); + + // Read a request + http::async_read(derived().stream(), buffer_, *parser_, + beast::bind_front_handler( + &session::on_read, + derived().shared_from_this())); + } + + 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 derived().do_close(); + + if (ec == http::error::partial_message) + return; // ignore + + if (ec) + return fail(ec, "http read"); + + req_ = parser_->get(); + + if (websocket::is_upgrade(req_)) + { + handle_websocket(); + return; + } + + // Send the response + handle_request(); + } + + void + on_write( + bool close, + beast::error_code ec, + std::size_t bytes_transferred + ) + { + boost::ignore_unused(bytes_transferred); + + if (ec) + return fail(ec, "http write"); + + if (close) + { + // This means we should close the connection, usually because + // the response indicated the "Connection: close" semantic. + return derived().do_close(); + } + + // We're done with the response so delete it + res_ = nullptr; + + // Read another request + do_read(); + } -private: - void - do_accept() - { - // The new connection gets its own strand - acceptor_.async_accept( - net::make_strand(ioc_), - beast::bind_front_handler( - &listener::on_accept, - shared_from_this())); - } - - void - on_accept(beast::error_code ec, tcp::socket socket) - { - if(ec) - { - fail(ec, "https accept"); - } - else - { - // Create the session and run it - std::make_shared<session>( - ioc_, - std::move(socket), - ctx_, - m_server)->run(); - } - - // Accept another connection - do_accept(); - } }; -/* Load a signed certificate into the ssl context, and configure - the context for use with a server. -*/ -void load_server_certificate(boost::asio::ssl::context& ctx, const fs::path& cert_path, const fs::path& key_path) +class plain_session: + public session<plain_session>, + public std::enable_shared_from_this<plain_session> { - /* - The certificate was generated from CMD.EXE on Windows 10 using: - - winpty openssl dhparam -out dh.pem 2048 - winpty openssl req -newkey rsa:4096 -sha256 -nodes -keyout key.pem -x509 -days 10000 -out cert.pem -subj "//C=DE\ST=BY\L=Munich\O=Reichwein\CN=reichwein.it" - */ - - std::string const dh = - "-----BEGIN DH PARAMETERS-----\n" - "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n" - "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n" - "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n" - "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n" - "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n" - "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n" - "-----END DH PARAMETERS-----\n"; - - ctx.set_options( - boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::single_dh_use); - - std::string cert; - if (cert_path.empty()) { - // use dummy self signed certificate. Will be replaced by real - // certificate if configured upon respective session - cert = - "-----BEGIN CERTIFICATE-----\n" - "MIIDnTCCAoWgAwIBAgIULkYtO+2Ddeg+qLZ+aDQpmA5b4L0wDQYJKoZIhvcNAQEL\n" - "BQAwXjELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0JhdmFyaWExDzANBgNVBAcMBk11\n" - "bmljaDEVMBMGA1UECgwMUmVpY2h3ZWluIElUMRUwEwYDVQQDDAxyZWljaHdlaW4u\n" - "aXQwHhcNMjAwNDA1MDgwNzIyWhcNNDcwODIyMDgwNzIyWjBeMQswCQYDVQQGEwJE\n" - "RTEQMA4GA1UECAwHQmF2YXJpYTEPMA0GA1UEBwwGTXVuaWNoMRUwEwYDVQQKDAxS\n" - "ZWljaHdlaW4gSVQxFTATBgNVBAMMDHJlaWNod2Vpbi5pdDCCASIwDQYJKoZIhvcN\n" - "AQEBBQADggEPADCCAQoCggEBALJNb0WLbz+xP+YITMMk+eeK/SIOCRFs/9aZIAyK\n" - "ParGauxa+8d25mlfJTAo6/G0h3sA240JHyNpOzVOogPU+v4dRWyGO0w5vHVD0caB\n" - "rDb1eEfmLtqfKLLUL9iPDReUh6WAE7qoNDtfoT551uSMIae1cpPUduVTnSkEgw8k\n" - "NjJSHYT800jSB2R+e7tJG3ErXDM63R3B8RbitZPoWACjpBxDT+Qrj0fBFS4AWw6b\n" - "z09uitv0RrgI6CW7xRh3UAdRwEBGHiU6HTIthX6LNgez1UL0sfu1iZ22wNmYZP/S\n" - "sL3b20WtSH9LN2PRJ4q3AGt6RMbmSGr65ljha9xkTFna0Y8CAwEAAaNTMFEwHQYD\n" - "VR0OBBYEFKd5/MGFZUAUV502vJ/Kcswax8WVMB8GA1UdIwQYMBaAFKd5/MGFZUAU\n" - "V502vJ/Kcswax8WVMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB\n" - "AIBS4AfM7wiunQ2UZQQ5A0Un99+BLax9e+h11h/jGeJ+/9maY/E9MK6UG9LXoOv2\n" - "z32Q7Ta2xKeRu6GC/qupwYJ0Xt3LENOfogsaNCAgxKlAN48LGlRyCTvzWsEMh28j\n" - "RaelWonh2qQoiryKLVnRwrg8g1Bu4v+V437cIBmeZPxf0spEL9EVqlN+iS8plmel\n" - "7/F4ULdybKGq39tgicuS7JhnY21ZzOFoq0bWnKBbAeTndmuROdb3pEppxW6pwu0q\n" - "TFdMrSJE38kiQh2O9IchPQbTZ+Rdj0HE9NxStlrNr5bu6rjikRm50/G3JoXpzYdp\n" - "AN4ZI2QZ6R6Y+TzDixKecNk=\n" - "-----END CERTIFICATE-----\n" - ; - } else { - cert = File::getFile(cert_path); - } - - ctx.use_certificate_chain( - boost::asio::buffer(cert.data(), cert.size())); - - std::string key; - if (key_path == "") { - // use dummy self signed key. Will be replaced by real - // certificate if configured upon respective session - key = - "-----BEGIN PRIVATE KEY-----\n" - "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyTW9Fi28/sT/m\n" - "CEzDJPnniv0iDgkRbP/WmSAMij2qxmrsWvvHduZpXyUwKOvxtId7ANuNCR8jaTs1\n" - "TqID1Pr+HUVshjtMObx1Q9HGgaw29XhH5i7anyiy1C/Yjw0XlIelgBO6qDQ7X6E+\n" - "edbkjCGntXKT1HblU50pBIMPJDYyUh2E/NNI0gdkfnu7SRtxK1wzOt0dwfEW4rWT\n" - "6FgAo6QcQ0/kK49HwRUuAFsOm89Pborb9Ea4COglu8UYd1AHUcBARh4lOh0yLYV+\n" - "izYHs9VC9LH7tYmdtsDZmGT/0rC929tFrUh/Szdj0SeKtwBrekTG5khq+uZY4Wvc\n" - "ZExZ2tGPAgMBAAECggEBAK9bJKIa3dCgPB257/TEOtsTgJyrfROcRYkCk9iBZOC9\n" - "v46wdIrZTwY2wtY4iMPwLoY0c7ijTfJ/nfFxYjmujyK4Gvz+jvcKmWQizP8TrRFo\n" - "HWFo6o+slFQ8BspO9itIspd7/OtIXgY+qNBO959Sig7sjsEA5eXoc9pRS6vqizq0\n" - "j4G/UO5Amr/l3ciEJiqMJgZsIVLDKaGlqFTymydSqkB8UHQYWK1kunQxhK4Ldycu\n" - "hTooQE7tXM0zvoFVV6v1fldV5OFsZk2kPMNtvMO6ZEpOM4rNMlg+vJy8kB1fb3Gs\n" - "iFE/DCUpZsMSserQMU9/hfrYlndgsFD5Sr1EVGEebhECgYEA1gc9qx+ugdhYTY5j\n" - "tJDXjOsnw8KY/l/1y+mQ8XNJ9MVdBGy1WB+uWB4teiyJchV49gn2XlKUK2rcCBvZ\n" - "vC5CwPmFi2t70JezQgnXtDlbR0bARPlRd741i4rBpD7hEiZNCTOd2HFBpUg/CGWN\n" - "E4n1ksazBm6jvv3Jo6WAa07Z390CgYEA1USrFqmc/fKGQpTCdH0qYZv3hQtrb1TQ\n" - "9YnrbhtaC0haPpintZKjvhU3tCd1fPuIDXtMAgaaKSyoGiE2aMvLxt1/eV08BkMi\n" - "kGIss9poYNi5+6ZD9QAHmHJhzZtVGj8U5L8379XmwxAByiBRVVE8CW1X/e6+iJpz\n" - "+CLgN+zEVlsCgYEAsuOAdtxXJm4meERwL8b0cvNF3Eh1Sf/42MPTAwzCntSrh3w5\n" - "InvwY/RtPHWnN/ScksEG7BWHhLafTCPDHJdp8hNcvIhNB68UBDln0loyYePP5pag\n" - "sj4IUSbb7SUlR989elhrMTKQlM5K6QDAJrmjyVdM4S5urL9A3wgAyzAvyP0CgYAO\n" - "paGuc8WxdzebWQYl4/bGL2UHgSpGwid7xZYiwMQlZDm2dNuHz+NpCaICwHcEN243\n" - "ptEojnWGAGgnK0LGXcDIDqxTlICr2W6FRgjV7Vkf1aKoUtn1+KOM58YpzdJBdDWm\n" - "JC/eS+2GVhIZZLDRUDv0VcsmSIBTd3AhiZumm588YwKBgBZfNqfmHAwIP2pM1wml\n" - "Ck3vaLLvonghj3iQW9CFJ/SqLOnfT4KJkFObR6oGbxY0RtXsCrmSqidIKgDd0Kkq\n" - "L6QbHp2j3+16GBdmLNUJlfjBTNPJp69IDKztjeCX7/8JZs79p/LAv+I9Sh4lVw4O\n" - "IrDprlB0yzP5zigcsAZeViYJ\n" - "-----END PRIVATE KEY-----\n" - ; - } else { - key = File::getFile(key_path); - } - ctx.use_private_key( - boost::asio::buffer(key.data(), key.size()), - boost::asio::ssl::context::file_format::pem); - - ctx.use_tmp_dh( - boost::asio::buffer(dh.data(), dh.size())); -} + beast::tcp_stream stream_; -int ServerNameError(SSL *s, HTTPS::Server::ctx_type& ctx_map) -{ - std::shared_ptr<ssl::context> ctx{ctx_map.at("")}; - SSL_set_SSL_CTX(s, ctx->native_handle()); - return SSL_CLIENT_HELLO_SUCCESS; // OK for now -} +public: + explicit plain_session( + boost::asio::io_context& ioc, + tcp::socket&& socket, + Server& server): + session(ioc, server), + stream_(std::move(socket)) + { + } + + void on_run() + { + // We need to be executing within a strand to perform async operations + // on the I/O objects in this session. Skip ssl handshake for plain http. + net::dispatch(stream_.get_executor(), + beast::bind_front_handler( + &session::do_read, + shared_from_this())); + } -std::string unbracketed(const std::string& s) -{ - if (s.size() >= 2 && s.front() == '[' && s.back() == ']') { - return s.substr(1, s.size() - 2); - } else { - return s; + void + do_close() + { + // Send a TCP shutdown + beast::error_code ec; + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); + // At this point the connection is closed gracefully } -} -int servername_callback(SSL *s, int *al, void *arg) -{ - HTTPS::Server::ctx_type& ctx_map = *(HTTPS::Server::ctx_type*)arg; - - if (0) { // not really necessary - int* numbers; - size_t numbers_size; - if (SSL_client_hello_get1_extensions_present(s, &numbers, &numbers_size) != 1) { - std::cout << "Error on SSL_client_hello_get1_extensions_present" << std::endl; - return ServerNameError(s, ctx_map); - } - bool server_name_available {false}; - for (size_t i = 0; i < numbers_size; i++) - if (numbers[i] == 0) - server_name_available = true; + beast::tcp_stream& stream() + { + return stream_; + } - OPENSSL_free(numbers); +}; // class - if (!server_name_available) { - std::cout << "Error: No server_name available at SSL_client_hello_get1_extensions_present" << std::endl; - return ServerNameError(s, ctx_map); - } +class ssl_session: + public session<ssl_session>, + public std::enable_shared_from_this<ssl_session> +{ + beast::ssl_stream<beast::tcp_stream> stream_; +public: + explicit ssl_session( + boost::asio::io_context& ioc, + tcp::socket&& socket, + ssl::context& ctx, + Server& server): + session(ioc, server), + stream_(std::move(socket), ctx) + { } + + void on_run() + { + // Set the timeout + beast::get_lowest_layer(stream_).expires_after( + std::chrono::seconds(30)); - const unsigned char* data; - size_t data_size; - // 0 is server_name - if (SSL_client_hello_get0_ext(s, 0, &data, &data_size) != 1) { - std::cout << "Warning: Error on SSL_client_hello_get0_ext: servername not available. Using dummy ctx." << std::endl; - return ServerNameError(s, ctx_map); + // Perform the SSL handshake + stream_.async_handshake( + ssl::stream_base::server, + beast::bind_front_handler( + &ssl_session::on_handshake, + shared_from_this())); } - // SNI Server Name, See https://tools.ietf.org/html/rfc6066 (TODO: why are there 5 bytes header?) - std::string server_name {std::string((const char*)data, (size_t)data_size)}; - if (server_name.size() >= 5 && server_name[0] == '\0') - server_name = server_name.substr(5); + void + on_handshake(beast::error_code ec) + { + if (ec) + return fail(ec, "https handshake"); - server_name = unbracketed(server_name); + do_read(); + } + + void + do_close() + { + // Set the timeout. + beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); - auto it {ctx_map.find(server_name)}; - std::shared_ptr<ssl::context> ctx{}; - if (it != ctx_map.end()) { - ctx = it->second; - } else { - std::cout << "Warning: server_name " << server_name << " (" << server_name.size() << ") not found in list of prepared contexts. Using dummy ctx." << std::endl; - return ServerNameError(s, ctx_map); + // Perform the SSL shutdown + stream_.async_shutdown( + beast::bind_front_handler( + &ssl_session::on_shutdown, + shared_from_this())); } - SSL_set_SSL_CTX(s, ctx->native_handle()); - - return SSL_CLIENT_HELLO_SUCCESS; -} + void + on_shutdown(beast::error_code ec) + { + if (ec) + return fail(ec, "https shutdown"); -} // anonymous namespace -//------------------------------------------------------------------------------ + // At this point the connection is closed gracefully + } -namespace HTTPS { + beast::ssl_stream<beast::tcp_stream>& stream() + { + return stream_; + } -Server::Server(Config& config, boost::asio::io_context& ioc, const Socket& socket, plugins_container_type& plugins, Statistics& statistics) - : ::Server(config, ioc, socket, plugins, statistics) -{ - load_certificates(); // load initially -} +}; // class -Server::~Server() +// Accepts incoming connections and launches the sessions +template<class Derived> +class listener { -} +private: + Derived& derived() + { + return static_cast<Derived&>(*this); + } + +protected: + net::io_context& ioc_; + tcp::acceptor acceptor_; + ::Server& server_; + +public: + explicit listener( + net::io_context& ioc, + tcp::endpoint endpoint, + Server& server): + ioc_(ioc), + acceptor_(ioc), + server_(server) + { + beast::error_code ec; + + // Open the acceptor + acceptor_.open(endpoint.protocol(), ec); + if (ec) + { + fail(ec, "http listener open"); + return; + } -void Server::load_certificates() + // Allow address reuse + acceptor_.set_option(net::socket_base::reuse_address(true), ec); + if (ec) + { + fail(ec, "http listener set_option"); + return; + } + + // Bind to the server address + acceptor_.bind(endpoint, ec); + if (ec) + { + fail(ec, "http listener bind"); + return; + } + + // Start listening for connections + acceptor_.listen(net::socket_base::max_listen_connections, ec); + if (ec) + { + fail(ec, "http listener listen"); + return; + } + } + + // Start accepting incoming connections + void + run() + { + do_accept(); + } + +protected: + void + do_accept() + { + // The new connection gets its own strand + acceptor_.async_accept( + net::make_strand(ioc_), + beast::bind_front_handler( + &Derived::on_accept, + derived().shared_from_this())); + } +}; // class + +class plain_listener: + public listener<plain_listener>, + public std::enable_shared_from_this<plain_listener> { - // initial dummy, before we can add specific ctx w/ certificate - std::shared_ptr<ssl::context> ctx_dummy{std::make_shared<ssl::context>(tls_method)}; - load_server_certificate(*ctx_dummy, "", ""); - SSL_CTX_set_client_hello_cb(ctx_dummy->native_handle(), servername_callback, &m_ctx); - m_ctx.emplace("", ctx_dummy); - - // import the real certificates - for (const auto& serve_site: m_socket.serve_sites) { - for (const auto& site: m_config.Sites()) { - if (site.first == serve_site) { - std::shared_ptr<ssl::context> ctx {std::make_shared<ssl::context>(tls_method)}; - - std::cout << "Creating SSL context/cert for site " << serve_site << " on port " << m_socket.port << std::endl; - - load_server_certificate(*ctx, site.second.cert_path, site.second.key_path); - SSL_CTX_set_client_hello_cb(ctx->native_handle(), servername_callback, &m_ctx); - - for (const auto& host: site.second.hosts) { - std::cout << " Adding Host " << host << std::endl; - m_ctx.emplace(unbracketed(host), ctx); - } - } +public: + explicit plain_listener( + net::io_context& ioc, + tcp::endpoint endpoint, + Server& server): + listener(ioc, endpoint, server) + { + } + + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if (ec) { + fail(ec, "plain listener accept"); + } else { + // Create the session and run it + std::make_shared<plain_session>( + ioc_, + std::move(socket), + server_)->run(); } + + // Accept another connection + do_accept(); } -} +}; // class -int Server::start() +class ssl_listener: + public listener<ssl_listener>, + public std::enable_shared_from_this<ssl_listener> { - auto const address = net::ip::make_address(m_socket.address); - auto const port = static_cast<unsigned short>(std::atoi(m_socket.port.data())); + ssl::context& ctx_; + +public: + explicit ssl_listener( + net::io_context& ioc, + ssl::context& ctx, + tcp::endpoint endpoint, + Server& server): + listener(ioc, endpoint, server), + ctx_(ctx) + { + } - // Create and launch a listening port - std::make_shared<listener>( - m_ioc, - *m_ctx[""], - tcp::endpoint{address, port}, - *this)->run(); + void + on_accept(beast::error_code ec, tcp::socket socket) + { + if (ec) { + fail(ec, "ssl listener accept"); + } else { + // Create the session and run it + std::make_shared<ssl_session>( + ioc_, + std::move(socket), + ctx_, + server_)->run(); + } + + // Accept another connection + do_accept(); + } +}; // class - return EXIT_SUCCESS; +} // namespace + +void make_listener(net::io_context& ioc, net::ip::address address, unsigned short port, Server& server) +{ + std::make_shared<plain_listener>( + ioc, + tcp::endpoint{address, port}, + server)->run(); } -} // namespace HTTPS +void make_listener(net::io_context& ioc, ssl::context& ctx, net::ip::address address, unsigned short port, Server& server) +{ + std::make_shared<ssl_listener>( + ioc, + ctx, + tcp::endpoint{address, port}, + server)->run(); +} |