PAGI
view release on metacpan or search on metacpan
docs/specs/main.mkdn view on Meta::CPAN
*Note: All code examples use modern Perl with subroutine signatures and ****\`\`**** for clarity. This is a documentation preference and ****not**** a requirement of the PAGI specification.*
**Version**: 0.2 (Draft)
## Introduction
While work has been done to support asynchronous response handling in PSGI (the `psgi.nonblocking` environment key), support for protocols with multiple input events (such as WebSockets) remained hacks. The use of non-blocking or asynchronous handlin...
Considering the above and the fact that similar specifications have existed for several years now for other languages, the time has arrived to create a fully asynchronous webserver gateway interface specification for Perl.
Perl does not natively support Python's `async/await` pattern, but we can achieve similar functionality using **Future::AsyncAwait** which provides `async/await` syntax on top of `Future.pm`. Below is how we translate key ASGI components into Perl.
## Abstract
This document proposes a standard interface between network protocol servers (particularly web servers) and Perl applications, intended to support multiple common protocol styles (including HTTP/1.1, HTTP/2, and WebSocket).
This base specification defines the APIs by which servers interact with and run application code. Each supported protocol (such as HTTP) includes a sub-specification detailing how to encode and decode that protocol into structured messages.
## Rationale
PSGI has worked well as a standard interface for synchronous Perl web applications. However, its design is tied to the HTTP-style request/response cycle, and cannot support full-duplex protocols such as WebSockets.
PAGI preserves a simple application interface while introducing a fully asynchronous message-based abstraction, enabling data to be sent and received at any time during a connection's lifecycle.
It defines:
- A standardized interface for communication between server and application.
- A set of message formats per protocol.
The primary goal is to support WebSockets, HTTP/2, and Server-Sent Events (SSE) alongside HTTP/1.1, while maintaining compatibility with existing PSGI applications through a transitional adapter layer.
## Overview
PAGI consists of two main components:
- A *protocol server* which manages sockets and translates network events into connection-level messages.
- An *application* which is invoked once per connection and communicates via asynchronous message passing.
Applications are written as asynchronous subroutines using `Future::AsyncAwait`. They receive a `scope` describing the connection, and two coderefs, `$recv` and `$send`, which return Futures representing event input and output.
Unlike PSGI, PAGI applications persist for the entire connection lifecycle. They process incoming events from the server and emit outgoing events in response.
Two important concepts in PAGI:
- *Connection scope*: A hashref describing the connection.
- *Events*: Hashrefs representing messages sent/received during the connection.
## Specification Details
### Connection Scope
Each incoming connection causes the application to be invoked with a `scope` hashref. Its keys include:
- `type`: Protocol type, e.g., `http`, `websocket`
- `pagi`: A hashref with at least:
- `version => '0.2'`
- `spec_version => '0.2'` (optional; protocol-specific override if it diverges)
- `features` (optional): a hashref of server-reported capabilities such as:
- `supports_streaming` => 1 if streaming responses are supported for this scope
- `max_request_body_size` => maximum accepted request body size (in bytes)
- `max_concurrent_streams` => maximum streams per HTTP/2/HTTP/3 connection
- `supports_trailers` => 1 if trailer frames are accepted for the response
Clarification on PAGI version keys:
- `version`: Signals the core PAGI specification version that governs scopes/events. This value **must** be a string.
- `spec_version`: Indicates the version for the specific protocol (such as HTTP or WebSocket). Servers may omit it, in which case clients should assume it matches `version`.
- `features`: Server-defined key/value pairs that describe optional behaviors for the connection. Values should use the permitted data types from this specification.
- Additional keys may be defined per protocol specification (e.g., HTTP, WebSocket) and must be documented there.
The scope describes the full lifetime of the connection, and persists until the connection is closed. Some protocols (e.g., HTTP) may treat each request as a distinct scope, while others (e.g., WebSocket) maintain one persistent scope for the entire ...
Specifically:
- HTTP treats each incoming request (for example, GET or POST requests) as a separate scope that exists only for the life of that request-response cycle. After the response is sent fully, the scope is discarded.
- WebSocket connection establishes a single scope at connection initiation, persisting for the full duration of the continuous message exchanges until the WebSocket connection closes.
Applications may need to wait for an initial event before sending any messages, depending on the protocol specification.
If the application does not recognize or support `scope->{type}`, it MUST throw an exception. The PAGI server must handle such exceptions gracefully, by closing the connection promptly, optionally logging the issue and providing relevant protocol-spe...
This explicit requirement ensures that servers never assume application-protocol compatibility without explicit support declared by implementations.
### Events
PAGI defines communication in terms of discrete events.
The `type` key in each event must be a namespaced string of the form `protocol.message_type`, such as `http.request` or `websocket.send`. This convention ensures clear protocol dispatching and avoids naming collisions.
Reserved `type` prefixes include:
- `http` -- standard HTTP events
- `websocket` -- WebSocket events
- `lifespan` -- process lifecycle events
- `pagi` -- reserved for future PAGI-defined system events
- `ext.` -- reserved for experimental or nonstandard events
Custom user-defined protocols should avoid clashing with these prefixes.
```perl
my $event = await $recv->();
await $send->({ type => 'http.response.start', ... });
```
#### Permitted Event Data Types
The following types are permitted in event data structures:
- **Strings** (Perl scalars):
- **Text strings**: Strings containing decoded Unicode characters. These must be explicitly encoded according to the relevant protocol before serialization or transmission.
- **Byte strings**: Strings containing opaque binary data. Their intended encoding or interpretation, if applicable, must be explicitly specified by the relevant protocol.
*Note*: Do **not** rely on Perl's internal UTF-8 flag (`utf8::is_utf8`) to distinguish between text and binary strings. The protocol or application context must explicitly indicate the intent.
- **Integers**: Signed 64-bit integers. While Perl supports arbitrary-size integers, PAGI restricts integers to this range to ensure interoperability across serialization formats and programming languages.
Integers commonly stand in for boolean values (`0` or `1`). When a protocol flag is described as boolean it **must** still use integers (never custom barewords) so transports remain consistent.
- **Floating-point numbers**: IEEE 754 double-precision floats. Special values (`NaN`, `Inf`, `-Inf`) are **not permitted**, due to inconsistent cross-platform and serialization support.
( run in 0.489 second using v1.01-cache-2.11-cpan-140bd7fdf52 )