1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
# please insert nothing before this line: -*- mode: cperl; cperl-indent-level: 4; cperl-continued-statement-offset: 4; indent-tabs-mode: nil -*-
use strict;
use warnings FATAL => 'all';
use Apache::Test;
use Apache::TestUtil;
use Apache::TestRequest;
plan tests => 12 * 2 + 3;
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 the most part GET
# and HEAD should behave exactly the same. however, when Apache
# sees a HEAD request with a C-L header of zero it takes special
# action and removes the C-L header. this is done to protect against
# handlers that called r->header_only (which was ok in 1.3 but is
# not in 2.0). So, GET and HEAD behave the same except when the
# content handler (plus filters) end up sending no content. see
# the lengthy comments in ap_http_header_filter in http_protocol.c.
#
# 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);
{
# if the response handler sends no data, and sets no C-L header,
# the client doesn't get C-L header at all.
#
# in 2.0 GET requests get a C-L of zero, while HEAD requests do
# not due to special processing.
my $uri = $location;
my $res = $method->($uri);
my $cl = 0;
my $head_cl = undef;
ok t_cmp $res->code, 200, "$method $uri code";
ok t_cmp ($res->header('Content-Length'),
$method eq 'GET' ? $cl : $head_cl,
"$method $uri C-L header");
ok t_cmp $res->content, "", "$method $uri content";
}
{
# if the response handler sends no data and sets C-L header,
# the client should receive the set content length. in 2.1
# this is the way it happens. see protocol.c -r1.150 -r1.151
#
# in 2.0 the client doesn't get C-L header for HEAD requests
# due to special processing, and GET requests get a calculated
# C-L of zero.
my $uri = "$location?set_content_length";
my $res = $method->($uri);
my $cl = 0;
my $head_cl;
## 2.2.1, 2.0.56, 2.0.57 were not released
## but we use the versions the changes went into
## to protect against wierd SVN checkout building.
## XXX: I'm starting to think this test is more
## trouble then its worth.
if (have_min_apache_version("2.2.1")) {
$head_cl = 25;
}
elsif (have_min_apache_version("2.2.0")) {
# $head_cl = undef; # avoid warnings
}
elsif (have_min_apache_version("2.0.56")) {
$head_cl = 25;
}
else {
# $head_cl = undef; # avoid warnings
}
ok t_cmp $res->code, 200, "$method $uri code";
ok t_cmp ($res->header('Content-Length'),
$method eq 'GET' ? $cl : $head_cl,
"$method $uri C-L header");
ok t_cmp $res->content, "", "$method $uri content";
}
{
# if the response handler sends data, and sets no C-L header,
# the client doesn't get C-L header.
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";
my $content = $method eq 'GET' ? 'This is a response string' : '';
ok t_cmp $res->content, $content, "$method $uri content";
}
{
# 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?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";
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";
}
|