File Coverage

blib/lib/AnyEvent/WebSocket/Server.pm
Criterion Covered Total %
statement 75 92 81.5
branch 24 36 66.6
condition 4 6 66.6
subroutine 16 17 94.1
pod 3 3 100.0
total 122 154 79.2


line stmt bran cond sub pod time code
1             package AnyEvent::WebSocket::Server;
2 8     8   458284 use strict;
  8         13  
  8         177  
3 8     8   25 use warnings;
  8         8  
  8         135  
4 8     8   23 use Carp;
  8         12  
  8         360  
5 8     8   3596 use AnyEvent::Handle;
  8         55416  
  8         206  
6 8     8   3557 use Protocol::WebSocket::Handshake::Server;
  8         996624  
  8         221  
7 8     8   53 use Try::Tiny;
  8         12  
  8         422  
8 8     8   2933 use AnyEvent::WebSocket::Connection;
  8         27963  
  8         5217  
9              
10             our $VERSION = "0.08";
11              
12             sub new {
13 45     45 1 373433 my ($class, %args) = @_;
14 45         92 my $validator = $args{validator};
15 45 50 66     192 if(defined($validator) && ref($validator) ne "CODE") {
16 0         0 croak "validator parameter must be a code-ref";
17             }
18             my $handshake = defined($args{handshake}) ? $args{handshake}
19 9     9   36 : defined($validator) ? sub { my ($req, $res) = @_; return ($res, $validator->($req)); }
  9         19  
20 45 100   159   225 : sub { $_[1] };
  159 100       770  
21 45 50       151 if(ref($handshake) ne "CODE") {
22 0         0 croak "handshake parameter must be a code-ref";
23             }
24             my $self = bless {
25             handshake => $handshake,
26 45         82 map { ($_ => $args{$_}) } qw(ssl_key_file ssl_cert_file),
  90         298  
27             }, $class;
28 45         141 return $self;
29             }
30              
31             sub _create_on_error {
32 203     203   248 my ($cv) = @_;
33             return sub {
34 4     4   403 my ($handle, $fatal, $message) = @_;
35 4 50       10 if($fatal) {
36 4         18 $cv->croak("connection error: $message");
37             }else {
38 0         0 warn $message;
39             }
40 203         1209 };
41             }
42              
43             sub _handle_args_tls {
44 203     203   196 my ($self) = @_;
45 203 100 66     681 if(!defined($self->{ssl_key_file}) && !defined($self->{ssl_cert_file})) {
46 68         152 return ();
47             }
48 135 50       312 if(!defined($self->{ssl_cert_file})) {
49 0         0 croak "Only ssl_key_file is specified. You need to specify ssl_cert_file, too.";
50             }
51             return (
52             tls => "accept",
53             tls_ctx => {
54             cert_file => $self->{ssl_cert_file},
55 135 100       770 defined($self->{ssl_key_file}) ? (key_file => $self->{ssl_key_file}) : ()
56             }
57             );
58             }
59              
60             sub _do_handshake {
61 203     203   300 my ($self, $cv_connection, $fh, $handshake) = @_;
62 203         257 my $handshake_code = $self->{handshake};
63 203         400 my $handle = AnyEvent::Handle->new(
64             $self->_handle_args_tls,
65             fh => $fh, on_error => _create_on_error($cv_connection)
66             );
67             my $read_cb = sub {
68             ## We don't receive handle object as an argument here. $handle
69             ## is imported in this closure so that $handle becomes
70             ## half-immortal.
71             try {
72 405 100       12590 if(!defined($handshake->parse($handle->{rbuf}))) {
73 4         755 die "handshake error: " . $handshake->error . "\n";
74             }
75 401 100       99727 return if !$handshake->is_done;
76 195 50       1635 if($handshake->version ne "draft-ietf-hybi-17") {
77 0         0 die "handshake error: unsupported WebSocket protocol version " . $handshake->version . "\n";
78             }
79 195         1291 my ($res, @other_results) = $handshake_code->($handshake->req, $handshake->res);
80 186 100       3808 if(!defined($res)) {
81 3         311 croak "handshake response was undef";
82             }
83 183 100       412 if(ref($res) eq "Protocol::WebSocket::Response") {
84 180         436 $res = $res->to_string;
85             }
86 183         42124 $handle->push_write("$res");
87 183         17116 $cv_connection->send(AnyEvent::WebSocket::Connection->new(handle => $handle), @other_results);
88 183         58194 undef $handle;
89 183         514 undef $cv_connection;
90             }catch {
91 16         356 my $e = shift;
92 16         64 $cv_connection->croak($e);
93 16         5650 undef $handle;
94 16         55 undef $cv_connection;
95 405     405   2530539 };
96 203         88111 };
97 203         354 $handle->{rbuf} = "";
98 203         293 $read_cb->(); ## in case the whole request is already consumed
99 203 50       4582 $handle->on_read($read_cb) if defined $handle;
100             }
101              
102             sub establish {
103 204     204 1 413237 my ($self, $fh) = @_;
104 204         4931 my $cv_connection = AnyEvent->condvar;
105 204 100       1446 if(!defined($fh)) {
106 1         7 $cv_connection->croak("fh parameter is mandatory for establish() method");
107 1         37 return $cv_connection;
108             }
109 203         1056 my $handshake = Protocol::WebSocket::Handshake::Server->new;
110 203         1211 $self->_do_handshake($cv_connection, $fh, $handshake);
111 203         4752 return $cv_connection;
112             }
113              
114             sub establish_psgi {
115 0     0 1   my ($self, $env, $fh) = @_;
116 0           my $cv_connection = AnyEvent->condvar;
117 0 0         if(!defined($env)) {
118 0           $cv_connection->croak("psgi_env parameter is mandatory");
119 0           return $cv_connection;
120             }
121 0 0         $fh = $env->{"psgix.io"} if not defined $fh;
122 0 0         if(!defined($fh)) {
123 0           $cv_connection->croak("No connection file handle provided. Maybe the PSGI server does not support psgix.io extension.");
124 0           return $cv_connection;
125             }
126 0           my $handshake = Protocol::WebSocket::Handshake::Server->new_from_psgi($env);
127 0           $self->_do_handshake($cv_connection, $fh, $handshake);
128 0           return $cv_connection;
129             }
130              
131             1;
132              
133             __END__