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