EV-Websockets

 view release on metacpan or  search on metacpan

README  view on Meta::CPAN


    "headers" is an optional hashref of headers to inject into the HTTP
    upgrade response (e.g., "Set-Cookie").

    "on_handshake" fires before the 101 response is sent (at
    "LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION"). It receives a hashref of
    request headers (same keys as "on_connect"). Return a hashref to inject
    per-connection response headers into the upgrade response. Return a
    false value ("undef", 0, "") to reject the connection (the client
    receives a 403).

   connections
    Returns a list of Connection objects whose state is "connected" or
    "closing" (i.e. the WebSocket handshake completed and the underlying wsi
    still exists). Conns still in "connecting" and conns already
    "closed"/"destroyed" are omitted.

        my @conns = $ctx->connections;
        $_->send("broadcast!") for @conns;

   adopt(%options)
    Adopt an existing IO handle (socket).

        my $conn = $ctx->adopt(
            fh               => $socket_handle,
            initial_data     => $already_read_bytes, # optional pre-read data
            max_message_size => 1048576,
            on_connect => sub { my ($conn, $headers) = @_; ... },
            on_message => sub { my ($conn, $data, $is_binary) = @_; ... },
            on_close   => sub { my ($conn, $code, $reason) = @_; ... },
            on_error   => sub { my ($conn, $err) = @_; ... },
            on_pong    => sub { my ($conn, $payload) = @_; ... },
            on_drain   => sub { my ($conn) = @_; ... },
        );

    Once adopted, "libwebsockets" takes ownership of the file descriptor.
    The module holds a reference to the Perl handle until the connection is
    destroyed, preventing premature fd closure. $headers in "on_connect" is
    always "undef" for adopted connections.

    If you already read data from the socket (e.g., the HTTP upgrade
    request), pass it via "initial_data" so lws can process the handshake.

  EV::Websockets::Connection
    Represents a WebSocket connection.

   send($data)
    Queue a text frame. Croaks if the connection is not open.

   send_binary($data)
    Queue a binary frame. Croaks if the connection is not open.

   send_ping([$payload])
    Queue a Ping frame. $payload is optional; if supplied it is silently
    truncated to 125 bytes per RFC 6455 §5.5. Croaks if the connection is
    not open.

   send_pong([$payload])
    Queue a Pong frame. Same payload rules as "send_ping". Most peers send
    Pong automatically in response to Ping; you only need this to send an
    unsolicited Pong (e.g. as a one-way keepalive).

   send_fragment($data, $is_binary = 0, $is_final = 1)
    Send one fragment of a streaming message. The first call starts a new
    fragmented message (text or binary per $is_binary); subsequent calls
    send continuation frames. Set $is_final true on the last fragment.

        $conn->send_fragment("part1", 0, 0);   # text, not final
        $conn->send_fragment("part2", 0, 0);   # continuation, not final
        $conn->send_fragment("part3", 0, 1);   # continuation, final

    Use this only if you need to interleave outbound writes with other I/O
    while streaming a single message. For ordinary sends, prefer
    "send"/"send_binary".

   send_queue_size
    Returns the number of payload bytes currently queued for sending
    (excludes WebSocket framing overhead). Useful for backpressure
    monitoring; pair with "on_drain" to gate further sends.

   stash
    Returns a hashref for storing arbitrary per-connection metadata. The
    hashref is lazily created on first access and lives until the connection
    is freed.

        $conn->stash->{user_id} = 42;
        my $uid = $conn->stash->{user_id};

   get_protocol
    Returns the negotiated "Sec-WebSocket-Protocol" value, or "undef" if no
    subprotocol was negotiated or the connection is closed.

   peer_address
    Returns the peer's IP address as a printable string (IPv4 dotted-quad or
    IPv6 colon notation, no brackets, no port), or "undef" if unavailable.

   close([$code = 1000], [$reason])
    Initiate a clean WebSocket close. Sends a Close frame with $code
    (default 1000, normal closure) and an optional UTF-8 $reason (truncated
    by lws to fit the frame). Pending sends are drained first, then the
    connection is torn down and "on_close" fires.

    This is a no-op (does not croak) if the connection is already closed,
    closing, or destroyed. It is also a no-op while the connection is still
    in the "connecting" state - calling close() before the handshake
    completes does not cancel the in-flight connect; use "connect_timeout"
    to bound the handshake instead.

   pause_recv
    Stop reading frames from this connection (TCP flow control). New
    incoming frames will back up in the kernel's socket buffer until
    "resume_recv" is called. Silently does nothing on a closed or destroyed
    connection.

   resume_recv
    Resume receiving after "pause_recv". Silently does nothing on a closed
    or destroyed connection.

   is_connected
    Returns true while "state" is "connected".

   is_connecting
    Returns true while "state" is "connecting". Returns false once the
    connection is established, closing, closed, or destroyed.

   state
    Returns the current state as one of:

    "connecting" - TCP/TLS handshake or HTTP upgrade in progress
    "connected" - open and ready to send/receive
    "closing" - close() has been called; pending sends still draining
    "closed" - the underlying wsi is gone but the Perl object is still alive
    "destroyed" - the C struct has been freed (further method calls will
    croak)

DEBUGGING
        EV::Websockets::_set_debug(1);

    Enables verbose debug output from both the module and libwebsockets. In
    tests, gate on $ENV{EV_WS_DEBUG}:

        EV::Websockets::_set_debug(1) if $ENV{EV_WS_DEBUG};

FEERSUM INTEGRATION
    Adopt WebSocket connections from a Feersum PSGI server via "psgix.io":

        use Feersum;
        use EV::Websockets;

        my $ctx = EV::Websockets::Context->new;
        my $feersum = Feersum->endjinn;
        $feersum->set_psgix_io(1);

        $feersum->psgi_request_handler(sub {
            my $env = shift;
            return [400,[],[]] unless ($env->{HTTP_UPGRADE}//'') =~ /websocket/i;

            my $io = $env->{'psgix.io'};

            # Reconstruct HTTP upgrade for lws
            my $path = $env->{REQUEST_URI} // '/';
            my $hdr = "GET $path HTTP/1.1\r\n";
            for (sort keys %$env) {
                next unless /^HTTP_(.+)/;
                (my $h=$1) =~ s/_/-/g;
                $hdr .= "$h: $env->{$_}\r\n";
            }
            $hdr .= "\r\n";

            $ctx->adopt(fh => $io, initial_data => $hdr,
                on_message => sub { $_[0]->send($_[1]) },  # echo
            );
            return;
        });

    See also "eg/feersum_native.pl" and "eg/feersum_psgi.pl" for full
    examples.

BENCHMARKS
    The "bench/" directory contains latency and throughput benchmarks.

        # Echo round-trip latency (native client + native server)
        perl bench/latency.pl

        # Throughput (messages/sec)
        perl bench/throughput.pl

        # Comparison with AnyEvent::WebSocket and Net::WebSocket::EVx
        perl bench/compare.pl

    Typical results on Linux (localhost, 1000 round-trips, 64-byte payload):



( run in 1.956 second using v1.01-cache-2.11-cpan-98e64b0badf )