diff options
-rw-r--r-- | src/mod_fastcgi.c | 106 | ||||
-rw-r--r-- | src/response.c | 2 | ||||
-rw-r--r-- | tests/docroot/www/Makefile.am | 2 | ||||
-rw-r--r-- | tests/docroot/www/sendfile.php | 13 | ||||
-rw-r--r-- | tests/lighttpd.conf | 2 | ||||
-rwxr-xr-x | tests/mod-fastcgi.t | 18 |
6 files changed, 135 insertions, 8 deletions
diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index 5bc4fb7d..7fa43bea 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -2224,6 +2224,8 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf handler_ctx *hctx = con->plugin_ctx[p->id]; fcgi_extension_host *host= hctx->host; + int have_sendfile2 = 0; + off_t sendfile2_content_length = 0; UNUSED(srv); @@ -2233,7 +2235,7 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf for (s = p->parse_response->ptr; NULL != (ns = strchr(s, '\n')); s = ns + 1) { char *key, *value; int key_len; - data_string *ds; + data_string *ds = NULL; /* a good day. Someone has read the specs and is sending a \r\n to us */ @@ -2296,6 +2298,79 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf con->parsed_response |= HTTP_CONNECTION; } break; + case 11: + if (host->allow_xsendfile && 0 == strncasecmp(key, "X-Sendfile2", key_len)&& hctx->send_content_body) { + char *pos = value; + have_sendfile2 = 1; + + while (*pos) { + char *filename, *range; + stat_cache_entry *sce; + off_t begin_range, end_range, range_len; + + while (' ' == *pos) pos++; + if (!*pos) break; + + filename = pos; + if (NULL == (range = strchr(pos, ' '))) { + /* missing range */ + return 1; + } + buffer_copy_string_len(srv->tmp_buf, filename, range - filename); + + /* find end of range */ + for (pos = ++range; *pos && *pos != ' ' && *pos != ','; pos++) ; + + buffer_urldecode_path(srv->tmp_buf); + if (HANDLER_ERROR == stat_cache_get_entry(srv, con, srv->tmp_buf, &sce)) { + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "send-file error: couldn't get stat_cache entry for X-Sendfile2:", + srv->tmp_buf); + } + return 1; + } else if (!S_ISREG(sce->st.st_mode)) { + if (p->conf.debug) { + log_error_write(srv, __FILE__, __LINE__, "sb", + "send-file error: wrong filetype for X-Sendfile2:", + srv->tmp_buf); + } + return 1; + } + /* found the file */ + + /* parse range */ + begin_range = 0; end_range = sce->st.st_size - 1; + { + char *rpos = NULL; + errno = 0; + begin_range = strtoll(range, &rpos, 10); + if (errno != 0 || begin_range < 0 || rpos == range) return 1; + if ('-' != *rpos++) return 1; + if (rpos != pos) { + range = rpos; + end_range = strtoll(range, &rpos, 10); + if (errno != 0 || end_range < 0 || rpos == range) return 1; + } + if (rpos != pos) return 1; + } + + /* no parameters accepted */ + + while (*pos == ' ') pos++; + if (*pos != '\0' && *pos != ',') return 1; + + range_len = end_range - begin_range + 1; + if (range_len < 0) return 1; + if (range_len != 0) { + http_chunk_append_file(srv, con, srv->tmp_buf, begin_range, range_len); + } + sendfile2_content_length += range_len; + + if (*pos == ',') pos++; + } + } + break; case 14: if (0 == strncasecmp(key, "Content-Length", key_len)) { con->response.content_length = strtol(value, NULL, 10); @@ -2309,6 +2384,26 @@ static int fcgi_response_parse(server *srv, connection *con, plugin_data *p, buf } } + if (have_sendfile2) { + data_string *dcls; + + hctx->send_content_body = 0; + joblist_append(srv, con); + + /* fix content-length */ + if (NULL == (dcls = (data_string *)array_get_unused_element(con->response.headers, TYPE_STRING))) { + dcls = data_response_init(); + } + + buffer_copy_string_len(dcls->key, "Content-Length", sizeof("Content-Length")-1); + buffer_copy_off_t(dcls->value, sendfile2_content_length); + dcls = (data_string*) array_replace(con->response.headers, (data_unset *)dcls); + if (dcls) dcls->free((data_unset*)dcls); + + con->parsed_response |= HTTP_CONTENT_LENGTH; + con->response.content_length = sendfile2_content_length; + } + /* CGI/1.1 rev 03 - 7.2.1.2 */ if ((con->parsed_response & HTTP_LOCATION) && !(con->parsed_response & HTTP_STATUS)) { @@ -2536,7 +2631,12 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { } /* parse the response header */ - fcgi_response_parse(srv, con, p, hctx->response_header); + if (fcgi_response_parse(srv, con, p, hctx->response_header)) { + con->http_status = 502; + hctx->send_content_body = 0; + con->file_started = 1; + break; + } con->file_started = 1; @@ -2547,7 +2647,7 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { hctx->send_content_body = 0; } - if (host->allow_xsendfile && + if (host->allow_xsendfile && hctx->send_content_body && (NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-LIGHTTPD-send-file")) || NULL != (ds = (data_string *) array_get_element(con->response.headers, "X-Sendfile")))) { stat_cache_entry *sce; diff --git a/src/response.c b/src/response.c index 3213bc95..df543a51 100644 --- a/src/response.c +++ b/src/response.c @@ -72,7 +72,7 @@ int http_response_write_header(server *srv, connection *con) { if (ds->value->used && ds->key->used && 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-LIGHTTPD-")) && - 0 != strcasecmp(ds->key->ptr, "X-Sendfile")) { + 0 != strncasecmp(ds->key->ptr, CONST_STR_LEN("X-Sendfile"))) { if (0 == strcasecmp(ds->key->ptr, "Date")) have_date = 1; if (0 == strcasecmp(ds->key->ptr, "Server")) have_server = 1; if (0 == strcasecmp(ds->key->ptr, "Content-Encoding") && 304 == con->http_status) continue; diff --git a/tests/docroot/www/Makefile.am b/tests/docroot/www/Makefile.am index f535cb25..5bc0c0b3 100644 --- a/tests/docroot/www/Makefile.am +++ b/tests/docroot/www/Makefile.am @@ -1,5 +1,5 @@ EXTRA_DIST=cgi.php cgi.pl index.html index.txt phpinfo.php \ redirect.php cgi-pathinfo.pl get-env.php get-server-env.php \ nph-status.pl prefix.fcgi get-header.pl ssi.shtml get-post-len.pl \ - exec-date.shtml 404.fcgi 404.html 404.pl send404.pl crlfcrash.pl + exec-date.shtml 404.fcgi 404.html 404.pl send404.pl crlfcrash.pl sendfile.php SUBDIRS=go indexfile expire diff --git a/tests/docroot/www/sendfile.php b/tests/docroot/www/sendfile.php new file mode 100644 index 00000000..0aa8786f --- /dev/null +++ b/tests/docroot/www/sendfile.php @@ -0,0 +1,13 @@ +<?php + +function pathencode($path) { + return str_replace(',', '%2c', urlencode($path)); +} + +$val = "X-Sendfile2: " . pathencode(getcwd() . "/index.txt") . " " . $_GET["range"]; + +if ($_GET["range2"]) $val .= ", " . pathencode(getcwd() . "/index.txt") . " " . $_GET["range2"]; + +header($val); + +?>
\ No newline at end of file diff --git a/tests/lighttpd.conf b/tests/lighttpd.conf index 2202100f..f93311d0 100644 --- a/tests/lighttpd.conf +++ b/tests/lighttpd.conf @@ -83,7 +83,7 @@ $HTTP["url"] =~ "\.pdf$" { } fastcgi.debug = 0 -fastcgi.server = ( ".php" => ( ( "host" => "127.0.0.1", "port" => 1026, "broken-scriptfilename" => "enable" ) ), +fastcgi.server = ( ".php" => ( ( "host" => "127.0.0.1", "port" => 1026, "broken-scriptfilename" => "enable", "allow-x-send-file" => "enable" ) ), "/prefix.fcgi" => ( ( "host" => "127.0.0.1", "port" => 1026, "check-local" => "disable", "broken-scriptfilename" => "enable" ) ) ) diff --git a/tests/mod-fastcgi.t b/tests/mod-fastcgi.t index 63c7b152..64cf63da 100755 --- a/tests/mod-fastcgi.t +++ b/tests/mod-fastcgi.t @@ -7,7 +7,7 @@ BEGIN { } use strict; -use Test::More tests => 54; +use Test::More tests => 56; use LightyTest; my $tf = LightyTest->new(); @@ -25,7 +25,7 @@ SKIP: { } SKIP: { - skip "no PHP running on port 1026", 31 unless $tf->listening_on(1026); + skip "no PHP running on port 1026", 33 unless $tf->listening_on(1026); ok($tf->start_proc == 0, "Starting lighttpd") or goto cleanup; @@ -174,6 +174,20 @@ EOF $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/foo/bar' } ]; ok($tf->handle_http($t) == 0, 'PATH_INFO, check-local off'); + $t->{REQUEST} = ( <<EOF +GET /sendfile.php?range=0- HTTP/1.0 +EOF + ); + $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Length' => 4348 } ]; + ok($tf->handle_http($t) == 0, 'X-Sendfile2'); + + $t->{REQUEST} = ( <<EOF +GET /sendfile.php?range=0-4&range2=5- HTTP/1.0 +EOF + ); + $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Length' => 4348 } ]; + ok($tf->handle_http($t) == 0, 'X-Sendfile2'); + ok($tf->stop_proc == 0, "Stopping lighttpd"); |