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