From e323dcf6c9c00cd1e0acce5638df147293e659d0 Mon Sep 17 00:00:00 2001 From: Veloman Yunkan Date: Fri, 5 Aug 2022 16:39:24 +0400 Subject: [PATCH] Redirecting /nonendpoint URLs to /content/nonendpoint --- src/server/internalServer.cpp | 6 +- test/server.cpp | 159 +++++++++++++--------------------- 2 files changed, 65 insertions(+), 100 deletions(-) diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp index f0a3750eb..43411260f 100644 --- a/src/server/internalServer.cpp +++ b/src/server/internalServer.cpp @@ -577,7 +577,11 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r if (isEndpointUrl(url, "catch")) return handle_catch(request); - return handle_content(request); + std::string contentUrl = m_root + "/content" + url; + const std::string query = request.get_query(); + if ( ! query.empty() ) + contentUrl += "?" + query; + return Response::build_redirect(*this, contentUrl); } catch (std::exception& e) { fprintf(stderr, "===== Unhandled error : %s\n", e.what()); return HTTP500Response(*this, request) diff --git a/test/server.cpp b/test/server.cpp index 8b77f97d9..886fcf542 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -55,9 +55,6 @@ const ResourceCollection resources200Compressible{ { NO_ETAG, "/ROOT/catch/external?source=www.example.com" }, - { 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" }, @@ -79,13 +76,8 @@ 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" }, @@ -201,15 +193,6 @@ R"EXPECTEDRESULT( download hash download magnet download torrent -)EXPECTEDRESULT" - }, - { - /* url */ "/ROOT/zimfile/A/index", -R"EXPECTEDRESULT( - - - - )EXPECTEDRESULT" }, { @@ -268,18 +251,12 @@ TEST_F(ServerTest, 400) const char* urls404[] = { "/", "/zimfile", - "/ROOT/non-existent-item", "/ROOT/skin/non-existent-skin-resource", "/ROOT/catalog", "/ROOT/catalog/", "/ROOT/catalog/non-existent-item", - "/ROOT/catalogBLABLABLA/root.xml", "/ROOT/catalog/v2/illustration/zimfile?size=48", "/ROOT/catalog/v2/illustration/6f1d19d0-633f-087b-fb55-7ac324ff9baf?size=96", - "/ROOT/meta", - "/ROOT/meta?content=zimfile", - "/ROOT/meta?content=zimfile&name=non-existent-item", - "/ROOT/meta?content=non-existent-book&name=title", "/ROOT/random", "/ROOT/random?content=non-existent-book", "/ROOT/random/", @@ -294,7 +271,6 @@ const char* urls404[] = { "/ROOT/catch/external", // missing ?source=URL "/ROOT/catch/external?source=", "/ROOT/catch/anythingotherthanexternal", - "/ROOT/zimfile/A/non-existent-article", "/ROOT/content/zimfile/A/non-existent-article", "/ROOT/raw/non-existent-book/meta/Title", @@ -699,30 +675,17 @@ TEST_F(ServerTest, Http404HtmlError)

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

Not Found

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

Make a full text search for whatever

)" }, - { /* url */ "/ROOT/zimfile/invalid-article", - book_name=="zimfile" && - book_title=="Ray Charles" && - expected_body==R"( -

Not Found

-

- The requested URL "/ROOT/zimfile/invalid-article" was not found on this server. -

-

- Make a full text search for invalid-article -

-)" }, - { /* url */ "/ROOT/content/zimfile/invalid-article", book_name=="zimfile" && book_title=="Ray Charles" && @@ -736,30 +699,17 @@ TEST_F(ServerTest, Http404HtmlError)

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

Not Found

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

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

)" }, - { /* url */ R"(/ROOT/zimfile/">)", - book_name=="zimfile" && - book_title=="Ray Charles" && - expected_body==R"( -

Not Found

-

- The requested URL "/ROOT/zimfile/"><svg onload=alert(1)>" was not found on this server. -

-

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

-)" }, - { /* url */ R"(/ROOT/content/zimfile/">)", book_name=="zimfile" && book_title=="Ray Charles" && @@ -773,20 +723,6 @@ TEST_F(ServerTest, Http404HtmlError)

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

Սխալ հասցե

-

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

-

- Որոնել invalid-article -

-)" }, - { /* url */ "/ROOT/content/zimfile/invalid-article?userlang=hy", expected_page_title=="Սխալ հասցե" && book_name=="zimfile" && @@ -1042,12 +978,6 @@ 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); @@ -1074,33 +1004,33 @@ TEST_F(ServerTest, UserLanguageControl) const TestData testData[] = { { - /*url*/ "/ROOT/zimfile/invalid-article", + /*url*/ "/ROOT/content/zimfile/invalid-article", /*Accept-Language:*/ "", /* expected

*/ "Not Found" }, { - /*url*/ "/ROOT/zimfile/invalid-article?userlang=en", + /*url*/ "/ROOT/content/zimfile/invalid-article?userlang=en", /*Accept-Language:*/ "", /* expected

*/ "Not Found" }, { - /*url*/ "/ROOT/zimfile/invalid-article?userlang=hy", + /*url*/ "/ROOT/content/zimfile/invalid-article?userlang=hy", /*Accept-Language:*/ "", /* expected

*/ "Սխալ հասցե" }, { - /*url*/ "/ROOT/zimfile/invalid-article", + /*url*/ "/ROOT/content/zimfile/invalid-article", /*Accept-Language:*/ "*", /* expected

*/ "Not Found" }, { - /*url*/ "/ROOT/zimfile/invalid-article", + /*url*/ "/ROOT/content/zimfile/invalid-article", /*Accept-Language:*/ "hy", /* expected

*/ "Սխալ հասցե" }, { // userlang query parameter takes precedence over Accept-Language - /*url*/ "/ROOT/zimfile/invalid-article?userlang=en", + /*url*/ "/ROOT/content/zimfile/invalid-article?userlang=en", /*Accept-Language:*/ "hy", /* expected

*/ "Not Found" }, @@ -1108,7 +1038,7 @@ TEST_F(ServerTest, UserLanguageControl) // The value of the Accept-Language header is not currently parsed. // In case of a comma separated list of languages (optionally weighted // with quality values) the default (en) language is used instead. - /*url*/ "/ROOT/zimfile/invalid-article", + /*url*/ "/ROOT/content/zimfile/invalid-article", /*Accept-Language:*/ "hy;q=0.9, en;q=0.2", /* expected

*/ "Not Found" }, @@ -1136,15 +1066,52 @@ TEST_F(ServerTest, RandomPageRedirectsToAnExistingArticle) ASSERT_TRUE(kiwix::startsWith(g->get_header_value("Location"), "/ROOT/content/zimfile/A/")); } +TEST_F(ServerTest, NonEndpointUrlsAreRedirectedToContentUrls) +{ + const std::string paths[] = { + "/zimfile", + "/zimfile/A/index", + "/some/path", + "/non-existent-item", + + // Validate that paths starting with a string matching an endpoint name + // are not misinterpreted as endpoint URLs + "/catalogarithm", + "/catalogistics/root.xml", + "/catch22", + "/catchy/song", + "/contention", + "/contents/is/not/the/same/as/content", + "/randomize", + "/randomestic/animals", + "/rawman", + "/rawkward/url", + "/searcheology", + "/searchitecture/history", + "/skinhead", + "/skineffect/formula", + "/suggesture", + "/suggestonia/tallinn", + + // The /meta endpoint has been replaced with /raw//meta + "/meta", + + // Make sure that the query is preserved in the redirect URL + "/does?P=NP" + }; + + for ( const std::string& p : paths ) + { + auto g = zfs1_->GET(("/ROOT" + p).c_str()); + const TestContext ctx{ { "path", p } }; + ASSERT_EQ(302, g->status) << ctx; + ASSERT_TRUE(g->has_header("Location")) << ctx; + ASSERT_EQ("/ROOT/content" + p, g->get_header_value("Location")) << ctx; + } +} + TEST_F(ServerTest, BookMainPageIsRedirectedToArticleIndex) { - { - auto g = zfs1_->GET("/ROOT/zimfile"); - ASSERT_EQ(302, g->status); - ASSERT_TRUE(g->has_header("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); @@ -1172,12 +1139,6 @@ TEST_F(ServerTest, RawEntry) EXPECT_EQ(200, p->status); EXPECT_EQ(std::string(p->body), std::string(entry.getItem(true).getData())); - // ... but the "normal" content is not - p = zfs1_->GET("/ROOT/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); - // ... but the "normal" content is not p = zfs1_->GET("/ROOT/content/zimfile/A/Ray_Charles"); EXPECT_EQ(200, p->status); @@ -1343,7 +1304,7 @@ TEST_F(ServerTest, IfNoneMatchRequestsWithMismatchingETagResultIn200Responses) TEST_F(ServerTest, ValidSingleRangeByteRangeRequestsAreHandledProperly) { - const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; + const char url[] = "/ROOT/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const auto full = zfs1_->GET(url); EXPECT_FALSE(full->has_header("Content-Range")); EXPECT_EQ("bytes", full->get_header_value("Accept-Ranges")); @@ -1393,7 +1354,7 @@ TEST_F(ServerTest, ValidSingleRangeByteRangeRequestsAreHandledProperly) TEST_F(ServerTest, InvalidAndMultiRangeByteRangeRequestsResultIn416Responses) { - const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; + const char url[] = "/ROOT/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const char* invalidRanges[] = { "0-10", "bytes=", "bytes=123", "bytes=-10-20", "bytes=10-20xxx", @@ -1414,7 +1375,7 @@ TEST_F(ServerTest, InvalidAndMultiRangeByteRangeRequestsResultIn416Responses) TEST_F(ServerTest, ValidByteRangeRequestsOfZeroSizedEntriesResultIn416Responses) { - const char url[] = "/ROOT/corner_cases/-/empty.js"; + const char url[] = "/ROOT/content/corner_cases/-/empty.js"; const char* ranges[] = { "bytes=0-", @@ -1433,7 +1394,7 @@ TEST_F(ServerTest, ValidByteRangeRequestsOfZeroSizedEntriesResultIn416Responses) TEST_F(ServerTest, RangeHasPrecedenceOverCompression) { - const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; + const char url[] = "/ROOT/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const Headers onlyRange{ {"Range", "bytes=123-456"} }; Headers rangeAndCompression(onlyRange); @@ -1448,7 +1409,7 @@ TEST_F(ServerTest, RangeHasPrecedenceOverCompression) TEST_F(ServerTest, RangeHeaderIsCaseInsensitive) { - const char url[] = "/ROOT/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; + const char url[] = "/ROOT/content/zimfile/I/m/Ray_Charles_classic_piano_pose.jpg"; const auto r0 = zfs1_->GET(url, { {"Range", "bytes=100-200"} } ); const char* header_variations[] = { "RANGE", "range", "rAnGe", "RaNgE" };