jptxs has asked for the wisdom of the Perl Monks concerning the following question:

I have a small sub that prints a bunch of info from caller and DBI error info to the browser when and error occurs. Sometimes, since I use this as a wrapper for all my usual calls to die, I end up calling it before I would have normally printed any http header. I want to know if there's a way to check and see if a header has already been printed or if I need to print one. Can that be done? Madly reading as we speak and finding nothing...

"A man's maturity -- consists in having found again the seriousness on +e had as a child, at play." --Nietzsch +e

Replies are listed 'Best First'.
(Ovid) Re: Did I already print a header?
by Ovid (Cardinal) on Nov 30, 2000 at 00:38 UTC
    If you're using the object-oriented version of CGI.pm, you can set $CGI::HEADERS_ONCE++ before instantiating the CGI object to suppress multiple headers. Then, just print headers with impunity (with the caveat that if you are passing cookies or redirect headers or other things like that which could conceivably change user's session, you'll lose them if they're not printed in the first header). This may work with the function oriented version, but I haven't tested it.

    The only way I know of to actually find out if the header has been printed to to examine the CGI object itself.

    $ perl -MData::Dumper -e "use CGI; my $q = CGI->new;print $q->header; +print Dumper($q)" Content-Type: text/html; charset=ISO-8859-1 $VAR1 = bless( { '.header_printed' => '1', '.charset' => 'ISO-8859-1', '.parameters' => [], '.fieldnames' => {} }, 'CGI' );
    As you can see, the object tracks if the header has been printed, but that's not a good thing to rely on as you shouldn't be messing with the internals. The following will test for a printed header:
    use CGI; my $q = new CGI; # do a whole bunch of stuff print "header printed!" if exists $q->{'.header_printed'};
    Again, that will work, but I don't recommend it. If anyone knows if CGI has a direct method for accessing that information, chime in!

    Cheers,
    Ovid

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      Any idea where I can find doc on this feature? Searching through perldoc and I don't see it anywhere...nor does /HEADERS

      "A man's maturity -- consists in having found again the seriousness on +e had as a child, at play." --Nietzsch +e
        As far as I know, this is not documented in Perldoc or in the CGI POD. In fact, I don't know how I found out about it. It's just something I picked up. You can see how it works by examing the CGI.pm source code, but bring along some aspirin :)

        I even went out to Lincoln Stein's home page and I can't find a reference there in his official docs. Sorry :(

        Cheers,
        Ovid

        Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Did I already print a header?
by Blue (Hermit) on Nov 30, 2000 at 01:03 UTC
    Perhaps you may want to use CGI::Carp, which will print out a minimal header if you don't have one already. A code snippet taken directly from perlman:CGI::Carp that might be applicable is:
    use CGI::Carp qw(fatalsToBrowser set_message); BEGIN { sub handle_errors { my $msg = shift; print "<h1>Oh gosh</h1>"; print "Got an error: $msg"; } set_message(\&handle_errors); }
    It looks like you could put your info from caller and DBI in the handle_errors subroutine, and then die works normal, no need to put it in a wrapper.

    =Blue
    ...you might be eaten by a grue...

      CGI::Carp has no idea if a header has been printed already. It always prints a new header.

      As far as I know, there's no way to tell if something has already been sent down standard output. You'll just need to have things which cooperate. {grin}

      -- Randal L. Schwartz, Perl hacker

        Tom Christensen offers a good way to filter in the Perl Cookbook in chapter 16.5 (Filtering Your Own Output) by forking open on your own STDOUT and then having the child process filter STDIN to STDOUT.

        #!/usr/bin/perl -w use strict; use CGI; oneheader(); my $q = new CGI; print $q->header; print $q->header; close(STDOUT); exit; sub oneheader { my $pid; my $seen = 0; return if $pid = open(STDOUT, "|-"); die "cannot fork: $!" unless defined $pid; while(<STDIN>) { if(m/^Content-Type/) { print unless $seen; $seen = 1; } else { print; } } exit; }
        Which produces:
        (offline mode: enter name=value pairs on standard input)
        Content-Type: text/html
        
        
        
        (yep, thats two blank lines)

        Another method is to tie a file handle and select it. This requires that you play nice and don't try to fool it by printing something in multiple chunks. This will also fail if CGI::Carp dosn't print to the selected filehandle but rather STDOUT directly. Please forgive me for not doing the return value from PRINT nicely and failing to implement PRINTF and WRITE.

        #!/usr/bin/perl -w use strict; use CGI; tie *FILTER, "OneHeader"; my $q = new CGI; select FILTER; print $q->header; print $q->header; print "Foo!\n"; exit; package OneHeader; sub TIEHANDLE { my $class = shift; my $me = 0; bless \$me, $class; } sub PRINT { my $self = shift; foreach my $item (@_) { if($item =~ m/^Content-Type/) { if(not $$self) { $$self = 1; print STDOUT $item; } } else { print STDOUT $item; } } 1; } 1;

        The output of this method is:

        (offline mode: enter name=value pairs on standard input)
        Content-Type: text/html
        
        Foo!
        

        Just looked at the code for CGI::Carp and it always specifies the filehandle that it prints to. Thus the tie method will require additional twists to get it to work. Still, the essence is there.

Re: Did I already print a header?
by extremely (Priest) on Nov 30, 2000 at 04:33 UTC
    Gah, you just aren't thinking outside the box today. =) Set a GLOBAL FLAG. There is occasionally a use for them, you know. =P

    --
    $you = new YOU;
    honk() if $you->love(perl)

Re: Did I already print a header?
by nop (Hermit) on Dec 01, 2000 at 00:48 UTC
    Along the lines of using a global, I cram my own flag right into the cgi object.

    Yeah, probably not the best idea, as my variable just might clobber some internal CGI variable, but, hey, it works...

    So whenever I start a new page, anywhere within my system of cooperating CGI apps, I note the header was sent:
    print $q->header(), $q->start_html(-title=>"R3: edit ads", -style=>{-code=>&css()}); $q->{header_was_sent}++;
    And then in my error code (same code shared by all the scripts in the system), I can see if I need a header or not.

    Since this on an intranet, and everyone using the apps is trusted and more-or-less SQL and database savvy, I send lots of info back to the user in the error call. (This would be a really bad idea for a public internet app!!!) Here's my error code:
    sub error { my (@msgs) = @_; my $firstline = shift(@msgs); if (!$q->{header_was_sent}) { print $q->header(), $q->start_html(-title=>"R3: error", -style=>{-code=>&css()}); } print "<br><marquee><font class=\"error\"> Need help? Something busted? + Confused? Email the error message below to XXXXXXXXXXXXXXX </font>< +/marquee><br>", "<h1 class=\"error\"> Error: $firstline </h1><h2 class=\"error\">" +, scalar(localtime), "<br>", join ("<br>",@msgs), "</h2>\n"; print $q->end_html; exit(0); }
    The marquee tag is annoying, but it gets noticed.

    When I trap DBI errors, I send the error routine lots of information to help the user figure out what went wrong, including the sql itself and the DBI error string...
    my @inqs = @{$dbh_g->selectcol_arrayref($sql,undef,$adid)} or &error +("Bad selectcol in get_inqs", $sql, $dbh_g->errstr);
    Again, this level of openness is not suitable for open web!