File Coverage

blib/lib/Net/WebSocket/Frame/close.pm
Criterion Covered Total %
statement 45 51 88.2
branch 13 20 65.0
condition 3 6 50.0
subroutine 9 9 100.0
pod 0 2 0.0
total 70 88 79.5


line stmt bran cond sub pod time code
1             package Net::WebSocket::Frame::close;
2              
3             =encoding utf-8
4              
5             =head1 NAME
6              
7             Net::WebSocket::Frame::close
8              
9             =head1 SYNOPSIS
10              
11             my $frm = Net::WebSocket::Frame::close->new(
12              
13             #Optional, can be either empty (default) or four random bytes
14             mask => q<>,
15              
16             code => 'SUCCESS', #See below
17              
18             reason => 'yeah, baby', #See below
19             );
20              
21             $frm->get_type(); #"close"
22              
23             $frm->is_control(); #1
24              
25             my $mask = $frm->get_mask_bytes();
26              
27             my ($code, $reason) = $frm->get_code_and_reason();
28              
29             #If, for some reason, you need the raw payload:
30             my $payload = $frm->get_payload();
31              
32             my $serialized = $frm->to_bytes();
33              
34             Note that, L,
35             close messages can have any of:
36              
37             =over
38              
39             =item * no code, and no reason
40              
41             Returned as undef (for the code) and an empty string. This diverges
42             from the RFC’s described behavior of returning code 1005.
43              
44             =item * a code, and no reason
45              
46             Returned as the code number and an empty string.
47              
48             =item * a code, and a reason that cannot exceed 123 bytes
49              
50             =back
51              
52             The code (i.e., C<$code>) is subject to
53             L.
54             You can also, in lieu of a numeric constant, use the following string
55             constants that derive from L:
56              
57             =over
58              
59             =item * C (1000)
60              
61             =item * C (1001)
62              
63             =item * C (1002)
64              
65             =item * C (1003)
66              
67             =item * C (1007)
68              
69             =item * C (1008)
70              
71             =item * C (1009)
72              
73             =item * C (1010)
74              
75             =item * C, aka C (1011)
76              
77             This appears as C in Microsoft’s documentation; however,
78             L updates
79             the RFC to have this status encompass client errors as well.
80              
81             Net::WebSocket recognizes either string, but its parsing logic will return
82             only C.
83              
84             =back
85              
86             The following additional status constants derive from
87             L
88             and are newer than either RFC 6455 or Microsoft’s API:
89              
90             =over
91              
92             =item * C (1012)
93              
94             =item * C (1013)
95              
96             =item * C (1014)
97              
98             =back
99              
100             It is hoped that a future update to the WebSocket specification
101             can include these or similar constant names.
102              
103             =cut
104              
105 12     12   100579 use strict;
  12         31  
  12         288  
106 12     12   46 use warnings;
  12         18  
  12         264  
107              
108 12         65 use parent qw(
109             Net::WebSocket::Base::ControlFrame
110 12     12   47 );
  12         29  
111              
112 12     12   4450 use Call::Context ();
  12         2504  
  12         186  
113              
114 12     12   61 use Net::WebSocket::Constants ();
  12         20  
  12         121  
115 12     12   42 use Net::WebSocket::X ();
  12         23  
  12         149  
116              
117 12     12   44 use constant get_opcode => 8;
  12         19  
  12         3976  
118              
119             sub new {
120 6     6 0 81 my ($class, %opts) = @_;
121              
122 6 100 66     38 if (!$opts{'payload_sr'} && !defined $opts{'payload'}) {
123 5         9 my $payload;
124              
125 5 50       24 if (my $code = delete $opts{'code'}) {
126 5         21 my $num = Net::WebSocket::Constants::status_name_to_code($code);
127 5 100       21 if (!$num) {
128 2         3 $num = $code;
129              
130 2 50       12 if ($num !~ m<\A[0-9]{4}\z> ) {
131 0         0 die Net::WebSocket::X->create('BadArg', 'code', $num, 'Invalid WebSocket status code');
132             }
133              
134 2 50       11 if ( !Net::WebSocket::Constants::status_code_to_name($num) ) {
135 2 50 33     9 if ( $num < 4000 || $num > 4999 ) {
136 0         0 die Net::WebSocket::X->create('BadArg', 'code', $num, 'Disallowed WebSocket status code');
137             }
138             }
139             }
140              
141 5         23 $payload = pack 'n', $num;
142              
143 5         11 my $reason = delete $opts{'reason'};
144 5 50       15 if (defined $reason) {
145 5 50       13 if (length $reason > 123) {
146 0         0 die Net::WebSocket::X->create('BadArg', 'reason', $reason, 'Reason cannot exceed 123 bytes!');
147             }
148              
149 5         11 $payload .= $reason;
150             }
151             }
152             else {
153 0         0 $payload = q<>;
154             }
155              
156 5         17 $opts{'payload'} = $payload;
157             }
158              
159 6         51 return $class->SUPER::new( %opts );
160             }
161              
162             sub get_code_and_reason {
163 20     20 0 17289 my ($self) = @_;
164              
165 20         56 Call::Context::must_be_list();
166              
167             #This shouldn’t happen … maybe leftover from previous architecture?
168 20 50       241 if ($self->get_type() ne 'close') {
169 0         0 my $type = $self->get_type();
170 0         0 die "Frame type is “$type”, not “close” as expected!";
171             }
172              
173 20 100       31 if (!length ${ $self->[$self->PAYLOAD] }) {
  20         142  
174 5         48 return ( undef, q<> );
175             }
176              
177 15         49 return unpack 'na*', $self->get_payload();
178             }
179              
180             1;