Nicer Internal Server Error page

Updated the Internal Server Error page to match the 404 (Content Not
Found) error page.
This commit is contained in:
Veloman Yunkan 2025-05-19 17:27:51 +04:00
parent 222e4396c7
commit 2ba29f76e1
10 changed files with 127 additions and 37 deletions

View File

@ -709,12 +709,10 @@ std::unique_ptr<Response> InternalServer::handle_request(const RequestContext& r
return Response::build_redirect(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(request) return HTTP500Response(request, m_root, request.get_full_url(), 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(request) return HTTP500Response(request, m_root, request.get_full_url());
+ nonParameterizedMessage("unknown-error");
} }
} }

View File

@ -491,15 +491,24 @@ HTTP400Response::HTTP400Response(const RequestContext& request)
*this += ParameterizedMessage("invalid-request", {{"url", requestUrl}}); *this += ParameterizedMessage("invalid-request", {{"url", requestUrl}});
} }
HTTP500Response::HTTP500Response(const RequestContext& request) HTTP500Response::HTTP500Response(const RequestContext& request,
: HTTPErrorResponse(request, const std::string& root,
const std::string& urlPath,
const std::string& /*errorText*/)
: ContentResponseBlueprint(&request,
MHD_HTTP_INTERNAL_SERVER_ERROR, MHD_HTTP_INTERNAL_SERVER_ERROR,
"500-page-title", "text/html; charset=utf-8",
"500-page-heading", RESOURCE::templates::sexy500_html,
std::string(),
/*includeKiwixResponseData=*/true) /*includeKiwixResponseData=*/true)
{ {
*this += nonParameterizedMessage("500-page-text"); *this->m_data = Data(Data::Object{
{"root", root },
{"url_path", urlPath},
{"PAGE_TITLE", Data::fromMsgId("500-page-title")},
{"PAGE_HEADING", Data::fromMsgId("500-page-heading")},
{"PAGE_TEXT", Data::fromMsgId("500-page-text")},
{"500_img_text", Data::fromMsgId("500-img-text")},
});
} }
std::unique_ptr<Response> Response::build_416(size_t resourceLength) std::unique_ptr<Response> Response::build_416(size_t resourceLength)

View File

@ -180,9 +180,12 @@ struct HTTP400Response : HTTPErrorResponse
explicit HTTP400Response(const RequestContext& request); explicit HTTP400Response(const RequestContext& request);
}; };
struct HTTP500Response : HTTPErrorResponse struct HTTP500Response : ContentResponseBlueprint
{ {
explicit HTTP500Response(const RequestContext& request); HTTP500Response(const RequestContext& request,
const std::string& root,
const std::string& urlPath,
const std::string& error = "");
}; };
class ItemResponse : public Response { class ItemResponse : public Response {

View File

@ -2,6 +2,7 @@ skin/caret.png
skin/bittorrent.png skin/bittorrent.png
skin/magnet.png skin/magnet.png
skin/404.svg skin/404.svg
skin/500.svg
skin/blocklink.svg skin/blocklink.svg
skin/feed.svg skin/feed.svg
skin/langSelector.svg skin/langSelector.svg
@ -47,6 +48,7 @@ templates/viewer_settings.js
templates/no_js_library_page.html templates/no_js_library_page.html
templates/no_js_download.html templates/no_js_download.html
templates/sexy404.html templates/sexy404.html
templates/sexy500.html
opensearchdescription.xml opensearchdescription.xml
ft_opensearchdescription.xml ft_opensearchdescription.xml
catalog_v2_searchdescription.xml catalog_v2_searchdescription.xml

1
static/skin/500.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -30,8 +30,9 @@
, "404-advice.p4": "Look for keywords or titles related to the information you're seeking" , "404-advice.p4": "Look for keywords or titles related to the information you're seeking"
, "404-advice.p5": "This approach should help you locate the desired content, even if the original link isn't working properly." , "404-advice.p5": "This approach should help you locate the desired content, even if the original link isn't working properly."
, "500-page-title" : "Internal Server Error" , "500-page-title" : "Internal Server Error"
, "500-page-heading" : "Internal Server Error" , "500-page-heading" : "Oops. Page isn't working."
, "500-page-text": "An internal server error occured. We are sorry about that :/" , "500-page-text": "The requested path cannot be properly delivered:"
, "500-img-text": "Page isn't working"
, "external-link-detected" : "External Link Detected" , "external-link-detected" : "External Link Detected"
, "caution-warning" : "Caution!" , "caution-warning" : "Caution!"
, "external-link-intro" : "You are about to leave Kiwix's ZIM reader to go online to" , "external-link-intro" : "You are about to leave Kiwix's ZIM reader to go online to"

View File

@ -36,6 +36,7 @@
"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", "500-page-text": "Text of the 500 error page",
"500-img-text": "Fallback text for the image on the 500 error page",
"external-link-detected": "Title & heading of the external link blocker page", "external-link-detected": "Title & heading of the external link blocker page",
"caution-warning": "Warning of action that shouldn't be carried out carelessly", "caution-warning": "Warning of action that shouldn't be carried out carelessly",
"external-link-intro": "Message introducing the external link (to be followed by the actual link)", "external-link-intro": "Message introducing the external link (to be followed by the actual link)",

View File

@ -22,6 +22,10 @@
, "404-advice.p3": "[I18N TESTING] Check the spelling of the URL path" , "404-advice.p3": "[I18N TESTING] Check the spelling of the URL path"
, "404-advice.p4": "[I18N TESTING] Press the dice button" , "404-advice.p4": "[I18N TESTING] Press the dice button"
, "404-advice.p5": "Good luck! [I18N TESTING]" , "404-advice.p5": "Good luck! [I18N TESTING]"
, "500-page-title" : "[I18N] Internal Server Error [TESTING]"
, "500-page-heading" : "Oops. [I18N] Page isn't [TESTING] working."
, "500-page-text": "The requested path [I18N TESTING] cannot be properly delivered:"
, "500-img-text": "Page [I18N] isn't [TESTING] working"
, "external-link-detected" : "External [I18] Link [TESTING] Detected" , "external-link-detected" : "External [I18] Link [TESTING] Detected"
, "caution-warning" : "[I18N] C5n! [TESTING]" , "caution-warning" : "[I18N] C5n! [TESTING]"
, "external-link-intro" : "[I18N TESTING] The following link may lead you to a place from which you won't ever be able to return" , "external-link-intro" : "[I18N TESTING] The following link may lead you to a place from which you won't ever be able to return"

View File

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>{{PAGE_TITLE}}</title>
<link type="text/css" href="{{root}}/skin/error.css?KIWIXCACHEID" rel="Stylesheet" />
<script>
window.KIWIX_RESPONSE_TEMPLATE = "{{KIWIX_RESPONSE_TEMPLATE}}";
window.KIWIX_RESPONSE_DATA = {{{KIWIX_RESPONSE_DATA}}};
</script>
</head>
<body>
<header>
<img src="{{root}}/skin/500.svg?KIWIXCACHEID"
alt="{{500_img_text}}"
aria-label="{{500_img_text}}"
title="{{500_img_text}}">
</header>
<section class="intro">
<h1>{{PAGE_HEADING}}</h1>
<p>{{PAGE_TEXT}}</p>
<p><code>{{url_path}}</code></p>
</section>
</body>
</html>

View File

@ -112,6 +112,8 @@ const ResourceCollection resources200Compressible{
const ResourceCollection resources200Uncompressible{ const ResourceCollection resources200Uncompressible{
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/404.svg" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/404.svg" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/404.svg?cacheid=b6d648af" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/404.svg?cacheid=b6d648af" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/500.svg" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/500.svg?cacheid=32eb0f20" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/bittorrent.png" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/bittorrent.png" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/bittorrent.png?cacheid=4f5c6882" }, { STATIC_CONTENT, "/ROOT%23%3F/skin/bittorrent.png?cacheid=4f5c6882" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/blank.html" }, { DYNAMIC_CONTENT, "/ROOT%23%3F/skin/blank.html" },
@ -1006,7 +1008,6 @@ std::string expectedSexy404ErrorHtml(const std::string& url)
TEST_F(ServerTest, HttpSexy404HtmlError) TEST_F(ServerTest, HttpSexy404HtmlError)
{ {
using namespace TestingOfHtmlResponses;
const std::vector<std::string> testUrls { const std::vector<std::string> testUrls {
// XXX: Nicer 404 error page no longer hints whether the error // XXX: Nicer 404 error page no longer hints whether the error
// XXX: is because of the missing book/ZIM-file or a missing article // XXX: is because of the missing book/ZIM-file or a missing article
@ -1209,35 +1210,79 @@ TEST_F(ServerTest, HttpXmlError)
} }
} }
TEST_F(ServerTest, 500) std::string expectedSexy500ErrorHtml(const std::string& url)
{ {
const std::string expectedBody = R"(<!DOCTYPE html> const auto urlWithoutQuery = replace(url, "\\?.*$", "");
<html xmlns="http://www.w3.org/1999/xhtml"> const auto htmlSafeUrl = htmlEscape(urlWithoutQuery);
const auto jsSafeUrl = escapeJsString(urlWithoutQuery);
const std::string englishText[] = {
"Internal Server Error",
"Page isn&apos;t working",
"Oops. Page isn&apos;t working.",
"The requested path cannot be properly delivered:",
};
const std::string translatedText[] = {
"[I18N] Internal Server Error [TESTING]",
"Page [I18N] isn&apos;t [TESTING] working",
"Oops. [I18N] Page isn&apos;t [TESTING] working.",
"The requested path [I18N TESTING] cannot be properly delivered:",
};
const bool shouldTranslate = url.find("userlang=test") != std::string::npos;
const std::string* const t = shouldTranslate ? translatedText : englishText;
return R"RAWSTRINGLITERAL(<!DOCTYPE html>
<html>
<head> <head>
<meta content="text/html;charset=UTF-8" http-equiv="content-type" /> <meta charset="utf-8">
<title>Internal Server Error</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>)RAWSTRINGLITERAL" + t[0] + R"RAWSTRINGLITERAL(</title>
<link type="text/css" href="/ROOT%23%3F/skin/error.css?cacheid=b3fa90cf" rel="Stylesheet" />
<script> <script>
window.KIWIX_RESPONSE_TEMPLATE = )" + ERROR_HTML_TEMPLATE_JS_STRING + R"(; window.KIWIX_RESPONSE_TEMPLATE = "&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n &lt;head&gt;\n &lt;meta charset=&quot;utf-8&quot;&gt;\n &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no&quot; /&gt;\n &lt;title&gt;{{PAGE_TITLE}}&lt;/title&gt;\n &lt;link type=&quot;text/css&quot; href=&quot;{{root}}/skin/error.css?cacheid=b3fa90cf&quot; rel=&quot;Stylesheet&quot; /&gt;\n &lt;script&gt;\n window.KIWIX_RESPONSE_TEMPLATE = &quot;{{KIWIX_RESPONSE_TEMPLATE}}&quot;;\n window.KIWIX_RESPONSE_DATA = {{{KIWIX_RESPONSE_DATA}}};\n &lt;/script&gt;\n &lt;/head&gt;\n &lt;body&gt;\n &lt;header&gt;\n &lt;img src=&quot;{{root}}/skin/500.svg?cacheid=32eb0f20&quot;\n alt=&quot;{{500_img_text}}&quot;\n aria-label=&quot;{{500_img_text}}&quot;\n title=&quot;{{500_img_text}}&quot;&gt;\n &lt;/header&gt;\n &lt;section class=&quot;intro&quot;&gt;\n &lt;h1&gt;{{PAGE_HEADING}}&lt;/h1&gt;\n &lt;p&gt;{{PAGE_TEXT}}&lt;/p&gt;\n &lt;p&gt;&lt;code&gt;{{url_path}}&lt;/code&gt;&lt;/p&gt;\n &lt;/section&gt;\n &lt;/body&gt;\n&lt;/html&gt;\n";
window.KIWIX_RESPONSE_DATA = { "CSS_URL" : false, "PAGE_HEADING" : { "msgid" : "500-page-heading", "params" : { } }, "PAGE_TITLE" : { "msgid" : "500-page-title", "params" : { } }, "details" : [ { "p" : { "msgid" : "500-page-text", "params" : { } } }, { "p" : { "msgid" : "non-translated-text", "params" : { "MSG" : "Entry redirect_loop.html is a redirect entry." } } } ] }; window.KIWIX_RESPONSE_DATA = { "500_img_text" : { "msgid" : "500-img-text", "params" : { } }, "PAGE_HEADING" : { "msgid" : "500-page-heading", "params" : { } }, "PAGE_TEXT" : { "msgid" : "500-page-text", "params" : { } }, "PAGE_TITLE" : { "msgid" : "500-page-title", "params" : { } }, "root" : "/ROOT%23%3F", "url_path" : ")RAWSTRINGLITERAL"
+ // inject the URL
jsSafeUrl // inject the URL
+ // inject the URL
R"RAWSTRINGLITERAL(" };
</script> </script>
</head> </head>
<body> <body>
<h1>Internal Server Error</h1> <header>
<p> <img src="/ROOT%23%3F/skin/500.svg?cacheid=32eb0f20"
An internal server error occured. We are sorry about that :/ alt=")RAWSTRINGLITERAL" + t[1] + R"RAWSTRINGLITERAL("
</p> aria-label=")RAWSTRINGLITERAL" + t[1] + R"RAWSTRINGLITERAL("
<p> title=")RAWSTRINGLITERAL" + t[1] + R"RAWSTRINGLITERAL(">
Entry redirect_loop.html is a redirect entry. </header>
</p> <section class="intro">
<h1>)RAWSTRINGLITERAL" + t[2] + R"RAWSTRINGLITERAL(</h1>
<p>)RAWSTRINGLITERAL" + t[3] + R"RAWSTRINGLITERAL(</p>
<p><code>)RAWSTRINGLITERAL"
+ // inject the URL
htmlSafeUrl // inject the URL
+ // inject the URL
R"RAWSTRINGLITERAL(</code></p>
</section>
</body> </body>
</html> </html>
)"; )RAWSTRINGLITERAL";
}
TEST_F(ServerTest, 500)
{ {
const auto r = zfs1_->GET("/ROOT%23%3F/content/poor/A/redirect_loop.html"); const std::vector<std::string> testUrls {
EXPECT_EQ(r->status, 500); "/ROOT%23%3F/content/poor/A/redirect_loop.html",
EXPECT_EQ(r->body, expectedBody); "/ROOT%23%3F/content/poor/A/redirect_loop.html?userlang=test",
EXPECT_EQ(r->get_header_value("Content-Type"), "text/html; charset=utf-8"); };
for ( const auto& url : testUrls ) {
const TestContext ctx{ {"url", url} };
const auto r = zfs1_->GET(url.c_str());
EXPECT_EQ(r->status, 500) << ctx;
EXPECT_EQ(r->get_header_value("Content-Type"), "text/html; charset=utf-8") << ctx;
EXPECT_EQ(r->body, expectedSexy500ErrorHtml(url)) << ctx;
} }
} }