File Coverage

blib/lib/POE/Filter/Stomp.pm
Criterion Covered Total %
statement 101 106 95.2
branch 23 36 63.8
condition 7 15 46.6
subroutine 13 13 100.0
pod 0 5 0.0
total 144 175 82.2


line stmt bran cond sub pod time code
1             #
2             # File: Stomp.pm
3             # Date: 30-Aug-2007
4             # By : Kevin Esteb
5             #
6             # This module will parse the input stream and create Net::Stomp::Frame
7             # objects from that input stream. A STOMP frame looks like this:
8             #
9             # command
10             # headers
11             #
12             # body
13             # \000
14             #
15             # notes for v0.04
16             #
17             # The protocol spec calls for "newline" as the EOL. All implementatons
18             # are translating this into "\n". This is fine, except that "\n" has
19             # differing meanings depending on OS and/or language you are using.
20             # This complicated matters when parsing packets.
21             #
22             # More information is located at http://stomp.codehaus.org/Protocol
23             #
24              
25             package POE::Filter::Stomp;
26              
27 9     9   342185 use 5.008;
  9         36  
  9         358  
28 9     9   53 use strict;
  9         19  
  9         329  
29 9     9   58 use warnings;
  9         14  
  9         281  
30              
31 9     9   8277 use Net::Stomp::Frame;
  9         52537  
  9         83  
32              
33             our $VERSION = '0.04';
34              
35             # Be strick in what you send...
36              
37 9     9   553 use constant EOL => "\n";
  9         22  
  9         759  
38 9     9   55 use constant EOF => "\000";
  9         20  
  9         19875  
39              
40             # But lenient in what you recieve...
41              
42             my $eof = "\000";
43             my $eol = qr((\015\012?|\012\015?|\015|\012));
44             #my $eol = qr((\012|\015|\015\012?|\012\015?));
45             my $cntrl = qr((?:[[:cntrl:]])+);
46              
47             # ---------------------------------------------------------------------
48             # Public methods
49             # ---------------------------------------------------------------------
50              
51             sub new {
52 8     8 0 1374 my $proto = shift;
53              
54 8         22 my $self = {};
55 8   33     71 my $class = ref($proto) || $proto;
56              
57 8         26 $self->{buffer} = "";
58              
59 8         26 bless($self, $class);
60              
61 8         30 return $self;
62              
63             }
64              
65             sub get_one_start {
66 17     17 0 12822 my ($self, $buffers) = @_;
67              
68 17 50       73 $buffers = [$buffers] unless (ref($buffers));
69 17         1827 $self->{buffer} .= join('', @$buffers);
70              
71             }
72              
73             sub get_one {
74 31     31 0 25360 my ($self) = shift;
75              
76 31         48 my $frame;
77             my $buffer;
78 0         0 my @ret;
79              
80 31         96 $frame = $self->_parse_frame();
81 31 50       106 push(@ret, $frame) if ($frame);
82              
83 31         86 return \@ret;
84              
85             }
86              
87             sub get_pending {
88 1     1 0 1712 my ($self) = shift;
89              
90 1         5 return($self->{buffer});
91              
92             }
93              
94             sub put {
95 4     4 0 6515 my ($self, $frames) = @_;
96              
97 4         8 my $string;
98 4         9 my $ret = [];
99              
100 4         10 foreach my $frame (@$frames) {
101              
102             # protocol spec is unclear about the case of the command,
103             # so uppercase the command, Why, just because I can.
104              
105 4         18 my $command = uc($frame->command);
106 4         37 my $headers = $frame->headers;
107 4         26 my $body = $frame->body;
108              
109 4         22 $string = $command . EOL;
110              
111 4 100       16 if ($headers->{bytes_message}) {
112              
113 2         6 delete $headers->{bytes_message};
114 2         5 $headers->{'content-length'} = length($body);
115              
116             }
117              
118             # protocol spec is unclear about spaces between headers and values
119             # nor the case of the header, so add a space and lowercase the
120             # header. Why, just because I can.
121              
122 4 50       6 while (my ($key, $value) = each %{$headers || {} }) {
  10         63  
123              
124 6         22 $string .= lc($key) . ': ' . $value . EOL;
125              
126             }
127              
128 4         9 $string .= EOL;
129 4   50     15 $string .= $body || '';
130 4         6 $string .= EOF;
131              
132 4         17 push (@$ret, $string);
133              
134             }
135              
136 4         14 return $ret;
137              
138             }
139              
140             # ---------------------------------------------------------------------
141             # Private methods
142             # ---------------------------------------------------------------------
143              
144             sub _read_line {
145 95     95   131 my ($self) = @_;
146              
147 95         111 my $buffer;
148              
149 95 100       2429 if ($self->{buffer} =~ s/^(.+?)$eol//) {
150              
151 64         169 $buffer = $1;
152              
153             }
154              
155 95         369 return $buffer;
156              
157             }
158              
159             sub _parse_frame {
160 31     31   48 my ($self) = @_;
161              
162 31         43 my $frame;
163             my $length;
164 0         0 my $clength;
165              
166             # check for a valid buffer, must have a EOL someplace
167              
168 31 50       634 return () if ($self->{buffer} !~ /$eol/);
169              
170             # read the command
171              
172 31 50       100 if (! $self->{command}) {
173              
174 31 50       100 if (my $command = $self->_read_line()) {
175              
176 31         90 $self->{command} = $command;
177              
178 0         0 } else { return (); }
179              
180             }
181              
182             # read the headers, parse until a double new line,
183             # punt if they are not found.
184              
185 31 50       101 if (! $self->{headers}) {
186              
187 31         843 $self->{buffer} =~ m/$eol$eol/g;
188 31   100     127 $clength = pos($self->{buffer}) || -1;
189              
190 31 100       663 if ($clength == -1) {
191              
192 2         7 pos($self->{buffer}) = 0;
193 2         44 $self->{buffer} =~ m/$eol$eof/g;
194 2   50     8 $clength = pos($self->{buffer}) || -1;
195              
196             }
197              
198 31         56 $length = length($self->{buffer});
199              
200 31 50       89 return () if ($clength == -1);
201              
202 31 50       1280 if ($clength <= $length) {
203              
204 31         66 my %headers = ();
205              
206 31         81 while (my $line = $self->_read_line()) {
207              
208 33 50       203 if ($line =~ /^([\w\-~]+)\s*:\s*(.*)/) {
209              
210 33         187 $headers{lc($1)} = $2;
211              
212             }
213              
214             }
215              
216 31         75 $self->{headers} = \%headers;
217 31         1160 $self->{buffer} =~ s/^$eol//;
218              
219 0         0 } else { return (); }
220              
221             }
222              
223             # read the body
224             #
225             # if "content-length" is defined then the body is binary,
226             # otherwise search the buffer until an EOF is found.
227              
228 31         61 $clength = 0;
229 31         81 $length = length($self->{buffer});
230              
231 31 100       90 if ($self->{headers}->{'content-length'}) {
232              
233 8         16 $self->{headers}->{bytes_message} = 1;
234 8         14 $clength = $self->{headers}->{'content-length'};
235              
236 8 50       22 if ($clength <= $length) {
237              
238 8         26 $self->{body} = substr($self->{buffer}, 0, $clength);
239 8         19 substr($self->{buffer}, 0, $clength) = "";
240              
241 0         0 } else { return (); }
242              
243             } else {
244              
245 23         946 $clength = index($self->{buffer}, $eof);
246              
247 23 50       60 return () if ($clength == -1);
248              
249 23 100       48 if ($clength == 0) {
250              
251 10         19 $self->{body} = " ";
252              
253             } else {
254              
255 13         424 $self->{body} = substr($self->{buffer}, 0, $clength);
256 13         37 substr($self->{buffer}, 0, $clength) = "";
257              
258             }
259              
260             }
261              
262             # remove the crap from between the frames
263              
264 31         434 $self->{buffer} =~ s/$cntrl//;
265              
266             # create the frame
267              
268 31 50 33     274 if ($self->{command} && $self->{headers} && $self->{body}) {
      33        
269              
270 31         268 $frame = Net::Stomp::Frame->new(
271             {
272             command => $self->{command},
273             headers => $self->{headers},
274             body => $self->{body}
275             }
276             );
277              
278 31         901 delete $self->{command};
279 31         54 delete $self->{headers};
280 31         55 delete $self->{body};
281              
282             }
283              
284 31         56 return $frame;
285              
286             }
287              
288             1;
289              
290             __END__