stas 2004/03/02 22:03:11
Modified: src/modules/perl modperl_cgi.c modperl_cgi.h
modperl_filter.c
t/apache send_cgi_header.t
t/response/TestApache send_cgi_header.pm
xs/Apache/Response Apache__Response.h
xs/tables/current/ModPerl FunctionTable.pm
. Changes
Added: ModPerl-Registry/t bin_resp.t
ModPerl-Registry/t/cgi-bin bin_resp_start_0.pl
Log:
Handle correctly the situation when response HTTP headers are printed
from the handler and the response body starts with \000, which is the
case with some images like .ico + updated and new tests
Revision Changes Path
1.3 +39 -2 modperl-2.0/src/modules/perl/modperl_cgi.c
Index: modperl_cgi.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_cgi.c,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- modperl_cgi.c 29 Jun 2002 20:38:33 -0000 1.2
+++ modperl_cgi.c 3 Mar 2004 06:03:10 -0000 1.3
@@ -1,17 +1,54 @@
#include "mod_perl.h"
MP_INLINE int modperl_cgi_header_parse(request_rec *r, char *buffer,
- const char **bodytext)
+ int *len, const char **body)
{
int status;
int termarg;
const char *location;
+ const char *tmp;
+ int tlen, newln;
if (!buffer) {
return DECLINED;
}
- status = ap_scan_script_header_err_strs(r, NULL, bodytext,
+ /* ap_scan_script_header_err_strs won't handle correctly binary
+ * data following the headers, e.g. when the terminating /\n\r?\n/
+ * is followed by \0\0 which is a part of the response
+ * body. Therefore we need to separate the headers from the body
+ * and not rely on ap_scan_script_header_err_strs to do that for
+ * us.
+ */
+ tmp = buffer;
+ newln = 0;
+ tlen = *len;
+ while (tlen--) {
+ /* that strange mix of CR and \n (and not LF) copied from
+ * util_script.c:ap_scan_script_header_err_core
+ */
+ if (*tmp != CR && *tmp != '\n') {
+ newln = 0;
+ }
+ if (*tmp == '\n') {
+ newln++;
+ }
+ tmp++;
+ if (newln == 2) {
+ break;
+ }
+ }
+
+ if (tmp - buffer >= *len) {
+ *body = NULL; /* no body along with headers */
+ *len = 0;
+ }
+ else {
+ *body = tmp;
+ *len = *len - (tmp - buffer);
+ }
+
+ status = ap_scan_script_header_err_strs(r, NULL, NULL,
&termarg, buffer, NULL);
/* code below from mod_cgi.c */
1.2 +16 -1 modperl-2.0/src/modules/perl/modperl_cgi.h
Index: modperl_cgi.h
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_cgi.h,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- modperl_cgi.h 8 May 2001 18:04:37 -0000 1.1
+++ modperl_cgi.h 3 Mar 2004 06:03:10 -0000 1.2
@@ -1,7 +1,22 @@
#ifndef MODPERL_CGI_H
#define MODPERL_CGI_H
+/**
+ * split the HTTP headers from the body (if any) and feed them to
+ * Apache. Populate the pointer to the remaining data in the buffer
+ * (body if any or NULL)
+ *
+ * @param r request_rec
+ * @param buffer a string with headers and potentially body
+ * (could be non-null terminated)
+ * @param len length of 'buffer' on entry
+ * length of 'body' on return
+ * @param body pointer to the body within the 'buffer' on return
+ * NULL if the buffer contained only headers
+ *
+ * @return status
+ */
MP_INLINE int modperl_cgi_header_parse(request_rec *r, char *buffer,
- const char **bodytext);
+ int *len, const char **body);
#endif /* MODPERL_CGI_H */
1.83 +9 -9 modperl-2.0/src/modules/perl/modperl_filter.c
Index: modperl_filter.c
===================================================================
RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_filter.c,v
retrieving revision 1.82
retrieving revision 1.83
diff -u -u -r1.82 -r1.83
--- modperl_filter.c 12 Feb 2004 19:42:47 -0000 1.82
+++ modperl_filter.c 3 Mar 2004 06:03:10 -0000 1.83
@@ -115,22 +115,23 @@
const char *work_buf = buf;
/* reset the counter to 0 as early as possible and in one place,
- * since this function will always either path the data out (and
+ * since this function will always either pass the data out (and
* it has 'len' already) or return an error.
*/
- wb->outcnt = 0;
+ wb->outcnt = 0;
if (wb->header_parse) {
request_rec *r = wb->r;
- const char *bodytext = NULL;
+ const char *body;
int status;
+
/*
* since wb->outbuf is persistent between requests, if the
* current response is shorter than the size of wb->outbuf
* it may include data from the previous request at the
* end. When this function receives a pointer to
* wb->outbuf as 'buf', modperl_cgi_header_parse may
- * return that irrelevant data as part of 'bodytext'. So
+ * return that irrelevant data as part of 'body'. So
* to avoid this risk, we create a new buffer of size 'len'
* XXX: if buf wasn't 'const char *buf' we could simply do
* buf[len] = '\0'
@@ -144,7 +145,7 @@
MP_TRACE_f(MP_FUNC, "\n\n\tparsing headers: %d bytes [%s]\n", len,
apr_pstrmemdup(wb->pool, work_buf, len));
- status = modperl_cgi_header_parse(r, (char *)work_buf, &bodytext);
+ status = modperl_cgi_header_parse(r, (char *)work_buf, &len, &body);
wb->header_parse = 0; /* only once per-request */
@@ -156,15 +157,14 @@
0, r->server, "%s did not send an HTTP header",
r->uri);
r->status = status;
- /* XXX: bodytext == NULL here */
+ /* XXX: body == NULL here */
return APR_SUCCESS;
}
- else if (!bodytext) {
+ else if (!len) {
return APR_SUCCESS;
}
- len -= (bodytext - work_buf);
- work_buf = bodytext;
+ work_buf = body;
}
bb = apr_brigade_create(wb->pool, ba);
1.3 +12 -4 modperl-2.0/t/apache/send_cgi_header.t
Index: send_cgi_header.t
===================================================================
RCS file: /home/cvs/modperl-2.0/t/apache/send_cgi_header.t,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -u -r1.2 -r1.3
--- send_cgi_header.t 18 Apr 2003 06:18:55 -0000 1.2
+++ send_cgi_header.t 3 Mar 2004 06:03:10 -0000 1.3
@@ -5,7 +5,7 @@
use Apache::TestUtil;
use Apache::TestRequest;
-plan tests => 3;
+plan tests => 4;
my $location = "/TestApache__send_cgi_header";
my $res = GET $location;
@@ -18,6 +18,14 @@
$res->header('Set-Cookie'),
"header test2");
-ok t_cmp("This not the end of the world\n",
- $res->content,
- "body test");
+my $expected = "\0\0This not the end of the world\0\0\n";
+my $received = $res->content;
+
+ok t_cmp(length($expected),
+ length($received),
+ "body length test");
+
+# \000 aren't seen when printed
+ok t_cmp($expected,
+ $received,
+ "body content test");
1.2 +2 -1 modperl-2.0/t/response/TestApache/send_cgi_header.pm
Index: send_cgi_header.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/t/response/TestApache/send_cgi_header.pm,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -u -r1.1 -r1.2
--- send_cgi_header.pm 14 Mar 2003 05:36:47 -0000 1.1
+++ send_cgi_header.pm 3 Mar 2004 06:03:10 -0000 1.2
@@ -10,12 +10,13 @@
sub handler {
my $r = shift;
+ # at the same time test the \0 binary at the beginning of the data
my $response = <<EOF;
Content-type: text/plain
X-Foo: X-Bar
Set-Cookie: Bad Programmer, No cookie!
-This not the end of the world
+\000\000This not the end of the world\000\000
EOF
# bah, we can send the header and the response here
1.11 +3 -3 modperl-2.0/xs/Apache/Response/Apache__Response.h
Index: Apache__Response.h
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/Apache/Response/Apache__Response.h,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -u -r1.10 -r1.11
--- Apache__Response.h 14 Mar 2003 05:36:48 -0000 1.10
+++ Apache__Response.h 3 Mar 2004 06:03:10 -0000 1.11
@@ -7,10 +7,10 @@
STRLEN len; \
const char *bodytext; \
MP_CGI_HEADER_PARSER_OFF(rcfg); \
- modperl_cgi_header_parse(r, SvPV(sv,len), &bodytext); \
- if (bodytext) {\
+ SvPV_force(sv, len); \
+ modperl_cgi_header_parse(r, SvPVX(sv), &len, &bodytext); \
+ if (len) {\
MP_CHECK_WBUCKET_INIT("$r->send_cgi_header"); \
- len -= (bodytext - SvPVX(sv)); \
modperl_wbucket_write(aTHX_ rcfg->wbucket, bodytext, &len); \
} \
}
1.147 +5 -1 modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm
Index: FunctionTable.pm
===================================================================
RCS file: /home/cvs/modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm,v
retrieving revision 1.146
retrieving revision 1.147
diff -u -u -r1.146 -r1.147
--- FunctionTable.pm 14 Feb 2004 17:00:26 -0000 1.146
+++ FunctionTable.pm 3 Mar 2004 06:03:11 -0000 1.147
@@ -435,8 +435,12 @@
'name' => 'buffer'
},
{
+ 'type' => 'int *',
+ 'name' => 'len'
+ }
+ {
'type' => 'const char **',
- 'name' => 'bodytext'
+ 'name' => 'body'
}
]
},
1.1 modperl-2.0/ModPerl-Registry/t/bin_resp.t
Index: bin_resp.t
===================================================================
use strict;
use warnings FATAL => 'all';
# testing various binary responses
use Apache::Test;
use Apache::TestUtil;
use Apache::TestRequest;
plan tests => 2;
# 2 sub-tests
{
# favicon.ico and other .ico image/x-icon images start with
# sequence:
my $expected = "\000\000\001\000";
my $location = "/registry/bin_resp_start_0.pl";
#my $location = "/cgi-bin/bin_resp_start_0.pl";
my $received = GET_BODY_ASSERT $location;
#t_debug "$received";
ok t_cmp(length($expected), length($received), "image size");
t_debug "comparing the binary contents";
ok $expected eq $received;
}
1.1 modperl-2.0/ModPerl-Registry/t/cgi-bin/bin_resp_start_0.pl
Index: bin_resp_start_0.pl
===================================================================
#!/usr/bin/perl -w
use strict;
use warnings FATAL => 'all';
# favicon.ico and other .ico image/x-icon images start with this sequence
my $response = "\000\000\001\000";
# test here that the cgi header parser doesn't get confused and decide
# that there is no response body if it starts with \000 sequence
print "Content-type: image/x-icon\n\n";
print $response;
1.337 +4 -0 modperl-2.0/Changes
Index: Changes
===================================================================
RCS file: /home/cvs/modperl-2.0/Changes,v
retrieving revision 1.336
retrieving revision 1.337
diff -u -u -r1.336 -r1.337
--- Changes 2 Mar 2004 01:30:09 -0000 1.336
+++ Changes 3 Mar 2004 06:03:11 -0000 1.337
@@ -12,6 +12,10 @@
=item 1.99_13-dev
+Handle correctly the situation when response HTTP headers are printed
+from the handler and the response body starts with \000, which is the
+case with some images like .ico. [Stas]
+
Apache::PerlSections->dump() and store(filename) [Gozer]
expose $c->keepalive related constants and $c->keepalives counter