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, $send interface) 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-Server distribution.

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-Tools distribution.

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-Server and PAGI-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-server CLI and PAGI::Server::Runner (the -s CLASS swappable-server runner behind pagi-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, the PAGI::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, use raw_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 via query_string and the body stream. If you need strict validation, decode yourself with Encode and FB_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 set Content-Length based 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-Server distribution)
PAGI::Server::Runner - Application runner (PAGI-Server distribution)
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.