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 15     15   392603 use strict;
  15         77  
  15         458  
106 15     15   78 use warnings;
  15         29  
  15         503  
107              
108 15         93 use parent qw(
109             Net::WebSocket::Base::ControlFrame
110 15     15   576 );
  15         359  
111              
112 15     15   8091 use Call::Context ();
  15         5979  
  15         322  
113              
114 15     15   108 use Net::WebSocket::Constants ();
  15         32  
  15         210  
115 15     15   67 use Net::WebSocket::X ();
  15         34  
  15         253  
116              
117 15     15   74 use constant get_opcode => 8;
  15         30  
  15         7550  
118              
119             sub new {
120 47     47 0 21632 my ($class, %opts) = @_;
121              
122 47 100 66     262 if (!$opts{'payload_sr'} && !defined $opts{'payload'}) {
123 46         77 my $payload;
124              
125 46 100       134 if (my $code = delete $opts{'code'}) {
126 39         138 my $num = Net::WebSocket::Constants::status_name_to_code($code);
127 39 100       467 if (!$num) {
128 9         58 $num = $code;
129              
130 9 100       77 if ($num !~ m<\A[1-4][0-9]{3}\z> ) {
131 3         23 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         155 $payload = pack 'n', $num;
142              
143 36         68 my $reason = delete $opts{'reason'};
144 36 100       130 if (defined $reason) {
145 20 50       76 if (length $reason > 123) {
146 0         0 die Net::WebSocket::X->create('BadArg', 'reason', $reason, 'Reason cannot exceed 123 bytes!');
147             }
148              
149 20         54 $payload .= $reason;
150             }
151             }
152             else {
153 7         13 my $reason = delete $opts{'reason'};
154              
155 7 50 66     30 if (defined $reason && length $reason) {
156 0         0 warn "close frame constructor received “reason” ($opts{'reason'}) but no “code”!";
157             }
158              
159 7         13 $payload = q<>;
160             }
161              
162 43         101 $opts{'payload'} = $payload;
163             }
164              
165 44         249 return $class->SUPER::new( %opts );
166             }
167              
168             sub get_code_and_reason {
169 51     51 0 36894 my ($self) = @_;
170              
171 51         193 Call::Context::must_be_list();
172              
173             #This shouldn’t happen … maybe leftover from previous architecture?
174 51 50       764 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 51 100       94 if (!length ${ $self->[$self->PAYLOAD] }) {
  51         260  
180 8         60 return ( undef, q<> );
181             }
182              
183 43         166 return unpack 'na*', $self->get_payload();
184             }
185              
186             1;