From 2e9bec95b05e84435ba5ca513a0197e928ed42fe Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 15:31:29 +0100 Subject: [PATCH 01/11] Proper URI-encoding in InternalServer::build_redirect() - Before this change `InternalServer::build_redirect()` only URI-encoded the article path, ignoring the book name and/or the root location components of the URL. - In order to be able to test this fix, corner_cases.zim was renamed to contain a couple of special URL symbols in its filename. The `create_corner_cases_zim_file` script was updated accordingly. --- src/server/internalServer.cpp | 5 ++-- .../{corner_cases.zim => corner_cases#&.zim} | Bin test/data/create_corner_cases_zim_file | 25 +++++++++++++++--- test/meson.build | 2 +- test/server.cpp | 16 +++++------ test/server_search.cpp | 2 +- test/server_testing_tools.h | 2 +- 7 files changed, 34 insertions(+), 18 deletions(-) rename test/data/{corner_cases.zim => corner_cases#&.zim} (100%) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 8548d4245..bef72d64b 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -1030,9 +1030,8 @@ ParameterizedMessage suggestSearchMsg(const std::string& searchURL, const std::s std::unique_ptr InternalServer::build_redirect(const std::string& bookName, const zim::Item& item) const { - const auto path = kiwix::urlEncode(item.getPath()); - const auto redirectUrl = m_root + "/content/" + bookName + "/" + path; - return Response::build_redirect(*this, redirectUrl); + const auto absPath = m_root + "/content/" + bookName + "/" + item.getPath(); + return Response::build_redirect(*this, kiwix::urlEncode(absPath)); } std::unique_ptr InternalServer::handle_content(const RequestContext& request) diff --git a/test/data/corner_cases.zim b/test/data/corner_cases#&.zim similarity index 100% rename from test/data/corner_cases.zim rename to test/data/corner_cases#&.zim diff --git a/test/data/create_corner_cases_zim_file b/test/data/create_corner_cases_zim_file index 5f11096a2..32615cad9 100755 --- a/test/data/create_corner_cases_zim_file +++ b/test/data/create_corner_cases_zim_file @@ -1,7 +1,24 @@ #!/usr/bin/env bash cd "$(dirname "$0")" -rm -f corner_cases.zim + +# The following symbols (that would be nice to include in testing) are not +# allowed under NTFS and/or FAT32 filesystems, and would result in the +# impossibility to git clone (or rather checkout) the libkiwix repository under +# Windows: +# +# ? +# = +# + (that's a pity, since the + symbol in a ZIM filename is replaced with the +# text 'plus' when the ZIM file is added to kiwix-serve's library and it +# would be nice to test that functionality) +# +# Assuming that tests are NOT run under Windows, above symbols can be included +# in testing if the file is renamed while copying to the build directory (see +# test/meson.build), though that would make maintenance slightly more confusing. +zimfilename='corner_cases#&.zim' + +rm -f "$zimfilename" zimwriterfs --withoutFTIndex --dont-check-arguments \ -w empty.html \ -I empty.png \ @@ -11,6 +28,6 @@ zimwriterfs --withoutFTIndex --dont-check-arguments \ -c "" \ -p "" \ corner_cases \ - corner_cases.zim \ -&& echo 'corner_cases.zim was successfully created' \ -|| echo '!!! Failed to create corner_cases.zim !!!' >&2 + "$zimfilename" \ +&& echo "$zimfilename was successfully created" \ +|| echo '!!! Failed to create' "$zimfilename" '!!!' >&2 diff --git a/test/meson.build b/test/meson.build index 772afda18..f7ed51f5d 100644 --- a/test/meson.build +++ b/test/meson.build @@ -34,7 +34,7 @@ if gtest_dep.found() and not meson.is_cross_build() 'example.zim', 'zimfile.zim', 'zimfile&other.zim', - 'corner_cases.zim', + 'corner_cases#&.zim', 'poor.zim', 'library.xml', 'lib_for_server_search_test.xml', diff --git a/test/server.cpp b/test/server.cpp index 5a224343a..3ac6e6fff 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -166,9 +166,9 @@ const ResourceCollection resources200Uncompressible{ { ZIM_CONTENT, "/ROOT/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg" }, - { ZIM_CONTENT, "/ROOT/content/corner_cases/empty.html" }, - { ZIM_CONTENT, "/ROOT/content/corner_cases/empty.css" }, - { ZIM_CONTENT, "/ROOT/content/corner_cases/empty.js" }, + { ZIM_CONTENT, "/ROOT/content/corner_cases%23%26/empty.html" }, + { ZIM_CONTENT, "/ROOT/content/corner_cases%23%26/empty.css" }, + { ZIM_CONTENT, "/ROOT/content/corner_cases%23%26/empty.js" }, // The following url's responses are too small to be compressed @@ -188,7 +188,7 @@ TEST(indexTemplateStringTest, emptyIndexTemplate) { const int PORT = 8001; const ZimFileServer::FilePathCollection ZIMFILES { "./test/zimfile.zim", - "./test/corner_cases.zim" + "./test/corner_cases#&.zim" }; ZimFileServer zfs(PORT, ZimFileServer::DEFAULT_OPTIONS, ZIMFILES, ""); @@ -199,7 +199,7 @@ TEST(indexTemplateStringTest, indexTemplateCheck) { const int PORT = 8001; const ZimFileServer::FilePathCollection ZIMFILES { "./test/zimfile.zim", - "./test/corner_cases.zim" + "./test/corner_cases#&.zim" }; ZimFileServer zfs(PORT, ZimFileServer::DEFAULT_OPTIONS, ZIMFILES, "" @@ -1291,10 +1291,10 @@ TEST_F(ServerTest, NonEndpointUrlsAreRedirectedToContentUrls) TEST_F(ServerTest, RedirectionsToURLsWithSpecialSymbols) { - auto g = zfs1_->GET("/ROOT/content/corner_cases/c_sharp.html"); + auto g = zfs1_->GET("/ROOT/content/corner_cases%23%26/c_sharp.html"); ASSERT_EQ(302, g->status); ASSERT_TRUE(g->has_header("Location")); - ASSERT_EQ(g->get_header_value("Location"), "/ROOT/content/corner_cases/c%23.html"); + ASSERT_EQ(g->get_header_value("Location"), "/ROOT/content/corner_cases%23%26/c%23.html"); ASSERT_EQ(getCacheControlHeader(*g), "max-age=0, must-revalidate"); ASSERT_FALSE(g->has_header("ETag")); } @@ -1611,7 +1611,7 @@ TEST_F(ServerTest, InvalidAndMultiRangeByteRangeRequestsResultIn416Responses) TEST_F(ServerTest, ValidByteRangeRequestsOfZeroSizedEntriesResultIn416Responses) { - const char url[] = "/ROOT/content/corner_cases/empty.js"; + const char url[] = "/ROOT/content/corner_cases%23%26/empty.js"; const char* ranges[] = { "bytes=0-", diff --git a/test/server_search.cpp b/test/server_search.cpp index e8af5f348..c0146a573 100644 --- a/test/server_search.cpp +++ b/test/server_search.cpp @@ -1517,7 +1517,7 @@ TEST(ServerSearchTest, searchInMultilanguageBookSetIsDenied) const ZimFileServer::FilePathCollection ZIMFILES{ "./test/zimfile.zim", // eng "./test/example.zim", // en - "./test/corner_cases.zim" // =en + "./test/corner_cases#&.zim" // =en }; ZimFileServer zfs(SERVER_PORT, ZimFileServer::DEFAULT_OPTIONS, ZIMFILES); diff --git a/test/server_testing_tools.h b/test/server_testing_tools.h index 7af5834e6..463c863fc 100644 --- a/test/server_testing_tools.h +++ b/test/server_testing_tools.h @@ -163,7 +163,7 @@ protected: "./test/zimfile.zim", "./test/example.zim", "./test/poor.zim", - "./test/corner_cases.zim" + "./test/corner_cases#&.zim" }; protected: From 58bb8b984325d135bf2c9b6cb2edb1b1cc3e6c34 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 16:42:35 +0100 Subject: [PATCH 02/11] ServerTest.RandomPageRedirectionsAreUrlEncoded --- test/server.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/test/server.cpp b/test/server.cpp index 3ac6e6fff..570c6cb55 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -1243,6 +1243,41 @@ TEST_F(ServerTest, RandomPageRedirectsToAnExistingArticle) ASSERT_FALSE(g->has_header("ETag")); } +TEST_F(ServerTest, RandomPageRedirectionsAreUriEncoded) +{ + // The purpose of this unit test is to validate that the URL to which the + // /random endpoint redirects is in URI-encoded form. Due to the difficulty + // of checking non-deterministic functionality, it is implemented by running + // /random sufficiently many times so that all articles are returned with + // close to 100% probability (this is done on an artificial small ZIM file + // with at least one article with a special symbol in its name). Such an + // approach effectively tests more than the original goal was, so this test + // may over time evolve into something more general whereupon it must be + // renamed. + + auto g = zfs1_->GET("/ROOT/random?content=corner_cases%23%26"); + + typedef std::set StringSet; + const StringSet frontArticles{ + {"/ROOT/content/corner_cases%23%26/c%23.html"}, + + // empty.html is missing because of a subtle bug + // in libzim::randomNumber(max) which returns a value equal to + // it argument with much lower probability than other numbers + // {"/ROOT/content/corner_cases%23%26/empty.html"} + }; + + StringSet randomPageUrls; + auto n = 10 * frontArticles.size(); + while ( --n && randomPageUrls.size() != frontArticles.size() ) { + ASSERT_EQ(302, g->status); + ASSERT_TRUE(g->has_header("Location")); + randomPageUrls.insert(g->get_header_value("Location")); + } + + ASSERT_EQ(frontArticles, randomPageUrls); +} + TEST_F(ServerTest, NonEndpointUrlsAreRedirectedToContentUrls) { const std::string paths[] = { From a807ce27f1e5736fc721ed839af56a4e5040f12d Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 17:31:59 +0100 Subject: [PATCH 03/11] URI-encoding when redirecting legacy URLs to /content Testing of this functionality revealed that the query part containing + symbols (as replacement for spaces in the parameter values) isn't forwarded properly as the + symbols are URI-encoded (this is a bug on the part of the `RequestContext::get_query()` the result of which already contains URI-encoded +'s). --- src/server/internalServer.cpp | 2 +- test/server.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index bef72d64b..e1017e287 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -607,7 +607,7 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r if (isEndpointUrl(url, "catch")) return handle_catch(request); - std::string contentUrl = m_root + "/content" + url; + std::string contentUrl = urlEncode(m_root + "/content" + url); const std::string query = request.get_query(); if ( ! query.empty() ) contentUrl += "?" + query; diff --git a/test/server.cpp b/test/server.cpp index 570c6cb55..ce89b45bc 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -1310,6 +1310,19 @@ TEST_F(ServerTest, NonEndpointUrlsAreRedirectedToContentUrls) // Make sure that the query is preserved in the redirect URL "/does?P=NP" + + // Make sure that reserved URI encoded symbols stay URI encoded + "/C%23", // # --> /C# + "/100%25_guarantee", // % --> /100%_guarantee + "/AT%26T", // & --> /AT&T + "/Quo_vadis%3F", // ? --> /Quo_vadis? + "/1%2B1%3D2", // +,= --> 1+1=2 + + // Make sure that URI-encoded query stays URI-encoded + "/encode?string=%23%25%26%2B%3D%3F", + + // Bug: the + symbol (that replaces space) in the query gets URI-encoded + //"/route?from=current+location&to=girlfriend%238", }; for ( const std::string& p : paths ) From 71a66e05288d2c7bdf62292fe45add53487c9133 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 21:07:01 +0100 Subject: [PATCH 04/11] Passing of unrooted URL into RequestContext() This change doesn't make much sense on its own - the real goal is to prepare some ground for easier implementation of URI-encoding of the root location. --- src/server/internalServer.cpp | 24 +++++++++++++++++++++--- src/server/request_context.cpp | 24 ++++-------------------- src/server/request_context.h | 4 ++-- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index e1017e287..a9d809e39 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -94,6 +94,22 @@ inline std::string normalizeRootUrl(std::string rootUrl) return rootUrl.empty() ? rootUrl : "/" + rootUrl; } +std::string +fullURL2LocalURL(const std::string& full_url, const std::string& rootLocation) +{ + if (rootLocation.empty()) { + // nothing special to handle. + return full_url; + } else if (full_url == rootLocation) { + return "/"; + } else if (full_url.size() > rootLocation.size() && + full_url.substr(0, rootLocation.size()+1) == rootLocation + "/") { + return full_url.substr(rootLocation.size()); + } else { + return ""; + } +} + Filter get_search_filter(const RequestContext& request, const std::string& prefix="") { auto filter = kiwix::Filter().valid(true).local(true); @@ -494,7 +510,7 @@ static MHD_Result staticHandlerCallback(void* cls, } MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection, - const char* url, + const char* fullUrl, const char* method, const char* version, const char* upload_data, @@ -505,8 +521,10 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection, if (m_verbose.load() ) { printf("======================\n"); printf("Requesting : \n"); - printf("full_url : %s\n", url); + printf("full_url : %s\n", fullUrl); } + + const auto url = fullURL2LocalURL(fullUrl, m_root); RequestContext request(connection, m_root, url, method, version); if (m_verbose.load() ) { @@ -527,7 +545,7 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection, printf("========== INTERNAL ERROR !! ============\n"); if (!m_verbose.load()) { printf("Requesting : \n"); - printf("full_url : %s\n", url); + printf("full_url : %s\n", fullUrl); request.print_debug_info(); } } diff --git a/src/server/request_context.cpp b/src/server/request_context.cpp index d418d75af..4002671fc 100644 --- a/src/server/request_context.cpp +++ b/src/server/request_context.cpp @@ -49,32 +49,16 @@ RequestMethod str2RequestMethod(const std::string& method) { else return RequestMethod::OTHER; } -std::string -fullURL2LocalURL(const std::string& full_url, const std::string& rootLocation) -{ - if (rootLocation.empty()) { - // nothing special to handle. - return full_url; - } else if (full_url == rootLocation) { - return "/"; - } else if (full_url.size() > rootLocation.size() && - full_url.substr(0, rootLocation.size()+1) == rootLocation + "/") { - return full_url.substr(rootLocation.size()); - } else { - return ""; - } -} - } // unnamed namespace RequestContext::RequestContext(struct MHD_Connection* connection, - std::string _rootLocation, - const std::string& _url, + const std::string& _rootLocation, + const std::string& unrootedUrl, const std::string& _method, const std::string& version) : rootLocation(_rootLocation), - full_url(_url), - url(fullURL2LocalURL(_url, _rootLocation)), + full_url(_rootLocation + unrootedUrl), + url(unrootedUrl), method(str2RequestMethod(_method)), version(version), requestIndex(s_requestIndex++), diff --git a/src/server/request_context.h b/src/server/request_context.h index 9017a6b28..bea0ff804 100644 --- a/src/server/request_context.h +++ b/src/server/request_context.h @@ -57,8 +57,8 @@ class IndexError: public std::runtime_error {}; class RequestContext { public: // functions RequestContext(struct MHD_Connection* connection, - std::string rootLocation, - const std::string& url, + const std::string& rootLocation, + const std::string& unrootedUrl, const std::string& method, const std::string& version); ~RequestContext(); From 2c5e84b6b3ea30e30b8d708a862ea47b43a5c46b Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 21:12:49 +0100 Subject: [PATCH 05/11] Simpler fullURL2LocalURL() --- src/server/internalServer.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index a9d809e39..098cb6ac4 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -95,16 +95,10 @@ inline std::string normalizeRootUrl(std::string rootUrl) } std::string -fullURL2LocalURL(const std::string& full_url, const std::string& rootLocation) +fullURL2LocalURL(const std::string& fullUrl, const std::string& rootLocation) { - if (rootLocation.empty()) { - // nothing special to handle. - return full_url; - } else if (full_url == rootLocation) { - return "/"; - } else if (full_url.size() > rootLocation.size() && - full_url.substr(0, rootLocation.size()+1) == rootLocation + "/") { - return full_url.substr(rootLocation.size()); + if ( kiwix::startsWith(fullUrl, rootLocation + "/") ) { + return fullUrl.substr(rootLocation.size()); } else { return ""; } From a7fe4193e3f693a0057d5081ba82193525b47640 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 21:16:10 +0100 Subject: [PATCH 06/11] Preparing to save a few CPU cycles --- src/server/internalServer.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 098cb6ac4..1536e86e6 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -97,8 +97,9 @@ inline std::string normalizeRootUrl(std::string rootUrl) std::string fullURL2LocalURL(const std::string& fullUrl, const std::string& rootLocation) { - if ( kiwix::startsWith(fullUrl, rootLocation + "/") ) { - return fullUrl.substr(rootLocation.size()); + assert(rootLocation.size() > 0 && rootLocation.back() == '/'); + if ( kiwix::startsWith(fullUrl, rootLocation) ) { + return fullUrl.substr(rootLocation.size() - 1); } else { return ""; } @@ -518,7 +519,7 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection, printf("full_url : %s\n", fullUrl); } - const auto url = fullURL2LocalURL(fullUrl, m_root); + const auto url = fullURL2LocalURL(fullUrl, m_root + "/"); RequestContext request(connection, m_root, url, method, version); if (m_verbose.load() ) { From 97f0314fe63054afc4bb3e7bbd2902308dc61632 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 21:22:39 +0100 Subject: [PATCH 07/11] Saving a few CPU cycles This silly optimization in fact helps to avoid a somewhat more serious waste of CPU cycles that would otherwise result in the next commit. --- src/server/internalServer.cpp | 3 ++- src/server/internalServer.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 1536e86e6..1513f95a8 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -415,6 +415,7 @@ InternalServer::InternalServer(Library* library, m_addr(addr), m_port(port), m_root(normalizeRootUrl(root)), + m_rootWithSeparator(m_root + "/"), m_nbThreads(nbThreads), m_multizimSearchLimit(multizimSearchLimit), m_verbose(verbose), @@ -519,7 +520,7 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection, printf("full_url : %s\n", fullUrl); } - const auto url = fullURL2LocalURL(fullUrl, m_root + "/"); + const auto url = fullURL2LocalURL(fullUrl, m_rootWithSeparator); RequestContext request(connection, m_root, url, method, version); if (m_verbose.load() ) { diff --git a/src/server/internalServer.h b/src/server/internalServer.h index c3990c44d..f00dfe8fb 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -164,6 +164,7 @@ class InternalServer { std::string m_addr; int m_port; std::string m_root; + std::string m_rootWithSeparator; int m_nbThreads; unsigned int m_multizimSearchLimit; std::atomic_bool m_verbose; From 05a66ead6efbd73b80450e6c2e07e5dc5b23d21e Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Wed, 8 Feb 2023 21:32:05 +0100 Subject: [PATCH 08/11] URI-encoding of the root location part Now the root location is URI-encoded too. In order to properly test this change the root location in the tests was changed from "/ROOT" to "/ROOT#?" (or "/ROOT%23%3F" in URI-encoded form), which is why this commit is so big. --- src/server/internalServer.cpp | 11 +- src/server/internalServer.h | 4 +- src/server/request_context.cpp | 6 +- src/server/request_context.h | 4 +- src/server/response.cpp | 4 +- test/library_server.cpp | 120 +++---- test/server.cpp | 602 ++++++++++++++++----------------- test/server_search.cpp | 118 +++---- test/server_testing_tools.h | 2 +- 9 files changed, 437 insertions(+), 434 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 1513f95a8..42cfa7f3e 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -430,7 +430,9 @@ InternalServer::InternalServer(Library* library, searchCache(getEnvVar("KIWIX_SEARCH_CACHE_SIZE", DEFAULT_CACHE_SIZE)), suggestionSearcherCache(getEnvVar("KIWIX_SUGGESTION_SEARCHER_CACHE_SIZE", std::max((unsigned int) (mp_library->getBookCount(true, true)*0.1), 1U))), m_customizedResources(new CustomizedResources) -{} +{ + m_root = urlEncode(m_root); +} InternalServer::~InternalServer() = default; @@ -621,7 +623,7 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r if (isEndpointUrl(url, "catch")) return handle_catch(request); - std::string contentUrl = urlEncode(m_root + "/content" + url); + std::string contentUrl = m_root + "/content" + urlEncode(url); const std::string query = request.get_query(); if ( ! query.empty() ) contentUrl += "?" + query; @@ -1044,8 +1046,9 @@ ParameterizedMessage suggestSearchMsg(const std::string& searchURL, const std::s std::unique_ptr InternalServer::build_redirect(const std::string& bookName, const zim::Item& item) const { - const auto absPath = m_root + "/content/" + bookName + "/" + item.getPath(); - return Response::build_redirect(*this, kiwix::urlEncode(absPath)); + const auto contentPath = "/content/" + bookName + "/" + item.getPath(); + const auto url = m_root + kiwix::urlEncode(contentPath); + return Response::build_redirect(*this, url); } std::unique_ptr InternalServer::handle_content(const RequestContext& request) diff --git a/src/server/internalServer.h b/src/server/internalServer.h index f00dfe8fb..aaa642cd0 100644 --- a/src/server/internalServer.h +++ b/src/server/internalServer.h @@ -163,8 +163,8 @@ class InternalServer { private: // data std::string m_addr; int m_port; - std::string m_root; - std::string m_rootWithSeparator; + std::string m_root; // URI-encoded + std::string m_rootWithSeparator; // URI-decoded int m_nbThreads; unsigned int m_multizimSearchLimit; std::atomic_bool m_verbose; diff --git a/src/server/request_context.cpp b/src/server/request_context.cpp index 4002671fc..9ae3de1a2 100644 --- a/src/server/request_context.cpp +++ b/src/server/request_context.cpp @@ -52,12 +52,12 @@ RequestMethod str2RequestMethod(const std::string& method) { } // unnamed namespace RequestContext::RequestContext(struct MHD_Connection* connection, - const std::string& _rootLocation, - const std::string& unrootedUrl, + const std::string& _rootLocation, // URI-encoded + const std::string& unrootedUrl, // URI-decoded const std::string& _method, const std::string& version) : rootLocation(_rootLocation), - full_url(_rootLocation + unrootedUrl), + full_url(_rootLocation + urlEncode(unrootedUrl)), url(unrootedUrl), method(str2RequestMethod(_method)), version(version), diff --git a/src/server/request_context.h b/src/server/request_context.h index bea0ff804..03ea271b3 100644 --- a/src/server/request_context.h +++ b/src/server/request_context.h @@ -57,8 +57,8 @@ class IndexError: public std::runtime_error {}; class RequestContext { public: // functions RequestContext(struct MHD_Connection* connection, - const std::string& rootLocation, - const std::string& unrootedUrl, + const std::string& rootLocation, // URI-encoded + const std::string& unrootedUrl, // URI-decoded const std::string& method, const std::string& version); ~RequestContext(); diff --git a/src/server/response.cpp b/src/server/response.cpp index 3ec50b8ca..db20a2fb4 100644 --- a/src/server/response.cpp +++ b/src/server/response.cpp @@ -200,7 +200,7 @@ HTTP404Response::HTTP404Response(const InternalServer& server, HTTPErrorResponse& HTTP404Response::operator+(UrlNotFoundMsg /*unused*/) { - const std::string requestUrl = m_request.get_full_url(); + const std::string requestUrl = urlDecode(m_request.get_full_url(), false); return *this + ParameterizedMessage("url-not-found", {{"url", requestUrl}}); } @@ -234,7 +234,7 @@ HTTP400Response::HTTP400Response(const InternalServer& server, HTTPErrorResponse& HTTP400Response::operator+(InvalidUrlMsg /*unused*/) { - std::string requestUrl = m_request.get_full_url(); + std::string requestUrl = urlDecode(m_request.get_full_url(), false); const auto query = m_request.get_query(); if (!query.empty()) { requestUrl += "?" + encodeDiples(query); diff --git a/test/library_server.cpp b/test/library_server.cpp index a8f349ef0..5c0d65b1f 100644 --- a/test/library_server.cpp +++ b/test/library_server.cpp @@ -73,7 +73,7 @@ std::string maskVariableOPDSFeedData(std::string s) " \n" \ " \n" + " href=\"/ROOT%23%3F/catalog/searchdescription.xml\" />\n" #define CATALOG_ENTRY(UUID, TITLE, SUMMARY, LANG, NAME, CATEGORY, TAGS, EXTRA_LINK, CONTENT_NAME, FILE_NAME, LENGTH) \ " \n" \ @@ -88,7 +88,7 @@ std::string maskVariableOPDSFeedData(std::string s) " " TAGS "\n" \ " 284\n" \ " 2\n" \ - " " EXTRA_LINK "\n" \ + " " EXTRA_LINK "\n" \ " \n" \ " Wikipedia\n" \ " \n" \ @@ -126,7 +126,7 @@ std::string maskVariableOPDSFeedData(std::string s) "wikipedia",\ "public_tag_without_a_value;_private_tag_without_a_value;wikipedia;_category:wikipedia;_pictures:no;_videos:no;_details:no;_ftindex:yes",\ "\n ", \ CONTENT_NAME, \ "zimfile", \ @@ -152,7 +152,7 @@ std::string maskVariableOPDSFeedData(std::string s) TEST_F(LibraryServerTest, catalog_root_xml) { - const auto r = zfs1_->GET("/ROOT/catalog/root.xml"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/root.xml"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -170,7 +170,7 @@ TEST_F(LibraryServerTest, catalog_root_xml) TEST_F(LibraryServerTest, catalog_searchdescription_xml) { - const auto r = zfs1_->GET("/ROOT/catalog/searchdescription.xml"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/searchdescription.xml"); EXPECT_EQ(r->status, 200); EXPECT_EQ(r->body, "\n" @@ -181,14 +181,14 @@ TEST_F(LibraryServerTest, catalog_searchdescription_xml) " xmlns:atom=\"http://www.w3.org/2005/Atom\"\n" " xmlns:k=\"http://kiwix.org/opensearchextension/1.0\"\n" " indexOffset=\"0\"\n" - " template=\"/ROOT/catalog/search?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}¬ag={k:notag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n" + " template=\"/ROOT%23%3F/catalog/search?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}¬ag={k:notag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n" "\n" ); } TEST_F(LibraryServerTest, catalog_search_by_phrase) { - const auto r = zfs1_->GET("/ROOT/catalog/search?q=\"ray%20charles\""); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?q=\"ray%20charles\""); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -207,7 +207,7 @@ TEST_F(LibraryServerTest, catalog_search_by_phrase) TEST_F(LibraryServerTest, catalog_search_by_words) { - const auto r = zfs1_->GET("/ROOT/catalog/search?q=ray%20charles"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?q=ray%20charles"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -228,7 +228,7 @@ TEST_F(LibraryServerTest, catalog_search_by_words) TEST_F(LibraryServerTest, catalog_prefix_search) { { - const auto r = zfs1_->GET("/ROOT/catalog/search?q=description:ray%20description:charles"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?q=description:ray%20description:charles"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -245,7 +245,7 @@ TEST_F(LibraryServerTest, catalog_prefix_search) ); } { - const auto r = zfs1_->GET("/ROOT/catalog/search?q=title:\"ray%20charles\""); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?q=title:\"ray%20charles\""); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -264,7 +264,7 @@ TEST_F(LibraryServerTest, catalog_prefix_search) TEST_F(LibraryServerTest, catalog_search_with_word_exclusion) { - const auto r = zfs1_->GET("/ROOT/catalog/search?q=ray%20-uncategorized"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?q=ray%20-uncategorized"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -283,7 +283,7 @@ TEST_F(LibraryServerTest, catalog_search_with_word_exclusion) TEST_F(LibraryServerTest, catalog_search_by_tag) { - const auto r = zfs1_->GET("/ROOT/catalog/search?tag=_category:jazz"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?tag=_category:jazz"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -301,7 +301,7 @@ TEST_F(LibraryServerTest, catalog_search_by_tag) TEST_F(LibraryServerTest, catalog_search_by_category) { - const auto r = zfs1_->GET("/ROOT/catalog/search?category=jazz"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?category=jazz"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -320,7 +320,7 @@ TEST_F(LibraryServerTest, catalog_search_by_category) TEST_F(LibraryServerTest, catalog_search_by_language) { { - const auto r = zfs1_->GET("/ROOT/catalog/search?lang=eng"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?lang=eng"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -337,7 +337,7 @@ TEST_F(LibraryServerTest, catalog_search_by_language) } { - const auto r = zfs1_->GET("/ROOT/catalog/search?lang=eng,fra"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?lang=eng,fra"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -358,7 +358,7 @@ TEST_F(LibraryServerTest, catalog_search_by_language) TEST_F(LibraryServerTest, catalog_search_results_pagination) { { - const auto r = zfs1_->GET("/ROOT/catalog/search?count=0"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?count=0"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -376,7 +376,7 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination) ); } { - const auto r = zfs1_->GET("/ROOT/catalog/search?count=1"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?count=1"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -392,7 +392,7 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination) ); } { - const auto r = zfs1_->GET("/ROOT/catalog/search?start=1&count=1"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?start=1&count=1"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -408,7 +408,7 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination) ); } { - const auto r = zfs1_->GET("/ROOT/catalog/search?start=100&count=10"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?start=100&count=10"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -426,20 +426,20 @@ TEST_F(LibraryServerTest, catalog_search_results_pagination) TEST_F(LibraryServerTest, catalog_v2_root) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/root.xml"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/root.xml"); EXPECT_EQ(r->status, 200); const char expected_output[] = R"( 12345678-90ab-cdef-1234-567890abcdef OPDS Catalog Root YYYY-MM-DDThh:mm:ssZ @@ -447,7 +447,7 @@ TEST_F(LibraryServerTest, catalog_v2_root) All entries YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -456,7 +456,7 @@ TEST_F(LibraryServerTest, catalog_v2_root) All entries (partial) YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -465,7 +465,7 @@ TEST_F(LibraryServerTest, catalog_v2_root) List of categories YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -474,7 +474,7 @@ TEST_F(LibraryServerTest, catalog_v2_root) List of languages YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -487,7 +487,7 @@ TEST_F(LibraryServerTest, catalog_v2_root) TEST_F(LibraryServerTest, catalog_v2_searchdescription_xml) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/searchdescription.xml"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/searchdescription.xml"); EXPECT_EQ(r->status, 200); EXPECT_EQ(r->body, "\n" @@ -498,24 +498,24 @@ TEST_F(LibraryServerTest, catalog_v2_searchdescription_xml) " xmlns:atom=\"http://www.w3.org/2005/Atom\"\n" " xmlns:k=\"http://kiwix.org/opensearchextension/1.0\"\n" " indexOffset=\"0\"\n" - " template=\"/ROOT/catalog/v2/entries?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n" + " template=\"/ROOT%23%3F/catalog/v2/entries?q={searchTerms?}&lang={language?}&name={k:name?}&tag={k:tag?}&maxsize={k:maxsize?}&count={count?}&start={startIndex?}\"/>\n" "\n" ); } TEST_F(LibraryServerTest, catalog_v2_categories) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/categories"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/categories"); EXPECT_EQ(r->status, 200); const char expected_output[] = R"( 12345678-90ab-cdef-1234-567890abcdef List of categories YYYY-MM-DDThh:mm:ssZ @@ -523,7 +523,7 @@ TEST_F(LibraryServerTest, catalog_v2_categories) jazz YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -532,7 +532,7 @@ TEST_F(LibraryServerTest, catalog_v2_categories) wikipedia YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -545,7 +545,7 @@ TEST_F(LibraryServerTest, catalog_v2_categories) TEST_F(LibraryServerTest, catalog_v2_languages) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/languages"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/languages"); EXPECT_EQ(r->status, 200); const char expected_output[] = R"( 12345678-90ab-cdef-1234-567890abcdef List of languages YYYY-MM-DDThh:mm:ssZ @@ -567,7 +567,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages) eng 1 YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -577,7 +577,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages) fra 1 YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -587,7 +587,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages) rus 1 YYYY-MM-DDThh:mm:ssZ 12345678-90ab-cdef-1234-567890abcdef @@ -606,13 +606,13 @@ TEST_F(LibraryServerTest, catalog_v2_languages) " 12345678-90ab-cdef-1234-567890abcdef\n" \ "\n" \ " \n" \ " \n" \ " \n" \ "\n" \ @@ -624,7 +624,7 @@ TEST_F(LibraryServerTest, catalog_v2_languages) TEST_F(LibraryServerTest, catalog_v2_entries) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entries"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_ENTRIES_PREAMBLE("") @@ -641,7 +641,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries) TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range) { { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?start=1"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?start=1"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_ENTRIES_PREAMBLE("?start=1") @@ -657,7 +657,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range) } { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?count=2"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?count=2"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_ENTRIES_PREAMBLE("?count=2") @@ -673,7 +673,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range) } { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?start=1&count=1"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?start=1&count=1"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_ENTRIES_PREAMBLE("?start=1&count=1") @@ -690,7 +690,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_range) TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_search_terms) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?q=\"ray%20charles\""); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?q=\"ray%20charles\""); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_ENTRIES_PREAMBLE("?q=%22ray%20charles%22") @@ -708,7 +708,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_search_terms) TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_language) { { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?lang=eng"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?lang=eng"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_ENTRIES_PREAMBLE("?lang=eng") @@ -723,7 +723,7 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_language) } { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entries?lang=eng,fra"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entries?lang=eng,fra"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_ENTRIES_PREAMBLE("?lang=eng%2Cfra") @@ -741,20 +741,20 @@ TEST_F(LibraryServerTest, catalog_v2_entries_filtered_by_language) TEST_F(LibraryServerTest, catalog_v2_individual_entry_access) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/entry/raycharles"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entry/raycharles"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), "\n" RAY_CHARLES_CATALOG_ENTRY ); - const auto r1 = zfs1_->GET("/ROOT/catalog/v2/entry/non-existent-entry"); + const auto r1 = zfs1_->GET("/ROOT%23%3F/catalog/v2/entry/non-existent-entry"); EXPECT_EQ(r1->status, 404); } TEST_F(LibraryServerTest, catalog_v2_partial_entries) { - const auto r = zfs1_->GET("/ROOT/catalog/v2/partial_entries"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/partial_entries"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), CATALOG_V2_PARTIAL_ENTRIES_PREAMBLE("") @@ -766,7 +766,7 @@ TEST_F(LibraryServerTest, catalog_v2_partial_entries) " Charles, Ray\n" " YYYY-MM-DDThh:mm:ssZ\n" " \n" " \n" " \n" @@ -774,7 +774,7 @@ TEST_F(LibraryServerTest, catalog_v2_partial_entries) " Ray Charles\n" " YYYY-MM-DDThh:mm:ssZ\n" " \n" " \n" " \n" @@ -782,7 +782,7 @@ TEST_F(LibraryServerTest, catalog_v2_partial_entries) " Ray (uncategorized) Charles\n" " YYYY-MM-DDThh:mm:ssZ\n" " \n" " \n" "\n" @@ -791,7 +791,7 @@ TEST_F(LibraryServerTest, catalog_v2_partial_entries) #define EXPECT_SEARCH_RESULTS(SEARCH_TERM, RESULT_COUNT, OPDS_ENTRIES) \ { \ - const auto r = zfs1_->GET("/ROOT/catalog/search?q=" SEARCH_TERM); \ + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?q=" SEARCH_TERM); \ EXPECT_EQ(r->status, 200); \ EXPECT_EQ(maskVariableOPDSFeedData(r->body), \ OPDS_FEED_TAG \ @@ -860,7 +860,7 @@ TEST_F(LibraryServerTest, catalog_search_excludes_hidden_tags) TEST_F(LibraryServerTest, no_name_mapper_returned_catalog_use_uuid_in_link) { resetServer(ZimFileServer::NO_NAME_MAPPER); - const auto r = zfs1_->GET("/ROOT/catalog/search?tag=_category:jazz"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/search?tag=_category:jazz"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), OPDS_FEED_TAG @@ -880,14 +880,14 @@ TEST_F(LibraryServerTest, no_name_mapper_returned_catalog_use_uuid_in_link) TEST_F(LibraryServerTest, no_name_mapper_catalog_v2_individual_entry_access) { resetServer(ZimFileServer::NO_NAME_MAPPER); - const auto r = zfs1_->GET("/ROOT/catalog/v2/entry/raycharles"); + const auto r = zfs1_->GET("/ROOT%23%3F/catalog/v2/entry/raycharles"); EXPECT_EQ(r->status, 200); EXPECT_EQ(maskVariableOPDSFeedData(r->body), "\n" RAY_CHARLES_CATALOG_ENTRY_NO_MAPPER ); - const auto r1 = zfs1_->GET("/ROOT/catalog/v2/entry/non-existent-entry"); + const auto r1 = zfs1_->GET("/ROOT%23%3F/catalog/v2/entry/non-existent-entry"); EXPECT_EQ(r1->status, 404); } diff --git a/test/server.cpp b/test/server.cpp index ce89b45bc..420c89864 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -47,136 +47,136 @@ std::ostream& operator<<(std::ostream& out, const Resource& r) typedef std::vector ResourceCollection; const ResourceCollection resources200Compressible{ - { DYNAMIC_CONTENT, "/ROOT/" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/" }, - { DYNAMIC_CONTENT, "/ROOT/viewer" }, - { DYNAMIC_CONTENT, "/ROOT/viewer?cacheid=whatever" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/viewer" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/viewer?cacheid=whatever" }, - { DYNAMIC_CONTENT, "/ROOT/skin/autoComplete.min.js" }, - { STATIC_CONTENT, "/ROOT/skin/autoComplete.min.js?cacheid=1191aaaf" }, - { DYNAMIC_CONTENT, "/ROOT/skin/css/autoComplete.css" }, - { STATIC_CONTENT, "/ROOT/skin/css/autoComplete.css?cacheid=08951e06" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/favicon.ico" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/favicon.ico?cacheid=fba03a27" }, - { DYNAMIC_CONTENT, "/ROOT/skin/i18n.js" }, - { STATIC_CONTENT, "/ROOT/skin/i18n.js?cacheid=6da2bca0" }, - { DYNAMIC_CONTENT, "/ROOT/skin/index.css" }, - { STATIC_CONTENT, "/ROOT/skin/index.css?cacheid=0f9ba34e" }, - { DYNAMIC_CONTENT, "/ROOT/skin/index.js" }, - { STATIC_CONTENT, "/ROOT/skin/index.js?cacheid=2f5a81ac" }, - { DYNAMIC_CONTENT, "/ROOT/skin/iso6391To3.js" }, - { STATIC_CONTENT, "/ROOT/skin/iso6391To3.js?cacheid=ecde2bb3" }, - { DYNAMIC_CONTENT, "/ROOT/skin/isotope.pkgd.min.js" }, - { STATIC_CONTENT, "/ROOT/skin/isotope.pkgd.min.js?cacheid=2e48d392" }, - { DYNAMIC_CONTENT, "/ROOT/skin/mustache.min.js" }, - { STATIC_CONTENT, "/ROOT/skin/mustache.min.js?cacheid=bd23c4fb" }, - { DYNAMIC_CONTENT, "/ROOT/skin/taskbar.css" }, - { STATIC_CONTENT, "/ROOT/skin/taskbar.css?cacheid=eb3bec90" }, - { DYNAMIC_CONTENT, "/ROOT/skin/viewer.js" }, - { STATIC_CONTENT, "/ROOT/skin/viewer.js?cacheid=03fd97ee" }, - { DYNAMIC_CONTENT, "/ROOT/skin/fonts/Poppins.ttf" }, - { STATIC_CONTENT, "/ROOT/skin/fonts/Poppins.ttf?cacheid=af705837" }, - { DYNAMIC_CONTENT, "/ROOT/skin/fonts/Roboto.ttf" }, - { STATIC_CONTENT, "/ROOT/skin/fonts/Roboto.ttf?cacheid=84d10248" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/autoComplete.min.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/autoComplete.min.js?cacheid=1191aaaf" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/css/autoComplete.css" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/css/autoComplete.css?cacheid=08951e06" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/favicon.ico" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/favicon.ico?cacheid=fba03a27" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/i18n.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/i18n.js?cacheid=6da2bca0" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.css" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/index.css?cacheid=0f9ba34e" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/index.js?cacheid=2f5a81ac" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/iso6391To3.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/iso6391To3.js?cacheid=ecde2bb3" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/isotope.pkgd.min.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/isotope.pkgd.min.js?cacheid=2e48d392" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/mustache.min.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/mustache.min.js?cacheid=bd23c4fb" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/taskbar.css?cacheid=eb3bec90" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/viewer.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/viewer.js?cacheid=03fd97ee" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/fonts/Poppins.ttf?cacheid=af705837" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/fonts/Roboto.ttf" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/fonts/Roboto.ttf?cacheid=84d10248" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/search" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/search" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/v2/root.xml" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/v2/entries" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/v2/partial_entries" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/v2/root.xml" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/v2/entries" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/v2/partial_entries" }, - { DYNAMIC_CONTENT, "/ROOT/search?content=zimfile&pattern=a" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/search?content=zimfile&pattern=a" }, - { DYNAMIC_CONTENT, "/ROOT/suggest?content=zimfile&term=ray" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/suggest?content=zimfile&term=ray" }, - { ZIM_CONTENT, "/ROOT/content/zimfile/A/index" }, - { ZIM_CONTENT, "/ROOT/content/zimfile/A/Ray_Charles" }, + { ZIM_CONTENT, "/ROOT%23%3F/content/zimfile/A/index" }, + { ZIM_CONTENT, "/ROOT%23%3F/content/zimfile/A/Ray_Charles" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/content/A/index" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/content/A/Ray_Charles" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/content/A/index" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/content/A/Ray_Charles" }, }; const ResourceCollection resources200Uncompressible{ - { DYNAMIC_CONTENT, "/ROOT/skin/bittorrent.png" }, - { STATIC_CONTENT, "/ROOT/skin/bittorrent.png?cacheid=4f5c6882" }, - { DYNAMIC_CONTENT, "/ROOT/skin/blank.html" }, - { STATIC_CONTENT, "/ROOT/skin/blank.html?cacheid=6b1fa032" }, - { DYNAMIC_CONTENT, "/ROOT/skin/caret.png" }, - { STATIC_CONTENT, "/ROOT/skin/caret.png?cacheid=22b942b4" }, - { DYNAMIC_CONTENT, "/ROOT/skin/download.png" }, - { STATIC_CONTENT, "/ROOT/skin/download.png?cacheid=a39aa502" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/android-chrome-192x192.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/android-chrome-192x192.png?cacheid=bfac158b" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/android-chrome-512x512.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/android-chrome-512x512.png?cacheid=380c3653" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/apple-touch-icon.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/apple-touch-icon.png?cacheid=f86f8df3" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/browserconfig.xml" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/browserconfig.xml?cacheid=f29a7c4a" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/favicon-16x16.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/favicon-16x16.png?cacheid=a986fedc" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/favicon-32x32.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/favicon-32x32.png?cacheid=79ded625" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/mstile-144x144.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/mstile-144x144.png?cacheid=c25a7641" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/mstile-150x150.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/mstile-150x150.png?cacheid=6fa6f467" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/mstile-310x150.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/mstile-310x150.png?cacheid=e0ed9032" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/mstile-310x310.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/mstile-310x310.png?cacheid=26b20530" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/mstile-70x70.png" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/mstile-70x70.png?cacheid=64ffd9dc" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/safari-pinned-tab.svg" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/safari-pinned-tab.svg?cacheid=8d487e95" }, - { DYNAMIC_CONTENT, "/ROOT/skin/favicon/site.webmanifest" }, - { STATIC_CONTENT, "/ROOT/skin/favicon/site.webmanifest?cacheid=bc396efb" }, - { DYNAMIC_CONTENT, "/ROOT/skin/hash.png" }, - { STATIC_CONTENT, "/ROOT/skin/hash.png?cacheid=f836e872" }, - { DYNAMIC_CONTENT, "/ROOT/skin/magnet.png" }, - { STATIC_CONTENT, "/ROOT/skin/magnet.png?cacheid=73b6bddf" }, - { DYNAMIC_CONTENT, "/ROOT/skin/search-icon.svg" }, - { STATIC_CONTENT, "/ROOT/skin/search-icon.svg?cacheid=b10ae7ed" }, - { DYNAMIC_CONTENT, "/ROOT/skin/search_results.css" }, - { STATIC_CONTENT, "/ROOT/skin/search_results.css?cacheid=76d39c84" }, - { DYNAMIC_CONTENT, "/ROOT/skin/i18n/test.json" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/bittorrent.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/bittorrent.png?cacheid=4f5c6882" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/blank.html" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/blank.html?cacheid=6b1fa032" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/caret.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/caret.png?cacheid=22b942b4" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/download.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/download.png?cacheid=a39aa502" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/android-chrome-192x192.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/android-chrome-192x192.png?cacheid=bfac158b" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/android-chrome-512x512.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/android-chrome-512x512.png?cacheid=380c3653" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/apple-touch-icon.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/apple-touch-icon.png?cacheid=f86f8df3" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/browserconfig.xml" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/browserconfig.xml?cacheid=f29a7c4a" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/favicon-16x16.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/favicon-16x16.png?cacheid=a986fedc" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/favicon-32x32.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/favicon-32x32.png?cacheid=79ded625" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-144x144.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-144x144.png?cacheid=c25a7641" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-150x150.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-150x150.png?cacheid=6fa6f467" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-310x150.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-310x150.png?cacheid=e0ed9032" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-310x310.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-310x310.png?cacheid=26b20530" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-70x70.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/mstile-70x70.png?cacheid=64ffd9dc" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/safari-pinned-tab.svg" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/safari-pinned-tab.svg?cacheid=8d487e95" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/favicon/site.webmanifest" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/favicon/site.webmanifest?cacheid=bc396efb" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/hash.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/hash.png?cacheid=f836e872" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/magnet.png" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/magnet.png?cacheid=73b6bddf" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/search-icon.svg" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/search-icon.svg?cacheid=b10ae7ed" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/search_results.css" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/search_results.css?cacheid=76d39c84" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/i18n/test.json" }, // TODO: implement cache management of i18n resources - //{ STATIC_CONTENT, "/ROOT/skin/i18n/test.json?cacheid=unknown" }, - { DYNAMIC_CONTENT, "/ROOT/skin/languages.js" }, - { STATIC_CONTENT, "/ROOT/skin/languages.js?cacheid=fe100348" }, + //{ STATIC_CONTENT, "/ROOT%23%3F/skin/i18n/test.json?cacheid=unknown" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/languages.js" }, + { STATIC_CONTENT, "/ROOT%23%3F/skin/languages.js?cacheid=fe100348" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Title" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Description" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Language" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Name" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Tags" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Date" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Creator" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Publisher" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Title" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Description" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Language" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Name" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Tags" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Date" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Creator" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Publisher" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/root.xml" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/searchdescription.xml" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/root.xml" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/searchdescription.xml" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/v2/categories" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/v2/languages" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/v2/searchdescription.xml" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/v2/illustration/6f1d19d0-633f-087b-fb55-7ac324ff9baf?size=48" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/v2/categories" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/v2/languages" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/v2/searchdescription.xml" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/v2/illustration/6f1d19d0-633f-087b-fb55-7ac324ff9baf?size=48" }, - { DYNAMIC_CONTENT, "/ROOT/catch/external?source=www.example.com" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catch/external?source=www.example.com" }, - { ZIM_CONTENT, "/ROOT/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg" }, + { ZIM_CONTENT, "/ROOT%23%3F/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg" }, - { ZIM_CONTENT, "/ROOT/content/corner_cases%23%26/empty.html" }, - { ZIM_CONTENT, "/ROOT/content/corner_cases%23%26/empty.css" }, - { ZIM_CONTENT, "/ROOT/content/corner_cases%23%26/empty.js" }, + { ZIM_CONTENT, "/ROOT%23%3F/content/corner_cases%23%26/empty.html" }, + { ZIM_CONTENT, "/ROOT%23%3F/content/corner_cases%23%26/empty.css" }, + { ZIM_CONTENT, "/ROOT%23%3F/content/corner_cases%23%26/empty.js" }, // The following url's responses are too small to be compressed - { DYNAMIC_CONTENT, "/ROOT/catalog/root.xml" }, - { DYNAMIC_CONTENT, "/ROOT/catalog/searchdescription.xml" }, - { DYNAMIC_CONTENT, "/ROOT/suggest?content=zimfile" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Creator" }, - { ZIM_CONTENT, "/ROOT/raw/zimfile/meta/Title" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/root.xml" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/catalog/searchdescription.xml" }, + { DYNAMIC_CONTENT, "/ROOT%23%3F/suggest?content=zimfile" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Creator" }, + { ZIM_CONTENT, "/ROOT%23%3F/raw/zimfile/meta/Title" }, }; ResourceCollection all200Resources() @@ -192,7 +192,7 @@ TEST(indexTemplateStringTest, emptyIndexTemplate) { }; ZimFileServer zfs(PORT, ZimFileServer::DEFAULT_OPTIONS, ZIMFILES, ""); - EXPECT_EQ(200, zfs.GET("/ROOT/")->status); + EXPECT_EQ(200, zfs.GET("/ROOT%23%3F/")->status); } TEST(indexTemplateStringTest, indexTemplateCheck) { @@ -209,7 +209,7 @@ TEST(indexTemplateStringTest, indexTemplateCheck) { EXPECT_EQ("" "Welcome to kiwix library" "" - "", zfs.GET("/ROOT/")->body); + "", zfs.GET("/ROOT%23%3F/")->body); } TEST_F(ServerTest, 200) @@ -220,11 +220,11 @@ TEST_F(ServerTest, 200) TEST_F(ServerTest, 200_IdNameMapper) { - EXPECT_EQ(404, zfs1_->GET("/ROOT/content/6f1d19d0-633f-087b-fb55-7ac324ff9baf/A/index")->status); - EXPECT_EQ(200, zfs1_->GET("/ROOT/content/zimfile/A/index")->status); + EXPECT_EQ(404, zfs1_->GET("/ROOT%23%3F/content/6f1d19d0-633f-087b-fb55-7ac324ff9baf/A/index")->status); + EXPECT_EQ(200, zfs1_->GET("/ROOT%23%3F/content/zimfile/A/index")->status); resetServer(ZimFileServer::NO_NAME_MAPPER); - EXPECT_EQ(200, zfs1_->GET("/ROOT/content/6f1d19d0-633f-087b-fb55-7ac324ff9baf/A/index")->status); - EXPECT_EQ(404, zfs1_->GET("/ROOT/content/zimfile/A/index")->status); + EXPECT_EQ(200, zfs1_->GET("/ROOT%23%3F/content/6f1d19d0-633f-087b-fb55-7ac324ff9baf/A/index")->status); + EXPECT_EQ(404, zfs1_->GET("/ROOT%23%3F/content/zimfile/A/index")->status); } TEST_F(ServerTest, CompressibleContentIsCompressedIfAcceptable) @@ -267,29 +267,29 @@ TEST_F(ServerTest, CacheIdsOfStaticResources) typedef std::pair UrlAndExpectedResult; const std::vector testData{ { - /* url */ "/ROOT/", -R"EXPECTEDRESULT( href="/ROOT/skin/index.css?cacheid=0f9ba34e" - - - - - - - - src: url("/ROOT/skin/fonts/Poppins.ttf?cacheid=af705837") format("truetype"); - src: url("/ROOT/skin/fonts/Roboto.ttf?cacheid=84d10248") format("truetype"); - - - + /* url */ "/ROOT%23%3F/", +R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/index.css?cacheid=0f9ba34e" + + + + + + + + src: url("/ROOT%23%3F/skin/fonts/Poppins.ttf?cacheid=af705837") format("truetype"); + src: url("/ROOT%23%3F/skin/fonts/Roboto.ttf?cacheid=84d10248") format("truetype"); + + + )EXPECTEDRESULT" }, { - /* url */ "/ROOT/skin/index.css", + /* url */ "/ROOT%23%3F/skin/index.css", R"EXPECTEDRESULT( background-image: url('../skin/search-icon.svg?cacheid=b10ae7ed'); )EXPECTEDRESULT" }, { - /* url */ "/ROOT/skin/index.js", + /* url */ "/ROOT%23%3F/skin/index.js", R"EXPECTEDRESULT( direct download download hash download magnet @@ -297,7 +297,7 @@ R"EXPECTEDRESULT( @@ -310,14 +310,14 @@ R"EXPECTEDRESULT( + /* url */ "/ROOT%23%3F/search?content=poor&pattern=whatever", +R"EXPECTEDRESULT( )EXPECTEDRESULT" }, }; @@ -333,15 +333,15 @@ R"EXPECTEDRESULT( GET("/ROOT/non-existent-item"); + // ServerTest.404 verifies that "/ROOT%23%3F/non-existent-item" doesn't exist + const auto r = zfs1_->GET("/ROOT%23%3F/non-existent-item"); EXPECT_EQ(r->status, 200); EXPECT_EQ(getHeaderValue(r->headers, "Content-Type"), "text/plain"); EXPECT_EQ(r->body, "Hello world!\n"); @@ -432,35 +432,35 @@ TEST_F(CustomizedServerTest, NewResourcesCanBeAdded) TEST_F(CustomizedServerTest, ContentOfAnyServableUrlCanBeOverriden) { { - const auto r = zfs1_->GET("/ROOT/"); + const auto r = zfs1_->GET("/ROOT%23%3F/"); EXPECT_EQ(r->status, 200); EXPECT_EQ(getHeaderValue(r->headers, "Content-Type"), "text/html"); EXPECT_EQ(r->body, "Welcome\n"); } { - const auto r = zfs1_->GET("/ROOT/skin/index.css"); + const auto r = zfs1_->GET("/ROOT%23%3F/skin/index.css"); EXPECT_EQ(r->status, 200); EXPECT_EQ(getHeaderValue(r->headers, "Content-Type"), "application/json"); EXPECT_EQ(r->body, "Hello world!\n"); } { - const auto r = zfs1_->GET("/ROOT/zimfile/A/Ray_Charles"); + const auto r = zfs1_->GET("/ROOT%23%3F/zimfile/A/Ray_Charles"); EXPECT_EQ(r->status, 200); EXPECT_EQ(getHeaderValue(r->headers, "Content-Type"), "ray/charles"); EXPECT_EQ(r->body, "Welcome\n"); } { - const auto r = zfs1_->GET("/ROOT/content/zimfile/A/Ray_Charles"); + const auto r = zfs1_->GET("/ROOT%23%3F/content/zimfile/A/Ray_Charles"); EXPECT_EQ(r->status, 200); EXPECT_EQ(getHeaderValue(r->headers, "Content-Type"), "charles/ray"); EXPECT_EQ(r->body, "Welcome\n"); } { - const auto r = zfs1_->GET("/ROOT/search?pattern=la+femme"); + const auto r = zfs1_->GET("/ROOT%23%3F/search?pattern=la+femme"); EXPECT_EQ(r->status, 200); EXPECT_EQ(getHeaderValue(r->headers, "Content-Type"), "text/html"); EXPECT_EQ(r->body, "Hello world!\n"); @@ -614,7 +614,7 @@ TEST_F(ServerTest, Http404HtmlError) { using namespace TestingOfHtmlResponses; const std::vector testData{ - { /* url */ "/ROOT/random?content=non-existent-book", + { /* url */ "/ROOT%23%3F/random?content=non-existent-book", expected_body==R"(

Not Found

@@ -622,7 +622,7 @@ TEST_F(ServerTest, Http404HtmlError)

)" }, - { /* url */ "/ROOT/random?content=non-existent-book&userlang=test", + { /* url */ "/ROOT%23%3F/random?content=non-existent-book&userlang=test", expected_page_title=="[I18N TESTING] Not Found - Try Again" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

@@ -631,7 +631,7 @@ TEST_F(ServerTest, Http404HtmlError)

)" }, - { /* url */ "/ROOT/suggest?content=no-such-book&term=whatever", + { /* url */ "/ROOT%23%3F/suggest?content=no-such-book&term=whatever", expected_body==R"(

Not Found

@@ -639,149 +639,149 @@ TEST_F(ServerTest, Http404HtmlError)

)" }, - { /* url */ "/ROOT/catalog/", + { /* url */ "/ROOT%23%3F/catalog/", expected_body==R"(

Not Found

- The requested URL "/ROOT/catalog/" was not found on this server. + The requested URL "/ROOT%23%3F/catalog/" was not found on this server.

)" }, - { /* url */ "/ROOT/catalog/?userlang=test", + { /* url */ "/ROOT%23%3F/catalog/?userlang=test", expected_page_title=="[I18N TESTING] Not Found - Try Again" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

- [I18N TESTING] URL not found: /ROOT/catalog/ + [I18N TESTING] URL not found: /ROOT%23%3F/catalog/

)" }, - { /* url */ "/ROOT/catalog/invalid_endpoint", + { /* url */ "/ROOT%23%3F/catalog/invalid_endpoint", expected_body==R"(

Not Found

- The requested URL "/ROOT/catalog/invalid_endpoint" was not found on this server. + The requested URL "/ROOT%23%3F/catalog/invalid_endpoint" was not found on this server.

)" }, - { /* url */ "/ROOT/catalog/invalid_endpoint?userlang=test", + { /* url */ "/ROOT%23%3F/catalog/invalid_endpoint?userlang=test", expected_page_title=="[I18N TESTING] Not Found - Try Again" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

- [I18N TESTING] URL not found: /ROOT/catalog/invalid_endpoint + [I18N TESTING] URL not found: /ROOT%23%3F/catalog/invalid_endpoint

)" }, - { /* url */ "/ROOT/content/invalid-book/whatever", + { /* url */ "/ROOT%23%3F/content/invalid-book/whatever", expected_body==R"(

Not Found

- The requested URL "/ROOT/content/invalid-book/whatever" was not found on this server. + The requested URL "/ROOT%23%3F/content/invalid-book/whatever" was not found on this server.

- Make a full text search for whatever + Make a full text search for whatever

)" }, - { /* url */ "/ROOT/content/zimfile/invalid-article", + { /* url */ "/ROOT%23%3F/content/zimfile/invalid-article", book_name=="zimfile" && book_title=="Ray Charles" && expected_body==R"(

Not Found

- The requested URL "/ROOT/content/zimfile/invalid-article" was not found on this server. + The requested URL "/ROOT%23%3F/content/zimfile/invalid-article" was not found on this server.

- Make a full text search for invalid-article + Make a full text search for invalid-article

)" }, - { /* url */ R"(/ROOT/content/">)", + { /* url */ R"(/ROOT%23%3F/content/">)", expected_body==R"(

Not Found

- The requested URL "/ROOT/content/"><svg onload=alert(1)>" was not found on this server. + The requested URL "/ROOT%23%3F/content/"><svg onload%3Dalert(1)>" was not found on this server.

- Make a full text search for "><svg onload=alert(1)> + Make a full text search for "><svg onload=alert(1)>

)" }, - { /* url */ R"(/ROOT/content/zimfile/">)", + { /* url */ R"(/ROOT%23%3F/content/zimfile/">)", book_name=="zimfile" && book_title=="Ray Charles" && expected_body==R"(

Not Found

- The requested URL "/ROOT/content/zimfile/"><svg onload=alert(1)>" was not found on this server. + The requested URL "/ROOT%23%3F/content/zimfile/"><svg onload%3Dalert(1)>" was not found on this server.

- Make a full text search for "><svg onload=alert(1)> + Make a full text search for "><svg onload=alert(1)>

)" }, - { /* url */ "/ROOT/content/zimfile/invalid-article?userlang=test", + { /* url */ "/ROOT%23%3F/content/zimfile/invalid-article?userlang=test", expected_page_title=="[I18N TESTING] Not Found - Try Again" && book_name=="zimfile" && book_title=="Ray Charles" && expected_body==R"(

[I18N TESTING] Content not found, but at least the server is alive

- [I18N TESTING] URL not found: /ROOT/content/zimfile/invalid-article + [I18N TESTING] URL not found: /ROOT%23%3F/content/zimfile/invalid-article

- [I18N TESTING] Make a full text search for invalid-article + [I18N TESTING] Make a full text search for invalid-article

)" }, - { /* url */ "/ROOT/raw/no-such-book/meta/Title", + { /* url */ "/ROOT%23%3F/raw/no-such-book/meta/Title", expected_body==R"(

Not Found

- The requested URL "/ROOT/raw/no-such-book/meta/Title" was not found on this server. + The requested URL "/ROOT%23%3F/raw/no-such-book/meta/Title" was not found on this server.

No such book: no-such-book

)" }, - { /* url */ "/ROOT/raw/zimfile/XYZ", + { /* url */ "/ROOT%23%3F/raw/zimfile/XYZ", expected_body==R"(

Not Found

- The requested URL "/ROOT/raw/zimfile/XYZ" was not found on this server. + The requested URL "/ROOT%23%3F/raw/zimfile/XYZ" was not found on this server.

XYZ is not a valid request for raw content.

)" }, - { /* url */ "/ROOT/raw/zimfile/meta/invalid-metadata", + { /* url */ "/ROOT%23%3F/raw/zimfile/meta/invalid-metadata", expected_body==R"(

Not Found

- The requested URL "/ROOT/raw/zimfile/meta/invalid-metadata" was not found on this server. + The requested URL "/ROOT%23%3F/raw/zimfile/meta/invalid-metadata" was not found on this server.

Cannot find meta entry invalid-metadata

)" }, - { /* url */ "/ROOT/raw/zimfile/content/invalid-article", + { /* url */ "/ROOT%23%3F/raw/zimfile/content/invalid-article", expected_body==R"(

Not Found

- The requested URL "/ROOT/raw/zimfile/content/invalid-article" was not found on this server. + The requested URL "/ROOT%23%3F/raw/zimfile/content/invalid-article" was not found on this server.

Cannot find content entry invalid-article

)" }, - { /* url */ "/ROOT/search?content=poor&pattern=whatever", + { /* url */ "/ROOT%23%3F/search?content=poor&pattern=whatever", expected_page_title=="Fulltext search unavailable" && - expected_css_url=="/ROOT/skin/search_results.css?cacheid=76d39c84" && + expected_css_url=="/ROOT%23%3F/skin/search_results.css?cacheid=76d39c84" && book_name=="poor" && book_title=="poor" && expected_body==R"( @@ -804,41 +804,41 @@ TEST_F(ServerTest, Http400HtmlError) { using namespace TestingOfHtmlResponses; const std::vector testData{ - { /* url */ "/ROOT/search", + { /* url */ "/ROOT%23%3F/search", expected_body== R"(

Invalid request

- The requested URL "/ROOT/search" is not a valid request. + The requested URL "/ROOT%23%3F/search" is not a valid request.

Too many books requested (4) where limit is 3

)" }, - { /* url */ "/ROOT/search?content=zimfile", + { /* url */ "/ROOT%23%3F/search?content=zimfile", expected_body==R"(

Invalid request

- The requested URL "/ROOT/search?content=zimfile" is not a valid request. + The requested URL "/ROOT%23%3F/search?content=zimfile" is not a valid request.

No query provided.

)" }, - { /* url */ "/ROOT/search?content=non-existing-book&pattern=asdfqwerty", + { /* url */ "/ROOT%23%3F/search?content=non-existing-book&pattern=asdfqwerty", expected_body==R"(

Invalid request

- The requested URL "/ROOT/search?content=non-existing-book&pattern=asdfqwerty" is not a valid request. + The requested URL "/ROOT%23%3F/search?content=non-existing-book&pattern=asdfqwerty" is not a valid request.

No such book: non-existing-book

)" }, - { /* url */ "/ROOT/search?content=non-existing-book&pattern=a\"