Programming :  Student Freelance Forum For Work Experience Builders' (CertificationPoint) The fastest message board... ever.
 
PERL-Redirecting Errors to the Client Instead of error_log
Posted by: adcertpoint (Moderator)
Date: April 29, 2020 04:35PM

Many error conditions result in an exception (or signal -- same thing) which is raised in order to tell the operating system that a condition has arisen which needs more urgent attention than can be given by other means. One of the most familiar ways of raising a signal is to hit Ctrl-C on your terminal's keyboard. The signal interrupts the processor. In the case of Ctrl-C the INT signal is generated and the interrupt is usually trapped by a default signal handler supplied by OS, which causes the operating system to stop the process currently attached to the terminal.

Under mod_perl, a Perl runtime error causes an exception. By default this exception is trapped by default mod_perl handler. The handler logs information about the error (such as the date and time that the error occurred) to error_log. If you want to redirect this information to the client instead of to error_log you have to take the responsibility yourself, by writing your own exception handler to implement this behaviour. See the section "Exception Handling for mod_perl" for more information.

The code examples below can be useful with your own exception handlers as well as with the default handlers.

META: Integrate the 2 sections

The CGI::Carp package implements handlers for signals. To trap (almost) all Perl run-time errors and send the output to the client instead of to Apache's error_log add this line to your script:
use CGI::Carp qw(fatalsToBrowser);

Refer to the CGI::Carp man page for more detailed information.

You can trap individual exceptions: for example you can write custom __DIE__ and __WARN__ signal handlers. The special %SIG hash contains references to signal handlers. The signal handler is just a subroutine, in the example below it is called "mydie". To install the handler we assign a reference to our handler to the appropriate element of the %SIG hash. This causes the signal handler to call mydie(error_message) whenever the die() sub is called as a result of something which happened when our script was executing.

Do not forget the local keyword! If you do, then after the signal handler has been loaded it will be called whenever die() is called by any script executed by the same process. Probably that's not what you want. If it is, you can put the assignment statement in any module, as long as it will be executed at the right time.

Here is an example of a handler which I wrote because I wanted users to know that there was an error, without displaying the error message, but instead email it to me. If the error is caused by user (e.g. uploading image whose size is bigger than the limit I had set) I want to tell them about it. I wrote this handler for the mod_perl environment, but it works correctly when called from the shell. The code shown below is a stripped-down version with additional comments.

The following code must be added to the script:
# Using the local() keyword restricts the scope of the directive to
# the block in which it is found, so this line must be added at the
# right place in the right script. It will not affect other blocks
# unless the local() keyword is removed. Usually you will want the
# directive to affect the entire script, so you just place it near
# the beginning of the file, where the innermost enclosing block is
# the file itself.
local $SIG{__DIE__} = \&mydie;

# The line above assumes that the subroutine "mydie" is in the same script.
# Alternatively you can create a separate module for the error handler.
# If you put the signal handler in a separate module, e.g. Error.pm,
# you must explicitly give the package name to set the handler in your
# script, using a line like this instead of the one above:
local $SIG{__DIE__} = \&Error::mydie;
# again within the script!

# Do not forget the C<local()>, unless you want this signal handler to
# be invoked every time any scripts dies (including events where this
# treatment may be undesirable).

my $max_image_size = 100*1024; # 100k
my $admin_email = 'foo@example.com';

# and the handler itself
# Here is the handler itself:
# The handler is called with a text message in a scalar argument
sub mydie{
my $why = shift;

chomp $why;
my $orig_why = $why; # an ASCII copy for email report

# handle the shell execution case (so we will not get all the HTML)
print("Error: $why\n"winking smiley, exit unless $ENV{MOD_PERL};

my $should_email = 0;
my $message = '';

$why =~ s/[<&>]/"&#".ord($&winking smiley.";"/ge; # entity escape

# Now we need to trap various kinds of errors that come from CGI.pm
# We don't want these errors to be emailed to us, since
# they aren't programmatical errors
if ($orig_why =~ /Client attempted to POST (\d+) bytes/o) {

$message = qq{
You cannot POST messages bigger than
@{[1024*$max_image_size]} bytes.<BR>
You have tried to post $1 bytes<BR>
If you are trying to upload an image, make sure its
size is no bigger than @{[1024*$max_image_size]}
bytes.<P>
Thank you!
};

} elsif ($orig_why =~ /Malformed multipart POST/o) {

$message = qq{
Have you tried to upload an image in the wrong way?<P>
To successfully upload an image you must use a browser that supports
image upload and use the 'Browse' button to select that image.
DO NOT type the path to the image into the upload field.<P>
Thank you!
};

} elsif ($orig_why =~ /closed socket during multipart read/o) {

$message = qq{
Have you pressed a 'STOP' button?<BR>
Please try again!<P>
Thank you!
};

} else {

$message = qq{
<B>You need take no action since
the error report has already been
sent to the webmaster. <BR><P>
<B>Thank you for your patience!</B>
};

$should_email = 1;
}


print qq{Content-type: text/html

<HTML><BODY BGCOLOR="white">
<B>Oops, Something went wrong.</B><P>
$message
</BODY></HTML>};

# send email report if appropriate
if ($should_email){

# import sendmail subs
use Mail ();
# prepare the email error report:
my $subject ="Error Report";
my $body = qq|
An error has happened:

$orig_why

|;

# send error reports to admin
send_mail($admin_email,$admin_email,$subject,$body);
print STDERR "[".scalar localtime()."] [SIGDIE] Sending Error Email\n";
}

# print to error_log so we will know there was an error
print STDERR "[".scalar localtime()."] [SIGDIE] $orig_why \n";

exit 1;
} # end of sub mydie

You may have noticed that I trap the CGI.pm's die() calls here, I don't see any reason why my users should see ugly error messages, but that's the way CGI.pm written. The workaround is to trap them yourself.

Please note that as of version 2.49, CGI.pm provides the cgi_error() method to print the errors and won't die() unless you want it to.

Options: ReplyQuote


Sorry, only registered users may post in this forum.
This forum powered by Phorum.