File Coverage

blib/lib/CGI/Carp/Throw.pm
Criterion Covered Total %
statement 35 105 33.3
branch 3 44 6.8
condition 1 26 3.8
subroutine 9 16 56.2
pod 2 4 50.0
total 50 195 25.6


line stmt bran cond sub pod time code
1             package CGI::Carp::Throw;
2              
3             #####################################################################
4             # CGI::Carp::Throw
5             #
6             # Provide the ability to represent thrown exceptions as user oriented
7             # messages rather than obvious error messages with technical tracing
8             # information without losing any of the capabilities for providing
9             # error tracing from CGI::Carp.
10             #
11             #####################################################################
12              
13 1     1   70259 use strict;
  1         8  
  1         77  
14 1     1   113 use warnings;
  1         5  
  1         94  
15              
16 1     1   42 use 5.006002;
  1         19  
  1         108  
17              
18             our $VERSION = '0.04';
19              
20 1     1   10 use Exporter;
  1         3  
  1         347  
21             # using !/ToBrowser/ on import doesn't work
22             use CGI::Carp (
23 11         40 @CGI::Carp::EXPORT,
24 1         4 (grep { ! /name=|^wrap$|ToBrowser/ } @CGI::Carp::EXPORT_OK)
25 1     1   1743 );
  1         10027  
26              
27 1     1   202 use base qw(Exporter);
  1         2  
  1         1389  
28              
29             our @EXPORT = (qw(
30             throw_browser
31             ), @CGI::Carp::EXPORT);
32              
33             our @EXPORT_OK = (qw(
34             throw_browser_cloaked throw_format_sub
35             ), @CGI::Carp::EXPORT_OK);
36              
37             our %EXPORT_TAGS = (
38             'all' => [ qw(
39             throw_browser throw_browser_cloaked throw_format_sub
40             ), @CGI::Carp::EXPORT, (grep { ! /\^name/ } @CGI::Carp::EXPORT_OK) ],
41             'carp_browser' => [ qw(
42             fatalsToBrowser warningsToBrowser throw_browser
43             ) ]
44             );
45              
46             *CGI::Carp::Throw::warningsToBrowser = *CGI::Carp::warningsToBrowser;
47              
48             my $final_warn_browser;
49              
50             #####################################################################
51             # Need to call CGI::Carp's import in a controlled manner and with
52             # a controlled environment.
53             #
54             # More complicated than I would like but guessing it's reasonably
55             # robust.
56             #####################################################################
57             sub import {
58 1     1   9 my $pkg = shift;
59              
60             # this section mostly taken from CGI::Carp
61 1         2 my @routines = grep { ! /^(?:name|:)/ } (@_, @EXPORT);
  4         11  
62 1         3 my($oldlevel) = $Exporter::ExportLevel;
63 1         1 $Exporter::ExportLevel = 1;
64 1         56 Exporter::import($pkg,@routines);
65 1         13 $Exporter::ExportLevel = $oldlevel;
66            
67             # already exported CGI:Carp methods but need to make sure
68             # other CGI::Carp import/Exporter functionality sees its arguments
69 0 0 0     0 my @forward_args = grep
70 1         2 { /warningsToBrowser/ or not ($CGI::Carp::Throw::{ $_ } or /^:/) }
71             @_;
72              
73 1 50       5 if (grep { /:(?:DEFAULT|carp_browser)/i } @_) {
  0         0  
74 0         0 $final_warn_browser = 1;
75 0         0 foreach my $to_brow (qw(fatalsToBrowser warningsToBrowser)) {
76 0         0 push @forward_args, $to_brow
77 0 0       0 unless (grep { /^$to_brow$/ } @forward_args);
78             }
79             }
80            
81             # compatibility with old CGI::Carp
82 1 50 33     15 if ($CGI::Carp::VERSION =~ /(\d*\.?\d*)/ and $1 < 1.24) {
83 0         0 @forward_args = grep { ! /^name=/ } @forward_args
  0         0  
84             }
85              
86             # be a bit careful what we might (re?)import to Throw module
87 1         2 local @CGI::Carp::EXPORT = ();
88 1         3 CGI::Carp::import($pkg, @forward_args);
89             }
90              
91             my $throw_cloaked;
92              
93             #####################################################################
94             # Do a little bit of message formatting where important.
95             # Basically get rid of some lines of confess information that reflect
96             # internal machinery and might be confusing and add a package marker.
97             #
98             # Add and tags if they appear to be missing.
99             #####################################################################
100             sub massage_mess {
101 0     0 0   my $mess = shift;
102              
103 0 0         unless ($throw_cloaked) {
104 0           my $confess_mess = CGI::Carp::_longmess;
105 0           $confess_mess =~ s/.*CGI::Carp(?!::Throw::)(?:.*?)line\s+\d*\s*//s;
106 0           $confess_mess =~ s/\s*CGI::Carp::Throw::_throw(?:.*?)line\s+\d*\s*?\n//;
107             # make package a variable
108 0           $mess .= '";
109             }
110            
111 0 0         unless ($mess =~ /<\s*html\b/i) {
112 0 0         unless ($mess =~ /<\s*body\b/i) {
113 0           $mess = "\n\n$mess\n\n";
114             }
115 0 0         unless ($mess =~ /<\s*head\b/i) {
116 0           $mess = "\nCGI::Carp::Throw page.\n$mess";
117             }
118 0           $mess = "\n$mess\n\n";
119             }
120              
121 0           return $mess;
122             }
123              
124              
125             #####################################################################
126             # Lifted in large part from CGI::Carp
127             #####################################################################
128             sub die_msg_io {
129 0     0 0   my $mess = massage_mess(shift);
130              
131 0           my $mod_perl = exists $ENV{MOD_PERL};
132 0 0         if ($mod_perl) {
133 0           my $r;
134 0 0 0       if ($ENV{MOD_PERL_API_VERSION} && $ENV{MOD_PERL_API_VERSION} == 2) {
135 0           $mod_perl = 2;
136 0           require Apache2::RequestRec;
137 0           require Apache2::RequestIO;
138 0           require Apache2::RequestUtil;
139 0           require APR::Pool;
140 0           require ModPerl::Util;
141 0           require Apache2::Response;
142 0           $r = Apache2::RequestUtil->request;
143             }
144             else {
145 0           $r = Apache->request;
146             }
147             # If bytes have already been sent, then
148             # we print the message out directly.
149             # Otherwise we make a custom error
150             # handler to produce the doc for us.
151 0 0         if ($r->bytes_sent) {
152 0           $r->print($mess);
153 0 0         $mod_perl == 2 ? ModPerl::Util::exit(0) : $r->exit;
154             } else {
155             # MSIE won't display a custom 500 response unless it is >512 bytes!
156 0 0         if ($ENV{HTTP_USER_AGENT} =~ /MSIE/) {
157 0           $mess = "\n$mess";
158             }
159 0           $r->custom_response(500,$mess);
160             }
161             } else {
162 0           my $bytes_written = eval{tell STDOUT};
  0            
163 0 0 0       if (defined $bytes_written && $bytes_written > 0) {
164 0           print STDOUT $mess;
165             }
166             else {
167 0           print STDOUT "Content-type: text/html\n\n";
168 0           print STDOUT $mess;
169             }
170             }
171             }
172              
173             my $throw_format_fref;
174              
175             #####################################################################
176             # Set / retrieve the throw_format_sub class attribute
177             #
178             # throw_format_sub class attribute is a user supplied routine to
179             # format error messages in some format, probably using template
180             # technology, resulting in an appearance compatible with a web site.
181             #####################################################################
182             sub throw_format_sub {
183            
184 0 0   0 1   if (@_) {
185 0           my $new_fref = shift;
186            
187 0 0 0       croak 'throw_format_sub setting must be code reference'
      0        
188             if ( $new_fref and
189             ( (not ref($new_fref)) or
190             ref($new_fref) !~ /CODE/i
191             )
192             );
193            
194 0           $throw_format_fref = $new_fref;
195             }
196            
197 0           return $throw_format_fref;
198             }
199              
200             my $old_fatals_to_browser = \&CGI::Carp::fatalsToBrowser;
201              
202             {
203 1     1   6 no warnings 'redefine';
  1         3  
  1         514  
204              
205             #####################################################################
206             # Partially replace fatalsToBrowser so that it gets called
207             # unless the exception came from one of our throw_browser routines.
208             #####################################################################
209             *CGI::Carp::fatalsToBrowser = sub {
210 0     0     my $msg = shift;
211            
212 0           my($pack,undef,undef,$sub) = caller(2);
213 0 0 0       if (($sub || '') =~ /::_throw_browser$/) {
214 0           die_msg_io($msg);
215             }
216             else {
217 0           $old_fatals_to_browser->($msg)
218             }
219             };
220             }
221              
222             #####################################################################
223             # Shared throw browser logic for cloaked and non-cloaked variants.
224             #
225             # If you called this you wanted CGI::Carp wrapping (unless you're in
226             # an eval) so turn that on. If a formatting routine was specified
227             # call it and die with its message. Otherwise die and let the
228             # fatalsToBrowser replacement take over.
229             #####################################################################
230             sub _throw_browser {
231 0 0 0 0     unless ($CGI::Carp::WRAP or CGI::Carp::ineval) {
232 0           $CGI::Carp::WRAP++;
233             }
234            
235 0 0         if ($throw_format_fref) {
236 0           my $die_msg = $throw_format_fref->(@_);
237 0 0         $die_msg =~ s/([^\n])$/$1\n/ if $die_msg;
238 0           die $die_msg;
239             }
240             else {
241 0 0 0       if ($_[-1] and $_[-1] !~ /\n$/) {
242 0           die @_, "\n";
243             }
244             else {
245 0           die @_;
246             }
247             }
248             }
249              
250             #####################################################################
251             # Standard throw browser. "Uncloaked" which includes stack trace
252             # HTML comment.
253             #####################################################################
254             sub throw_browser {
255 0     0     undef $throw_cloaked;
256 0           _throw_browser(@_);
257             }
258              
259             #####################################################################
260             # Standard throw browser. "Cloaked" to hide stack trace HTML comment.
261             #####################################################################
262             sub throw_browser_cloaked {
263 0     0 1   $throw_cloaked = 1;
264 0           _throw_browser(@_);
265             }
266              
267             END {
268 1 50   1   867153 CGI::Carp::warningsToBrowser(1) if $final_warn_browser;
269             }
270              
271             1;
272             __END__