POST working: passing regression tests now

This commit is contained in:
Neale Pickett 2012-02-24 11:47:39 -07:00
parent 03d532240a
commit 4536afe4a6
2 changed files with 113 additions and 99 deletions

209
eris.c
View File

@ -448,133 +448,146 @@ start_cgi(int nph, const char *pathinfo, const char *const *envp)
int pid; int pid;
char cgiheader[BUFFER_SIZE]; char cgiheader[BUFFER_SIZE];
size_t cgiheaderlen = BUFFER_SIZE; size_t cgiheaderlen = BUFFER_SIZE;
int fd[2], int cin[2];
df[2]; int cout[2];
FILE *cin; FILE *cinf;
if (pipe(fd) || pipe(df) || !(cin = fdopen(fd[0], "r"))) { if (pipe(cin) || pipe(cout) || !(cinf = fdopen(cin[0], "rb"))) {
badrequest(500, "Internal Server Error", badrequest(500, "Internal Server Error",
"Server Resource problem."); "Server Resource problem.");
} }
if ((pid = fork())) { pid = fork();
if (pid > 0) { if (-1 == pid) {
badrequest(500, "Internal Server Error",
"Unable to fork.");
}
if (pid) {
/* Parent */
int passthru = nph;
fcntl(cin[0], F_SETFL, O_NONBLOCK);
signal(SIGCHLD, cgi_child);
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
close(cin[1]);
close(cout[0]);
alarm(CGI_TIMEOUT);
while (1) {
int nfds; int nfds;
fd_set rfds, wfds; fd_set rfds, wfds;
int passthru = nph;
fcntl(fd[0], F_SETFL, O_NONBLOCK);
signal(SIGCHLD, cgi_child);
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
close(df[0]);
close(fd[1]);
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_ZERO(&wfds); FD_ZERO(&wfds);
FD_SET(fd[0], &rfds); FD_SET(cin[0], &rfds);
nfds = fd[0]; nfds = cin[0];
if (post_len) { if (post_len) {
/* have post data */ /* have post data */
FD_SET(df[0], &wfds); FD_SET(cout[1], &wfds);
if (df[0] > nfds) { if (cout[1] > nfds) {
nfds = df[0]; nfds = cout[1];
} }
} else if (df[1] >= 0) { } else if (cout[1] >= 0) {
close(df[1]); /* no post data */ close(cout[1]); /* no post data */
df[1] = -1; cout[1] = -1;
} }
while (select(nfds+1, &rfds, &wfds, NULL, NULL) != -1) { if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) {
if (FD_ISSET(fd[0], &rfds)) { break;
if (passthru) { }
size_t len;
/* Re-use this big buffer */ if (FD_ISSET(cin[0], &rfds)) {
len = fread(cgiheader, 1, sizeof cgiheader, cin); if (passthru) {
if (0 == len) { size_t len;
/* CGI is done */
break; /* Re-use this big buffer */
} len = fread(cgiheader, 1, sizeof cgiheader, cinf);
fwrite(cgiheader, 1, len, stdout); if (0 == len) {
size += len; /* CGI is done */
break;
}
fwrite(cgiheader, 1, len, stdout);
size += len;
} else {
int ret;
ret = read_header(cinf, cgiheader, &cgiheaderlen);
if (0 == ret) {
/* Call read_header again */
} else if (-1 == ret) {
/* EOF or error */
badrequest(500, "CGI Error",
"CGI output too weird");
} else { } else {
int ret; /* Entire header is in memory now */
passthru = 1;
ret = read_header(cin, cgiheader, &cgiheaderlen);
if (0 == ret) { /* XXX: I think we need to look for Location:
/* Call read_header again */ * anywhere, but fnord got away with checking
} else if (-1 == ret) { * only the first header field, so I will too.
/* EOF or error */ */
badrequest(500, "CGI Error", if (memcmp(cgiheader, "Location: ", 10) == 0) {
"CGI output too weird"); retcode = 302;
} else { printf
/* Entire header is in memory now */ ("HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n");
passthru = 1;
/* XXX: I think we need to look for Location:
* anywhere, but fnord got away with checking
* only the first header field, so I will too.
*/
if (memcmp(cgiheader, "Location: ", 10) == 0) {
retcode = 302;
printf
("HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n");
fwrite(cgiheader, 1, cgiheaderlen, stdout);
dolog(0);
exit(0);
}
retcode = 200;
printf("HTTP/1.0 200 OK\r\nServer: "
FNORD
"\r\nPragma: no-cache\r\nConnection: close\r\n");
signal(SIGCHLD, SIG_IGN);
fwrite(cgiheader, 1, cgiheaderlen, stdout); fwrite(cgiheader, 1, cgiheaderlen, stdout);
dolog(0);
exit(0);
} }
}
} else if (FD_ISSET(df[1], &wfds)) {
/*
* write to cgi the post data
*/
if (post_len) {
size_t len;
char buf[BUFFER_SIZE];
size_t nmemb = min(BUFFER_SIZE, post_len);
len = fread(buf, 1, nmemb, stdin); retcode = 200;
if (len < 1) { printf("HTTP/1.0 200 OK\r\nServer: "
break; FNORD
} "\r\nPragma: no-cache\r\nConnection: close\r\n");
post_len -= len; signal(SIGCHLD, SIG_IGN);
write(df[1], buf, len); fwrite(cgiheader, 1, cgiheaderlen, stdout);
} else {
close(df[1]);
} }
} }
} } else if (FD_ISSET(cout[1], &wfds)) {
/*
* write to cgi the post data
*/
if (post_len) {
size_t len;
char buf[BUFFER_SIZE];
size_t nmemb = min(BUFFER_SIZE, post_len);
fflush(stdout); len = fread(buf, 1, nmemb, stdin);
dolog(size); if (len < 1) {
#ifdef TCP_CORK break;
{ }
int zero = 0; post_len -= len;
setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero)); write(cout[1], buf, len);
} else {
close(cout[1]);
}
} }
#endif
} }
alarm(0);
fflush(stdout);
dolog(size);
#ifdef TCP_CORK
{
int zero = 0;
setsockopt(1, IPPROTO_TCP, TCP_CORK, &zero, sizeof(zero));
}
#endif
} else { } else {
close(df[1]); /* Child */
close(fd[0]);
dup2(df[0], 0); close(cout[1]);
dup2(fd[1], 1); close(cin[0]);
close(df[0]); dup2(cout[0], 0);
close(fd[1]); dup2(cin[1], 1);
close(cout[0]);
close(cin[1]);
alarm(CGI_TIMEOUT);
do_cgi(pathinfo, envp); do_cgi(pathinfo, envp);
} }
exit(0); exit(0);

View File

@ -70,6 +70,7 @@ class BasicTests(LinesTests):
so, se = self.p.communicate(h.encode('utf-8')) so, se = self.p.communicate(h.encode('utf-8'))
return (so, se) return (so, se)
class DirTests(BasicTests): class DirTests(BasicTests):
args = ['-d'] args = ['-d']
@ -106,7 +107,7 @@ class CGITests(BasicTests):
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.assertLinesEqual(se, b'10.1.2.3 200 330 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')
# XXX: Test posting to static html with keepalive # XXX: Test posting to static html with keepalive