mirror of https://github.com/nealey/eris.git
found another fnord bug
This commit is contained in:
parent
4536afe4a6
commit
98f853c8ac
2
README
2
README
|
@ -12,7 +12,7 @@ Significant differences between eris and fnord are:
|
||||||
* elimination of user switching (you can use tcpserver -[ug])
|
* elimination of user switching (you can use tcpserver -[ug])
|
||||||
* elimination of chroot code (you can use chroot)
|
* elimination of chroot code (you can use chroot)
|
||||||
* several bugfixes (sent to the fnord mail list)
|
* several bugfixes (sent to the fnord mail list)
|
||||||
* removal of (non-functional) content-negotiation
|
* ignores Accept header (fnord does too)
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,10 @@ fail () {
|
||||||
failures=$(expr $failures + 1)
|
failures=$(expr $failures + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d () {
|
||||||
|
tr '\r\n' '#%'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if [ ! -f fnord-1.10.tar.bz2 ]; then
|
if [ ! -f fnord-1.10.tar.bz2 ]; then
|
||||||
wget http://www.fefe.de/fnord/fnord-1.10.tar.bz2
|
wget http://www.fefe.de/fnord/fnord-1.10.tar.bz2
|
||||||
|
@ -82,7 +86,7 @@ printf 'GET / HTTP/1.0\r\nHost: empty\r\n\r\n' | $HTTPD_IDX 2>/dev/null | grep -
|
||||||
|
|
||||||
# 2. Should output \r\n\r\n; instead outputs \r\n\n
|
# 2. Should output \r\n\r\n; instead outputs \r\n\n
|
||||||
title "CGI output bare newlines"
|
title "CGI output bare newlines"
|
||||||
printf 'GET /a.cgi HTTP/1.0\r\n\r\n' | $HTTPD_CGI 2>/dev/null | grep -qU '^\r$' && pass || fail
|
printf 'GET /a.cgi HTTP/1.0\r\n\r\n' | $HTTPD_CGI 2>/dev/null | d | grep -q '#%#%' && pass || fail
|
||||||
|
|
||||||
# 3. Should process both requests; instead drops second
|
# 3. Should process both requests; instead drops second
|
||||||
title "Multiple requests in one packet"
|
title "Multiple requests in one packet"
|
||||||
|
@ -102,7 +106,13 @@ title "Second MIME-Type"
|
||||||
title "POST to static HTML"
|
title "POST to static HTML"
|
||||||
(printf 'POST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n';
|
(printf 'POST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n';
|
||||||
ls / > /dev/null
|
ls / > /dev/null
|
||||||
printf 'aPOST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\na') | $HTTPD 2>/dev/null | grep -c '^HTTP/1.' | grep -q 2 && pass || fail
|
printf 'aPOST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\na') | $HTTPD 2>/dev/null | grep -c '200 OK' | grep -q 2 && pass || fail
|
||||||
|
|
||||||
|
# 7. HTTP/1.1 should default to keepalive; instead connection is closed
|
||||||
|
title "HTTP/1.1 default keepalive"
|
||||||
|
(printf 'GET / HTTP/1.1\r\nHost: a\r\n\r\n'
|
||||||
|
ls / >/dev/null
|
||||||
|
printf 'GET / HTTP/1.1\r\nHost: a\r\n\r\n') | $HTTPD 2>/dev/null | grep -c '^HTTP/' | grep -q 2 && pass || fail
|
||||||
|
|
||||||
|
|
||||||
cat <<EOD
|
cat <<EOD
|
||||||
|
|
179
eris.c
179
eris.c
|
@ -73,25 +73,15 @@
|
||||||
#define USE_MMAP
|
#define USE_MMAP
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
* TCP_CORK is a Linux extension to work around a TCP problem.
|
|
||||||
* http://www.baus.net/on-tcp_cork has a good description.
|
|
||||||
* XXX: Since we do our own buffering, TCP_CORK may not be helping
|
|
||||||
* with anything. This needs testing.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef TCP_CORK
|
|
||||||
static int corked;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
enum { UNKNOWN, GET, HEAD, POST } method;
|
enum { UNKNOWN, GET, HEAD, POST } method;
|
||||||
|
|
||||||
static long retcode = 404; /* used for logging code */
|
static long retcode = 404; /* used for logging code */
|
||||||
char *host = "?"; /* Host: header */
|
char *host = "?"; /* Host: header */
|
||||||
char *port; /* also Host: header, :80 part */
|
char *port; /* also Host: header, :80 part */
|
||||||
char *args; /* URL behind ? (for CGIs) */
|
char *args; /* URL behind ? (for CGIs) */
|
||||||
char *url; /* string between GET and HTTP/1.0, *
|
char *path; /* string between GET and HTTP/1.0, *
|
||||||
* demangled */
|
* demangled */
|
||||||
|
char rpath[PATH_MAX];
|
||||||
char *ua = "?"; /* user-agent */
|
char *ua = "?"; /* user-agent */
|
||||||
char *refer; /* Referrer: header */
|
char *refer; /* Referrer: header */
|
||||||
char *accept_enc; /* Accept-Encoding */
|
char *accept_enc; /* Accept-Encoding */
|
||||||
|
@ -128,6 +118,25 @@ char *remote_ident;
|
||||||
#define BUFFER_SIZE 8192
|
#define BUFFER_SIZE 8192
|
||||||
char stdout_buf[BUFFER_SIZE];
|
char stdout_buf[BUFFER_SIZE];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TCP_CORK is a Linux extension to work around a TCP problem.
|
||||||
|
* http://www.baus.net/on-tcp_cork has a good description.
|
||||||
|
* XXX: Since we do our own buffering, TCP_CORK may not be helping
|
||||||
|
* with anything. This needs testing.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cork(int enable)
|
||||||
|
{
|
||||||
|
#ifdef TCP_CORK
|
||||||
|
static int corked = 0;
|
||||||
|
|
||||||
|
if (enable != corked) {
|
||||||
|
setsockopt(1, IPPROTO_TCP, TCP_CORK, &enable, sizeof(enable));
|
||||||
|
corked = enable;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sanitize(char *ua)
|
sanitize(char *ua)
|
||||||
{ /* replace strings with underscores for *
|
{ /* replace strings with underscores for *
|
||||||
|
@ -149,7 +158,7 @@ dolog(off_t len)
|
||||||
|
|
||||||
fprintf(stderr, "%s %ld %lu %s %s %s %s\n",
|
fprintf(stderr, "%s %ld %lu %s %s %s %s\n",
|
||||||
remote_ip ? remote_ip : "0.0.0.0",
|
remote_ip ? remote_ip : "0.0.0.0",
|
||||||
retcode, (unsigned long) len, host, ua, refer, url);
|
retcode, (unsigned long) len, host, ua, refer, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -243,6 +252,11 @@ read_header(FILE *f, char *buf, size_t * buflen)
|
||||||
static int bare = 1; /* LF here would be bare */
|
static int bare = 1; /* LF here would be bare */
|
||||||
static int bufsize = 0;
|
static int bufsize = 0;
|
||||||
|
|
||||||
|
if (! buf) {
|
||||||
|
lastbuf = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (lastbuf != buf) {
|
if (lastbuf != buf) {
|
||||||
lastbuf = buf;
|
lastbuf = buf;
|
||||||
bare = 1;
|
bare = 1;
|
||||||
|
@ -330,7 +344,7 @@ do_cgi(const char *pathinfo, const char *const *envp)
|
||||||
cgi_env[i++] = env_append("SERVER_PORT", port);
|
cgi_env[i++] = env_append("SERVER_PORT", port);
|
||||||
cgi_env[i++] = env_append("REQUEST_METHOD", method_name[method]);
|
cgi_env[i++] = env_append("REQUEST_METHOD", method_name[method]);
|
||||||
cgi_env[i++] = env_append("REQUEST_URI", uri);
|
cgi_env[i++] = env_append("REQUEST_URI", uri);
|
||||||
cgi_env[i++] = env_append("SCRIPT_NAME", url);
|
cgi_env[i++] = env_append("SCRIPT_NAME", path);
|
||||||
if (remote_ip)
|
if (remote_ip)
|
||||||
cgi_env[i++] = env_append("REMOTE_ADDR", remote_ip);
|
cgi_env[i++] = env_append("REMOTE_ADDR", remote_ip);
|
||||||
if (remote_port)
|
if (remote_port)
|
||||||
|
@ -396,9 +410,9 @@ do_cgi(const char *pathinfo, const char *const *envp)
|
||||||
{
|
{
|
||||||
char tmp[PATH_MAX];
|
char tmp[PATH_MAX];
|
||||||
|
|
||||||
i = strrchr(url, '/') - url;
|
i = strrchr(rpath, '/') - rpath;
|
||||||
if (i) {
|
if (i) {
|
||||||
strncpy(tmp, url + 1, i);
|
strncpy(tmp, rpath + 1, i);
|
||||||
tmp[i] = 0;
|
tmp[i] = 0;
|
||||||
chdir(tmp);
|
chdir(tmp);
|
||||||
}
|
}
|
||||||
|
@ -412,7 +426,7 @@ do_cgi(const char *pathinfo, const char *const *envp)
|
||||||
*/
|
*/
|
||||||
cgi_arg[0] = tmp;
|
cgi_arg[0] = tmp;
|
||||||
tmp[0] = '.';
|
tmp[0] = '.';
|
||||||
strcpy(tmp + 1, url + i);
|
strcpy(tmp + 1, rpath + i);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* start cgi
|
* start cgi
|
||||||
|
@ -570,12 +584,7 @@ start_cgi(int nph, const char *pathinfo, const char *const *envp)
|
||||||
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
dolog(size);
|
dolog(size);
|
||||||
#ifdef TCP_CORK
|
cork(0);
|
||||||
{
|
|
||||||
int zero = 0;
|
|
||||||
setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
/* Child */
|
/* Child */
|
||||||
|
|
||||||
|
@ -1028,7 +1037,6 @@ handledirlist(const char *origurl)
|
||||||
unsigned int nl;
|
unsigned int nl;
|
||||||
const char *nurl = origurl;
|
const char *nurl = origurl;
|
||||||
|
|
||||||
url = (char *) origurl;
|
|
||||||
while (nurl[0] == '/')
|
while (nurl[0] == '/')
|
||||||
++nurl;
|
++nurl;
|
||||||
if (nurl <= origurl)
|
if (nurl <= origurl)
|
||||||
|
@ -1138,7 +1146,8 @@ handleindexcgi(const char *testurl, const char *origurl, char *space)
|
||||||
if (!(st.st_mode & ul))
|
if (!(st.st_mode & ul))
|
||||||
return 0; /* should be executable */
|
return 0; /* should be executable */
|
||||||
*(--test) = '/';
|
*(--test) = '/';
|
||||||
url = test;
|
|
||||||
|
strncpy(rpath, test, sizeof rpath);
|
||||||
return 1; /* Wow... now start "index.cgi" */
|
return 1; /* Wow... now start "index.cgi" */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1273,9 +1282,7 @@ static int
|
||||||
serve_static_data(int fd)
|
serve_static_data(int fd)
|
||||||
{
|
{
|
||||||
off_t len = rangeend - rangestart;
|
off_t len = rangeend - rangestart;
|
||||||
#ifdef TCP_CORK
|
|
||||||
corked = 0;
|
|
||||||
#endif
|
|
||||||
if (len < 4096) { /* for small files, sendfile is actually
|
if (len < 4096) { /* for small files, sendfile is actually
|
||||||
* slower */
|
* slower */
|
||||||
char tmp[4096];
|
char tmp[4096];
|
||||||
|
@ -1291,13 +1298,8 @@ serve_static_data(int fd)
|
||||||
#ifdef USE_SENDFILE
|
#ifdef USE_SENDFILE
|
||||||
{
|
{
|
||||||
off_t offset = rangestart;
|
off_t offset = rangestart;
|
||||||
#ifdef TCP_CORK
|
|
||||||
{
|
cork(1);
|
||||||
int one = 1;
|
|
||||||
setsockopt(1, IPPROTO_TCP, TCP_CORK, &one, sizeof(one));
|
|
||||||
corked = 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
{
|
{
|
||||||
off_t l = rangeend - rangestart;
|
off_t l = rangeend - rangestart;
|
||||||
|
@ -1305,11 +1307,11 @@ serve_static_data(int fd)
|
||||||
off_t c;
|
off_t c;
|
||||||
c = (l > (1ul << 31)) ? 1ul << 31 : l;
|
c = (l > (1ul << 31)) ? 1ul << 31 : l;
|
||||||
if (sendfile(1, fd, &offset, c) == -1)
|
if (sendfile(1, fd, &offset, c) == -1)
|
||||||
#ifdef USE_MMAP
|
# ifdef USE_MMAP
|
||||||
return serve_mmap(fd);
|
return serve_mmap(fd);
|
||||||
#else
|
# else
|
||||||
return serve_read_write(fd);
|
return serve_read_write(fd);
|
||||||
#endif
|
# endif
|
||||||
l -= c;
|
l -= c;
|
||||||
} while (l);
|
} while (l);
|
||||||
}
|
}
|
||||||
|
@ -1317,18 +1319,14 @@ serve_static_data(int fd)
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
#ifdef TCP_CORK
|
|
||||||
{
|
/* XXX: Is this really the right place to uncork? */
|
||||||
int one = 1;
|
cork(0);
|
||||||
setsockopt(1, IPPROTO_TCP, TCP_CORK, &one, sizeof(one));
|
# ifdef USE_MMAP
|
||||||
corked = 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_MMAP
|
|
||||||
return serve_mmap(fd);
|
return serve_mmap(fd);
|
||||||
#else
|
# else
|
||||||
return serve_read_write(fd);
|
return serve_read_write(fd);
|
||||||
#endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1337,10 +1335,11 @@ int
|
||||||
main(int argc, char *argv[], const char *const *envp)
|
main(int argc, char *argv[], const char *const *envp)
|
||||||
{
|
{
|
||||||
char headerbuf[MAXHEADERLEN];
|
char headerbuf[MAXHEADERLEN];
|
||||||
char *nurl,
|
char *url,
|
||||||
*origurl;
|
*nurl;
|
||||||
int doauth = 0;
|
int doauth = 0;
|
||||||
int docgi = 0;
|
int docgi = 0;
|
||||||
|
int dokeepalive = 1;
|
||||||
int dirlist = 0;
|
int dirlist = 0;
|
||||||
int redirect = 0;
|
int redirect = 0;
|
||||||
int portappend = 0;
|
int portappend = 0;
|
||||||
|
@ -1349,7 +1348,7 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
{
|
{
|
||||||
int opt;
|
int opt;
|
||||||
|
|
||||||
while (-1 != (opt = getopt(argc, argv, "acdrp"))) {
|
while (-1 != (opt = getopt(argc, argv, "acdhkprv"))) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'a':
|
case 'a':
|
||||||
doauth = 1;
|
doauth = 1;
|
||||||
|
@ -1360,15 +1359,29 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
case 'd':
|
case 'd':
|
||||||
dirlist = 1;
|
dirlist = 1;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'k':
|
||||||
redirect = 1;
|
dokeepalive = 0;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
portappend = 1;
|
portappend = 1;
|
||||||
break;
|
break;
|
||||||
|
case 'r':
|
||||||
|
redirect = 1;
|
||||||
|
break;
|
||||||
|
case 'v':
|
||||||
|
printf(FNORD "\n");
|
||||||
|
return 0;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Usage: %s [-c] [-d] [-r] [-p]\n",
|
fprintf(stderr, "Usage: %s [OPTIONS]\n",
|
||||||
argv[0]);
|
argv[0]);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr, "-a Enable authentication\n");
|
||||||
|
fprintf(stderr, "-c Enable CGI\n");
|
||||||
|
fprintf(stderr, "-d Enable directory listing\n");
|
||||||
|
fprintf(stderr, "-k No default keepalive with HTTP/1.1\n");
|
||||||
|
fprintf(stderr, "-p Append port to hostname directory\n");
|
||||||
|
fprintf(stderr, "-r Enable symlink redirection\n");
|
||||||
|
fprintf(stderr, "-v Print version and exit\n");
|
||||||
return 69;
|
return 69;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1381,10 +1394,12 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
|
|
||||||
handlenext:
|
handlenext:
|
||||||
|
|
||||||
|
/* This is cancelled by later calls to alarm */
|
||||||
alarm(READTIMEOUT);
|
alarm(READTIMEOUT);
|
||||||
|
|
||||||
headerlen = sizeof headerbuf;
|
headerlen = sizeof headerbuf;
|
||||||
// XXX: I need a way to signal that this is a new read with the same buffer.
|
/* Clear static variables. This is such a kludge. I should fix it. */
|
||||||
|
read_header(NULL, NULL, NULL);
|
||||||
switch (read_header(stdin, headerbuf, &headerlen)) {
|
switch (read_header(stdin, headerbuf, &headerlen)) {
|
||||||
case -1:
|
case -1:
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1394,24 +1409,22 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
alarm(0);
|
|
||||||
|
|
||||||
if (headerlen < 10)
|
if (headerlen < 10)
|
||||||
badrequest(400, "Bad Request", "That does not look like HTTP");
|
badrequest(400, "Bad Request", "That does not look like HTTP");
|
||||||
|
|
||||||
if (!memcmp(headerbuf, "GET /", 5)) {
|
if (!memcmp(headerbuf, "GET /", 5)) {
|
||||||
method = GET;
|
method = GET;
|
||||||
url = headerbuf + 4;
|
path = headerbuf + 4;
|
||||||
} else if (!memcmp(headerbuf, "POST /", 6)) {
|
} else if (!memcmp(headerbuf, "POST /", 6)) {
|
||||||
method = POST;
|
method = POST;
|
||||||
url = headerbuf + 5;
|
path = headerbuf + 5;
|
||||||
} else if (!memcmp(headerbuf, "HEAD /", 6)) {
|
} else if (!memcmp(headerbuf, "HEAD /", 6)) {
|
||||||
method = HEAD;
|
method = HEAD;
|
||||||
url = headerbuf + 5;
|
path = headerbuf + 5;
|
||||||
} else
|
} else
|
||||||
badrequest(405, "Method Not Allowed", "Unsupported HTTP method.");
|
badrequest(405, "Method Not Allowed", "Unsupported HTTP method.");
|
||||||
|
|
||||||
origurl = url;
|
url = path;
|
||||||
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
@ -1431,7 +1444,11 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
if (httpversion > 1) {
|
if (httpversion > 1) {
|
||||||
badrequest(400, "Bad Request", "HTTP Version not supported");
|
badrequest(400, "Bad Request", "HTTP Version not supported");
|
||||||
}
|
}
|
||||||
keepalive = 0;
|
if (httpversion > 0) {
|
||||||
|
keepalive = dokeepalive;
|
||||||
|
} else {
|
||||||
|
keepalive = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* demangle path in-place
|
* demangle path in-place
|
||||||
|
@ -1470,10 +1487,7 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
*d = 0;
|
*d = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
strncpy(rpath, url, sizeof rpath);
|
||||||
* XXX: check use of uri to see if it needs to be duplicated
|
|
||||||
*/
|
|
||||||
uri = strdup(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -1665,12 +1679,7 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
if ((method == HEAD))
|
if ((method == HEAD))
|
||||||
badrequest(400, "Bad Request",
|
badrequest(400, "Bad Request",
|
||||||
"Illegal HTTP method for Gateway call.");
|
"Illegal HTTP method for Gateway call.");
|
||||||
#ifdef TCP_CORK
|
cork(1);
|
||||||
{
|
|
||||||
int one = 1;
|
|
||||||
setsockopt(1, IPPROTO_TCP, TCP_CORK, &one, sizeof(one));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
for (i = nurl - url; i > -1; --i) {
|
for (i = nurl - url; i > -1; --i) {
|
||||||
if ((nurl[0] == '/') && (nurl[1] == 'n')
|
if ((nurl[0] == '/') && (nurl[1] == 'n')
|
||||||
&& (nurl[2] == 'p') && (nurl[3] == 'h')
|
&& (nurl[2] == 'p') && (nurl[3] == 'h')
|
||||||
|
@ -1732,6 +1741,11 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
(unsigned long long) st.st_size);
|
(unsigned long long) st.st_size);
|
||||||
}
|
}
|
||||||
fputs("\r\n", stdout);
|
fputs("\r\n", stdout);
|
||||||
|
|
||||||
|
for (; post_len; post_len -= 1) {
|
||||||
|
getchar();
|
||||||
|
}
|
||||||
|
|
||||||
if (method == GET || method == POST) {
|
if (method == GET || method == POST) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -1747,13 +1761,7 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
case 1:
|
case 1:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
#ifdef TCP_CORK
|
cork(0);
|
||||||
if (corked) {
|
|
||||||
int zero = 0;
|
|
||||||
setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero,
|
|
||||||
sizeof(zero));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (keepalive > 0) {
|
if (keepalive > 0) {
|
||||||
close(fd);
|
close(fd);
|
||||||
fchdir(rootdir);
|
fchdir(rootdir);
|
||||||
|
@ -1763,8 +1771,11 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
exit(0);
|
exit(0);
|
||||||
error500:
|
error500:
|
||||||
retcode = 500;
|
retcode = 500;
|
||||||
} else
|
} else {
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retcode = 404;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (retcode) {
|
switch (retcode) {
|
||||||
|
@ -1772,11 +1783,11 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
{
|
{
|
||||||
char *space = alloca(strlen(url) + 2);
|
char *space = alloca(strlen(url) + 2);
|
||||||
|
|
||||||
if (handleindexcgi(url, origurl, space))
|
if (handleindexcgi(url, path, space))
|
||||||
goto indexcgi;
|
goto indexcgi;
|
||||||
handleredirect(url, origurl);
|
handleredirect(url, path);
|
||||||
if (dirlist) {
|
if (dirlist) {
|
||||||
handledirlist(origurl);
|
handledirlist(path);
|
||||||
}
|
}
|
||||||
badrequest(404, "Not Found", "No such file or directory.");
|
badrequest(404, "Not Found", "No such file or directory.");
|
||||||
}
|
}
|
||||||
|
@ -1789,5 +1800,5 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
case 500:
|
case 500:
|
||||||
badrequest(500, "Internal Server Error", "");
|
badrequest(500, "Internal Server Error", "");
|
||||||
}
|
}
|
||||||
return 1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
#! /bin/sh
|
||||||
|
|
||||||
|
: ${HTTPD:=./eris}
|
||||||
|
: ${HTTPD_CGI:=./eris -c}
|
||||||
|
: ${HTTPD_IDX:=./eris -d}
|
||||||
|
|
||||||
|
H () {
|
||||||
|
echo
|
||||||
|
echo "$@"
|
||||||
|
echo "==================================="
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
BR () {
|
||||||
|
echo
|
||||||
|
echo "-----------------------------------"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
title() {
|
||||||
|
printf "* %-50s: " "$1"
|
||||||
|
tests=$(expr $tests + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
successes=0
|
||||||
|
pass () {
|
||||||
|
echo 'ok'
|
||||||
|
successes=$(expr $successes + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
failures=0
|
||||||
|
fail () {
|
||||||
|
echo 'FAIL'
|
||||||
|
failures=$(expr $failures + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
d () {
|
||||||
|
tr '\r\n' '#%'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if [ ! -d default ]; then
|
||||||
|
mkdir default
|
||||||
|
echo james > default/index.html
|
||||||
|
touch default/a
|
||||||
|
cat <<EOD > default/a.cgi
|
||||||
|
#! /bin/sh
|
||||||
|
echo 'Content-type: text/plain'
|
||||||
|
ls / > /dev/null # delay a little
|
||||||
|
echo
|
||||||
|
echo james
|
||||||
|
EOD
|
||||||
|
chmod +x default/a.cgi
|
||||||
|
mkdir empty:80
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "HTTPD: $HTTPD "
|
||||||
|
echo "CGI: $HTTPD_CGI "
|
||||||
|
echo "IDX: $HTTPD_IDX "
|
||||||
|
|
||||||
|
H "Basic tests"
|
||||||
|
|
||||||
|
title "GET"
|
||||||
|
printf 'GET / HTTP/1.0\r\n\r\n' | $HTTPD 2>/dev/null | d | grep -q 'HTTP/1.0 200 OK#%Server: [a-z]*/[0-9.]*#%Content-Type: text/html; charset=UTF-8#%Content-Length: 6#%Last-Modified: ..., .. ... 20.. ..:..:.. GMT#%#%james%' && pass || fail
|
||||||
|
|
||||||
|
title "POST"
|
||||||
|
printf 'POST / HTTP/1.0\r\nContent-Type: a\r\nContent-Length: 5\r\n\r\njames' | $HTTPD 2>/dev/null | d | grep -q 'HTTP/1.0 200 OK#%Server: [a-z]*/[0-9.]*#%Content-Type: text/html; charset=UTF-8#%Content-Length: 6#%Last-Modified: ..., .. ... 20.. ..:..:.. GMT#%#%james%' && pass || fail
|
||||||
|
|
||||||
|
title "Logging /"
|
||||||
|
(printf 'GET / HTTP/1.1\r\nHost: host\r\n\r\n' |
|
||||||
|
PROTO=TCP TCPREMOTEPORT=1234 TCPREMOTEIP=10.0.0.2 $HTTPD >/dev/null) 2>&1 | grep -q '^10.0.0.2 200 6 host (null) (null) /$' && pass || fail
|
||||||
|
|
||||||
|
title "Logging /index.html"
|
||||||
|
(printf 'GET /index.html HTTP/1.1\r\nHost: host\r\n\r\n' |
|
||||||
|
PROTO=TCP TCPREMOTEPORT=1234 TCPREMOTEIP=10.0.0.2 $HTTPD >/dev/null) 2>&1 | grep -q '^10.0.0.2 200 6 host (null) (null) /index.html$' && pass || fail
|
||||||
|
|
||||||
|
H "fnord bugs"
|
||||||
|
|
||||||
|
# 1. Should return directory listing of /; instead segfaults
|
||||||
|
title "Directory indexing of /"
|
||||||
|
printf 'GET / HTTP/1.0\r\nHost: empty\r\n\r\n' | $HTTPD_IDX 2>/dev/null | grep -q 200 && pass || fail
|
||||||
|
|
||||||
|
# 2. Should output \r\n\r\n; instead outputs \r\n\n
|
||||||
|
title "CGI output bare newlines"
|
||||||
|
printf 'GET /a.cgi HTTP/1.0\r\n\r\n' | $HTTPD_CGI 2>/dev/null | d | grep -q '#%#%' && pass || fail
|
||||||
|
|
||||||
|
# 3. Should process both requests; instead drops second
|
||||||
|
title "Multiple requests in one packet"
|
||||||
|
printf 'GET / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\n\r\nGET / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\n\r\n' | $HTTPD 2>/dev/null | grep -c '^HTTP/1.' | grep -q 2 && pass || fail
|
||||||
|
|
||||||
|
# 4. Should return 406 Not Acceptable; instead ignores Accept header
|
||||||
|
title "Accept header"
|
||||||
|
printf 'GET / HTTP/1.0\r\nAccept: nothing\r\n\r\n' | $HTTPD 2>/dev/null | grep 406 && pass || fail
|
||||||
|
|
||||||
|
# 5. Should serve second request as default MIME-Type (text/plain); instead uses previous mime type
|
||||||
|
title "Second MIME-Type"
|
||||||
|
(printf 'GET / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\n\r\n'
|
||||||
|
ls / > /dev/null # Delay required to work around test #3
|
||||||
|
printf 'GET /a HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\n\r\n') | $HTTPD 2>/dev/null | grep -q 'text/plain\|application/octet-stream' && pass || fail
|
||||||
|
|
||||||
|
# 6. Should consume POST data; instead tries to read POST data as second request
|
||||||
|
title "POST to static HTML"
|
||||||
|
(printf 'POST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n';
|
||||||
|
ls / > /dev/null
|
||||||
|
printf 'aPOST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\na') | $HTTPD 2>/dev/null | grep -c '200 OK' | grep -q 2 && pass || fail
|
||||||
|
|
||||||
|
# 7. HTTP/1.1 should default to keepalive; instead connection is closed
|
||||||
|
title "HTTP/1.1 default keepalive"
|
||||||
|
(printf 'GET / HTTP/1.1\r\nHost: a\r\n\r\n'
|
||||||
|
ls / >/dev/null
|
||||||
|
printf 'GET / HTTP/1.1\r\nHost: a\r\n\r\n') | $HTTPD 2>/dev/null | grep -c '^HTTP/' | grep -q 2 && pass || fail
|
||||||
|
|
||||||
|
BR
|
||||||
|
echo "$successes of $tests tests passed ($failures failed)."
|
||||||
|
|
||||||
|
exit $failures
|
|
@ -110,6 +110,11 @@ class CGITests(BasicTests):
|
||||||
self.assertLinesEqual(se, b'10.1.2.3 200 306 default (null) (null) /cgi/set.cgi\n')
|
self.assertLinesEqual(se, b'10.1.2.3 200 306 default (null) (null) /cgi/set.cgi\n')
|
||||||
self.assertLinesEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2.0\r\nPragma: no-cache\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nGATEWAY_INTERFACE:CGI/1.1\nSERVER_PROTOCOL:HTTP/1.0\nSERVER_SOFTWARE:eris/2.0\nSERVER_NAME:default\nSERVER_PORT:80\nREQUEST_METHOD:POST\nREQUEST_URI:/cgi/set.cgi\nSCRIPT_NAME:/cgi/set.cgi\nREMOTE_ADDR:10.1.2.3\nREMOTE_PORT:5858\nCONTENT_TYPE:application/x-www-form-urlencoded\nCONTENT_LENGTH:11\nForm data: a=1&b=2&c=3')
|
self.assertLinesEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2.0\r\nPragma: no-cache\r\nConnection: close\r\nContent-Type: text/plain\r\n\r\nGATEWAY_INTERFACE:CGI/1.1\nSERVER_PROTOCOL:HTTP/1.0\nSERVER_SOFTWARE:eris/2.0\nSERVER_NAME:default\nSERVER_PORT:80\nREQUEST_METHOD:POST\nREQUEST_URI:/cgi/set.cgi\nSCRIPT_NAME:/cgi/set.cgi\nREMOTE_ADDR:10.1.2.3\nREMOTE_PORT:5858\nCONTENT_TYPE:application/x-www-form-urlencoded\nCONTENT_LENGTH:11\nForm data: a=1&b=2&c=3')
|
||||||
|
|
||||||
|
class BufferingTests(BasicTests):
|
||||||
|
def testDoubleGet(self):
|
||||||
|
so, se = self.p.communicate(b'GET / HTTP/1.0\r\n\r\nGET / HTTP/1.0\r\n\r\n')
|
||||||
|
self.assertLinesEqual(so, b'')
|
||||||
|
|
||||||
# XXX: Test posting to static html with keepalive
|
# XXX: Test posting to static html with keepalive
|
||||||
# (it probably won't discard content-length octets)
|
# (it probably won't discard content-length octets)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue