File Coverage

blib/lib/MojoX/Transaction/WebSocket76.pm
Criterion Covered Total %
statement 15 63 23.8
branch 0 20 0.0
condition 0 6 0.0
subroutine 5 11 45.4
pod 3 3 100.0
total 23 103 22.3


line stmt bran cond sub pod time code
1             package MojoX::Transaction::WebSocket76;
2              
3 1     1   24050 use Mojo::Util ('md5_bytes');
  1         93524  
  1         214  
4              
5 1     1   14 use Mojo::Base 'Mojo::Transaction::WebSocket';
  1         2  
  1         11  
6              
7              
8             our $VERSION = '0.04';
9              
10              
11 1     1   161317 use constant DEBUG => &Mojo::Transaction::WebSocket::DEBUG;
  1         10  
  1         142  
12              
13             use constant {
14 1         968 TEXT => &Mojo::Transaction::WebSocket::TEXT,
15             BINARY => &Mojo::Transaction::WebSocket::BINARY,
16             CLOSE => &Mojo::Transaction::WebSocket::CLOSE,
17 1     1   6 };
  1         2  
18              
19              
20             sub build_frame {
21 0     0 1   my ($self, undef, undef, undef, undef, $type, $bytes) = @_;
22              
23 0           warn("-- Building frame (undef, undef, undef, undef, " . $type . ")\n") if DEBUG;
24              
25 0           my $length = length($bytes);
26              
27 0           warn("-- Payload (" . length($bytes) . ")\n" . $bytes . "\n") if DEBUG;
28              
29 0 0         return "\xff" if $type == CLOSE;
30 0           return "\x00" . $bytes . "\xff";
31             }
32              
33             sub parse_frame {
34 0     0 1   my ($self, $buffer) = @_;
35              
36 0           my $index = index($$buffer, "\xff");
37              
38 0 0         return if $index < 0;
39              
40 0 0         my $type = $index == 0 ? CLOSE : TEXT;
41 0           my $length = $index - 1;
42 0 0         my $bytes = $length
43             ? substr(substr($$buffer, 0, $index + 1, ''), 1, $length)
44             : '';
45              
46 0           warn("-- Parsing frame (undef, undef, undef, undef, " . $type . ")\n") if DEBUG;
47 0           warn("-- Payload (" . $length . ")\n" . $bytes . "\n") if DEBUG;
48              
49             # Result does compatible with Mojo::Transaction::WebSocket.
50 0           return [1, 0, 0, 0, $type, $bytes];
51             }
52              
53             sub server_handshake {
54 0     0 1   my ($self) = @_;
55              
56 0           my $req = $self->req;
57 0           my $content = $req->content;
58              
59             # Fetch request body.
60 0           $content->headers->content_length(length($content->leftovers));
61 0           $content->parse_body();
62              
63 0           my $res = bless($self->res, 'MojoX::Transaction::WebSocket76::_Response');
64 0           my $headers = $req->headers;
65              
66 0           $res->code(101);
67 0           $res->message('WebSocket Protocol Handshake');
68 0           $res->body(
69             $self->_challenge(
70             scalar($headers->header('Sec-WebSocket-Key1')),
71             scalar($headers->header('Sec-WebSocket-Key2')),
72             $req->body # Key3 data.
73             )
74             );
75              
76 0           my $url = $req->url;
77 0 0         my $scheme = $url->to_abs->scheme eq 'https' ? 'wss' : 'ws';
78 0           my $location = $url->to_abs->scheme($scheme)->to_string();
79 0           my $origin = $headers->header('Origin');
80 0           my $protocol = $headers->sec_websocket_protocol;
81              
82 0           $headers = $res->headers;
83 0           $headers->upgrade('WebSocket');
84 0           $headers->connection('Upgrade');
85 0           $headers->header('Sec-WebSocket-Location' => $location);
86 0 0         $headers->header('Sec-WebSocket-Origin' => $origin) if $origin;
87 0 0         $headers->sec_websocket_protocol($protocol) if $protocol;
88              
89 0           return $self;
90             }
91              
92             sub _challenge {
93 0     0     my ($self, $key1, $key2, $key3) = @_;
94              
95 0 0 0       return unless $key1 && $key2 && $key3;
      0        
96 0           return md5_bytes(join('',
97             pack('N', join('', $key1 =~ /(\d)/g) / ($key1 =~ tr/\ //)),
98             pack('N', join('', $key2 =~ /(\d)/g) / ($key2 =~ tr/\ //)),
99             $key3
100             ));
101             }
102              
103              
104             1;
105              
106              
107             package # Hide form PAUSE.
108             MojoX::Transaction::WebSocket76::_Response;
109              
110 1     1   7 use Mojo::Base 'Mojo::Message::Response';
  1         2  
  1         13  
111              
112              
113             sub fix_headers {
114 0     0     my ($self) = @_;
115              
116 0           $self->SUPER::fix_headers(@_[1 .. $#_]);
117             # Suppress "Content-Length" header.
118 0           $self->headers->remove('Content-Length');
119              
120 0           return $self;
121             }
122              
123             sub is_empty {
124 0     0     my ($self) = @_;
125              
126 0 0         return unless my $code = $self->code;
127             # Handshake response has a body.
128 0 0         return if $code == 101;
129 0           return $self->SUPER::is_empty;
130             }
131              
132              
133             1;
134              
135              
136             __END__