mirror of https://github.com/nealey/eris.git
trying to fix \r\n\n in cgi
This commit is contained in:
parent
db4455f6c9
commit
23123e1cac
72
eris.c
72
eris.c
|
@ -44,7 +44,7 @@
|
||||||
* the following is the time in seconds that fnord should wait for a valid
|
* the following is the time in seconds that fnord should wait for a valid
|
||||||
* HTTP request
|
* HTTP request
|
||||||
*/
|
*/
|
||||||
#define READTIMEOUT 20
|
#define READTIMEOUT 2
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the following is the time in seconds that fnord should wait before
|
* the following is the time in seconds that fnord should wait before
|
||||||
|
@ -217,7 +217,7 @@ elen(register const char *const *e)
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
read_header(int fd, char *buf, size_t buflen)
|
read_header(int fd, char *buf, size_t buflen, size_t *headerlen)
|
||||||
{
|
{
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
int found = 0;
|
int found = 0;
|
||||||
|
@ -235,15 +235,24 @@ read_header(int fd, char *buf, size_t buflen)
|
||||||
}
|
}
|
||||||
len += tmp;
|
len += tmp;
|
||||||
|
|
||||||
for (; p < len; p += 1) {
|
for (; (found < 2) && (p < len); p += 1) {
|
||||||
if (buf[p] == '\n') {
|
switch (buf[p]) {
|
||||||
if (++found == 2) {
|
case '\n':
|
||||||
|
found += 1;
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
found = 0;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (headerlen) {
|
||||||
|
*headerlen = p;
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,10 +490,11 @@ start_cgi(int nph, const char *pathinfo, const char *const *envp)
|
||||||
|
|
||||||
if (startup) {
|
if (startup) {
|
||||||
/* XXX: could block :< */
|
/* XXX: could block :< */
|
||||||
len = read_header(fd[0], ibuf, sizeof ibuf);
|
len = read_header(fd[0], ibuf, sizeof ibuf, NULL);
|
||||||
} else {
|
} else {
|
||||||
len = read(fd[0], ibuf, sizeof ibuf);
|
len = read(fd[0], ibuf, sizeof ibuf);
|
||||||
}
|
}
|
||||||
|
DUMP_d(len);
|
||||||
|
|
||||||
if (0 == len) {
|
if (0 == len) {
|
||||||
break;
|
break;
|
||||||
|
@ -1457,8 +1467,8 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
int dirlist = 0;
|
int dirlist = 0;
|
||||||
int redirect = 0;
|
int redirect = 0;
|
||||||
int portappend = 0;
|
int portappend = 0;
|
||||||
int len;
|
size_t len;
|
||||||
int in;
|
ssize_t in;
|
||||||
|
|
||||||
{
|
{
|
||||||
int opt;
|
int opt;
|
||||||
|
@ -1491,48 +1501,9 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
|
|
||||||
handlenext:
|
handlenext:
|
||||||
encoding = 0;
|
encoding = 0;
|
||||||
// alarm(20);
|
alarm(READTIMEOUT);
|
||||||
|
|
||||||
{
|
in = read_header(0, buf, sizeof buf, &len);
|
||||||
int found = 0;
|
|
||||||
time_t fini,
|
|
||||||
now;
|
|
||||||
struct pollfd duh;
|
|
||||||
|
|
||||||
fini = time(&now) + READTIMEOUT;
|
|
||||||
duh.fd = 0;
|
|
||||||
duh.events = POLLIN;
|
|
||||||
for (in = len = 0; found < 2;) {
|
|
||||||
int tmp;
|
|
||||||
switch (poll(&duh, 1, READTIMEOUT * 1000)) {
|
|
||||||
case 0:
|
|
||||||
if (time(&now) < fini)
|
|
||||||
continue; /* fall through */
|
|
||||||
case -1: /* timeout or error */
|
|
||||||
// badrequest(408,"Request Time-out","No request
|
|
||||||
// appeared
|
|
||||||
// within a reasonable time period.");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
tmp = read(0, buf + len, MAXHEADERLEN - len - 5);
|
|
||||||
if (tmp < 0)
|
|
||||||
return 1;
|
|
||||||
if (tmp == 0)
|
|
||||||
return 1;
|
|
||||||
in += tmp;
|
|
||||||
now = time(0);
|
|
||||||
for (; (found < 2) && (len < in); ++len) {
|
|
||||||
if (buf[len] == '\r')
|
|
||||||
continue;
|
|
||||||
if (buf[len] == '\n')
|
|
||||||
++found;
|
|
||||||
else
|
|
||||||
found = 0;
|
|
||||||
if (found > 1)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (len < 10)
|
if (len < 10)
|
||||||
badrequest(400, "Bad Request",
|
badrequest(400, "Bad Request",
|
||||||
"<title>Bad Request</title>That does not look like HTTP to me...");
|
"<title>Bad Request</title>That does not look like HTTP to me...");
|
||||||
|
@ -1678,7 +1649,6 @@ main(int argc, char *argv[], const char *const *envp)
|
||||||
"<title>Bad Request</title>Bullshit Host header");
|
"<title>Bad Request</title>Bullshit Host header");
|
||||||
if (host[0] == '.')
|
if (host[0] == '.')
|
||||||
goto hostb0rken;
|
goto hostb0rken;
|
||||||
// fprintf(stderr,"host %s\n",host);
|
|
||||||
if (keepalive > 0) {
|
if (keepalive > 0) {
|
||||||
if ((rootdir = open(".", O_RDONLY)) < 0)
|
if ((rootdir = open(".", O_RDONLY)) < 0)
|
||||||
keepalive = -1;
|
keepalive = -1;
|
||||||
|
|
|
@ -11,12 +11,16 @@ def eris(*args):
|
||||||
'TCPREMOTEPORT': '5858',
|
'TCPREMOTEPORT': '5858',
|
||||||
'TCPREMOTEIP': '10.1.2.3'})
|
'TCPREMOTEIP': '10.1.2.3'})
|
||||||
|
|
||||||
class ArgTests(unittest.TestCase):
|
class LinesTests(unittest.TestCase):
|
||||||
|
def assertLinesEqual(self, a, b):
|
||||||
|
self.assertSequenceEqual(a.split(b'\n'), b.split(b'\n'))
|
||||||
|
|
||||||
|
class ArgTests(LinesTests):
|
||||||
def check_index(self, *args):
|
def check_index(self, *args):
|
||||||
p = eris(*args)
|
p = eris(*args)
|
||||||
so, se = p.communicate(b'GET / HTTP/1.0\r\n\r\n')
|
so, se = p.communicate(b'GET / HTTP/1.0\r\n\r\n')
|
||||||
self.assertRegexpMatches(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 6\r\nLast-Modified: (Mon|Tue|Wed|Thu|Fri|Sat|Sun), .. (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2... ..:..:.. GMT\r\n\r\njames\n')
|
self.assertRegexpMatches(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 6\r\nLast-Modified: (Mon|Tue|Wed|Thu|Fri|Sat|Sun), .. (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2... ..:..:.. GMT\r\n\r\njames\n')
|
||||||
self.assertEqual(se, b'10.1.2.3 200 6 127.0.0.1 (null) (null) /index.html\n')
|
self.assertLinesEqual(se, b'10.1.2.3 200 6 127.0.0.1 (null) (null) /index.html\n')
|
||||||
|
|
||||||
def testArgs(self):
|
def testArgs(self):
|
||||||
"Make sure index.html is the same for all arguments"
|
"Make sure index.html is the same for all arguments"
|
||||||
|
@ -30,7 +34,7 @@ class ArgTests(unittest.TestCase):
|
||||||
p = eris('-p')
|
p = eris('-p')
|
||||||
so, se = p.communicate(b'GET / HTTP/1.0\r\n\r\n')
|
so, se = p.communicate(b'GET / HTTP/1.0\r\n\r\n')
|
||||||
self.assertRegexpMatches(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 6\r\nLast-Modified: (Mon|Tue|Wed|Thu|Fri|Sat|Sun), .. (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2... ..:..:.. GMT\r\n\r\njames\n')
|
self.assertRegexpMatches(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nContent-Type: text/html; charset=UTF-8\r\nContent-Length: 6\r\nLast-Modified: (Mon|Tue|Wed|Thu|Fri|Sat|Sun), .. (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) 2... ..:..:.. GMT\r\n\r\njames\n')
|
||||||
self.assertEqual(se, b'10.1.2.3 200 6 127.0.0.1:80 (null) (null) /index.html\n')
|
self.assertLinesEqual(se, b'10.1.2.3 200 6 127.0.0.1:80 (null) (null) /index.html\n')
|
||||||
|
|
||||||
def testBadArgs(self):
|
def testBadArgs(self):
|
||||||
"Make sure bad arguments actually fail"
|
"Make sure bad arguments actually fail"
|
||||||
|
@ -38,7 +42,7 @@ class ArgTests(unittest.TestCase):
|
||||||
self.assertRaises(AssertionError, self.check_index, '-Z')
|
self.assertRaises(AssertionError, self.check_index, '-Z')
|
||||||
|
|
||||||
|
|
||||||
class BasicTests(unittest.TestCase):
|
class BasicTests(LinesTests):
|
||||||
args = []
|
args = []
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -62,38 +66,42 @@ class DirTests(BasicTests):
|
||||||
|
|
||||||
def testRootDir(self):
|
def testRootDir(self):
|
||||||
so, se = self.get('/', 'empty')
|
so, se = self.get('/', 'empty')
|
||||||
self.assertEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<h3>Directory Listing: /</h3>\n<pre>\n</pre>\n')
|
self.assertLinesEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<h3>Directory Listing: /</h3>\n<pre>\n</pre>\n')
|
||||||
self.assertEqual(se, b'10.1.2.3 200 32 empty (null) (null) /\n')
|
self.assertLinesEqual(se, b'10.1.2.3 200 32 empty (null) (null) /\n')
|
||||||
|
|
||||||
def testNoTrailingSlash(self):
|
def testNoTrailingSlash(self):
|
||||||
so, se = self.get('/files', 'default')
|
so, se = self.get('/files', 'default')
|
||||||
self.assertEqual(so, b'HTTP/1.0 404 Not Found\r\nConnection: close\r\nContent-Length: 50\r\nContent-Type: text/html\r\n\r\n<title>Not Found</title>No such file or directory.')
|
self.assertLinesEqual(so, b'HTTP/1.0 404 Not Found\r\nConnection: close\r\nContent-Length: 50\r\nContent-Type: text/html\r\n\r\n<title>Not Found</title>No such file or directory.')
|
||||||
self.assertEqual(se, b'10.1.2.3 404 0 default (null) (null) /files\n')
|
self.assertLinesEqual(se, b'10.1.2.3 404 0 default (null) (null) /files\n')
|
||||||
|
|
||||||
def testFiles(self):
|
def testFiles(self):
|
||||||
so, se = self.get('/files/', 'default')
|
so, se = self.get('/files/', 'default')
|
||||||
|
|
||||||
self.assertEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<h3>Directory Listing: /files/</h3>\n<pre>\n<a href="/">Parent directory</a>\n[TXT] <a href="1.txt">1.txt</a>\n</pre>\n')
|
self.assertLinesEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\r\nConnection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\n<h3>Directory Listing: /files/</h3>\n<pre>\n<a href="/">Parent directory</a>\n[TXT] <a href="1.txt">1.txt</a>\n</pre>\n')
|
||||||
self.assertEqual(se, b'10.1.2.3 200 110 default (null) (null) /files/\n')
|
self.assertLinesEqual(se, b'10.1.2.3 200 110 default (null) (null) /files/\n')
|
||||||
|
|
||||||
|
|
||||||
class CGITests(BasicTests):
|
class CGITests(BasicTests):
|
||||||
args = ['-c']
|
args = ['-c']
|
||||||
|
maxDiff = None
|
||||||
|
|
||||||
def testSet(self):
|
def testSet(self):
|
||||||
so, se = self.get('/cgi/set.cgi', 'default')
|
so, se = self.get('/cgi/set.cgi', 'default')
|
||||||
self.assertEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\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\nSERVER_NAME:default\nSERVER_PORT:80\nREQUEST_METHOD:GET\nREQUEST_URI:/cgi/set.cgi\nSCRIPT_NAME:/cgi/set.cgi\nREMOTE_ADDR:10.1.2.3\nREMOTE_PORT:5858\n')
|
self.assertLinesEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\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\nSERVER_NAME:default\nSERVER_PORT:80\nREQUEST_METHOD:GET\nREQUEST_URI:/cgi/set.cgi\nSCRIPT_NAME:/cgi/set.cgi\nREMOTE_ADDR:10.1.2.3\nREMOTE_PORT:5858\n')
|
||||||
self.assertEqual(se, b'10.1.2.3 200 242 default (null) (null) /cgi/set.cgi\n')
|
self.assertLinesEqual(se, b'10.1.2.3 200 242 default (null) (null) /cgi/set.cgi\n')
|
||||||
|
|
||||||
def testSetArgs(self):
|
def testSetArgs(self):
|
||||||
so, se = self.get('/cgi/set.cgi?a=1&b=2&c=3', 'default')
|
so, se = self.get('/cgi/set.cgi?a=1&b=2&c=3', 'default')
|
||||||
self.assertEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\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\nSERVER_NAME:default\nSERVER_PORT:80\nREQUEST_METHOD:GET\nREQUEST_URI:/cgi/set.cgi\nSCRIPT_NAME:/cgi/set.cgi\nREMOTE_ADDR:10.1.2.3\nREMOTE_PORT:5858\nQUERY_STRING:a=1&b=2&c=3\n')
|
self.assertLinesEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\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\nSERVER_NAME:default\nSERVER_PORT:80\nREQUEST_METHOD:GET\nREQUEST_URI:/cgi/set.cgi\nSCRIPT_NAME:/cgi/set.cgi\nREMOTE_ADDR:10.1.2.3\nREMOTE_PORT:5858\nQUERY_STRING:a=1&b=2&c=3\n')
|
||||||
self.assertEqual(se, b'10.1.2.3 200 267 default (null) (null) /cgi/set.cgi\n')
|
self.assertLinesEqual(se, b'10.1.2.3 200 267 default (null) (null) /cgi/set.cgi\n')
|
||||||
|
|
||||||
def testPost(self):
|
def testPost(self):
|
||||||
so, se = self.post('/cgi/set.cgi', 'default', 'a=1&b=2&c=3')
|
so, se = self.post('/cgi/set.cgi', 'default', 'a=1&b=2&c=3')
|
||||||
self.assertEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\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\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(se, b'10.1.2.3 200 330 default (null) (null) /cgi/set.cgi\n')
|
||||||
self.assertEqual(se, b'10.1.2.3 200 330 default (null) (null) /cgi/set.cgi\n')
|
self.assertLinesEqual(so, b'HTTP/1.0 200 OK\r\nServer: eris/2\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\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')
|
||||||
|
|
||||||
|
# XXX: Test posting to static html with keepalive
|
||||||
|
# (it probably won't discard content-length octets)
|
||||||
|
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue