File Coverage

blib/lib/Net/WebSocket/Frame/close.pm
Criterion Covered Total %
statement 49 54 90.7
branch 18 22 81.8
condition 5 9 55.5
subroutine 9 9 100.0
pod 0 2 0.0
total 81 96 84.3


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 14     14   263851 use strict;
  14         51  
  14         543  
106 14     14   140 use warnings;
  14         37  
  14         434  
107              
108 14         82 use parent qw(
109             Net::WebSocket::Base::ControlFrame
110 14     14   625 );
  14         401  
111              
112 14     14   7930 use Call::Context ();
  14         5550  
  14         302  
113              
114 14     14   101 use Net::WebSocket::Constants ();
  14         30  
  14         189  
115 14     14   68 use Net::WebSocket::X ();
  14         28  
  14         242  
116              
117 14     14   68 use constant get_opcode => 8;
  14         43  
  14         6906  
118              
119             sub new {
120 45     45 0 22341 my ($class, %opts) = @_;
121              
122 45 100 66     265 if (!$opts{'payload_sr'} && !defined $opts{'payload'}) {
123 44         79 my $payload;
124              
125 44 100       116 if (my $code = delete $opts{'code'}) {
126 39         123 my $num = Net::WebSocket::Constants::status_name_to_code($code);
127 39 100       95 if (!$num) {
128 9         60 $num = $code;
129              
130 9 100       65 if ($num !~ m<\A[1-4][0-9]{3}\z> ) {
131 3         24 die Net::WebSocket::X->create('BadArg', 'code', $num, "Invalid WebSocket status code ($num) given");
132             }
133              
134 6 100       27 if ( !Net::WebSocket::Constants::status_code_to_name($num) ) {
135 5 50 33     33 if ( $num < 4000 || $num > 4999 ) {
136 0         0 die Net::WebSocket::X->create('BadArg', 'code', $num, "Disallowed WebSocket status code ($num) given");
137             }
138             }
139             }
140              
141 36         140 $payload = pack 'n', $num;
142              
143 36         74 my $reason = delete $opts{'reason'};
144 36 100       88 if (defined $reason) {
145 20 50       65 if (length $reason > 123) {
146 0         0 die Net::WebSocket::X->create('BadArg', 'reason', $reason, 'Reason cannot exceed 123 bytes!');
147             }
148              
149 20         61 $payload .= $reason;
150             }
151             }
152             else {
153 5         9 my $reason = delete $opts{'reason'};
154              
155 5 50 66     17 if (defined $reason && length $reason) {
156 0         0 warn "close frame constructor received “reason” ($opts{'reason'}) but no “code”!";
157             }
158              
159 5         11 $payload = q<>;
160             }
161              
162 41         100 $opts{'payload'} = $payload;
163             }
164              
165 42         199 return $class->SUPER::new( %opts );
166             }
167              
168             sub get_code_and_reason {
169 49     49 0 29604 my ($self) = @_;
170              
171 49         166 Call::Context::must_be_list();
172              
173             #This shouldn’t happen … maybe leftover from previous architecture?
174 49 50       680 if ($self->get_type() ne 'close') {
175 0         0 my $type = $self->get_type();
176 0         0 die "Frame type is “$type”, not “close” as expected!";
177             }
178              
179 49 100       85 if (!length ${ $self->[$self->PAYLOAD] }) {
  49         242  
180 6         44 return ( undef, q<> );
181             }
182              
183 43         156 return unpack 'na*', $self->get_payload();
184             }
185              
186             1;