NAME
Unblock::WebSocket::Handshake - WebSocket handshake helpers using Uniform::HTTP
SYNOPSIS
The opening handshake happens over HTTP, before any WebSocket messages.
Client:
use Unblock::WebSocket::Handshake;
use Unblock::WebSocket::Client;
my ($handshake, $request) =
Unblock::WebSocket::Handshake->client_request(
'wss://example.com/chat',
http_version => '1.1',
);
# Your HTTP client sends $request and receives $response.
$handshake->validate_client_response($response);
my $ws = Unblock::WebSocket::Client->new(
%{ $handshake->connection_options },
on_message => sub { ... },
);
Server:
use Unblock::WebSocket::Handshake;
use Unblock::WebSocket::Server;
my $handshake =
Unblock::WebSocket::Handshake->server_accept($request);
my $response = $handshake->server_response;
# Your HTTP server sends $response and switches the connection.
my $ws = Unblock::WebSocket::Server->new(
%{ $handshake->connection_options },
on_message => sub { ... },
);
These are the WebSocket-specific steps, not a complete HTTP client or server. See examples/handshake-memory.pl for an executable in-memory handshake and message exchange.
DESCRIPTION
This module owns WebSocket-specific HTTP handshake rules.
It does not send HTTP and it does not own the HTTP connection. Instead it creates and validates Uniform::HTTP request and response objects.
An invalid handshake throws an exception. The surrounding HTTP application decides how to send an HTTP error response or close the connection.
Three opening handshake forms are supported:
HTTP/1.1 RFC 6455 Upgrade
HTTP/2 Extended CONNECT
HTTP/3 Extended CONNECT
CLIENT
Create a request:
my ($handshake, $request) =
Unblock::WebSocket::Handshake->client_request(
'wss://example.com/chat',
http_version => '1.1',
);
Send $request through the HTTP implementation of your choice. The default HTTP version is 1.1. The URL scheme must be ws or wss; the helper does not open the destination.
After receiving the HTTP response:
$handshake->validate_client_response($response);
Then create the established protocol engine:
my $ws = Unblock::WebSocket::Client->new(
%{ $handshake->connection_options },
on_message => sub {
my ($ws, $payload, $type) = @_;
},
);
For HTTP/2 or HTTP/3, use http_version => '2' or http_version => '3'. The generated request uses Extended CONNECT with :protocol = websocket.
Subprotocols
Offer subprotocols with:
subprotocols => [ 'chat', 'superchat' ]
After response validation:
my $selected = $handshake->subprotocol;
permessage-deflate
Offer RFC 7692 compression with:
permessage_deflate => 1
Or provide negotiation preferences:
permessage_deflate => {
server_no_context_takeover => 1,
client_no_context_takeover => 1,
server_max_window_bits => 12,
client_max_window_bits => undef,
}
A valueless client_max_window_bits offer is represented by undef.
After response validation, connection_options() contains the compression parameters that should be passed to the established Client object.
SERVER
Validate an incoming Uniform request:
my $handshake =
Unblock::WebSocket::Handshake->server_accept(
$request,
subprotocols => [ 'chat' ],
permessage_deflate => 1,
);
Build the HTTP response:
my $response = $handshake->server_response;
Send that response through your HTTP implementation. For HTTP/1.1, the accepted response has status 101 and the entire connection changes from HTTP to WebSocket. For HTTP/2 and HTTP/3, the accepted response is 200 and only that streaming transaction becomes WebSocket. Do not feed HTTP handshake bytes to the established WebSocket engine.
Then create the established server engine:
my $ws = Unblock::WebSocket::Server->new(
%{ $handshake->connection_options },
on_message => sub {
my ($ws, $payload, $type) = @_;
},
);
The server may supply a compression policy instead of a simple true value:
permessage_deflate => {
server_no_context_takeover => 1,
client_max_window_bits => 12,
}
METHODS
client_request
my ($handshake, $request) =
Unblock::WebSocket::Handshake->client_request($url, %options);
Creates a client handshake and a canonical Uniform::HTTP request. The http_version option accepts 1.1, 2, or 3. Optional subprotocols is an array reference of names to offer. permessage_deflate offers compression. origin adds the Origin header. headers adds extra name/value header pairs. For HTTP/1.1 testing, key can supply a valid fixed Sec-WebSocket-Key; normal calls generate a random key.
server_accept
my $handshake =
Unblock::WebSocket::Handshake->server_accept($request, %options);
Validates an incoming WebSocket handshake request. Optional subprotocols lists protocols the server supports; permessage_deflate enables or configures compression. The result records the negotiated values used by server_response() and connection_options().
server_response
my $response = $handshake->server_response;
Returns the successful Uniform::HTTP response for a validated server handshake.
validate_client_response
$handshake->validate_client_response($response);
Validates a server response to a client handshake. Throws on a rejected, malformed, or unexpected HTTP response. Call this before creating a WebSocket::Client object or sending WebSocket messages.
connection_options
my $options = $handshake->connection_options;
Returns a hash reference of negotiated established-connection options.
At present this contains permessage_deflate when RFC 7692 was negotiated.
permessage_deflate
my $config = $handshake->permessage_deflate;
Returns the negotiated RFC 7692 parameters, or undef when compression was not negotiated.
subprotocol
Returns the selected WebSocket subprotocol, or undef.
http_version
Returns 1.1, 2, or 3.
accept_key
my $accept =
Unblock::WebSocket::Handshake->accept_key($sec_websocket_key);
Calculates the RFC 6455 Sec-WebSocket-Accept value for a valid client key.
FASTPATH
Exact canonical Uniform::HTTP objects may be inspected through Uniform::HTTP::FastPath during handshake validation.
This is only an optimization. Uniform subclasses and adapters continue to use the normal portable Uniform::HTTP methods.
WebSocket-generated handshake objects still use normal validated Uniform constructors.