package Apache::HEADRegistry; use Apache::Constants qw(DONE); use Apache::RegistryNG; use strict; @Apache::HEADRegistry::ISA = qw(Apache::RegistryNG); $Apache::HEADRegistry::VERSION = '0.01'; sub new { my ($class, $r) = @_; $r ||= Apache->request; tie *STDOUT, $class, $r; return tied *STDOUT; } sub PRINT { my ($self, @data) = @_; my $r = $self->{r}; # we're emulating mod_cgi, where there is no auto-dereferencing... my $data = join '', @data; my $dlm = "\015?\012"; # a bit borrowed from LWP::UserAgent my ($key, $value); unless ($r->sent_header) { # if we have already sent the headers, no reason to scan the output while((my $header, $data) = split /$dlm/, $data, 2) { # scan the incoming data for headers if ($header && $header =~ m/^(\S+?):\s*(.*)$/) { # if the data looks like a header, add it to the header table ($key, $value) = ($1, $2); last unless $key; $r->cgi_header_out($key, $value); } else { # since we're done with the headers, send them along... $r->send_http_header; $r->sent_header(DONE); last; } } } # if this is a HEAD request, we're done return if $r->header_only; # otherwise, send whatever data we have left to the client $r->write_client($data); } # note that the perltie manpage is wrong - no need to append a newline # verified by #p5p (thanks rafael :) and fixed in bleedperl sub PRINTF { my $self = shift; $self->PRINT(sprintf(shift, @_)); } # BINMODE and CLOSE are both no-ops in Apache.xs sub BINMODE {}; sub CLOSE {}; sub TIEHANDLE { my ($class, $r) = @_; return bless { r => $r }, $class; } 1; __END__ =head1 NAME Apache::HEADRegistry - Apache::Registry drop-in for HEAD requests =head1 SYNOPSIS httpd.conf: PerlModule Apache::HEADRegsitry SetHandler perl-script PerlHandler Apache::HEADRegistry Options +ExecCGI PerlSendHeader On =head1 DESCRIPTION Apache::HEADRegistry is a drop-in for Apache::Registry that properly handles HEAD requests. Currently, Apache::Registry incorrectly handles HEAD requests - it acts as though they are GET requests, returning both the headers and content. So, not only does represent a way in which mod_cgi and Apache::Registry are different, but Apache::Registry is not RFC compliant and causes trouble with some modern browsers. This module attempts to correct the wrong in Apache::Registry by intercepting headers much in the way that mod_perl does, but then respecting the value of $r->header_only. =head1 NOTES Apache::HEADRegistry is a subclass of Apache::RegistryNG, which means that it doesn't behave _exactly_ the same as Apache::Registry. Namely, it uses the filename of the script to determine the unique package namespace, whereas Apache::Registry uses the URI. HEADRegistry also does not do any of the auto-dereferencing in its print() method - if you want that type of thing, then you are obviously relying on the mod_perl API and can therefore check $r->header_only yourself. This module is meant for those who want mod_cgi emulation only. =head1 FEATURES/BUGS The only current bug seems to be for scripts that handle redirects, such as: use CGI; $cgi = CGI->new; print $cgi->redirect ("http://www.foo.com/"); or print "Location: http://www.foo.com/"; What happens is that the default Apache 302 error is displayed instead of just the headers. This is a bug both with Apache::Registry and Apache::HEADRegistry and seems to lie with mod_perl and it's internal messing with the $r->assbackwards flag (but I'm not entirely sure). This module also does not handle write() calls at the moment - if you have a need for that let me know. =head1 SEE ALSO perl(1), mod_perl(3), Apache(3), Apache::Registry(3), Apache::RegistryNG =head1 AUTHOR Geoffrey Young =head1 COPYRIGHT Copyright (c) 2002, Geoffrey Young. All rights reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself. =head1 HISTORY This code is derived in part from examples in the "The mod_perl Developer's Cookbook" For more information, visit http://www.modperlcookbook.org/ It also contains code lifted from various mod_perl internal sources, such as Apache.pm and mod_perl.c, and LWP. Thanks all for being good open source contributors. =cut