Commit Diff


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 <cassert>
+
+
 #pragma once
 
 
@@ -11,7 +14,7 @@ template <class T>
 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<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)
@@ -30,7 +64,7 @@ bool init()
 }
 
 
-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)
@@ -92,17 +126,47 @@ void create_gemini_server_ctx(not_null<czstring> cert_
 }
 
 
-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
@@ -2,7 +2,12 @@
 
 #include <zstring>
 #include <not_null>
+#include <non_copiable>
+#include <unique_fd>
+#include <tuple>
 #include <tls.h>
+#include <array>
+#include <vector>
 
 
 #pragma once
@@ -13,6 +18,10 @@ namespace vostok
 namespace transport
 {
 
+
+extern const std::array<char, 2> CRLF;
+
+
 /** `struct tls *` smart pointer */
 typedef std::unique_ptr<struct tls, decltype(&tls_free)> context_t;
 
@@ -22,10 +31,45 @@ bool init();
 
 
 /* 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
@@ -2,13 +2,17 @@
 
 #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
@@ -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<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)
@@ -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;