diff options
author | Roland Reichwein <mail@reichwein.it> | 2020-04-04 16:32:10 +0200 |
---|---|---|
committer | Roland Reichwein <mail@reichwein.it> | 2020-04-04 16:32:10 +0200 |
commit | 938fbe7a2f2f10a3abb530a9463e57fc20f40038 (patch) | |
tree | 62ee0c285c672b10a42b0690a011ede7a0bf00b6 /http.cpp | |
parent | 95d5acc8c7e60255b19e7084e374eb26cc5d0ba3 (diff) |
HTTP and HTTPs
Diffstat (limited to 'http.cpp')
-rw-r--r-- | http.cpp | 141 |
1 files changed, 33 insertions, 108 deletions
@@ -1,13 +1,11 @@ -#include <boost/beast/version.hpp> - -#if BOOST_VERSION == 107100 +#include "http.h" -#include "server_certificate.h" +#include "server.h" +#include <boost/beast/version.hpp> #include <boost/beast/core.hpp> #include <boost/beast/http.hpp> #include <boost/beast/version.hpp> -#include <boost/beast/ssl.hpp> #include <boost/asio/dispatch.hpp> #include <boost/asio/strand.hpp> #include <boost/config.hpp> @@ -23,9 +21,10 @@ 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> using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp> +namespace { + // Return a reasonable mime type based on the extension of a file. beast::string_view mime_type(beast::string_view path) @@ -107,7 +106,7 @@ handle_request( [&req](beast::string_view why) { http::response<http::string_body> res{http::status::bad_request, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::server, VersionString); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = std::string(why); @@ -120,7 +119,7 @@ handle_request( [&req](beast::string_view target) { http::response<http::string_body> res{http::status::not_found, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::server, VersionString); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "The resource '" + std::string(target) + "' was not found."; @@ -133,7 +132,7 @@ handle_request( [&req](beast::string_view what) { http::response<http::string_body> res{http::status::internal_server_error, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::server, VersionString); res.set(http::field::content_type, "text/html"); res.keep_alive(req.keep_alive()); res.body() = "An error occurred: '" + std::string(what) + "'"; @@ -177,7 +176,7 @@ handle_request( if(req.method() == http::verb::head) { http::response<http::empty_body> res{http::status::ok, req.version()}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::server, VersionString); res.set(http::field::content_type, mime_type(path)); res.content_length(size); res.keep_alive(req.keep_alive()); @@ -189,7 +188,7 @@ handle_request( std::piecewise_construct, std::make_tuple(std::move(body)), std::make_tuple(http::status::ok, req.version())}; - res.set(http::field::server, BOOST_BEAST_VERSION_STRING); + res.set(http::field::server, VersionString); res.set(http::field::content_type, mime_type(path)); res.content_length(size); res.keep_alive(req.keep_alive()); @@ -202,26 +201,6 @@ handle_request( void fail(beast::error_code ec, char const* what) { - // ssl::error::stream_truncated, also known as an SSL "short read", - // indicates the peer closed the connection without performing the - // required closing handshake (for example, Google does this to - // improve performance). Generally this can be a security issue, - // but if your communication protocol is self-terminated (as - // it is with both HTTP and WebSocket) then you may simply - // ignore the lack of close_notify. - // - // https://github.com/boostorg/beast/issues/38 - // - // https://security.stackexchange.com/questions/91435/how-to-handle-a-malicious-ssl-tls-shutdown - // - // When a short read would cut off the end of an HTTP message, - // Beast returns the error beast::http::error::partial_message. - // Therefore, if we see a short read here, it has occurred - // after the message has been completed, so it is safe to ignore it. - - if(ec == net::ssl::error::stream_truncated) - return; - std::cerr << what << ": " << ec.message() << "\n"; } @@ -265,7 +244,7 @@ class session : public std::enable_shared_from_this<session> } }; - beast::ssl_stream<beast::tcp_stream> stream_; + beast::tcp_stream stream_; beast::flat_buffer buffer_; std::shared_ptr<std::string const> doc_root_; http::request<http::string_body> req_; @@ -273,13 +252,11 @@ class session : public std::enable_shared_from_this<session> send_lambda lambda_; public: - // Take ownership of the socket - explicit + // Take ownership of the stream session( tcp::socket&& socket, - ssl::context& ctx, std::shared_ptr<std::string const> const& doc_root) - : stream_(std::move(socket), ctx) + : stream_(std::move(socket)) , doc_root_(doc_root) , lambda_(*this) { @@ -293,35 +270,10 @@ public: // on the I/O objects in this session. Although not strictly necessary // for single-threaded contexts, this example code is written to be // thread-safe by default. - 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, "handshake"); - - do_read(); + net::dispatch(stream_.get_executor(), + beast::bind_front_handler( + &session::do_read, + shared_from_this())); } void @@ -332,7 +284,7 @@ public: req_ = {}; // Set the timeout. - beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); + stream_.expires_after(std::chrono::seconds(30)); // Read a request http::async_read(stream_, buffer_, req_, @@ -387,21 +339,9 @@ public: 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, "shutdown"); + // Send a TCP shutdown + beast::error_code ec; + stream_.socket().shutdown(tcp::socket::shutdown_send, ec); // At this point the connection is closed gracefully } @@ -413,19 +353,16 @@ public: class listener : public std::enable_shared_from_this<listener> { net::io_context& ioc_; - ssl::context& ctx_; tcp::acceptor acceptor_; std::shared_ptr<std::string const> doc_root_; public: listener( net::io_context& ioc, - ssl::context& ctx, tcp::endpoint endpoint, std::shared_ptr<std::string const> const& doc_root) : ioc_(ioc) - , ctx_(ctx) - , acceptor_(ioc) + , acceptor_(net::make_strand(ioc)) , doc_root_(doc_root) { beast::error_code ec; @@ -495,7 +432,6 @@ private: // Create the session and run it std::make_shared<session>( std::move(socket), - ctx_, doc_root_)->run(); } @@ -504,37 +440,26 @@ private: } }; +} // anonymous namespace + //------------------------------------------------------------------------------ -int http_server(int argc, char* argv[]) +namespace HTTP { + +int server(Config& config) { - // Check command line arguments. - if (argc != 5) - { - std::cerr << - "Usage: http-server-async-ssl <address> <port> <doc_root> <threads>\n" << - "Example:\n" << - " http-server-async-ssl 0.0.0.0 8080 . 1\n"; - return EXIT_FAILURE; - } - auto const address = net::ip::make_address(argv[1]); - auto const port = static_cast<unsigned short>(std::atoi(argv[2])); - auto const doc_root = std::make_shared<std::string>(argv[3]); - auto const threads = std::max<int>(1, std::atoi(argv[4])); + // TODO: Config + auto const address = net::ip::make_address(config.Sockets()[0].address); + auto const port = static_cast<unsigned short>(std::atoi(config.Sockets()[0].port.data())); + auto const doc_root = std::make_shared<std::string>(config.Sites()[0].paths[0].params.at("target")); + auto const threads = std::max<int>(1, config.Threads()); // The io_context is required for all I/O net::io_context ioc{threads}; - // The SSL context is required, and holds certificates - ssl::context ctx{ssl::context::tlsv12}; - - // This holds the self-signed certificate used by the server - load_server_certificate(ctx); - // Create and launch a listening port std::make_shared<listener>( ioc, - ctx, tcp::endpoint{address, port}, doc_root)->run(); @@ -552,4 +477,4 @@ int http_server(int argc, char* argv[]) return EXIT_SUCCESS; } -#endif +} // namespace HTTP |