diff --git a/src/server/i18n.h b/src/server/i18n.h
index e1cbcc68f..79005b721 100644
--- a/src/server/i18n.h
+++ b/src/server/i18n.h
@@ -111,6 +111,12 @@ private: // data
const Parameters params;
};
+inline ParameterizedMessage nonParameterizedMessage(const std::string& msgId)
+{
+ const ParameterizedMessage::Parameters noParams;
+ return ParameterizedMessage(msgId, noParams);
+}
+
struct LangPreference
{
const std::string lang;
diff --git a/src/server/internalServer.cpp b/src/server/internalServer.cpp
index c8446cb17..72b71b044 100644
--- a/src/server/internalServer.cpp
+++ b/src/server/internalServer.cpp
@@ -190,12 +190,6 @@ ParameterizedMessage tooManyBooksMsg(size_t nbBooks, size_t limit)
);
}
-ParameterizedMessage nonParameterizedMessage(const std::string& msgId)
-{
- const ParameterizedMessage::Parameters noParams;
- return ParameterizedMessage(msgId, noParams);
-}
-
struct Error : public std::runtime_error {
explicit Error(const ParameterizedMessage& message)
: std::runtime_error("Error while handling request"),
@@ -564,7 +558,7 @@ MHD_Result InternalServer::handlerCallback(struct MHD_Connection* connection,
response->set_etag_body(getLibraryId());
}
- auto ret = response->send(request, connection);
+ auto ret = response->send(request, m_verbose.load(), connection);
auto end_time = std::chrono::steady_clock::now();
auto time_span = std::chrono::duration_cast>(end_time - start_time);
if (m_verbose.load()) {
@@ -593,20 +587,19 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r
{
try {
if (! request.is_valid_url()) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
if ( request.get_url() == "" ) {
// Redirect /ROOT_LOCATION to /ROOT_LOCATION/ (note the added slash)
// so that relative URLs are resolved correctly
const std::string query = getSearchComponent(request);
- return Response::build_redirect(*this, m_root + "/" + query);
+ return Response::build_redirect(m_root + "/" + query);
}
const ETag etag = get_matching_if_none_match_etag(request, getLibraryId());
if ( etag )
- return Response::build_304(*this, etag);
+ return Response::build_304(etag);
const auto url = request.get_url();
if ( isLocallyCustomizedResource(url) )
@@ -647,15 +640,15 @@ std::unique_ptr InternalServer::handle_request(const RequestContext& r
const std::string contentUrl = m_root + "/content" + urlEncode(url);
const std::string query = getSearchComponent(request);
- return Response::build_redirect(*this, contentUrl + query);
+ return Response::build_redirect(contentUrl + query);
} catch (std::exception& e) {
fprintf(stderr, "===== Unhandled error : %s\n", e.what());
- return HTTP500Response(*this, request)
- + e.what();
+ return HTTP500Response(request)
+ + ParameterizedMessage("non-translated-text", {{"MSG", e.what()}});
} catch (...) {
fprintf(stderr, "===== Unhandled unknown error\n");
- return HTTP500Response(*this, request)
- + "Unknown error";
+ return HTTP500Response(request)
+ + nonParameterizedMessage("unknown-error");
}
}
@@ -668,7 +661,7 @@ MustacheData InternalServer::get_default_data() const
std::unique_ptr InternalServer::build_homepage(const RequestContext& request)
{
- return ContentResponse::build(*this, m_indexTemplateString, get_default_data(), "text/html; charset=utf-8");
+ return ContentResponse::build(m_indexTemplateString, get_default_data(), "text/html; charset=utf-8");
}
/**
@@ -697,8 +690,7 @@ std::unique_ptr InternalServer::handle_suggest(const RequestContext& r
}
if ( startsWith(request.get_url(), "/suggest/") ) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
std::string bookName, bookId;
@@ -712,7 +704,7 @@ std::unique_ptr InternalServer::handle_suggest(const RequestContext& r
}
if (archive == nullptr) {
- return HTTP404Response(*this, request)
+ return HTTP404Response(request)
+ noSuchBookErrorMsg(bookName);
}
@@ -747,7 +739,7 @@ std::unique_ptr InternalServer::handle_suggest(const RequestContext& r
results.addFTSearchSuggestion(request.get_user_language(), queryString);
}
- return ContentResponse::build(*this, results.getJSON(), "application/json; charset=utf-8");
+ return ContentResponse::build(results.getJSON(), "application/json; charset=utf-8");
}
std::unique_ptr InternalServer::handle_viewer_settings(const RequestContext& request)
@@ -762,7 +754,7 @@ std::unique_ptr InternalServer::handle_viewer_settings(const RequestCo
{"enable_library_button", m_withLibraryButton ? "true" : "false" },
{"default_user_language", request.get_user_language() }
};
- return ContentResponse::build(*this, RESOURCE::templates::viewer_settings_js, data, "application/javascript; charset=utf-8");
+ return ContentResponse::build(RESOURCE::templates::viewer_settings_js, data, "application/javascript; charset=utf-8");
}
std::string InternalServer::getNoJSDownloadPageHTML(const std::string& bookId, const std::string& userLang) const
@@ -817,19 +809,13 @@ std::unique_ptr InternalServer::handle_no_js(const RequestContext& req
const auto bookId = mp_nameMapper->getIdForName(urlParts[2]);
content = getNoJSDownloadPageHTML(bookId, userLang);
} catch (const std::out_of_range&) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
} else {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
- return ContentResponse::build(
- *this,
- content,
- "text/html; charset=utf-8"
- );
+ return ContentResponse::build(content, "text/html; charset=utf-8");
}
namespace
@@ -867,14 +853,12 @@ std::unique_ptr InternalServer::handle_skin(const RequestContext& requ
try {
const auto accessType = staticResourceAccessType(request, resourceCacheId);
auto response = ContentResponse::build(
- *this,
getResource(resourceName),
getMimeTypeForFile(resourceName));
response->set_kind(accessType);
return std::move(response);
} catch (const ResourceNotFound& e) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
}
@@ -887,20 +871,17 @@ std::unique_ptr InternalServer::handle_search(const RequestContext& re
if ( startsWith(request.get_url(), "/search/") ) {
if (request.get_url() == "/search/searchdescription.xml") {
return ContentResponse::build(
- *this,
RESOURCE::ft_opensearchdescription_xml,
get_default_data(),
"application/opensearchdescription+xml");
}
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
try {
return handle_search_request(request);
} catch (const Error& e) {
- return HTTP400Response(*this, request)
- + invalidUrlMsg
+ return HTTP400Response(request)
+ e.message();
}
}
@@ -942,7 +923,7 @@ std::unique_ptr InternalServer::handle_search_request(const RequestCon
// Searcher->search will throw a runtime error if there is no valid xapian database to do the search.
// (in case of zim file not containing a index)
const auto cssUrl = renderUrl(m_root, RESOURCE::templates::url_of_search_results_css);
- HTTPErrorResponse response(*this, request, MHD_HTTP_NOT_FOUND,
+ HTTPErrorResponse response(request, MHD_HTTP_NOT_FOUND,
"fulltext-search-unavailable",
"404-page-heading",
cssUrl);
@@ -972,13 +953,11 @@ std::unique_ptr InternalServer::handle_search_request(const RequestCon
renderer.setPageLength(pageLength);
if (request.get_requested_format() == "xml") {
return ContentResponse::build(
- *this,
renderer.getXml(*mp_nameMapper, mp_library.get()),
"application/rss+xml; charset=utf-8"
);
}
auto response = ContentResponse::build(
- *this,
renderer.getHtml(*mp_nameMapper, mp_library.get()),
"text/html; charset=utf-8"
);
@@ -1001,8 +980,7 @@ std::unique_ptr InternalServer::handle_random(const RequestContext& re
}
if ( startsWith(request.get_url(), "/random/") ) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
std::string bookName;
@@ -1016,7 +994,7 @@ std::unique_ptr InternalServer::handle_random(const RequestContext& re
}
if (archive == nullptr) {
- return HTTP404Response(*this, request)
+ return HTTP404Response(request)
+ noSuchBookErrorMsg(bookName);
}
@@ -1024,7 +1002,7 @@ std::unique_ptr InternalServer::handle_random(const RequestContext& re
auto entry = archive->getRandomEntry();
return build_redirect(bookName, getFinalItem(*archive, entry));
} catch(zim::EntryNotFound& e) {
- return HTTP404Response(*this, request)
+ return HTTP404Response(request)
+ nonParameterizedMessage("random-article-failure");
}
}
@@ -1037,13 +1015,12 @@ std::unique_ptr InternalServer::handle_captured_external(const Request
} catch (const std::out_of_range& e) {}
if (source.empty()) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
auto data = get_default_data();
data.set("source", source);
- return ContentResponse::build(*this, RESOURCE::templates::captured_external_html, data, "text/html; charset=utf-8");
+ return ContentResponse::build(RESOURCE::templates::captured_external_html, data, "text/html; charset=utf-8");
}
std::unique_ptr InternalServer::handle_catch(const RequestContext& request)
@@ -1056,8 +1033,7 @@ std::unique_ptr InternalServer::handle_catch(const RequestContext& req
return handle_captured_external(request);
}
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
std::vector
@@ -1117,7 +1093,7 @@ InternalServer::build_redirect(const std::string& bookName, const zim::Item& ite
{
const auto contentPath = "/content/" + bookName + "/" + item.getPath();
const auto url = m_root + kiwix::urlEncode(contentPath);
- return Response::build_redirect(*this, url);
+ return Response::build_redirect(url);
}
std::unique_ptr InternalServer::handle_content(const RequestContext& request)
@@ -1141,15 +1117,14 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r
if (archive == nullptr) {
const std::string searchURL = m_root + "/search?pattern=" + kiwix::urlEncode(pattern);
- return HTTP404Response(*this, request)
- + urlNotFoundMsg
+ return UrlNotFoundResponse(request)
+ suggestSearchMsg(searchURL, kiwix::urlDecode(pattern));
}
const std::string archiveUuid(archive->getUuid());
const ETag etag = get_matching_if_none_match_etag(request, archiveUuid);
if ( etag )
- return Response::build_304(*this, etag);
+ return Response::build_304(etag);
auto urlStr = url.substr(prefixLength + bookName.size());
if (urlStr[0] == '/') {
@@ -1168,7 +1143,7 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r
// '-' namespaces, in which case that resource is returned instead.
return build_redirect(bookName, getFinalItem(*archive, entry));
}
- auto response = ItemResponse::build(*this, request, entry.getItem());
+ auto response = ItemResponse::build(request, entry.getItem());
response->set_etag_body(archiveUuid);
if ( !startsWith(entry.getItem().getMimetype(), "application/pdf") ) {
@@ -1189,8 +1164,7 @@ std::unique_ptr InternalServer::handle_content(const RequestContext& r
printf("Failed to find %s\n", urlStr.c_str());
std::string searchURL = m_root + "/search?content=" + bookName + "&pattern=" + kiwix::urlEncode(pattern);
- return HTTP404Response(*this, request)
- + urlNotFoundMsg
+ return UrlNotFoundResponse(request)
+ suggestSearchMsg(searchURL, kiwix::urlDecode(pattern));
}
}
@@ -1208,13 +1182,11 @@ std::unique_ptr 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 HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
if (kind != "meta" && kind!= "content") {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg
+ return UrlNotFoundResponse(request)
+ invalidRawAccessMsg(kind);
}
@@ -1225,15 +1197,14 @@ std::unique_ptr InternalServer::handle_raw(const RequestContext& reque
} catch (const std::out_of_range& e) {}
if (archive == nullptr) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg
+ return UrlNotFoundResponse(request)
+ noSuchBookErrorMsg(bookName);
}
const std::string archiveUuid(archive->getUuid());
const ETag etag = get_matching_if_none_match_etag(request, archiveUuid);
if ( etag )
- return Response::build_304(*this, etag);
+ return Response::build_304(etag);
// Remove the beggining of the path:
// /raw///foo
@@ -1244,7 +1215,7 @@ std::unique_ptr InternalServer::handle_raw(const RequestContext& reque
try {
if (kind == "meta") {
auto item = archive->getMetadataItem(itemPath);
- auto response = ItemResponse::build(*this, request, item);
+ auto response = ItemResponse::build(request, item);
response->set_etag_body(archiveUuid);
return response;
} else {
@@ -1252,7 +1223,7 @@ std::unique_ptr InternalServer::handle_raw(const RequestContext& reque
if (entry.isRedirect()) {
return build_redirect(bookName, entry.getItem(true));
}
- auto response = ItemResponse::build(*this, request, entry.getItem());
+ auto response = ItemResponse::build(request, entry.getItem());
response->set_etag_body(archiveUuid);
return response;
}
@@ -1260,8 +1231,7 @@ std::unique_ptr InternalServer::handle_raw(const RequestContext& reque
if (m_verbose.load()) {
printf("Failed to find %s\n", itemPath.c_str());
}
- return HTTP404Response(*this, request)
- + urlNotFoundMsg
+ return UrlNotFoundResponse(request)
+ rawEntryNotFoundMsg(kind, itemPath);
}
}
@@ -1286,12 +1256,10 @@ std::unique_ptr InternalServer::handle_locally_customized_resource(con
auto byteRange = request.get_range().resolve(resourceData.size());
if (byteRange.kind() != ByteRange::RESOLVED_FULL_CONTENT) {
- return Response::build_416(*this, resourceData.size());
+ return Response::build_416(resourceData.size());
}
- return ContentResponse::build(*this,
- resourceData,
- crd.mimeType);
+ return ContentResponse::build(resourceData, crd.mimeType);
}
}
diff --git a/src/server/internalServer.h b/src/server/internalServer.h
index cdd7cebe2..53b39d986 100644
--- a/src/server/internalServer.h
+++ b/src/server/internalServer.h
@@ -188,10 +188,6 @@ class InternalServer {
class CustomizedResources;
std::unique_ptr m_customizedResources;
-
- friend std::unique_ptr Response::build(const InternalServer& server);
- friend std::unique_ptr ContentResponse::build(const InternalServer& server, const std::string& content, const std::string& mimetype);
- friend std::unique_ptr ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item);
};
}
diff --git a/src/server/internalServer_catalog.cpp b/src/server/internalServer_catalog.cpp
index fa50450a9..1c48117fc 100644
--- a/src/server/internalServer_catalog.cpp
+++ b/src/server/internalServer_catalog.cpp
@@ -63,8 +63,7 @@ std::unique_ptr InternalServer::handle_catalog(const RequestContext& r
host = request.get_header("Host");
url = request.get_url_part(1);
} catch (const std::out_of_range&) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
if (url == "v2") {
@@ -72,12 +71,11 @@ std::unique_ptr InternalServer::handle_catalog(const RequestContext& r
}
if (url != "searchdescription.xml" && url != "root.xml" && url != "search") {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
if (url == "searchdescription.xml") {
- auto response = ContentResponse::build(*this, RESOURCE::opensearchdescription_xml, get_default_data(), "application/opensearchdescription+xml");
+ auto response = ContentResponse::build(RESOURCE::opensearchdescription_xml, get_default_data(), "application/opensearchdescription+xml");
return std::move(response);
}
@@ -95,7 +93,6 @@ std::unique_ptr InternalServer::handle_catalog(const RequestContext& r
}
auto response = ContentResponse::build(
- *this,
opdsDumper.dumpOPDSFeed(bookIdsToDump, request.get_query()),
opdsMimeType[OPDS_ACQUISITION_FEED]);
return std::move(response);
@@ -111,15 +108,14 @@ std::unique_ptr InternalServer::handle_catalog_v2(const RequestContext
try {
url = request.get_url_part(2);
} catch (const std::out_of_range&) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
if (url == "root.xml") {
return handle_catalog_v2_root(request);
} else if (url == "searchdescription.xml") {
const std::string endpoint_root = m_root + "/catalog/v2";
- return ContentResponse::build(*this,
+ return ContentResponse::build(
RESOURCE::catalog_v2_searchdescription_xml,
kainjow::mustache::object({{"endpoint_root", endpoint_root}}),
"application/opensearchdescription+xml"
@@ -138,8 +134,7 @@ std::unique_ptr InternalServer::handle_catalog_v2(const RequestContext
} else if (url == "illustration") {
return handle_catalog_v2_illustration(request);
} else {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
}
@@ -147,7 +142,6 @@ std::unique_ptr InternalServer::handle_catalog_v2_root(const RequestCo
{
const std::string libraryId = getLibraryId();
return ContentResponse::build(
- *this,
RESOURCE::templates::catalog_v2_root_xml,
kainjow::mustache::object{
{"date", gen_date_str()},
@@ -170,7 +164,6 @@ std::unique_ptr InternalServer::handle_catalog_v2_entries(const Reques
const auto bookIds = search_catalog(request, opdsDumper);
const auto opdsFeed = opdsDumper.dumpOPDSFeedV2(bookIds, request.get_query(), partial);
return ContentResponse::build(
- *this,
opdsFeed,
opdsMimeType[OPDS_ACQUISITION_FEED]
);
@@ -181,8 +174,7 @@ std::unique_ptr InternalServer::handle_catalog_v2_complete_entry(const
try {
mp_library->getBookById(entryId);
} catch (const std::out_of_range&) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
OPDSDumper opdsDumper(mp_library.get(), mp_nameMapper.get());
@@ -190,7 +182,6 @@ std::unique_ptr InternalServer::handle_catalog_v2_complete_entry(const
opdsDumper.setLibraryId(getLibraryId());
const auto opdsFeed = opdsDumper.dumpOPDSCompleteEntry(entryId);
return ContentResponse::build(
- *this,
opdsFeed,
opdsMimeType[OPDS_ENTRY]
);
@@ -202,7 +193,6 @@ std::unique_ptr InternalServer::handle_catalog_v2_categories(const Req
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
return ContentResponse::build(
- *this,
opdsDumper.categoriesOPDSFeed(),
opdsMimeType[OPDS_NAVIGATION_FEED]
);
@@ -214,7 +204,6 @@ std::unique_ptr InternalServer::handle_catalog_v2_languages(const Requ
opdsDumper.setRootLocation(m_root);
opdsDumper.setLibraryId(getLibraryId());
return ContentResponse::build(
- *this,
opdsDumper.languagesOPDSFeed(),
opdsMimeType[OPDS_NAVIGATION_FEED]
);
@@ -228,13 +217,11 @@ std::unique_ptr InternalServer::handle_catalog_v2_illustration(const R
auto size = request.get_argument("size");
auto illustration = book.getIllustration(size);
return ContentResponse::build(
- *this,
illustration->getData(),
illustration->mimeType
);
} catch(...) {
- return HTTP404Response(*this, request)
- + urlNotFoundMsg;
+ return UrlNotFoundResponse(request);
}
}
diff --git a/src/server/response.cpp b/src/server/response.cpp
index bc8c85e51..8ccab1313 100644
--- a/src/server/response.cpp
+++ b/src/server/response.cpp
@@ -119,9 +119,8 @@ const char* getCacheControlHeader(Response::Kind k)
} // unnamed namespace
-Response::Response(bool verbose)
- : m_verbose(verbose),
- m_returnCode(MHD_HTTP_OK)
+Response::Response()
+ : m_returnCode(MHD_HTTP_OK)
{
add_header(MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, "*");
}
@@ -133,14 +132,14 @@ void Response::set_kind(Kind k)
m_etag.set_option(ETag::ZIM_CONTENT);
}
-std::unique_ptr Response::build(const InternalServer& server)
+std::unique_ptr Response::build()
{
- return std::unique_ptr(new Response(server.m_verbose.load()));
+ return std::make_unique();
}
-std::unique_ptr Response::build_304(const InternalServer& server, const ETag& etag)
+std::unique_ptr Response::build_304(const ETag& etag)
{
- auto response = Response::build(server);
+ auto response = Response::build();
response->set_code(MHD_HTTP_NOT_MODIFIED);
response->m_etag = etag;
if ( etag.get_option(ETag::ZIM_CONTENT) ) {
@@ -152,9 +151,6 @@ std::unique_ptr Response::build_304(const InternalServer& server, cons
return response;
}
-const UrlNotFoundMsg urlNotFoundMsg;
-const InvalidUrlMsg invalidUrlMsg;
-
std::string ContentResponseBlueprint::getMessage(const std::string& msgId) const
{
return getTranslatedString(m_request.get_user_language(), msgId);
@@ -162,19 +158,17 @@ std::string ContentResponseBlueprint::getMessage(const std::string& msgId) const
std::unique_ptr ContentResponseBlueprint::generateResponseObject() const
{
- auto r = ContentResponse::build(m_server, m_template, m_data, m_mimeType);
+ auto r = ContentResponse::build(m_template, m_data, m_mimeType);
r->set_code(m_httpStatusCode);
return r;
}
-HTTPErrorResponse::HTTPErrorResponse(const InternalServer& server,
- const RequestContext& request,
+HTTPErrorResponse::HTTPErrorResponse(const RequestContext& request,
int httpStatusCode,
const std::string& pageTitleMsgId,
const std::string& headingMsgId,
const std::string& cssUrl)
- : ContentResponseBlueprint(&server,
- &request,
+ : ContentResponseBlueprint(&request,
httpStatusCode,
request.get_requested_format() == "html" ? "text/html; charset=utf-8" : "application/xml; charset=utf-8",
request.get_requested_format() == "html" ? RESOURCE::templates::error_html : RESOURCE::templates::error_xml)
@@ -188,31 +182,26 @@ HTTPErrorResponse::HTTPErrorResponse(const InternalServer& server,
};
}
-HTTP404Response::HTTP404Response(const InternalServer& server,
- const RequestContext& request)
- : HTTPErrorResponse(server,
- request,
+HTTP404Response::HTTP404Response(const RequestContext& request)
+ : HTTPErrorResponse(request,
MHD_HTTP_NOT_FOUND,
"404-page-title",
"404-page-heading")
{
}
-HTTPErrorResponse& HTTP404Response::operator+(UrlNotFoundMsg /*unused*/)
+UrlNotFoundResponse::UrlNotFoundResponse(const RequestContext& request)
+ : HTTP404Response(request)
{
const std::string requestUrl = urlDecode(m_request.get_full_url(), false);
- return *this + ParameterizedMessage("url-not-found", {{"url", requestUrl}});
-}
-
-HTTPErrorResponse& HTTPErrorResponse::operator+(const std::string& msg)
-{
- m_data["details"].push_back({"p", msg});
- return *this;
+ *this += ParameterizedMessage("url-not-found", {{"url", requestUrl}});
}
HTTPErrorResponse& HTTPErrorResponse::operator+(const ParameterizedMessage& details)
{
- return *this + details.getText(m_request.get_user_language());
+ const std::string msg = details.getText(m_request.get_user_language());
+ m_data["details"].push_back({"p", msg});
+ return *this;
}
HTTPErrorResponse& HTTPErrorResponse::operator+=(const ParameterizedMessage& details)
@@ -222,50 +211,40 @@ HTTPErrorResponse& HTTPErrorResponse::operator+=(const ParameterizedMessage& det
}
-HTTP400Response::HTTP400Response(const InternalServer& server,
- const RequestContext& request)
- : HTTPErrorResponse(server,
- request,
+HTTP400Response::HTTP400Response(const RequestContext& request)
+ : HTTPErrorResponse(request,
MHD_HTTP_BAD_REQUEST,
"400-page-title",
"400-page-heading")
-{
-}
-
-HTTPErrorResponse& HTTP400Response::operator+(InvalidUrlMsg /*unused*/)
{
std::string requestUrl = urlDecode(m_request.get_full_url(), false);
const auto query = m_request.get_query();
if (!query.empty()) {
requestUrl += "?" + encodeDiples(query);
}
- kainjow::mustache::mustache msgTmpl(R"(The requested URL "{{{url}}}" is not a valid request.)");
- return *this + msgTmpl.render({"url", requestUrl});
+ *this += ParameterizedMessage("invalid-request", {{"url", requestUrl}});
}
-HTTP500Response::HTTP500Response(const InternalServer& server,
- const RequestContext& request)
- : HTTPErrorResponse(server,
- request,
+HTTP500Response::HTTP500Response(const RequestContext& request)
+ : HTTPErrorResponse(request,
MHD_HTTP_INTERNAL_SERVER_ERROR,
"500-page-title",
"500-page-heading")
{
- // operator+() is a state-modifying operator (akin to operator+=)
- *this + "An internal server error occured. We are sorry about that :/";
+ *this += nonParameterizedMessage("500-page-text");
}
std::unique_ptr HTTP500Response::generateResponseObject() const
{
const std::string mimeType = "text/html;charset=utf-8";
- auto r = ContentResponse::build(m_server, m_template, m_data, mimeType);
+ auto r = ContentResponse::build(m_template, m_data, mimeType);
r->set_code(m_httpStatusCode);
return r;
}
-std::unique_ptr Response::build_416(const InternalServer& server, size_t resourceLength)
+std::unique_ptr Response::build_416(size_t resourceLength)
{
- auto response = Response::build(server);
+ auto response = Response::build();
// [FIXME] (compile with recent enough version of libmicrohttpd)
// response->set_code(MHD_HTTP_RANGE_NOT_SATISFIABLE);
response->set_code(416);
@@ -277,9 +256,9 @@ std::unique_ptr Response::build_416(const InternalServer& server, size
}
-std::unique_ptr Response::build_redirect(const InternalServer& server, const std::string& redirectUrl)
+std::unique_ptr Response::build_redirect(const std::string& redirectUrl)
{
- auto response = Response::build(server);
+ auto response = Response::build();
response->m_returnCode = MHD_HTTP_FOUND;
response->add_header(MHD_HTTP_HEADER_LOCATION, redirectUrl);
return response;
@@ -374,7 +353,7 @@ ContentResponse::create_mhd_response(const RequestContext& request)
return response;
}
-MHD_Result Response::send(const RequestContext& request, MHD_Connection* connection)
+MHD_Result Response::send(const RequestContext& request, bool verbose, MHD_Connection* connection)
{
MHD_Response* response = create_mhd_response(request);
@@ -390,7 +369,7 @@ MHD_Result Response::send(const RequestContext& request, MHD_Connection* connect
if (m_returnCode == MHD_HTTP_OK && m_byteRange.kind() == ByteRange::RESOLVED_PARTIAL_CONTENT)
m_returnCode = MHD_HTTP_PARTIAL_CONTENT;
- if (m_verbose)
+ if (verbose)
print_response_info(m_returnCode, response);
auto ret = MHD_queue_response(connection, m_returnCode, response);
@@ -398,9 +377,8 @@ MHD_Result Response::send(const RequestContext& request, MHD_Connection* connect
return ret;
}
-ContentResponse::ContentResponse(const std::string& root, bool verbose, const std::string& content, const std::string& mimetype) :
- Response(verbose),
- m_root(root),
+ContentResponse::ContentResponse(const std::string& content, const std::string& mimetype) :
+ Response(),
m_content(content),
m_mimeType(mimetype)
{
@@ -408,29 +386,23 @@ ContentResponse::ContentResponse(const std::string& root, bool verbose, const st
}
std::unique_ptr ContentResponse::build(
- const InternalServer& server,
const std::string& content,
const std::string& mimetype)
{
- return std::unique_ptr(new ContentResponse(
- server.m_root,
- server.m_verbose.load(),
- content,
- mimetype));
+ return std::make_unique(content, mimetype);
}
std::unique_ptr ContentResponse::build(
- const InternalServer& server,
const std::string& template_str,
kainjow::mustache::data data,
const std::string& mimetype)
{
auto content = render_template(template_str, data);
- return ContentResponse::build(server, content, mimetype);
+ return ContentResponse::build(content, mimetype);
}
-ItemResponse::ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange) :
- Response(verbose),
+ItemResponse::ItemResponse(const zim::Item& item, const std::string& mimetype, const ByteRange& byterange) :
+ Response(),
m_item(item),
m_mimeType(mimetype)
{
@@ -439,30 +411,26 @@ ItemResponse::ItemResponse(bool verbose, const zim::Item& item, const std::strin
add_header(MHD_HTTP_HEADER_CONTENT_TYPE, m_mimeType);
}
-std::unique_ptr ItemResponse::build(const InternalServer& server, const RequestContext& request, const zim::Item& item)
+std::unique_ptr ItemResponse::build(const RequestContext& request, const zim::Item& item)
{
const std::string mimetype = get_mime_type(item);
auto byteRange = request.get_range().resolve(item.getSize());
const bool noRange = byteRange.kind() == ByteRange::RESOLVED_FULL_CONTENT;
if (noRange && is_compressible_mime_type(mimetype)) {
// Return a contentResponse
- auto response = ContentResponse::build(server, item.getData(), mimetype);
+ auto response = ContentResponse::build(item.getData(), mimetype);
response->set_kind(Response::ZIM_CONTENT);
response->m_byteRange = byteRange;
return std::move(response);
}
if (byteRange.kind() == ByteRange::RESOLVED_UNSATISFIABLE) {
- auto response = Response::build_416(server, item.getSize());
+ auto response = Response::build_416(item.getSize());
response->set_kind(Response::ZIM_CONTENT);
return response;
}
- return std::unique_ptr(new ItemResponse(
- server.m_verbose.load(),
- item,
- mimetype,
- byteRange));
+ return std::make_unique(item, mimetype, byteRange);
}
MHD_Response*
diff --git a/src/server/response.h b/src/server/response.h
index 4ed07e628..b1636fa9c 100644
--- a/src/server/response.h
+++ b/src/server/response.h
@@ -41,7 +41,6 @@ class Archive;
namespace kiwix {
-class InternalServer;
class RequestContext;
class Response {
@@ -54,15 +53,15 @@ class Response {
};
public:
- Response(bool verbose);
+ Response();
virtual ~Response() = default;
- static std::unique_ptr build(const InternalServer& server);
- static std::unique_ptr build_304(const InternalServer& server, const ETag& etag);
- static std::unique_ptr build_416(const InternalServer& server, size_t resourceLength);
- static std::unique_ptr build_redirect(const InternalServer& server, const std::string& redirectUrl);
+ static std::unique_ptr build();
+ static std::unique_ptr build_304(const ETag& etag);
+ static std::unique_ptr build_416(size_t resourceLength);
+ static std::unique_ptr build_redirect(const std::string& redirectUrl);
- MHD_Result send(const RequestContext& request, MHD_Connection* connection);
+ MHD_Result send(const RequestContext& request, bool verbose, MHD_Connection* connection);
void set_code(int code) { m_returnCode = code; }
void set_kind(Kind k);
@@ -78,7 +77,6 @@ class Response {
protected: // data
Kind m_kind = DYNAMIC_CONTENT;
- bool m_verbose;
int m_returnCode;
ByteRange m_byteRange;
ETag m_etag;
@@ -91,18 +89,14 @@ class Response {
class ContentResponse : public Response {
public:
ContentResponse(
- const std::string& root,
- bool verbose,
const std::string& content,
const std::string& mimetype);
static std::unique_ptr build(
- const InternalServer& server,
const std::string& content,
const std::string& mimetype);
static std::unique_ptr build(
- const InternalServer& server,
const std::string& template_str,
kainjow::mustache::data data,
const std::string& mimetype);
@@ -114,7 +108,6 @@ class ContentResponse : public Response {
private:
- std::string m_root;
std::string m_content;
std::string m_mimeType;
};
@@ -122,13 +115,11 @@ class ContentResponse : public Response {
class ContentResponseBlueprint
{
public: // functions
- ContentResponseBlueprint(const InternalServer* server,
- const RequestContext* request,
+ ContentResponseBlueprint(const RequestContext* request,
int httpStatusCode,
const std::string& mimeType,
const std::string& templateStr)
- : m_server(*server)
- , m_request(*request)
+ : m_request(*request)
, m_httpStatusCode(httpStatusCode)
, m_mimeType(mimeType)
, m_template(templateStr)
@@ -136,14 +127,9 @@ public: // functions
virtual ~ContentResponseBlueprint() = default;
- operator std::unique_ptr() const
- {
- return generateResponseObject();
- }
-
operator std::unique_ptr() const
{
- return operator std::unique_ptr();
+ return generateResponseObject();
}
@@ -152,7 +138,6 @@ protected: // functions
virtual std::unique_ptr generateResponseObject() const;
public: //data
- const InternalServer& m_server;
const RequestContext& m_request;
const int m_httpStatusCode;
const std::string m_mimeType;
@@ -162,48 +147,34 @@ public: //data
struct HTTPErrorResponse : ContentResponseBlueprint
{
- HTTPErrorResponse(const InternalServer& server,
- const RequestContext& request,
+ HTTPErrorResponse(const RequestContext& request,
int httpStatusCode,
const std::string& pageTitleMsgId,
const std::string& headingMsgId,
const std::string& cssUrl = "");
- HTTPErrorResponse& operator+(const std::string& msg);
HTTPErrorResponse& operator+(const ParameterizedMessage& errorDetails);
HTTPErrorResponse& operator+=(const ParameterizedMessage& errorDetails);
};
-class UrlNotFoundMsg {};
-
-extern const UrlNotFoundMsg urlNotFoundMsg;
-
struct HTTP404Response : HTTPErrorResponse
{
- HTTP404Response(const InternalServer& server,
- const RequestContext& request);
-
- using HTTPErrorResponse::operator+;
- HTTPErrorResponse& operator+(UrlNotFoundMsg /*unused*/);
+ explicit HTTP404Response(const RequestContext& request);
};
-class InvalidUrlMsg {};
-
-extern const InvalidUrlMsg invalidUrlMsg;
+struct UrlNotFoundResponse : HTTP404Response
+{
+ explicit UrlNotFoundResponse(const RequestContext& request);
+};
struct HTTP400Response : HTTPErrorResponse
{
- HTTP400Response(const InternalServer& server,
- const RequestContext& request);
-
- using HTTPErrorResponse::operator+;
- HTTPErrorResponse& operator+(InvalidUrlMsg /*unused*/);
+ explicit HTTP400Response(const RequestContext& request);
};
struct HTTP500Response : HTTPErrorResponse
{
- HTTP500Response(const InternalServer& server,
- const RequestContext& request);
+ explicit HTTP500Response(const RequestContext& request);
private: // overrides
// generateResponseObject() is overriden in order to produce a minimal
@@ -213,8 +184,8 @@ private: // overrides
class ItemResponse : public Response {
public:
- ItemResponse(bool verbose, const zim::Item& item, const std::string& mimetype, const ByteRange& byterange);
- static std::unique_ptr build(const InternalServer& server, const RequestContext& request, const zim::Item& item);
+ ItemResponse(const zim::Item& item, const std::string& mimetype, const ByteRange& byterange);
+ static std::unique_ptr build(const RequestContext& request, const zim::Item& item);
private:
MHD_Response* create_mhd_response(const RequestContext& request);
diff --git a/static/skin/i18n/en.json b/static/skin/i18n/en.json
index aaea04978..8c4a6b0bc 100644
--- a/static/skin/i18n/en.json
+++ b/static/skin/i18n/en.json
@@ -12,6 +12,7 @@
, "suggest-search" : "Make a full text search for {{PATTERN}}"
, "random-article-failure" : "Oops! Failed to pick a random article :("
, "invalid-raw-data-type" : "{{DATATYPE}} is not a valid request for raw content."
+ , "invalid-request" : "The requested URL \"{{{url}}}\" is not a valid request."
, "no-value-for-arg": "No value provided for argument {{ARGUMENT}}"
, "no-query" : "No query provided."
, "raw-entry-not-found" : "Cannot find {{DATATYPE}} entry {{ENTRY}}"
@@ -21,6 +22,7 @@
, "404-page-heading" : "Not Found"
, "500-page-title" : "Internal Server Error"
, "500-page-heading" : "Internal Server Error"
+ , "500-page-text": "An internal server error occured. We are sorry about that :/"
, "fulltext-search-unavailable" : "Fulltext search unavailable"
, "no-search-results": "The fulltext search engine is not available for this content."
, "library-button-text": "Go to welcome page"
@@ -51,4 +53,6 @@
, "download-links-heading": "Download links for {{BOOK_TITLE}}"
, "download-links-title": "Download book"
, "preview-book": "Preview"
+ , "non-translated-text": "{{MSG}}"
+ , "unknown-error": "Unknown error"
}
diff --git a/static/skin/i18n/qqq.json b/static/skin/i18n/qqq.json
index 18c97a609..4fa2ef94e 100644
--- a/static/skin/i18n/qqq.json
+++ b/static/skin/i18n/qqq.json
@@ -15,6 +15,7 @@
"suggest-search": "Suggest a search when the URL points to a non existing article",
"random-article-failure": "Failure of the random article selection procedure",
"invalid-raw-data-type": "Invalid DATATYPE was used with the /raw endpoint (/raw//DATATYPE/...); allowed values are 'meta' and 'content'",
+ "invalid-request" : "Error text for malformed URLs.",
"no-value-for-arg": "Error text when no value has been provided for ARGUMENT in the request's query string",
"no-query": "Error text when no query has been provided for fulltext search",
"raw-entry-not-found": "Entry requested via the /raw endpoint was not found",
@@ -24,6 +25,7 @@
"404-page-heading": "Heading of the 404 error page",
"500-page-title": "Title of the 500 error page",
"500-page-heading": "Heading of the 500 error page",
+ "500-page-text": "Text of the 500 error page",
"fulltext-search-unavailable": "Title of the error page returned when search is attempted in a book without fulltext search database",
"no-search-results": "Text of the error page returned when search is attempted in a book without fulltext search database",
"library-button-text": "Tooltip of the button leading to the welcome page",
@@ -52,5 +54,7 @@
"welcome-to-kiwix-server": "Title shown in browser's title bar/page tab",
"download-links-heading": "Heading for no-js download page",
"download-links-title": "Title for no-js download page",
- "preview-book": "Tooltip of book-tile leading to the book"
+ "preview-book": "Tooltip of book-tile leading to the book",
+ "non-translated-text": "Used to display text that is generated at runtime and cannot be translated. Nothing to translate about this one.",
+ "unknown-error": "Unknown error"
}
diff --git a/static/skin/i18n/test.json b/static/skin/i18n/test.json
index 7686471ab..29def8eac 100644
--- a/static/skin/i18n/test.json
+++ b/static/skin/i18n/test.json
@@ -40,4 +40,6 @@
, "download-links-heading": "[I18N] Download links for {{BOOK_TITLE}} [TESTING]"
, "download-links-title": "[I18N TESTING]Download book"
, "preview-book": "[I18N] Preview [TESTING]"
+ , "no-query" : "[I18N TESTING] Kiwix can read your thoughts but it is against GDPR. Please provide your query explicitly."
+ , "invalid-request" : "[I18N TESTING] Invalid URL: \"{{{url}}}\""
}
diff --git a/test/server.cpp b/test/server.cpp
index 73ba7b1ff..86171cd4a 100644
--- a/test/server.cpp
+++ b/test/server.cpp
@@ -927,6 +927,19 @@ TEST_F(ServerTest, Http400HtmlError)
Too many books requested (4) where limit is 3
)" },
+
+ // Testing of translation
+ { /* url */ "/ROOT%23%3F/search?content=zimfile&userlang=test",
+ expected_page_title=="[I18N TESTING] Invalid request ($400 fine must be paid)" &&
+ expected_body==R"(
+ [I18N TESTING] -400 karma for an invalid request
+
+ [I18N TESTING] Invalid URL: "/ROOT%23%3F/search?content=zimfile&userlang=test"
+
+
+ [I18N TESTING] Kiwix can read your thoughts but it is against GDPR. Please provide your query explicitly.
+
+)" },
};
for ( const auto& t : testData ) {