|
18 | 18 | USA |
19 | 19 | */ |
20 | 20 |
|
21 | | -// Error-page helpers, split out of detail/webserver_dispatch.cpp to |
22 | | -// keep that TU under the per-file LOC ceiling (FILE_LOC_MAX in |
23 | | -// scripts/check-file-size.sh). |
24 | | -// |
25 | | -// Five small functions live here: the three synth-response helpers |
26 | | -// (not_found_page, method_not_allowed_page, internal_error_page), the |
27 | | -// guarded user-logger forwarder (log_dispatch_error), and the |
28 | | -// double-fault-safe wrapper around the user internal_error_handler |
29 | | -// (run_internal_error_handler_safely). They are pure const helpers off |
30 | | -// webserver_impl and share no state with the rest of the dispatch TU |
31 | | -// beyond `parent->*` user handlers and `mr->request`. |
| 21 | +// Thin webserver_impl forwarders into the error_pages behavior service |
| 22 | +// and the log_dispatch_error free function (DR-014 §4.11). The real logic |
| 23 | +// moved to detail/error_pages.cpp and detail/dispatch_util.cpp. These |
| 24 | +// forwarders keep the existing in-class call sites |
| 25 | +// (not_found_page(mr) / log_dispatch_error(msg) / ...) compiling |
| 26 | +// unchanged during the migration; they are removed once every caller is |
| 27 | +// itself a service holding error_pages& / the config bag directly. |
32 | 28 |
|
33 | 29 | #include "httpserver/webserver.hpp" |
34 | 30 | #include "httpserver/detail/webserver_impl.hpp" |
35 | 31 |
|
36 | | -#include <string> |
37 | 32 | #include <string_view> |
38 | 33 |
|
39 | | -#include "httpserver/constants.hpp" |
40 | 34 | #include "httpserver/create_webserver.hpp" |
41 | | -#include "httpserver/detail/modded_request.hpp" |
42 | | -#include "httpserver/http_request.hpp" |
43 | 35 | #include "httpserver/http_response.hpp" |
44 | | -#include "httpserver/http_utils.hpp" |
| 36 | +#include "httpserver/detail/dispatch_util.hpp" |
45 | 37 |
|
46 | 38 | namespace httpserver { |
47 | | - |
48 | | -using httpserver::http::http_utils; |
49 | | - |
50 | 39 | namespace detail { |
51 | 40 |
|
52 | 41 | http_response webserver_impl::not_found_page(detail::modded_request* mr) const { |
53 | | - if (parent->config.not_found_handler != nullptr) { |
54 | | - return parent->config.not_found_handler(*mr->request); |
55 | | - } |
56 | | - return http_response::string(std::string{constants::NOT_FOUND_ERROR}) |
57 | | - .with_status(http_utils::http_not_found); |
| 42 | + return errors_.not_found_page(mr); |
58 | 43 | } |
59 | 44 |
|
60 | | -http_response webserver_impl::method_not_allowed_page(detail::modded_request* mr) const { |
61 | | - if (parent->config.method_not_allowed_handler != nullptr) { |
62 | | - return parent->config.method_not_allowed_handler(*mr->request); |
63 | | - } |
64 | | - return http_response::string(std::string{constants::METHOD_ERROR}) |
65 | | - .with_status(http_utils::http_method_not_allowed); |
| 45 | +http_response webserver_impl::method_not_allowed_page( |
| 46 | + detail::modded_request* mr) const { |
| 47 | + return errors_.method_not_allowed_page(mr); |
66 | 48 | } |
67 | 49 |
|
68 | | -http_response webserver_impl::internal_error_page( |
69 | | - detail::modded_request* mr, |
70 | | - std::string_view msg, |
71 | | - bool force_our) const { |
72 | | - // The double-fault fallback. Used |
73 | | - // when the user-supplied internal_error_handler itself threw or |
74 | | - // when the belt-and-suspenders site after |
75 | | - // get_raw_response_with_fallback fires. The body is intentionally |
76 | | - // empty and the message is intentionally ignored. |
77 | | - if (force_our) { |
78 | | - return http_response::empty() |
79 | | - .with_status(http_utils::http_internal_server_error); |
80 | | - } |
81 | | - // Invoke the user handler with the originating message. |
82 | | - if (parent->config.internal_error_handler != nullptr) { |
83 | | - return parent->config.internal_error_handler(*mr->request, msg); |
84 | | - } |
85 | | - // The default body is the fixed string |
86 | | - // "Internal Server Error" to avoid CWE-209 information disclosure of |
87 | | - // e.what() text (which routinely embeds file paths, SQL fragments, |
88 | | - // internal identifiers, attacker-influenced input). The originating |
89 | | - // message is still surfaced via the configured log_error callback |
90 | | - // (see log_dispatch_error). Application code that needs the v1 |
91 | | - // verbose body (for development) must opt in via |
92 | | - // create_webserver::expose_exception_messages(true). |
93 | | - const auto status = http_utils::http_internal_server_error; |
94 | | - if (parent->config.expose_exception_messages) { |
95 | | - return http_response::string(std::string{msg}).with_status(status); |
96 | | - } |
97 | | - return http_response::string( |
98 | | - std::string{constants::INTERNAL_SERVER_ERROR}) |
99 | | - .with_status(status); |
| 50 | +http_response webserver_impl::internal_error_page(detail::modded_request* mr, |
| 51 | + std::string_view msg, |
| 52 | + bool force_our) const { |
| 53 | + return errors_.internal_error_page(mr, msg, force_our); |
100 | 54 | } |
101 | 55 |
|
102 | | -void webserver_impl::log_dispatch_error(std::string_view msg) const noexcept { |
103 | | - if (parent->config.log_error == nullptr) { |
104 | | - return; |
105 | | - } |
106 | | - // msg is forwarded VERBATIM regardless |
107 | | - // of create_webserver::expose_exception_messages. The error log is |
108 | | - // the canonical destination for the verbatim exception text; only |
109 | | - // the HTTP response body path is sanitized. |
110 | | - // |
111 | | - // Framework contract (CWE-532): msg may contain e.what() text from |
112 | | - // a handler exception, which could include sensitive data (DB connection |
113 | | - // strings, file paths, user-supplied input that triggered the exception). |
114 | | - // Application code should sanitize or wrap exceptions that might expose |
115 | | - // sensitive information before re-throwing them. |
116 | | - // |
117 | | - // A misbehaving user logger must not poison the catch from inside |
118 | | - // the catch. Swallow any exception it throws; we have no recovery |
119 | | - // beyond dropping the log line. |
120 | | - try { |
121 | | - parent->config.log_error(std::string(msg)); |
122 | | - } catch (...) { |
123 | | - // Intentionally suppressed. |
124 | | - } |
| 56 | +http_response webserver_impl::run_internal_error_handler_safely( |
| 57 | + detail::modded_request* mr, |
| 58 | + std::string_view msg) const { |
| 59 | + return errors_.run_internal_error_handler_safely(mr, msg); |
125 | 60 | } |
126 | 61 |
|
127 | | -http_response |
128 | | -webserver_impl::run_internal_error_handler_safely( |
129 | | - detail::modded_request* mr, |
130 | | - std::string_view msg) const { |
131 | | - try { |
132 | | - return internal_error_page(mr, msg, /*force_our=*/false); |
133 | | - } catch (...) { |
134 | | - // The user handler itself threw. Log generically |
135 | | - // and return an empty-body 500. No exception escapes from here. |
136 | | - log_dispatch_error("internal_error_handler threw; " |
137 | | - "sending hardcoded empty-body 500"); |
138 | | - return internal_error_page(mr, "", /*force_our=*/true); |
139 | | - } |
| 62 | +void webserver_impl::log_dispatch_error(std::string_view msg) const noexcept { |
| 63 | + detail::log_dispatch_error(parent->config, msg); |
140 | 64 | } |
141 | 65 |
|
142 | 66 | } // namespace detail |
143 | | - |
144 | 67 | } // namespace httpserver |
0 commit comments