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.
This commit is contained in:
Veloman Yunkan 2022-06-25 17:54:21 +04:00
parent a4b18893aa
commit 1b1c1e352e
3 changed files with 104 additions and 14 deletions

View File

@ -545,6 +545,9 @@ std::unique_ptr<Response> 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<Response>
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<Response> 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<zim::Archive> archive;
try {
@ -973,7 +971,7 @@ std::unique_ptr<Response> 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);
}

View File

@ -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

View File

@ -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( <img src="../skin/download.png?
R"EXPECTEDRESULT(<link type="root" href="/ROOT"><link type="text/css" href="/ROOT/skin/taskbar.css?cacheid=26082885" rel="Stylesheet" />
<link type="text/css" href="/ROOT/skin/css/autoComplete.css?cacheid=08951e06" rel="Stylesheet" />
<script type="text/javascript" src="/ROOT/skin/taskbar.js?cacheid=1aec4a68" defer></script>
<script type="text/javascript" src="/ROOT/skin/autoComplete.min.js?cacheid=1191aaaf"></script>
<label for="kiwix_button_show_toggle"><img src="/ROOT/skin/caret.png?cacheid=22b942b4" alt=""></label>
)EXPECTEDRESULT"
},
{
/* url */ "/ROOT/content/zimfile/A/index",
R"EXPECTEDRESULT(<link type="root" href="/ROOT"><link type="text/css" href="/ROOT/skin/taskbar.css?cacheid=26082885" rel="Stylesheet" />
<link type="text/css" href="/ROOT/skin/css/autoComplete.css?cacheid=08951e06" rel="Stylesheet" />
<script type="text/javascript" src="/ROOT/skin/taskbar.js?cacheid=1aec4a68" defer></script>
<script type="text/javascript" src="/ROOT/skin/autoComplete.min.js?cacheid=1191aaaf"></script>
<label for="kiwix_button_show_toggle"><img src="/ROOT/skin/caret.png?cacheid=22b942b4" alt=""></label>
)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, "<html><head></head><body>Welcome</body></html>\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, "<html><head></head><body>Welcome</body></html>\n");
}
{
const auto r = zfs1_->GET("/ROOT/search?pattern=la+femme");
EXPECT_EQ(r->status, 200);
@ -685,6 +713,19 @@ TEST_F(ServerTest, Http404HtmlError)
</p>
)" },
{ /* url */ "/ROOT/content/zimfile/invalid-article",
book_name=="zimfile" &&
book_title=="Ray Charles" &&
expected_body==R"(
<h1>Not Found</h1>
<p>
The requested URL "/ROOT/content/zimfile/invalid-article" was not found on this server.
</p>
<p>
Make a full text search for <a href="/ROOT/search?content=zimfile&pattern=invalid-article">invalid-article</a>
</p>
)" },
{ /* url */ R"(/ROOT/"><svg onload=alert(1)>)",
expected_body==R"(
<h1>Not Found</h1>
@ -709,6 +750,19 @@ TEST_F(ServerTest, Http404HtmlError)
</p>
)" },
{ /* url */ R"(/ROOT/content/zimfile/"><svg onload=alert(1)>)",
book_name=="zimfile" &&
book_title=="Ray Charles" &&
expected_body==R"(
<h1>Not Found</h1>
<p>
The requested URL "/ROOT/content/zimfile/&quot;&gt;&lt;svg onload=alert(1)&gt;" was not found on this server.
</p>
<p>
Make a full text search for <a href="/ROOT/search?content=zimfile&pattern=%22%3E%3Csvg%20onload%3Dalert(1)%3E">&quot;&gt;&lt;svg onload=alert(1)&gt;</a>
</p>
)" },
{ /* url */ "/ROOT/zimfile/invalid-article?userlang=hy",
expected_page_title=="Սխալ հասցե" &&
book_name=="zimfile" &&
@ -723,6 +777,20 @@ TEST_F(ServerTest, Http404HtmlError)
</p>
)" },
{ /* url */ "/ROOT/content/zimfile/invalid-article?userlang=hy",
expected_page_title=="Սխալ հասցե" &&
book_name=="zimfile" &&
book_title=="Ray Charles" &&
expected_body==R"(
<h1>Սխալ հասցե</h1>
<p>
Սխալ հասցե՝ /ROOT/content/zimfile/invalid-article
</p>
<p>
Որոնել <a href="/ROOT/search?content=zimfile&pattern=invalid-article">invalid-article</a>
</p>
)" },
{ /* url */ "/ROOT/raw/no-such-book/meta/Title",
expected_body==R"(
<h1>Not Found</h1>
@ -964,9 +1032,17 @@ TEST_F(ServerTest, 500)
</html>
)";
{
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)