commit - c966293b4ff0b2a267372fb1fb1229089ecb3145
commit + 4a354f2fcb6d0403fad19c421b6c2839dddfb227
blob - b7a75a85a349403c66a00ae0978b5c7e78d38f4c
blob + f0cd56105db826dd94ffb4065683264163df99da
--- vostok/transport.cc
+++ vostok/transport.cc
#include "error.h"
#include "transport.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+
namespace vostok
{
namespace transport
}
-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
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
} // 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;
}
{
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:
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:
}
// > 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());
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())
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;
}