Skip to content

feat: add horse-provider-nghttp2 provider (HTTP/2 + gRPC, Delphi + FPC) - #545

Closed
freitasjca wants to merge 0 commit into
HashLoad:masterfrom
freitasjca:master
Closed

feat: add horse-provider-nghttp2 provider (HTTP/2 + gRPC, Delphi + FPC)#545
freitasjca wants to merge 0 commit into
HashLoad:masterfrom
freitasjca:master

Conversation

@freitasjca

@freitasjca freitasjca commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds HORSE_PROVIDER_NGHTTP2 provider-selection support to Horse.pas, following the pattern already used by CrossSocket, mORMot, ICS, epoll and IOCP.

3 files, +56 / −7. Horse.pas is +42 / −0 — nothing is removed or renamed, and every line is inside a conditional. Without the define, compilation is byte-identical to upstream on both compilers.

  • Legacy alias HORSE_NGHTTP2 maps to the canonical define
  • Compile-time FATAL guards for impossible combinations (host-managed + self-hosted, provider mutual exclusivity)
  • Console, VCL and Daemon shapes, Delphi and FPC
  • THorseProvider resolves to THorseProviderNghttp2 when active
  • README.md / README.pt-BR.md gain a provider table row and install note

Correction since this PR was opened

Two things in the original description are now out of date, and both made the ask look larger than it is.

FPC 3.2.2 is supported. The original text said "FPC trunk 3.3.1". That was wrong even then, and it matters here: .github/workflows/tests.yml installs the compiler with apt-get install -y fpc, which is 3.2.2. The HTTP/2 transport, TLS, the epoll engine, streaming and WebSocket all build and pass on 3.2.2 — verified over 25 stages. Only the gRPC layer needs trunk, because 3.2.2's Rtti unit declares no TCustomAttribute, and it is separable behind HORSE_NGHTTP2_NO_GRPC. Nothing in this PR requires a compiler Horse's CI does not already install.

The provider is smaller than it was. The gRPC layer has moved out of horse-provider-nghttp2 into Delphi-nghttp2 (1.5.0–1.7.0). It never referenced a Horse unit — only its unit names did — so the move was a rename. What remains in the provider is the adapter: lifecycle shapes, request/response bridges, context pool, worker pool, WebSocket, stream writer. Structurally the same shape as Horse.Provider.CrossSocket.*.

Dependencies

  • horse-provider-nghttp2 ≥ 1.7.0 and Delphi-nghttp2 ≥ 1.7.0, via boss install
  • libnghttp2 ≥ 1.59, dynamically loaded — no link-time dependency, and the framework compiles and links without it
  • Delphi 10.4+ or FPC 3.2.2 (trunk 3.3.1 only for gRPC)

Validation

Toolchain Result
FPC trunk 3.3.1 (Linux x86-64) 33 stages, 0 failures — 106-check suite over h2c/TLS/mTLS and again through the epoll engine, gRPC 35/35, streaming, backpressure, graceful drain, two-stage GOAWAY, WebSocket RFC 8441
FPC 3.2.2 (Linux x86-64) 25 passed, 0 failed, 2 explicit skips (the gRPC layer, which needs trunk — it skips loudly rather than silently)
Delphi 12 Win64 5 projects clean; 106/106 including streaming and SSE; gRPC 35/35 at runtime
Interop grpcurl against the server on both Linux and Windows

For this PR specifically: compiling without the define produces no change from upstream; with HORSE_PROVIDER_NGHTTP2 it routes to Horse.Provider.Nghttp2; the legacy define behaves identically; and each invalid host/provider combination fails at compile time with the intended FATAL.

Relationship to the other open PRs

These are independent of this one — the hooks compile and route without them — but the provider needs them at runtime, and #552 affects existing users regardless of whether this PR is merged:

If it helps to take them in an order: #552 first (one clause, no dependency on anything else here), then #549 and #551, then #550, and this one last since it is the one that carries a policy question rather than a defect.

The policy question

This would be Horse's first provider with a third-party runtime dependency. libnghttp2 is dynamically loaded, so the framework still compiles and links without it and a user who never sets the define never encounters it — but the repository's dependency story changes from "none" to "none unless you opt in". That seems worth deciding deliberately rather than absorbing as a side effect of a merge, and I would rather it be discussed here than assumed.

Reverting is one commit: the additions are conditional and nothing existing was modified.

@regyssilveira

Copy link
Copy Markdown
Contributor

Obrigado pelo trabalho de integração do provider nghttp2. Antes de avaliarmos a decisão arquitetural e aprovarmos este PR, a branch precisa ser atualizada e reduzida ao escopo anunciado.

Os PRs #552, #551 e #550 já foram integrados ao master. A branch deste PR contém cópias dessas mesmas alterações e também inclui a implementação ainda pendente do #549. Por favor:

  1. Faça rebase sobre o master atual.
  2. Remova da branch as alterações de:
  3. Mantenha neste PR somente a integração/seletor HORSE_PROVIDER_NGHTTP2 e a documentação diretamente necessária ao provider.
  4. Retire .gitattributes deste PR ou envie essa política de line endings separadamente, pois ela afeta todo o repositório e não é necessária para habilitar o provider.
  5. Atualize a descrição e o resumo de arquivos para refletirem o diff real após o rebase.
  6. Execute novamente a matriz declarada para Delphi e FPC sobre o master atualizado e publique os resultados.

Depois dessa limpeza conseguiremos revisar isoladamente a questão principal: aceitar um provider opt-in com dependência dinâmica de libnghttp2, sem misturar essa decisão com correções independentes do core.

@freitasjca freitasjca closed this Aug 28, 2026
freitasjca added a commit to freitasjca/horse that referenced this pull request Sep 2, 2026
Adds the opt-in define HORSE_PROVIDER_NGHTTP2 (and strict-mode alias
HORSE_PROVIDER_NGHTTP2_REQUIRED) to Horse.pas so the nghttp2 HTTP/2
transport can be selected without touching any existing provider code.

horse-provider-nghttp2 supplies TListen, DoStartListenFunc, and
DoStopListenFunc; Horse.pas routes to them when the define is set.
All other providers (Indy, fphttpserver, CrossSocket, mORMot, ICS,
HTTP.sys, epoll, IOCP, host-managed) are completely unaffected. Without
the define this file compiles identically to the upstream original on
both Delphi and Lazarus/FPC.

Supersedes closed PR HashLoad#545.
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.

2 participants