Advertisement
Guest User

Untitled

a guest
Mar 23rd, 2019
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.53 KB | None | 0 0
  1. //
  2. // Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
  3. //
  4. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  5. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  6. //
  7. // Official repository: https://github.com/boostorg/beast
  8. //
  9.  
  10. //------------------------------------------------------------------------------
  11. //
  12. // Example: HTTP server, asynchronous
  13. //
  14. //------------------------------------------------------------------------------
  15.  
  16. #include <boost/beast/core.hpp>
  17. #include <boost/beast/http.hpp>
  18. #include <boost/beast/version.hpp>
  19. #include <boost/asio/bind_executor.hpp>
  20. #include <boost/asio/ip/tcp.hpp>
  21. #include <boost/asio/strand.hpp>
  22. #include <boost/config.hpp>
  23. #include <algorithm>
  24. #include <cstdlib>
  25. #include <functional>
  26. #include <iostream>
  27. #include <memory>
  28. #include <string>
  29. #include <thread>
  30. #include <vector>
  31. #include <boost/filesystem.hpp>
  32.  
  33. namespace fs = boost::filesystem;
  34. using tcp = boost::asio::ip::tcp;       // from <boost/asio/ip/tcp.hpp>
  35. namespace http = boost::beast::http;    // from <boost/beast/http.hpp>
  36.  
  37. // Return a reasonable mime type based on the extension of a file.
  38. boost::beast::string_view
  39. mime_type(boost::beast::string_view path)
  40. {
  41.     using boost::beast::iequals;
  42.     auto const ext = [&path]
  43.     {
  44.         auto const pos = path.rfind(".");
  45.         if (pos == boost::beast::string_view::npos)
  46.             return boost::beast::string_view{};
  47.         return path.substr(pos);
  48.     }();
  49.     if (iequals(ext, ".htm"))  return "text/html";
  50.     if (iequals(ext, ".html")) return "text/html";
  51.     if (iequals(ext, ".php"))  return "text/html";
  52.     if (iequals(ext, ".css"))  return "text/css";
  53.     if (iequals(ext, ".txt"))  return "text/plain";
  54.     if (iequals(ext, ".js"))   return "application/javascript";
  55.     if (iequals(ext, ".json")) return "application/json";
  56.     if (iequals(ext, ".xml"))  return "application/xml";
  57.     if (iequals(ext, ".swf"))  return "application/x-shockwave-flash";
  58.     if (iequals(ext, ".flv"))  return "video/x-flv";
  59.     if (iequals(ext, ".png"))  return "image/png";
  60.     if (iequals(ext, ".jpe"))  return "image/jpeg";
  61.     if (iequals(ext, ".jpeg")) return "image/jpeg";
  62.     if (iequals(ext, ".jpg"))  return "image/jpeg";
  63.     if (iequals(ext, ".gif"))  return "image/gif";
  64.     if (iequals(ext, ".bmp"))  return "image/bmp";
  65.     if (iequals(ext, ".ico"))  return "image/vnd.microsoft.icon";
  66.     if (iequals(ext, ".tiff")) return "image/tiff";
  67.     if (iequals(ext, ".tif"))  return "image/tiff";
  68.     if (iequals(ext, ".svg"))  return "image/svg+xml";
  69.     if (iequals(ext, ".svgz")) return "image/svg+xml";
  70.     return "application/text";
  71. }
  72.  
  73. // Append an HTTP rel-path to a local filesystem path.
  74. // The returned path is normalized for the platform.
  75. std::string
  76. path_cat(
  77.     boost::beast::string_view base,
  78.     boost::beast::string_view path)
  79. {
  80.     if (base.empty())
  81.         return path.to_string();
  82.     std::string result = base.to_string();
  83. #if BOOST_MSVC
  84.     char constexpr path_separator = '\\';
  85.     if (result.back() == path_separator)
  86.         result.resize(result.size() - 1);
  87.     result.append(path.data(), path.size());
  88.     for (auto& c : result)
  89.         if (c == '/')
  90.             c = path_separator;
  91. #else
  92.     char constexpr path_separator = '/';
  93.     if (result.back() == path_separator)
  94.         result.resize(result.size() - 1);
  95.     result.append(path.data(), path.size());
  96. #endif
  97.     return result;
  98. }
  99.  
  100. // This function produces an HTTP response for the given
  101. // request. The type of the response object depends on the
  102. // contents of the request, so the interface requires the
  103. // caller to pass a generic lambda for receiving the response.
  104. template<
  105.     class Body, class Allocator, class Send>
  106.     void handle_request(boost::beast::string_view doc_root, http::request<Body, http::basic_fields<Allocator>>&& req, Send&& send, std::string address)
  107. {
  108.     std::cout << address << ": " << req.target() << std::endl;
  109.  
  110.     /*if (req.target() == "/")
  111.     {
  112.         return send(directory());
  113.     }*/
  114.     /*auto const test =
  115.         [&req, &address]()
  116.     {
  117.         http::response<http::string_body> res{ http::status::ok, req.version() };
  118.         res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
  119.         res.set(http::field::content_type, "text/html");
  120.         res.keep_alive(req.keep_alive());
  121.         res.body() = "Hello " + address + "\r\n";
  122.         res.prepare_payload();
  123.         return res;
  124.     };
  125.     return send(test());
  126.     */
  127.  
  128.  
  129.     // Returns a bad request response
  130.     auto const bad_request =
  131.         [&req](boost::beast::string_view why)
  132.     {
  133.         http::response<http::string_body> res{ http::status::bad_request, req.version() };
  134.         res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
  135.         res.set(http::field::content_type, "text/html");
  136.         res.keep_alive(req.keep_alive());
  137.         res.body() = why.to_string();
  138.         res.prepare_payload();
  139.         return res;
  140.     };
  141.  
  142.     // Returns a not found response
  143.     auto const not_found =
  144.         [&req](boost::beast::string_view target)
  145.     {
  146.         http::response<http::string_body> res{ http::status::not_found, req.version() };
  147.         res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
  148.         res.set(http::field::content_type, "text/html");
  149.         res.keep_alive(req.keep_alive());
  150.         res.body() = "The resource '" + target.to_string() + "' was not found.";
  151.         res.prepare_payload();
  152.         return res;
  153.     };
  154.  
  155.     // Returns a server error response
  156.     auto const server_error =
  157.         [&req](boost::beast::string_view what)
  158.     {
  159.         http::response<http::string_body> res{ http::status::internal_server_error, req.version() };
  160.         res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
  161.         res.set(http::field::content_type, "text/html");
  162.         res.keep_alive(req.keep_alive());
  163.         res.body() = "An error occurred: '" + what.to_string() + "'";
  164.         res.prepare_payload();
  165.         return res;
  166.     };
  167.  
  168.     // Make sure we can handle the method
  169.     if (req.method() != http::verb::get &&
  170.         req.method() != http::verb::head)
  171.         return send(bad_request("Unknown HTTP-method"));
  172.  
  173.     // Request path must be absolute and not contain "..".
  174.     if (req.target().empty() ||
  175.         req.target()[0] != '/' ||
  176.         req.target().find("..") != boost::beast::string_view::npos)
  177.         return send(bad_request("Illegal request-target"));
  178.  
  179.     // Build the path to the requested file
  180.     std::string path = path_cat(doc_root, req.target());
  181.     if (req.target().back() == '/')
  182.         path.append("index.html");
  183.  
  184.     // Attempt to open the file
  185.     boost::beast::error_code ec;
  186.     http::file_body::value_type body;
  187.     body.open(path.c_str(), boost::beast::file_mode::scan, ec);
  188.  
  189.     // Handle the case where the file doesn't exist
  190.     if (ec == boost::system::errc::no_such_file_or_directory)
  191.         return send(not_found(req.target()));
  192.  
  193.     // Handle an unknown error
  194.     if (ec)
  195.         return send(server_error(ec.message()));
  196.  
  197.     // Cache the size since we need it after the move
  198.     auto const size = body.size();
  199.  
  200.     // Respond to HEAD request
  201.     if (req.method() == http::verb::head)
  202.     {
  203.         http::response<http::empty_body> res{ http::status::ok, req.version() };
  204.         res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
  205.         res.set(http::field::content_type, mime_type(path));
  206.         res.content_length(size);
  207.         res.keep_alive(req.keep_alive());
  208.         return send(std::move(res));
  209.     }
  210.  
  211.     // Respond to GET request
  212.     http::response<http::file_body> res{
  213.         std::piecewise_construct,
  214.         std::make_tuple(std::move(body)),
  215.         std::make_tuple(http::status::ok, req.version()) };
  216.     res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
  217.     res.set(http::field::content_type, mime_type(path));
  218.     res.content_length(size);
  219.     res.keep_alive(req.keep_alive());
  220.     return send(std::move(res));
  221. }
  222.  
  223. //------------------------------------------------------------------------------
  224.  
  225. // Report a failure
  226. void
  227. fail(boost::system::error_code ec, char const* what)
  228. {
  229.     std::cerr << what << ": " << ec.message() << "\n";
  230. }
  231.  
  232. // Handles an HTTP server connection
  233. class session : public std::enable_shared_from_this<session>
  234. {
  235.     // This is the C++11 equivalent of a generic lambda.
  236.     // The function object is used to send an HTTP message.
  237.     struct send_lambda
  238.     {
  239.         session& self_;
  240.  
  241.         explicit
  242.             send_lambda(session& self)
  243.             : self_(self)
  244.         {
  245.         }
  246.  
  247.         template<bool isRequest, class Body, class Fields>
  248.         void
  249.             operator()(http::message<isRequest, Body, Fields>&& msg) const
  250.         {
  251.             // The lifetime of the message has to extend
  252.             // for the duration of the async operation so
  253.             // we use a shared_ptr to manage it.
  254.             auto sp = std::make_shared<
  255.                 http::message<isRequest, Body, Fields>>(std::move(msg));
  256.  
  257.             // Store a type-erased version of the shared
  258.             // pointer in the class to keep it alive.
  259.             self_.res_ = sp;
  260.  
  261.             // Write the response
  262.             http::async_write(
  263.                 self_.socket_,
  264.                 *sp,
  265.                 boost::asio::bind_executor(
  266.                     self_.strand_,
  267.                     std::bind(
  268.                         &session::on_write,
  269.                         self_.shared_from_this(),
  270.                         std::placeholders::_1,
  271.                         std::placeholders::_2,
  272.                         sp->need_eof())));
  273.         }
  274.     };
  275.  
  276.     tcp::socket socket_;
  277.     boost::asio::strand<
  278.         boost::asio::io_context::executor_type> strand_;
  279.     boost::beast::flat_buffer buffer_;
  280.     std::shared_ptr<std::string const> doc_root_;
  281.     http::request<http::string_body> req_;
  282.     std::shared_ptr<void> res_;
  283.     send_lambda lambda_;
  284.  
  285. public:
  286.     // Take ownership of the socket
  287.     explicit
  288.         session(
  289.             tcp::socket socket,
  290.             std::shared_ptr<std::string const> const& doc_root)
  291.         : socket_(std::move(socket))
  292.         , strand_(socket_.get_executor())
  293.         , doc_root_(doc_root)
  294.         , lambda_(*this)
  295.     {
  296.     }
  297.  
  298.     // Start the asynchronous operation
  299.     void
  300.         run()
  301.     {
  302.         do_read();
  303.     }
  304.  
  305.     void
  306.         do_read()
  307.     {
  308.         // Make the request empty before reading,
  309.         // otherwise the operation behavior is undefined.
  310.         req_ = {};
  311.  
  312.         // Read a request
  313.         http::async_read(socket_, buffer_, req_,
  314.             boost::asio::bind_executor(
  315.                 strand_,
  316.                 std::bind(
  317.                     &session::on_read,
  318.                     shared_from_this(),
  319.                     std::placeholders::_1,
  320.                     std::placeholders::_2)));
  321.     }
  322.  
  323.     void
  324.         on_read(
  325.             boost::system::error_code ec,
  326.             std::size_t bytes_transferred)
  327.     {
  328.         boost::ignore_unused(bytes_transferred);
  329.  
  330.         // This means they closed the connection
  331.         if (ec == http::error::end_of_stream)
  332.             return do_close();
  333.  
  334.         if (ec)
  335.             return fail(ec, "read");
  336.  
  337.         // Send the response
  338.         handle_request(*doc_root_, std::move(req_), lambda_, socket_.remote_endpoint().address().to_string());
  339.     }
  340.  
  341.     void
  342.         on_write(
  343.             boost::system::error_code ec,
  344.             std::size_t bytes_transferred,
  345.             bool close)
  346.     {
  347.         boost::ignore_unused(bytes_transferred);
  348.  
  349.         if (ec)
  350.             return fail(ec, "write");
  351.  
  352.         if (close)
  353.         {
  354.             // This means we should close the connection, usually because
  355.             // the response indicated the "Connection: close" semantic.
  356.             return do_close();
  357.         }
  358.  
  359.         // We're done with the response so delete it
  360.         res_ = nullptr;
  361.  
  362.         // Read another request
  363.         do_read();
  364.     }
  365.  
  366.     void
  367.         do_close()
  368.     {
  369.         // Send a TCP shutdown
  370.         boost::system::error_code ec;
  371.         socket_.shutdown(tcp::socket::shutdown_send, ec);
  372.  
  373.         // At this point the connection is closed gracefully
  374.     }
  375. };
  376.  
  377. //------------------------------------------------------------------------------
  378.  
  379. // Accepts incoming connections and launches the sessions
  380. class listener : public std::enable_shared_from_this<listener>
  381. {
  382.     tcp::acceptor acceptor_;
  383.     tcp::socket socket_;
  384.     std::shared_ptr<std::string const> doc_root_;
  385.  
  386. public:
  387.     listener(
  388.         boost::asio::io_context& ioc,
  389.         tcp::endpoint endpoint,
  390.         std::shared_ptr<std::string const> const& doc_root)
  391.         : acceptor_(ioc)
  392.         , socket_(ioc)
  393.         , doc_root_(doc_root)
  394.     {
  395.         boost::system::error_code ec;
  396.  
  397.         // Open the acceptor
  398.         acceptor_.open(endpoint.protocol(), ec);
  399.         if (ec)
  400.         {
  401.             fail(ec, "open");
  402.             return;
  403.         }
  404.  
  405.         // Allow address reuse
  406.         acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec);
  407.         if (ec)
  408.         {
  409.             fail(ec, "set_option");
  410.             return;
  411.         }
  412.  
  413.         // Bind to the server address
  414.         acceptor_.bind(endpoint, ec);
  415.         if (ec)
  416.         {
  417.             fail(ec, "bind");
  418.             return;
  419.         }
  420.  
  421.         // Start listening for connections
  422.         acceptor_.listen(
  423.             boost::asio::socket_base::max_listen_connections, ec);
  424.         if (ec)
  425.         {
  426.             fail(ec, "listen");
  427.             return;
  428.         }
  429.     }
  430.  
  431.     // Start accepting incoming connections
  432.     void
  433.         run()
  434.     {
  435.         if (!acceptor_.is_open())
  436.             return;
  437.         do_accept();
  438.     }
  439.  
  440.     void
  441.         do_accept()
  442.     {
  443.         acceptor_.async_accept(
  444.             socket_,
  445.             std::bind(
  446.                 &listener::on_accept,
  447.                 shared_from_this(),
  448.                 std::placeholders::_1));
  449.     }
  450.  
  451.     void
  452.         on_accept(boost::system::error_code ec)
  453.     {
  454.         if (ec)
  455.         {
  456.             fail(ec, "accept");
  457.         }
  458.         else
  459.         {
  460.             // Create the session and run it
  461.             std::make_shared<session>(
  462.                 std::move(socket_),
  463.                 doc_root_)->run();
  464.         }
  465.  
  466.         // Accept another connection
  467.         do_accept();
  468.     }
  469. };
  470.  
  471. //------------------------------------------------------------------------------
  472.  
  473. int main(int argc, char* argv[])
  474. {
  475.     // Check command line arguments.
  476.     if (argc != 5)
  477.     {
  478.         std::cerr <<
  479.             "Usage: http-server-async <address> <port> <doc_root> <threads>\n" <<
  480.             "Example:\n" <<
  481.             "    http-server-async 0.0.0.0 8080 . 1\n";
  482.         return EXIT_FAILURE;
  483.     }
  484.     auto const address = boost::asio::ip::make_address(argv[1]);
  485.     auto const port = static_cast<unsigned short>(std::atoi(argv[2]));
  486.     auto const doc_root = std::make_shared<std::string>(argv[3]);
  487.     auto const threads = std::max<int>(1, std::atoi(argv[4]));
  488.  
  489.     // The io_context is required for all I/O
  490.     boost::asio::io_context ioc{ threads };
  491.  
  492.     // Create and launch a listening port
  493.     std::make_shared<listener>(
  494.         ioc,
  495.         tcp::endpoint{ address, port },
  496.         doc_root)->run();
  497.  
  498.     // Run the I/O service on the requested number of threads
  499.     std::vector<std::thread> v;
  500.     v.reserve(threads - 1);
  501.     for (auto i = threads - 1; i > 0; --i)
  502.         v.emplace_back(
  503.             [&ioc]
  504.     {
  505.         ioc.run();
  506.     });
  507.     ioc.run();
  508.  
  509.     return EXIT_SUCCESS;
  510. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement