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   262462 use strict;
  14         47  
  14         439  
106 14     14   74 use warnings;
  14         26  
  14         416  
107              
108 14         79 use parent qw(
109             Net::WebSocket::Base::ControlFrame
110 14     14   539 );
  14         345  
111              
112 14     14   7027 use Call::Context ();
  14         5402  
  14         292  
113              
114 14     14   94 use Net::WebSocket::Constants ();
  14         31  
  14         185  
115 14     14   62 use Net::WebSocket::X ();
  14         36  
  14         234  
116              
117 14     14   67 use constant get_opcode => 8;
  14         28  
  14         6741  
118              
119             sub new {
120 45     45 0 22180 my ($class, %opts) = @_;
121              
122 45 100 66     246 if (!$opts{'payload_sr'} && !defined $opts{'payload'}) {
123 44         76 my $payload;
124              
125 44 100       128 if (my $code = delete $opts{'code'}) {
126 39         154 my $num = Net::WebSocket::Constants::status_name_to_code($code);
127 39 100       106 if (!$num) {
128 9         59 $num = $code;
129              
130 9 100       56 if ($num !~ m<\A[1-4][0-9]{3}\z> ) {
131 3         22 die Net::WebSocket::X->create('BadArg', 'code', $num, "Invalid WebSocket status code ($num) given");
132             }
133              
134 6 100       28 if ( !Net::WebSocket::Constants::status_code_to_name($num) ) {
135 5 50 33     40 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         139 $payload = pack 'n', $num;
142              
143 36         75 my $reason = delete $opts{'reason'};
144 36 100       88 if (defined $reason) {
145 20 50       77 if (length $reason > 123) {
146 0         0 die Net::WebSocket::X->create('BadArg', 'reason', $reason, 'Reason cannot exceed 123 bytes!');
147             }
148              
149 20         58 $payload .= $reason;
150             }
151             }
152             else {
153 5         10 my $reason = delete $opts{'reason'};
154              
155 5 50 66     19 if (defined $reason && length $reason) {
156 0         0 warn "close frame constructor received “reason” ($opts{'reason'}) but no “code”!";
157             }
158              
159 5         10 $payload = q<>;
160             }
161              
162 41         93 $opts{'payload'} = $payload;
163             }
164              
165 42         218 return $class->SUPER::new( %opts );
166             }
167              
168             sub get_code_and_reason {
169 49     49 0 28870 my ($self) = @_;
170              
171 49         157 Call::Context::must_be_list();
172              
173             #This shouldn’t happen … maybe leftover from previous architecture?
174 49 50       698 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       109 if (!length ${ $self->[$self->PAYLOAD] }) {
  49         267  
180 6         54 return ( undef, q<> );
181             }
182              
183 43         152 return unpack 'na*', $self->get_payload();
184             }
185              
186             1;