Skip to content

willsaibott/LibHTTP

Repository files navigation

Build Status:

Master:

Windows Build Linux_Build

Dev:

Windows Build Linux_Build

LibHTTP

LibHTTP is a C++ library similar to NodeJS Express in usage, and is based on boost beast. [In development]

Main Classes:

  • http_server< Router, Listener >:

    Base class that represents a HTTP Server that is associted with a boost::asio::io_context

    To modify how the routes are handled, it's just necessary to specialize the Router template argument

    To modify how the connection should be open, binded and accepted, it's just necessary to specialize the Listener template argument

    Declaration:

      template <class Router   = http_regex_router,
                class Listener = http_listener<http_session<http_request_handler<Router>>>>
      class  http_server
  • https_server< Router >:

    This class is a specialization of the base class http_server that has a http_listener with a https_session (inherits http_section) associated with.

    To modify how the routes are handled, it's just necessary to specialize the Router template argument

    Declaration:

      template <class Router>
      using https_server =
          http_server<Router, http_listener<https_session<http_request_handler<Router>>>>;
  • websocket_server< MessageHandler >:

    This class is a specialization of the base class http_server that has a http_listener with a websocket_session associated with.

    To modify how the routes are handled, it's just necessary to specialize the MessageHandler template argument.

    Note: Currently, the behaviour of the websocket_server is reactive, it calls the MessageHandler when receives a message. It'll be modifiable in the future.

    Declaration:

      template <class MessageHandler>
      using websocket_server =
          http_server<MessageHandler, http_listener<websocket_section<MessageHandler>, http_error_handler>>;
  • websocket_ssl_server< MessageHandler >:

    This class is a specialization of the base class http_server that has a http_listener with a websocket_ssl_session (inherits websocket_section) associated with.

    To modify how the routes are handled, it's just necessary to specialize the MessageHandler template argument

    Note: Currently, the behaviour of the websocket_ssl_server is reactive, it calls the MessageHandler when receives a message. It'll be modifiable in the future.

    Declaration:

      template <class MessageHandler>
      using websocket_ssl_server =
          http_server<MessageHandler, http_listener<websocket_ssl_section<MessageHandler>, http_error_handler>>;

HTTP Router Usage Example:

router.h:

#include <LibHttp\http_server.h>

class http_custom_router : public http::http_regex_router {
public:
  http_custom_router();
};

router.cpp:

#include "router.h"

http_custom_router::
http_custom_router() {

  get("/", [](const auto& root,
              const auto& request,
              const auto& matches,
              auto&       sender)
  {
    namespace http = boost::beast::http;
    http::response<http::string_body> res{ http::status::ok, request.version() };
    res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
    res.set(http::field::content_type, helper::mime_type(".json"));
    res.keep_alive(request.keep_alive());
    res.body() = "{ \"Root\": \"reached\" }";
    res.prepare_payload();
    sender.async_send(std::move(res));

    boost::ignore_unused(root);
    boost::ignore_unused(matches);
    return true;
  });

  get("/print(.*)/?", [](const auto& root,
                         const auto& request,
                         const auto& matches,
                         auto&       sender)
  {
    namespace http = boost::beast::http;
    http::response<http::string_body> res{http::status::ok, request.version()};
    res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
    res.set(http::field::content_type, "text/html; charset=utf-8");
    res.keep_alive(request.keep_alive());
    res.body() = "<!DOCTYPE html><html>"
                 "<head>"
                   "<title>Express C++</title>"
                   "<link rel=\"stylesheet\" href=\"/stylesheets/style.css\">"
                 "</head>"
                 "<body>"
                   "<h1>Express</h1>"
                   "<p>Welcome to Express C++ using Boost Beast (Asio)</p>"
                   "<p>Parameter: <b>" + matches[1] + "</b></p>"
                 "</body>"
                 "</html>";
    res.prepare_payload();
    sender.async_send(std::move(res));

    boost::ignore_unused(root);
    return true;
  });
  
  post("/post(.*)", [](const auto& root,
                       const auto& request,
                       const auto& matches,
                       auto&       sender)
  {
    namespace http = boost::beast::http;
    http::response<http::string_body> res{http::status::ok, request.version()};
    res.set(http::field::server, BOOST_BEAST_VERSION_STRING);
    res.set(http::field::content_type, helper::mime_type(".json"));
    res.keep_alive(request.keep_alive());
    res.body() = std::string{ "{ \"print\": \"" } +
                 std::string{ matches[static_cast<int>(matches.size() - 1ull)] } +
                 "\"}";
    res.prepare_payload();
    sender.async_send(std::move(res));

    boost::ignore_unused(root);
    boost::ignore_unused(matches);
    return true;
  });
}

http_server.cpp:

#include <thread>
#include <string>
#include <filesystem>
#include <LibHttp/http_server.h>
#include "router.h"

int main() {
  const std::string interface_address{ "127.0.0.1" };
  http::http_server<http_custom_router>
        http_server { interface_address, 18080, std::filesystem::current_path() };
        
  std::thread thread {
    [&http_server]() {
      http_server.start(std::thread::hardware_concurrency());
      if (http_server.last_error()) {
        std::cerr << "Error starting HTTP server: "
                  << http_server.last_error().message()
                  << std::endl;
      }
    }
  };
  std::cout << "HTTP server running at http://" << interface_address << ":18080..." <<  std::endl;
  std::this_thread::sleep_for(std::chrono::minutes{ 1000 });
  http_server.stop();
  thread.join();
  return 0;
}

HTTPS Router Usage Example:

https_server.cpp:

#include <thread>
#include <string>
#include <filesystem>
#include <LibHttp/http_server.h>
#include "router.h"

int main() {
  const std::string interface_address{ "127.0.0.1" };
  std::string const cert =
          "-----BEGIN CERTIFICATE-----\n"
          "MIIDaDCCAlCgAwIBAgIJAO8vBu8i8exWMA0GCSqGSIb3DQEBCwUAMEkxCzAJBgNV\n"
          "BAYTAlVTMQswCQYDVQQIDAJDQTEtMCsGA1UEBwwkTG9zIEFuZ2VsZXNPPUJlYXN0\n"
          "Q049d3d3LmV4YW1wbGUuY29tMB4XDTE3MDUwMzE4MzkxMloXDTQ0MDkxODE4Mzkx\n"
          "MlowSTELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMS0wKwYDVQQHDCRMb3MgQW5n\n"
          "ZWxlc089QmVhc3RDTj13d3cuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUA\n"
          "A4IBDwAwggEKAoIBAQDJ7BRKFO8fqmsEXw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcF\n"
          "xqGitbnLIrOgiJpRAPLy5MNcAXE1strVGfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7b\n"
          "Fu8TsCzO6XrxpnVtWk506YZ7ToTa5UjHfBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO\n"
          "9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wWKIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBp\n"
          "yY8anC8u4LPbmgW0/U31PH0rRVfGcBbZsAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrv\n"
          "enu2tOK9Qx6GEzXh3sekZkxcgh+NlIxCNxu//Dk9AgMBAAGjUzBRMB0GA1UdDgQW\n"
          "BBTZh0N9Ne1OD7GBGJYz4PNESHuXezAfBgNVHSMEGDAWgBTZh0N9Ne1OD7GBGJYz\n"
          "4PNESHuXezAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmTJVT\n"
          "LH5Cru1vXtzb3N9dyolcVH82xFVwPewArchgq+CEkajOU9bnzCqvhM4CryBb4cUs\n"
          "gqXWp85hAh55uBOqXb2yyESEleMCJEiVTwm/m26FdONvEGptsiCmF5Gxi0YRtn8N\n"
          "V+KhrQaAyLrLdPYI7TrwAOisq2I1cD0mt+xgwuv/654Rl3IhOMx+fKWKJ9qLAiaE\n"
          "fQyshjlPP9mYVxWOxqctUdQ8UnsUKKGEUcVrA08i1OAnVKlPFjKBvk+r7jpsTPcr\n"
          "9pWXTO9JrYMML7d+XRSZA1n3856OqZDX4403+9FnXCvfcLZLLKTBvwwFgEFGpzjK\n"
          "UEVbkhd5qstF6qWK\n"
          "-----END CERTIFICATE-----\n";

  std::string const key =
          "-----BEGIN PRIVATE KEY-----\n"
          "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDJ7BRKFO8fqmsE\n"
          "Xw8v9YOVXyrQVsVbjSSGEs4Vzs4cJgcFxqGitbnLIrOgiJpRAPLy5MNcAXE1strV\n"
          "GfdEf7xMYSZ/4wOrxUyVw/Ltgsft8m7bFu8TsCzO6XrxpnVtWk506YZ7ToTa5UjH\n"
          "fBi2+pWTxbpN12UhiZNUcrRsqTFW+6fO9d7xm5wlaZG8cMdg0cO1bhkz45JSl3wW\n"
          "KIES7t3EfKePZbNlQ5hPy7Pd5JTmdGBpyY8anC8u4LPbmgW0/U31PH0rRVfGcBbZ\n"
          "sAoQw5Tc5dnb6N2GEIbq3ehSfdDHGnrvenu2tOK9Qx6GEzXh3sekZkxcgh+NlIxC\n"
          "Nxu//Dk9AgMBAAECggEBAK1gV8uETg4SdfE67f9v/5uyK0DYQH1ro4C7hNiUycTB\n"
          "oiYDd6YOA4m4MiQVJuuGtRR5+IR3eI1zFRMFSJs4UqYChNwqQGys7CVsKpplQOW+\n"
          "1BCqkH2HN/Ix5662Dv3mHJemLCKUON77IJKoq0/xuZ04mc9csykox6grFWB3pjXY\n"
          "OEn9U8pt5KNldWfpfAZ7xu9WfyvthGXlhfwKEetOuHfAQv7FF6s25UIEU6Hmnwp9\n"
          "VmYp2twfMGdztz/gfFjKOGxf92RG+FMSkyAPq/vhyB7oQWxa+vdBn6BSdsfn27Qs\n"
          "bTvXrGe4FYcbuw4WkAKTljZX7TUegkXiwFoSps0jegECgYEA7o5AcRTZVUmmSs8W\n"
          "PUHn89UEuDAMFVk7grG1bg8exLQSpugCykcqXt1WNrqB7x6nB+dbVANWNhSmhgCg\n"
          "VrV941vbx8ketqZ9YInSbGPWIU/tss3r8Yx2Ct3mQpvpGC6iGHzEc/NHJP8Efvh/\n"
          "CcUWmLjLGJYYeP5oNu5cncC3fXUCgYEA2LANATm0A6sFVGe3sSLO9un1brA4zlZE\n"
          "Hjd3KOZnMPt73B426qUOcw5B2wIS8GJsUES0P94pKg83oyzmoUV9vJpJLjHA4qmL\n"
          "CDAd6CjAmE5ea4dFdZwDDS8F9FntJMdPQJA9vq+JaeS+k7ds3+7oiNe+RUIHR1Sz\n"
          "VEAKh3Xw66kCgYB7KO/2Mchesu5qku2tZJhHF4QfP5cNcos511uO3bmJ3ln+16uR\n"
          "GRqz7Vu0V6f7dvzPJM/O2QYqV5D9f9dHzN2YgvU9+QSlUeFK9PyxPv3vJt/WP1//\n"
          "zf+nbpaRbwLxnCnNsKSQJFpnrE166/pSZfFbmZQpNlyeIuJU8czZGQTifQKBgHXe\n"
          "/pQGEZhVNab+bHwdFTxXdDzr+1qyrodJYLaM7uFES9InVXQ6qSuJO+WosSi2QXlA\n"
          "hlSfwwCwGnHXAPYFWSp5Owm34tbpp0mi8wHQ+UNgjhgsE2qwnTBUvgZ3zHpPORtD\n"
          "23KZBkTmO40bIEyIJ1IZGdWO32q79nkEBTY+v/lRAoGBAI1rbouFYPBrTYQ9kcjt\n"
          "1yfu4JF5MvO9JrHQ9tOwkqDmNCWx9xWXbgydsn/eFtuUMULWsG3lNjfst/Esb8ch\n"
          "k5cZd6pdJZa4/vhEwrYYSuEjMCnRb0lUsm7TsHxQrUd6Fi/mUuFU/haC0o0chLq7\n"
          "pVOUFq5mW8p0zbtfHbjkgxyF\n"
          "-----END PRIVATE KEY-----\n";

  std::string const dh =
          "-----BEGIN DH PARAMETERS-----\n"
          "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n"
          "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n"
          "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n"
          "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n"
          "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n"
          "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n"
          "-----END DH PARAMETERS-----\n";
          
  http::https_server<http_custom_router>
        https_server { interface_address, 18045, std::filesystem::current_path() };
        
  https_server.set_ssl_context(http::ssl_dh_context_creator{}(cert, key, dh, "test"));
  
  std::thread thread {
    [&https_server]() {
      https_server.start(std::thread::hardware_concurrency());
      if (httsp_server.last_error()) {
        std::cerr << "Error starting HTTPS server: "
                  << https_server.last_error().message()
                  << std::endl;
      }
    }
  };
  
  std::cout << "HTTPS server running at https://" << interface_address << ":18045..." <<  std::endl;
  std::this_thread::sleep_for(std::chrono::minutes{ 1000 });
  https_server.stop();
  thread.join();
  return 0;
}

WebSocket server Usage Example:

int main() {
  const std::string interface_address{ "127.0.0.1" };
  http::websocket_server<http::echo_message_handler>
        websocket_server{ interface_address, 18090, std::filesystem::current_path() };
        
  std::thread thread {
    [&websocket_server]() {
      websocket_server.start(std::thread::hardware_concurrency());
      if (websocket_server.last_error()) {
        std::cerr << "Error starting WebSocket server: "
                  << websocket_server.last_error().message()
                  << std::endl;
      }
    }
  };
  
  std::cout << "WebSocket server running at ws://" << interface_address << ":18090..." <<  std::endl;
  std::this_thread::sleep_for(std::chrono::minutes{ 1000 });
  websocket_server.stop();
  thread.join();
  return 0;
}

WebSocket SSL server Usage Example:

int main() {
  ... declarations of std::string cert, key, dh...
  
  const std::string interface_address{ "127.0.0.1" };
  http::websocket_ssl_server<http::echo_message_handler>
        websocket_ssl_server{ interface_address, 18095, std::filesystem::current_path() };
  
  ... Similar to HTTPS:
  websocket_ssl_server.set_ssl_context(http::ssl_dh_context_creator{}(cert, key, dh, "test"));
  
  std::thread thread {
    [&websocket_ssl_server]() {
      websocket_ssl_server.start(std::thread::hardware_concurrency());
      if (websocket_ssl_server.last_error()) {
        std::cerr << "Error starting WebSocket server: "
                  << websocket_ssl_server.last_error().message()
                  << std::endl;
      }
    }
  };
  
  std::cout << "WebSocket Secure Server running at wss://" << interface_address << ":18095..." <<  std::endl;
  std::this_thread::sleep_for(std::chrono::minutes{ 1000 });
  websocket_ssl_server.stop();
  thread.join();
  return 0;
}

WebSocket::MessageHandler

  struct  echo_message_handler {

    inline websocket_message
    operator()(const websocket_message& message) const {
      websocket_message response = message;
      return response.write(" \"[echo]\"");
    }
    
  };

About

LibHTTP that is similar to ExpressJS in usage, but it's built using Boost Beast Framework, and it's written in modern C++17. Please, take a look:

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published