Summary
On the HTTP.sys engine Request.RemoteIpAddress is always empty, RemotePort is always 0, and LocalPort is always 80 (also on 443). Your own rate limiter and forwarded-headers middleware depend on that address, and both stop doing their job without any error.
Where
Sources/Server/Dext.Server.HttpSys.pas, TDextHttpSysConnection.Init:
FLocalPort := 80;
FRemotePort := 0;
FRemoteAddress := '';
Nothing assigns them afterwards. The data is in the request: HTTP_REQUEST.Address carries pRemoteAddress and pLocalAddress (SOCKADDR, AF_INET or AF_INET6). But Dext.Server.HttpSys.Api declares them as PNetAddr = Pointer; // Placeholder, and nothing reads them.
TDextNativeHttpContext then passes AConnection.RemoteAddress to TDextNativeHttpRequest, so the empty string reaches IHttpRequest.RemoteIpAddress.
Consequences
Dext.RateLimiting with psIpAddress: every client lands in the same '' partition. One busy client uses up the limit for everybody, and the limit no longer protects against any single client.
Dext.Web.ForwardedHeaders: IsProxyTrusted('') is never true. Behind a reverse proxy (IIS/ARR, the usual setup in front of HTTP.sys), X-Forwarded-For is therefore always ignored.
- Anything that logs or audits the client address records nothing.
We noticed it through our own login rate limit, which counts per IP. On Indy it works; on HTTP.sys every client counted as the same one.
Suggested fix
In Init, read the two sockaddrs:
- address from
sin_addr or sin6_addr, formatted with InetNtopW or RtlIpv4AddressToStringW/RtlIpv6AddressToStringW;
- port from
sin_port/sin6_port, converted with ntohs.
It may be cleaner to type HTTP_TRANSPORT_ADDRESS properly in the Api unit first. We are happy to send a PR in whatever shape you prefer.
Checked against f440e79a.
🤖 Generated with Claude Code
Summary
On the HTTP.sys engine
Request.RemoteIpAddressis always empty,RemotePortis always0, andLocalPortis always80(also on 443). Your own rate limiter and forwarded-headers middleware depend on that address, and both stop doing their job without any error.Where
Sources/Server/Dext.Server.HttpSys.pas,TDextHttpSysConnection.Init:Nothing assigns them afterwards. The data is in the request:
HTTP_REQUEST.AddresscarriespRemoteAddressandpLocalAddress(SOCKADDR,AF_INETorAF_INET6). ButDext.Server.HttpSys.Apideclares them asPNetAddr = Pointer; // Placeholder, and nothing reads them.TDextNativeHttpContextthen passesAConnection.RemoteAddresstoTDextNativeHttpRequest, so the empty string reachesIHttpRequest.RemoteIpAddress.Consequences
Dext.RateLimitingwithpsIpAddress: every client lands in the same''partition. One busy client uses up the limit for everybody, and the limit no longer protects against any single client.Dext.Web.ForwardedHeaders:IsProxyTrusted('')is never true. Behind a reverse proxy (IIS/ARR, the usual setup in front of HTTP.sys),X-Forwarded-Foris therefore always ignored.We noticed it through our own login rate limit, which counts per IP. On Indy it works; on HTTP.sys every client counted as the same one.
Suggested fix
In
Init, read the two sockaddrs:sin_addrorsin6_addr, formatted withInetNtopWorRtlIpv4AddressToStringW/RtlIpv6AddressToStringW;sin_port/sin6_port, converted withntohs.It may be cleaner to type
HTTP_TRANSPORT_ADDRESSproperly in the Api unit first. We are happy to send a PR in whatever shape you prefer.Checked against
f440e79a.🤖 Generated with Claude Code