CGI passing some tests

This commit is contained in:
Neale Pickett 2012-03-07 22:16:02 -07:00
parent c5550f134b
commit ba030d7917
3 changed files with 70 additions and 67 deletions

126
cgi.c
View File

@ -15,12 +15,12 @@ cgi_child(const char *relpath)
setenv("REMOTE_ADDR", remote_ip, 1); setenv("REMOTE_ADDR", remote_ip, 1);
setenv("REMOTE_PORT", remote_port, 1); setenv("REMOTE_PORT", remote_port, 1);
setenv("REMOTE_IDENT", remote_ident, 1); setenv("REMOTE_IDENT", remote_ident, 1);
setenv("CONTENT_TYPE", content_type, 1); if (content_length) {
{
char cl[20]; char cl[20];
snprintf(cl, sizeof cl, "%llu", (unsigned long long) content_length); snprintf(cl, sizeof cl, "%llu", (unsigned long long) content_length);
setenv("CONTENT_LENGTH", cl, 1); setenv("CONTENT_LENGTH", cl, 1);
setenv("CONTENT_TYPE", content_type, 1);
} }
execl(relpath, relpath, NULL); execl(relpath, relpath, NULL);
@ -28,44 +28,45 @@ cgi_child(const char *relpath)
} }
void void
cgi_parent(int cin, int cout) cgi_parent(int cin, int cout, int passthru)
{ {
char cgiheader[BUFFER_SIZE];
size_t cgiheaderlen = 0;
FILE *cinf = fdopen(cin, "rb"); FILE *cinf = fdopen(cin, "rb");
int passthru = nph; size_t size = 0;
int header_sent = 0;
fcntl(child_in, F_SETFL, O_NONBLOCK); fcntl(cin, F_SETFL, O_NONBLOCK);
signal(SIGCHLD, sigchld); signal(SIGCHLD, sigchld);
signal(SIGPIPE, SIG_IGN); /* NO! no signal! */ signal(SIGPIPE, SIG_IGN); /* NO! no signal! */
/* Eris is not this smart yet */
keepalive = 0;
while (1) { while (1) {
int nfds; int nfds;
fd_set rfds, wfds; fd_set rfds, wfds;
FD_ZERO(&rfds); FD_ZERO(&rfds);
FD_ZERO(&wfds); FD_ZERO(&wfds);
FD_SET(cin[0], &rfds); FD_SET(cin, &rfds);
nfds = cin[0]; nfds = cin;
if (post_len) { if (content_length) {
/* have post data */ /* have post data */
FD_SET(cout[1], &wfds); FD_SET(cout, &wfds);
if (cout[1] > nfds) { if (cout > nfds) {
nfds = cout[1]; nfds = cout;
} }
} else if (cout[1] >= 0) { } else if (cout >= 0) {
close(cout[1]); /* no post data */ close(cout); /* no post data */
cout[1] = -1; cout = -1;
} }
if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) { if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) {
break; break;
} }
if (FD_ISSET(cin[0], &rfds)) { if (FD_ISSET(cin, &rfds)) {
if (passthru) { if (passthru) {
/* Pass everything through verbatim */
size_t len; size_t len;
/* Re-use this big buffer */ /* Re-use this big buffer */
@ -77,72 +78,74 @@ cgi_parent(int cin, int cout)
fwrite(cgiheader, 1, len, stdout); fwrite(cgiheader, 1, len, stdout);
size += len; size += len;
} else { } else {
int ret; /* Interpret header fields */
size_t readlen = (sizeof cgiheader) - cgiheaderlen;
ret = read_header(cinf, cgiheader, &cgiheaderlen); if (NULL == fgets(cgiheader + cgiheaderlen, readlen, cinf)) {
if (0 == ret) {
/* Call read_header again */
} else if (-1 == ret) {
/* EOF or error */ /* EOF or error */
badrequest(500, "CGI Error", badrequest(500, "CGI Error", "CGI output too weird");
"CGI output too weird"); }
} else { cgiheaderlen = strlen(cgiheader);
/* Entire header is in memory now */
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; if ('\n' == cgiheader[cgiheaderlen - 1]) {
printf("HTTP/1.0 200 OK\r\nServer: " /* We read a whole line */
FNORD size_t len;
"\r\nPragma: no-cache\r\nConnection: close\r\n"); char *val;
signal(SIGCHLD, SIG_IGN);
fwrite(cgiheader, 1, cgiheaderlen, stdout); len = extract_header_field(cgiheader, &val, 0);
if (! len) {
/* We've read the entire header block */
passthru = 1;
eoh();
} else {
if (! header_sent) {
if (! strcasecmp(cgiheader, "Location")) {
header(302, "CGI Redirect");
fwrite(cgiheader, 1, cgiheaderlen, stdout);
dolog(302, 0);
exit(0);
}
header(200, "OK");
printf("Pragma: no-cache\r\n");
header_sent = 1;
}
printf("%s: %s\r\n", cgiheader, val);
cgiheaderlen = 0;
}
} }
} }
} else if (FD_ISSET(cout[1], &wfds)) { } else if (FD_ISSET(cout, &wfds)) {
/* /*
* write to cgi the post data * write to cgi the post data
*/ */
if (post_len) { if (content_length) {
size_t len; size_t len;
char buf[BUFFER_SIZE]; char buf[BUFFER_SIZE];
size_t nmemb = min(BUFFER_SIZE, post_len); size_t nmemb = min(BUFFER_SIZE, content_length);
len = fread(buf, 1, nmemb, stdin); len = fread(buf, 1, nmemb, stdin);
if (len < 1) { if (len < 1) {
break; break;
} }
post_len -= len; content_length -= len;
write(cout[1], buf, len); write(cout, buf, len);
} else { } else {
close(cout[1]); close(cout);
} }
} }
} }
alarm(0);
fflush(stdout); fflush(stdout);
dolog(size); dolog(200, size);
cork(0); cork(0);
} }
void void
serve_cgi(char *relpath) serve_cgi(char *relpath)
{ {
size_t size = 0;
int pid; int pid;
char buf[BUFFER_SIZE];
int cin[2]; int cin[2];
int cout[2]; int cout[2];
@ -158,9 +161,14 @@ serve_cgi(char *relpath)
close(cin[1]); close(cin[1]);
close(cout[0]); close(cout[0]);
alarm(CHILD_TIMEOUT); /* Eris is not this smart yet */
cgi_parent(cin[0], cout[1]); keepalive = 0;
alarm(CGI_TIMEOUT);
cgi_parent(cin[0], cout[1], 0);
alarm(0); alarm(0);
exit(0);
} else { } else {
close(cwd); close(cwd);
close(cout[1]); close(cout[1]);

9
eris.c
View File

@ -102,7 +102,6 @@ char stdout_buf[BUFFER_SIZE];
#include "strings.c" #include "strings.c"
#include "mime.c" #include "mime.c"
#include "time.c" #include "time.c"
#include "cgi.c"
/* /*
* TCP_CORK is a Linux extension to work around a TCP problem. * TCP_CORK is a Linux extension to work around a TCP problem.
@ -173,6 +172,8 @@ badrequest(long code, const char *httpcomment, const char *message)
exit(0); exit(0);
} }
#include "cgi.c"
void void
not_found() not_found()
{ {
@ -408,12 +409,6 @@ serve_idx(int fd, char *path)
fflush(stdout); fflush(stdout);
} }
void
serve_cgi(char *path)
{
DUMP_s(path);
}
void void
find_serve_file(char *relpath) find_serve_file(char *relpath)
{ {

View File

@ -124,7 +124,7 @@ printf 'GET /empty/ HTTP/1.0\r\n\r\n' | $HTTPD_IDX 2>/dev/null | d | grep -Fq '<
H "CGI" H "CGI"
title "Basic CGI" title "Basic CGI"
printf 'GET /a.cgi HTTP/1.0\r\n\r\n' | $HTTPD_CGI 2>/dev/null | d | grep -q 'HTTP/1.0 200 OK#%Server: .*#%Pragma: no-cache#%Connection: close#%Content-type: text/plain#%#%james%' && pass || fail printf 'GET /a.cgi HTTP/1.0\r\n\r\n' | $HTTPD_CGI 2>/dev/null | d | grep -q 'HTTP/1.0 200 OK#%Server: .*#%Connection: close#%Pragma: no-cache#%Content-type: text/plain#%#%james%' && pass || fail
title "GET with arguments" title "GET with arguments"
printf 'GET /a.cgi?foo HTTP/1.0\r\n\r\n' | $HTTPD_CGI 2>/dev/null | d | grep -q 'HTTP/1.0 200 OK#%Server: .*#%Pragma: no-cache#%Connection: close#%Content-type: text/plain#%#%foo%' && pass || fail printf 'GET /a.cgi?foo HTTP/1.0\r\n\r\n' | $HTTPD_CGI 2>/dev/null | d | grep -q 'HTTP/1.0 200 OK#%Server: .*#%Pragma: no-cache#%Connection: close#%Content-type: text/plain#%#%foo%' && pass || fail