mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-17 10:59:39 +00:00
Read request body after route matching and pre-request handler
Previously, for regular handlers the request body was read in routing() before the route was matched, so the pre-request handler always saw an already-read body. The ContentReader path, in contrast, ran the pre-request handler before the body was read. This inconsistency made it impossible to reject a request (e.g. failed per-route authentication via req.matched_route) without buffering a potentially large body. Move the read_content() call into dispatch_request(), after route matching and the pre-request handler, so both paths behave the same: route matching -> pre_request_handler -> body read -> handler. A request rejected by the pre-request handler no longer reads the body at all; the existing keep-alive drain logic still consumes any framed body afterwards. Note: code that referenced req.body or body-derived form fields inside the pre-request handler will now see an empty body. Inspect headers, path, query parameters, or matched_route instead. Also document the handler execution order in README and update the pre-request cookbook pages (en/ja).
This commit is contained in:
1 parent
79d83feb18
commit
77bdf7921a
5 files changed
+133
-25
No files matched your search
@@ -1812,8 +1812,8 @@ private:
|
||||
const std::string &etag, time_t mtime) const;
|
||||
bool check_if_range(Request &req, const std::string &etag,
|
||||
time_t mtime) const;
|
||||
bool dispatch_request(Request &req, Response &res,
|
||||
const Handlers &handlers) const;
|
||||
bool dispatch_request(Request &req, Response &res, const Handlers &handlers,
|
||||
Stream &strm);
|
||||
bool dispatch_request_for_content_reader(
|
||||
Request &req, Response &res, ContentReader content_reader,
|
||||
const HandlersForContentReader &handlers) const;
|
||||
@@ -11955,26 +11955,26 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
}
|
||||
}
|
||||
|
||||
// Read content into `req.body`
|
||||
if (!read_content(strm, req, res)) {
|
||||
output_error_log(Error::Read, &req);
|
||||
return false;
|
||||
}
|
||||
// NOTE: `req.body` is not read here. For a regular handler the body is
|
||||
// read inside dispatch_request(), after the route has matched and the
|
||||
// pre-request handler has approved the request, so that a rejected
|
||||
// request (e.g. failed authentication) never forces us to buffer a
|
||||
// potentially large body.
|
||||
}
|
||||
|
||||
// Regular handler
|
||||
if (req.method == "GET" || req.method == "HEAD") {
|
||||
return dispatch_request(req, res, get_handlers_);
|
||||
return dispatch_request(req, res, get_handlers_, strm);
|
||||
} else if (req.method == "POST") {
|
||||
return dispatch_request(req, res, post_handlers_);
|
||||
return dispatch_request(req, res, post_handlers_, strm);
|
||||
} else if (req.method == "PUT") {
|
||||
return dispatch_request(req, res, put_handlers_);
|
||||
return dispatch_request(req, res, put_handlers_, strm);
|
||||
} else if (req.method == "DELETE") {
|
||||
return dispatch_request(req, res, delete_handlers_);
|
||||
return dispatch_request(req, res, delete_handlers_, strm);
|
||||
} else if (req.method == "OPTIONS") {
|
||||
return dispatch_request(req, res, options_handlers_);
|
||||
return dispatch_request(req, res, options_handlers_, strm);
|
||||
} else if (req.method == "PATCH") {
|
||||
return dispatch_request(req, res, patch_handlers_);
|
||||
return dispatch_request(req, res, patch_handlers_, strm);
|
||||
}
|
||||
|
||||
res.status = StatusCode::BadRequest_400;
|
||||
@@ -11982,17 +11982,29 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm) {
|
||||
}
|
||||
|
||||
inline bool Server::dispatch_request(Request &req, Response &res,
|
||||
const Handlers &handlers) const {
|
||||
const Handlers &handlers, Stream &strm) {
|
||||
for (const auto &x : handlers) {
|
||||
const auto &matcher = x.first;
|
||||
const auto &handler = x.second;
|
||||
|
||||
if (matcher->match(req)) {
|
||||
req.matched_route = matcher->pattern();
|
||||
if (!pre_request_handler_ ||
|
||||
pre_request_handler_(req, res) != HandlerResponse::Handled) {
|
||||
handler(req, res);
|
||||
|
||||
// Run the pre-request handler before reading the body so a rejected
|
||||
// request (e.g. failed authentication) never forces us to buffer a
|
||||
// potentially large body. `req.matched_route` is available here.
|
||||
if (pre_request_handler_ &&
|
||||
pre_request_handler_(req, res) == HandlerResponse::Handled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// The route matched and the request was approved; read the body now.
|
||||
if (detail::expect_content(req) && !read_content(strm, req, res)) {
|
||||
output_error_log(Error::Read, &req);
|
||||
return false;
|
||||
}
|
||||
|
||||
handler(req, res);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user