commit - 9a6cb9d51e84cd86ea58319a30046fc2f434246d
commit + 917c32a4f0ec162835da762d7dd0afdb11d69392
blob - 860b2a9fb3270c16c3135fc92a26426bbef9a3e7
blob + 49d2831bde02a0492e0c54f8da84be81887302e4
--- Makefile
+++ Makefile
server:
${MAKE} -C vostokd
+
+run_server: server
+ ./vostokd/vostokd -c cert/server.crt -k cert/server.key
blob - b9362c84dc012a844b06927892c48b03ef6817a1
blob + 611e231ad28b76cc73de58ba46028d9a0be60ff0
--- shared/span
+++ shared/span
return (offset < m_count) ? span<element_type>{m_p + offset, m_count - offset} : span<element_type>{};
}
+ element_type &operator[](size_t idx) const
+ {
+ assert(idx < m_count);
+ return m_p[idx];
+ }
+
private:
element_type *m_p;
size_t m_count;
blob - bbfa867bfda0bfeec12c6582edd249a27ade03a0
blob + 786dcf7a7c39c38a3e601a0c8a7d966355dfc76a
--- vostokd/url_normalization.cc
+++ vostokd/url_normalization.cc
#include "url_normalization.h"
#include "cut_null"
+#include "error.h"
+#include <list>
+
namespace vostok
{
+
namespace
{
+
const auto gemini_scheme = cut_null("gemini://");
-} // namespace <unnamed>
+class path_normalization
+{
+public:
+ using path_components_t = std::list< span<const char> >;
-url_normalization_result parse_url(span<char> url, span<char> &path)
+ url_normalization_result operator() (span<char> url_path, zs_url_path_t &zs_url_path);
+
+protected:
+ url_normalization_result on_component();
+ url_normalization_result on_component_ok()
+ {
+ m_inprogress = decltype(m_inprogress){};
+ return url_ok;
+ }
+
+ void fill(zs_url_path_t &zs_url_path) const;
+
+private:
+ path_components_t::value_type m_inprogress;
+ path_components_t m_result;
+};
+
+
+url_normalization_result path_normalization::operator() (span<char> url_path, zs_url_path_t &zs_url_path)
{
+ m_inprogress = path_components_t::value_type{nullptr, 0};
+ m_result.clear();
+
+ for (auto p = url_path.begin(); p != url_path.end(); ++p)
+ {
+ if (*p != '/')
+ {
+ if (m_inprogress.size())
+ m_inprogress = decltype(m_inprogress){m_inprogress.begin(), m_inprogress.size() + 1};
+ else
+ m_inprogress = decltype(m_inprogress){p, 1};
+ continue;
+ }
+
+ const auto parse_result = on_component();
+ if (parse_result != url_ok)
+ return parse_result;
+ }
+ const auto parse_result = on_component();
+ if (parse_result != url_ok)
+ return parse_result;
+
+ fill(zs_url_path);
+ return url_ok;
+}
+
+
+url_normalization_result path_normalization::on_component()
+{
+ if ((m_inprogress.size() == 0) || (m_inprogress.size() == 1 && m_inprogress[0] == '.'))
+ {
+ return on_component_ok();
+ }
+ if (m_inprogress.size() == 2 && m_inprogress[0] == '.' && m_inprogress[1] == '.')
+ {
+ if (m_result.empty())
+ return url_root_traverse;
+
+ m_result.pop_back();
+ return on_component_ok();
+ }
+
+ m_result.push_back(std::move(m_inprogress));
+ return on_component_ok();
+}
+
+void path_normalization::fill(zs_url_path_t &zs_url_path) const
+{
+ auto current = zs_url_path.begin();
+ for (auto it = m_result.cbegin(); it != m_result.cend(); ++it)
+ {
+ if (current != zs_url_path.begin())
+ {
+ // non-first path component: insert separator
+ *current = '/';
+ ++current;
+ }
+ current = std::copy(it->begin(), it->end(), current);
+ }
+ *current = '\0';
+}
+
+} // namespace <unnamed>
+
+
+url_normalization_result parse_url(span<char> url, zs_url_path_t &zs_url_path)
+{
// check and skip scheme
if (url.size() < gemini_scheme.size())
return url_too_short;
if (current == url.end())
url = decltype(url){};
- path = url;
- return url_ok;
+ // normalize '.' and '..'
+ path_normalization normalizer;
+ return normalizer(url, zs_url_path);
}
blob - 9d1800b61158c3a0756ab9deb68803d60e1d92eb
blob + 7cda673779438908d7cb51350167a1a8c9d82c4c
--- vostokd/url_normalization.h
+++ vostokd/url_normalization.h
/** URL normalization */
#include "span"
+#include "gemini.h"
#pragma once
url_root_traverse,
};
+/** Zero-terminated path from gemini URL */
+using zs_url_path_t = std::array<char, gemini::MAX_URL_LENGTH + 1>;
-/** Inplace: extract normalized path from URL */
-url_normalization_result parse_url(span<char> url, span<char> &path);
+/** Extract normalized path from URL as list null-terminated string */
+url_normalization_result parse_url(span<char> url, zs_url_path_t &zs_url_path);
} // namespace vostok
blob - d2bbca5e948ac44e480e5a641162aa6e918408c8
blob + 1feff3e2ae1dc163916656fc132100293d01c78d
--- vostokd/vostokd.cc
+++ vostokd/vostokd.cc
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");
+const char sz_bad_request[] = "Bad request";
+const auto bad_request = cut_null(sz_bad_request);
+
+const char sz_url_too_short[] = "URL is too short";
+const auto url_too_short = cut_null(sz_url_too_short);
+
+const char sz_non_gemini[] = "No proxying to non-Gemini content";
+const auto non_gemini = cut_null(sz_non_gemini);
+
+const char sz_root_traverse[] = "Wrong traverse";
+const auto root_traverse = cut_null(sz_root_traverse);
} // namespace meta
return;
}
- span<char> path_from_url;
- const auto parse_result = parse_url(url, path_from_url);
+ zs_url_path_t zs_url_path;
+ const auto parse_result = parse_url(url, zs_url_path);
switch (parse_result)
{
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);
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);
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);
return;
break;
}
- error::g_log << "Requested URL: \"";
- for (auto c : path_from_url)
- error::g_log << c;
- error::g_log << "\"" << std::endl;
+ error::g_log << "Requested URL: \"" << zs_url_path.data() << "\"" << std::endl;
// > If <META> is an empty string, the MIME type MUST default to "text/gemini; charset=utf-8".