Skip to content

fix(router): permite tratar exceções nos middlewares antes do fallback do core - #616

Merged
regyssilveira merged 4 commits into
HashLoad:masterfrom
regyssilveira:codex/fix-middleware-exception-propagation
Oct 14, 2026
Merged

regyssilveira merged 4 commits into
HashLoad:masterfrom
regyssilveira:codex/fix-middleware-exception-propagation

Conversation

@regyssilveira

@regyssilveira regyssilveira commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Problema

Os roteadores Tree e Radix capturavam as exceções durante a execução dos callbacks e encerravam o tratamento dentro do core. Assim, uma rota que lançasse EHorseException ou Exception não permitia que o except de um middleware envolvendo Next recebesse o erro, mesmo com um middleware de tratamento de exceções registrado.

Correção

  • Remove o tratamento antecipado dos callbacks do Tree e faz o fluxo do Radix relançar a exceção, permitindo que ela percorra a pilha até os middlewares externos.
  • Move o tratamento padrão para o limite externo da execução de cada roteador, incluindo os caminhos Delphi e FPC. O fallback só é aplicado quando a exceção escapa da cadeia de middlewares.
  • Mantém a precedência do fallback: EHorseException gera JSON com o status da exceção e application/json; charset=UTF-8; outras exceções acionam OnError, quando configurado, ou geram a resposta padrão 500 se ainda não houver um status de erro.
  • Preserva o controle de fluxo de EHorseCallbackInterrupted e as respostas de erro já definidas.
  • Mantém o envio dos cookies após o fallback no Radix.

Com isso, o middleware pode personalizar a resposta de ambos os tipos de exceção. Se relançar a exceção ou não houver middleware para tratá-la, o core continua fornecendo o fallback.

Testes e validação

Foram adicionados 36 casos DUnitX, 18 para cada roteador, e 34 casos de regressão específicos para FPC (detalhados abaixo). A cobertura DUnitX inclui:

  • Captura de Exception e EHorseException pelo middleware.
  • Exceções em callbacks posteriores e em middlewares internos.
  • Prioridade do middleware sobre OnError.
  • Fallback sem middleware e após o middleware relançar a exceção.
  • Conteúdo, status e tipo da resposta JSON; preservação de respostas de erro existentes.
  • Interrupção da cadeia sem acionar OnError nem executar os callbacks seguintes.
  • Envio dos cookies quando o fallback trata uma exceção.

Antes da correção, oito casos iniciais reproduziram o defeito: o middleware não recebia a exceção.

Validação das suítes dos dois roteadores, API HTTP e lifecycle hooks:

Compilador Configuração Resultado
Delphi 10 Seattle Default / Tree 95 testes, sem falhas ou erros
Delphi 10 Seattle Default + Radix 95 testes, sem falhas ou erros
Delphi 13 Florence Default / Tree 95 testes, sem falhas ou erros
Delphi 13 Florence Default + Radix 95 testes, sem falhas ou erros

Validação adicional no FPC

Foi adicionado um teste de regressão independente em tests/fpc-exception-propagation, com 34 casos (17 para Tree e 17 para Radix) e callbacks procedurais compatíveis com FPC.

O teste foi compilado e executado em Linux x86_64, via Docker, com Free Pascal 3.2.2. Foram usados -B para recompilar todos os fontes, -gh para verificar memória e -gl para diagnóstico.

Configuração FPC Tree Radix Memória (heaptrc)
Padrão 17/17 passaram 17/17 passaram 0 blocos não liberados
HORSE_PROVIDER_EPOLL 17/17 passaram 17/17 passaram 0 blocos não liberados

A validação cobre captura dos dois tipos de exceção pelo middleware, callbacks posteriores, middlewares internos, prioridade sobre OnError, fallback sem middleware e após relançamento, JSON/status/Content-Type, preservação de respostas de erro e interrupção com e sem OnError.

Reprodução antes da correção: executando o mesmo teste com os três arquivos dos roteadores do commit base 31697a4, houve 18 falhas e 16 aprovações, com saída 1. As falhas apontam que a exceção não chegou ao middleware. Com a correção, foram 34/34 aprovações em cada configuração, com saída 0.

Para reproduzir na raiz do repositório em Linux com FPC/FCL:

sh tests/fpc-exception-propagation/run-exception-propagation-test.sh
sh tests/fpc-exception-propagation/run-exception-propagation-test.sh -dHORSE_PROVIDER_EPOLL

Esses testes FPC exercitam diretamente os dois roteadores, sem abrir servidor HTTP. A configuração Epoll confirma a compilação e execução com esse define; não constitui um teste do transporte HTTP. Os testes de API HTTP e lifecycle hooks da tabela anterior foram executados em Delphi.

git diff --check também passou.

Antes de iniciar a correção, foi confirmado que o master local, origin/master do fork e upstream/master do Horse estavam sincronizados.

CI e manutenção da cobertura

As duas configurações da regressão FPC foram integradas ao workflow tests. Durante a reativação do CI, o job legado falhou porque o endereço antigo do Boss retornava Not Found; uma compilação de controle confirmou também que Console.dpr depende de Delphi/DUnitX, indisponível no ambiente FPC do job.

O job Linux passou a executar as suítes portáveis existentes: propagação de exceções (padrão e Epoll), WebSocket EAGAIN, lifetime de roteadores, decodificação de URL, factory de streaming e configuração de providers. A suíte Delphi/DUnitX continua validada pelo script tests/run_delphi_tests.ps1, com os resultados locais registrados acima. O job de keep-alive em FPC trunk permanece no workflow.

@regyssilveira
regyssilveira merged commit da9cd36 into HashLoad:master Oct 14, 2026
2 checks passed
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.

1 participant