The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

UniEvent::WebSocket::Server - Asynchronous event-loop based WebSocket server

SYNOPSIS

    my $server = UniEvent::WebSocket::Server->new({
        locations => [
            {host => "*", port => 80, reuse_port => 1, backlog => 1024},
            {host => "*", port => 443, reuse_port => 1, backlog => 1024, ssl_ctx => $ssl_ctx},
        ],
        max_frame_size   => 10000,
        max_message_size => 100000,
        deflate          => {
            compression_level     => 3,
            compression_threshold => 1000,
        },
    });
    
    $server->connection_callback(sub {
        my ($server, $client) = @_;
        $client->message_callback(sub {
            my ($client, $message) = @_;
            say $message->payload;
        })
        $client->peer_close_callback(sub {
            my ($client, $message) = @_;
            say $message->close_code;
            say $message->close_message;
        });
        
        $client->send_text("hello from server");
        
        push @client, $client;
    });
    $server->run;
    ...
    UE::Loop->default->run;
    
    
    # upgrade from different http server
    
    my $ws_server = UniEvent::WebSocket::Server->new(...);
    $ws_server->run;
    
    my $http_server = UniEvent::HTTP::Server->new(...);
    $http_server->run;
    
    $http_server->request_callback(sub {
        my $req = shift;
        if ($req->path eq "/websocket") {
            $ws_server->upgrade_connection($req);
            return;
        }
        
        ...
    });
    
    UE::Loop->default->run;

DESCRIPTION

UniEvent::WebSocket::Server can be run as standalone server. Also it can upgrade http requests from http server and manage the rest of lifetime of the connection.

If locations config parameter is specified, UE::WebSocket::Server will run its own UniEvent::HTTP server listening for the specified locations. It will automatically upgrade all websocket http requests for any URIs (it is possible to control this process). Also it can act as normal http server and serve http requets as UniEvent::HTTP.

If locations config parameter is not specified, UE::WebSocket::Server will not listen anything, will not run http server and will be idle and wait for upgrade_connection() calls. Therefore you can run your own http server and transfer connections for upgrade to websocket server.

There is no differences between standalone and non-standalone versions of websocket server other than that.

Websocket server should be created, then run() should be called and then you should run the corresponding event loop. I.e. run() will not block and you can also run a number of other servers and frameworks in the same loop at once.

METHODS

new(\%config, [$loop = default loop])

Create websocket server for a given config and UniEvent::Loop. This server will work when you run specified event loop.

See configure() method for details on what config supports.

configure(\%config)

Configures websocket server with new config. Can be called at any time, even if server is already serving a number of connections. New config will only apply for newly established connections.

If some location is absent in new config and there are active connections accepted from that location, they remain and will be served normally. Server will not listen for such location anymore.

config should be a hash reference with the following fields:

locations

Array of locations to listen and their params. If specified will run http server by itself. See UniEvent::HTTP::Server for description of this parameter (because http server implements listening).

everything that UniEvent::WebSocket::Connection's configure() supports
config params for http server (if locations are specified)

i.e. everything that UniEvent::HTTP::Server's configure() supports.

    $server->configure({
        locations => [{host => '127.0.0.1', port => '80', tcp_nodelay => 1}], # listen
        max_message_size => 100000, # websocket parameter
        max_headers_size => 8000,   # http parameter
    });

loop()

Returns UniEvent::Loop object in which the server runs.

run()

Starts websocket server. This function will not block and returns immediately. It just creates and registers a number of event handlers in the event loop.

stop([$close_code = UE::WebSocket::CLOSE_AWAY])

Send close message with $close_code to all peers, close all connections and stop the server immediately. Appropriate callbacks (close_event) will be called on each connection as if every connection is normally closed.

If you exit the process immediately after that, some messages in queues and even close messages may not get sent (because shutting down a connection is an asynchronous action). To ensure everything is sent okay, you may return to event loop after stop() and it should bail out of it's run() execution if or when there are no more active handles (other servers/frameworks/etc) remain in the event loop.

stop_listening()

Temporarily suspend listening for new connections / http requests. Will continue to server active connections. Only meaningful in standalone mode. See UniEvent::HTTP::Server stop_listening().

start_listening()

Resumes listening for new connections after stop_listening(). See UniEvent::HTTP::Server start_listening().

get_connection($id)

Returns UniEvent::WebSocket::ServerConnection object with id $id. Each connection in websocket server gets an unique id (uint64_t). It is accessible via $connection->id. See UniEvent::WebSocket::ServerConnection

listeners()

Returns server listeners (event loop handles for listening sockets) as arrayref of UniEvent::Stream objects. See UniEvent::HTTP::Server listeners(). They can be used for low-level tuning / overloading. Be careful :-)

connections()

Returns an iterator to all connections that the server has. Iterator is an object with the only method next() which will return next UniEvent::WebSocket::ServerConnection object or undef if there are no more. If server has zero connections, it will anyway return an iterator which will return undef on the first next() call.

sockaddr()

Returns socket address of the first listener as Net::SockAddr object. If there are no listeners (or not running / etc), will return error.

May return error

upgrade_connection($request)

Upgrades foreign http connection represented and requested by $request (for now, it must be only UniEvent::HTTP::ServerRequest object). Connection is removed from http server and added to websocket server. connection_callback will be called on websocket server as if it was normally connected to standalone websocket server.

http()

Returns underlying http server object as UniEvent::HTTP::Server. Only meaningful for standalone websocket servers (which have underlying http server).

This object may be used to add handlers for normal http requests or for restricting condition for connection upgrades (however it is easier to do via handshake_callback feature).

    my $ws = UE::WebSocket::Server->new({
        locations => [...],
    });

    $ws->http->request_callback(sub {
        my $req = shift;
        if ($req->path == "/index.html") {
            $req->respond({
                code => 200,
                body => "hello",
            });
        }
        elsif ($req->path == "/private/websocket") {
            if ($req->headers->{secret} eq "foobar") {
                $ws->upgrade_connection($req);
            } else {
                $req->respond({code => 400});
            }
        }
        
        # everything that hasn't been responed will be upgraded automatically (of course, only websocket-upgrade-requests)
    });

There is not much to document here because it's a standart UniEvent::HTTP::Server API. Read its docs for details.

handshake_callback([\&new_callback])

Gets or sets handshake callback. This callback is called when server receives a websocket http upgrade request and before handshake http response is sent. It is possible here to deny websocket upgrade even if handshake request params are okay.

The signature of callback is:

    my ($server, $connection, $request) = @_;

Where $server is the server object itself.

$connection is the UniEvent::WebSocket::ServerConnection object

$request is the received websocket upgrade request, Protocol::WebSocket::Fast::ConnectRequest object. All http and websocket properties if the request can be read from this object.

Custom successful or error handshake reponse can be sent from this callback overriding default behaviour.

    $server->handshake_callback(sub {
        my ($server, $conn, $req) = @_;
        return if $req->error; # default error response will be sent automatically
        
        if ($req->uri->path ne '/my/ws') {
            $conn->send_accept_error({
                code => 404,
                body => 'wrong uri',
            });
        }
        elsif (!check_auth($req->headers->{'My-Auth'})) {
            $conn->send_accept_error({
                code => 400,
                body => 'authorization required',
            });
        }
        else {
            $conn->send_accept_response({
                headers => {'My-Custom-Data' => $data},
            });
        }
    });

See UniEvent::WebSocket::ServerConnection's send_accept_response() and send_accept_error() for details.

To remove handshake_callback, call

    $server->handshake_callback(undef);

connection_callback($sub)

connection_event()

Callbacks set via these methods will be invoked when new websocket connection is accepted and handshake process is successfully completed (i.e. after handshake_callback).

Callback signature:

    my ($server, $connection, $request) = @_;
    

Where $server is the server object itself.

$connection is the UniEvent::WebSocket::ServerConnection object

$request is the received websocket upgrade request, Protocol::WebSocket::Fast::ConnectRequest object. All http and websocket properties if the request can be read from this object.

At this moment, handshake has been completed and no more http responses could be sent. If at this stage you don't like something, the only option is to close the websocket connection.

    $connection->close(CLOSE_BAD_REQUEST);

However it is better to do such checks in handshake_callback.

This is the best place to add listeners for messages to the connection.

Connection object is held by the server till the end, no need to remember it in your code.

See "EVENT CALLBACKS" in UniEvent for differences between _callback and _event versions of methods.

disconnection_callback($sub)

disconnection_event()

Callbacks set via these methods will be invoked when a connection is closed (if user calls close() method locally or in react to peer's close packet).

Callback signature:

    my ($server, $connection) = @_;
    

Where $server is the server object itself.

$connection is the UniEvent::WebSocket::ServerConnection object

At this moment, connection is fully removed from server and inaccessible via get_connection(). No communication should be done via this connection anymore.

See "EVENT CALLBACKS" in UniEvent for differences between _callback and _event versions of methods.