File Coverage

blib/lib/XAS/Lib/Stomp/Frame.pm
Criterion Covered Total %
statement 30 121 24.7
branch 0 28 0.0
condition 0 2 0.0
subroutine 10 20 50.0
pod 2 2 100.0
total 42 173 24.2


line stmt bran cond sub pod time code
1             package XAS::Lib::Stomp::Frame;
2              
3             our $VERSION = '0.03';
4              
5 1     1   628 use XAS::Constants 'CRLF LF :stomp';
  1         1  
  1         9  
6              
7             use XAS::Class
8 1         11 debug => 0,
9             version => $VERSION,
10             base => 'XAS::Base',
11             utils => 'dotid',
12             accessors => 'eol header',
13             mutators => 'command body',
14             codec => 'unicode',
15             vars => {
16             PARAMS => {
17             -body => { optional => 1, default => undef },
18             -command => { optional => 1, default => undef },
19             -headers => { optional => 1, default => undef },
20             -target => { optional => 1, default => undef, regex => STOMP_LEVELS },
21             }
22             }
23 1     1   263 ;
  1         2  
24              
25             our %ENCODE_MAP = (
26             "\r" => "\\r",
27             "\n" => "\\n",
28             ":" => "\\c",
29             "\\" => "\\\\",
30             );
31              
32             our %DECODE_MAP = reverse %ENCODE_MAP;
33              
34             #use Data::Dumper;
35             #use Data::Hexdumper;
36              
37             # ----------------------------------------------------------------------
38             # Public Methods
39             # ----------------------------------------------------------------------
40              
41             sub as_string {
42 0     0 1   my $self = shift;
43              
44             # protocol spec is unclear about the case of the command,
45             # so uppercase the command, Why, just because I can.
46              
47 0           my $frame;
48 0           my $command = uc($self->command);
49 0           my $headers = $self->header->devolve;
50 0           my $body = $self->body;
51              
52             # special handling for NOOPs
53              
54 0 0         if ($command eq 'NOOP') {
55              
56 0           $command = '';
57 0           $headers = {};
58 0           $body = '';
59              
60             }
61              
62 0 0         if ($self->target > 1.1) {
63              
64 0           $frame = encode('utf8', $command) . $self->eol;
65              
66             } else {
67              
68 0           $frame = $command . $self->eol;
69              
70             }
71              
72             # v1.0 and v1.1 is unclear about spaces between headers and values
73             # nor the case of the header.
74             #
75             # v1.2 says there should be no 'padding' in headers and values, not
76             # sure what 'padding' means. It also adds the capability to 'escape'
77             # certain values. Please see %ENCODE_MAP and %DECODE_MAP for those
78             # values.
79             #
80             # So add a space and lowercase the header. Why, just because I can.
81              
82 0 0         if (keys %{$headers}) {
  0            
83              
84 0 0         $self->_encode_headers(\$headers) if ($self->target > 1.1);
85              
86 0           while (my ($key, $value) = each(%{$headers})) {
  0            
87              
88 0 0         if (defined($value)) {
89              
90 0           $frame .= lc($key) . ': ' . $value . $self->eol();
91              
92             }
93              
94             }
95              
96             } else {
97              
98 0           $frame .= $self->eol();
99              
100             }
101              
102 0           $frame .= $self->eol();
103 0           $frame .= $body;
104 0           $frame .= STOMP_EOF;
105              
106 0           return $frame;
107              
108             }
109              
110             # ----------------------------------------------------------------------
111             # Private Methods
112             # ----------------------------------------------------------------------
113              
114             sub init {
115 0     0 1   my $class = shift;
116              
117 0           my $self = $class->SUPER::init(@_);
118            
119 0 0         unless (defined($self->{'target'})) {
120              
121 0           $self->{'target'} = $self->env->mqlevel;
122              
123             }
124              
125 0   0       my $headers = $self->headers || {};
126              
127 0 0         $self->{'eol'} = ($self->target > 1.1) ? CRLF : LF;
128              
129 0 0         $self->_decode_headers(\$headers) if ($self->target > 1.1);
130 0           $self->{'header'} = XAS::Lib::Stomp::Frame::Headers->new($headers);
131              
132 0           return $self;
133              
134             }
135              
136             sub _encode_headers {
137 0     0     my $self = shift;
138 0           my $headers = shift; # a pointer to a reference of a hash, oh my...
139              
140 0           my $ENCODE_KEYS = '['.join('', map(sprintf('\\x%02x', ord($_)), keys(%ENCODE_MAP))).']';
141              
142 0           while (my ($k, $v) = each(%$$headers)) {
143              
144 0           $k = encode('utf8', $k);
145 0           $v = encode('utf8', $v);
146              
147 0           $v =~ s/($ENCODE_KEYS)/$ENCODE_MAP{$1}/ego;
  0            
148 0           $k =~ s/($ENCODE_KEYS)/$ENCODE_MAP{$1}/ego;
  0            
149              
150 0           $$headers->{$k} = $v;
151              
152             }
153              
154             }
155              
156             sub _decode_headers {
157 0     0     my $self = shift;
158 0           my $headers = shift; # a pointer to a reference of a hash, oh my...
159              
160 0           while (my ($k, $v) = each(%$$headers)) {
161              
162 0           $k = decode('utf8', $k);
163 0           $v = decode('utf8', $v);
164              
165 0 0         if ($v =~ m/(\\.)/) {
166              
167 0 0         unless ($v =~ s/(\\.)/$DECODE_MAP{$1}/eg) {
  0            
168              
169 0           $self->throw_msg(
170             dotid($self->class) . '.decode_header.badval',
171             'stomp_badval',
172             );
173              
174             }
175              
176             }
177              
178 0 0         if ($k =~ m/(\\.)/) {
179              
180 0 0         unless ($k =~ s/(\\.)/$DECODE_MAP{$1}/eg) {
  0            
181              
182 0           $self->throw_msg(
183             dotid($self->class) . '.decode_header.badkey',
184             'stomp_badkey'
185             );
186              
187             }
188              
189             }
190              
191 0           $$headers->{$k} = $v;
192              
193             }
194              
195             }
196              
197             package # hide from pause...
198             XAS::Lib::Stomp::Frame::Headers;
199              
200             our $VERSION = '0.02';
201              
202 1     1   1282 use XAS::Lib::Set::Light;
  1         1  
  1         34  
203              
204             use XAS::Class
205 1         3 debug => 0,
206             version => $VERSION,
207             base => 'XAS::Base',
208             constants => 'REFS',
209             accessors => 'methods',
210 1     1   3 ;
  1         2  
211              
212             #use Data::Dumper;
213              
214             sub remove {
215 0     0     my ($self, $key) = @_;
216              
217 0           $self->methods->remove($key);
218              
219 0           delete($self->{$key});
220              
221 1     1   458 no warnings;
  1         1  
  1         40  
222 1     1   4 no strict REFS;
  1         1  
  1         80  
223              
224 0           *$key = undef;
225              
226             }
227              
228             sub add {
229 0     0     my ($self, $key, $value) = @_;
230              
231 0           $key =~ s/-/_/g;
232              
233 0           $self->{$key} = $value;
234 0           $self->methods->insert($key);
235              
236 1     1   4 no warnings;
  1         1  
  1         25  
237 1     1   2 no strict REFS;
  1         2  
  1         180  
238              
239             *$key = sub {
240 0     0     my $self = shift;
241 0 0         $self->{$key} = shift if @_;
242 0           return $self->{$key};
243 0           };
244              
245             }
246              
247             sub devolve {
248 0     0     my $self = shift;
249              
250 0           my $value;
251 0           my $header = {};
252              
253 0           foreach my $key ($self->methods->items()) {
254              
255 0           $value = $self->{$key};
256 0           $key =~ s/_/-/g;
257 0           $header->{$key} = $value;
258              
259             }
260              
261 0           return $header;
262              
263             }
264              
265             sub init {
266 0     0     my $self = shift;
267 0           my $configs = shift;
268              
269 0           $self->{'config'} = $configs;
270 0           $self->{'methods'} = XAS::Lib::Set::Light->new();
271              
272             # turn frame headers into mutators of there values
273              
274 0           while (my ($key, $value) = each(%$configs)) {
275              
276 0           $key =~ s/-/_/g;
277              
278 0           $self->{$key} = $value;
279 0           $self->methods->insert($key);
280              
281 1     1   3 no warnings;
  1         1  
  1         29  
282 1     1   3 no strict REFS;
  1         2  
  1         73  
283              
284             *$key = sub {
285 0     0     my $self = shift;
286 0 0         $self->{$key} = shift if @_;
287 0           return $self->{$key};
288 0           };
289              
290             }
291              
292 0           return $self;
293              
294             }
295              
296             1;
297              
298             __END__