| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
										
											2019-02-21 07:00:31 -08:00
										 |  |  | // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | //
 | 
					
						
							|  |  |  | // Distributed under the Boost Software License, Version 1.0. (See accompanying
 | 
					
						
							|  |  |  | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Official repository: https://github.com/boostorg/beast
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //------------------------------------------------------------------------------
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // Example: HTTP SSL server, asynchronous
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //------------------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "example/common/server_certificate.hpp"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <boost/beast/core.hpp>
 | 
					
						
							|  |  |  | #include <boost/beast/http.hpp>
 | 
					
						
							| 
									
										
										
										
											2019-02-23 10:46:43 -08:00
										 |  |  | #include <boost/beast/ssl.hpp>
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | #include <boost/beast/version.hpp>
 | 
					
						
							|  |  |  | #include <boost/asio/strand.hpp>
 | 
					
						
							|  |  |  | #include <boost/config.hpp>
 | 
					
						
							|  |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <cstdlib>
 | 
					
						
							|  |  |  | #include <functional>
 | 
					
						
							|  |  |  | #include <iostream>
 | 
					
						
							|  |  |  | #include <memory>
 | 
					
						
							|  |  |  | #include <string>
 | 
					
						
							|  |  |  | #include <thread>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  | 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>
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | namespace ssl = boost::asio::ssl;       // from <boost/asio/ssl.hpp>
 | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  | using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Return a reasonable mime type based on the extension of a file.
 | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  | beast::string_view | 
					
						
							|  |  |  | mime_type(beast::string_view path) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     using beast::iequals; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     auto const ext = [&path] | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         auto const pos = path.rfind("."); | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |         if(pos == beast::string_view::npos) | 
					
						
							|  |  |  |             return beast::string_view{}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         return path.substr(pos); | 
					
						
							|  |  |  |     }(); | 
					
						
							|  |  |  |     if(iequals(ext, ".htm"))  return "text/html"; | 
					
						
							|  |  |  |     if(iequals(ext, ".html")) return "text/html"; | 
					
						
							|  |  |  |     if(iequals(ext, ".php"))  return "text/html"; | 
					
						
							|  |  |  |     if(iequals(ext, ".css"))  return "text/css"; | 
					
						
							|  |  |  |     if(iequals(ext, ".txt"))  return "text/plain"; | 
					
						
							|  |  |  |     if(iequals(ext, ".js"))   return "application/javascript"; | 
					
						
							|  |  |  |     if(iequals(ext, ".json")) return "application/json"; | 
					
						
							|  |  |  |     if(iequals(ext, ".xml"))  return "application/xml"; | 
					
						
							|  |  |  |     if(iequals(ext, ".swf"))  return "application/x-shockwave-flash"; | 
					
						
							|  |  |  |     if(iequals(ext, ".flv"))  return "video/x-flv"; | 
					
						
							|  |  |  |     if(iequals(ext, ".png"))  return "image/png"; | 
					
						
							|  |  |  |     if(iequals(ext, ".jpe"))  return "image/jpeg"; | 
					
						
							|  |  |  |     if(iequals(ext, ".jpeg")) return "image/jpeg"; | 
					
						
							|  |  |  |     if(iequals(ext, ".jpg"))  return "image/jpeg"; | 
					
						
							|  |  |  |     if(iequals(ext, ".gif"))  return "image/gif"; | 
					
						
							|  |  |  |     if(iequals(ext, ".bmp"))  return "image/bmp"; | 
					
						
							|  |  |  |     if(iequals(ext, ".ico"))  return "image/vnd.microsoft.icon"; | 
					
						
							|  |  |  |     if(iequals(ext, ".tiff")) return "image/tiff"; | 
					
						
							|  |  |  |     if(iequals(ext, ".tif"))  return "image/tiff"; | 
					
						
							|  |  |  |     if(iequals(ext, ".svg"))  return "image/svg+xml"; | 
					
						
							|  |  |  |     if(iequals(ext, ".svgz")) return "image/svg+xml"; | 
					
						
							|  |  |  |     return "application/text"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Append an HTTP rel-path to a local filesystem path.
 | 
					
						
							|  |  |  | // The returned path is normalized for the platform.
 | 
					
						
							|  |  |  | std::string | 
					
						
							|  |  |  | path_cat( | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     beast::string_view base, | 
					
						
							|  |  |  |     beast::string_view path) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | { | 
					
						
							|  |  |  |     if(base.empty()) | 
					
						
							| 
									
										
										
										
											2019-02-07 23:47:05 +01:00
										 |  |  |         return std::string(path); | 
					
						
							|  |  |  |     std::string result(base); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | #if BOOST_MSVC
 | 
					
						
							|  |  |  |     char constexpr path_separator = '\\'; | 
					
						
							|  |  |  |     if(result.back() == path_separator) | 
					
						
							|  |  |  |         result.resize(result.size() - 1); | 
					
						
							|  |  |  |     result.append(path.data(), path.size()); | 
					
						
							|  |  |  |     for(auto& c : result) | 
					
						
							|  |  |  |         if(c == '/') | 
					
						
							|  |  |  |             c = path_separator; | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  |     char constexpr path_separator = '/'; | 
					
						
							|  |  |  |     if(result.back() == path_separator) | 
					
						
							|  |  |  |         result.resize(result.size() - 1); | 
					
						
							|  |  |  |     result.append(path.data(), path.size()); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This function produces an HTTP response for the given
 | 
					
						
							|  |  |  | // request. The type of the response object depends on the
 | 
					
						
							|  |  |  | // contents of the request, so the interface requires the
 | 
					
						
							|  |  |  | // caller to pass a generic lambda for receiving the response.
 | 
					
						
							|  |  |  | template< | 
					
						
							|  |  |  |     class Body, class Allocator, | 
					
						
							|  |  |  |     class Send> | 
					
						
							|  |  |  | void | 
					
						
							|  |  |  | handle_request( | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     beast::string_view doc_root, | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     http::request<Body, http::basic_fields<Allocator>>&& req, | 
					
						
							|  |  |  |     Send&& send) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // Returns a bad request response
 | 
					
						
							|  |  |  |     auto const bad_request = | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     [&req](beast::string_view why) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-09-12 13:49:45 -07:00
										 |  |  |         http::response<http::string_body> res{http::status::bad_request, req.version()}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.set(http::field::server, BOOST_BEAST_VERSION_STRING); | 
					
						
							|  |  |  |         res.set(http::field::content_type, "text/html"); | 
					
						
							|  |  |  |         res.keep_alive(req.keep_alive()); | 
					
						
							| 
									
										
										
										
											2019-02-07 23:47:05 +01:00
										 |  |  |         res.body() = std::string(why); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.prepare_payload(); | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Returns a not found response
 | 
					
						
							|  |  |  |     auto const not_found = | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     [&req](beast::string_view target) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-09-12 13:49:45 -07:00
										 |  |  |         http::response<http::string_body> res{http::status::not_found, req.version()}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.set(http::field::server, BOOST_BEAST_VERSION_STRING); | 
					
						
							|  |  |  |         res.set(http::field::content_type, "text/html"); | 
					
						
							|  |  |  |         res.keep_alive(req.keep_alive()); | 
					
						
							| 
									
										
										
										
											2019-02-07 23:47:05 +01:00
										 |  |  |         res.body() = "The resource '" + std::string(target) + "' was not found."; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.prepare_payload(); | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Returns a server error response
 | 
					
						
							|  |  |  |     auto const server_error = | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     [&req](beast::string_view what) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-09-12 13:49:45 -07:00
										 |  |  |         http::response<http::string_body> res{http::status::internal_server_error, req.version()}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.set(http::field::server, BOOST_BEAST_VERSION_STRING); | 
					
						
							|  |  |  |         res.set(http::field::content_type, "text/html"); | 
					
						
							|  |  |  |         res.keep_alive(req.keep_alive()); | 
					
						
							| 
									
										
										
										
											2019-02-07 23:47:05 +01:00
										 |  |  |         res.body() = "An error occurred: '" + std::string(what) + "'"; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.prepare_payload(); | 
					
						
							|  |  |  |         return res; | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Make sure we can handle the method
 | 
					
						
							|  |  |  |     if( req.method() != http::verb::get && | 
					
						
							|  |  |  |         req.method() != http::verb::head) | 
					
						
							|  |  |  |         return send(bad_request("Unknown HTTP-method")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Request path must be absolute and not contain "..".
 | 
					
						
							|  |  |  |     if( req.target().empty() || | 
					
						
							|  |  |  |         req.target()[0] != '/' || | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |         req.target().find("..") != beast::string_view::npos) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         return send(bad_request("Illegal request-target")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Build the path to the requested file
 | 
					
						
							|  |  |  |     std::string path = path_cat(doc_root, req.target()); | 
					
						
							|  |  |  |     if(req.target().back() == '/') | 
					
						
							|  |  |  |         path.append("index.html"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Attempt to open the file
 | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     beast::error_code ec; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     http::file_body::value_type body; | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     body.open(path.c_str(), beast::file_mode::scan, ec); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // Handle the case where the file doesn't exist
 | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     if(ec == beast::errc::no_such_file_or_directory) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         return send(not_found(req.target())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle an unknown error
 | 
					
						
							|  |  |  |     if(ec) | 
					
						
							|  |  |  |         return send(server_error(ec.message())); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-22 11:57:00 -08:00
										 |  |  |     // Cache the size since we need it after the move
 | 
					
						
							|  |  |  |     auto const size = body.size(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     // Respond to HEAD request
 | 
					
						
							|  |  |  |     if(req.method() == http::verb::head) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-09-12 13:49:45 -07:00
										 |  |  |         http::response<http::empty_body> res{http::status::ok, req.version()}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.set(http::field::server, BOOST_BEAST_VERSION_STRING); | 
					
						
							|  |  |  |         res.set(http::field::content_type, mime_type(path)); | 
					
						
							| 
									
										
										
										
											2018-01-22 11:57:00 -08:00
										 |  |  |         res.content_length(size); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         res.keep_alive(req.keep_alive()); | 
					
						
							|  |  |  |         return send(std::move(res)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-08 17:38:48 -07:00
										 |  |  |     // Respond to GET request
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     http::response<http::file_body> res{ | 
					
						
							|  |  |  |         std::piecewise_construct, | 
					
						
							|  |  |  |         std::make_tuple(std::move(body)), | 
					
						
							| 
									
										
										
										
											2017-09-12 13:49:45 -07:00
										 |  |  |         std::make_tuple(http::status::ok, req.version())}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     res.set(http::field::server, BOOST_BEAST_VERSION_STRING); | 
					
						
							|  |  |  |     res.set(http::field::content_type, mime_type(path)); | 
					
						
							| 
									
										
										
										
											2018-01-22 11:57:00 -08:00
										 |  |  |     res.content_length(size); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     res.keep_alive(req.keep_alive()); | 
					
						
							|  |  |  |     return send(std::move(res)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //------------------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Report a failure
 | 
					
						
							|  |  |  | void | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  | fail(beast::error_code ec, char const* what) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-23 06:34:52 -08:00
										 |  |  |     // 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; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     std::cerr << what << ": " << ec.message() << "\n"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Handles an HTTP server connection
 | 
					
						
							|  |  |  | class session : public std::enable_shared_from_this<session> | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // This is the C++11 equivalent of a generic lambda.
 | 
					
						
							|  |  |  |     // The function object is used to send an HTTP message.
 | 
					
						
							|  |  |  |     struct send_lambda | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         session& self_; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         explicit | 
					
						
							|  |  |  |         send_lambda(session& self) | 
					
						
							|  |  |  |             : self_(self) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         template<bool isRequest, class Body, class Fields> | 
					
						
							|  |  |  |         void | 
					
						
							|  |  |  |         operator()(http::message<isRequest, Body, Fields>&& msg) const | 
					
						
							|  |  |  |         { | 
					
						
							| 
									
										
										
										
											2017-08-18 07:53:41 -07:00
										 |  |  |             // The lifetime of the message has to extend
 | 
					
						
							|  |  |  |             // for the duration of the async operation so
 | 
					
						
							|  |  |  |             // we use a shared_ptr to manage it.
 | 
					
						
							|  |  |  |             auto sp = std::make_shared< | 
					
						
							|  |  |  |                 http::message<isRequest, Body, Fields>>(std::move(msg)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Store a type-erased version of the shared
 | 
					
						
							|  |  |  |             // pointer in the class to keep it alive.
 | 
					
						
							|  |  |  |             self_.res_ = sp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Write the response
 | 
					
						
							|  |  |  |             http::async_write( | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |                 self_.stream_, | 
					
						
							| 
									
										
										
										
											2017-08-18 07:53:41 -07:00
										 |  |  |                 *sp, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |                 beast::bind_front_handler( | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |                     &session::on_write, | 
					
						
							|  |  |  |                     self_.shared_from_this(), | 
					
						
							|  |  |  |                     sp->need_eof())); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-18 12:42:24 -08:00
										 |  |  |     beast::ssl_stream<beast::tcp_stream> stream_; | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     beast::flat_buffer buffer_; | 
					
						
							| 
									
										
										
										
											2018-05-03 14:18:33 -07:00
										 |  |  |     std::shared_ptr<std::string const> doc_root_; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     http::request<http::string_body> req_; | 
					
						
							| 
									
										
										
										
											2017-08-18 07:53:41 -07:00
										 |  |  |     std::shared_ptr<void> res_; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     send_lambda lambda_; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     // Take ownership of the socket
 | 
					
						
							|  |  |  |     explicit | 
					
						
							|  |  |  |     session( | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |         tcp::socket&& socket, | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         ssl::context& ctx, | 
					
						
							| 
									
										
										
										
											2018-05-03 14:18:33 -07:00
										 |  |  |         std::shared_ptr<std::string const> const& doc_root) | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |         : stream_(std::move(socket), ctx) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         , doc_root_(doc_root) | 
					
						
							|  |  |  |         , lambda_(*this) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Start the asynchronous operation
 | 
					
						
							|  |  |  |     void | 
					
						
							|  |  |  |     run() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |         // Set the timeout.
 | 
					
						
							|  |  |  |         beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         // Perform the SSL handshake
 | 
					
						
							|  |  |  |         stream_.async_handshake( | 
					
						
							|  |  |  |             ssl::stream_base::server, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |             beast::bind_front_handler( | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |                 &session::on_handshake, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |                 shared_from_this())); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     on_handshake(beast::error_code ec) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         if(ec) | 
					
						
							|  |  |  |             return fail(ec, "handshake"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         do_read(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void | 
					
						
							|  |  |  |     do_read() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2018-02-26 20:25:27 -08:00
										 |  |  |         // Make the request empty before reading,
 | 
					
						
							|  |  |  |         // otherwise the operation behavior is undefined.
 | 
					
						
							|  |  |  |         req_ = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |         // Set the timeout.
 | 
					
						
							|  |  |  |         beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         // Read a request
 | 
					
						
							|  |  |  |         http::async_read(stream_, buffer_, req_, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |             beast::bind_front_handler( | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |                 &session::on_read, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |                 shared_from_this())); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void | 
					
						
							| 
									
										
										
										
											2017-09-03 18:06:09 -07:00
										 |  |  |     on_read( | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |         beast::error_code ec, | 
					
						
							| 
									
										
										
										
											2017-09-03 18:06:09 -07:00
										 |  |  |         std::size_t bytes_transferred) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-09-03 18:06:09 -07:00
										 |  |  |         boost::ignore_unused(bytes_transferred); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         // This means they closed the connection
 | 
					
						
							|  |  |  |         if(ec == http::error::end_of_stream) | 
					
						
							|  |  |  |             return do_close(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if(ec) | 
					
						
							|  |  |  |             return fail(ec, "read"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Send the response
 | 
					
						
							| 
									
										
										
										
											2018-05-03 14:18:33 -07:00
										 |  |  |         handle_request(*doc_root_, std::move(req_), lambda_); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void | 
					
						
							| 
									
										
										
										
											2017-09-03 18:06:09 -07:00
										 |  |  |     on_write( | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |         bool close, | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |         beast::error_code ec, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |         std::size_t bytes_transferred) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2017-09-03 18:06:09 -07:00
										 |  |  |         boost::ignore_unused(bytes_transferred); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-20 10:19:58 -07:00
										 |  |  |         if(ec) | 
					
						
							|  |  |  |             return fail(ec, "write"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if(close) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         { | 
					
						
							|  |  |  |             // This means we should close the connection, usually because
 | 
					
						
							|  |  |  |             // the response indicated the "Connection: close" semantic.
 | 
					
						
							|  |  |  |             return do_close(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-18 07:53:41 -07:00
										 |  |  |         // We're done with the response so delete it
 | 
					
						
							|  |  |  |         res_ = nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         // Read another request
 | 
					
						
							|  |  |  |         do_read(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void | 
					
						
							|  |  |  |     do_close() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |         // Set the timeout.
 | 
					
						
							|  |  |  |         beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         // Perform the SSL shutdown
 | 
					
						
							|  |  |  |         stream_.async_shutdown( | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |             beast::bind_front_handler( | 
					
						
							| 
									
										
										
										
											2019-02-09 20:26:48 -08:00
										 |  |  |                 &session::on_shutdown, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |                 shared_from_this())); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     on_shutdown(beast::error_code ec) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         if(ec) | 
					
						
							|  |  |  |             return fail(ec, "shutdown"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // At this point the connection is closed gracefully
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //------------------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Accepts incoming connections and launches the sessions
 | 
					
						
							|  |  |  | class listener : public std::enable_shared_from_this<listener> | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-02-18 12:42:24 -08:00
										 |  |  |     net::io_context& ioc_; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     ssl::context& ctx_; | 
					
						
							|  |  |  |     tcp::acceptor acceptor_; | 
					
						
							| 
									
										
										
										
											2018-05-03 14:18:33 -07:00
										 |  |  |     std::shared_ptr<std::string const> doc_root_; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							|  |  |  |     listener( | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |         net::io_context& ioc, | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         ssl::context& ctx, | 
					
						
							|  |  |  |         tcp::endpoint endpoint, | 
					
						
							| 
									
										
										
										
											2018-05-03 14:18:33 -07:00
										 |  |  |         std::shared_ptr<std::string const> const& doc_root) | 
					
						
							| 
									
										
										
										
											2019-02-18 12:42:24 -08:00
										 |  |  |         : ioc_(ioc) | 
					
						
							|  |  |  |         , ctx_(ctx) | 
					
						
							| 
									
										
										
										
											2017-09-07 07:39:52 -07:00
										 |  |  |         , acceptor_(ioc) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         , doc_root_(doc_root) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |         beast::error_code ec; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Open the acceptor
 | 
					
						
							|  |  |  |         acceptor_.open(endpoint.protocol(), ec); | 
					
						
							|  |  |  |         if(ec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             fail(ec, "open"); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-21 12:35:07 -08:00
										 |  |  |         // Allow address reuse
 | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |         acceptor_.set_option(net::socket_base::reuse_address(true), ec); | 
					
						
							| 
									
										
										
										
											2018-02-21 12:35:07 -08:00
										 |  |  |         if(ec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             fail(ec, "set_option"); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         // Bind to the server address
 | 
					
						
							|  |  |  |         acceptor_.bind(endpoint, ec); | 
					
						
							|  |  |  |         if(ec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             fail(ec, "bind"); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Start listening for connections
 | 
					
						
							|  |  |  |         acceptor_.listen( | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |             net::socket_base::max_listen_connections, ec); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         if(ec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             fail(ec, "listen"); | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Start accepting incoming connections
 | 
					
						
							|  |  |  |     void | 
					
						
							|  |  |  |     run() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         do_accept(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-06 05:40:49 -08:00
										 |  |  | private: | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     void | 
					
						
							|  |  |  |     do_accept() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-02-20 07:07:01 -08:00
										 |  |  |         // The new connection gets its own strand
 | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         acceptor_.async_accept( | 
					
						
							| 
									
										
										
										
											2019-03-02 05:22:02 -08:00
										 |  |  |             net::make_strand(ioc_), | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |             beast::bind_front_handler( | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |                 &listener::on_accept, | 
					
						
							| 
									
										
										
										
											2019-02-14 16:20:27 -08:00
										 |  |  |                 shared_from_this())); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void | 
					
						
							| 
									
										
										
										
											2019-02-20 07:07:01 -08:00
										 |  |  |     on_accept(beast::error_code ec, tcp::socket socket) | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         if(ec) | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             fail(ec, "accept"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         else | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             // Create the session and run it
 | 
					
						
							|  |  |  |             std::make_shared<session>( | 
					
						
							| 
									
										
										
										
											2019-02-20 07:07:01 -08:00
										 |  |  |                 std::move(socket), | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |                 ctx_, | 
					
						
							|  |  |  |                 doc_root_)->run(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Accept another connection
 | 
					
						
							|  |  |  |         do_accept(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //------------------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int main(int argc, char* argv[]) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     // 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; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     auto const address = net::ip::make_address(argv[1]); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |     auto const port = static_cast<unsigned short>(std::atoi(argv[2])); | 
					
						
							| 
									
										
										
										
											2018-05-03 14:18:33 -07:00
										 |  |  |     auto const doc_root = std::make_shared<std::string>(argv[3]); | 
					
						
							| 
									
										
										
										
											2017-09-07 07:39:52 -07:00
										 |  |  |     auto const threads = std::max<int>(1, std::atoi(argv[4])); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-07 07:39:52 -07:00
										 |  |  |     // The io_context is required for all I/O
 | 
					
						
							| 
									
										
										
										
											2018-11-30 14:58:38 -08:00
										 |  |  |     net::io_context ioc{threads}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // The SSL context is required, and holds certificates
 | 
					
						
							| 
									
										
										
										
											2019-03-14 17:40:41 +01:00
										 |  |  |     ssl::context ctx{ssl::context::tlsv12}; | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // This holds the self-signed certificate used by the server
 | 
					
						
							|  |  |  |     load_server_certificate(ctx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Create and launch a listening port
 | 
					
						
							|  |  |  |     std::make_shared<listener>( | 
					
						
							| 
									
										
										
										
											2017-09-07 07:39:52 -07:00
										 |  |  |         ioc, | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         ctx, | 
					
						
							|  |  |  |         tcp::endpoint{address, port}, | 
					
						
							|  |  |  |         doc_root)->run(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Run the I/O service on the requested number of threads
 | 
					
						
							|  |  |  |     std::vector<std::thread> v; | 
					
						
							|  |  |  |     v.reserve(threads - 1); | 
					
						
							|  |  |  |     for(auto i = threads - 1; i > 0; --i) | 
					
						
							|  |  |  |         v.emplace_back( | 
					
						
							| 
									
										
										
										
											2017-09-07 07:39:52 -07:00
										 |  |  |         [&ioc] | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         { | 
					
						
							| 
									
										
										
										
											2017-09-07 07:39:52 -07:00
										 |  |  |             ioc.run(); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-09-07 07:39:52 -07:00
										 |  |  |     ioc.run(); | 
					
						
							| 
									
										
										
										
											2017-08-04 19:55:28 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return EXIT_SUCCESS; | 
					
						
							|  |  |  | } |