NAME

PAGI::Context::WebSocket - WebSocket context with protocol operations

SYNOPSIS

use PAGI::Context;
use Future::AsyncAwait;

# Simple echo — $ctx is all you need
async sub echo_handler {
    my ($scope, $receive, $send) = @_;

    my $ctx = PAGI::Context->new($scope, $receive, $send);
    await $ctx->accept;

    await $ctx->each_text(async sub {
        my ($text) = @_;
        await $ctx->send_text("Echo: $text");
    });
}

# Event-dispatched chat with channels
async sub chat_handler {
    my ($scope, $receive, $send) = @_;

    my $ctx = PAGI::Context->new($scope, $receive, $send);

    my $room = $ctx->path_param('room', strict => 0) // 'general';
    my $user = $ctx->query('user') // 'anonymous';

    await $ctx->accept;

    my $reason = await $ctx
        ->on('websocket.receive', async sub {
            my ($ctx, $event) = @_;
            # handle incoming message
        })
        ->on('chat.message', async sub {
            my ($ctx, $event) = @_;
            await $ctx->send_json($event);
        })
        ->on_error(sub {
            my ($ctx, $err, $source) = @_;
            warn "[$source] $err";
        })
        ->run;
}

DESCRIPTION

Returned by PAGI::Context->new(...) when $scope->{type} is 'websocket'. Inherits all shared methods from PAGI::Context (scope accessors, stash, session, event dispatcher) and adds WebSocket protocol operations delegated to PAGI::WebSocket.

This means handler code can use a single $ctx object for everything: dispatching events with on()/run(), sending messages with send_json(), reading query params with query(), and so on.

Underlying object access

my $ws = $ctx->websocket;   # or $ctx->ws

The underlying PAGI::WebSocket object is still available if you need direct access. In most cases you should not need it.

Methods NOT delegated

The following PAGI::WebSocket methods are not available on $ctx. They are intentionally omitted because PAGI::Context has its own versions with different semantics:

on() — On $ctx, this is the generic event dispatcher ("on" in PAGI::Context) that accepts any event type string. On PAGI::WebSocket, it is a Socket.IO-style dispatcher limited to message, close, error.
on_error() — On $ctx, callbacks receive ($ctx, $error, $source). On PAGI::WebSocket, they receive ($error).
on_close(), on_message()PAGI::WebSocket-specific callback registration. Use $ctx->on('websocket.disconnect', ...) and $ctx->on('websocket.receive', ...) instead.
run() — On $ctx, this is the generic event dispatch loop ("run" in PAGI::Context). On PAGI::WebSocket, it only dispatches on_message callbacks.
state() — On $ctx, returns $scope->{state} (app-level shared state from lifespan). On PAGI::WebSocket, same method exists but could diverge in meaning.

CONNECTION LIFECYCLE

accept

await $ctx->accept;
await $ctx->accept(subprotocol => 'chat');
await $ctx->accept(headers => [['x-custom', 'value']]);

Accepts the WebSocket connection. Optionally specify a subprotocol and additional response headers.

close

await $ctx->close;
await $ctx->close(4000, 'Custom reason');

Closes the connection. Default code is 1000 (normal closure). Idempotent — calling multiple times only sends close once.

SEND METHODS

send_text, send_bytes, send_json

await $ctx->send_text("Hello!");
await $ctx->send_bytes("\x00\x01\x02");
await $ctx->send_json({ action => 'greet', name => 'Alice' });

Send a message. Dies if connection is closed.

try_send_text, try_send_bytes, try_send_json

my $sent = await $ctx->try_send_json($data);

Returns true if sent, false if failed or closed. Does not throw. Useful for broadcasting to multiple clients.

send_text_if_connected, send_bytes_if_connected, send_json_if_connected

await $ctx->send_json_if_connected($data);

Silent no-op if connection is closed. Useful for fire-and-forget.

RECEIVE METHODS

receive_text, receive_bytes, receive_json

my $text  = await $ctx->receive_text;
my $bytes = await $ctx->receive_bytes;
my $data  = await $ctx->receive_json;

Wait for a specific frame type, skipping others. Returns undef on disconnect. receive_json dies on invalid JSON.

ITERATION HELPERS

each_message, each_text, each_bytes, each_json

await $ctx->each_text(async sub {
    my ($text) = @_;
    await $ctx->send_text("Got: $text");
});

Loop until disconnect, calling callback for each message.

STATE INSPECTION

is_connected

if ($ctx->is_connected) { ... }

Overrides the base class method. Returns true if the WebSocket handshake is complete and the connection is not yet closed. The base PAGI::Context version checks the TCP-level pagi.connection object; this version checks WebSocket protocol state, which is what handler code actually cares about.

is_closed

if ($ctx->is_closed) { ... }

True after close or disconnect.

close_code, close_reason

my $code   = $ctx->close_code;     # 1000, 4000, etc.
my $reason = $ctx->close_reason;   # 'Normal closure'

Available after connection closes.

subprotocols

my $protos = $ctx->subprotocols;   # ['chat', 'json']

Requested subprotocols from the client.

QUERY PARAMETERS

query

my $value = $ctx->query('user');
my $value = $ctx->query('page', strict => 1);

Returns a single query parameter value. Accepts strict and raw options.

query_params

my $params = $ctx->query_params;   # Hash::MultiValue

All query parameters as Hash::MultiValue.

raw_query, raw_query_params

my $val    = $ctx->raw_query('name');
my $params = $ctx->raw_query_params;

Skip UTF-8 decoding, return raw bytes after URL decoding.

HEADER EXTRAS

header_all

my @cookies = $ctx->header_all('cookie');

All values for a multi-value header. The base class header() method returns only the last value; header_all returns all of them.

http_version

my $ver = $ctx->http_version;   # '1.1' or '2'

KEEPALIVE

keepalive

await $ctx->keepalive(30);        # Ping every 30 seconds
await $ctx->keepalive(30, 20);    # Ping every 30s, pong timeout 20s

Enables WebSocket protocol-level ping/pong keepalive.

SEE ALSO

PAGI::Context, PAGI::WebSocket, PAGI::Context::SSE, PAGI::Context::HTTP

1 POD Error

The following errors were encountered while parsing the POD:

Around line 96:

Non-ASCII character seen before =encoding in '—'. Assuming UTF-8