File Coverage

blib/lib/Net/BEEP/Lite/Frame.pm
Criterion Covered Total %
statement 105 124 84.6
branch 35 62 56.4
condition 6 15 40.0
subroutine 19 20 95.0
pod 15 15 100.0
total 180 236 76.2


line stmt bran cond sub pod time code
1             # $Id: Frame.pm,v 1.8 2004/03/29 19:02:37 davidb Exp $
2             #
3             # Copyright (C) 2003 Verisign, Inc.
4             #
5             # This library is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU Lesser General Public
7             # License as published by the Free Software Foundation; either
8             # version 2.1 of the License, or (at your option) any later version.
9             #
10             # This library is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13             # Lesser General Public License for more details.
14             #
15             # You should have received a copy of the GNU Lesser General Public
16             # License along with this library; if not, write to the Free Software
17             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18             # USA
19              
20             package Net::BEEP::Lite::Frame;
21              
22             =head1 NAME
23              
24             Net::BEEP::Lite::Frame
25              
26             =head1 SYNOPSIS
27              
28             my $frame1 = Net::BEEP::Lite::Frame->new
29             (Header => $header,
30             Payload => $payload);
31              
32             my $frame2 = Net::BEEP::Lite::Frame->new
33             (Buffer => $header_plus_payload);
34              
35             my $frame3 = Net::BEEP::Lite::Frame->new
36             (Type => "MSG",
37             Msgno => $message_number,
38             Size => $size,
39             More => '.',
40             Seqno => $sequence_number,
41             Channel => $channel_number);
42              
43             =head1 DESCRIPTION
44              
45             "Net::BEEP::Lite::Frame" is a class used for describing BEEP frames, the
46             underlying unit of transport in BEEP. This is generally not used in
47             user code. Instead, it is used internally by the
48             C class for sending and receiving messages.
49              
50             =cut
51              
52 6     6   34434 use Carp;
  6         14  
  6         412  
53              
54 6     6   183 use strict;
  6         14  
  6         203  
55 6     6   31 use warnings;
  6         12  
  6         16967  
56              
57             =head1 CONSTRUCTOR
58              
59             =over 4
60              
61             =item new( I )
62              
63             This is the main constructor for the class. It takes a named argument list. The following arguments are supported:
64              
65             =over 4
66              
67             =item Header
68              
69             An unparsed frame header (e.g, "MSG 1 23 . 15563 49")
70              
71             =item Payload
72              
73             The frame payload (the frame minus the header and trailer).
74              
75             =item Type
76              
77             The frame type: one of (MSG, RPY, ERR, ANS, NUL, SEQ).
78              
79             =item Msgno
80              
81             The frame's message number.
82              
83             =item Size
84              
85             The size of the payload (not including trailer)
86              
87             =item More
88              
89             Either "." (no more), or "*" (more). This is a flag that indicates
90             whether the message being described by this frame is complete.
91              
92             =item Seqno
93              
94             The sequence number of this frame. This is generally the number of
95             octets already seen on the given channel.
96              
97             =item Channel
98              
99             The channel number.
100              
101             =back
102              
103             =back
104              
105             =cut
106              
107             sub new {
108 14     14 1 3093 my $this = shift;
109 14   33     68 my $class = ref($this) || $this;
110 14         74 my %args = @_;
111              
112 14         32 my $self = {};
113 14         41 bless $self, $class;
114              
115             # set some defaults
116 14         41 $self->{more} = '.';
117 14         28 $self->{size} = 0;
118 14         24 $self->{seqno} = 0;
119              
120 14         72 for (keys %args) {
121 74         116 my $val = $args{$_};
122              
123 74 100       165 /^Header/i and do {
124 2         6 $self->_parse_header($val);
125 2         4 next;
126             };
127 72 100       141 /^Payload/i and do {
128 9         27 $self->set_payload($val);
129             };
130 72 100       195 /^Type/i and do {
131 11         54 $self->{type} = uc $val;
132 11         26 next;
133             };
134 61 100       128 /^Msgno/i and do {
135 10         22 $self->{msgno} = $val;
136 10         21 next;
137             };
138 51 100       104 /^More/i and do {
139 10         22 $self->{more} = $val;
140 10         20 next;
141             };
142 41 100       132 /^Seqno/i and do {
143 10         21 $self->{seqno} = $val;
144 10         15 next;
145             };
146 31 100       69 /^Ansno/i and do {
147 9         21 $self->{ansno} = $val;
148 9         16 next;
149             };
150 22 100       47 /^Ackno/i and do {
151 1         2 $self->{ackno} = $val;
152 1         1 next;
153             };
154 21 100       42 /^Window/i and do {
155 1         2 $self->{window} = $val;
156 1         2 next;
157             };
158 20 100       53 /^Channel/i and do {
159             # FIXME: this might be a channel object, if we had defined one.
160             # For now we have to assume that it is a number (generally, 0 or
161             # 1 in this implementation.)
162 11         72 $self->{channel_number} = $val;
163 11         20 next;
164             };
165 9 50       29 /^Payload/i and do {
166 9         19 $self->set_payload($val);
167 9         17 next;
168             };
169             }
170              
171              
172 14         63 $self;
173             }
174              
175             =head1 METHODS
176              
177             =over 4
178              
179             =item type()
180              
181             Returns the type of the frame. (e.g., "MSG", "RPY, "SEQ", etc.).
182              
183             =cut
184              
185             sub type {
186 30     30 1 992 my $self = shift;
187 30         135 $self->{type};
188             }
189              
190             =item msgno()
191              
192             Returns the message number of the frame.
193              
194             =cut
195              
196             sub msgno {
197 10     10 1 17 my $self = shift;
198 10         78 $self->{msgno};
199             }
200              
201             =item size()
202              
203             Returns the size of the frame. If there is a payload, it will return
204             the size of that. In the absence of a payload, it will whatever it
205             has been set to (presumably by parsing a frame header).
206              
207             =cut
208              
209             sub size {
210 14     14 1 24 my $self = shift;
211 14 100       29 return length($self->payload()) if ($self->payload());
212 4         15 $self->{size};
213             }
214              
215             =item more()
216              
217             Returns either "." (no more) or "*" (more), a flag indicating whether
218             or not this frame completes the message.
219              
220             =cut
221              
222             sub more {
223 7     7 1 13 my $self = shift;
224 7         29 $self->{more};
225             }
226              
227             =item completes()
228              
229             Return true if this is a completing frame. I.e., return true if
230             the more field is ".".
231              
232             =cut
233              
234             sub completes {
235 2     2 1 3 my $self = shift;
236              
237 2 100       16 $self->{more} eq '.' ? 1 : 0;
238             }
239              
240              
241             =item seqno()
242              
243             Returns the sequence number of the frame.
244              
245             =cut
246              
247             sub seqno {
248 9     9 1 14 my $self = shift;
249 9         603 $self->{seqno};
250             }
251              
252             =item ansno()
253              
254             Returns the answer number. This only has meaning for ANS frames.
255              
256             =cut
257              
258             sub ansno {
259 7     7 1 45 my $self = shift;
260 7         24 $self->{ansno};
261             }
262              
263             =item channel_number()
264              
265             Returns the channel number of the frame.
266              
267             =cut
268              
269             sub channel_number {
270 12     12 1 20 my $self = shift;
271 12         42 $self->{channel_number};
272             }
273              
274             =item payload()
275              
276             Return the payload of the frame.
277              
278             =cut
279              
280             sub payload {
281 39     39 1 56 my $self = shift;
282 39         156 $self->{payload};
283             }
284              
285             =item ackno()
286              
287             Returns the acknowledgment number of the frame. (SEQ frames only).
288              
289             =cut
290              
291             sub ackno {
292 2     2 1 2 my $self = shift;
293 2         8 $self->{ackno};
294             }
295              
296             =item window()
297              
298             Returns the window size advertised by the frame. (SEQ frames only).
299              
300             =cut
301              
302             sub window {
303 2     2 1 3 my $self = shift;
304 2         5 $self->{window};
305             }
306              
307             =item set_payload($payload)
308              
309             Sets this frame's payload to $payload.
310              
311             =cut
312              
313             sub set_payload {
314 20     20 1 443 my $self = shift;
315 20         27 my $payload = shift;
316              
317 20         84 $self->{payload} = $payload;
318             }
319              
320             sub _parse_header {
321 2     2   3 my $self = shift;
322 2         3 my $header = shift;
323              
324 2 50       7 if (not $header) {
325 0         0 die "*** data frame header malformed: empty header encountered\n";
326             }
327              
328             #DEBUG
329             # print "frame header: $header";
330              
331 2         8 my @fields = split(/\s+/, $header);
332              
333 2         5 $self->{type} = shift @fields;
334              
335 2 50       9 if (! defined $self->{type}) {
    50          
336             # FIXME: should we die here? For now, it seems good.
337 0         0 die "*** data frame header malformed: type undefined\n";
338             } elsif ($self->{type} eq "SEQ") {
339 0         0 $self->{channel_number} = shift @fields;
340 0         0 $self->{ackno} = shift @fields;
341 0         0 $self->{window} = shift @fields;
342             } else {
343 2 50 33     11 if (scalar @fields != 5 and scalar @fields != 6) {
344             # FIXME: should we die here? For now, it seems good.
345             # Mis-parsing a header means we are probably hopelessly lost in
346             # the stream, or the peer is sending garbage.
347 0         0 die "*** data frame header malformed: ", scalar @fields,
348             " fields instead of 5 (or 6 for ANS): '$header'\n";
349             }
350              
351 2         5 $self->{channel_number} = shift @fields;
352 2         3 $self->{msgno} = shift @fields;
353 2         5 $self->{more} = shift @fields;
354 2         3 $self->{seqno} = shift @fields;
355 2         3 $self->{size} = shift @fields;
356              
357 2 50       3 $self->{ansno} = shift @fields if ($self->type() eq 'ANS');
358             }
359             }
360              
361             sub _check_frame {
362 0     0   0 my $self = shift;
363              
364 0         0 my $type = $self->type();
365 0 0       0 return 0 if not $type =~ /^(SEQ|MSG|RPY|ERR|ANS|NUL)$/;
366 0 0       0 return 0 if not defined $self->channel_number();
367 0 0       0 if ($type eq 'SEQ') {
368 0 0       0 return 0 if not defined $self->ackno();
369 0 0       0 return 0 if not defined $self->window();
370 0         0 return 1;
371             }
372              
373 0 0       0 return 0 if not defined $self->msgno();
374 0 0       0 return 0 if not $self->more();
375 0 0       0 return 0 if not defined $self->seqno();
376 0 0       0 return 0 if not defined $self->size();
377 0 0 0     0 return 0 if $type eq 'ANS' and not defined $self->ansno();
378             }
379              
380              
381             =item header_to_string()
382              
383             Returns the string form of the header. This is valid for the wire.
384              
385             =cut
386              
387             sub header_to_string {
388 6     6 1 10 my $self = shift;
389              
390 6         11 my $res = "";
391              
392 6         15 $res .= $self->type() . " " . $self->channel_number();
393 6 100       18 if ($self->type() eq "SEQ") {
394 2         5 $res .= " " . $self->ackno() . " " . $self->window();
395             }
396             else {
397 4         14 $res .= " " . $self->msgno() . " " . $self->more() . " " .
398             $self->seqno() . " " . $self->size();
399 4 100       13 $res .= " " . $self->ansno() if $self->type() eq "ANS";
400             }
401              
402 6         10 $res .= "\r\n";
403              
404 6         13 $res;
405             }
406              
407             =item to_string()
408              
409             Returns the string form of the entire frame (header, payload, and
410             trailer). This valid for the wire.
411              
412             =cut
413              
414             sub to_string {
415 4     4 1 560 my $self = shift;
416              
417 4         13 my $res = $self->header_to_string();
418 4 50 66     11 if ($self->payload() and $self->type() ne 'NUL' and
      66        
419             $self->type() ne 'SEQ') {
420 3         8 $res .= $self->payload() . "END\r\n";
421             }
422              
423 4         13 $res;
424             }
425              
426             =pod
427              
428             =back
429              
430             =head1 SEE ALSO
431              
432             =over 4
433              
434             =item L
435              
436             =back
437              
438             =cut
439              
440             1;