File Coverage

lib/Config/Proxy/Impl/pound.pm
Criterion Covered Total %
statement 188 255 73.7
branch 57 92 61.9
condition 16 25 64.0
subroutine 26 30 86.6
pod 0 6 0.0
total 287 408 70.3


line stmt bran cond sub pod time code
1             package Config::Proxy::Impl::pound;
2 3     3   21 use strict;
  3         6  
  3         123  
3 3     3   18 use warnings;
  3         5  
  3         236  
4 3     3   14 use parent 'Config::Proxy::Base';
  3         11  
  3         26  
5 3     3   222 use Config::Proxy::Node::Root;
  3         6  
  3         84  
6 3     3   1734 use Config::Pound::Node::Section;
  3         9  
  3         98  
7 3     3   16 use Config::Proxy::Node::Comment;
  3         20  
  3         80  
8 3     3   33 use Config::Proxy::Node::Statement;
  3         5  
  3         56  
9 3     3   10 use Config::Proxy::Node::Empty;
  3         5  
  3         94  
10 3     3   1184 use Config::Pound::Node::Verbatim;
  3         9  
  3         88  
11 3     3   1214 use Config::Pound::Node::IP;
  3         8  
  3         85  
12 3     3   15 use Text::Locus;
  3         4  
  3         161  
13 3     3   14 use Text::ParseWords;
  3         4  
  3         165  
14 3     3   12 use Carp;
  3         4  
  3         149  
15 3     3   1782 use Data::Dumper;
  3         22853  
  3         842  
16              
17             our $VERSION = '1.0';
18              
19             sub new {
20 3     3 0 8 my $class = shift;
21 3   50     45 return $class->SUPER::new(shift // '/etc/pound.cfg', 'pound -c -f');
22             }
23              
24             sub dequote {
25 0     0 0 0 my ($self, $text) = @_;
26 0         0 my $q = ($text =~ s{^"(.*)"$}{$1});
27 0 0       0 if ($q) {
28 0         0 $text =~ s{\\(.)}{$1}g;
29             }
30 0 0       0 if (wantarray) {
31 0         0 return ($text, $q)
32             } else {
33 0         0 return $text;
34             }
35             }
36              
37             sub select {
38 2     2 0 978 my $self = shift;
39 2         21 my @query;
40 2         9 while (my $cond = shift @_) {
41 3 50       6 if ($cond eq 'name') {
42 0         0 $cond = 'name_ci';
43             }
44 3         10 push @query, $cond, shift(@_);
45             }
46 2         11 $self->SUPER::select(@query);
47             }
48              
49             use constant {
50 3         11127 PARSER_OK => 0,
51             PARSER_END => 1
52 3     3   26 };
  3         5  
53              
54             sub _parser_End {
55 18     18   94 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
56 18         104 $parent->append_node(
57             new Config::Proxy::Node::Statement(
58             kw => $kw,
59             argv => $words,
60             orig => $orig,
61             locus => new Text::Locus($filename, $line)
62             )
63             );
64 18         58 return (PARSER_END, $filename, $line);
65             }
66              
67             my %generic_section = ( 'end' => \&_parser_End );
68              
69             my %match_section;
70              
71             %match_section = (
72             'match' => \%match_section,
73             'not' => \&_parser_Not,
74             'acl' => \&_parser_ACL,
75             'end' => \&_parser_End
76             );
77              
78             my %rewrite_section = (
79             'else' => \&_parser_Else,
80             'rewrite' => \&_parser_Rewrite,
81             'match' => \%match_section,
82             'not' => \&_parser_Not,
83             'acl' => \&_parser_ACL,
84             'end' => \&_parser_End
85             );
86              
87             my %resolver_section = (
88             'configtext' => \&_parser_ConfigText,
89             'end' => \&_parser_End
90             );
91              
92             my %service_section = (
93             'backend' => \%generic_section,
94             'match' => \%match_section,
95             'not' => \&_parser_Not,
96             'acl' => \&_parser_ACL,
97             'rewrite' => \&_parser_Rewrite,
98             'session' => \%generic_section,
99             'end' => \&_parser_End
100             );
101              
102             my %http_section = (
103             'service' => \%service_section,
104             'end' => \&_parser_End
105             );
106              
107             my %top_section = (
108             # FIXME: If Include is to be expanded, this line should appear in all
109             # sections (since pound commit 6c7258cb2e).
110             # 'include' => \&_parser_Include,
111             'listenhttp' => \%http_section,
112             'listenhttps' => \%http_section,
113             'acl' => \&_parser_ACL,
114             'service' => \%service_section,
115             'backend' => \%generic_section,
116             'resolver' => \%resolver_section,
117             );
118              
119             sub _parser_section {
120 18     18   51 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
121             # FIXME: extract label from $words etc
122 18         59 my $section = Config::Pound::Node::Section->new(
123             kw => $kw,
124             argv => $words,
125             orig => $orig,
126             locus => new Text::Locus($filename, $line));
127 18         28 my $r;
128 18         76 ($r, $filename, $line) = $self->_parser($section, $filename, $line,
129             $fh, $ptab);
130 18         78 $section->locus->add($filename, $line);
131 18         195 $parent->append_node($section);
132 18         68 return ($r, $filename, $line)
133             }
134              
135             sub _parser {
136 21     21   50 my ($self, $parent, $filename, $line, $fh, $ptab) = @_;
137 21         43 my $start_locus = "$filename:$line";
138 21         170 while (<$fh>) {
139 81         113 $line++;
140 81         113 chomp;
141 81         115 my $orig = $_;
142 81         306 s/^\s+//;
143 81         216 s/\s+$//;
144              
145 81 100       163 if ($_ eq "") {
146 9         26 $parent->append_node(
147             new Config::Proxy::Node::Empty(
148             orig => $orig,
149             locus => new Text::Locus($filename, $line)
150             )
151             );
152 9         53 next;
153             }
154              
155 72 100       165 if (/^#.*/) {
156 3         17 $parent->append_node(
157             new Config::Proxy::Node::Comment(
158             orig => $orig,
159             locus => new Text::Locus($filename, $line)
160             )
161             );
162 3         15 next;
163             }
164              
165 69         171 my @words = parse_line('\s+', 1, $_);
166 69         4437 my $kw = shift @words;
167 69 100       213 if (my $meth = $ptab->{lc($kw)}) {
168 41         51 my $r;
169              
170 41 100       117 if (ref($meth) eq 'CODE') {
    50          
171 28         48 ($r, $filename, $line) = $self->${ \$meth }(
  28         64  
172             $parent,
173             $kw,
174             \@words,
175             $orig,
176             $filename,
177             $line,
178             $fh,
179             $ptab
180             );
181             } elsif (ref($meth) eq 'HASH') {
182 13         63 ($r, $filename, $line) = $self->_parser_section(
183             $parent,
184             $kw,
185             \@words,
186             $orig,
187             $filename,
188             $line,
189             $fh,
190             $meth
191             );
192             } else {
193 0         0 croak "Unsupported element type: " . ref($meth);
194             }
195 41 100       226 return (PARSER_OK, $filename, $line) if $r == PARSER_END;
196             } else {
197 28         98 $parent->append_node(
198             new Config::Proxy::Node::Statement(
199             kw => $kw,
200             argv => \@words,
201             orig => $orig,
202             locus => new Text::Locus($filename, $line)
203             )
204             );
205             }
206             }
207 3 50       13 if (exists($ptab->{end})) {
208 0         0 croak "End statement missing in statement started at $start_locus"
209             }
210 3         8 return (PARSER_OK, $filename, $line);
211             }
212              
213             sub _parser_Include {
214 0     0   0 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
215 0 0       0 my $includefile = $words->[0] or
216             croak "$filename:$line: Filename is missing";
217              
218             # FIXME: Make sure filename is quoted
219 0         0 $includefile = $self->dequote($includefile);
220              
221 0 0       0 open(my $ifh, "<", $includefile) or
222             croak "can't open $filename: $!";
223              
224 0         0 my $stmt = new Config::Proxy::Node::Statement(
225             kw => $kw,
226             argv => $words,
227             orig => $orig,
228             locus => new Text::Locus($includefile, 1)
229             );
230              
231 0         0 my ($r) = $self->_parser($stmt, $includefile, 0, $ifh, $ptab);
232 0         0 close($ifh);
233 0         0 $parent->append_node($stmt);
234 0         0 return ($r, $filename, $line);
235             }
236              
237             sub _parser_ACL {
238 0     0   0 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
239              
240 0 0 0     0 if (!$parent->is_root && @$words) {
241 0         0 $parent->append_node(
242             Config::Proxy::Node::Statement->new(
243             kw => $kw,
244             argv => $words,
245             orig => $orig,
246             locus => new Text::Locus($filename, $line)
247             )
248             );
249 0         0 return (PARSER_OK, $filename, $line);
250             }
251              
252             # FIXME: Check $words
253 0         0 my $section = Config::Pound::Node::Section->new(
254             kw => $kw,
255             argv => $words,
256             orig => $orig,
257             locus => new Text::Locus($filename, $line)
258             );
259 0         0 my $start_locus = "$filename:$line";
260 0         0 while (<$fh>) {
261 0         0 $line++;
262 0         0 chomp;
263 0         0 $section->locus->add($filename, $line);
264 0 0       0 if (/^\s+$/) {
    0          
    0          
    0          
265 0         0 $section->append_node(
266             new Config::Proxy::Node::Empty(
267             orig => $_,
268             locus => new Text::Locus($filename, $line)
269             )
270             );
271             } elsif (/^\s*#.*$/) {
272 0         0 $section->append_node(
273             new Config::Proxy::Node::Comment(
274             orig => $_,
275             locus => new Text::Locus($filename, $line)
276             )
277             );
278             } elsif (/^\s*(end)\s*(?#.*)?$/i) {
279 0         0 $section->append_node(
280             new Config::Proxy::Node::Statement(
281             kw => $1,
282             orig => $_,
283             locus => new Text::Locus($filename, $line)
284             )
285             );
286 0         0 $parent->append_node($section);
287 0         0 return (PARSER_OK, $filename, $line);
288             } elsif (/^\s*"(.+?)"\s*(?#.*)?$/) {
289 0         0 $section->append_node(
290             new Config::Pound::Node::IP(
291             kw => $1,
292             orig => $_,
293             locus => new Text::Locus($filename, $line)
294             )
295             );
296             } else {
297 0         0 my $orig = $_;
298 0         0 s/^\s*//;
299 0         0 s/\s+$//;
300 0         0 my @words = parse_line('\s+', 1, $_);
301 0         0 my $kw = shift @words;
302 0         0 $section->append_node(
303             new Config::Proxy::Node::Statement(
304             kw => $kw,
305             argv => \@words,
306             orig => $orig,
307             locus => new Text::Locus($filename, $line)
308             )
309             );
310             }
311             }
312              
313 0         0 croak "missing End in ACL statement started at $start_locus";
314             }
315              
316             sub _parser_Not {
317 7     7   32 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
318              
319 7         101 $orig =~ s/^(\s*$kw)//;
320              
321 7         21 my $sec = new Config::Pound::Node::Section(
322             kw => $kw,
323             orig => $1,
324             locus => new Text::Locus($filename, $line)
325             );
326              
327 7 50       9 if (@{$words} == 0) {
  7         14  
328 0         0 croak "$filename:$line: \"Not\" statement missing arguments";
329             } else {
330 7         18 $kw = shift @{$words};
  7         9  
331 7 100       23 if ($kw =~ /^(match|not)$/i) {
332 5         7 my $meth;
333 5 100       9 if ($kw =~ /^match$/i) {
334 3         5 $meth = '_parser_section'
335             } else {
336 2         3 $meth = '_parser_Not'
337             }
338              
339 5         6 (undef, $filename, $line) = $self->${ \$meth }(
  5         18  
340             $sec,
341             $kw,
342             $words,
343             $orig,
344             $filename,
345             $line,
346             $fh,
347             $ptab
348             );
349 5         9 $sec->locus->add($filename, $line)
350             } else {
351 2         4 my $stmt = new Config::Proxy::Node::Statement(
352             kw => $kw,
353             argv => $words,
354             orig => $orig,
355             locus => new Text::Locus($filename, $line)
356             );
357 2         6 $sec->append_node($stmt);
358             }
359             }
360 7         45 $parent->append_node($sec);
361 7         18 return (PARSER_OK, $filename, $line)
362             }
363              
364             sub _parser_Else {
365 3     3   12 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
366 3         60 my $section = new Config::Pound::Node::Section(
367             kw => $kw,
368             argv => $words,
369             orig => $orig,
370             locus => new Text::Locus($filename, $line)
371             );
372 3         12 $parent->append_node($section);
373 3         14 return (PARSER_OK, $filename, $line);
374             }
375              
376             sub _parser_Rewrite {
377 2     2   8 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
378 2         4 my $r;
379 2         20 ($r, $filename, $line) = $self->_parser_section(
380             $parent,
381             $kw,
382             $words,
383             $orig,
384             $filename,
385             $line,
386             $fh,
387             \%rewrite_section
388             );
389              
390 2         9 my $rwr = $parent->tree(-1);
391 2         14 my $itr = $rwr->iterator(inorder => 1, recursive => 0);
392 2         4 my $branch;
393 2         8 while (defined(my $node = $itr->next)) {
394 14         37 my $kw = lc($node->kw);
395 14 100       49 if ($kw eq 'else') {
    100          
    100          
396 3         9 $branch = $node;
397             } elsif ($kw eq 'end') {
398 2         6 last;
399             } elsif ($branch) {
400 5         16 $branch->append_node($node);
401 5         24 $node->drop(); # FIXME: see mark_dirty in Pound.pm
402             }
403             }
404 2         19 return ($r, $filename, $line);
405             }
406              
407             sub _parser_ConfigText{
408 0     0   0 my ($self, $parent, $kw, $words, $orig, $filename, $line, $fh, $ptab) = @_;
409 0         0 my $section = new Config::Pound::Node::Section(
410             kw => $kw,
411             argv => $words,
412             orig => $orig,
413             locus => new Text::Locus($filename, $line)
414             );
415 0         0 my $start_locus = "$filename:$line";
416 0         0 while (<$fh>) {
417 0         0 $line++;
418 0         0 chomp;
419 0         0 $section->locus->add($filename, $line);
420 0 0       0 if (/^\s*(end)\s*(?#.*)?$/i) {
421 0         0 $section->append_node(
422             new Config::Proxy::Node::Statement(
423             kw => $1,
424             orig => $_,
425             locus => new Text::Locus($filename, $line)
426             )
427             );
428 0         0 $parent->append_node($section);
429 0         0 return (PARSER_OK, $filename, $line)
430             } else {
431 0         0 $section->append_node(
432             new Config::Pound::Node::Verbatim(
433             orig => $_,
434             locus => new Text::Locus($filename, $line)
435             )
436             );
437             }
438             }
439 0         0 croak "missing End in $kw statement started at $start_locus"
440             }
441              
442             sub parse {
443 3     3 0 6 my ($self, %args) = @_;
444 3         6 my $fh;
445 3 50       7 if ($args{fh}) {
446             $fh = $args{fh}
447 0         0 } else {
448 3 50       24 open($fh, '<', $self->filename)
449             or croak "can't open ".$self->filename.": $!";
450             }
451 3         15 $self->reset();
452 3   50     22 $self->_parser($self->tree, $self->filename, $args{line} // 0,
453             $fh, \%top_section);
454 3 50       44 close $fh unless $args{fh};
455 3         59 return $self
456             }
457              
458             sub topmost_not_node {
459 14     14 0 19 my $node = shift;
460 14         15 my $topmost;
461              
462 14         27 $node = $node->parent;
463 14   66     24 while (!$node->parent->is_root &&
      100        
464             $node->parent->is_section &&
465             lc($node->parent->kw) eq 'not') {
466 4         8 $topmost = $node->parent;
467 4         6 $node = $topmost;
468             }
469 14         34 return $topmost;
470             }
471              
472             sub write {
473 4     4 0 1892 my $self = shift;
474 4         9 my $file = shift;
475 4         6 my $fh;
476              
477 4 50       14 if (!defined($file)) {
478 0         0 $file = \*STDOUT
479             }
480 4 100       14 if (ref($file) eq 'GLOB') {
481 1         2 $fh = $file;
482             } else {
483 3 50       46 open($fh, '>', $file) or croak "can't open $file: $!";
484             }
485              
486 4         16 local %_ = @_;
487 4         29 my $itr = $self->iterator(inorder => 1);
488              
489 4         11 my @rws = ([-1, 0]);
490 4         30 while (defined(my $node = $itr->next)) {
491 175         377 my $s = $node->as_string;
492 175 100       440 if ($_{indent}) {
493 69 100       178 if ($node->is_comment) {
494 3 50       9 if ($_{reindent_comments}) {
495 3         21 my $indent = ' ' x ($_{indent} * $node->depth);
496 3         16 $s =~ s/^\s+//;
497 3         8 $s = $indent . $s;
498             }
499             } else {
500             # print STDERR "\n# ".$node->as_string . "; depth ".$node->depth."; correction ", $rws[-1]->[1] . " (".(@rws+0).")\n";
501 66         139 my $depth = $node->depth - $rws[-1]->[1];
502 66 100 100     139 if ($node->is_section) {
    100          
503 21 100       42 if (lc($node->kw) eq 'rewrite') {
    100          
504 2         25 push @rws, [ 0, $rws[-1]->[1] ];
505             } elsif (lc($node->kw) eq 'else') {
506 3 100       11 if (!$rws[-1]->[0]) {
507 2         5 $rws[-1]->[0] = 1;
508 2         5 $rws[-1]->[1]++;
509 2         3 $depth--;
510             }
511             }
512             } elsif ($node->is_statement && lc($node->kw) eq 'end') {
513 16 100 33     33 if ($node->parent->is_section &&
    100 66        
514             $node->parent->kw &&
515             lc($node->parent->kw) eq 'rewrite') {
516 2         5 pop @rws;
517             } elsif (my $topnot = topmost_not_node($node)) {
518             # print STDERR " # pop\n";
519 3         5 pop @rws;
520 3         7 $depth = $topnot->depth - $rws[-1]->[1];
521             } else {
522             # print $fh "# Decr $depth\n";
523 11         17 $depth--;
524             }
525             }
526             # print STDERR "# Depth $depth\n";
527 66         196 my $indent = ' ' x ($_{indent} * $depth);
528 66 50       134 if ($_{tabstop}) {
529 0         0 $s = $indent . $node->kw;
530 0         0 for (my $i = 0; my $arg = $node->arg($i); $i++) {
531 0         0 my $off = 1;
532 0 0       0 if ($i < @{$_{tabstop}}) {
  0         0  
533 0 0       0 if (($off = $_{tabstop}[$i] - length($s)) <= 0) {
534 0         0 $off = 1;
535             }
536             }
537 0         0 $s .= (' ' x $off) . $arg;
538             }
539             } else {
540 66         234 $s =~ s/^\s+//;
541 66         163 $s = $indent . $s;
542             }
543             }
544             }
545 175         407 print $fh $s;
546 175 100 100     375 if ($node->is_section && lc($node->kw) eq 'not') {
547 7         11 my $delta = $rws[-1]->[1];
548 7         30 while (lc($node->kw) eq 'not') {
549 9         19 $node = $itr->next;
550 9         18 ($s = $node->as_string) =~ s/^\s+//;
551 9         22 print $fh " $s";
552 9         19 ++$delta
553             }
554 7 100       14 if ($node->is_section) {
555 5         14 push @rws, [ undef, $delta ];
556             # print STDERR "# Push $rws[-1]->[1]\n";
557             }
558             }
559 175         501 print $fh "\n";
560             }
561              
562 4 100       40 close $fh unless ref($file) eq 'GLOB';
563             }
564              
565             1;
566             __END__