NAME

Punk::WebSocket - a WebSocket connection

SYNOPSIS

# in the app
websocket '/chat' => 'WS::Chat#join';

# MyApp::Controller::WS::Chat
sub join {
    my ($c, $ws) = @_;               # after a validated upgrade

    $ws->on(open => sub {
        my ($ws) = @_;
        Punk::WebSocket::Room->named('lobby')->join($ws);
    });
    $ws->on(message => sub {
        my ($ws, $text) = @_;
        Punk::WebSocket::Room->named('lobby')->broadcast($text);
    });
    $ws->on(close => sub {
        my ($ws, $code, $reason) = @_;
        Punk::WebSocket::Room->named('lobby')->leave($ws);
    });
}

DESCRIPTION

One WebSocket connection. The handler for a websocket route is called with the Punk::Context and this object once the upgrade handshake has been validated and answered; it wires the events it cares about and returns. Reading, framing, ping/pong and the closing handshake then run on the worker's event loop with no further Perl involvement until a message completes.

Frames are decoded in C to RFC 6455, strictly: unmasked client frames, fragmented or over-long control frames and reserved opcodes are protocol errors (close 1002), text payloads and close reasons must be valid UTF-8 (1007), and a message over max_message_size is refused from the frame header before it is buffered (1009).

EVENTS

$ws->on($event => sub { ... }), one handler per event; an unknown event name croaks. Every handler receives the connection first.

  • open ($ws) - the handshake is complete.

  • message ($ws, $text) - a complete text message, character-decoded.

  • binary ($ws, $bytes) - a complete binary message.

  • ping ($ws, $payload) / pong ($ws, $payload) - a pong is sent automatically before the ping event fires.

  • close ($ws, $code, $reason) - fires exactly once, for a clean close or a dropped connection (code 1006).

  • error ($ws, $message) - a handler died, or the codec rejected the peer's framing.

A handler that dies does not take the worker down: the error event fires and the connection is closed with 1011.

METHODS

send($text)

send_binary($bytes)

Queue a message. Writes are buffered and drained on the loop, so a slow consumer never blocks the worker (past write_buffer_limit the connection is closed with 1008).

ping($payload?)

pong($payload?)

close($code = 1000, $reason = '')

Start the closing handshake; the close event fires when it completes or times out.

state / is_open / is_closing / is_closed

protocol

The negotiated subprotocol, or undef.

fd

The underlying file descriptor.

SEE ALSO

Punk, Punk::WebSocket::Room, "detach" in Hyperman.

AUTHOR

LNATION <email@lnation.org>

LICENSE AND COPYRIGHT

This software is Copyright (c) 2026 by LNATION <email@lnation.org>.

This is free software, licensed under:

The Artistic License 2.0 (GPL Compatible)