PAGI-Server
view release on metacpan or search on metacpan
lib/PAGI/Server/Compliance.pod view on Meta::CPAN
=encoding utf8
=head1 NAME
PAGI::Server::Compliance - HTTP/1.1, HTTP/2, WebSocket, and Security Compliance Documentation
=head1 DESCRIPTION
This document details the compliance testing results for PAGI::Server against
HTTP/1.1 (RFC 7230/7231), HTTP/2 (RFC 9113), WebSocket (RFC 6455), and common
security attack vectors.
PAGI::Server demonstrates full HTTP/1.1 compliance, 93.8% HTTP/2 conformance
(matching the nghttp2 library ceiling), strong security posture, stable resource
management, and 71% WebSocket RFC 6455 compliance.
=head1 TEST ENVIRONMENT
=over 4
=item * B<Server>: PAGI::Server with IO::Async and EV backend
=item * B<Platform>: macOS Darwin / Linux
=item * B<Event Loop>: EV with kqueue (macOS) or epoll (Linux)
=item * B<Test Date>: February 2026
=back
=head1 RESULTS SUMMARY
+---------------------------+-------+--------+--------+
| Category | Tests | Passed | Failed |
+---------------------------+-------+--------+--------+
| HTTP/1.1 Compliance | 10 | 10 | 0 |
| HTTP/2 (h2spec) | 146 | 137 | 9 |
| Slow HTTP Attacks | 4 | 4 | 0 |
| Concurrent Attack+Traffic | 1 | 1 | 0 |
| Request Smuggling | 6 | 6 | 0 |
| nikto Scanner | 4 | 4 | 0 |
| Protocol Fuzzing | 49 | 49 | 0 |
| Memory/Resource Leaks | 4 | 4 | 0 |
| WebSocket (Autobahn) | 301 | 215 | 86 |
+---------------------------+-------+--------+--------+
=head1 PAGI SPECIFICATION SUPPORT
Beyond protocol conformance, PAGI::Server implements the optional capabilities
defined in L<PAGI::Spec::Www>, across both HTTP/1.1 and HTTP/2:
=over 4
=item * B<Transport flow control> (C<pagi.transport>) -- C<buffered_amount>,
high/low watermarks, and C<on_high_water>/C<on_drain> backpressure callbacks. On
HTTP/1.1 for http/websocket/sse; on HTTP/2 for http streaming and sse. B<Not yet>
on WebSocket-over-HTTP/2 (see L</"Transport Flow Control (pagi.transport)">).
=item * B<Connection state> (C<pagi.connection>) for HTTP scopes --
C<is_connected>, C<disconnect_reason>, C<on_disconnect> (abnormal only),
C<on_complete> (success only), and C<disconnect_future>.
=item * B<WebSocket Denial Response> (the C<websocket.http.response> extension)
-- reject a handshake with a custom HTTP response instead of a bare C<403>, on
both HTTP/1.1 and HTTP/2.
=item * B<WebSocket over HTTP/2> (RFC 8441 Extended CONNECT).
=item * B<TLS introspection> (the C<tls> extension) -- certificates, negotiated
version, and cipher suite. The server negotiates TLS 1.3 by default
(C<min_version> is a floor, not a pin).
=item * B<Server-Sent Events> over all HTTP methods, with keepalive.
=back
In development mode the server validates outbound events against the spec and
rejects malformed ones.
=head1 HTTP/1.1 COMPLIANCE
PAGI::Server is fully compliant with RFC 7230 (HTTP/1.1 Message Syntax and Routing)
and RFC 7231 (HTTP/1.1 Semantics and Content).
=head2 Compliance Tests
+------------------------------+----------+--------+--------+
| Test | Expected | Actual | Status |
+------------------------------+----------+--------+--------+
| Normal request | 200 | 200 | PASS |
| HTTP/1.1 missing Host | 400 | 400 | PASS |
| HTTP/1.0 no Host | 200 | 200 | PASS |
| Content-Length: abc | 400 | 400 | PASS |
| Content-Length: -1 | 400 | 400 | PASS |
| Content-Length: overflow | 413 | 413 | PASS |
| Content-Length: with spaces | 400 | 400 | PASS |
| URI > 8KB | 414 | 414 | PASS |
| Header > 8KB | 431 | 431 | PASS |
| CL+TE conflict | 400 | 400 | PASS |
+------------------------------+----------+--------+--------+
=head2 RFC 7230 Section 5.4 - Host Header
HTTP/1.1 requests without a Host header correctly return 400 Bad Request.
HTTP/1.0 requests without Host are allowed per specification.
=head2 RFC 9112 Section 6.3.3 - Message Body Length
A request carrying both Transfer-Encoding and Content-Length is rejected with
400 Bad Request rather than resolved by precedence â the two are treated as
mutually exclusive. This is the stricter posture permitted by RFC 9112
Section 6.3.3 and closes the CL/TE desync that enables HTTP request smuggling.
=head1 HTTP/2 COMPLIANCE (EXPERIMENTAL)
B<HTTP/2 support in PAGI::Server is experimental.> The API and behavior may
lib/PAGI/Server/Compliance.pod view on Meta::CPAN
=head2 Load Testing (h2load)
Tool: h2load (nghttp2)
Protocol: h2 over TLS (ALPN negotiated)
+--------------+------+----------+-----------+---------+---------+
| Requests | Conn | Streams | Req/sec | Errors | Timeout |
+--------------+------+----------+-----------+---------+---------+
| 10,000 | 10 | 100 | 8,294 | 0 | 0 |
| 50,000 | 50 | 100 | 6,823 | 0 | 0 |
+--------------+------+----------+-----------+---------+---------+
Zero failures across 60,000 total requests under concurrent load.
=head2 nghttp Client Validation
Tool: nghttp (nghttp2 CLI client)
=over 4
=item * B<GET request>: Clean h2 exchange with ALPN, SETTINGS, HPACK compression
=item * B<POST with body>: DATA frame + END_STREAM handled correctly
=item * B<16 concurrent streams>: All responded 200 on a single connection in 3ms
=back
=head2 HTTP/2 Features
=over 4
=item * ALPN negotiation (h2, http/1.1)
=item * Stream multiplexing (100 concurrent streams default)
=item * HPACK header compression (93.7% space savings observed)
=item * Flow control (per-stream and connection-level)
=item * GOAWAY handling (graceful session shutdown)
=item * Stream state validation (END_STREAM tracking, RST_STREAM on violations)
=item * Extended CONNECT for WebSocket over HTTP/2 (RFC 8441)
=item * Server SETTINGS: configurable max_concurrent_streams, initial_window_size,
max_frame_size, max_header_list_size
=back
=head2 Transport Flow Control (pagi.transport)
PAGI exposes outbound flow-control introspection to applications through the
C<pagi.transport> scope key (see L<PAGI::Spec::Www/"Transport Flow Control">):
C<buffered_amount>, the high/low watermarks, and the
C<on_high_water>/C<on_drain> backpressure callbacks. Over HTTP/2 the handle
measures the B<per-stream> send backlog, so each multiplexed stream is bounded
independently.
PAGI::Server provides C<pagi.transport> for HTTP/2 C<http> (streaming responses)
and C<sse> scopes, matching its HTTP/1.1 coverage of C<http>, C<sse>, and
C<websocket>.
B<Known gap:> WebSocket over HTTP/2 (RFC 8441) does B<not> yet provide
C<pagi.transport>. The HTTP/2 WebSocket send path hands each frame to nghttp2
through its push-style C<submit_data> entry point, which keeps no per-stream
send queue for the handle to measure, and the binding exposes no per-stream
buffered-byte API. Closing the gap means converting the HTTP/2 WebSocket send
path to the same pull-based data-provider model that HTTP/2 streaming and SSE
use. Until then a WebSocket application running over HTTP/2 sees
C<pagi.transport> as absent: the L<PAGI::WebSocket> helpers degrade quietly
(C<buffered_amount> reports C<0>, C<is_writable> stays true, and the callbacks
are no-ops). WebSocket over HTTP/1.1 is unaffected and provides the handle
normally.
The long-term goal is parity: C<pagi.transport> for every streaming scope type
over every transport, so an application need not know whether it runs over
HTTP/1.1 or HTTP/2.
=head2 Enabling HTTP/2
# TLS mode (h2 via ALPN)
pagi-server --http2 --ssl-cert cert.pem --ssl-key key.pem --app myapp.pl
# Cleartext mode (h2c)
pagi-server --http2 --app myapp.pl
Requires L<Net::HTTP2::nghttp2> (XS bindings for the nghttp2 C library).
=head1 SECURITY TESTING
=head2 Slow HTTP Attack Resistance
PAGI::Server's async architecture provides natural resistance to slow HTTP attacks.
Unlike pre-fork servers (Apache, Starman), slow connections don't consume worker
processes.
=head3 Slowloris Attack (Slow Headers)
Test: 500 connections sending headers very slowly (10 second intervals)
Duration: 30 seconds
Result: Service remained fully available
Status: PASS
=head3 Slow POST Attack (Slow Body)
Test: 500 connections sending POST body very slowly
Duration: 30 seconds
Result: Server actively closed slow connections
Status: PASS
=head3 Slow Read Attack
Test: 500 connections reading responses very slowly (32 bytes per 5 seconds)
Duration: 30 seconds
Result: Service remained fully available
Status: PASS
=head3 Concurrent Attack + Normal Traffic
Test: Slowloris attack while serving normal traffic
Attack: 500 slow connections
Normal traffic results:
- Requests/sec: 3,843
- Total requests: 76,963
- Errors: 0
- p99 latency: 33ms
Status: PASS
=head2 HTTP Request Smuggling
PAGI::Server is B<NOT VULNERABLE> to HTTP Request Smuggling attacks.
=head3 CL.TE and TE.CL Attack Vectors
When a request presents both Transfer-Encoding and Content-Length, the two are
( run in 1.142 second using v1.01-cache-2.11-cpan-6aa56a78535 )