| blib/lib/CGI/ExceptionManager/StackTrace.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 58 | 58 | 100.0 |
| branch | 19 | 24 | 79.1 |
| condition | 4 | 8 | 50.0 |
| subroutine | 8 | 8 | 100.0 |
| pod | 0 | 3 | 0.0 |
| total | 89 | 101 | 88.1 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package CGI::ExceptionManager::StackTrace; | ||||||
| 2 | 3 | 3 | 16 | use strict; | |||
| 3 | 7 | ||||||
| 3 | 122 | ||||||
| 3 | 3 | 3 | 17 | use warnings; | |||
| 3 | 5 | ||||||
| 3 | 2645 | ||||||
| 4 | |||||||
| 5 | # from MENTA and NanoA | ||||||
| 6 | |||||||
| 7 | sub _escape_html { | ||||||
| 8 | 80 | 80 | 137 | my $str = shift; | |||
| 9 | 80 | 135 | $str =~ s/&/&/g; | ||||
| 10 | 80 | 312 | $str =~ s/>/>/g; | ||||
| 11 | 80 | 169 | $str =~ s/</g; | ||||
| 12 | 80 | 124 | $str =~ s/"/"/g; | ||||
| 13 | 80 | 136 | $str =~ s/'/'/g; | ||||
| 14 | 80 | 672 | return $str; | ||||
| 15 | } | ||||||
| 16 | |||||||
| 17 | sub new { | ||||||
| 18 | 4 | 4 | 0 | 72 | my ($klass, $message) = @_; | ||
| 19 | 4 | 9 | my @trace; | ||||
| 20 | |||||||
| 21 | 4 | 41 | for (my $i = 1; my ($package, $file, $line) = caller($i); $i++) { | ||||
| 22 | 17 | 52 | push @trace, { | ||||
| 23 | file => $file, | ||||||
| 24 | line => $line, | ||||||
| 25 | func => undef, | ||||||
| 26 | }; | ||||||
| 27 | 17 | 100 | 126 | if (my @c = caller($i + 1)) { | |||
| 28 | 13 | 50 | 116 | $trace[-1]->{func} = $c[3] | |||
| 29 | if $c[3]; | ||||||
| 30 | } | ||||||
| 31 | } | ||||||
| 32 | 4 | 100 | 66 | 81 | if ($message =~ / at ([^ ]+) line (\d+)/ | ||
| 33 | |||||||
| 33 | && ($1 ne $trace[0]->{file} || $2 != $trace[0]->{line})) { | ||||||
| 34 | 1 | 7 | unshift @trace, { | ||||
| 35 | file => $1, | ||||||
| 36 | line => $2, | ||||||
| 37 | }; | ||||||
| 38 | } | ||||||
| 39 | |||||||
| 40 | bless { | ||||||
| 41 | 4 | 27 | message => $message, | ||||
| 42 | trace => \@trace, | ||||||
| 43 | }, $klass; | ||||||
| 44 | } | ||||||
| 45 | |||||||
| 46 | sub _build_context { | ||||||
| 47 | 9 | 9 | 17 | my ($file, $linenum) = @_; | |||
| 48 | 9 | 11 | my $code; | ||||
| 49 | 9 | 50 | 273 | if (-f $file) { | |||
| 50 | 9 | 21 | my $start = $linenum - 3; | ||||
| 51 | 9 | 18 | my $end = $linenum + 3; | ||||
| 52 | 9 | 50 | 22 | $start = $start < 1 ? 1 : $start; | |||
| 53 | 2 | 50 | 2 | 13 | open my $fh, '<:encoding(utf8)', $file | ||
| 2 | 9 | ||||||
| 2 | 18 | ||||||
| 9 | 410 | ||||||
| 54 | or die "cannot open $file:$!"; | ||||||
| 55 | 9 | 62611 | my $cur_line = 0; | ||||
| 56 | 9 | 602 | while (my $line = <$fh>) { | ||||
| 57 | 232 | 734 | ++$cur_line; | ||||
| 58 | 232 | 100 | 474 | last if $cur_line > $end; | |||
| 59 | 223 | 100 | 702 | next if $cur_line < $start; | |||
| 60 | 63 | 119 | $line =~ s|\t| |g; | ||||
| 61 | 63 | 100 | 215 | my @tag = $cur_line == $linenum | |||
| 62 | ? (q{}, '') | ||||||
| 63 | : ('', ''); | ||||||
| 64 | 63 | 419 | $code .= sprintf( | ||||
| 65 | '%s%5d: %s%s', $tag[0], $cur_line, _escape_html($line), | ||||||
| 66 | $tag[1], | ||||||
| 67 | ); | ||||||
| 68 | } | ||||||
| 69 | 9 | 283 | close $file; | ||||
| 70 | } | ||||||
| 71 | 9 | 109 | return $code; | ||||
| 72 | } | ||||||
| 73 | |||||||
| 74 | sub as_html { | ||||||
| 75 | 2 | 2 | 0 | 7 | my ($err, %args) = @_; | ||
| 76 | 2 | 7 | my $msg = _escape_html($err->{message}); | ||||
| 77 | 2 | 30 | my $out = qq{500 Internal Server Error${msg}
|
||||
| 78 | 2 | 26 | for my $stack (@{$err->{trace}}) { | ||||
| 2 | 7 | ||||||
| 79 | 9 | 100 | 50 | 59 | $out .= join( | ||
| 50 | |||||||
| 80 | '', | ||||||
| 81 | ' |
||||||
| 82 | $stack->{func} ? _escape_html("in $stack->{func}") : '', | ||||||
| 83 | ' at ', | ||||||
| 84 | $stack->{file} ? _escape_html($stack->{file}) : '', | ||||||
| 85 | ' line ', | ||||||
| 86 | $stack->{line}, | ||||||
| 87 | q( |
||||||
| 88 | _build_context($stack->{file}, $stack->{line}) || '', | ||||||
| 89 | q(), | ||||||
| 90 | ); | ||||||
| 91 | } | ||||||
| 92 | 2 | 12 | $out .= qq{ Powered by $args{powered_by} }; |
||||
| 93 | 2 | 16 | $out; | ||||
| 94 | } | ||||||
| 95 | |||||||
| 96 | sub output { | ||||||
| 97 | 3 | 3 | 0 | 13 | my ($err, %args) = @_; | ||
| 98 | |||||||
| 99 | 3 | 25 | warn $err->{message}; | ||||
| 100 | |||||||
| 101 | 3 | 65 | print "Status: 500\r\n"; | ||||
| 102 | 3 | 36 | print "Content-type: text/html; charset=utf-8\r\n"; | ||||
| 103 | 3 | 34 | print "\r\n"; | ||||
| 104 | |||||||
| 105 | 3 | 100 | 42 | my $body = $args{renderer} ? $args{renderer}->($err, %args) : $err->as_html(%args); | |||
| 106 | 3 | 17 | utf8::encode($body); | ||||
| 107 | 3 | 20 | print $body; | ||||
| 108 | } | ||||||
| 109 | |||||||
| 110 | 1; |