commit b20335632804754482ef930b97f325c08c4ac8fc from: Aleksey Ryndin date: Tue Aug 15 20:15:58 2023 UTC Read file content and return it commit - 529cc6f4bf81cb3c4dc794c56969c93ee40fe333 commit + b20335632804754482ef930b97f325c08c4ac8fc blob - 927d234350bb21ec83c22f006949aa24f2b5e23c blob + 8931ba4f492fc02e35c0967addd746637eaa662b --- Makefile +++ Makefile @@ -9,4 +9,4 @@ server: ${MAKE} -C vostokd run_server: server - ./vostokd/vostokd -c cert/server.crt -k cert/server.key -d static/ + ./vostokd/vostokd -c cert/server.crt -k cert/server.key -f static/ blob - fb1b6382ef772db709bc9de4897bc5b28d551339 blob + a99927eecbfa09bd8a7e7ed7903d4d3205a28c69 --- shared/gemini.cc +++ shared/gemini.cc @@ -13,7 +13,9 @@ const std::array CRLF{'\r', '\n'}; const std::array SPACE{' '}; const status_t STATUS_20_SUCCESS{'2', '0'}; +const status_t STATUS_40_TEMPORARY_FAILURE{'4', '0'}; const status_t STATUS_50_PERMANENT_FAILURE{'5', '0'}; +const status_t STATUS_51_NOT_FOUND{'5', '1'}; const status_t STATUS_53_PROXY_REQUEST_REFUSED{'5', '3'}; const status_t STATUS_59_BAD_REQUEST{'5', '9'}; blob - 2230496a1499e62f60cfa32596d71516e0576f7f blob + 3b9b02c570f7adc25dcbcbb98661afa9c95160af --- shared/gemini.h +++ shared/gemini.h @@ -19,7 +19,9 @@ extern const std::array SPACE; using status_t = std::array; extern const status_t STATUS_20_SUCCESS; +extern const status_t STATUS_40_TEMPORARY_FAILURE; extern const status_t STATUS_50_PERMANENT_FAILURE; +extern const status_t STATUS_51_NOT_FOUND; extern const status_t STATUS_53_PROXY_REQUEST_REFUSED; extern const status_t STATUS_59_BAD_REQUEST; blob - aa8fb179940b64dd6a25670b63d036b0023b888f blob + a07cc088e96481e3b281957882e83d37e0e73818 --- vostokd/command_line_arguments.cc +++ vostokd/command_line_arguments.cc @@ -27,7 +27,7 @@ bool usage(const char *program) error::g_log << "\t-p PORT : TCP/IP port number (1965 by default)" << std::endl; error::g_log << "\t-c FILE : Server certificate file [REQUIRED]" << std::endl; error::g_log << "\t-k FILE : Server key file [REQUIRED]" << std::endl; - error::g_log << "\t-d DIR : Path to data directory [REQUIRED]" << std::endl; + error::g_log << "\t-f PATH : Path to file system data [REQUIRED]" << std::endl; return false; } @@ -39,7 +39,7 @@ bool command_line_arguments::parse_command_line(int ar { int ch; char *p = nullptr; - while ((ch = getopt(argc, argv, "a:p:c:k:d:")) != -1) { + while ((ch = getopt(argc, argv, "a:p:c:k:f:")) != -1) { switch (ch) { case 'a': m_addr = optarg; @@ -59,8 +59,8 @@ bool command_line_arguments::parse_command_line(int ar case 'k': m_key_file = optarg; break; - case 'd': - m_directory.reset(open(optarg, O_RDONLY | O_DIRECTORY)); + case 'f': + m_directory.reset(open(optarg, O_RDONLY)); if (!m_directory) { error::occurred( blob - 1feff3e2ae1dc163916656fc132100293d01c78d blob + 72390f99d42cc57e7c7fba2e9f80a4437334c892 --- vostokd/vostokd.cc +++ vostokd/vostokd.cc @@ -7,12 +7,14 @@ #include "cut_null" #include "unique_fd" +#include +#include #include #include #include #include - +#include namespace vostok @@ -43,10 +45,16 @@ 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); + +const char sz_not_found[] = "Not found"; +const auto not_found = cut_null(sz_not_found); + +const char sz_temporary_failure[] = "Temporary failure"; +const auto temporary_failure = cut_null(sz_temporary_failure); } // namespace meta -void client_thread(transport::accepted_context::value raw_value) +void client_thread(transport::accepted_context::value raw_value, int directory_fd) { const transport::accepted_context ctx{raw_value}; assert(ctx); @@ -80,17 +88,90 @@ void client_thread(transport::accepted_context::value break; } - error::g_log << "Requested URL: \"" << zs_url_path.data() << "\"" << std::endl; + const unique_fd opened_file{openat(directory_fd, zs_url_path.data(), O_RDONLY)}; + if (!opened_file) + { + const auto error_code = errno; + error::occurred( + [&zs_url_path] + { + error::g_log << "Open file \"" << zs_url_path.data() << "\""; + }, + error::print{error_code} + ); + switch (error_code) + { + case ENOTDIR: + case ENOENT: + case EACCES: + case ELOOP: + case EISDIR: + case ENXIO: + transport::send_response(ctx.get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::not_found); + break; + default: + transport::send_response(ctx.get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::temporary_failure); + break; + } + return; + } + struct stat sb{}; + if (fstat(opened_file.get(), &sb) == -1) + { + error::occurred( + [&zs_url_path] + { + error::g_log << "Stat file \"" << zs_url_path.data() << "\""; + }, + error::print{} + ); + transport::send_response(ctx.get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::temporary_failure); + return; + } + if (S_ISDIR(sb.st_mode)) + { + error::occurred( + [&zs_url_path] + { + error::g_log << "Open file \"" << zs_url_path.data() << "\""; + }, + error::print{EISDIR} + ); + transport::send_response(ctx.get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::not_found); + return; + } // > If 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, {}); - static const char content[] = "# Vostok server\r\n\r\n...work-in-progress..."; - transport::send(ctx.get_ctx(), cut_null(content)); + for (; ; ) + { + const auto ret = read(opened_file.get(), buffer.data(), buffer.size()); + if (ret == -1) + { + error::occurred( + [&zs_url_path] + { + error::g_log << "Read file \"" << zs_url_path.data() << "\""; + }, + error::print{} + ); + return; + } + const auto readed = static_cast(ret); + if (readed) + { + if (!transport::send(ctx.get_ctx(), span{buffer.data(), readed})) + return; + } + if (readed < buffer.size()) + break; + } + error::g_log << "20 " << "\"" << zs_url_path.data() << "\"" << std::endl; } -bool server_loop(int server_socket, not_nullctx) +bool server_loop(int server_socket, not_nullctx, int directory_fd) { error::g_log << "🚀 Vostok server listening..." << std::endl; for (; ; ) @@ -117,7 +198,7 @@ bool server_loop(int server_socket, not_null