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) = @_;

        #...
    },
);

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

    #Only necessary for non-blocking I/O;
    #it’s meaningless in blocking I/O.
    #See below for an alternative pattern for use with POE, etc.
    if ( $ept->get_write_queue_size() ) {
        $ept->flush_write_queue();
    }

    #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();

    #INSTEAD OF flush_write_queue(), you might want to send the write
    #queue off to a multiplexing framework like POE, for which this
    #would be useful:
    while ( my $frame = $ept->shift_write_queue() ) {
        #… do something with $frame->to_bytes() -- probably send it
    }

    #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::Write 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_sr => \q<>,
            fin => $_[0]->get_fin(),
        );
    },

OBJ->get_next_message()

The “workhorse” method. It returns a data message if one is available and is the next frame; otherwise, it returns 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: Set the internal ping counter to zero. If the pong is unrecognized (i.e., we’ve not sent the payload in a ping), then we send a PROTOCOL_ERROR close frame.

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

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.

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.