Commit Diff


commit - 59d5920624133077e335a537038eca69788aed94
commit + 9a6cb9d51e84cd86ea58319a30046fc2f434246d
blob - /dev/null
blob + e124e72c5414367ddacc70318254ba0945a59634 (mode 644)
--- /dev/null
+++ shared/cut_null
@@ -0,0 +1,20 @@
+/** Remove null terminating character */
+
+
+#pragma once
+
+
+namespace vostok
+{
+
+
+/** Remove null terminating character and return as span */
+template <size_t N>
+constexpr span<const char> cut_null(const char (&arr)[N])
+{
+    static_assert(N > 0, "!(N > 0)");
+    return span<const char>{arr, N - 1};
+}
+
+
+}   // vostok
blob - ac1dcb2ed0c78c1415494b53d0518659100a06fb
blob + fb1b6382ef772db709bc9de4897bc5b28d551339
--- shared/gemini.cc
+++ shared/gemini.cc
@@ -13,9 +13,8 @@ const std::array<char, 2> CRLF{'\r', '\n'};
 const std::array<char, 1> SPACE{' '};
 
 const status_t STATUS_20_SUCCESS{'2', '0'};
-
+const status_t STATUS_50_PERMANENT_FAILURE{'5', '0'};
 const status_t STATUS_53_PROXY_REQUEST_REFUSED{'5', '3'};
-
 const status_t STATUS_59_BAD_REQUEST{'5', '9'};
 
 
blob - 350150182502bef33d71ddafd969c9b088bfc363
blob + 2230496a1499e62f60cfa32596d71516e0576f7f
--- shared/gemini.h
+++ shared/gemini.h
@@ -19,6 +19,7 @@ extern const std::array<char, 1> SPACE;
 
 using status_t = std::array<char, 2>;
 extern const status_t STATUS_20_SUCCESS;
+extern const status_t STATUS_50_PERMANENT_FAILURE;
 extern const status_t STATUS_53_PROXY_REQUEST_REFUSED;
 extern const status_t STATUS_59_BAD_REQUEST;
 
blob - 08a5d754c5e4e8f433a230fce92b6a1438a8ade7
blob + b9362c84dc012a844b06927892c48b03ef6817a1
--- shared/span
+++ shared/span
@@ -1,5 +1,8 @@
 /** Simply std::span implementation for C++11 */
 
+#include <cassert>
+#include <array>
+
 #pragma once
 
 
blob - 4f075e2d60ec499d9d3872b8bc76526e840a77b8
blob + d80d60dae0f1e151f0e6f93adba30d597190355f
--- shared/transport.cc
+++ shared/transport.cc
@@ -1,7 +1,7 @@
 /** Wrap libtls for gemini protocol */
 
-#include <error.h>
-#include <transport.h>
+#include "error.h"
+#include "transport.h"
 
 
 namespace vostok
blob - 9f0cf3c93d468653b56bbdb97def9bd47655f70a
blob + c842f06d2f8051e40b5b6eafeeb763195a489e99
--- shared/transport.h
+++ shared/transport.h
@@ -1,11 +1,10 @@
 /** Wrap libtls for gemini protocol */
 
-#include <zstring>
-#include <not_null>
-#include <non_copiable>
-#include <unique_fd>
-#include <span>
-
+#include "zstring"
+#include "not_null"
+#include "non_copiable"
+#include "unique_fd"
+#include "span"
 #include "gemini.h"
 
 #include <tls.h>
blob - 9d3cb21091bc3851c1517677466510156fa16d9a
blob + 673540d1db66509e57e5ec76656eeadd308f3a3f
--- vostokd/Makefile
+++ vostokd/Makefile
@@ -9,14 +9,17 @@ HXXFILES        += ${.PATH}../shared/unique_fd
 HXXFILES        += ${.PATH}../shared/non_copiable
 HXXFILES        += ${.PATH}../shared/unique_fd
 HXXFILES        += ${.PATH}../shared/span
+HXXFILES	+= ${.PATH}../shared/cut_null
 CXXFILES	+= ${.PATH}../shared/transport.cc
 HXXFILES	+= ${.PATH}../shared/transport.h
 CXXFILES	+= ${.PATH}../shared/error.cc
 HXXFILES	+= ${.PATH}../shared/error.h
-CXXFILES	+= command_line_arguments.cc
-HXXFILES	+= command_line_arguments.h
 CXXFILES	+= ${.PATH}../shared/gemini.cc
 HXXFILES	+= ${.PATH}../shared/gemini.h
+CXXFILES	+= command_line_arguments.cc
+HXXFILES	+= command_line_arguments.h
+CXXFILES	+= url_normalization.cc
+HXXFILES	+= url_normalization.h
 
 vostokd: ${CXXFILES} ${HXXFILES}
 	${CXX} ${CXXFLAGS} ${CXXFILES} -o vostokd
blob - 2b52c558ec36360add80c7d61fc57b7506b75a58
blob + d2bbca5e948ac44e480e5a641162aa6e918408c8
--- vostokd/vostokd.cc
+++ vostokd/vostokd.cc
@@ -3,8 +3,10 @@
 #include "error.h"
 #include "transport.h"
 #include "command_line_arguments.h"
+#include "url_normalization.h"
+#include "cut_null"
+#include "unique_fd"
 
-#include <unique_fd>
 #include <sys/socket.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
@@ -33,11 +35,10 @@ namespace meta
 const auto bad_request = cut_null("Bad request");
 const auto url_too_short = cut_null("URL is too short");
 const auto non_gemini = cut_null("No proxying to non-Gemini content");
+const auto root_traverse = cut_null("Wrong traverse");
 }   // namespace meta
 
-const auto gemini_scheme = cut_null("gemini://");
 
-
 void client_thread(transport::accepted_context::value raw_value)
 {
     const transport::accepted_context ctx{raw_value};
@@ -51,35 +52,26 @@ void client_thread(transport::accepted_context::value 
         return;
     }
 
-    // check and skip scheme
-    if (url.size() < gemini_scheme.size())
+    span<char> path_from_url;
+    const auto parse_result = parse_url(url, path_from_url);
+    switch (parse_result)
     {
+    case url_too_short:
         transport::send_response(ctx.get_ctx(), gemini::STATUS_59_BAD_REQUEST, meta::url_too_short);
         return;
-    }
-    if (!std::equal(gemini_scheme.begin(), gemini_scheme.end(), url.begin()))
-    {
+    case url_non_gemini:
         transport::send_response(ctx.get_ctx(), gemini::STATUS_53_PROXY_REQUEST_REFUSED, meta::non_gemini);
         return;
-    }
-    url = url.subspan(gemini_scheme.size());
+    case url_root_traverse:
+        transport::send_response(ctx.get_ctx(), gemini::STATUS_50_PERMANENT_FAILURE, meta::root_traverse);
+        return;
 
-    // skip domain[:port]
-    const char *current = url.begin();
-    for (; current != url.end(); ++current)
-    {
-        if (*current == '/')
-        {
-            const auto skip_len = (current + 1) - url.begin();
-            url = url.subspan(skip_len);
-            break;
-        }
+    case url_ok:
+        break;
     }
-    if (current == url.end())
-        url = decltype(url){};
 
     error::g_log << "Requested URL: \"";
-    for (auto c : url)
+    for (auto c : path_from_url)
         error::g_log << c;
     error::g_log << "\"" << std::endl;
 
blob - /dev/null
blob + bbfa867bfda0bfeec12c6582edd249a27ade03a0 (mode 644)
--- /dev/null
+++ vostokd/url_normalization.cc
@@ -0,0 +1,44 @@
+/** URL normalization */
+
+#include "url_normalization.h"
+#include "cut_null"
+
+
+namespace vostok
+{
+namespace
+{
+const auto gemini_scheme = cut_null("gemini://");
+}   // namespace <unnamed>
+
+
+
+url_normalization_result parse_url(span<char> url, span<char> &path)
+{
+    // check and skip scheme
+    if (url.size() < gemini_scheme.size())
+        return url_too_short;
+    if (!std::equal(gemini_scheme.begin(), gemini_scheme.end(), url.begin()))
+        return url_non_gemini;
+    url = url.subspan(gemini_scheme.size());
+
+    // skip domain[:port]
+    const char *current = url.begin();
+    for (; current != url.end(); ++current)
+    {
+        if (*current == '/')
+        {
+            const auto skip_len = (current + 1) - url.begin();
+            url = url.subspan(skip_len);
+            break;
+        }
+    }
+    if (current == url.end())
+        url = decltype(url){};
+
+    path = url;
+    return url_ok;
+}
+
+
+}   // namespace vostok
blob - /dev/null
blob + 9d1800b61158c3a0756ab9deb68803d60e1d92eb (mode 644)
--- /dev/null
+++ vostokd/url_normalization.h
@@ -0,0 +1,27 @@
+/** URL normalization */
+
+#include "span"
+
+
+#pragma once
+
+
+namespace vostok
+{
+
+
+enum url_normalization_result
+{
+    url_ok,
+
+    url_too_short,
+    url_non_gemini,
+    url_root_traverse,
+};
+
+
+/** Inplace: extract normalized path from URL */
+url_normalization_result parse_url(span<char> url, span<char> &path);
+
+
+}   // namespace vostok