Skip to content

Reset the response before sending a 500 after an exception - #566

Merged
michaelJustin merged 1 commit into
masterfrom
564-reset-response-before-500
Oct 14, 2026
Merged

michaelJustin merged 1 commit into
masterfrom
564-reset-response-before-500

Conversation

@michaelJustin

Copy link
Copy Markdown
Owner

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:

  • a stale 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;
  • its Content-Type, so the HTML error page went out labelled application/json;
  • its custom headers;
  • for a filter, its partial body.

Changes

  • New unit djErrorResponse, shared by the three places that build a 500:
    • ResetResponse calls Indy's Clear, keeping the server name, the keep-alive setting and the cookies (Indy stores a new session's cookie there). It also drops ContentText and ContentStream.
    • SetErrorResponse resets 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.Handle builds the error response for every exception from the dispatch: lazy Start, the filter chain and the component. Filters are covered too, and the development-mode page still names the component. InvokeService now only logs and re-raises.
  • TdjWebComponentContextHandler.DoHandle resets the response before calling the ErrorHandler. If the ErrorHandler raises, its partial output is replaced with the default page. Once the headers have been sent, the ErrorHandler is skipped.
  • TdjHTTPConnector.OnCommand uses the same SetErrorResponse, replacing its own copy of the page.
  • Delphi test project: Unittests.dpr and .dproj list the new unit.

Tests

Three new tests in TAPIConfigTests:

  • a component, and separately a filter, that set Content-Type, a too-short Content-Length, a custom header and part of a body before raising;
  • an ErrorHandler that sets only a body.

All three fail on the old code (checked by running them against the unchanged sources), and pass now.

  • FPC (run-fpc.cmd): 201 tests, 0 errors, 0 failures; heap trace at the clean baseline (6 blocks / 744 bytes).
  • Delphi 2009 (run-delphi.cmd): OK, 201 tests.
  • djErrorResponse also compiles with DARAJA_PROJECT_STAGE_DEVELOPMENT on 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

@michaelJustin michaelJustin added this to the 3.5.0 milestone Oct 14, 2026
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
michaelJustin force-pushed the 564-reset-response-before-500 branch from ce8950c to 3bafbf3 Compare October 14, 2026 17:44
@michaelJustin
michaelJustin merged commit 965de0a into master Oct 14, 2026
2 checks passed
@michaelJustin
michaelJustin deleted the 564-reset-response-before-500 branch October 14, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reset the response before sending a 500 after an exception

1 participant