diff --git a/eris.c b/eris.c index 28ffc95..7567fa3 100644 --- a/eris.c +++ b/eris.c @@ -44,7 +44,7 @@ * the following is the time in seconds that fnord should wait for a valid * HTTP request */ -#define READTIMEOUT 20 +#define READTIMEOUT 2 /* * 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 -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; int found = 0; @@ -235,15 +235,24 @@ read_header(int fd, char *buf, size_t buflen) } len += tmp; - for (; p < len; p += 1) { - if (buf[p] == '\n') { - if (++found == 2) { + for (; (found < 2) && (p < len); p += 1) { + switch (buf[p]) { + case '\n': + found += 1; + break; + case '\r': + break; + default: + found = 0; break; - } } } } + if (headerlen) { + *headerlen = p; + } + return len; } @@ -481,10 +490,11 @@ start_cgi(int nph, const char *pathinfo, const char *const *envp) if (startup) { /* XXX: could block :< */ - len = read_header(fd[0], ibuf, sizeof ibuf); + len = read_header(fd[0], ibuf, sizeof ibuf, NULL); } else { len = read(fd[0], ibuf, sizeof ibuf); } + DUMP_d(len); if (0 == len) { break; @@ -1457,8 +1467,8 @@ main(int argc, char *argv[], const char *const *envp) int dirlist = 0; int redirect = 0; int portappend = 0; - int len; - int in; + size_t len; + ssize_t in; { int opt; @@ -1491,48 +1501,9 @@ main(int argc, char *argv[], const char *const *envp) handlenext: encoding = 0; - // alarm(20); + alarm(READTIMEOUT); - { - 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; - } - } - } + in = read_header(0, buf, sizeof buf, &len); if (len < 10) badrequest(400, "Bad Request", "Bad RequestThat does not look like HTTP to me..."); @@ -1678,7 +1649,6 @@ main(int argc, char *argv[], const char *const *envp) "Bad RequestBullshit Host header"); if (host[0] == '.') goto hostb0rken; - // fprintf(stderr,"host %s\n",host); if (keepalive > 0) { if ((rootdir = open(".", O_RDONLY)) < 0) keepalive = -1; diff --git a/tests/test.py b/tests/test.py index f117089..99e5592 100755 --- a/tests/test.py +++ b/tests/test.py @@ -11,12 +11,16 @@ def eris(*args): 'TCPREMOTEPORT': '5858', '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): p = eris(*args) 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.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): "Make sure index.html is the same for all arguments" @@ -30,7 +34,7 @@ class ArgTests(unittest.TestCase): p = eris('-p') 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.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): "Make sure bad arguments actually fail" @@ -38,7 +42,7 @@ class ArgTests(unittest.TestCase): self.assertRaises(AssertionError, self.check_index, '-Z') -class BasicTests(unittest.TestCase): +class BasicTests(LinesTests): args = [] def setUp(self): @@ -62,38 +66,42 @@ class DirTests(BasicTests): def testRootDir(self): 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

Directory Listing: /

\n
\n
\n') - self.assertEqual(se, b'10.1.2.3 200 32 empty (null) (null) /\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

Directory Listing: /

\n
\n
\n') + self.assertLinesEqual(se, b'10.1.2.3 200 32 empty (null) (null) /\n') def testNoTrailingSlash(self): 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\nNot FoundNo such file or directory.') - self.assertEqual(se, b'10.1.2.3 404 0 default (null) (null) /files\n') + 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\nNot FoundNo such file or directory.') + self.assertLinesEqual(se, b'10.1.2.3 404 0 default (null) (null) /files\n') def testFiles(self): 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

Directory Listing: /files/

\n
\nParent directory\n[TXT] 1.txt\n
\n') - self.assertEqual(se, b'10.1.2.3 200 110 default (null) (null) /files/\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

Directory Listing: /files/

\n
\nParent directory\n[TXT] 1.txt\n
\n') + self.assertLinesEqual(se, b'10.1.2.3 200 110 default (null) (null) /files/\n') class CGITests(BasicTests): args = ['-c'] + maxDiff = None def testSet(self): 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.assertEqual(se, b'10.1.2.3 200 242 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:GET\nREQUEST_URI:/cgi/set.cgi\nSCRIPT_NAME:/cgi/set.cgi\nREMOTE_ADDR:10.1.2.3\nREMOTE_PORT:5858\n') + self.assertLinesEqual(se, b'10.1.2.3 200 242 default (null) (null) /cgi/set.cgi\n') def testSetArgs(self): 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.assertEqual(se, b'10.1.2.3 200 267 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: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(se, b'10.1.2.3 200 267 default (null) (null) /cgi/set.cgi\n') def testPost(self): 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.assertEqual(se, b'10.1.2.3 200 330 default (null) (null) /cgi/set.cgi\n') + self.assertLinesEqual(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()