http-benchmark.cpp

1
2
3
4
5
6
7
8
#include <felspar/io.hpp>
#include <felspar/memory/hexdump.hpp>

#include <fstream>
#include <iostream>


namespace {

HTTP response options

12
13
    std::span<std::byte const> short_text();
    std::span<std::byte const> big_octets();

HTTP requests

Processes HTTP requests on the connection.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    felspar::io::warden::task<void> http_request(
            felspar::io::warden &ward,
            felspar::posix::fd fd,
            std::span<std::byte const> const response) {
        felspar::io::read_buffer<std::array<char, 2 << 10>> buffer;
        [[maybe_unused]] auto const request{
                co_await felspar::io::read_until_lf_strip_cr(ward, fd, buffer)};
        for (auto header = co_await felspar::io::read_until_lf_strip_cr(
                     ward, fd, buffer);
             header.size();
             header = co_await felspar::io::read_until_lf_strip_cr(
                     ward, fd, buffer)) {
            // TODO The headers should really be processed
        }
        co_await write_all(ward, fd, response);
        co_await ward.close(std::move(fd));
        co_return;
    }

Accept loop

There will be one of these per thread.

45
46
47
48
49
50
51
52
53
54
    felspar::io::warden::task<void> accept_loop(
            felspar::io::warden &ward, const felspar::posix::fd &socket) {
        felspar::io::warden::starter<void> co;
        for (auto connections = felspar::io::accept(ward, socket);
             auto cnx = co_await connections.next();) {
            co.post(http_request, std::ref(ward), felspar::posix::fd{*cnx},
                    big_octets());
            co.garbage_collect_completed();
        }
    }

Main

58
59
60
    felspar::io::warden::task<int> co_main(felspar::io::warden &ward) {
        constexpr std::uint16_t port{4040};
        constexpr int backlog = 64;

Create the accept socket

63
64
65
66
        auto fd = ward.create_socket(AF_INET, SOCK_STREAM, 0);
        felspar::posix::set_reuse_port(fd);
        felspar::posix::bind_to_any_address(fd, port);
        felspar::posix::listen(fd, backlog);

Run the web server forever

 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
        co_await ::accept_loop(ward, fd);

        co_return 0;
    }


}


int main() {
    try {
        std::cout << "Starting web server for current directory\n";
        felspar::posix::promise_to_never_use_select();
        felspar::io::poll_warden ward{};
        return ward.run(co_main);
    } catch (std::exception const &e) {
        std::cerr << "Caught an exception: " << e.what() << '\n';
        return -1;
    }
}


namespace {


    [[maybe_unused]] std::span<std::byte const> short_text() {
        constexpr std::string_view sv{
                "HTTP/1.0 200 OK\r\n"
                "Content-Length: 3\r\n"
                "\r\n"
                "OK\n"};
        return {reinterpret_cast<std::byte const *>(sv.data()), sv.size()};
    }

    constexpr std::string_view prefix{
            "HTTP/1.0 200 OK\r\n"
            "Content-Length: "};
    auto const big{[]() {
        std::string buffer{prefix};
        constexpr std::size_t bytes{10 << 10};
        buffer += std::to_string(bytes);
        buffer += "\r\n\r\n";
        std::vector<char> data(bytes);
        std::ifstream{"/dev/urandom"}.read(data.data(), bytes);
        buffer += std::string_view{data.data(), bytes};
        return buffer;
    }()};
    std::span<std::byte const> big_octets() {
        return {reinterpret_cast<std::byte const *>(big.data()), big.size()};
    }

}