Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,16 @@ A _provider_ is the HTTP transport that owns the socket and hands requests to yo
| 🆕 **[epoll](./doc/epoll.md)** _(Linux-native asynchronous event loop)_ | `HORSE_PROVIDER_EPOLL` |    ✔️ |     ✔️ |
| 🆕 **[horse-provider-ics](https://github.com/freitasjca/horse-provider-ics)** _(Delphi; Win + Linux/macOS)_ | `HORSE_PROVIDER_ICS` |    ✔️ |     ❌ |
| 🆕 **[IOCP](./doc/iocp.md)** _(Windows-native asynchronous I/O completion ports)_ | `HORSE_PROVIDER_IOCP` |    ✔️ |     ✔️ |
| 🆕 **[horse-provider-nghttp2](https://github.com/freitasjca/horse-provider-nghttp2)** _(HTTP/2 + gRPC; requires [Delphi-nghttp2](https://github.com/freitasjca/Delphi-nghttp2))_ | `HORSE_PROVIDER_NGHTTP2` |    ✔️ |     ✔️ |

> **Note** — Apache / ISAPI / CGI / FastCGI Application types (below) do **not** use any of these Providers. The host process (Apache, IIS, the web server) owns the socket; Horse runs in-process. See [Providers & Application types](./doc/providers.md) for the full model.

> **Delphi-Cross-Socket installation** — clone [`winddriver/Delphi-Cross-Socket`](https://github.com/winddriver/Delphi-Cross-Socket) (upstream) **plus** [`cnpack/cnvcl/.../Crypto`](https://github.com/cnpack/cnvcl/tree/master/Source/Crypto) for the required CnPack/Crypto units, and add search paths to your project. Three previously-fork-only bug fixes have been merged into upstream as of 2026-Q2, so the upstream mainline is correct for general use. For server-side **mutual TLS** (`SSLVerifyPeer = True` + `SSLCACertFile = ...`) use the pre-built release [`freitasjca/Delphi-Cross-Socket v1.0.3`](https://github.com/freitasjca/Delphi-Cross-Socket/releases/tag/v1.0.3) — single clone, CnPack bundled, mTLS APIs (`SetCACertificateFile` + `SetVerifyPeer`) ready to use. See [horse-provider-crosssocket Installation](./doc/providers.md#crosssocket-optional) for the full two-path breakdown.

> **OverbyteICS installation** — the ICS Provider requires [OverbyteICS](https://wiki.overbyte.eu/wiki/index.php/ICS_Download) (v9.x). **Install ICS following the official ICS instructions** — download/clone ICS and add its `Source/` folder to your project search path (ICS is not Boss-installable). For TLS, the OpenSSL libraries ship with ICS (DLLs on Windows, `.so` on Linux). The ICS Provider is **Delphi only — Windows and POSIX (Linux64 / macOS)** via ICS's own `Ics.Posix.*` message pump (on Linux use `HORSE_APPTYPE_DAEMON` + `THorseICSLinuxDaemonApp.Run`); a **Lazarus/FPC** port is not viable — ICS's POSIX layer rides the Delphi POSIX RTL and ICS compiles out OpenSSL under FPC. Its distinctive value is ICS's OpenSSL 3.x / 4.x stack (TLS 1.3, SNI, mTLS). See [horse-provider-ics](https://github.com/freitasjca/horse-provider-ics) for setup, the A–K test suite, and known limitations.

> **Delphi-nghttp2 installation** — `boss install github.com/freitasjca/horse-provider-nghttp2` pulls the provider and its [`Delphi-nghttp2`](https://github.com/freitasjca/Delphi-nghttp2) dependency automatically. At runtime, **libnghttp2 ≥ 1.59** must be present (dynamic-loaded — no link-time dependency): on Windows, download the prebuilt DLL from the [curl for Windows bundle](https://curl.se/windows/); on Linux, `sudo apt install libnghttp2-14`; on macOS, `brew install nghttp2`. For TLS and gRPC, OpenSSL 3.x or 1.1 is auto-detected. **FPC 3.2.2 and trunk 3.3.1 are both supported for the HTTP/2 transport; gRPC requires trunk 3.3.1** — build on 3.2.2 with `-dHORSE_NGHTTP2_NO_GRPC`. See [horse-provider-nghttp2](https://github.com/freitasjca/horse-provider-nghttp2) for the full setup guide, TLS/mTLS config, and gRPC samples.

> **HttpSys** — **no install**: the `Horse.Provider.HttpSys` unit ships with Horse and binds directly to Windows' `httpapi.dll` (http.sys), so there's no external library. Set `HORSE_PROVIDER_HTTPSYS` (Windows; Delphi or Lazarus). Because http.sys is a kernel-mode, machine-wide HTTP stack, binding a non-`localhost` host or a privileged port needs a one-time URL reservation (`netsh http add urlacl url=http://+:9000/ user=Everyone`) or Administrator rights; HTTPS uses the Windows certificate store via `netsh http add sslcert`. It is mutually exclusive with the CrossSocket / mORMot / ICS Providers (one transport per build).

> **IOCP** — **no install**: the `Horse.Provider.IOCP` unit ships with Horse and binds directly to Windows' input/output completion ports using Winsock2 API for extremely high performance and scalability on Windows self-hosted application types. Set `HORSE_PROVIDER_IOCP` (Windows; Delphi or Lazarus). It is mutually exclusive with Indy, HttpSys and other socket providers (one transport per build).
Expand Down
3 changes: 3 additions & 0 deletions README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,16 @@ Um _provider_ é o transporte HTTP que é dono do socket e entrega requisições
| 🆕 **[epoll](./doc/epoll.pt-BR.md)** _(event loop assíncrono nativo do Linux)_ | `HORSE_PROVIDER_EPOLL` |    ✔️ |     ✔️ |
| 🆕 **[horse-provider-ics](https://github.com/freitasjca/horse-provider-ics)** _(Delphi; Win + Linux/macOS)_ | `HORSE_PROVIDER_ICS` |    ✔️ |     ❌ |
| 🆕 **[IOCP](./doc/iocp.pt-BR.md)** _(portas de conclusão de E/S assíncronas nativas do Windows)_ | `HORSE_PROVIDER_IOCP` |    ✔️ |     ✔️ |
| 🆕 **[horse-provider-nghttp2](https://github.com/freitasjca/horse-provider-nghttp2)** _(HTTP/2 + gRPC; requer [Delphi-nghttp2](https://github.com/freitasjca/Delphi-nghttp2))_ | `HORSE_PROVIDER_NGHTTP2` |    ✔️ |     ✔️ |

> **Nota** — Os tipos de aplicação Apache / ISAPI / CGI / FastCGI (abaixo) **não** usam nenhum desses Providers. O processo host (Apache, IIS, o webserver) é dono do socket; o Horse roda in-process. Veja [Providers e Tipos de aplicação](./doc/providers.pt-BR.md) para o modelo completo.

> **Instalação do Delphi-Cross-Socket** — clone [`winddriver/Delphi-Cross-Socket`](https://github.com/winddriver/Delphi-Cross-Socket) (upstream) **junto com** [`cnpack/cnvcl/.../Crypto`](https://github.com/cnpack/cnvcl/tree/master/Source/Crypto) para as units CnPack/Crypto exigidas, e adicione os _search paths_ ao seu projeto. Três correções que antes eram exclusivas do fork já foram incorporadas ao upstream desde o 2º trimestre de 2026, então o mainline do upstream é adequado para uso geral. Para **mTLS** do lado servidor (`SSLVerifyPeer = True` + `SSLCACertFile = ...`) use o release pré-empacotado [`freitasjca/Delphi-Cross-Socket v1.0.3`](https://github.com/freitasjca/Delphi-Cross-Socket/releases/tag/v1.0.3) — um único clone, CnPack já incluso, APIs de mTLS (`SetCACertificateFile` + `SetVerifyPeer`) prontas para uso. Veja [Instalação do horse-provider-crosssocket](./doc/providers.pt-BR.md#crosssocket-opcional) para o detalhamento completo dos dois caminhos.

> **Instalação do OverbyteICS** — o Provider ICS requer o [OverbyteICS](https://wiki.overbyte.eu/wiki/index.php/ICS_Download) (v9.x). **Instale o ICS seguindo as instruções oficiais do ICS** — baixe/clone o ICS e adicione a pasta `Source/` ao _search path_ do seu projeto (o ICS não é instalável via Boss). Para TLS, as bibliotecas OpenSSL acompanham o ICS (DLLs no Windows, `.so` no Linux). O Provider ICS é **somente Delphi — Windows e POSIX (Linux64 / macOS)** via o pump de mensagens próprio do ICS (`Ics.Posix.*`) (no Linux use `HORSE_APPTYPE_DAEMON` + `THorseICSLinuxDaemonApp.Run`); um port para **Lazarus/FPC não é viável** — a camada POSIX do ICS usa a RTL POSIX do Delphi e o ICS desativa o OpenSSL no FPC. Seu diferencial é a pilha OpenSSL 3.x / 4.x do ICS (TLS 1.3, SNI, mTLS). Veja [horse-provider-ics](https://github.com/freitasjca/horse-provider-ics) para configuração, a suíte de testes A–K e limitações conhecidas.

> **Instalação do Delphi-nghttp2** — `boss install github.com/freitasjca/horse-provider-nghttp2` instala o provider e sua dependência [`Delphi-nghttp2`](https://github.com/freitasjca/Delphi-nghttp2) automaticamente. Em tempo de execução, a **libnghttp2 ≥ 1.59** precisa estar presente (carregada dinamicamente — sem dependência de link): no Windows, baixe a DLL pré-compilada do [pacote curl for Windows](https://curl.se/windows/); no Linux, `sudo apt install libnghttp2-14`; no macOS, `brew install nghttp2`. Para TLS e gRPC, o OpenSSL 3.x ou 1.1 é detectado automaticamente. **FPC 3.2.2 e trunk 3.3.1 são ambos suportados para o transporte HTTP/2; gRPC exige o trunk 3.3.1** — compile em 3.2.2 com `-dHORSE_NGHTTP2_NO_GRPC`. Veja [horse-provider-nghttp2](https://github.com/freitasjca/horse-provider-nghttp2) para o guia completo de configuração, TLS/mTLS e exemplos de gRPC.

> **HttpSys** — **sem instalação**: a unit `Horse.Provider.HttpSys` acompanha o Horse e usa diretamente a `httpapi.dll` do Windows (http.sys), portanto não há biblioteca externa. Defina `HORSE_PROVIDER_HTTPSYS` (Windows; Delphi ou Lazarus). Como o http.sys é uma pilha HTTP em modo kernel e de escopo da máquina, vincular um host diferente de `localhost` ou uma porta privilegiada exige uma reserva de URL única (`netsh http add urlacl url=http://+:9000/ user=Everyone`) ou direitos de Administrador; o HTTPS usa o repositório de certificados do Windows via `netsh http add sslcert`. É mutuamente exclusivo com os Providers CrossSocket / mORMot / ICS (um transporte por build).

> **IOCP** — **sem instalação**: a unit `Horse.Provider.IOCP` acompanha o Horse e se vincula diretamente às portas de conclusão de E/S (Input/Output Completion Ports) do Windows utilizando a API Winsock2 para altíssimo desempenho e escalabilidade em tipos de aplicação self-hosted no Windows. Defina `HORSE_PROVIDER_IOCP` (Windows; Delphi ou Lazarus). É mutuamente exclusivo com Indy, HttpSys e outros providers de socket (um transporte por build).
Expand Down
74 changes: 71 additions & 3 deletions src/Horse.pas
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
{$IFDEF HORSE_ISAPI} {$DEFINE HORSE_HOST_ISAPI} {$ENDIF}
{$IFDEF HORSE_CGI} {$DEFINE HORSE_HOST_CGI} {$ENDIF}
{$IFDEF HORSE_FCGI} {$DEFINE HORSE_HOST_FCGI} {$ENDIF}
{$IFDEF HORSE_NGHTTP2} {$DEFINE HORSE_PROVIDER_NGHTTP2} {$ENDIF}

{ ===========================================================================
PATCH-HORSE-1 — Architecturally-impossible combination guard (expanded)
Expand Down Expand Up @@ -183,6 +184,21 @@
{$IFEND}
{$IFEND}

{$IF DEFINED(HORSE_PROVIDER_NGHTTP2)}
{$IF DEFINED(HORSE_HOST_ISAPI)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 cannot combine with HORSE_HOST_ISAPI — IIS owns the socket; a self-hosted Provider cannot coexist.'}
{$ENDIF}
{$IF DEFINED(HORSE_HOST_APACHE)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 cannot combine with HORSE_HOST_APACHE — Apache owns the socket; a self-hosted Provider cannot coexist.'}
{$ENDIF}
{$IF DEFINED(HORSE_HOST_CGI)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 cannot combine with HORSE_HOST_CGI — the web server owns the socket; a self-hosted Provider cannot coexist.'}
{$ENDIF}
{$IF DEFINED(HORSE_HOST_FCGI)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 cannot combine with HORSE_HOST_FCGI — FastCGI talks to a web server; a self-hosted Provider cannot coexist.'}
{$ENDIF}
{$IFEND}

{ Rule 2 — cross-platform Application-type mismatch }
{$IF DEFINED(HORSE_APPTYPE_VCL) and DEFINED(FPC)}
{$MESSAGE FATAL 'HORSE_APPTYPE_VCL is Delphi-only — use HORSE_APPTYPE_LCL for Lazarus/FPC.'}
Expand All @@ -196,7 +212,7 @@

{ Rule 3 — HORSE_NOPROVIDER × anything else }
{$IF DEFINED(HORSE_NOPROVIDER)}
{$IF DEFINED(HORSE_PROVIDER_CROSSSOCKET) or DEFINED(HORSE_PROVIDER_MORMOT) or DEFINED(HORSE_PROVIDER_ICS) or DEFINED(HORSE_APPTYPE_VCL) or DEFINED(HORSE_APPTYPE_DAEMON) or DEFINED(HORSE_APPTYPE_LCL) or DEFINED(HORSE_HOST_APACHE) or DEFINED(HORSE_HOST_ISAPI) or DEFINED(HORSE_HOST_CGI) or DEFINED(HORSE_HOST_FCGI)}
{$IF DEFINED(HORSE_PROVIDER_CROSSSOCKET) or DEFINED(HORSE_PROVIDER_MORMOT) or DEFINED(HORSE_PROVIDER_ICS) or DEFINED(HORSE_PROVIDER_NGHTTP2) or DEFINED(HORSE_APPTYPE_VCL) or DEFINED(HORSE_APPTYPE_DAEMON) or DEFINED(HORSE_APPTYPE_LCL) or DEFINED(HORSE_HOST_APACHE) or DEFINED(HORSE_HOST_ISAPI) or DEFINED(HORSE_HOST_CGI) or DEFINED(HORSE_HOST_FCGI)}
{$MESSAGE FATAL 'HORSE_NOPROVIDER is mutually exclusive with all HORSE_PROVIDER_*, HORSE_APPTYPE_*, and HORSE_HOST_* defines — remove one.'}
{$IFEND}
{$IFEND}
Expand All @@ -211,10 +227,25 @@
{$IF DEFINED(HORSE_PROVIDER_MORMOT) and DEFINED(HORSE_PROVIDER_ICS)}
{$MESSAGE FATAL 'HORSE_PROVIDER_MORMOT and HORSE_PROVIDER_ICS are mutually exclusive — pick exactly one transport Provider per build.'}
{$IFEND}
{$IF DEFINED(HORSE_PROVIDER_HTTPSYS) and (DEFINED(HORSE_PROVIDER_CROSSSOCKET) or DEFINED(HORSE_PROVIDER_MORMOT))}
{$IF DEFINED(HORSE_PROVIDER_NGHTTP2) and DEFINED(HORSE_PROVIDER_CROSSSOCKET)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 and HORSE_PROVIDER_CROSSSOCKET are mutually exclusive — pick exactly one transport Provider per build.'}
{$IFEND}
{$IF DEFINED(HORSE_PROVIDER_NGHTTP2) and DEFINED(HORSE_PROVIDER_MORMOT)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 and HORSE_PROVIDER_MORMOT are mutually exclusive — pick exactly one transport Provider per build.'}
{$IFEND}
{$IF DEFINED(HORSE_PROVIDER_NGHTTP2) and DEFINED(HORSE_PROVIDER_ICS)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 and HORSE_PROVIDER_ICS are mutually exclusive — pick exactly one transport Provider per build.'}
{$IFEND}
{ HORSE_PROVIDER_IOCP is tested BEFORE HORSE_PROVIDER_NGHTTP2 in the selector
chains below, so without this guard defining both compiles cleanly and IOCP
wins silently — the build would use a transport the developer did not ask for. }
{$IF DEFINED(HORSE_PROVIDER_NGHTTP2) and DEFINED(HORSE_PROVIDER_IOCP)}
{$MESSAGE FATAL 'HORSE_PROVIDER_NGHTTP2 and HORSE_PROVIDER_IOCP are mutually exclusive — pick exactly one transport Provider per build.'}
{$IFEND}
{$IF DEFINED(HORSE_PROVIDER_HTTPSYS) and (DEFINED(HORSE_PROVIDER_CROSSSOCKET) or DEFINED(HORSE_PROVIDER_MORMOT) or DEFINED(HORSE_PROVIDER_NGHTTP2))}
{$MESSAGE FATAL 'HORSE_PROVIDER_HTTPSYS is mutually exclusive with other transport Providers — pick exactly one per build.'}
{$IFEND}
{$IF DEFINED(HORSE_PROVIDER_EPOLL) and (DEFINED(HORSE_PROVIDER_CROSSSOCKET) or DEFINED(HORSE_PROVIDER_MORMOT) or DEFINED(HORSE_PROVIDER_HTTPSYS))}
{$IF DEFINED(HORSE_PROVIDER_EPOLL) and (DEFINED(HORSE_PROVIDER_CROSSSOCKET) or DEFINED(HORSE_PROVIDER_MORMOT) or DEFINED(HORSE_PROVIDER_HTTPSYS) or DEFINED(HORSE_PROVIDER_NGHTTP2))}
{$MESSAGE FATAL 'HORSE_PROVIDER_EPOLL is mutually exclusive with other transport Providers — pick exactly one per build.'}
{$IFEND}
{ =========================================================================== }
Expand Down Expand Up @@ -266,6 +297,15 @@ interface
{$ELSE}
{$MESSAGE ERROR 'HORSE_PROVIDER_IOCP is only supported on Windows.'}
{$ENDIF}
{$ELSEIF DEFINED(HORSE_PROVIDER_NGHTTP2)}
{ FPC lifecycle shape selected by the application type. }
{$IF DEFINED(HORSE_APPTYPE_DAEMON)}
Horse.Provider.Nghttp2.FPC.Daemon,
{$ELSEIF DEFINED(HORSE_APPTYPE_LCL)}
Horse.Provider.Nghttp2.FPC.LCL,
{$ELSE}
Horse.Provider.Nghttp2.FPC.HTTPApplication,
{$ENDIF}
{$ELSEIF DEFINED(HORSE_APPTYPE_DAEMON)}
Horse.Provider.FPC.Daemon,
{$ELSEIF DEFINED(HORSE_APPTYPE_LCL)}
Expand Down Expand Up @@ -334,6 +374,15 @@ interface
{$ELSE}
Horse.Provider.ICS, { Console-shape — Delphi default for ICS }
{$ENDIF}
{$ELSEIF DEFINED(HORSE_PROVIDER_NGHTTP2)}
System.SysUtils,
{$IF DEFINED(HORSE_APPTYPE_VCL)}
Horse.Provider.Nghttp2.VCL, { Windows GUI host — auto-Listen from FormCreate }
{$ELSEIF DEFINED(HORSE_APPTYPE_DAEMON)}
Horse.Provider.Nghttp2.Daemon, { Windows Service (TService) or Linux daemon (SIGTERM handler) }
{$ELSE}
Horse.Provider.Nghttp2, { Console-shape — Delphi default for nghttp2 }
{$ENDIF}
{$ELSE}
System.SysUtils,
Horse.Provider.Console,
Expand Down Expand Up @@ -481,6 +530,25 @@ interface
{$ELSE}
THorseProvider = Horse.Provider.ICS.THorseProviderICS;
{$ENDIF}
{$ELSEIF DEFINED(HORSE_PROVIDER_NGHTTP2)}
THorseProvider =
{$IFDEF FPC}
{$IF DEFINED(HORSE_APPTYPE_DAEMON)}
Horse.Provider.Nghttp2.FPC.Daemon.THorseProviderNghttp2FPCDaemon;
{$ELSEIF DEFINED(HORSE_APPTYPE_LCL)}
Horse.Provider.Nghttp2.FPC.LCL.THorseProviderNghttp2FPCLCL;
{$ELSE}
Horse.Provider.Nghttp2.FPC.HTTPApplication.THorseProviderNghttp2FPCHTTPApplication;
{$ENDIF}
{$ELSE}
{$IF DEFINED(HORSE_APPTYPE_VCL)}
Horse.Provider.Nghttp2.VCL.THorseProviderNghttp2VCL;
{$ELSEIF DEFINED(HORSE_APPTYPE_DAEMON)}
Horse.Provider.Nghttp2.Daemon.THorseProviderNghttp2Daemon;
{$ELSE}
Horse.Provider.Nghttp2.THorseProviderNghttp2;
{$ENDIF}
{$ENDIF}
{$ELSEIF DEFINED(HORSE_APPTYPE_DAEMON)}
THorseProvider =
{$IF DEFINED(FPC)}
Expand Down