summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mod_fastcgi.c106
-rw-r--r--src/response.c2
-rw-r--r--tests/docroot/www/Makefile.am2
-rw-r--r--tests/docroot/www/sendfile.php13
-rw-r--r--tests/lighttpd.conf2
-rwxr-xr-xtests/mod-fastcgi.t18
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");