Add additional HttpStatusCodes and implement a custom error handler (#439)

This adds:

Various lesser-used HTTP status codes to the HttpStatusCode enumeration.
Getter and setter for customErrorHandler, which is a function that generates a HttpResponsePtr given an HttpStatusCode. This is intended to be similar to the custom404 functionality, allowing a user to override the layout of the error page.
The custom404 functions were kept even though this supersedes that to avoid breaking current code.

Finally, all of the Routers were updated to use the error handler for their 405/403 responses.

If no custom error handler is set, a default is used. The default behavior is identical to what exists now, an empty body with the status code set.
This commit is contained in:
Zach Hilman 2020-05-18 23:50:44 -04:00 committed by GitHub
parent 4423d836f4
commit c754d65cf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 116 additions and 29 deletions

View File

@ -124,6 +124,23 @@ class HttpAppFramework : public trantor::NonCopyable
virtual HttpAppFramework &setCustom404Page(const HttpResponsePtr &resp,
bool set404 = true) = 0;
/// Set custom error handler
/**
* @param resp_generator is invoked when an error in the framework needs to
* be sent to the client to provide a custom layout.
*/
virtual HttpAppFramework &setCustomErrorHandler(
std::function<HttpResponsePtr(HttpStatusCode)> &&resp_generator) = 0;
/// Get custom error handler
/**
* @return A const-reference to the error handler set using
* setCustomErrorHandler. If none was provided, the default error handler is
* returned.
*/
virtual const std::function<HttpResponsePtr(HttpStatusCode)>
&getCustomErrorHandler() const = 0;
/// Get the plugin object registered in the framework
/**
* @note

View File

@ -37,6 +37,7 @@ enum HttpStatusCode
k304NotModified = 304,
k305UseProxy = 305,
k307TemporaryRedirect = 307,
k308PermanentRedirect = 308,
k400BadRequest = 400,
k401Unauthorized = 401,
k402PaymentRequired = 402,
@ -53,14 +54,22 @@ enum HttpStatusCode
k413RequestEntityTooLarge = 413,
k414RequestURITooLarge = 414,
k415UnsupportedMediaType = 415,
k416Requestedrangenotsatisfiable = 416,
k416RequestedRangeNotSatisfiable = 416,
k417ExpectationFailed = 417,
k421MisdirectedRequest = 421,
k425TooEarly = 425,
k426UpgradeRequired = 426,
k428PreconditionRequired = 428,
k429TooManyRequests = 429,
k431RequestHeaderFieldsTooLarge = 431,
k451UnavailableForLegalReasons = 451,
k500InternalServerError = 500,
k501NotImplemented = 501,
k502BadGateway = 502,
k503ServiceUnavailable = 503,
k504GatewayTimeout = 504,
k505HTTPVersionnotsupported = 505,
k505HTTPVersionNotSupported = 505,
k510NotExtended = 510,
};
enum class Version

View File

@ -109,6 +109,10 @@ std::string getGitCommit()
{
return DROGON_VERSION_SHA1;
}
HttpResponsePtr defaultErrorHandler(HttpStatusCode code)
{
return std::make_shared<HttpResponseImpl>(code, CT_TEXT_HTML);
}
} // namespace drogon
static void godaemon(void)
{
@ -947,4 +951,17 @@ HttpAppFramework &HttpAppFrameworkImpl::addALocation(
bool HttpAppFrameworkImpl::areAllDbClientsAvailable() const noexcept
{
return dbClientManagerPtr_->areAllDbClientsAvailable();
}
}
HttpAppFramework &HttpAppFrameworkImpl::setCustomErrorHandler(
std::function<HttpResponsePtr(HttpStatusCode)> &&resp_generator)
{
customErrorHandler_ = std::move(resp_generator);
return *this;
}
const std::function<HttpResponsePtr(HttpStatusCode)>
&HttpAppFrameworkImpl::getCustomErrorHandler() const
{
return customErrorHandler_;
}

View File

@ -27,6 +27,8 @@
namespace drogon
{
HttpResponsePtr defaultErrorHandler(HttpStatusCode code);
struct InitBeforeMainFunction
{
explicit InitBeforeMainFunction(const std::function<void()> &func)
@ -34,6 +36,7 @@ struct InitBeforeMainFunction
func();
}
};
class HttpAppFrameworkImpl : public HttpAppFramework
{
public:
@ -81,6 +84,10 @@ class HttpAppFrameworkImpl : public HttpAppFramework
return *this;
}
HttpAppFramework &setCustomErrorHandler(
std::function<HttpResponsePtr(HttpStatusCode)> &&resp_generator)
override;
const HttpResponsePtr &getCustom404Page();
virtual void forward(
@ -433,6 +440,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework
}
virtual bool areAllDbClientsAvailable() const noexcept override;
const std::function<HttpResponsePtr(HttpStatusCode)>
&getCustomErrorHandler() const override;
private:
virtual void registerHttpController(
@ -516,6 +525,8 @@ class HttpAppFrameworkImpl : public HttpAppFramework
std::unique_ptr<SessionManager> sessionManagerPtr_;
Json::Value jsonConfig_;
HttpResponsePtr custom404_;
std::function<HttpResponsePtr(HttpStatusCode)> customErrorHandler_ =
&defaultErrorHandler;
static InitBeforeMainFunction initFirst_;
bool enableServerHeader_{true};
bool enableDateHeader_{true};

View File

@ -427,16 +427,15 @@ void HttpControllersRouter::route(
if (!binder)
{
// Invalid Http Method
auto res = drogon::HttpResponse::newHttpResponse();
if (req->method() != Options)
{
res->setStatusCode(k405MethodNotAllowed);
callback(
app().getCustomErrorHandler()(k405MethodNotAllowed));
}
else
{
res->setStatusCode(k403Forbidden);
callback(app().getCustomErrorHandler()(k403Forbidden));
}
callback(res);
return;
}
if (!postRoutingObservers_.empty())

View File

@ -105,16 +105,14 @@ void HttpSimpleControllersRouter::route(
if (!binder)
{
// Invalid Http Method
auto res = drogon::HttpResponse::newHttpResponse();
if (req->method() != Options)
{
res->setStatusCode(k405MethodNotAllowed);
callback(app().getCustomErrorHandler()(k405MethodNotAllowed));
}
else
{
res->setStatusCode(k403Forbidden);
callback(app().getCustomErrorHandler()(k403Forbidden));
}
callback(res);
return;
}
// Do post routing advices.

View File

@ -241,6 +241,11 @@ const string_view &statusCodeToString(int code)
static string_view sv = "Temporary Redirect";
return sv;
}
case 308:
{
static string_view sv = "Permanent Redirect";
return sv;
}
case 400:
{
static string_view sv = "Bad Request";
@ -323,7 +328,7 @@ const string_view &statusCodeToString(int code)
}
case 416:
{
static string_view sv = "Requested range not satisfiable";
static string_view sv = "Requested Range Not Satisfiable";
return sv;
}
case 417:
@ -331,6 +336,41 @@ const string_view &statusCodeToString(int code)
static string_view sv = "Expectation Failed";
return sv;
}
case 421:
{
static string_view sv = "Misdirected Request";
return sv;
}
case 425:
{
static string_view sv = "Too Early";
return sv;
}
case 426:
{
static string_view sv = "Upgrade Required";
return sv;
}
case 428:
{
static string_view sv = "Precondition Required";
return sv;
}
case 429:
{
static string_view sv = "Too Many Requests";
return sv;
}
case 431:
{
static string_view sv = "Request Header Fields Too Large";
return sv;
}
case 451:
{
static string_view sv = "Unavailable For Legal Reasons";
return sv;
}
case 500:
{
static string_view sv = "Internal Server Error";
@ -358,7 +398,12 @@ const string_view &statusCodeToString(int code)
}
case 505:
{
static string_view sv = "HTTP Version not supported";
static string_view sv = "HTTP Version Not Supported";
return sv;
}
case 510:
{
static string_view sv = "Not Extended";
return sv;
}
default:

View File

@ -61,9 +61,7 @@ void StaticFileRouter::route(
if (path.find("/../") != std::string::npos)
{
// Downloading files from the parent folder is forbidden.
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k403Forbidden);
callback(resp);
callback(app().getCustomErrorHandler()(k403Forbidden));
return;
}
auto lPath = path;
@ -114,9 +112,7 @@ void StaticFileRouter::route(
auto pos = restOfThePath.rfind('/');
if (pos != 0 && pos != string_view::npos && !location.isRecursive_)
{
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k403Forbidden);
callback(resp);
callback(app().getCustomErrorHandler()(k403Forbidden));
return;
}
if (!location.allowAll_)
@ -124,9 +120,7 @@ void StaticFileRouter::route(
pos = restOfThePath.rfind('.');
if (pos == string_view::npos)
{
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k403Forbidden);
callback(resp);
callback(app().getCustomErrorHandler()(k403Forbidden));
return;
}
std::string extension{restOfThePath.data() + pos + 1,
@ -137,9 +131,7 @@ void StaticFileRouter::route(
tolower);
if (fileTypeSet_.find(extension) == fileTypeSet_.end())
{
auto resp = HttpResponse::newHttpResponse();
resp->setStatusCode(k403Forbidden);
callback(resp);
callback(app().getCustomErrorHandler()(k403Forbidden));
return;
}
}

View File

@ -110,16 +110,15 @@ void WebsocketControllersRouter::route(
if (!binder)
{
// Invalid Http Method
auto res = drogon::HttpResponse::newHttpResponse();
if (req->method() != Options)
{
res->setStatusCode(k405MethodNotAllowed);
callback(
app().getCustomErrorHandler()(k405MethodNotAllowed));
}
else
{
res->setStatusCode(k403Forbidden);
callback(app().getCustomErrorHandler()(k403Forbidden));
}
callback(res);
return;
}
// Do post routing advices.