File Coverage

blib/lib/WebDyne/Err.pm
Criterion Covered Total %
statement 30 107 28.0
branch 0 28 0.0
condition 0 28 0.0
subroutine 10 14 71.4
pod 0 2 0.0
total 40 179 22.3


line stmt bran cond sub pod time code
1             #
2             # This file is part of WebDyne.
3             #
4             # This software is copyright (c) 2026 by Andrew Speer .
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9             # Full license text is available at:
10             #
11             #
12             #
13             package WebDyne::Err;
14              
15              
16             # Compiler Pragma
17             #
18 7     7   43 use strict qw(vars);
  7         14  
  7         370  
19 7     7   41 use vars qw($VERSION);
  7         14  
  7         383  
20 7     7   36 use warnings;
  7         12  
  7         495  
21 7     7   36 no warnings qw(uninitialized);
  7         13  
  7         320  
22              
23              
24             # Webmod Modules.
25             #
26 7     7   35 use WebDyne::Constant;
  7         14  
  7         61  
27 7     7   4048 use WebDyne::Err::Constant;
  7         22  
  7         74  
28 7     7   43 use WebDyne::Util;
  7         13  
  7         83  
29              
30              
31             # External modules
32             #
33 7     7   43 use HTTP::Status qw(:constants is_error);
  7         11  
  7         3234  
34 7     7   278 use File::Spec;
  7         18  
  7         183  
35 7     7   30 use Data::Dumper;
  7         9  
  7         8524  
36             $Data::Dumper::Indent=1;
37              
38              
39             # Version information
40             #
41             $VERSION='2.075';
42              
43              
44             # Debug
45             #
46             0 && debug("%s loaded, version $VERSION", __PACKAGE__);
47              
48              
49             # Package wide vars
50             #
51             my %Package;
52              
53              
54             # Fix issues if mod_perl loads legacy Carp with modern Carp::Heavy
55             #
56             { my $cr=sub {return \@_};
57             foreach my $method (qw(shortmess_real longmess_real shortmess_heavy longmess_heavy)) {
58 0     0     *{"Carp::${method}"}=sub {return @_}
59             unless Carp->can($method);
60             }
61             }
62              
63              
64             # And done
65             #
66             1;
67              
68              
69             #------------------------------------------------------------------------------
70              
71              
72             sub err_html {
73              
74              
75             # Output errors to browser.
76             #
77 0     0 0   my ($self, $errstr)=@_;
78 0           $errstr=sprintf($errstr, @_[2..$#_]);
79              
80              
81             # Debug
82             #
83 0           0 && debug("in error routine self $self, errstr $errstr, caller %s", join(',', (caller(0))[0..3]));
84              
85              
86             # Get errstr from stack if not supplied, or add if it
87             # has been
88             #
89 0 0         if ($errstr) {err($errstr)}
  0            
90             else {
91 0   0       $errstr=errstr() || do {err($_='undefined error from handler'); $_}
92             }
93              
94             #$errstr ? err($errstr) : ($errstr=errstr() || do {err($_='undefined error from handler'); $_});
95 0           0 && debug("final errstr $errstr");
96              
97              
98             # Try to get request handler;
99             #
100 0           my $r;
101 0 0         if ($r=eval {$self->{'_r'}}) {
  0            
102              
103             # Get main request handler in case we are in subrequest
104             #
105 0   0       $r=$r->main() || $r;
106              
107             }
108 0           0 && debug("r $r");
109              
110              
111             # Print errstr and exit immediately if no request object yet, or in error loop - something
112             # is seriously wrong;
113             #
114 0 0         if (!$r) {
115 0           print(errdump());
116 0           CORE::exit 0;
117             }
118              
119              
120             # Try to get CGI object from class, or create if not present - may
121             # not have been initialised before error occured);
122             #
123 0   0       my $cgi_or=$self->{'_CGI'} || CGI::Simple->new($r);
124 0           0 && debug("cgi_or $cgi_or");
125              
126              
127             # Log the error
128             #
129 0           $r->log_error($errstr);
130              
131              
132             # Status must be internal error if not set to something else already
133             #
134 0 0 0       unless ($r->status() && (is_error($r->status()))) {
135 0           $r->status(HTTP_INTERNAL_SERVER_ERROR);
136             }
137              
138              
139             # Do not run any more handlers
140             #
141 0           $r->set_handlers(PerlHandler => undef);
142              
143              
144             # Optionally kill this Apache process afterwards to make sure it does
145             # not behave badly after this error, if that is what the user has
146             # configured
147             #
148 0 0         if ($WEBDYNE_ERROR_EXIT) {
149 0     0     my $cr=sub {CORE::exit()};
  0            
150 0 0         $MP2 ? $r->pool->cleanup_register($cr) : $r->register_cleanup($cr);
151             }
152              
153              
154             # Error can be text or HTML, must be text if in Safe eval mode
155             #
156 0 0 0       if ($WEBDYNE_ERROR_TEXT || $WEBDYNE_EVAL_SAFE || $self->{'_error_handler_run'}++ || !$cgi_or) {
      0        
      0        
157              
158              
159             # Text error, set content type
160             #
161             0 && debug(
162             "using text error (%s:%s:%s:%s) - update $r content_type",
163 0           $WEBDYNE_ERROR_TEXT, $WEBDYNE_EVAL_SAFE, $self->{'_error_handler_run'}, $cgi_or
164             );
165             #$r->content_type('text/plain');
166 0           $r->content_type($WEBDYNE_CONTENT_TYPE_TEXT);
167            
168              
169             # Push error
170             #
171 0           my $err_text=errdump(
172             {
173              
174             'URI' => $r->uri(),
175             #'Line' => scalar $self->data_ar_html_line_no(pop @{$self->{'_data_ar_err'}}),
176             'Line' => scalar $self->data_ar_html_line_no(),
177              
178             });
179              
180              
181             # Clear error stack and $@.
182             #
183 0 0         errclr(); eval {undef} if $@;
  0            
  0            
184              
185              
186             # Print error and return
187             #
188 0 0         $r->send_http_header() if !$MP2;
189 0           $r->print($err_text);
190 0           return &Apache::OK;
191              
192              
193             }
194             else {
195              
196              
197             # Get error parameters, must make copy of stack, data block - they will be erased.
198             #
199 0           0 && debug('using html error');
200 0           my @errstack=@{&errstack()};
  0            
201             my %param=(
202              
203             errstr => $errstr,
204             errstack_ar => \@errstack,
205             err_eval_perl_sr => $self->{'_err_eval_perl_sr'},
206             err_eval_line => $self->{'_err_eval_line'},
207 0           data_ar_err => $self->{'_data_ar_err'},
208              
209             );
210              
211              
212             # Clear error stack and $@ so this render works without errors
213             #
214 0 0         errclr(); eval {undef} if $@;
  0            
  0            
215              
216              
217             # Wrap everything in eval block in case this error was thrown interally by
218             # WebDyne not being able to load/start etc, in which case trying to run it
219             # again won't be helpful
220             #
221 0           my $status;
222 0           eval {
223              
224              
225             # Only compile container once if we can help it
226             #
227 0           local $SIG{__DIE__};
228 0           require WebDyne::Compile;
229 0   0       my $container_ar=(
230              
231             # Don't cache it - only minor penalty to recompile and WEBDYNE_RELOAD=1 breaks error handler
232             # if multiple errors.
233             #$Package{'container_ar'} ||= &WebDyne::Compile::compile(
234             $self->WebDyne::Compile::compile({
235              
236             srce => $WEBDYNE_ERR_TEMPLATE,
237             nofilter => 1
238              
239             })) || return $self->err_html('fatal problem in error handler during compile !');
240              
241              
242             # Get the data portion of the container (meta info not needed) and render. Bit of cheating
243             # to use internal
244             #
245 0           my $data_ar=$container_ar->[$WEBDYNE_CONTAINER_DATA_IX];
246 0           0 && debug("err_html data_ar: %s", Dumper($param{'data_ar'}));
247              
248              
249             # Reset render state and render error page
250             #
251 0           $self->render_reset($data_ar);
252             #my $html_sr=$self->render({
253             ##
254             # data => $data_ar,
255             # param => \%param
256             #
257             # }) || return $self->err_html('fatal problem in error handler during render: %s !', errstr() || 'undefined error');
258 0   0       my $html_sr=$self->render_data_ar(
259              
260             data => $data_ar,
261             param => \%param
262              
263             ) || return $self->err_html('fatal problem in error handler during render: %s !', errstr() || 'undefined error');
264              
265              
266             # Set custom handler
267             #
268 0           $status=$r->status();
269 0           0 && debug("send custom response for status $status on r $r");
270 0           $r->custom_response($status, ${$html_sr});
  0            
271              
272              
273             # Clear error stack again, make sure all is clean before we return.
274             #
275 0 0         errclr(); eval {} if $@;
  0            
276              
277             };
278              
279              
280             # Check if render went OK, if not revert to text - better than
281             # showing nothing ..
282             #
283 0 0 0       if ($@ || !$status) {
284 0           0 && debug("unable to render HTML template, reverting to text");
285 0 0         err($@) if $@;
286 0           err('previous error stack %s', Data::Dumper::Dumper(\@errstack));
287 0           my $webdyne_error_text_save=$WEBDYNE_ERROR_TEXT;
288 0           $WEBDYNE_ERROR_TEXT=1;
289 0           $status=$self->err_html($errstr);
290 0           $WEBDYNE_ERROR_TEXT=$webdyne_error_text_save;
291              
292             }
293              
294             # Return result
295             #
296 0           0 && debug("return status: $status");
297 0           return $status
298              
299             }
300              
301             }
302              
303              
304             sub err_eval {
305              
306             # Special handler for eval errors
307             #
308 0     0 0   my ($self, $message, $perl_sr, $inode)=@_;
309 0           0 && debug("err_eval $message, %s, caller %s", Dumper($perl_sr), Dumper([caller()]));
310              
311              
312             # Try to scrape line from message
313             #
314 0           my ($err_eval_line)=($message=~/WebDyne::${inode}\s+line\s+(\d+)/);
315 0 0         unless ($err_eval_line) {
316              
317             # Only if Devel::Confess installed will this parse work
318             #
319             # Illegal division by zero at (eval 211)[WebDyne::6d8da02b63e4707b80466fda560173a6:5] line 1.
320             #
321 0           ($err_eval_line)=($message=~/\[WebDyne::${inode}:(\d+)\]/);
322             }
323 0           0 && debug("err_eval eval_line:$err_eval_line");
324              
325              
326             # Store away for future ref by error handler
327             #
328 0           $self->{'_err_eval_perl_sr'}=$perl_sr;
329 0           $self->{'_err_eval_line'}=$err_eval_line;
330              
331              
332             # Send message off to main error handler and return
333             #
334 0           return &errsubst($message);
335              
336             }
337