diff options
author | Roland Reichwein <mail@reichwein.it> | 2023-01-12 15:50:15 +0100 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2023-01-12 15:50:15 +0100 |
commit | 2bb0d2ab46bf8104ab6e0b96fdefbeb20aa4c9e4 (patch) | |
tree | 7bd0e7104d687206422d2d882592cd70b35ea80b /https.cpp | |
parent | 7472e4e8e4897adc90f9e9030bb35ea07ded8b32 (diff) |
Move https.* to http.*
Diffstat (limited to 'https.cpp')
-rw-r--r-- | https.cpp | 463 |
1 files changed, 0 insertions, 463 deletions
diff --git a/https.cpp b/https.cpp deleted file mode 100644 index 20fab61..0000000 --- a/https.cpp +++ /dev/null @@ -1,463 +0,0 @@ -#include "https.h" - -#include "config.h" -#include "error.h" -#include "server.h" -#include "response.h" -#include "websocket.h" - -#include <openssl/ssl.h> -#include <openssl/crypto.h> - -#include <boost/asio/buffer.hpp> -#include <boost/beast/core.hpp> -#include <boost/beast/http.hpp> -#include <boost/beast/websocket.hpp> -#include <boost/beast/websocket/ssl.hpp> -#include <boost/asio/buffers_iterator.hpp> -#include <boost/asio/dispatch.hpp> -#include <boost/asio/ssl/context.hpp> -#include <boost/beast/ssl.hpp> -#include <boost/asio/strand.hpp> -#include <boost/config.hpp> - -#include <algorithm> -#include <cstddef> -#include <cstdlib> -#include <filesystem> -#include <functional> -#include <iostream> -#include <memory> -#include <optional> -#include <string> -#include <thread> -#include <vector> - -namespace fs = std::filesystem; -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> -namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp> -namespace websocket = beast::websocket; -using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> -using namespace Reichwein; - -namespace { - -// Handles an HTTP server connection -template<class Derived> -class session -{ -private: - Derived& derived() - { - return static_cast<Derived&>(*this); - } - - boost::asio::io_context& ioc_; - beast::flat_buffer buffer_; - 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_; - - void handle_request() - { - 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( - derived().stream(), - *sp, - beast::bind_front_handler( - &session::on_write, - derived().shared_from_this(), - sp->need_eof())); - } - - void handle_websocket() - { - 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: - 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())); - } - - 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(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(); - } - -}; - -class plain_session: - public session<plain_session>, - public std::enable_shared_from_this<plain_session> -{ - beast::tcp_stream stream_; - -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())); - } - - 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 - } - - beast::tcp_stream& stream() - { - return stream_; - } - -}; // class - -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)); - - // Perform the SSL handshake - stream_.async_handshake( - ssl::stream_base::server, - beast::bind_front_handler( - &ssl_session::on_handshake, - shared_from_this())); - } - - void - on_handshake(beast::error_code ec) - { - if (ec) - return fail(ec, "https handshake"); - - 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( - &ssl_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 - } - - beast::ssl_stream<beast::tcp_stream>& stream() - { - return stream_; - } - -}; // class - -// 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; - } - - // 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> -{ -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 - -class ssl_listener: - public listener<ssl_listener>, - public std::enable_shared_from_this<ssl_listener> -{ - 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) - { - } - - 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 - -} // 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(); -} - -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(); -} - |