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 )