File Coverage

blib/lib/XAS/Lib/Stomp/Parser.pm
Criterion Covered Total %
statement 12 84 14.2
branch 0 28 0.0
condition 0 10 0.0
subroutine 4 7 57.1
pod 2 3 66.6
total 18 132 13.6


line stmt bran cond sub pod time code
1             package XAS::Lib::Stomp::Parser;
2              
3             our $VERSION = '0.03';
4              
5 1     1   657 use XAS::Lib::Stomp::Frame;
  1         2  
  1         23  
6 1     1   4 use XAS::Constants ':stomp';
  1         1  
  1         4  
7              
8             use XAS::Class
9 1         14 debug => 0,
10             version => $VERSION,
11             base => 'XAS::Base',
12             mixin => 'XAS::Lib::Mixins::Bufops',
13             utils => 'trim',
14             vars => {
15             PARAMS => {
16             -target => { optional => 1, default => undef, regex => STOMP_LEVELS },
17             }
18             }
19 1     1   256 ;
  1         1  
20              
21              
22             my $HEADER = STOMP_HEADER;
23             my $CNTRL = STOMP_CNTRL;
24             my $BEOH = STOMP_BEOH;
25             my $EOF = STOMP_EOF;
26             my $EOH = STOMP_EOH;
27             my $EOL = STOMP_EOL;
28              
29             #use Data::Dumper;
30             #use Data::Hexdumper;
31              
32             # ----------------------------------------------------------------------
33             # Public Methods
34             # ----------------------------------------------------------------------
35              
36             sub parse {
37 0     0 0   my $self = shift;
38 0           my $buffer = shift;
39              
40 0           my $line;
41             my $length;
42 0           my $clength;
43 0           my $count = 0;
44 0           my $frame = undef;
45              
46 1     1   626 use bytes;
  1         1  
  1         6  
47              
48 0           $self->{buffer} .= $buffer;
49              
50 0           $self->log->debug('stomp-parser: begin');
51             # $self->log->debug(hexdump($self->{buffer}));
52              
53             # A valid frame is usually this:
54             #
55             # command - command
56             # header - 0 or more
57             # - seperator
58             # body - body
59             #
60             # as of v1.1 this is a valid frame
61             #
62             #
63             #
64             # and is used as a NOOP, which is used as a protocol keepalive. This
65             # module will create a fake 'NOOP' command for that frame. Frame
66             # stringification does the right thing.
67             #
68             # All current versions define as \000.
69             #
70             # In v1.0 and v1.1, was defined as NEWLINE, which is \012, but,
71             # common usage was \n, which is platform specific, hence the $EOL, $EOH
72             # and $BEOH regexs to match against.
73             #
74             # v1.2 changes to \015\012
75             #
76              
77 0           for (;;) {
78              
79 0           $self->log->debug('stomp-parser: state = ' . $self->{state});
80              
81 0 0         if ($self->{state} eq 'command') {
    0          
    0          
    0          
82              
83             # start of the frame
84             # check for a valid buffer, must have a EOL someplace.
85              
86 0 0         if ($line = $self->buf_get_line(\$self->{buffer}, $EOL)) {
87              
88 0           $self->log->debug('stomp-parser: command');
89             # $self->log->debug(hexdump($line));
90              
91 0           $line = trim($line);
92              
93 0 0         $self->{command} = ($line eq '') ? 'NOOP' : $line;
94 0           $self->{state} = 'headers';
95              
96 0           } else { last; }
97              
98             } elsif ($self->{state} eq 'headers') {
99              
100             # start of the headers, they last until a standalone
101             # or is reached.
102              
103 0           $self->log->debug("stomp-parser: header");
104              
105 0           $length = length($self->{buffer});
106              
107 0           $self->{buffer} =~ m/$EOH/g;
108 0   0       $clength = pos($self->{buffer}) || -1;
109              
110 0           $self->log->debug("stomp-parser: end of headers, length: $clength");
111              
112 0 0         if ($clength == -1) {
113              
114 0           pos($self->{buffer}) = 0;
115 0           $self->{buffer} =~ m/$BEOH/g;
116 0   0       $clength = pos($self->{buffer}) || -1;
117              
118 0           $self->log->debug("stomp-parser: end of frame, length: $clength");
119              
120             }
121              
122 0 0 0       if (($clength != -1) && ($clength <= $length)) {
123              
124 0           $line = $self->buf_slurp(\$self->{buffer}, $clength);
125             # $self->log->debug(hexdump($line));
126              
127 0           while ($line =~ s/^$HEADER//) {
128              
129 0           $self->log->debug('stomp-parser: valid header');
130              
131 0           my $key = lc($1);
132 0           my $value = trim($2);
133              
134             # v1.2 says that the first defined header is
135             # to be honored. v1.0 and v1.1 implies that
136             # the last defined header is honored. The duplictes
137             # are discarded.
138              
139 0 0         if ($self->target < 1.2) {
140              
141 0           $self->{headers}->{$key} = $value;
142              
143             } else {
144              
145 0 0         unless (defined($self->{headers}->{$key})) {
146              
147 0           $self->{headers}->{$key} = $value;
148              
149             }
150              
151             }
152              
153 0           $line =~ s/$EOL//;
154              
155             }
156              
157 0           $self->{state} = 'body';
158              
159 0           } else { last; }
160              
161             } elsif ($self->{state} eq 'body') {
162              
163 0           $self->log->debug('stomp-parser: body');
164              
165             # start of the body, determine wither to use
166             # content-length or EOF to find the end
167              
168 0           $length = length($self->{buffer});
169              
170 0 0         if ($clength = $self->{headers}->{'content-length'}) {
171              
172 0           $self->log->debug('stomp-parser: using content-length');
173              
174 0 0         if ($clength <= $length) {
175              
176 0           $self->{body} = $self->buf_slurp(\$self->{buffer}, $clength);
177 0           $self->{state} = 'frame';
178              
179 0           } else { last; }
180              
181             } else {
182              
183 0           $self->log->debug('stomp-parser: using EOF');
184              
185 0           $clength = index($self->{buffer}, $EOF);
186              
187 0 0 0       if (($clength != -1) && ($clength <= $length)) {
188              
189 0           $self->{body} = $self->buf_get_line(\$self->{buffer}, $EOF);
190 0           chop $self->{body};
191 0           $self->{state} = 'frame';
192              
193 0           } else { last; }
194              
195             }
196              
197             } elsif ($self->{state} eq 'frame') {
198              
199 0           $self->log->debug('stomp-parser: building frame');
200              
201             # clear out inter-frame crap and create the object.
202              
203 0           $self->{buffer} =~ s/^$CNTRL//;
204              
205             $frame = XAS::Lib::Stomp::Frame->new(
206             -command => $self->{command},
207             -headers => $self->{headers},
208             -body => $self->{body}
209 0           );
210              
211             # reset ourselves
212              
213 0           $count = 0;
214              
215 0           delete $self->{command};
216 0           delete $self->{headers};
217 0           delete $self->{body};
218              
219 0           $self->{state} = 'command';
220              
221             }
222              
223             }
224              
225 0           return $frame;
226              
227             }
228              
229             sub get_pending {
230 0     0 1   my $self = shift;
231              
232 0           return $self->{buffer};
233              
234             }
235              
236             # ----------------------------------------------------------------------
237             # Private Methods
238             # ----------------------------------------------------------------------
239              
240             sub init {
241 0     0 1   my $class = shift;
242              
243 0           my $self = $class->SUPER::init(@_);
244              
245 0 0         unless (defined($self->{target})) {
246              
247 0           $self->{target} = $self->env->mqlevel;
248              
249             }
250              
251 0           $self->{state} = 'command';
252              
253 0           return $self;
254              
255             }
256              
257             1;
258              
259             __END__