Repository navigation
Reset the response before sending a 500 after an exception - #566
Merged
Merged
Conversation
When a Web Component or Web Filter raised after it had started its response, the 500 kept what it had set: a stale Content-Length (Indy only recalculates it when it is -1), its Content-Type, custom headers and, for a filter, its partial body. - New unit djErrorResponse: ResetResponse (Indy's Clear, keeping the server name, keep-alive setting and cookies; drops ContentText and ContentStream) and SetErrorResponse (reset + the framework's 500 page, or -- once headers were sent -- drop pending content and close the connection instead of appending to a partial body). - TdjWebComponentHandler.Handle builds the error response for every exception from the dispatch (lazy Start, filter chain, component), so filters are covered and the development page still names the component. InvokeService now only logs and re-raises. - TdjWebComponentContextHandler.DoHandle resets before calling the ErrorHandler, and replaces a failed ErrorHandler's partial output with the default page; the ErrorHandler is skipped once headers were sent. - TdjHTTPConnector.OnCommand uses the same SetErrorResponse. Tests: a component and a filter that set Content-Type, a short Content-Length, a custom header and part of a body before raising, and an ErrorHandler that only sets a body. All three fail on the old code. Co-Authored-By: Claude Opus 5.5 <[email protected]>
michaelJustin
force-pushed
the
564-reset-response-before-500
branch
from
October 14, 2026 17:44
ce8950c to
3bafbf3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #564.
When a Web Component or Web Filter raised after it had started its response, the 500 kept what the handler had already set:
Content-Length. Indy recalculates it only when it is-1, so the error page arrived truncated, or the client waited for bytes that never came;Content-Type, so the HTML error page went out labelledapplication/json;Changes
djErrorResponse, shared by the three places that build a 500:ResetResponsecalls Indy'sClear, keeping the server name, the keep-alive setting and the cookies (Indy stores a new session's cookie there). It also dropsContentTextandContentStream.SetErrorResponseresets and writes the framework's 500 page. If the headers were already sent, it drops pending content and closes the connection instead of appending to a partial body.TdjWebComponentHandler.Handlebuilds the error response for every exception from the dispatch: lazyStart, the filter chain and the component. Filters are covered too, and the development-mode page still names the component.InvokeServicenow only logs and re-raises.TdjWebComponentContextHandler.DoHandleresets the response before calling theErrorHandler. If theErrorHandlerraises, its partial output is replaced with the default page. Once the headers have been sent, theErrorHandleris skipped.TdjHTTPConnector.OnCommanduses the sameSetErrorResponse, replacing its own copy of the page.Unittests.dprand.dprojlist the new unit.Tests
Three new tests in
TAPIConfigTests:Content-Type, a too-shortContent-Length, a custom header and part of a body before raising;ErrorHandlerthat sets only a body.All three fail on the old code (checked by running them against the unchanged sources), and pass now.
run-fpc.cmd): 201 tests, 0 errors, 0 failures; heap trace at the clean baseline (6 blocks / 744 bytes).run-delphi.cmd): OK, 201 tests.djErrorResponsealso compiles withDARAJA_PROJECT_STAGE_DEVELOPMENTon both compilers. The madExcept and JCL stack-trace variants are not built here.The development-mode page changes slightly. It now shows the exception class and message, the component name and the stack trace in a single layout, and it's also used for filter exceptions.
🤖 Generated with Claude Code