Commit Diff


commit - c966293b4ff0b2a267372fb1fb1229089ecb3145
commit + 4a354f2fcb6d0403fad19c421b6c2839dddfb227
blob - b7a75a85a349403c66a00ae0978b5c7e78d38f4c
blob + f0cd56105db826dd94ffb4065683264163df99da
--- vostok/transport.cc
+++ vostok/transport.cc
@@ -3,7 +3,10 @@
 #include "error.h"
 #include "transport.h"
 
+#include <sys/socket.h>
+#include <netinet/in.h>
 
+
 namespace vostok
 {
 namespace transport
@@ -120,20 +123,27 @@ void create_server(not_null<czstring> cert_file, not_n
 }
 
 
-void accept(
-    not_null<struct tls *>server_ctx,
-    unique_fd &client_socket,           // Socket ownership transfer
-    accepted_context &ctx
-)
+accepted_client::accepted_client(int server_socket, struct tls *server_ctx)
 {
-    struct tls *client_ctx = nullptr;
-    if (tls_accept_socket(server_ctx, &client_ctx, client_socket.get()) == -1)
+    struct sockaddr_in addr{};
+    socklen_t addrlen = sizeof(addr);
+
+    m_fd.reset(accept(server_socket, (struct sockaddr *)&addr, &addrlen));
+    if (!m_fd)
     {
+        error::occurred("Accept socket", error::print{});
+        return;
+    }
+
+    struct tls *ctx = nullptr;
+    if (tls_accept_socket(server_ctx, &ctx, m_fd.get()) == -1)
+    {
         error::occurred("TLS accept", error::none{});
-        ctx.reset();
         return;
     }
-    ctx.reset(accepted_context::value{client_socket.release(), client_ctx});
+
+    m_ctx.reset(ctx);
+    m_is_accepted = true;
 }
 
 
blob - ca021031a6a36547413206e674339b82f4dd03f4
blob + b1c2577de7e8aff3a85580b7e0fa96d2226f98cb
--- vostok/transport.h
+++ vostok/transport.h
@@ -19,55 +19,31 @@ namespace transport
 typedef std::unique_ptr<struct tls, decltype(&tls_free)> context_t;
 
 
-/** Pair: `struct tls *` pointer and file descriptor as smart pointer */
-class accepted_context : non_copiable
+/** 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 */
+class accepted_client
 {
 public:
-    struct value
-    {
-        int m_fd;
-        struct tls *m_ctx;
-        value(int fd = -1, struct tls *ctx = nullptr) : m_fd{fd}, m_ctx{ctx} {}
-    };
+    accepted_client(int server_socket, struct tls *server_ctx);
+    bool is_accepted() const { return m_is_accepted; }
 
-    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(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; }
-
 private:
     unique_fd m_fd;
     context_t m_ctx{nullptr, tls_free};
+    bool m_is_accepted{false};
 };
 
 
-/** 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,
-    unique_fd &client_socket,           // Socket ownership transfer
-    accepted_context &ctx
-);
-
-
 /** Read genimi request and return url (empty url - error) */
 span<const char> read_request(not_null<struct tls *> ctx, std::array<char, gemini::MAX_REQUEST_LENGTH> &buffer);
 
blob - aeb7bca4f490ff9d8be8d2958cef7d6a1063092c
blob + 5e4f404de41c1332628cda9dfd8386191e71d32f
--- vostok/vostok.cc
+++ vostok/vostok.cc
@@ -41,16 +41,16 @@ const auto temporary_failure = cut_null(sz_temporary_f
 }   // namespace meta
 
 
-void client_thread(transport::accepted_context::value raw_value, int directory_fd)
+void client_thread(const transport::accepted_client *accepted_client, int directory_fd)
 {
-    const transport::accepted_context ctx{raw_value};
-    assert(ctx);
+    assert(accepted_client);
+    std::unique_ptr<const transport::accepted_client> accepted_client_deleter{accepted_client};
 
     std::array<char, gemini::MAX_REQUEST_LENGTH> buffer;
-    auto url = transport::read_request(ctx.get_ctx(), buffer);
+    auto url = transport::read_request(accepted_client->get_ctx(), buffer);
     if (!url.size())
     {
-        transport::send_response(ctx.get_ctx(), gemini::STATUS_59_BAD_REQUEST, meta::bad_request);
+        transport::send_response(accepted_client->get_ctx(), gemini::STATUS_59_BAD_REQUEST, meta::bad_request);
         return;
     }
 
@@ -60,15 +60,15 @@ void client_thread(transport::accepted_context::value 
     {
     case url_too_short:
         error::occurred("parse URL", []{error::g_log << meta::sz_url_too_short << "." << std::endl;});
-        transport::send_response(ctx.get_ctx(), gemini::STATUS_59_BAD_REQUEST, meta::url_too_short);
+        transport::send_response(accepted_client->get_ctx(), gemini::STATUS_59_BAD_REQUEST, meta::url_too_short);
         return;
     case url_non_gemini:
         error::occurred("parse URL", []{error::g_log << meta::sz_non_gemini << "." << std::endl;});
-        transport::send_response(ctx.get_ctx(), gemini::STATUS_53_PROXY_REQUEST_REFUSED, meta::non_gemini);
+        transport::send_response(accepted_client->get_ctx(), gemini::STATUS_53_PROXY_REQUEST_REFUSED, meta::non_gemini);
         return;
     case url_root_traverse:
         error::occurred("parse URL", []{error::g_log << meta::sz_root_traverse << "." << std::endl;});
-        transport::send_response(ctx.get_ctx(), gemini::STATUS_50_PERMANENT_FAILURE, meta::root_traverse);
+        transport::send_response(accepted_client->get_ctx(), gemini::STATUS_50_PERMANENT_FAILURE, meta::root_traverse);
         return;
 
     case url_ok:
@@ -80,10 +80,10 @@ void client_thread(transport::accepted_context::value 
     switch (open_file_result)
     {
     case file_not_found:
-        transport::send_response(ctx.get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::not_found);
+        transport::send_response(accepted_client->get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::not_found);
         return;
     case file_opening_error:
-        transport::send_response(ctx.get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::temporary_failure);
+        transport::send_response(accepted_client->get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::temporary_failure);
         return;
 
     case file_opened:
@@ -92,7 +92,7 @@ void client_thread(transport::accepted_context::value 
     }
 
     // > If <META> is an empty string, the MIME type MUST default to "text/gemini; charset=utf-8".
-    transport::send_response(ctx.get_ctx(), gemini::STATUS_20_SUCCESS, {});
+    transport::send_response(accepted_client->get_ctx(), gemini::STATUS_20_SUCCESS, {});
     for (; ; )
     {
         const auto ret = read(opened_file.get(), buffer.data(), buffer.size());
@@ -110,7 +110,7 @@ void client_thread(transport::accepted_context::value 
         const auto readed = static_cast<size_t>(ret);
         if (readed)
         {
-            if (!transport::send(ctx.get_ctx(), span<const char>{buffer.data(), readed}))
+            if (!transport::send(accepted_client->get_ctx(), span<const char>{buffer.data(), readed}))
                 return;
         }
         if (readed < buffer.size())
@@ -131,36 +131,28 @@ bool server_loop(int server_socket, not_null<struct tl
             return false;
         }
 
-        struct sockaddr_in addr{};
-        socklen_t addrlen = sizeof(addr);
-        unique_fd client_socket{accept(server_socket, (struct sockaddr *)&addr, &addrlen)};
-        if (!client_socket)
+        std::unique_ptr<const transport::accepted_client> accepted_client{
+            new transport::accepted_client(server_socket, ctx)
+        };
+        if (accepted_client->is_accepted())
         {
-            error::occurred("Accept socket", error::print{});
-            continue;
+            try
+            {
+                std::thread{client_thread, accepted_client.get(), directory_fd}.detach();
+                accepted_client.release(); // move ownership to thread
+            }
+            catch (const std::system_error &e)
+            {
+                error::occurred(
+                    "Create client thread",
+                    [&e]
+                    {
+                        error::g_log << "Error: " << std::dec <<  e.code() 
+                                     << ". " << e.what()  << std::endl;
+                    }
+                );
+            }
         }
-
-        transport::accepted_context client_ctx;
-        transport::accept(ctx, client_socket, client_ctx);
-        if (!client_ctx)
-            continue;
-
-        try
-        {
-            std::thread{client_thread, client_ctx.get(), directory_fd}.detach();
-            client_ctx.release(); // move ownership to thread
-        }
-        catch (const std::system_error &e)
-        {
-            error::occurred(
-                "Create client thread",
-                [&e]
-                {
-                    error::g_log << "Error: " << std::dec <<  e.code() 
-                                 << ". " << e.what()  << std::endl;
-                }
-            );
-        }
     }
     return true;
 }