From 1b1c1e352eb4ec65c2736116230ab7f85f7b1c4d Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Sat, 25 Jun 2022 17:54:21 +0400 Subject: [PATCH] Introduced /content endpoint Book content is now served under /content/book/... The old access to book content via a top-level URL /book/... is so far preserved for backward compatibility. Redirects were changed to use the new URL scheme. Links in the search results still use the old scheme. --- src/server/internalServer.cpp | 22 ++++--- test/data/customized_resources.txt | 1 + test/server.cpp | 95 +++++++++++++++++++++++++++++- 3 files changed, 104 insertions(+), 14 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index 75a0e4738..d3d5aef42 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -545,6 +545,9 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r if (startsWith(request.get_url(), "/skin/")) return handle_skin(request); + if (startsWith(request.get_url(), "/content/")) + return handle_content(request); + if (startsWith(request.get_url(), "/catalog/")) return handle_catalog(request); @@ -922,15 +925,6 @@ InternalServer::search_catalog(const RequestContext& request, namespace { -std::string get_book_name(const RequestContext& request) -{ - try { - return request.get_url_part(0); - } catch (const std::out_of_range& e) { - return std::string(); - } -} - ParameterizedMessage suggestSearchMsg(const std::string& searchURL, const std::string& pattern) { return ParameterizedMessage("suggest-search", @@ -945,7 +939,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 { - auto redirectUrl = m_root + "/" + bookName + "/" + kiwix::urlEncode(item.getPath()); + const auto path = kiwix::urlEncode(item.getPath()); + const auto redirectUrl = m_root + "/content/" + bookName + "/" + path; return Response::build_redirect(*this, redirectUrl); } @@ -957,7 +952,10 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r printf("** running handle_content\n"); } - const std::string bookName = get_book_name(request); + const std::string contentPrefix = "/content/"; + const bool isContentPrefixedUrl = startsWith(url, contentPrefix); + const size_t prefixLength = isContentPrefixedUrl ? contentPrefix.size() : 1; + const std::string bookName = request.get_url_part(isContentPrefixedUrl); std::shared_ptr archive; try { @@ -973,7 +971,7 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r + TaskbarInfo(bookName); } - auto urlStr = request.get_url().substr(bookName.size()+1); + auto urlStr = url.substr(prefixLength + bookName.size()); if (urlStr[0] == '/') { urlStr = urlStr.substr(1); } diff --git a/test/data/customized_resources.txt b/test/data/customized_resources.txt index 5c5f2b72e..16c4b69c2 100644 --- a/test/data/customized_resources.txt +++ b/test/data/customized_resources.txt @@ -2,4 +2,5 @@ / text/html ./test/welcome.html /skin/index.css application/json ./test/helloworld.txt /zimfile/A/Ray_Charles ray/charles ./test/welcome.html +/content/zimfile/A/Ray_Charles charles/ray ./test/welcome.html /search text/html ./test/helloworld.txt diff --git a/test/server.cpp b/test/server.cpp index 034a398cb..b50dbe396 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -6,6 +6,8 @@ #define SERVER_PORT 8001 #include "server_testing_tools.h" +#include "../src/tools/stringTools.h" + bool is_valid_etag(const std::string& etag) { @@ -56,6 +58,9 @@ const ResourceCollection resources200Compressible{ { WITH_ETAG, "/ROOT/zimfile/A/index" }, { WITH_ETAG, "/ROOT/zimfile/A/Ray_Charles" }, + { WITH_ETAG, "/ROOT/content/zimfile/A/index" }, + { WITH_ETAG, "/ROOT/content/zimfile/A/Ray_Charles" }, + { WITH_ETAG, "/ROOT/raw/zimfile/content/A/index" }, { WITH_ETAG, "/ROOT/raw/zimfile/content/A/Ray_Charles" }, }; @@ -75,11 +80,17 @@ const ResourceCollection resources200Uncompressible{ { NO_ETAG, "/ROOT/catalog/v2/illustration/6f1d19d0-633f-087b-fb55-7ac324ff9baf?size=48" }, { WITH_ETAG, "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg" }, + { WITH_ETAG, "/ROOT/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg" }, { WITH_ETAG, "/ROOT/corner_cases/A/empty.html" }, { WITH_ETAG, "/ROOT/corner_cases/-/empty.css" }, { WITH_ETAG, "/ROOT/corner_cases/-/empty.js" }, + { WITH_ETAG, "/ROOT/content/corner_cases/A/empty.html" }, + { WITH_ETAG, "/ROOT/content/corner_cases/-/empty.css" }, + { WITH_ETAG, "/ROOT/content/corner_cases/-/empty.js" }, + + // The following url's responses are too small to be compressed { NO_ETAG, "/ROOT/catalog/root.xml" }, { NO_ETAG, "/ROOT/catalog/searchdescription.xml" }, @@ -197,6 +208,15 @@ R"EXPECTEDRESULT( + + +)EXPECTEDRESULT" + }, + { + /* url */ "/ROOT/content/zimfile/A/index", +R"EXPECTEDRESULT( + + )EXPECTEDRESULT" @@ -265,6 +285,7 @@ const char* urls404[] = { "/ROOT/suggest?content=non-existent-book&term=abcd", "/ROOT/catch/external", "/ROOT/zimfile/A/non-existent-article", + "/ROOT/content/zimfile/A/non-existent-article", "/ROOT/raw/non-existent-book/meta/Title", "/ROOT/raw/zimfile/wrong-kind/Foo", @@ -335,6 +356,13 @@ TEST_F(CustomizedServerTest, ContentOfAnyServableUrlCanBeOverriden) EXPECT_EQ(r->body, "Welcome\n"); } + { + const auto r = zfs1_->GET("/ROOT/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"); EXPECT_EQ(r->status, 200); @@ -685,6 +713,19 @@ TEST_F(ServerTest, Http404HtmlError)

)" }, + { /* url */ "/ROOT/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. +

+

+ Make a full text search for invalid-article +

+)" }, + { /* url */ R"(/ROOT/">)", expected_body==R"(

Not Found

@@ -709,6 +750,19 @@ TEST_F(ServerTest, Http404HtmlError)

)" }, + { /* url */ R"(/ROOT/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. +

+

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

+)" }, + { /* url */ "/ROOT/zimfile/invalid-article?userlang=hy", expected_page_title=="Սխալ հասցե" && book_name=="zimfile" && @@ -723,6 +777,20 @@ TEST_F(ServerTest, Http404HtmlError)

)" }, + { /* url */ "/ROOT/content/zimfile/invalid-article?userlang=hy", + expected_page_title=="Սխալ հասցե" && + book_name=="zimfile" && + book_title=="Ray Charles" && + expected_body==R"( +

Սխալ հասցե

+

+ Սխալ հասցե՝ /ROOT/content/zimfile/invalid-article +

+

+ Որոնել invalid-article +

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

Not Found

@@ -964,9 +1032,17 @@ TEST_F(ServerTest, 500) )"; + { const auto r = zfs1_->GET("/ROOT/poor/A/redirect_loop.html"); EXPECT_EQ(r->status, 500); EXPECT_EQ(r->body, expectedBody); + } + + { + const auto r = zfs1_->GET("/ROOT/content/poor/A/redirect_loop.html"); + EXPECT_EQ(r->status, 500); + EXPECT_EQ(r->body, expectedBody); + } } TEST_F(ServerTest, UserLanguageControl) @@ -1047,15 +1123,24 @@ TEST_F(ServerTest, RandomPageRedirectsToAnExistingArticle) auto g = zfs1_->GET("/ROOT/random?content=zimfile"); ASSERT_EQ(302, g->status); ASSERT_TRUE(g->has_header("Location")); - ASSERT_TRUE(g->get_header_value("Location").find("/zimfile/A/") != std::string::npos); + ASSERT_TRUE(kiwix::startsWith(g->get_header_value("Location"), "/ROOT/content/zimfile/A/")); } TEST_F(ServerTest, BookMainPageIsRedirectedToArticleIndex) { + { auto g = zfs1_->GET("/ROOT/zimfile"); ASSERT_EQ(302, g->status); ASSERT_TRUE(g->has_header("Location")); - ASSERT_EQ("/ROOT/zimfile/A/index", g->get_header_value("Location")); + ASSERT_EQ("/ROOT/content/zimfile/A/index", g->get_header_value("Location")); + } + + { + auto g = zfs1_->GET("/ROOT/content/zimfile"); + ASSERT_EQ(302, g->status); + ASSERT_TRUE(g->has_header("Location")); + ASSERT_EQ("/ROOT/content/zimfile/A/index", g->get_header_value("Location")); + } } @@ -1082,6 +1167,12 @@ TEST_F(ServerTest, RawEntry) EXPECT_EQ(200, p->status); EXPECT_NE(std::string(p->body), std::string(entry.getItem(true).getData())); EXPECT_TRUE(p->body.find("taskbar") != std::string::npos); + + // ... but the "normal" content is not + p = zfs1_->GET("/ROOT/content/zimfile/A/Ray_Charles"); + EXPECT_EQ(200, p->status); + EXPECT_NE(std::string(p->body), std::string(entry.getItem(true).getData())); + EXPECT_TRUE(p->body.find("taskbar") != std::string::npos); } TEST_F(ServerTest, HeadMethodIsSupported)