commit 901087f6637474e0688de1040c3003eaac518a76 from: Aleksey Ryndin date: Wed Aug 09 17:11:25 2023 UTC Receive request implemented commit - f2a85a75526335d0b3c26a79bd9c50f7f5ef98a6 commit + 901087f6637474e0688de1040c3003eaac518a76 blob - 61c3190a7d73098284fab48113e2ea55ec0fa3ff blob + 8e28b9616f39d3517678e609f64d5317b850e017 --- shared/not_null +++ shared/not_null @@ -1,6 +1,9 @@ /** Restricts a pointer or smart pointer to only hold non-null values. */ +#include + + #pragma once @@ -11,7 +14,7 @@ template class not_null { public: - not_null(T p) :m_p{p} {} + not_null(T p) :m_p{p} { assert(m_p != nullptr); } T get() const {return m_p;} operator T() const {return m_p;} blob - ff03f00b2d778d4c85fb427b0ce7a9aa43609ff2 blob + 88b33aba9a823beaa8573b363cecf05c42860eda --- shared/transport.cc +++ shared/transport.cc @@ -14,10 +14,44 @@ namespace using config_t = std::unique_ptr; constexpr auto protocols = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3; +constexpr auto MAX_URL_LENGTH = 1024; +// constexpr auto MAX_META_LENGTH = 1024; + +bool read(not_null ctx, std::vector &buff) +{ + ssize_t ret{}; + for (; ; ) + { + ret = tls_read(ctx, &buff.at(0), buff.size()); + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) + continue; + break; + } + if (ret == -1) + { + const auto last_error = tls_error(ctx); + error::occurred( + "TLS read", + [last_error] + { + if (last_error) + error::g_log << "Error: " << last_error << std::endl; + } + ); + return false; + } + buff.resize(ret); + return true; +} + + } // namespace +const std::array CRLF{'\r', '\n'}; + + bool init() { if (tls_init() == -1) @@ -30,7 +64,7 @@ bool init() } -void create_gemini_server_ctx(not_null cert_file, not_null key_file, context_t &ret_ctx) +void create_server(not_null cert_file, not_null key_file, context_t &ret_ctx) { config_t cfg{tls_config_new(), tls_config_free}; if (!cfg) @@ -92,17 +126,47 @@ void create_gemini_server_ctx(not_null cert_ } -void accept(struct tls *server_ctx, int client_socket, context_t &ret_ctx) +void accept( + not_nullserver_ctx, + unique_fd &client_socket, // Socket ownership transfer + accepted_context &ctx +) { struct tls *client_ctx = nullptr; - if (tls_accept_socket(server_ctx, &client_ctx, client_socket) == -1) + if (tls_accept_socket(server_ctx, &client_ctx, client_socket.get()) == -1) { error::occurred("TLS accept", error::none{}); - ret_ctx.reset(); + ctx.reset(); return; } - ret_ctx.reset(client_ctx); + ctx.reset(accepted_context::value{client_socket.release(), client_ctx}); } + +bool read_request(not_null ctx, std::vector &url) +{ + url.resize(MAX_URL_LENGTH + CRLF.size()); + + if (!read(ctx, url)) + return false; + + for (auto current = url.cbegin(); current < url.end(); ++current) + { + const auto next = (current + 1); + if (next == url.end()) + break; + + if (*current == CRLF[0] && *next == CRLF[1]) + { + // > servers MUST ignore anything sent after the first occurrence of a . + url.resize(current - url.cbegin()); + return true; + } + } + error::occurred("Parse request", error::none{}); + return false; +} + + } // namespace transport } // namespace vostok blob - 674b353f1e4433e3260fd17613a369a41a292999 blob + 28299ad9baa535fb9945cd2423e1c0eef48915c4 --- shared/transport.h +++ shared/transport.h @@ -2,7 +2,12 @@ #include #include +#include +#include +#include #include +#include +#include #pragma once @@ -13,6 +18,10 @@ namespace vostok namespace transport { + +extern const std::array CRLF; + + /** `struct tls *` smart pointer */ typedef std::unique_ptr context_t; @@ -22,10 +31,45 @@ bool init(); /* Create new TLS context for gemini server */ -void create_gemini_server_ctx(not_null cert_file, not_null key_file, context_t &ctx); +void create_server(not_null cert_file, not_null key_file, context_t &ctx); -/** Accept new client */ -void accept(struct tls *server_ctx, int client_socket, context_t &ctx); +class accepted_context : non_copiable +{ +public: + typedef std::tuple< int, struct tls *> value; + accepted_context(int fd = -1, struct tls *ctx = nullptr) { reset(value{fd, ctx}); } + accepted_context(value v) { reset(v); } + + struct tls *get_ctx() const { return m_ctx.get(); } + int get_fd() const { return m_fd.get(); } + + value get() const { return value{m_fd.get(), m_ctx.get()}; } + value release() { return value{m_fd.release(), m_ctx.release()}; } + void reset(value v=value{-1, nullptr}) + { + m_fd.reset(std::get<0>(v)); + m_ctx.reset(std::get<1>(v)); + assert((m_fd && m_ctx) || (!m_fd && !m_ctx)); + } + operator bool () const { return m_fd && m_ctx; } + +private: + unique_fd m_fd; + context_t m_ctx{nullptr, tls_free}; +}; + +/** Accept new client. Socket ownership transfer */ +void accept( + not_nullserver_ctx, + unique_fd &client_socket, // Socket ownership transfer + accepted_context &ctx +); + + +/** Read genimi request and return url */ +bool read_request(not_null ctx, std::vector &url); + + } // namespace transport } // namespace vostok blob - 0a39a90119400afb1373a43976637682d7e806e2 blob + b6b52626e864f4106e5f532d51e7c851a32015be --- vostokd/vostokd.cc +++ vostokd/vostokd.cc @@ -2,13 +2,17 @@ #include #include + #include #include #include #include #include +#include + + namespace vostok { namespace @@ -86,8 +90,29 @@ struct command_line_arguments }; -bool server_loop(int server_socket, struct tls *ctx) +void client_thread(transport::accepted_context::value raw_value) { + const transport::accepted_context ctx{raw_value}; + assert(ctx); + + std::vector url; + if (!transport::read_request(ctx.get_ctx(), url)) + { + // transport::send_response + return; + } + + error::g_log << "Request URL: \""; + for (const char c : url) + error::g_log << c; + error::g_log << "\"" << std::endl; + +} + + +bool server_loop(int server_socket, not_nullctx) +{ + error::g_log << "🚀 Vostok server listening..." << std::endl; for (; ; ) { if (listen(server_socket, 1024) == -1) @@ -105,13 +130,15 @@ bool server_loop(int server_socket, struct tls *ctx) continue; } - transport::context_t client_ctx{nullptr, tls_free}; - transport::accept(ctx, client_socket.get(), client_ctx); + transport::accepted_context client_ctx; + transport::accept(ctx, client_socket, client_ctx); if (!client_ctx) continue; try { + std::thread{client_thread, client_ctx.get()}.detach(); + client_ctx.release(); // move ownership to thread } catch (const std::system_error &e) { @@ -136,7 +163,7 @@ bool main(const command_line_arguments &args) return false; transport::context_t ctx{nullptr, tls_free}; - transport::create_gemini_server_ctx(args.m_cert_file, args.m_key_file, ctx); + transport::create_server(args.m_cert_file, args.m_key_file, ctx); if (!ctx) return false;