mirror of https://github.com/kiwix/libkiwix.git
Merge pull request #732 from kiwix/HTTP404HtmlResponse
New way of building 404 error HTML responses
This commit is contained in:
commit
49f24d18df
|
@ -73,9 +73,6 @@ IllustrationInfo getBookIllustrationInfo(const Book& book)
|
|||
kainjow::mustache::object getSingleBookData(const Book& book)
|
||||
{
|
||||
const auto bookDate = book.getDate() + "T00:00:00Z";
|
||||
const MustacheData bookUrl = book.getUrl().empty()
|
||||
? MustacheData(false)
|
||||
: MustacheData(book.getUrl());
|
||||
return kainjow::mustache::object{
|
||||
{"id", book.getId()},
|
||||
{"name", book.getName()},
|
||||
|
@ -92,7 +89,7 @@ kainjow::mustache::object getSingleBookData(const Book& book)
|
|||
{"media_count", to_string(book.getMediaCount())},
|
||||
{"author_name", book.getCreator()},
|
||||
{"publisher_name", book.getPublisher()},
|
||||
{"url", bookUrl},
|
||||
{"url", onlyAsNonEmptyMustacheValue(book.getUrl())},
|
||||
{"size", to_string(book.getSize())},
|
||||
{"icons", getBookIllustrationInfo(book)},
|
||||
};
|
||||
|
@ -194,7 +191,7 @@ string OPDSDumper::dumpOPDSFeed(const std::vector<std::string>& bookIds, const s
|
|||
{"date", gen_date_str()},
|
||||
{"root", rootLocation},
|
||||
{"feed_id", gen_uuid(libraryId + "/catalog/search?"+query)},
|
||||
{"filter", query.empty() ? MustacheData(false) : MustacheData(query)},
|
||||
{"filter", onlyAsNonEmptyMustacheValue(query)},
|
||||
{"totalResults", to_string(m_totalResults)},
|
||||
{"startIndex", to_string(m_startIndex)},
|
||||
{"itemsPerPage", to_string(m_count)},
|
||||
|
@ -214,7 +211,7 @@ string OPDSDumper::dumpOPDSFeedV2(const std::vector<std::string>& bookIds, const
|
|||
{"date", gen_date_str()},
|
||||
{"endpoint_root", endpointRoot},
|
||||
{"feed_id", gen_uuid(libraryId + endpoint + "?" + query)},
|
||||
{"filter", query.empty() ? MustacheData(false) : MustacheData(query)},
|
||||
{"filter", onlyAsNonEmptyMustacheValue(query)},
|
||||
{"query", query.empty() ? "" : "?" + urlEncode(query)},
|
||||
{"totalResults", to_string(m_totalResults)},
|
||||
{"startIndex", to_string(m_startIndex)},
|
||||
|
|
|
@ -278,8 +278,10 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection,
|
|||
std::unique_ptr<Response> InternalServer::handle_request(const RequestContext& request)
|
||||
{
|
||||
try {
|
||||
if (! request.is_valid_url())
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
if (! request.is_valid_url()) {
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
|
||||
const ETag etag = get_matching_if_none_match_etag(request);
|
||||
if ( etag )
|
||||
|
@ -387,6 +389,15 @@ SuggestionsList_t getSuggestions(SuggestionSearcherCache& cache, const zim::Arch
|
|||
return suggestions;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
std::string noSuchBookErrorMsg(const std::string& bookName)
|
||||
{
|
||||
return "No such book: " + bookName;
|
||||
}
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
std::unique_ptr<Response> InternalServer::handle_suggest(const RequestContext& request)
|
||||
{
|
||||
|
@ -405,8 +416,9 @@ std::unique_ptr<Response> InternalServer::handle_suggest(const RequestContext& r
|
|||
}
|
||||
|
||||
if (archive == nullptr) {
|
||||
const std::string error_details = "No such book: " + bookName;
|
||||
return Response::build_404(*this, "", bookName, "", error_details);
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ noSuchBookErrorMsg(bookName)
|
||||
+ TaskbarInfo(bookName);
|
||||
}
|
||||
|
||||
const auto queryString = request.get_optional_param("term", std::string());
|
||||
|
@ -476,7 +488,8 @@ std::unique_ptr<Response> InternalServer::handle_skin(const RequestContext& requ
|
|||
response->set_cacheable();
|
||||
return std::move(response);
|
||||
} catch (const ResourceNotFound& e) {
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -519,9 +532,8 @@ std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& re
|
|||
data.set("pattern", encodeDiples(patternString));
|
||||
data.set("root", m_root);
|
||||
auto response = ContentResponse::build(*this, RESOURCE::templates::no_search_result_html, data, "text/html; charset=utf-8");
|
||||
response->set_taskbar(bookName, archive ? getArchiveTitle(*archive) : "");
|
||||
response->set_code(MHD_HTTP_NOT_FOUND);
|
||||
return std::move(response);
|
||||
return withTaskbarInfo(bookName, archive.get(), std::move(response));
|
||||
}
|
||||
|
||||
std::shared_ptr<zim::Searcher> searcher;
|
||||
|
@ -591,9 +603,7 @@ std::unique_ptr<Response> InternalServer::handle_search(const RequestContext& re
|
|||
renderer.setSearchProtocolPrefix(m_root + "/search?");
|
||||
renderer.setPageLength(pageLength);
|
||||
auto response = ContentResponse::build(*this, renderer.getHtml(), "text/html; charset=utf-8");
|
||||
response->set_taskbar(bookName, archive ? getArchiveTitle(*archive) : "");
|
||||
|
||||
return std::move(response);
|
||||
return withTaskbarInfo(bookName, archive.get(), std::move(response));
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return Response::build_500(*this, e.what());
|
||||
|
@ -617,8 +627,9 @@ std::unique_ptr<Response> InternalServer::handle_random(const RequestContext& re
|
|||
}
|
||||
|
||||
if (archive == nullptr) {
|
||||
const std::string error_details = "No such book: " + bookName;
|
||||
return Response::build_404(*this, "", bookName, "", error_details);
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ noSuchBookErrorMsg(bookName)
|
||||
+ TaskbarInfo(bookName);
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -626,7 +637,8 @@ std::unique_ptr<Response> InternalServer::handle_random(const RequestContext& re
|
|||
return build_redirect(bookName, getFinalItem(*archive, entry));
|
||||
} catch(zim::EntryNotFound& e) {
|
||||
const std::string error_details = "Oops! Failed to pick a random article :(";
|
||||
return Response::build_404(*this, "", bookName, getArchiveTitle(*archive), error_details);
|
||||
auto response = Response::build_404(*this, "", error_details);
|
||||
return withTaskbarInfo(bookName, archive.get(), std::move(response));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,8 +649,10 @@ std::unique_ptr<Response> InternalServer::handle_captured_external(const Request
|
|||
source = kiwix::urlDecode(request.get_argument("source"));
|
||||
} catch (const std::out_of_range& e) {}
|
||||
|
||||
if (source.empty())
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
if (source.empty()) {
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
|
||||
auto data = get_default_data();
|
||||
data.set("source", source);
|
||||
|
@ -657,7 +671,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog(const RequestContext& r
|
|||
host = request.get_header("Host");
|
||||
url = request.get_url_part(1);
|
||||
} catch (const std::out_of_range&) {
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
|
||||
if (url == "v2") {
|
||||
|
@ -665,7 +680,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog(const RequestContext& r
|
|||
}
|
||||
|
||||
if (url != "searchdescription.xml" && url != "root.xml" && url != "search") {
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
|
||||
if (url == "searchdescription.xml") {
|
||||
|
@ -802,7 +818,8 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
|
|||
std::string searchURL = m_root + "/search?pattern=" + kiwix::urlEncode(pattern, true); // Make a full search on the entire library.
|
||||
const std::string details = searchSuggestionHTML(searchURL, kiwix::urlDecode(pattern));
|
||||
|
||||
return Response::build_404(*this, request.get_full_url(), bookName, "", details);
|
||||
auto response = Response::build_404(*this, request.get_full_url(), details);
|
||||
return withTaskbarInfo(bookName, nullptr, std::move(response));
|
||||
}
|
||||
|
||||
auto urlStr = request.get_url().substr(bookName.size()+1);
|
||||
|
@ -819,7 +836,7 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
|
|||
}
|
||||
auto response = ItemResponse::build(*this, request, entry.getItem());
|
||||
try {
|
||||
dynamic_cast<ContentResponse&>(*response).set_taskbar(bookName, getArchiveTitle(*archive));
|
||||
dynamic_cast<ContentResponse&>(*response).set_taskbar(bookName, archive.get());
|
||||
} catch (std::bad_cast& e) {}
|
||||
|
||||
if (m_verbose.load()) {
|
||||
|
@ -835,7 +852,8 @@ std::unique_ptr<Response> InternalServer::handle_content(const RequestContext& r
|
|||
std::string searchURL = m_root + "/search?content=" + bookName + "&pattern=" + kiwix::urlEncode(pattern, true); // Make a search on this specific book only.
|
||||
const std::string details = searchSuggestionHTML(searchURL, kiwix::urlDecode(pattern));
|
||||
|
||||
return Response::build_404(*this, request.get_full_url(), bookName, getArchiveTitle(*archive), details);
|
||||
auto response = Response::build_404(*this, request.get_full_url(), details);
|
||||
return withTaskbarInfo(bookName, archive.get(), std::move(response));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -852,12 +870,13 @@ std::unique_ptr<Response> InternalServer::handle_raw(const RequestContext& reque
|
|||
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, "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
|
||||
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);
|
||||
return Response::build_404(*this, request.get_full_url(), error_details);
|
||||
}
|
||||
|
||||
std::shared_ptr<zim::Archive> archive;
|
||||
|
@ -867,8 +886,9 @@ std::unique_ptr<Response> InternalServer::handle_raw(const RequestContext& reque
|
|||
} 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);
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg
|
||||
+ noSuchBookErrorMsg(bookName);
|
||||
}
|
||||
|
||||
// Remove the beggining of the path:
|
||||
|
@ -893,7 +913,7 @@ std::unique_ptr<Response> InternalServer::handle_raw(const RequestContext& reque
|
|||
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);
|
||||
return Response::build_404(*this, request.get_full_url(), error_details);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2(const RequestContext
|
|||
try {
|
||||
url = request.get_url_part(2);
|
||||
} catch (const std::out_of_range&) {
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
|
||||
if (url == "root.xml") {
|
||||
|
@ -69,7 +70,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2(const RequestContext
|
|||
} else if (url == "illustration") {
|
||||
return handle_catalog_v2_illustration(request);
|
||||
} else {
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +112,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_complete_entry(const
|
|||
try {
|
||||
mp_library->getBookById(entryId);
|
||||
} catch (const std::out_of_range&) {
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
|
||||
OPDSDumper opdsDumper(mp_library);
|
||||
|
@ -158,7 +161,8 @@ std::unique_ptr<Response> InternalServer::handle_catalog_v2_illustration(const R
|
|||
auto illustration = book.getIllustration(size);
|
||||
return ContentResponse::build(*this, illustration->getData(), illustration->mimeType);
|
||||
} catch(...) {
|
||||
return Response::build_404(*this, request.get_full_url(), "", "");
|
||||
return HTTP404HtmlResponse(*this, request)
|
||||
+ urlNotFoundMsg;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "tools/regexTools.h"
|
||||
#include "tools/stringTools.h"
|
||||
#include "tools/otherTools.h"
|
||||
#include "tools/archiveTools.h"
|
||||
|
||||
#include "string.h"
|
||||
#include <mustache.hpp>
|
||||
|
@ -83,19 +84,71 @@ std::unique_ptr<Response> Response::build_304(const InternalServer& server, cons
|
|||
return response;
|
||||
}
|
||||
|
||||
std::unique_ptr<Response> Response::build_404(const InternalServer& server, const std::string& url, const std::string& bookName, const std::string& bookTitle, const std::string& details)
|
||||
kainjow::mustache::data make404ResponseData(const std::string& url, const std::string& details)
|
||||
{
|
||||
MustacheData results;
|
||||
kainjow::mustache::list pList;
|
||||
if ( !url.empty() ) {
|
||||
results.set("url", url);
|
||||
kainjow::mustache::mustache msgTmpl(R"(The requested URL "{{url}}" was not found on this server.)");
|
||||
const auto urlNotFoundMsg = msgTmpl.render({"url", url});
|
||||
pList.push_back({"p", urlNotFoundMsg});
|
||||
}
|
||||
pList.push_back({"p", details});
|
||||
return {"details", pList};
|
||||
}
|
||||
results.set("details", details);
|
||||
|
||||
auto response = ContentResponse::build(server, RESOURCE::templates::_404_html, results, "text/html");
|
||||
std::unique_ptr<ContentResponse> Response::build_404(const InternalServer& server, const std::string& url, const std::string& details)
|
||||
{
|
||||
return build_404(server, make404ResponseData(url, details));
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentResponse> Response::build_404(const InternalServer& server, const kainjow::mustache::data& data)
|
||||
{
|
||||
auto response = ContentResponse::build(server, RESOURCE::templates::_404_html, data, "text/html");
|
||||
response->set_code(MHD_HTTP_NOT_FOUND);
|
||||
response->set_taskbar(bookName, bookTitle);
|
||||
|
||||
return std::move(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
extern const UrlNotFoundMsg urlNotFoundMsg;
|
||||
|
||||
std::unique_ptr<ContentResponse> ContentResponseBlueprint::generateResponseObject() const
|
||||
{
|
||||
auto r = ContentResponse::build(m_server, m_template, m_data, m_mimeType);
|
||||
r->set_code(m_httpStatusCode);
|
||||
return m_taskbarInfo
|
||||
? withTaskbarInfo(m_taskbarInfo->bookName, m_taskbarInfo->archive, std::move(r))
|
||||
: std::move(r);
|
||||
}
|
||||
|
||||
HTTP404HtmlResponse::HTTP404HtmlResponse(const InternalServer& server,
|
||||
const RequestContext& request)
|
||||
: ContentResponseBlueprint(&server,
|
||||
&request,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
"text/html",
|
||||
RESOURCE::templates::_404_html)
|
||||
{
|
||||
kainjow::mustache::list emptyList;
|
||||
this->m_data = kainjow::mustache::object{{"details", emptyList}};
|
||||
}
|
||||
|
||||
HTTP404HtmlResponse& HTTP404HtmlResponse::operator+(UrlNotFoundMsg /*unused*/)
|
||||
{
|
||||
const std::string requestUrl = m_request.get_full_url();
|
||||
kainjow::mustache::mustache msgTmpl(R"(The requested URL "{{url}}" was not found on this server.)");
|
||||
return *this + msgTmpl.render({"url", requestUrl});
|
||||
}
|
||||
|
||||
HTTP404HtmlResponse& HTTP404HtmlResponse::operator+(const std::string& msg)
|
||||
{
|
||||
m_data["details"].push_back({"p", msg});
|
||||
return *this;
|
||||
}
|
||||
|
||||
ContentResponseBlueprint& ContentResponseBlueprint::operator+(const TaskbarInfo& taskbarInfo)
|
||||
{
|
||||
this->m_taskbarInfo.reset(new TaskbarInfo(taskbarInfo));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<Response> Response::build_416(const InternalServer& server, size_t resourceLength)
|
||||
|
@ -332,10 +385,10 @@ MHD_Result Response::send(const RequestContext& request, MHD_Connection* connect
|
|||
return ret;
|
||||
}
|
||||
|
||||
void ContentResponse::set_taskbar(const std::string& bookName, const std::string& bookTitle)
|
||||
void ContentResponse::set_taskbar(const std::string& bookName, const zim::Archive* archive)
|
||||
{
|
||||
m_bookName = bookName;
|
||||
m_bookTitle = bookTitle;
|
||||
m_bookTitle = archive ? getArchiveTitle(*archive) : "";
|
||||
}
|
||||
|
||||
|
||||
|
@ -383,6 +436,15 @@ std::unique_ptr<ContentResponse> ContentResponse::build(
|
|||
return ContentResponse::build(server, content, mimetype, isHomePage);
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentResponse> withTaskbarInfo(
|
||||
const std::string& bookName,
|
||||
const zim::Archive* archive,
|
||||
std::unique_ptr<ContentResponse> r)
|
||||
{
|
||||
r->set_taskbar(bookName, archive);
|
||||
return r;
|
||||
}
|
||||
|
||||
ItemResponse::ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange) :
|
||||
Response(verbose),
|
||||
m_item(item),
|
||||
|
|
|
@ -33,12 +33,16 @@ extern "C" {
|
|||
#include "microhttpd_wrapper.h"
|
||||
}
|
||||
|
||||
namespace zim {
|
||||
class Archive;
|
||||
} // namespace zim
|
||||
|
||||
namespace kiwix {
|
||||
|
||||
class InternalServer;
|
||||
class RequestContext;
|
||||
|
||||
class EntryResponse;
|
||||
class ContentResponse;
|
||||
|
||||
class Response {
|
||||
public:
|
||||
|
@ -47,7 +51,8 @@ class Response {
|
|||
|
||||
static std::unique_ptr<Response> build(const InternalServer& server);
|
||||
static std::unique_ptr<Response> build_304(const InternalServer& server, const ETag& etag);
|
||||
static std::unique_ptr<Response> build_404(const InternalServer& server, const std::string& url, const std::string& bookName, const std::string& bookTitle, const std::string& details="");
|
||||
static std::unique_ptr<ContentResponse> build_404(const InternalServer& server, const kainjow::mustache::data& data);
|
||||
static std::unique_ptr<ContentResponse> build_404(const InternalServer& server, const std::string& url, const std::string& details="");
|
||||
static std::unique_ptr<Response> build_416(const InternalServer& server, size_t resourceLength);
|
||||
static std::unique_ptr<Response> build_500(const InternalServer& server, const std::string& msg);
|
||||
static std::unique_ptr<Response> build_redirect(const InternalServer& server, const std::string& redirectUrl);
|
||||
|
@ -100,7 +105,7 @@ class ContentResponse : public Response {
|
|||
const std::string& mimetype,
|
||||
bool isHomePage = false);
|
||||
|
||||
void set_taskbar(const std::string& bookName, const std::string& bookTitle);
|
||||
void set_taskbar(const std::string& bookName, const zim::Archive* archive);
|
||||
|
||||
private:
|
||||
MHD_Response* create_mhd_response(const RequestContext& request);
|
||||
|
@ -124,6 +129,84 @@ class ContentResponse : public Response {
|
|||
std::string m_bookTitle;
|
||||
};
|
||||
|
||||
struct TaskbarInfo
|
||||
{
|
||||
const std::string bookName;
|
||||
const zim::Archive* const archive;
|
||||
|
||||
TaskbarInfo(const std::string& bookName, const zim::Archive* a = nullptr)
|
||||
: bookName(bookName)
|
||||
, archive(a)
|
||||
{}
|
||||
};
|
||||
|
||||
std::unique_ptr<ContentResponse> withTaskbarInfo(const std::string& bookName,
|
||||
const zim::Archive* archive,
|
||||
std::unique_ptr<ContentResponse> r);
|
||||
|
||||
class ContentResponseBlueprint
|
||||
{
|
||||
public: // functions
|
||||
ContentResponseBlueprint(const InternalServer* server,
|
||||
const RequestContext* request,
|
||||
int httpStatusCode,
|
||||
const std::string& mimeType,
|
||||
const std::string& templateStr)
|
||||
: m_server(*server)
|
||||
, m_request(*request)
|
||||
, m_httpStatusCode(httpStatusCode)
|
||||
, m_mimeType(mimeType)
|
||||
, m_template(templateStr)
|
||||
{}
|
||||
|
||||
virtual ~ContentResponseBlueprint() = default;
|
||||
|
||||
ContentResponseBlueprint& operator+(kainjow::mustache::data&& data)
|
||||
{
|
||||
this->m_data = std::move(data);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator std::unique_ptr<ContentResponse>() const
|
||||
{
|
||||
return generateResponseObject();
|
||||
}
|
||||
|
||||
operator std::unique_ptr<Response>() const
|
||||
{
|
||||
return operator std::unique_ptr<ContentResponse>();
|
||||
}
|
||||
|
||||
|
||||
ContentResponseBlueprint& operator+(const TaskbarInfo& taskbarInfo);
|
||||
|
||||
protected: // functions
|
||||
virtual std::unique_ptr<ContentResponse> generateResponseObject() const;
|
||||
|
||||
public: //data
|
||||
const InternalServer& m_server;
|
||||
const RequestContext& m_request;
|
||||
const int m_httpStatusCode;
|
||||
const std::string m_mimeType;
|
||||
const std::string m_template;
|
||||
kainjow::mustache::data m_data;
|
||||
std::unique_ptr<TaskbarInfo> m_taskbarInfo;
|
||||
};
|
||||
|
||||
class UrlNotFoundMsg {};
|
||||
|
||||
extern const UrlNotFoundMsg urlNotFoundMsg;
|
||||
|
||||
struct HTTP404HtmlResponse : ContentResponseBlueprint
|
||||
{
|
||||
HTTP404HtmlResponse(const InternalServer& server,
|
||||
const RequestContext& request);
|
||||
|
||||
using ContentResponseBlueprint::operator+;
|
||||
HTTP404HtmlResponse& operator+(UrlNotFoundMsg /*unused*/);
|
||||
HTTP404HtmlResponse& operator+(const std::string& errorDetails);
|
||||
};
|
||||
|
||||
class ItemResponse : public Response {
|
||||
public:
|
||||
ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange);
|
||||
|
|
|
@ -370,6 +370,13 @@ std::string kiwix::gen_uuid(const std::string& s)
|
|||
return kiwix::to_string(zim::Uuid::generate(s));
|
||||
}
|
||||
|
||||
kainjow::mustache::data kiwix::onlyAsNonEmptyMustacheValue(const std::string& s)
|
||||
{
|
||||
return s.empty()
|
||||
? kainjow::mustache::data(false)
|
||||
: kainjow::mustache::data(s);
|
||||
}
|
||||
|
||||
std::string kiwix::render_template(const std::string& template_str, kainjow::mustache::data data)
|
||||
{
|
||||
kainjow::mustache::mustache tmpl(template_str);
|
||||
|
|
|
@ -48,6 +48,10 @@ namespace kiwix
|
|||
std::string gen_date_str();
|
||||
std::string gen_uuid(const std::string& s);
|
||||
|
||||
// if s is empty then returns kainjow::mustache::data(false)
|
||||
// otherwise kainjow::mustache::data(value)
|
||||
kainjow::mustache::data onlyAsNonEmptyMustacheValue(const std::string& s);
|
||||
|
||||
std::string render_template(const std::string& template_str, kainjow::mustache::data data);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,14 +6,9 @@
|
|||
</head>
|
||||
<body>
|
||||
<h1>Not Found</h1>
|
||||
{{#url}}
|
||||
<p>
|
||||
The requested URL "{{url}}" was not found on this server.
|
||||
</p>
|
||||
{{/url}}
|
||||
{{#details}}
|
||||
<p>
|
||||
{{{details}}}
|
||||
{{{p}}}
|
||||
</p>
|
||||
{{/details}}
|
||||
</body>
|
||||
|
|
|
@ -530,7 +530,6 @@ TEST_F(ServerTest, 404WithBodyTesting)
|
|||
{ /* url */ "/ROOT/random?content=non-existent-book",
|
||||
expected_body==R"(
|
||||
<h1>Not Found</h1>
|
||||
//EOLWHITESPACEMARKER
|
||||
<p>
|
||||
No such book: non-existent-book
|
||||
</p>
|
||||
|
@ -539,7 +538,6 @@ TEST_F(ServerTest, 404WithBodyTesting)
|
|||
{ /* url */ "/ROOT/suggest?content=no-such-book&term=whatever",
|
||||
expected_body==R"(
|
||||
<h1>Not Found</h1>
|
||||
//EOLWHITESPACEMARKER
|
||||
<p>
|
||||
No such book: no-such-book
|
||||
</p>
|
||||
|
@ -551,9 +549,6 @@ TEST_F(ServerTest, 404WithBodyTesting)
|
|||
<p>
|
||||
The requested URL "/ROOT/catalog/" was not found on this server.
|
||||
</p>
|
||||
<p>
|
||||
//EOLWHITESPACEMARKER
|
||||
</p>
|
||||
)" },
|
||||
|
||||
{ /* url */ "/ROOT/catalog/invalid_endpoint",
|
||||
|
@ -562,9 +557,6 @@ TEST_F(ServerTest, 404WithBodyTesting)
|
|||
<p>
|
||||
The requested URL "/ROOT/catalog/invalid_endpoint" was not found on this server.
|
||||
</p>
|
||||
<p>
|
||||
//EOLWHITESPACEMARKER
|
||||
</p>
|
||||
)" },
|
||||
|
||||
{ /* url */ "/ROOT/invalid-book/whatever",
|
||||
|
@ -638,8 +630,6 @@ TEST_F(ServerTest, 404WithBodyTesting)
|
|||
)" },
|
||||
|
||||
{ /* url */ "/ROOT/raw/zimfile/meta/invalid-metadata",
|
||||
book_name=="zimfile" &&
|
||||
book_title=="Ray Charles" &&
|
||||
expected_body==R"(
|
||||
<h1>Not Found</h1>
|
||||
<p>
|
||||
|
@ -651,8 +641,6 @@ TEST_F(ServerTest, 404WithBodyTesting)
|
|||
)" },
|
||||
|
||||
{ /* url */ "/ROOT/raw/zimfile/content/invalid-article",
|
||||
book_name=="zimfile" &&
|
||||
book_title=="Ray Charles" &&
|
||||
expected_body==R"(
|
||||
<h1>Not Found</h1>
|
||||
<p>
|
||||
|
|
Loading…
Reference in New Issue