commit - f2a85a75526335d0b3c26a79bd9c50f7f5ef98a6
commit + 901087f6637474e0688de1040c3003eaac518a76
blob - 61c3190a7d73098284fab48113e2ea55ec0fa3ff
blob + 8e28b9616f39d3517678e609f64d5317b850e017
--- shared/not_null
+++ shared/not_null
/** Restricts a pointer or smart pointer to only hold non-null values. */
+#include <cassert>
+
+
#pragma once
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
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)
+{
+ 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 <unnamed>
+const std::array<char, 2> CRLF{'\r', '\n'};
+
+
bool init()
{
if (tls_init() == -1)
}
-void create_gemini_server_ctx(not_null<czstring> cert_file, not_null<czstring> key_file, context_t &ret_ctx)
+void create_server(not_null<czstring> cert_file, not_null<czstring> key_file, context_t &ret_ctx)
{
config_t cfg{tls_config_new(), tls_config_free};
if (!cfg)
}
-void accept(struct tls *server_ctx, int client_socket, context_t &ret_ctx)
+void accept(
+ not_null<struct tls *>server_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<struct tls *> ctx, std::vector<char> &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 <CR><LF>.
+ 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
#include <zstring>
#include <not_null>
+#include <non_copiable>
+#include <unique_fd>
+#include <tuple>
#include <tls.h>
+#include <array>
+#include <vector>
#pragma once
namespace transport
{
+
+extern const std::array<char, 2> CRLF;
+
+
/** `struct tls *` smart pointer */
typedef std::unique_ptr<struct tls, decltype(&tls_free)> context_t;
/* Create new TLS context for gemini server */
-void create_gemini_server_ctx(not_null<czstring> cert_file, not_null<czstring> key_file, context_t &ctx);
+void create_server(not_null<czstring> cert_file, not_null<czstring> 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_null<struct tls *>server_ctx,
+ unique_fd &client_socket, // Socket ownership transfer
+ accepted_context &ctx
+);
+
+
+/** Read genimi request and return url */
+bool read_request(not_null<struct tls *> ctx, std::vector<char> &url);
+
+
} // namespace transport
} // namespace vostok
blob - 0a39a90119400afb1373a43976637682d7e806e2
blob + b6b52626e864f4106e5f532d51e7c851a32015be
--- vostokd/vostokd.cc
+++ vostokd/vostokd.cc
#include <error.h>
#include <transport.h>
+
#include <unistd.h>
#include <unique_fd>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
+#include <thread>
+
+
namespace vostok
{
namespace
};
-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<char> 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_null<struct tls *>ctx)
+{
+ error::g_log << "🚀 Vostok server listening..." << std::endl;
for (; ; )
{
if (listen(server_socket, 1024) == -1)
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)
{
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;