summaryrefslogtreecommitdiffhomepage
path: root/http.cpp
diff options
context:
space:
mode:
authorRoland Reichwein <mail@reichwein.it>2020-04-04 16:32:10 +0200
committerRoland Reichwein <mail@reichwein.it>2020-04-04 16:32:10 +0200
commit938fbe7a2f2f10a3abb530a9463e57fc20f40038 (patch)
tree62ee0c285c672b10a42b0690a011ede7a0bf00b6 /http.cpp
parent95d5acc8c7e60255b19e7084e374eb26cc5d0ba3 (diff)
HTTP and HTTPs
Diffstat (limited to 'http.cpp')
-rw-r--r--http.cpp141
1 files changed, 33 insertions, 108 deletions
diff --git a/http.cpp b/http.cpp
index 406b384..82e1a39 100644
--- a/http.cpp
+++ b/http.cpp
@@ -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