Add `raw` endpoint.

As the name suggests it, this endpoint is not smart :
It returns the content as it is and only if it is present
(no compatibility or whatever).

The only "smart" thing is to return a redirect if the entry is a redirect.
This commit is contained in:
Matthieu Gautier 2021-12-08 16:15:39 +01:00
parent 160a74f5f8
commit dc15a9a824
3 changed files with 95 additions and 0 deletions

View File

@ -285,6 +285,9 @@ std::unique_ptr<Response> InternalServer::handle_request(const RequestContext& r
if (startsWith(request.get_url(), "/catalog/"))
return handle_catalog(request);
if (startsWith(request.get_url(), "/raw/"))
return handle_raw(request);
if (request.get_url() == "/meta")
return handle_meta(request);
@ -878,4 +881,62 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
}
}
std::unique_ptr<Response> InternalServer::handle_raw(const RequestContext& request)
{
if (m_verbose.load()) {
printf("** running handle_raw\n");
}
std::string bookName;
std::string kind;
try {
bookName = request.get_url_part(1);
kind = request.get_url_part(2);
} catch (const std::out_of_range& e) {
return Response::build_404(*this, request.get_full_url(), bookName, "", "");
}
if (kind != "meta" && kind!= "content") {
const std::string error_details = kind + " is not a valid request for raw content.";
return Response::build_404(*this, request.get_full_url(), bookName, "", error_details);
}
std::shared_ptr<zim::Archive> archive;
try {
const std::string bookId = mp_nameMapper->getIdForName(bookName);
archive = mp_library->getArchiveById(bookId);
} catch (const std::out_of_range& e) {}
if (archive == nullptr) {
const std::string error_details = "No such book: " + bookName;
return Response::build_404(*this, request.get_full_url(), bookName, "", error_details);
}
// Remove the beggining of the path:
// /raw/<bookName>/<kind>/foo
// ^^^^^ ^ ^
// 5 + 1 + 1 = 7
auto itemPath = request.get_url().substr(bookName.size()+kind.size()+7);
try {
if (kind == "meta") {
auto item = archive->getMetadataItem(itemPath);
return ItemResponse::build(*this, request, item, /*raw=*/true);
} else {
auto entry = archive->getEntryByPath(itemPath);
if (entry.isRedirect()) {
return build_redirect(bookName, entry.getItem(true));
}
return ItemResponse::build(*this, request, entry.getItem(), /*raw=*/true);
}
} catch (zim::EntryNotFound& e ) {
if (m_verbose.load()) {
printf("Failed to find %s\n", itemPath.c_str());
}
const std::string error_details = "Cannot find " + kind + " entry " + itemPath;
return Response::build_404(*this, request.get_full_url(), bookName, getArchiveTitle(*archive), error_details);
}
}
}

View File

@ -89,6 +89,7 @@ class InternalServer {
std::unique_ptr<Response> handle_random(const RequestContext& request);
std::unique_ptr<Response> handle_captured_external(const RequestContext& request);
std::unique_ptr<Response> handle_content(const RequestContext& request);
std::unique_ptr<Response> handle_raw(const RequestContext& request);
std::vector<std::string> search_catalog(const RequestContext& request,
kiwix::OPDSDumper& opdsDumper);

View File

@ -185,6 +185,9 @@ const ResourceCollection resources200Compressible{
{ WITH_ETAG, "/ROOT/zimfile/A/index" },
{ WITH_ETAG, "/ROOT/zimfile/A/Ray_Charles" },
{ WITH_ETAG, "/ROOT/raw/zimfile/content/A/index" },
{ WITH_ETAG, "/ROOT/raw/zimfile/content/A/Ray_Charles" },
};
const ResourceCollection resources200Uncompressible{
@ -208,6 +211,10 @@ const ResourceCollection resources200Uncompressible{
{ WITH_ETAG, "/ROOT/corner_cases/A/empty.html" },
{ WITH_ETAG, "/ROOT/corner_cases/-/empty.css" },
{ WITH_ETAG, "/ROOT/corner_cases/-/empty.js" },
// The title and creator are too small to be compressed
{ WITH_ETAG, "/ROOT/raw/zimfile/meta/Creator" },
{ WITH_ETAG, "/ROOT/raw/zimfile/meta/Title" },
};
ResourceCollection all200Resources()
@ -313,6 +320,32 @@ TEST_F(ServerTest, BookMainPageIsRedirectedToArticleIndex)
ASSERT_EQ("/ROOT/zimfile/A/index", g->get_header_value("Location"));
}
TEST_F(ServerTest, RawEntry)
{
auto p = zfs1_->GET("/ROOT/raw/zimfile/meta/Title");
EXPECT_EQ(200, p->status);
EXPECT_EQ(p->body, std::string("Ray Charles"));
p = zfs1_->GET("/ROOT/raw/zimfile/meta/Creator");
EXPECT_EQ(200, p->status);
EXPECT_EQ(p->body, std::string("Wikipedia"));
// The raw content of Ray_Charles returned by the server is
// the same as the one in the zim file.
auto archive = zim::Archive("./test/zimfile.zim");
auto entry = archive.getEntryByPath("A/Ray_Charles");
p = zfs1_->GET("/ROOT/raw/zimfile/content/A/Ray_Charles");
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);
}
TEST_F(ServerTest, HeadMethodIsSupported)
{
for ( const Resource& res : all200Resources() )