NAME

Net::WebSocket::Endpoint::Server

SYNOPSIS

my $ept = Net::WebSocket::Endpoint::Server->new(
    parser => $parser_obj,

    out => $out_fh,

    #optional, # of pings to send before we send a close
    max_pings => 5,

    #optional
    on_data_frame => sub {
        my ($frame_obj) = @_;

        #...
    },
);

my $msg_to_send = $ept->create_message('text', 'Hello!');

if ( _we_timed_out_waiting_for_read_readiness() ) {
    $ept->check_heartbeat();
}
else {

    #This should only be called when reading won’t produce an error.
    #For example, in non-blocking I/O you’ll need a select() in front
    #of this. (Blocking I/O can just call it and wait!)
    $ept->get_next_message();

    #Check for this at the end of each cycle.
    _custom_logic_to_finish_up() if $ept->is_closed();
}

DESCRIPTION

This module, like its twin, Net::WebSocket::Endpoint::Client, attempts to wrap up “obvious” bits of a WebSocket endpoint’s workflow into a reusable component.

The basic workflow is shown in the SYNOPSIS; descriptions of the individual methods follow:

METHODS

CLASS->new( %OPTS )

Instantiate the class. Nothing is actually done here. Options are:

  • parser (required) - An instance of Net::WebSocket::Parser.

  • out (required) - The endpoint’s output object. An instance of IO::Framed or a compatible class.

  • max_pings (optional) - The maximum # of pings to send before we send a close frame (which ends the session).

  • on_data_frame (optional) - A callback that receives every data frame that get_next_message() receives. Use this to facilitate chunking.

    If you want to avoid buffering a large message, you can do this:

    on_data_frame => sub {
        #... however you’re going to handle this chunk
    
        $_[0] = (ref $_[0])->new(
            payload => q<>,
            fin => $_[0]->get_fin(),
        );
    },

OBJ->get_next_message()

The “workhorse” method. It returns one of the following:

  • a data message if one is available

  • empty string if the Parser’s get_next_frame() indicated end-of-file without an exception

  • otherwise, undef

This method also handles control frames that arrive before or among message frames:

  • close: Respond (immediately) with the identical close frame. See below for more information.

  • ping: Send the appropriate pong frame.

  • pong: As per the protocol specification.

This method may not be called after a close frame has been sent (i.e., if the is_closed() method returns true).

OBJ->create_message( FRAME_TYPE, PAYLOAD )

Creates an unfragmented message with the given PAYLOAD.

FRAME_TYPE can be either text or binary (for Net::WebSocket’s default frame classes) or full package names (e.g., to use a custom frame class).

This is a convenience method that takes care of mask creation and module loading for you.

See Net::WebSocket::PMCE::deflate::Data for similar functionality to create a compressed message.

OBJ->check_heartbeat()

Ordinarily, sends a distinct ping frame to the remote server and increments the ping counter. Once a sent ping is received back (i.e., a pong), the ping counter gets reset.

If the internal ping counter has already reached max_pings, then we send a PROTOCOL_ERROR close frame. Further I/O attempts on this object will prompt an appropriate exception to be thrown.

OBJ->sent_close_frame()

Returns a Net::WebSocket::Frame::close object or undef to represent the frame that the object has sent, either via the close() method directly or automatically via the internal handling of control messages.

OBJ->received_close_frame()

Returns a Net::WebSocket::Frame::close object or undef to represent the frame that the object has received.

OBJ->is_closed()

DEPRECATED: Returns 1 or 0 to indicate whether we have sent a close frame. Note that sent_close_frame() provides a more useful variant of the same functionality; there is no good reason to use this method anymore.

OBJ->do_not_die_on_close()

Ordinarily, receipt of a close frame prompts an exception after the response close frame is sent. This is, arguably, a suboptimal design choice since receipt of a close frame is a perfectly normal thing to happen; i.e., it’s not “exception-al”. If you want to check for close yourself instead, you can do so by calling this method.

OBJ->die_on_close()

The inverse of do_not_die_on_close(): restores the default behavior when a close frame is received.

WHEN A CLOSE FRAME IS RECEIVED

get_next_message() will automatically send a close frame in response when it receives one. The received close frame is not returned to the application but, like ping and pong, is handled transparently.

Rationale: WebSocket is often billed as “TCP for the web”; however, the protocol curiously diverges from TCP in not supporting “half-close”; a WebSocket connection is either fully open (i.e., bidirectional) or fully closed. (There is some leeway given for finishing up an in-progress message, but this is a much more limited concept.)

EXTENSIONS

This module has several controls for supporting WebSocket extensions:

  • get_next_message()’s returned messages will always contain a get_frames() method, which you can use to read the reserved bits of the individual data frames.

  • You can create on_* methods on a subclass of this module to handle different types of control frames. (e.g., on_foo(FRAME)) to handle frames of type foo.) The parser object that you pass to the constructor has to be aware of such messages; for more details, see the documentation for Net::WebSocket::Parser.