commit - 901087f6637474e0688de1040c3003eaac518a76
commit + 50eb955cfb20f96fd59942b27e83fe533e1a3fcc
blob - 88b33aba9a823beaa8573b363cecf05c42860eda
blob + d970243ef62291dfabea388e5760d215099c7676
--- shared/transport.cc
+++ shared/transport.cc
using config_t = std::unique_ptr<struct tls_config, decltype(&tls_config_free)>;
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<struct tls *> ctx, std::vector<char> &buff)
+std::span<char> read(not_null<struct tls *> ctx, std::span<char> 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;
error::g_log << "Error: " << last_error << std::endl;
}
);
- return false;
+ return std::span<char>{};
}
- buff.resize(ret);
- return true;
+ return std::span<char>{&buff[0], static_cast<size_t>(ret)};
}
const std::array<char, 2> CRLF{'\r', '\n'};
+const std::array<char, 1> SPACE{' '};
+const std::array<char, 2> STATUS_20_SUCCESS{'2', '0'};
+const std::array<char, 2> STATUS_59_BAD_REQUEST{'5', '9'};
bool init()
}
-bool read_request(not_null<struct tls *> ctx, std::vector<char> &url)
+std::span<char> read_request(not_null<struct tls *> ctx, std::array<char, MAX_REQUEST_LENGTH> &buffer)
{
- url.resize(MAX_URL_LENGTH + CRLF.size());
+ const auto request = read(ctx, buffer);
+ if (!request.size())
+ return std::span<char>{};
- 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 <CR><LF>.
- url.resize(current - url.cbegin());
- return true;
+ return std::span<char>{&request[0], static_cast<size_t>(current - request.begin())};
}
}
error::occurred("Parse request", error::none{});
- return false;
+ return std::span<char>{};
}
+bool send_response(not_null<struct tls *> ctx, std::array<char, 2> status, std::span<const char> meta)
+{
+ // > <STATUS><SPACE><META><CR><LF>
+ std::array<char, status.size() + SPACE.size() + MAX_META_LENGTH + CRLF.size()> 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<char const>{&buff[0], static_cast<size_t>(current - buff.begin())});
+}
+
+
+bool send(not_null<struct tls *> ctx, std::span<const char> 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<const char>{&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
#include <not_null>
#include <non_copiable>
#include <unique_fd>
-#include <tuple>
-#include <tls.h>
+
#include <array>
-#include <vector>
+#include <span>
+#include <tls.h>
#pragma once
{
+constexpr auto MAX_URL_LENGTH = 1024;
+constexpr auto MAX_META_LENGTH = 1024;
+
extern const std::array<char, 2> CRLF;
+extern const std::array<char, 2> STATUS_20_SUCCESS;
+extern const std::array<char, 2> STATUS_59_BAD_REQUEST;
+constexpr auto MAX_REQUEST_LENGTH = MAX_URL_LENGTH + CRLF.size();
+
/** `struct tls *` smart pointer */
typedef std::unique_ptr<struct tls, decltype(&tls_free)> context_t;
-/** Per-process initialization */
-bool init();
-
-
-/* Create new TLS context for gemini server */
-void create_server(not_null<czstring> cert_file, not_null<czstring> 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); }
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; }
context_t m_ctx{nullptr, tls_free};
};
+
+/** Per-process initialization */
+bool init();
+
+
+/* Create new TLS context for gemini server */
+void create_server(not_null<czstring> cert_file, not_null<czstring> key_file, context_t &ctx);
+
+
/** Accept new client. Socket ownership transfer */
void accept(
not_null<struct tls *>server_ctx,
);
-/** Read genimi request and return url */
-bool read_request(not_null<struct tls *> ctx, std::vector<char> &url);
+/** Read genimi request and return url (empty url - error) */
+std::span<char> read_request(not_null<struct tls *> ctx, std::array<char, MAX_REQUEST_LENGTH> &buffer);
+/** Write gemini response */
+bool send_response(not_null<struct tls *> ctx, std::array<char, 2> status, std::span<const char> meta);
+
+
+/** Send raw bytes */
+bool send(not_null<struct tls *> ctx, std::span<const char> buff);
+
+
} // namespace transport
} // namespace vostok
blob - 7455e658ed0f78bb460d284f6809c819fde5b551
blob + 50d12fd6adfe4e61ca5b62655d187117f60b628d
--- vostokd/Makefile
+++ vostokd/Makefile
-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
namespace
{
+template <std::size_t N>
+constexpr std::span<const char> literal_to_span(const char (&arr)[N])
+{
+ static_assert(N > 0);
+ return std::span<const char>{&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<czstring> m_addr{"127.0.0.1"};
const transport::accepted_context ctx{raw_value};
assert(ctx);
- std::vector<char> url;
- if (!transport::read_request(ctx.get_ctx(), url))
+ std::array<char, transport::MAX_REQUEST_LENGTH> 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));
}