commit 744cd5d55114a8cc73324a86d0e654c8115b049b from: Aleksey Ryndin date: Tue Feb 13 20:28:51 2024 UTC Fix: redirect "dir" to "dir/" commit - aedb452477c60551dc13c223da74f0878488caeb commit + 744cd5d55114a8cc73324a86d0e654c8115b049b blob - 49612ff96d3235a80905edc5971fcfb2421069b8 blob + 353cef1cf625b5329877f12bc183fb59a1ccf013 --- tests/test_open_file.cc +++ tests/test_open_file.cc @@ -28,106 +28,158 @@ TEST_START(test_open_file) struct stat sb1{}; IS_TRUE_ERRNO(fstat(dir.get(), &sb1) != -1, "Stat file " << ss.str().c_str()) + { + UniqueFd opened_fd; + IS_TRUE(open_file::open(dir.get(), "", opened_fd) == open_file::SUCCESS_DIRECTORY); - std::string file_name; + UniqueFd not_opened_fd; + IS_TRUE(open_file::open(opened_fd.get(), "index.gmi", not_opened_fd) == open_file::ERROR_NOT_EXIST); + struct stat sb2{}; + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); + IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); + } { UniqueFd opened_fd; - file_name = ""; - IS_TRUE(open_file::open(dir.get(), file_name, opened_fd) == open_file::OPENED_DIRECTORY); + IS_TRUE(open_file::open(dir.get(), "/", opened_fd) == open_file::SUCCESS_DIRECTORY); + UniqueFd not_opened_fd; + IS_TRUE(open_file::open(opened_fd.get(), "index.gmi", not_opened_fd) == open_file::ERROR_NOT_EXIST); + struct stat sb2{}; IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } { UniqueFd opened_fd; - file_name = "/"; - IS_TRUE(open_file::open(dir.get(), file_name, opened_fd) == open_file::OPENED_DIRECTORY); + IS_TRUE(open_file::open(dir.get(), ".", opened_fd) == open_file::SUCCESS_DIRECTORY); + UniqueFd not_opened_fd; + IS_TRUE(open_file::open(opened_fd.get(), "index.gmi", not_opened_fd) == open_file::ERROR_NOT_EXIST); + struct stat sb2{}; IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } + { UniqueFd opened_fd; - file_name = "non-existent-file"; - IS_TRUE(open_file::open(dir.get(), file_name, opened_fd) == open_file::NOT_FOUND); + IS_TRUE(open_file::open(dir.get(), "non-existent-file", opened_fd) == open_file::ERROR_NOT_EXIST); } { UniqueFd opened_fd; - file_name = "non-existent-dir/file"; - IS_TRUE(open_file::open(dir.get(), file_name, opened_fd) == open_file::NOT_FOUND); + IS_TRUE(open_file::open(dir.get(), "non-existent-dir/file", opened_fd) == open_file::ERROR_NOT_EXIST); } const UniqueFd index_gmi{openat(dir.get(), "index.gmi", O_RDONLY | O_CREAT | O_EXCL, S_IRWXU)}; IS_TRUE_ERRNO(index_gmi, "Create file " << ss.str().c_str() << "/index.gmi"); IS_TRUE_ERRNO(fstat(index_gmi.get(), &sb1) != -1, "Stat file " << ss.str().c_str() << "/index.gmi"); -/* + + { - OpenedFile opened_file; - file_name = ""; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::OPENED); - IS_TRUE(opened_file.get_path() == "index.gmi"); + UniqueFd opened_dir; + IS_TRUE(open_file::open(dir.get(), "", opened_dir) == open_file::SUCCESS_DIRECTORY); + UniqueFd opened_fd; + IS_TRUE(open_file::open(opened_dir.get(), "index.gmi", opened_fd) == open_file::SUCCESS_FILE); + struct stat sb2{}; - IS_TRUE_ERRNO(fstat(opened_file.get_fd(), &sb2) != -1, "Stat file " << ss.str().c_str() << "/index.gmi"); + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } + { + UniqueFd opened_dir; + IS_TRUE(open_file::open(dir.get(), "/", opened_dir) == open_file::SUCCESS_DIRECTORY); + UniqueFd opened_fd; + IS_TRUE(open_file::open(opened_dir.get(), "index.gmi", opened_fd) == open_file::SUCCESS_FILE); + + struct stat sb2{}; + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); + IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); + } { - OpenedFile opened_file; - file_name = "index.gmi"; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::OPENED); - IS_TRUE(opened_file.get_path() == "index.gmi"); + UniqueFd opened_dir; + IS_TRUE(open_file::open(dir.get(), ".", opened_dir) == open_file::SUCCESS_DIRECTORY); + + UniqueFd opened_fd; + IS_TRUE(open_file::open(opened_dir.get(), "index.gmi", opened_fd) == open_file::SUCCESS_FILE); + struct stat sb2{}; - IS_TRUE_ERRNO(fstat(opened_file.get_fd(), &sb2) != -1, "Stat file " << ss.str().c_str() << "/index.gmi"); + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } + { + UniqueFd opened_fd; + IS_TRUE(open_file::open(dir.get(), "index.gmi", opened_fd) == open_file::SUCCESS_FILE); + + struct stat sb2{}; + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); + IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); + } + IS_TRUE_ERRNO(mkdirat(dir.get(), "subdir", S_IRWXU) != -1, "Create directory " << ss.str().c_str() << "/subdir"); + const UniqueFd subdir{openat(dir.get(), "subdir", O_RDONLY | O_DIRECTORY)}; + IS_TRUE_ERRNO(subdir, "Open directory " << ss.str().c_str() << "/subdir"); + IS_TRUE_ERRNO(fstat(subdir.get(), &sb1) != -1, "Stat file " << ss.str().c_str()) + { - OpenedFile opened_file; - file_name = "subdir"; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::NOT_FOUND); + UniqueFd opened_fd; + IS_TRUE(open_file::open(dir.get(), "subdir", opened_fd) == open_file::SUCCESS_DIRECTORY); + + UniqueFd not_opened_fd; + IS_TRUE(open_file::open(opened_fd.get(), "index.gmi", not_opened_fd) == open_file::ERROR_NOT_EXIST); + + struct stat sb2{}; + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); + IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } { - OpenedFile opened_file; - file_name = "subdir/"; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::NOT_FOUND); + UniqueFd opened_fd; + IS_TRUE(open_file::open(dir.get(), "subdir/", opened_fd) == open_file::SUCCESS_DIRECTORY); + + UniqueFd not_opened_fd; + IS_TRUE(open_file::open(opened_fd.get(), "index.gmi", not_opened_fd) == open_file::ERROR_NOT_EXIST); + + struct stat sb2{}; + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); + IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } - const UniqueFd subdir{openat(dir.get(), "subdir", O_RDONLY | O_DIRECTORY)}; - IS_TRUE_ERRNO(subdir, "Open directory " << ss.str().c_str() << "/subdir"); const UniqueFd sub_index_gmi{openat(subdir.get(), "index.gmi", O_RDONLY | O_CREAT | O_EXCL, S_IRWXU)}; IS_TRUE_ERRNO(sub_index_gmi, "Create file " << ss.str().c_str() << "/subdir/index.gmi"); IS_TRUE_ERRNO(fstat(sub_index_gmi.get(), &sb1) != -1, "Stat file " << ss.str().c_str() << "/subdir/index.gmi"); { - OpenedFile opened_file; - file_name = "subdir"; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::OPENED); - IS_TRUE(opened_file.get_path() == "index.gmi"); + UniqueFd opened_dir; + IS_TRUE(open_file::open(dir.get(), "subdir", opened_dir) == open_file::SUCCESS_DIRECTORY); + + UniqueFd opened_fd; + IS_TRUE(open_file::open(opened_dir.get(), "index.gmi", opened_fd) == open_file::SUCCESS_FILE); + struct stat sb2{}; - IS_TRUE_ERRNO(fstat(opened_file.get_fd(), &sb2) != -1, "Stat file " << ss.str().c_str() << "/subdir/index.gmi"); + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } { - OpenedFile opened_file; - file_name = "subdir/"; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::OPENED); - IS_TRUE(opened_file.get_path() == "index.gmi"); + UniqueFd opened_dir; + IS_TRUE(open_file::open(dir.get(), "subdir/", opened_dir) == open_file::SUCCESS_DIRECTORY); + + UniqueFd opened_fd; + IS_TRUE(open_file::open(opened_dir.get(), "index.gmi", opened_fd) == open_file::SUCCESS_FILE); + struct stat sb2{}; - IS_TRUE_ERRNO(fstat(opened_file.get_fd(), &sb2) != -1, "Stat file " << ss.str().c_str() << "/subdir/index.gmi"); + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } + { - OpenedFile opened_file; - file_name = "subdir/index.gmi"; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::OPENED); - IS_TRUE(opened_file.get_path() == "subdir/index.gmi"); + UniqueFd opened_fd; + IS_TRUE(open_file::open(dir.get(), "subdir/index.gmi", opened_fd) == open_file::SUCCESS_FILE); + struct stat sb2{}; - IS_TRUE_ERRNO(fstat(opened_file.get_fd(), &sb2) != -1, "Stat file " << ss.str().c_str() << "/subdir/index.gmi"); + IS_TRUE_ERRNO(fstat(opened_fd.get(), &sb2) != -1, "Stat file " << ss.str().c_str()); IS_TRUE((sb1.st_dev == sb2.st_dev) && (sb1.st_ino == sb2.st_ino)); } @@ -135,15 +187,13 @@ TEST_START(test_open_file) IS_TRUE_ERRNO(eperm, "Create file " << ss.str().c_str() << "/subdir/index.gmi"); { - OpenedFile opened_file; - file_name = "subdir/EPERM.gmi"; - IS_TRUE(opened_file.open(dir.get(), file_name) == OpenedFile::OPENING_ERROR); + UniqueFd opened_fd; + IS_TRUE(open_file::open(dir.get(), "subdir/EPERM.gmi", opened_fd) == open_file::ERROR); } IS_TRUE_ERRNO(unlinkat(subdir.get(), "EPERM.gmi", 0) != -1, "Remove file " << ss.str().c_str() << "/subdir/EPERM.gmi"); - IS_TRUE_ERRNO(unlinkat(subdir.get(), "index.gmi", 0) != -1, "Remove file " << ss.str().c_str() << "/subdir/index.gmi"); + IS_TRUE_ERRNO(unlinkat(subdir.get(), "index.gmi", 0) != -1, "Remove file " << ss.str().c_str() << "/subdir/index.gmi"); IS_TRUE_ERRNO(unlinkat(dir.get(), "subdir", AT_REMOVEDIR) != -1, "Remove directory " << ss.str().c_str() << "/subdir"); -*/ IS_TRUE_ERRNO(unlinkat(dir.get(), "index.gmi", 0) != -1, "Remove file " << ss.str().c_str() << "/index.gmi"); IS_TRUE_ERRNO(rmdir(ss.str().c_str()) != -1, "Remove directory " << ss.str().c_str()); TEST_END() blob - b5b7f57ed4ca0a2f5dd7b6298a98b2c28431e105 blob + 7f087caa76e95ee647bee77a1c0d230aad3f151a --- vostok/gemini.cc +++ vostok/gemini.cc @@ -12,6 +12,7 @@ const std::array CRLF{'\r', '\n'}; const std::array SPACE{' '}; const Status STATUS_20_SUCCESS{'2', '0'}; +const Status STATUS_31_REDIRECT_PERMANENT{'3', '1'}; const Status STATUS_40_TEMPORARY_FAILURE{'4', '0'}; const Status STATUS_50_PERMANENT_FAILURE{'5', '0'}; const Status STATUS_51_NOT_FOUND{'5', '1'}; blob - b2961666037e48305f30dad7d0e7aec54c6ab38f blob + cc773ff8a5e870b10d30c9be82bc14f75c33ec24 --- vostok/gemini.h +++ vostok/gemini.h @@ -19,6 +19,7 @@ extern const std::array SPACE; using Status = std::array; extern const Status STATUS_20_SUCCESS; +extern const Status STATUS_31_REDIRECT_PERMANENT; extern const Status STATUS_40_TEMPORARY_FAILURE; extern const Status STATUS_50_PERMANENT_FAILURE; extern const Status STATUS_51_NOT_FOUND; blob - 6b9722c01a9c7d891f6f144c06294b9746b0f490 blob + 446f09e8e1c185b1f0dfc5b60b0217c4db190f41 --- vostok/open_file.cc +++ vostok/open_file.cc @@ -9,11 +9,12 @@ namespace vostok { - namespace open_file { +namespace +{ -static + const char *get_pathname(const std::string &path) { if (path.empty() || (path.size() == 1 && path[0] == '/')) @@ -23,6 +24,10 @@ const char *get_pathname(const std::string &path) return path.c_str(); } + +} // namespace + + Result open( /* in */ int directory_fd, /* in */ const std::string &path, @@ -40,7 +45,7 @@ Result open( }, error::Print{error_code} ); - return (error_code == ENOENT) ? NOT_FOUND : OPENING_ERROR; + return (error_code == ENOENT) ? ERROR_NOT_EXIST : ERROR; } struct stat sb{}; if (fstat(opened_fd.get(), &sb) == -1) @@ -52,62 +57,11 @@ Result open( }, error::Print{} ); - return OPENING_ERROR; + return ERROR; } - return S_ISDIR(sb.st_mode) ? OPENED_DIRECTORY : OPENED; + return S_ISDIR(sb.st_mode) ? SUCCESS_DIRECTORY : SUCCESS_FILE; } } // namespace open_file - - -namespace open_directory -{ - - -const std::string g_index_gmi{"index.gmi"}; - - -Result open( - /* in */ int directory_fd, - /* out */ UniqueFd &opened_fd -) -{ - opened_fd.reset(openat(directory_fd, g_index_gmi.c_str(), O_RDONLY)); - if (!opened_fd) - { - const auto error_code = errno; - error::occurred( - [] - { - error::g_log << "Open file \"" << g_index_gmi << "\""; - }, - error::Print{error_code} - ); - return (error_code == ENOENT) ? NOT_FOUND : OPENING_ERROR; - } - struct stat sb{}; - if (fstat(opened_fd.get(), &sb) == -1) - { - error::occurred( - [] - { - error::g_log << "Stat file \"" << g_index_gmi << "\""; - }, - error::Print{} - ); - return OPENING_ERROR; - } - if (S_ISDIR(sb.st_mode)) - { - opened_fd.reset(); - return NOT_FOUND; - } - return OPENED; -} - - -} // namespace open_directory - - } // namespace vostok blob - 1b9e6dcf59466e29a5c2269d3c64da36ed8f34c2 blob + 65b466d343ad662d961706df5f678d590d7e0bfd --- vostok/open_file.h +++ vostok/open_file.h @@ -8,18 +8,17 @@ namespace vostok { - namespace open_file { enum Result { - OPENED, - OPENED_DIRECTORY, + SUCCESS_FILE, + SUCCESS_DIRECTORY, - NOT_FOUND, - OPENING_ERROR, + ERROR_NOT_EXIST, + ERROR, }; Result open( @@ -30,28 +29,4 @@ Result open( } // namespace open_file - - -namespace open_directory -{ - - -enum Result -{ - OPENED, - - NOT_FOUND, - OPENING_ERROR, -}; - -Result open( - /* in */ int directory_fd, - /* out */ UniqueFd &opened_fd -); - -extern const std::string g_index_gmi; - - -} // namespace open_directory - } // namespace vostok blob - df7bf0dab186cee47db6271b9a9a8be91a51a3b5 blob + 5523241a2da909a0eb343c9dcfcf1bfc999b9e49 --- vostok/vostok.cc +++ vostok/vostok.cc @@ -30,9 +30,12 @@ const std::string NON_GEMINI{"No proxying to non-Gemin const std::string ROOT_TRAVERSE{"Wrong traverse"}; const std::string NOT_FOUND{"Not found"}; const std::string TEMPORARY_FAILURE{"Temporary failure"}; +const std::string ROOT{"/"}; } // namespace meta +const std::string g_index_gmi{"index.gmi"}; + bool send_response(NotNull ctx, gemini::Status status, const std::string &meta) { // @@ -86,40 +89,71 @@ void client_thread(const transport::AcceptedClient *ac break; } - OpenedFile opened_file; - const auto open_file_result = opened_file.open(directory_fd, path); + UniqueFd opened_dir; + UniqueFd opened_fd; + auto open_file_result = open_file::open(directory_fd, path, opened_fd); + const std::string *opened_path{nullptr}; switch (open_file_result) { - case OpenedFile::NOT_FOUND: + case open_file::ERROR_NOT_EXIST: send_response(accepted_client->get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::NOT_FOUND); return; - case OpenedFile::OPENING_ERROR: + case open_file::ERROR: send_response(accepted_client->get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::TEMPORARY_FAILURE); return; - - case OpenedFile::OPENED: + case open_file::SUCCESS_DIRECTORY: + if (path.empty()) + { + send_response(accepted_client->get_ctx(), gemini::STATUS_31_REDIRECT_PERMANENT, meta::ROOT); + error::g_log << "31 " << " -> \"" << meta::ROOT << "\"" << std::endl; + return; + } + if (*path.crbegin() != '/') + { + assert(path[0] != '/'); + path.insert(0, 1, '/'); + path.push_back('/'); + send_response(accepted_client->get_ctx(), gemini::STATUS_31_REDIRECT_PERMANENT, path); + error::g_log << "31 " << " -> \"" << path << "\"" << std::endl; + return; + } + opened_dir.reset(opened_fd.release()); + open_file_result = open_file::open(opened_dir.get(), g_index_gmi, opened_fd); + switch(open_file_result) + { + case open_file::ERROR_NOT_EXIST: + case open_file::SUCCESS_DIRECTORY: + send_response(accepted_client->get_ctx(), gemini::STATUS_51_NOT_FOUND, meta::NOT_FOUND); + return; + case open_file::ERROR: + send_response(accepted_client->get_ctx(), gemini::STATUS_40_TEMPORARY_FAILURE, meta::TEMPORARY_FAILURE); + return; + case open_file::SUCCESS_FILE: + opened_path = &g_index_gmi; + break; + } break; + case open_file::SUCCESS_FILE: + opened_path = &path; + break; } // > If is an empty string, the MIME type MUST default to "text/gemini; charset=utf-8". - const auto mime_type = mime.lookup(opened_file.get_path()); - send_response( - accepted_client->get_ctx(), - gemini::STATUS_20_SUCCESS, - mime_type ? *mime_type : meta::_EMPTY - ); + const auto _mime_type = mime.lookup(*opened_path); + const std::string &meta_string = _mime_type ? *_mime_type : meta::_EMPTY; + send_response(accepted_client->get_ctx(), gemini::STATUS_20_SUCCESS, meta_string); std::vector buffer; buffer.resize(64 * 1024); for (; ; ) { - const auto ret = read(opened_file.get_fd(), buffer.data(), buffer.size()); + const auto ret = read(opened_fd.get(), buffer.data(), buffer.size()); if (ret == -1) { error::occurred( - [&opened_file] + [&path] { - error::g_log << "Read file \"" << opened_file.get_path() << "\""; + error::g_log << "Read file \"" << path << "\""; }, error::Print{} ); @@ -134,7 +168,7 @@ void client_thread(const transport::AcceptedClient *ac if (readed < buffer.size()) break; } - error::g_log << "20 " << (mime_type ? *mime_type : meta::_EMPTY) << " \"" << opened_file.get_path() << "\"" << std::endl; + error::g_log << "20 " << meta_string << " \"" << path << "\"" << std::endl; }