this repo has no description
at main 107 lines 3.4 kB view raw
1// Project Includes 2#include "client.hpp" 3#include "error.hpp" 4#include "file_descriptor.hpp" 5#include "server.hpp" 6#include "uring_context.hpp" 7// Standard Library Includes 8#include <cstdint> 9#include <exception> 10#include <expected> 11#include <netinet/in.h> 12#include <print> 13// Third-Party Library Includes 14#include <argparse/argparse.hpp> 15#include <exec/async_scope.hpp> 16#include <exec/repeat_effect_until.hpp> 17#include <exec/static_thread_pool.hpp> 18#include <stdexec/execution.hpp> 19 20std::expected<uint32_t, Error> parse_port(int argc, char *argv[]) 21{ 22 if (argc < 2) 23 { 24 return std::unexpected(Error("Port number not provided")); 25 } 26 try 27 { 28 int port = std::stoi(argv[1]); 29 if (port < 1 || port > 65535) 30 { 31 return std::unexpected(Error("Port number must be between 1 and 65535")); 32 } 33 return static_cast<uint16_t>(port); 34 } 35 catch (std::exception const &ex) 36 { 37 auto message = std::string("Invalid port number: ") + ex.what(); 38 return std::unexpected(Error(message)); 39 } 40} 41 42kev::task<void> listen(Server &server, UringContext &uring_ctx) 43{ 44 exec::async_scope scope{}; 45 while (true) 46 { 47 RawFileDescriptor fd{-1}; 48 try 49 { 50 fd = co_await uring_ctx.async_accept(server.m_server_fd.get()); 51 } 52 catch (const std::exception &e) 53 { 54 std::println(std::cerr, "Error accepting connection: {}", e.what()); 55 break; 56 } 57 auto client_pipeline = handle_connection_coroutine(FileDescriptor(fd), server.m_context, uring_ctx) | 58 stdexec::upon_error([](std::exception_ptr ptr) { 59 try 60 { 61 if (ptr) 62 { 63 std::rethrow_exception(ptr); 64 } 65 } 66 catch (const std::exception &e) 67 { 68 std::println(std::cerr, "Error in client connection: {}", e.what()); 69 } 70 }); 71 scope.spawn(std::move(client_pipeline)); 72 } 73} 74 75int main(int argc, char *argv[]) 76{ 77 argparse::ArgumentParser program("webserver"); 78 uint16_t port{}; 79 std::string root; 80 program.add_argument("--port", "-p") 81 .default_value<uint16_t>(8080) 82 .help("Port number to listen on") 83 .store_into(port); 84 program.add_argument("--root", "-r") 85 .default_value(std::string(".")) 86 .help("Root directory for serving files") 87 .store_into(root); 88 try 89 { 90 program.parse_args(argc, argv); 91 } 92 catch (const std::exception &e) 93 { 94 std::println(std::cerr, "Error parsing arguments: {}", e.what()); 95 return 1; 96 } 97 // Validate that the root directory exists 98 if (!std::filesystem::exists(root) || !std::filesystem::is_directory(root)) 99 { 100 std::println(std::cerr, "Error: Root directory '{}' does not exist or is not a directory.", root); 101 return 1; 102 } 103 104 UringContext uring_ctx = UringContext(1024); 105 auto server = Server(port, root); 106 uring_ctx.run(listen(server, uring_ctx)); 107}