mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-09-03 07:13:48 +00:00
Server::process_request() wraps only routing() in a try/catch. Everything else the user supplies runs outside it: - the content provider, from write_response_core() - post_routing_handler_, error_handler_, logger_ - expect_100_continue_handler_ - a WebSocket handler, and pre_routing_handler_ on the upgrade path An exception from any of those unwinds out of process_and_close_socket() into the task queue, which calls the job without a catch, so it reaches the top of a pool thread and terminates the process. One handler that throws takes down every other connection the server is holding. Add Server::serve_guarded() and run the serving loop through it in both process_and_close_socket() overloads. The exception is not turned into a 500: by the time a content provider runs, the status line and headers are already on the wire, so there is nothing left to replace. Report it through the error logger as Error::UserCallbackException and drop the connection, which is what the peer observes regardless. Requests on other connections are unaffected, and the socket is still drained and closed - which unwinding used to skip on the non-SSL path, since drain_and_close_socket() sits after the call rather than in a scope guard. The error logger is a user callback too, so the report inside the guard is itself wrapped: a throwing logger must not be able to open the guard back up. Adds ServerExceptionTest: a throwing content provider, post-routing handler, WebSocket handler and error logger, plus the content provider case against SSLServer, each checking that a later request on a new connection still succeeds. Every test runs the server on a single worker thread, so a guard that catches the exception but still loses the thread shows up as the follow-up request never being served. Note that all of them abort the test binary without this change - which is the bug, but it means a regression here fails the run rather than one test.