commit 50eb955cfb20f96fd59942b27e83fe533e1a3fcc from: Aleksey Ryndin date: Fri Aug 11 14:56:45 2023 UTC Return temporary response implementation commit - 901087f6637474e0688de1040c3003eaac518a76 commit + 50eb955cfb20f96fd59942b27e83fe533e1a3fcc blob - 88b33aba9a823beaa8573b363cecf05c42860eda blob + d970243ef62291dfabea388e5760d215099c7676 --- shared/transport.cc +++ shared/transport.cc @@ -14,16 +14,13 @@ 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) +std::span read(not_null ctx, std::span buff) { ssize_t ret{}; for (; ; ) { - ret = tls_read(ctx, &buff.at(0), buff.size()); + ret = tls_read(ctx, &buff[0], buff.size()); if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue; break; @@ -39,10 +36,9 @@ bool read(not_null ctx, std::vector{}; } - buff.resize(ret); - return true; + return std::span{&buff[0], static_cast(ret)}; } @@ -50,6 +46,9 @@ bool read(not_null ctx, std::vector CRLF{'\r', '\n'}; +const std::array SPACE{' '}; +const std::array STATUS_20_SUCCESS{'2', '0'}; +const std::array STATUS_59_BAD_REQUEST{'5', '9'}; bool init() @@ -143,30 +142,71 @@ void accept( } -bool read_request(not_null ctx, std::vector &url) +std::span read_request(not_null ctx, std::array &buffer) { - url.resize(MAX_URL_LENGTH + CRLF.size()); + const auto request = read(ctx, buffer); + if (!request.size()) + return std::span{}; - if (!read(ctx, url)) - return false; - - for (auto current = url.cbegin(); current < url.end(); ++current) + for (auto current = request.begin(); current < request.end(); ++current) { const auto next = (current + 1); - if (next == url.end()) + if (next == request.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; + return std::span{&request[0], static_cast(current - request.begin())}; } } error::occurred("Parse request", error::none{}); - return false; + return std::span{}; } +bool send_response(not_null ctx, std::array status, std::span meta) +{ + // > + std::array buff; + auto current = buff.begin(); + current = std::copy(status.cbegin(), status.cend(), current); + current = std::copy(SPACE.cbegin(), SPACE.cend(), current); + assert(meta.size() <= MAX_META_LENGTH); + current = std::copy(meta.begin(), meta.end(), current); + current = std::copy(CRLF.cbegin(), CRLF.cend(), current); + + return send(ctx, std::span{&buff[0], static_cast(current - buff.begin())}); +} + + +bool send(not_null ctx, std::span buff) +{ + ssize_t ret{0}; + while (buff.size() > 0) + { + ret = tls_write(ctx, &buff[0], buff.size()); + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) + continue; + break; + buff = std::span{&buff[ret], buff.size() - ret}; + } + if (ret == -1) + { + const auto last_error = tls_error(ctx); + error::occurred( + "TLS write", + [last_error] + { + if (last_error) + error::g_log << "Error: " << last_error << std::endl; + } + ); + return false; + } + return true; +} + + } // namespace transport } // namespace vostok blob - 28299ad9baa535fb9945cd2423e1c0eef48915c4 blob + 73cdc0d8a6131b9393b5dcd70420546bffd0f6a3 --- shared/transport.h +++ shared/transport.h @@ -4,11 +4,11 @@ #include #include #include -#include -#include + #include -#include +#include +#include #pragma once @@ -19,25 +19,30 @@ namespace transport { +constexpr auto MAX_URL_LENGTH = 1024; +constexpr auto MAX_META_LENGTH = 1024; + extern const std::array CRLF; +extern const std::array STATUS_20_SUCCESS; +extern const std::array STATUS_59_BAD_REQUEST; +constexpr auto MAX_REQUEST_LENGTH = MAX_URL_LENGTH + CRLF.size(); + /** `struct tls *` smart pointer */ typedef std::unique_ptr context_t; -/** Per-process initialization */ -bool init(); - - -/* Create new TLS context for gemini server */ -void create_server(not_null cert_file, not_null key_file, context_t &ctx); - - +/** Pair: `struct tls *` pointer and file descriptor as smart pointer */ class accepted_context : non_copiable { public: - typedef std::tuple< int, struct tls *> value; + struct value + { + int m_fd{-1}; + struct tls *m_ctx{nullptr}; + }; + accepted_context(int fd = -1, struct tls *ctx = nullptr) { reset(value{fd, ctx}); } accepted_context(value v) { reset(v); } @@ -48,8 +53,8 @@ class accepted_context : non_copiable (public) 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)); + m_fd.reset(v.m_fd); + m_ctx.reset(v.m_ctx); assert((m_fd && m_ctx) || (!m_fd && !m_ctx)); } operator bool () const { return m_fd && m_ctx; } @@ -59,6 +64,15 @@ class accepted_context : non_copiable (public) context_t m_ctx{nullptr, tls_free}; }; + +/** Per-process initialization */ +bool init(); + + +/* Create new TLS context for gemini server */ +void create_server(not_null cert_file, not_null key_file, context_t &ctx); + + /** Accept new client. Socket ownership transfer */ void accept( not_nullserver_ctx, @@ -67,9 +81,17 @@ void accept( ); -/** Read genimi request and return url */ -bool read_request(not_null ctx, std::vector &url); +/** Read genimi request and return url (empty url - error) */ +std::span read_request(not_null ctx, std::array &buffer); +/** Write gemini response */ +bool send_response(not_null ctx, std::array status, std::span meta); + + +/** Send raw bytes */ +bool send(not_null ctx, std::span buff); + + } // namespace transport } // namespace vostok blob - 7455e658ed0f78bb460d284f6809c819fde5b551 blob + 50d12fd6adfe4e61ca5b62655d187117f60b628d --- vostokd/Makefile +++ vostokd/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS = -Wall -Wextra -std=c++11 +CXXFLAGS = -Wall -Wextra -std=c++20 CXXFLAGS += -I${.CURDIR}/../shared CXXFLAGS += -ltls blob - b6b52626e864f4106e5f532d51e7c851a32015be blob + 6e2205ba496a0fb0ece746868d149c26fdae1681 --- vostokd/vostokd.cc +++ vostokd/vostokd.cc @@ -18,7 +18,20 @@ namespace vostok namespace { +template +constexpr std::span literal_to_span(const char (&arr)[N]) +{ + static_assert(N > 0); + return std::span{&arr[0], N - 1}; +} +namespace meta +{ +const auto badrequest = literal_to_span("Bad request"); +const auto textgemini = literal_to_span("text/gemini"); +} // namespace meta + + struct command_line_arguments { not_null m_addr{"127.0.0.1"}; @@ -95,18 +108,22 @@ void client_thread(transport::accepted_context::value const transport::accepted_context ctx{raw_value}; assert(ctx); - std::vector url; - if (!transport::read_request(ctx.get_ctx(), url)) + std::array buffer; + const auto url = transport::read_request(ctx.get_ctx(), buffer); + + for (auto c : url) + error::g_log << c; + error::g_log << std::endl; + + if (!url.size()) { - // transport::send_response + transport::send_response(ctx.get_ctx(), transport::STATUS_59_BAD_REQUEST, meta::badrequest); return; } - error::g_log << "Request URL: \""; - for (const char c : url) - error::g_log << c; - error::g_log << "\"" << std::endl; - + transport::send_response(ctx.get_ctx(), transport::STATUS_20_SUCCESS, meta::textgemini); + const char content[] = "# Vostok server\r\n\r\n...work-in-progress..."; + transport::send(ctx.get_ctx(), literal_to_span(content)); }