Browse Source

[mod_scgi] tests/mod-scgi.t unit tests

(copied from tests/mod-fastcgi.t fcgi-responder tests)
personal/stbuehler/mod-csrf
Glenn Strauss 5 years ago
parent
commit
4a68780e1b
  1. 1
      tests/CMakeLists.txt
  2. 5
      tests/Makefile.am
  3. 1
      tests/SConscript
  4. 91
      tests/mod-scgi.t
  5. 219
      tests/scgi-responder.c
  6. 154
      tests/scgi-responder.conf

1
tests/CMakeLists.txt

@ -9,6 +9,7 @@ if(HAVE_FASTCGI_H OR HAVE_FASTCGI_FASTCGI_H)
target_link_libraries(fcgi-responder fcgi)
endif()
endif()
add_executable(scgi-responder scgi-responder.c)
set(T_FILES
prepare.sh

5
tests/Makefile.am

@ -2,7 +2,7 @@
testdir=$(srcdir)/tmp/lighttpd/
if CHECK_WITH_FASTCGI
check_PROGRAMS=fcgi-auth fcgi-responder
check_PROGRAMS=fcgi-auth fcgi-responder scgi-responder
fcgi_auth_SOURCES=fcgi-auth.c
fcgi_auth_LDADD=-lfcgi
@ -11,6 +11,8 @@ fcgi_responder_SOURCES=fcgi-responder.c
fcgi_responder_LDADD=-lfcgi
endif
scgi_responder_SOURCES=scgi-responder.c
TESTS=\
prepare.sh \
run-tests.pl \
@ -57,6 +59,7 @@ CONFS=\
mod-userdir.t \
proxy.conf \
request.t \
scgi-responder.conf \
symlink.t \
var-include-sub.conf \
var-include.conf

1
tests/SConscript

@ -35,6 +35,7 @@ extra_dist = Split('fastcgi-10.conf \
fcgi_auth = None
fcgi_responder = None
scgi_responder = env.Program("scgi-responder", "scgi-responder.c")
if env['LIBFCGI']:
fcgi_auth = env.Program("fcgi-auth", "fcgi-auth.c", LIBS=[env['LIBFCGI'], env['APPEND_LIBS']])

91
tests/mod-scgi.t

@ -0,0 +1,91 @@
#!/usr/bin/env perl
BEGIN {
# add current source dir to the include-path
# we need this for make distcheck
(my $srcdir = $0) =~ s,/[^/]+$,/,;
unshift @INC, $srcdir;
}
use strict;
use Test::More tests => 10;
use LightyTest;
my $tf = LightyTest->new();
my $t;
SKIP: {
skip "no scgi-responder found", 11 unless -x $tf->{BASEDIR}."/tests/scgi-responder" || -x $tf->{BASEDIR}."/tests/scgi-responder.exe";
$tf->{CONFIGFILE} = 'scgi-responder.conf';
ok($tf->start_proc == 0, "Starting lighttpd with $tf->{CONFIGFILE}") or die();
$t->{REQUEST} = ( <<EOF
GET /index.scgi?lf HTTP/1.0
Host: www.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'test123' } ];
ok($tf->handle_http($t) == 0, 'line-ending \n\n');
$t->{REQUEST} = ( <<EOF
GET /index.scgi?crlf HTTP/1.0
Host: www.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'test123' } ];
ok($tf->handle_http($t) == 0, 'line-ending \r\n\r\n');
$t->{REQUEST} = ( <<EOF
GET /index.scgi?slow-lf HTTP/1.0
Host: www.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'test123' } ];
ok($tf->handle_http($t) == 0, 'line-ending \n + \n');
$t->{REQUEST} = ( <<EOF
GET /index.scgi?slow-crlf HTTP/1.0
Host: www.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'test123' } ];
ok($tf->handle_http($t) == 0, 'line-ending \r\n + \r\n');
$t->{REQUEST} = ( <<EOF
GET /abc/def/ghi?path_info HTTP/1.0
Host: wsgi.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/abc/def/ghi' } ];
ok($tf->handle_http($t) == 0, 'PATH_INFO (wsgi)');
$t->{REQUEST} = ( <<EOF
GET /abc/def/ghi?script_name HTTP/1.0
Host: wsgi.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '' } ];
ok($tf->handle_http($t) == 0, 'SCRIPT_NAME (wsgi)');
$t->{REQUEST} = ( <<EOF
GET /index.scgi?die-at-end HTTP/1.0
Host: www.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'test123' } ];
ok($tf->handle_http($t) == 0, 'killing scgi and wait for restart');
# (might take lighttpd 1 sec to detect backend exit)
select(undef, undef, undef, .9);
select(undef, undef, undef, .1) while (!$tf->listening_on(10000));
$t->{REQUEST} = ( <<EOF
GET /index.scgi?crlf HTTP/1.0
Host: www.example.org
EOF
);
$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'test123' } ];
ok($tf->handle_http($t) == 0, 'regular response of after restart');
ok($tf->stop_proc == 0, "Stopping lighttpd");
}

219
tests/scgi-responder.c

@ -0,0 +1,219 @@
/*
* simple and trivial SCGI server with hard-coded results for use in unit tests
* - listens on STDIN_FILENO (socket on STDIN_FILENO must be set up by caller)
* - processes a single SCGI request at a time
* - arbitrary limitation: reads request headers netstring up to 64k in size
* - expect recv data for request headers netstring every 10ms or less
* - no read timeouts for request body; might block reading request body
* - no write timeouts; might block writing response
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#ifndef MSG_DONTWAIT
#define MSG_DONTWAIT 0
#endif
static int finished;
static char buf[65536];
static char *
scgi_getenv(char *r, const unsigned long rlen, const char * const name)
{
/* simple search;
* if many lookups are done, then should use more efficient data structure*/
char * const end = r+rlen;
char *z;
const size_t len = strlen(name);
do {
if (0 == strcmp(r, name)) return r+len+1;
z = memchr(r, '\0', (size_t)(end-r));
if (NULL == z) return NULL;
z = memchr(z+1, '\0', (size_t)(end-r));
if (NULL == z) return NULL;
r = z+1;
} while (r < end);
return NULL;
}
static void
scgi_process (const int fd)
{
ssize_t rd = 0, offset = 0;
int num_requests = 1;
char *p = NULL, *r;
unsigned long rlen;
long long cl;
assert(fd == STDOUT_FILENO); /*(required for response sent with printf())*/
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
do {
struct pollfd pfd = { fd, POLLIN, 0 };
switch (poll(&pfd, 1, 10)) { /* 10ms timeout */
default: /* 1; the only pfd has revents */
break;
case -1: /* error */
case 0: /* timeout */
pfd.revents |= POLLERR;
break;
}
if (!(pfd.revents & POLLIN))
break;
do {
rd = recv(fd, buf+offset, sizeof(buf)-offset, MSG_DONTWAIT);
} while (rd < 0 && errno == EINTR);
if (rd > 0)
offset += rd;
else if (0 == rd) {
p = memchr(buf, ':', offset);
break;
}
else if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
else
break;
} while (NULL == (p = memchr(buf,':',offset)) && offset < 21);
if (NULL == p)
return; /* timeout or error receiving start of netstring */
rlen = strtoul(buf, &p, 10);
if (*buf == '-' || *p != ':' || p == buf || rlen == ULONG_MAX)
return; /* invalid netstring (and rlen == ULONG_MAX is too long)*/
if (rlen > sizeof(buf) - (p - buf) - 2)
return; /* netstring longer than arbitrary limit we accept here */
rlen += (p - buf) + 2;
while ((ssize_t)rlen < offset) {
struct pollfd pfd = { fd, POLLIN, 0 };
switch (poll(&pfd, 1, 10)) { /* 10ms timeout */
default: /* 1; the only pfd has revents */
break;
case -1: /* error */
case 0: /* timeout */
pfd.revents |= POLLERR;
break;
}
if (!(pfd.revents & POLLIN))
break;
do {
rd = recv(fd, buf+offset, sizeof(buf)-offset, MSG_DONTWAIT);
} while (rd < 0 && errno == EINTR);
if (rd > 0)
offset += rd;
else if (0 == rd)
break;
else if (errno == EAGAIN || errno == EWOULDBLOCK)
continue;
else
break;
}
if (offset < (ssize_t)rlen)
return; /* timeout or error receiving netstring */
if (buf[rlen-1] != ',')
return; /* invalid netstring */
rlen -= (p - buf) + 2;
r = p+1;
/* not checking for empty headers in SCGI request (empty values allowed) */
/* SCGI request must contain "SCGI" header with value "1" */
p = scgi_getenv(r, rlen, "SCGI");
if (NULL == p || p[0] != '1' || p[1] != '\0')
return; /* missing or invalid SCGI header */
/* CONTENT_LENGTH must be first header in SCGI request; always required */
if (0 != strcmp(r, "CONTENT_LENGTH"))
return; /* missing CONTENT_LENGTH */
errno = 0;
cl = strtoll(r+sizeof("CONTENT_LENGTH"), &p, 10);
if (*p != '\0' || p == r+sizeof("CONTENT_LENGTH") || cl < 0 || 0 != errno)
return; /* invalid CONTENT_LENGTH */
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK);
/* read,discard request body (currently ignored in these SCGI unit tests)
* (make basic effort to read body; ignore any timeouts or errors here) */
cl -= (offset - (r+rlen+1 - buf));
while (cl > 0) {
char x[8192];
do {
rd = recv(fd, x, (cl>(long long)sizeof(x)?sizeof(x):(size_t)cl), 0);
} while (rd < 0 && errno == EINTR);
if (rd <= 0)
break;
cl -= rd;
}
/*(from fcgi-responder.c, substituting scgi_getenv() for getenv())*/
{
if (NULL != (p = scgi_getenv(r, rlen, "QUERY_STRING"))) {
if (0 == strcmp(p, "lf")) {
printf("Status: 200 OK\n\n");
} else if (0 == strcmp(p, "crlf")) {
printf("Status: 200 OK\r\n\r\n");
} else if (0 == strcmp(p, "slow-lf")) {
printf("Status: 200 OK\n");
fflush(stdout);
printf("\n");
} else if (0 == strcmp(p,"slow-crlf")) {
printf("Status: 200 OK\r\n");
fflush(stdout);
printf("\r\n");
} else if (0 == strcmp(p, "die-at-end")) {
printf("Status: 200 OK\r\n\r\n");
num_requests--;
} else {
printf("Status: 200 OK\r\n\r\n");
}
} else {
printf("Status: 500 Internal Foo\r\n\r\n");
}
if (0 == strcmp(p, "path_info")) {
printf("%s", scgi_getenv(r, rlen, "PATH_INFO"));
} else if (0 == strcmp(p, "script_name")) {
printf("%s", scgi_getenv(r, rlen, "SCRIPT_NAME"));
} else if (0 == strcmp(p, "var")) {
p = scgi_getenv(r, rlen, "X_LIGHTTPD_FCGI_AUTH");
printf("%s", p ? p : "(no value)");
} else {
printf("test123");
}
}
fflush(stdout);
if (0 == num_requests) finished = 1;
}
int
main (void)
{
int fd;
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) & ~O_NONBLOCK);
close(STDOUT_FILENO); /*(so that accept() returns fd to STDOUT_FILENO)*/
do {
fd = accept(STDIN_FILENO, NULL, NULL);
if (fd < 0)
continue;
assert(fd == STDOUT_FILENO);
scgi_process(fd);
} while (fd > 0 ? 0 == close(fd) && !finished : errno == EINTR);
return 0;
}

154
tests/scgi-responder.conf

@ -0,0 +1,154 @@
server.document-root = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/"
#debug.log-request-header = "enable"
#debug.log-response-header = "enable"
#debug.log-request-handling = "enable"
#debug.log-state-handling = "enable"
#scgi.debug = 1
## bind to port (default: 80)
server.port = 2048
## bind to localhost (default: all interfaces)
server.bind = "localhost"
server.errorlog = env.SRCDIR + "/tmp/lighttpd/logs/lighttpd.error.log"
server.breakagelog = env.SRCDIR + "/tmp/lighttpd/logs/lighttpd.breakage.log"
server.name = "www.example.org"
server.tag = "Apache 1.3.29"
server.dir-listing = "enable"
server.modules = (
"mod_rewrite",
"mod_access",
"mod_auth",
"mod_authn_file",
"mod_status",
"mod_expire",
"mod_redirect",
"mod_scgi",
"mod_cgi",
"mod_compress",
"mod_accesslog",
)
server.indexfiles = (
"index.php",
"index.html",
"index.htm",
"default.htm",
)
######################## MODULE CONFIG ############################
accesslog.filename = env.SRCDIR + "/tmp/lighttpd/logs/lighttpd.access.log"
mimetype.assign = (
".png" => "image/png",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".gif" => "image/gif",
".html" => "text/html",
".htm" => "text/html",
".pdf" => "application/pdf",
".swf" => "application/x-shockwave-flash",
".spl" => "application/futuresplash",
".txt" => "text/plain",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".gz" => "application/x-gzip",
".c" => "text/plain",
".conf" => "text/plain",
)
compress.cache-dir = env.SRCDIR + "/tmp/lighttpd/cache/compress/"
compress.filetype = (
"text/plain",
"text/html",
)
scgi.debug = 0
scgi.server = (
".scgi" => (
"grisu" => (
"host" => "127.0.0.1",
"port" => 10000,
"bin-path" => env.SRCDIR + "/scgi-responder",
"check-local" => "disable",
"max-procs" => 1,
"min-procs" => 1,
),
),
)
cgi.assign = (
".pl" => env.PERL,
".cgi" => env.PERL,
)
auth.backend = "plain"
auth.backend.plain.userfile = env.SRCDIR + "/tmp/lighttpd/lighttpd.user"
auth.backend.plain.groupfile = "lighttpd.group"
#auth.backend.ldap.hostname = "localhost"
#auth.backend.ldap.base-dn = "dc=my-domain,dc=com"
#auth.backend.ldap.filter = "(uid=$)"
auth.require = (
"/server-status" => (
"method" => "digest",
"realm" => "download archiv",
"require" => "group=www|user=jan|host=192.168.2.10",
),
"/auth.php" => (
"method" => "basic",
"realm" => "download archiv",
"require" => "user=jan",
),
"/server-config" => (
"method" => "basic",
"realm" => "download archiv",
"require" => "group=www|user=jan|host=192.168.2.10",
),
)
url.access-deny = (
"~",
".inc",
)
url.redirect = (
"^/redirect/$" => "http://localhost:2048/",
)
expire.url = (
"/buggy/" => "access 2 hours",
"/asdhas/" => "access plus 1 seconds 2 minutes",
)
#### status module
status.status-url = "/server-status"
status.config-url = "/server-config"
$HTTP["host"] == "vvv.example.org" {
server.document-root = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/"
}
$HTTP["host"] == "zzz.example.org" {
server.document-root = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/"
server.name = "zzz.example.org"
}
$HTTP["host"] == "wsgi.example.org" {
scgi.server = (
"/" => ( (
"host" => "127.0.0.1", "port" => 10000,
"fix-root-scriptname" => "enable",
"check-local" => "disable",
"bin-path" => env.SRCDIR + "/scgi-responder",
"max-procs" => 1,
) ),
)
}
Loading…
Cancel
Save