NAME
PAGI - Perl Asynchronous Gateway Interface
DEDICATION
This project is dedicated to the memory of Matt S. Trout (mst), who I wish was still around to tell me all the things wrong with my code while simultaneously offering brilliant ideas to make it better.
Matt encouraged my first CPAN contribution. Without that encouragement, PAGI and pretty much everything I've released on CPAN over 20+ years would never have happened.
Thank you, Matt. The Perl community misses you.
SYNOPSIS
# Raw PAGI application
use Future::AsyncAwait;
async sub app {
my ($scope, $receive, $send) = @_;
die "Unsupported: $scope->{type}" if $scope->{type} ne 'http';
await $send->({
type => 'http.response.start',
status => 200,
headers => [['content-type', 'text/plain']],
});
await $send->({
type => 'http.response.body',
body => 'Hello from PAGI!',
more => 0,
});
}
DESCRIPTION
PAGI (Perl Asynchronous Gateway Interface) is a specification for asynchronous Perl web applications, designed as a spiritual successor to PSGI. It defines a standard interface between async-capable Perl web servers, frameworks, and applications, supporting HTTP/1.1, WebSocket, and Server-Sent Events (SSE).
This document presents a high level overview of PAGI. If you are a web developer who is looking to write PAGI compliant apps, you should also review the tutorial: PAGI::Tutorial. Coming from PSGI? See PAGI::PSGI. Building a framework on PAGI? See PAGI::Building.
Why PAGI?
PSGI models an application as a single, synchronous coderef that takes a request and returns a response. That model has served Perl well, but it cannot express long-lived connections such as long-poll HTTP or WebSockets: there is only one path in (the request) and one path out (the response). Even made non-blocking, a single request/response path cannot represent protocols that deliver multiple incoming events over the life of a connection, such as WebSocket frames.
PAGI keeps the simple "your application is a coderef" idea but makes it asynchronous and message-based. An application receives a $scope describing the connection and two async coderefs: $receive for events arriving from the client and $send for events going back. Both return Futures, so backpressure is explicit. This allows any number of incoming and outgoing events per connection, and leaves room for background work (for example, listening on an external trigger like a message queue) alongside the request/response flow.
PAGI is also a superset of PSGI: there is a defined translation between the two, so existing PSGI applications can run under a PAGI server through a PSGI adapter provided in the PAGI-Tools distribution.
Beta Software Notice
WARNING: This is beta software.
This distribution (PAGI) is the specification only: this module plus the PAGI::Spec documentation. The reference implementations live in separate distributions, each with its own stability level:
- Stable: PAGI Specification
-
The PAGI specification (
$scope,$receive,$sendinterface) is stable. Breaking changes will not be made except for critical security issues. Raw PAGI applications you write today will continue to work.See PAGI::Spec.
- Beta: PAGI-Server distribution
-
The reference server (PAGI::Server, validated against PAGI::Server::Compliance) handles HTTP/1.1, HTTP/2, WebSocket, and SSE correctly, but has not been battle-tested in production. Recommendation: run behind a reverse proxy like nginx, Apache, or Caddy. Rely only on the PAGI::Spec interface, not on the server's internals. Ships in the
PAGI-Serverdistribution. - Beta: PAGI-Tools distribution
-
The application toolkit (PAGI::Request, PAGI::Response, PAGI::WebSocket, PAGI::SSE, PAGI::Endpoint::Router, PAGI::App::Router, middleware, and bundled apps) is convenience built on top of the spec. These APIs may change between releases as the helpers evolve. Ships in the
PAGI-Toolsdistribution.
If you are interested in contributing to the future of async Perl web development, your feedback, bug reports, and contributions are welcome.
THE PAGI ECOSYSTEM
PAGI is split across three distributions so that applications can depend on the specification without pulling in a particular server or toolkit:
PAGI(this distribution)-
The specification: this module plus the PAGI::Spec documents (PAGI::Spec, PAGI::Spec::Www, PAGI::Spec::Lifespan, PAGI::Spec::Extensions, PAGI::Spec::Tls, PAGI::Spec::Server). The specification modules are pure documentation; during the transition from the combined distribution this distribution also pulls in
PAGI-ServerandPAGI-Tools(see "INSTALLATION AND BACKWARD COMPATIBILITY"). PAGI-Server-
The reference server (PAGI::Server): an IO::Async-based implementation supporting HTTP/1.1, HTTP/2, WebSocket, SSE, TLS, and multi-worker pre-forking, validated against PAGI::Server::Compliance. Provides the
pagi-serverCLI and PAGI::Server::Runner (the-s CLASSswappable-server runner behindpagi-server). Any server implementing the contract in PAGI::Spec::Server is a drop-in alternative. PAGI-Tools-
The application toolkit: the
PAGI::Middleware::*suite,PAGI::App::*ready-made apps, thePAGI::Endpoint::*framework, PAGI::Request/PAGI::Response/PAGI::Context ergonomics, and PAGI::Test::Client and friends for in-process testing.
This PAGI distribution is the canonical starting point for the ecosystem: the specification, the tutorial, a cookbook of worked recipes, a migration guide for people coming from PSGI, a guide for framework authors, and a set of raw-protocol example applications under examples/ (including a complete little web framework built on PAGI). The reference server lives in the PAGI-Server distribution and the application toolkit in PAGI-Tools. The project repository is https://github.com/jjn1056/pagi; its history holds the original combined distribution from before the split.
Beyond the core distributions, other projects build on PAGI -- for example Thunderhorse, an asynchronous web framework. See the project repository for an up-to-date list of conforming servers, frameworks, and tools.
INSTALLATION AND BACKWARD COMPATIBILITY
Before the split, the PAGI distribution bundled the reference server, the application toolkit, and the specification together, so cpanm PAGI (or a requires 'PAGI' line in a cpanfile) installed all of them.
To avoid breaking existing dependents, the PAGI distribution continues to pull in PAGI::Server (from the PAGI-Server distribution) and PAGI::Tools (from the PAGI-Tools distribution) as runtime dependencies during the transition. Installing PAGI therefore still gives you the server and the toolkit, exactly as before the split.
This is temporary. These convenience dependencies will be removed in a future release. If your code uses the reference server or the toolkit, please update your dependencies to require PAGI::Server and/or PAGI::Tools directly, and depend on PAGI only when you want the specification itself.
PAGI APPLICATION INTERFACE
PAGI applications are async coderefs with this signature:
async sub app {
my ($scope, $receive, $send) = @_;
... }
Parameters
$scope-
Hashref containing connection metadata including type, headers, path, method, query string, and server-advertised extensions.
$receive-
Async coderef that returns a Future resolving to the next event from the client (e.g., request body chunks, WebSocket messages).
$send-
Async coderef that takes an event hashref and returns a Future. Used to send responses back to the client.
Scope Types
Applications dispatch on $scope->{type}:
http-
HTTP request/response (one scope per request)
websocket-
Persistent WebSocket connection
sse-
Server-Sent Events stream
lifespan-
Process startup/shutdown lifecycle events
UTF-8 HANDLING OVERVIEW
PAGI scopes provide decoded text where mandated by the spec and preserve raw bytes where the application must decide. Broad guidance:
$scope->{path}is UTF-8 decoded from the percent-encoded$scope->{raw_path}. If UTF-8 decoding fails (invalid byte sequences), the original bytes are preserved as-is (Mojolicious-style fallback). If you need exact on-the-wire bytes, useraw_path.$scope->{query_string}and request bodies arrive as percent-encoded or raw bytes. Higher-level frameworks may auto-decode with replacement by default, but raw values remain available viaquery_stringand the body stream. If you need strict validation, decode yourself withEncodeandFB_CROAK.Response bodies and header values sent over the wire must be encoded to bytes. If you construct raw events, encode with
Encode::encode('UTF-8', $str, FB_CROAK)(or another charset you set in Content-Type) and setContent-Lengthbased on byte length.
Raw PAGI example with explicit UTF-8 handling:
use Future::AsyncAwait;
use Encode qw(encode decode);
async sub app {
my ($scope, $receive, $send) = @_;
# Handle lifespan if your server sends it; otherwise fail on unsupported types.
die "Unsupported type: $scope->{type}" unless $scope->{type} eq 'http';
# Decode query param manually (percent-decoded bytes)
my $text = '';
if ($scope->{query_string} =~ /text=([^&]+)/) {
my $bytes = $1; $bytes =~ s/%([0-9A-Fa-f]{2})/chr hex $1/eg;
$text = decode('UTF-8', $bytes, Encode::FB_DEFAULT); # replacement for invalid
}
my $body = "You sent: $text";
my $encoded = encode('UTF-8', $body, Encode::FB_CROAK);
await $send->({
type => 'http.response.start',
status => 200,
headers => [
['content-type', 'text/plain; charset=utf-8'],
['content-length', length($encoded)],
],
});
await $send->({
type => 'http.response.body',
body => $encoded,
more => 0,
});
}
QUICK START
This distribution is the specification. To write and run PAGI applications, install the reference server and toolkit:
# Reference server (provides the pagi-server CLI) and toolkit
cpanm PAGI::Server PAGI::Tools
# Start a PAGI app
pagi-server --app ./app.pl --port 5000
# Test it
curl http://localhost:5000/
See PAGI::Tutorial for a step-by-step guide to the protocol, and PAGI::Spec for the full specification.
REQUIREMENTS
The specification modules (PAGI.pm and the PAGI::Spec::* POD) are pure documentation and need only Perl 5.18+. During the transition the distribution additionally pulls in PAGI-Server and PAGI-Tools for backward compatibility (see "INSTALLATION AND BACKWARD COMPATIBILITY"); those distributions declare their own dependencies (the PAGI-Server distribution requires IO::Async and Future::AsyncAwait; the PAGI-Tools distribution requires Future::AsyncAwait).
SEE ALSO
- PAGI::Tutorial - A step-by-step guide to the protocol
- PAGI::Cookbook - Worked, runnable recipes for each protocol feature
- PAGI::PSGI - Coming to PAGI from PSGI
- PAGI::Building - Building frameworks and toolkits on PAGI
- PAGI::Spec - The full PAGI specification
- PAGI::Spec::Extensions - The server extension mechanism
- PAGI::Spec::Server - The server runner contract for swappable servers
- PAGI::Server - Reference server (
PAGI-Serverdistribution) - PAGI::Server::Runner - Application runner (
PAGI-Serverdistribution) - PSGI - The synchronous predecessor to PAGI
- Future::AsyncAwait - Async/await for Perl
CONTRIBUTING
This project is in active development. If you're interested in advancing async web programming in Perl, contributions are welcome:
Bug reports and feature requests
Documentation improvements
Test coverage
Protocol support (HTTP/2, HTTP/3)
Performance optimizations
AUTHOR
John Napiorkowski <jjnapiork@cpan.org>
LICENSE
This software is licensed under the same terms as Perl itself.