mirror of
https://github.com/kiwix/libkiwix.git
synced 2025-09-09 07:08:38 -04:00
Merge pull request #1196 from kiwix/nicer_internal_server_error_page
Nicer Internal Server Error page
This commit is contained in:
commit
33c83eec4b
@ -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");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,15 +491,30 @@ 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,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
const std::string& urlPath,
|
||||||
"500-page-title",
|
const std::string& errorText)
|
||||||
"500-page-heading",
|
: ContentResponseBlueprint(&request,
|
||||||
std::string(),
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
/*includeKiwixResponseData=*/true)
|
"text/html; charset=utf-8",
|
||||||
|
RESOURCE::templates::sexy500_html,
|
||||||
|
/*includeKiwixResponseData=*/true)
|
||||||
{
|
{
|
||||||
*this += nonParameterizedMessage("500-page-text");
|
auto pageParams = 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")},
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( !errorText.empty() ) {
|
||||||
|
pageParams["error"] = errorText;
|
||||||
|
}
|
||||||
|
|
||||||
|
*this->m_data = Data(pageParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Response> Response::build_416(size_t resourceLength)
|
std::unique_ptr<Response> Response::build_416(size_t resourceLength)
|
||||||
|
@ -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 {
|
||||||
|
@ -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
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 |
@ -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"
|
||||||
|
@ -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)",
|
||||||
|
@ -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"
|
||||||
|
31
static/templates/sexy500.html
Normal file
31
static/templates/sexy500.html
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<!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>
|
||||||
|
{{#error}}
|
||||||
|
<section class="advice">
|
||||||
|
<p>{{error}}</p>
|
||||||
|
</section>
|
||||||
|
{{/error}}
|
||||||
|
</body>
|
||||||
|
</html>
|
106
test/server.cpp
106
test/server.cpp
@ -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,98 @@ TEST_F(ServerTest, HttpXmlError)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ServerTest, 500)
|
std::string expectedSexy500ErrorHtml(const std::string& url,
|
||||||
|
const std::string& error)
|
||||||
{
|
{
|
||||||
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't working",
|
||||||
|
"Oops. Page isn't working.",
|
||||||
|
"The requested path cannot be properly delivered:",
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string translatedText[] = {
|
||||||
|
"[I18N] Internal Server Error [TESTING]",
|
||||||
|
"Page [I18N] isn't [TESTING] working",
|
||||||
|
"Oops. [I18N] Page isn'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 = "<!DOCTYPE html>\n<html>\n <head>\n <meta charset="utf-8">\n <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />\n <title>{{PAGE_TITLE}}</title>\n <link type="text/css" href="{{root}}/skin/error.css?cacheid=b3fa90cf" rel="Stylesheet" />\n <script>\n window.KIWIX_RESPONSE_TEMPLATE = "{{KIWIX_RESPONSE_TEMPLATE}}";\n window.KIWIX_RESPONSE_DATA = {{{KIWIX_RESPONSE_DATA}}};\n </script>\n </head>\n <body>\n <header>\n <img src="{{root}}/skin/500.svg?cacheid=32eb0f20"\n alt="{{500_img_text}}"\n aria-label="{{500_img_text}}"\n title="{{500_img_text}}">\n </header>\n <section class="intro">\n <h1>{{PAGE_HEADING}}</h1>\n <p>{{PAGE_TEXT}}</p>\n <p><code>{{url_path}}</code></p>\n </section>\n{{#error}}\n <section class="advice">\n <p>{{error}}</p>\n </section>\n{{/error}}\n </body>\n</html>\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" : { } }, "error" : ")RAWSTRINGLITERAL"
|
||||||
|
+ // inject the error
|
||||||
|
escapeJsString(error) // inject the error
|
||||||
|
+ // inject the error
|
||||||
|
R"RAWSTRINGLITERAL(", "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>
|
||||||
|
<section class="advice">
|
||||||
|
<p>)RAWSTRINGLITERAL" + error + R"RAWSTRINGLITERAL(</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");
|
{
|
||||||
EXPECT_EQ(r->status, 500);
|
struct TestData {
|
||||||
EXPECT_EQ(r->body, expectedBody);
|
const std::string url;
|
||||||
EXPECT_EQ(r->get_header_value("Content-Type"), "text/html; charset=utf-8");
|
const std::string error;
|
||||||
|
};
|
||||||
|
const TestData testData[] = {
|
||||||
|
{
|
||||||
|
"/ROOT%23%3F/content/poor/A/redirect_loop.html",
|
||||||
|
"Entry redirect_loop.html is a redirect entry."
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"/ROOT%23%3F/content/poor/A/redirect_loop.html?userlang=test",
|
||||||
|
"Entry redirect_loop.html is a redirect entry."
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for ( const auto& td : testData ) {
|
||||||
|
const TestContext ctx{ {"url", td.url} };
|
||||||
|
const auto r = zfs1_->GET(td.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(td.url, td.error)) << ctx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user