diff --git a/README.md b/README.md index 3928e1d..9451a7a 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ 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. @@ -123,6 +124,8 @@ A _provider_ is the HTTP transport that owns the socket and hands requests to yo > **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). diff --git a/README.pt-BR.md b/README.pt-BR.md index 4fa8986..b504776 100644 --- a/README.pt-BR.md +++ b/README.pt-BR.md @@ -116,6 +116,7 @@ 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. @@ -123,6 +124,8 @@ Um _provider_ Ă© o transporte HTTP que Ă© dono do socket e entrega requisições > **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). diff --git a/src/Horse.pas b/src/Horse.pas index 1814648..1b5b070 100644 --- a/src/Horse.pas +++ b/src/Horse.pas @@ -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) @@ -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.'} @@ -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} @@ -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} { =========================================================================== } @@ -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)} @@ -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, @@ -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)}