Skip to content

xiadnoring/manapi-http

Repository files navigation

logo Manapi Http

Introduction

HTTP server written on C++ which supports HTTP/1.1, HTTP/2 and HTTP/3 (over QUIC)

Caution

This project is in development!!!

About

console

This HTTP server should simplify development of web applications, API-interfaces and other things.

Many important utils will be supported out of the box, for example, JSON, MySQL-client, PostgreSQL-client, JSON-masks, Big Int, modules, plugins.

Installation

To compile this project, you need to install below projects:

  • libuv 1.49.2 or greater [required]
  • openssl 3.3.1 or greater [optional]
  • zlib 1.3.1 or greater [optional]
  • gmp 6.3.0 or greater [optional]
  • curl 8.8.0-1 or greater [optional]
  • wolfssl 5.5.0 or greater [optional]
  • quiche 0.22.0 or greater [optional]
  • grpc 1.72.0 or greater [optional]
  • cpptrace 0.74.1 or greater [optional]
  • brotli 1.1.0 or greater [optional]
  • zstd 1.5.5 or greater [optional]
  • nghttp2 1.59.0 or greater [optional]
  • nghttp3 1.6.0 or greater [optional]

Benchmark

Using 16 threads on my laptop (Intel i5-12500H) wrk (HTTP/1.1) gave me the following results

wrk http://0.0.0.0:8889/main -d 10 -t 4 -c 200
Running 10s test @ http://127.0.0.1:8889/main
  4 threads and 200 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   282.72us  531.03us  19.43ms   95.73%
    Req/Sec   203.80k    20.56k  286.61k    76.00%
  8106400 requests in 10.02s, 1.28GB read
Requests/sec: 809023.73
Transfer/sec:    131.16MB

For Arch Linux

pacman -Sy libuv
# optinal
pacman -Sy gmp openssl zlib curl

or

paru -Sy libuv
paru -Sy gmp openssl zlib curl

For Windows

MSVC C++ (Tested Executable Only)

For MacOs

No support (will be soon)

Build

cmake -DCMAKE_BUILD_TYPE=Debug/Release ...

Build as Executable

cmake ... -DMANAPIHTTP_BUILD_TYPE=exe

Build as Library

cmake ... -DMANAPIHTTP_BUILD_TYPE=lib

Build with Conan

cmake ... -DMANAPIHTTP_BUILD_METHOD=conan

Build with cmake Fetch

Warning

not everything is supported

(gmp (linux), libuv, zlib, curl, openssl, nghttp2, nghttp3)

cmake ... -DMANAPIHTTP_BUILD_METHOD=fetch

Example

int main () {
    /* creates 2 threads for blocking I/O syscalls */
    manapi::async::context::threadpoolfs(2);
    /* disable several signals */
    manapi::async::context::gbs (manapi::async::context::blockedsignals());
    /* creates 4 additional threads for 4 additional event loops */
    auto ctx = manapi::async::context::create(4).unwrap();
    /* handle interrupt signals */
    ctx->eventloop()->setup_handle_interrupt();
    /* HTTP context for multiple HTTP routers (threadsafe) */
    auto router_ctx = manapi::net::http::server_ctx::create().unwrap();
    /* metric */
    std::atomic<int> cnt = 0;
    /* runs main event loop and 4 additional event loops */
    manapi::async::context::run(ctx, 4, [&cnt, router_ctx] (std::function<void()> bind) -> void {
        using http = manapi::net::http::server;
        
        auto router = manapi::net::http::server::create(router_ctx).unwrap();
        
        auto db = manapi::ext::pq::connection::create().unwrap();

        router.GET ("/", [&cnt] (http::req &req, http::uresp resp) mutable -> void {
            resp->text(std::format("Hello World! Count: {}", cnt.fetch_add(1))).unwrap();
            resp.finish();
        }).unwrap();

        router.GET("/+error", [](http::req &req, http::resp &resp) -> manapi::future<> {
            resp.replacers({
                {"status_code", std::to_string(resp.status_code())},
                {"status_message", std::string{resp.status_message()}}
            }).unwrap();

            co_return resp.file ("../examples/error.html").unwrap();
        }).unwrap();

        router.POST("/+error", [](http::req &req, http::resp &resp) -> manapi::future<> {
            co_return resp.json({{"error", resp.status_code()},
                    {"msg", std::string{resp.status_message()}}}).unwrap();
        }).unwrap();

        router.GET("/cat", [](http::req &req, http::resp &resp) -> manapi::future<> {
            auto fetch = (co_await manapi::net::fetch2::fetch ("https://dragonball-api.com/api/planets/7", {
                {"verify_peer", false},
                {"alpn", true},
                {"method", "GET"}
            })).unwrap();

            if (!fetch.ok()) {
                co_return resp.json ({{"error", true}, {"message", "fetch failed"}}).unwrap();
            }

            auto data = (co_await fetch.json()).unwrap();

            co_return resp.text(std::move(data["description"].as_string())).unwrap();
        }).unwrap();

        router.GET("/proxy", [](http::req &req, http::resp &resp) -> manapi::future<> {
            co_return resp.proxy("http://127.0.0.1:8889/video").unwrap();
        }).unwrap();

        router.GET("/video", [](http::req &req, http::resp &resp) -> manapi::future<> {
            resp.partial_enabled(true);
            resp.compress_enabled(false);
            co_return resp.file("video.mp4").unwrap();
        }).unwrap();

        router.GET("/stop", [](http::req &req, http::resp &resp) -> manapi::future<> {
            /* stop the app */
            co_await manapi::async::current()->stop();
            co_return resp.text("stopped").unwrap();
        }).unwrap();

        router.GET("/timeout", [](http::req &req, http::resp &resp) -> manapi::future<> {
            /* stop the app */
            co_await manapi::async::delay{10000};
            co_return resp.text("10sec").unwrap();
        }).unwrap();

        router.GET("/pq/[id]", [db](manapi::net::http::request& req, manapi::net::http::response& resp) mutable -> manapi::future<> {
            auto msg = req.param("id").unwrap();
            char *end;
            auto res1 = co_await db.exec("INSERT INTO for_test (id, str_col) VALUES ($2, $1);","no way", std::strtoll(msg.data(), &end, 10));
            if (!res1) {
                if (res1.sqlcode() != manapi::ext::pq::SQL_STATE_UNIQUE_VIOLATION)
                    res1.err().log();
            }

            auto res = co_await db.exec("SELECT * FROM for_test;");
            if (res) {
                std::string content = "b";
                for (const auto &row: res.unwrap()) {
                    content += std::to_string(row["id"].as<int>()) + " - " + row["str_col"].as<std::string>() + "<hr/>";
                }

                co_return resp.text(std::move(content)).unwrap();
            }

            co_return resp.text(std::string{res.is_sqlerr() ? res.sqlmsg() : res.message()}).unwrap();
        }).unwrap();

        /**
         * starts I/O jobs.
         * works in this context as long as possible.
         */
        manapi::async::run([router, db] () mutable -> manapi::future<> {
            (co_await db.connect("127.0.0.1", "7879", "development", "password", "db")).unwrap();
            (co_await router.config_object({
                {"pools", manapi::json::array({
                    {
                        {"address", "127.0.0.1"},
                        {"http", manapi::json::array({"2", "1.1"})},
                        {"transport", "tls"},
                        {"partial_data_min_size", 0},
                        {"implementation", "openssl"},
                        {"port", "8888"},
                        {"ssl", {
                            {"cert", "../examples/self-signed-ssl/cert.crt"},
                            {"key", "../examples/self-signed-ssl/cert.key"},
                            {"enabled", true},
                            {"tls", "1.3"}
                        }},
                        {"tcp_no_delay", true}
                    }
                })},
                {"save_config", false}
            })).unwrap();

            (co_await router.start()).unwrap();
        });

        /* bind event loop in the current context */
        bind();
    }).unwrap();

    manapi::clear_tools::curl_library_clear();
    manapi::clear_tools::ev_library_clear();
    manapi::clear_tools::ssl_library_clear();

    return 0;
}

Params

Option name Description Values
MANAPIHTTP_GMP_DEPENDENCY Bigint Support ON/OFF
MANAPIHTTP_CURL_DEPENDENCY Fetch Support ON/OFF
MANAPIHTTP_ZLIB_DEPENDENCY gzip/deflate Support ON/OFF
MANAPIHTTP_QUICHE_DEPENDENCY HTTP3/QUIC by Cloudflare ON/OFF
MANAPIHTTP_OPENSSL_DEPENDENCY TLS/QUIC by OpenSSL ON/OFF
MANAPIHTTP_WOLFSSL_DEPENDENCY TLS by WolfSSL ON/OFF
MANAPIHTTP_GRPC_DEPENDENCY gRPC Support ON/OFF
MANAPIHTTP_BROTLI_DEPENDENCY brotli Support ON/OFF
MANAPIHTTP_ZSTD_DEPENDENCY zstd Support ON/OFF
MANAPIHTTP_WOLFSSL_WITH_ALPN WolfSSL with ALPN ON/OFF
MANAPIHTTP_NGHTTP2_DEPENDENCY HTTP2 by nghttp2 ON/OFF
MANAPIHTTP_NGHTTP3_DEPENDENCY HTTP3 by nghttp3 ON/OFF
MANAPIHTTP_CPPTRACE_DEPENDENCY CppTrace ON/OFF
MANAPIHTTP_JSON_DEBUG JSON debug symbols ON/OFF
MANAPIHTTP_STD_BACKTRACE_DEPENDENCY STD C++23 stacktrace ON/OFF
BUILD_SHARED_LIBS Build as a shared lib ON/OFF
MANAPIHTTP_BUILD_METHOD Build Method conan/default/fetch
MANAPIHTTP_DEPENDENCY_FETCH_SHARED Bsuild shared depends ON/OFF
MANAPIHTTP_OPENSSL_SYSTEM_DISABLE Disable default OpenSSL ON/OFF
MANAPIHTTP_CURL_SYSTEM_DISABLE Disable default cURL ON/OFF
MANAPIHTTP_DEPENDENCY_FETCH_OUTPUT Depends Output Path <PATH>/OFF
MANAPIHTTP_BUILD_TYPE Binary or Library exe/lib/test
MANAPIHTTP_INSTALL_ARCH '/x86_64-linux-gnu' '/<PATH>'/OFF

TODO

  • Async
    • Mutex, Conditional Variable, Future
    • Default Async Context
    • ⭐ Some improvements
  • Debugging
    • Error Codes
    • Stack Error
    • Async I/O Debug
  • Configuration
    • limit-rate (TCP: HTTP/1.1, HTTP/2)
    • limit-rate (UDP: HTTP/3)
    • connections limit
    • minimum speed requirements
  • HTTP
    • Default HTTP/1.1 realization
    • Default HTTP/2 realization
    • Support HTTP/1.1 (Default)
    • Support HTTP/2 (nghttp2, Default)
    • Support HTTP/3 (nghttp3)
  • HTTP Features
    • Chunked Transmission (HTTP/1.1 - 3)
    • Ranges
    • FormData
    • JSON (chunked transmission)
    • Multi-Ranges
    • Trailers (recv)
    • Trailers (send)
  • TLS
    • OpenSSL
    • WolfSSL
  • QUIC
    • quiche
    • OpenSSL
  • Fetch
    • Async CURL support
    • Async/sync read callbacks
    • Async/sync write callbacks
    • Chunked Transmission (HTTP/1.1 - 3)
  • Other Protocols
    • WebSockets
  • Cross-Platform Build
    • Linux
    • MacOs
    • Windows

Tested

  • Hyprland Arch Linux x86_64 kernel 6.9.3-zen1-1-zen wayland Debug/Release
  • Windows 11 Pro 22h2 x86_64 MSVC Debug (exe)

Made in

ГБОУ РИЛИ

Башкортостан