Changes for version 0.002002 - 2026-07-15

  • PAGI::Middleware::Session: non-'http' scopes that carry a headers key (WebSocket, SSE upgrades) now get a READ-ONLY session populated the same way 'http' does, instead of being skipped; nothing is ever saved for them, and an unresolvable id yields an empty hashref rather than a missing key. See the new "NON-HTTP SCOPES" POD section.
  • PAGI::Middleware::CSRF: new 'enforce' config -- 'header' (default, unchanged: unsafe methods need a matching header token or a 403) or 'app' (issue-only: mint/stash the token for every method, never auto-reject, and let the app validate). 'app' exists because the middleware cannot read form params without buffering the body stream.
  • PAGI::Context: add csrf_token and csrf_verify (timing-safe token check). Both inherit into PAGI::Nano contexts.
  • REMOVED PAGI::Middleware::CSRF's 'token_param' config: it was dead (the form-field lookup was never implemented). Use enforce => 'app' with $ctx->csrf_verify for form tokens.
  • PAGI::Utils::SecureCompare: new module for the constant-time string compare shared by PAGI::Middleware::CSRF and PAGI::Context.
  • Docs: PAGI::Middleware::CSRF's USAGE section no longer contradicts itself (a JS getCookie() against an HttpOnly cookie); it now documents the header and form flows separately.
  • t/integration/sse-decline-end-to-end.t now skips unless PAGI::Server is >= 0.002005, the release that added the sse.http.response.* decline protocol it exercises. Fixes a CPAN Testers FAIL against 0.002001 (older servers answer an unmatched SSE route with 500 instead of 404).
  • PAGI::Middleware::Auth::Bearer: _secure_compare now rejects undef arguments instead of numifying them, so two undef (or undef vs empty) signatures no longer compare equal.
  • PAGI::Middleware::CSRF: new 'secure' config (default 0) to set the Secure attribute on the CSRF cookie; enable for HTTPS deployments.
  • PAGI::WebSocket / PAGI::SSE: a per-message callback that dies in the iteration helpers (each_message/each_text/each_bytes/each_json, SSE each() and every()) now runs on_close cleanup before re-raising, instead of bypassing it. WebSocket::run() is unchanged -- it isolates errors and keeps serving.
  • PAGI::Context::WebSocket / PAGI::Context::SSE: a terminal disconnect driven through $c->run now fires the underlying object's on_close (previously it only recorded the reason, so on_close never fired and is_closed/is_connected lied). Both also gain on_close() stubs pointing at $c->websocket->on_close / $c->sse->on_close.
  • PAGI::Test::Client: start() now drives the event loop during lifespan startup and dies on failure, instead of busy-waiting a 5s deadline without advancing the loop (which hung off-loop startup hooks and ignored lifespan.startup.failed).

Documentation

Recipes for Common PAGI Tasks
Optional convenience helpers for PAGI applications

Modules

Try apps in sequence until success
Serve files with directory listing
Serve static files
Health check endpoint app
Load PAGI app from file
Customizable 404 response
HTTP reverse proxy (DEMO ONLY - NOT FOR PRODUCTION)
URL redirect app for the dynamic case
Unified routing for HTTP, WebSocket, and SSE
Pub/sub Server-Sent Events
Rate-limited request processing
Mount apps at URL path prefixes
Pub/sub WebSocket broadcast
Multi-room chat application
Echo WebSocket messages back to sender
Execute CGI scripts as PAGI apps
PSGI-to-PAGI adapter
Per-request context with protocol-specific subclasses
HTTP-specific context subclass
SSE context with protocol operations
WebSocket context with protocol operations
Class-based HTTP endpoint handler
Class-based router with wrapped handlers
Class-based Server-Sent Events endpoint handler
Class-based WebSocket endpoint handler
ordered, case-insensitive, multi-value HTTP header container
Wrap a PAGI app with lifecycle management
Base class for PAGI middleware
Request logging middleware
HTTP Basic Authentication middleware
Bearer token authentication middleware
DSL for composing PAGI middleware
Cross-Origin Resource Sharing middleware
Cross-Site Request Forgery protection middleware
Conditional GET/HEAD request handling
Auto Content-Length header middleware
HTTP content negotiation middleware
Cookie parsing middleware
Development debug panel middleware
ETag generation middleware
Exception handling middleware
Form request body parsing middleware
Response compression middleware
Force HTTPS redirect middleware
HEAD request handling middleware
Health check endpoint middleware
JSON request body parsing middleware
Validate PAGI application compliance
Serve maintenance page when enabled
Override HTTP method from request data
Request rate limiting middleware
Unique request ID middleware
Handle X-Forwarded-* headers from reverse proxies
URL rewriting middleware
Request timing middleware
Add retry hints to SSE events
Security headers middleware
Session management middleware with pluggable State/Store
Base class for session state extraction
Bearer token session ID transport
Custom coderef-based session ID transport
Cookie-based session ID transport
Header-based session ID transport
Base class for async session storage
Static file serving middleware
Host header validation middleware
Rate limiting for WebSocket connections
Delegate file serving to reverse proxy
Convenience wrapper for PAGI request scope
Streaming body consumption for PAGI requests
Async multipart/form-data parser
Pull-based streaming multipart/form-data engine
Content negotiation utilities for PAGI
Uploaded file representation
Fluent response builder for PAGI applications
Convenience wrapper for PAGI Server-Sent Events connections
Standalone helper object for session data access
Standalone helper for per-request shared state
Test client for PAGI applications
the pagi.connection object provided by PAGI::Test
HTTP response wrapper for testing
Server-Sent Events connection for testing PAGI applications
WebSocket connection for testing PAGI applications
Application toolkit for the PAGI specification
Shared utility helpers for PAGI
Cryptographically secure random bytes
Constant-time string comparison
Convenience wrapper for PAGI WebSocket connections

Provides

in lib/PAGI/App/WrapPSGI.pm
in lib/PAGI/Endpoint/Router.pm
in lib/PAGI/Middleware/Cookie.pm
in lib/PAGI/Request/MultipartStream.pm
in lib/PAGI/Response.pm

Examples