stas 2004/08/04 11:30:54
Modified: t/apache content_length_header.t t/response/TestApache content_length_header.pm Log: 1) improve the discussion based on input from Geoff 2) restore the explicit HEAD test where we want to get C-L, but no body is sent (this time using $r->rflush to force an early headers sending) Revision Changes Path 1.2 +58 -20 modperl-2.0/t/apache/content_length_header.t Index: content_length_header.t =================================================================== RCS file: /home/cvs/modperl-2.0/t/apache/content_length_header.t,v retrieving revision 1.1 retrieving revision 1.2 diff -u -u -r1.1 -r1.2 --- content_length_header.t 2 Aug 2004 18:51:25 -0000 1.1 +++ content_length_header.t 4 Aug 2004 18:30:54 -0000 1.2 @@ -1,29 +1,26 @@ use strict; use warnings FATAL => 'all'; -# testing nuances of the HEAD request (e.g. when C-L header makes it -# through) -# -# because apache proclaims itself governor of the C-L header via -# the C-L filter, the important thing to test here is not when -# a C-L header is allowed to pass, but rather whether GET and HEAD -# behave the same wrt C-L under varying circumstances. -# for more discussion on why it is important to get HEAD requests -# right, see these threads from the mod_perl list -# http://marc.theaimsgroup.com/?l=apache-modperl&m=108647669726915&w=2 -# http://marc.theaimsgroup.com/?t=109122984600001&r=1&w=2 -# as well as this bug report from mozilla, which shows how they -# are using HEAD requests in the wild -# http://bugzilla.mozilla.org/show_bug.cgi?id=245447 - use Apache::Test; use Apache::TestUtil; use Apache::TestRequest; -plan tests => 12 * 2, todo => [2,5]; +plan tests => 12 * 2 + 3, todo => [2,5]; my $location = "/TestApache__content_length_header"; +# 1. because Apache proclaims itself governor of the C-L header via +# the C-L filter (ap_content_length_filter at +# httpd-2.0/server/protocol.c), test whether GET and HEAD behave the +# same wrt C-L under varying circumstances. for more discussion on +# why it is important to get HEAD requests right, see these threads +# from the mod_perl list +# http://marc.theaimsgroup.com/?l=apache-modperl&m=108647669726915&w=2 +# http://marc.theaimsgroup.com/?t=109122984600001&r=1&w=2 +# as well as this bug report from mozilla, which shows how they +# are using HEAD requests in the wild +# http://bugzilla.mozilla.org/show_bug.cgi?id=245447 + foreach my $method qw(GET HEAD) { no strict qw(refs); @@ -34,7 +31,8 @@ my $uri = $location; my $res = $method->($uri); ok t_cmp $res->code, 200, "$method $uri code"; - ok t_cmp $res->header('Content-Length'), undef, "$method $uri C-L header"; + ok t_cmp $res->header('Content-Length'), undef, + "$method $uri C-L header"; ok t_cmp $res->content, "", "$method $uri content"; } @@ -44,7 +42,8 @@ my $uri = "$location?set_content_length"; my $res = $method->($uri); ok t_cmp $res->code, 200, "$method $uri code"; - ok t_cmp $res->header('Content-Length'), undef, "$method $uri C-L header"; + ok t_cmp $res->header('Content-Length'), undef, + "$method $uri C-L header"; ok t_cmp $res->content, "", "$method $uri content"; } @@ -54,7 +53,8 @@ my $uri = "$location?send_body"; my $res = $method->($uri); ok t_cmp $res->code, 200, "$method $uri code"; - ok t_cmp $res->header('Content-Length'), undef, "$method $uri C-L header"; + ok t_cmp $res->header('Content-Length'), undef, + "$method $uri C-L header"; my $content = $method eq 'GET' ? 'This is a response string' : ''; ok t_cmp $res->content, $content, "$method $uri content"; @@ -66,9 +66,47 @@ my $uri = "$location?send_body+set_content_length"; my $res = $method->($uri); ok t_cmp $res->code, 200, "$method $uri code"; - ok t_cmp $res->header('Content-Length'), 25, "$method $uri C-L header"; + ok t_cmp $res->header('Content-Length'), 25, + "$method $uri C-L header"; my $content = $method eq 'GET' ? 'This is a response string' : ''; ok t_cmp $res->content, $content, "$method $uri content"; } +} + +# 2. even though the spec says that content handlers should send an +# identical response for GET and HEAD requests, some folks try to +# avoid the overhead of generating the response body, which Apache is +# going to discard anyway for HEAD requests. The following discussion +# assumes that we deal with a HEAD request. +# +# When Apache sees EOS and no headers and no response body were sent, +# ap_content_length_filter (httpd-2.0/server/protocol.c) sets C-L to +# 0. Later on ap_http_header_filter +# (httpd-2.0/modules/http/http_protocol.c) removes the C-L header for +# the HEAD requests +# +# the workaround is to force the sending of the response headers, +# before EOS was sent. The simplest solution is to use rflush(): +# +# if ($r->header_only) { # HEAD +# $body_len = calculate_body_len(); +# $r->set_content_length($body_len); +# $r->rflush; +# } +# else { # GET +# # generate and send the body +# } +# +# now if the handler sets the C-L header it'll be delivered to the +# client unmodified. + +{ + # if the response handler sends data (e.g. one char string), and + # sets C-L header, the client gets the C-L header + my $uri = "$location?head_no_body+set_content_length"; + my $res = HEAD $uri; + ok t_cmp $res->code, 200, "HEAD $uri code"; + ok t_cmp $res->header('Content-Length'), 25, "HEAD $uri C-L header"; + ok t_cmp $res->content, '', "HEAD $uri content"; } 1.2 +8 -3 modperl-2.0/t/response/TestApache/content_length_header.pm Index: content_length_header.pm =================================================================== RCS file: /home/cvs/modperl-2.0/t/response/TestApache/content_length_header.pm,v retrieving revision 1.1 retrieving revision 1.2 diff -u -u -r1.1 -r1.2 --- content_length_header.pm 2 Aug 2004 18:51:25 -0000 1.1 +++ content_length_header.pm 4 Aug 2004 18:30:54 -0000 1.2 @@ -1,6 +1,6 @@ package TestApache::content_length_header; -# see the client for the comments +# see the client t/apache/content_length_header.t for the comments use strict; use warnings FATAL => 'all'; @@ -25,9 +25,14 @@ } if ($args =~ /send_body/) { - # really could send just about anything, since Apache discards - # the response body on HEAD requests $r->print($body); + } + + if ($args =~ /head_no_body/) { + if ($r->header_only) { + # see #2 in the discussion in the client + $r->rflush; + } } Apache::OK;