From ba030d7917013799162cc4b809c275521a61d43f Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Wed, 7 Mar 2012 22:16:02 -0700 Subject: [PATCH] CGI passing some tests --- cgi.c | 126 ++++++++++++++++++++++++++++++-------------------------- eris.c | 9 +--- test.sh | 2 +- 3 files changed, 70 insertions(+), 67 deletions(-) diff --git a/cgi.c b/cgi.c index e66dc9d..7801e79 100644 --- a/cgi.c +++ b/cgi.c @@ -15,12 +15,12 @@ cgi_child(const char *relpath) setenv("REMOTE_ADDR", remote_ip, 1); setenv("REMOTE_PORT", remote_port, 1); setenv("REMOTE_IDENT", remote_ident, 1); - setenv("CONTENT_TYPE", content_type, 1); - { + if (content_length) { char cl[20]; snprintf(cl, sizeof cl, "%llu", (unsigned long long) content_length); setenv("CONTENT_LENGTH", cl, 1); + setenv("CONTENT_TYPE", content_type, 1); } execl(relpath, relpath, NULL); @@ -28,44 +28,45 @@ cgi_child(const char *relpath) } 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"); - 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(SIGPIPE, SIG_IGN); /* NO! no signal! */ - /* Eris is not this smart yet */ - keepalive = 0; - while (1) { int nfds; fd_set rfds, wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); - FD_SET(cin[0], &rfds); - nfds = cin[0]; + FD_SET(cin, &rfds); + nfds = cin; - if (post_len) { + if (content_length) { /* have post data */ - FD_SET(cout[1], &wfds); - if (cout[1] > nfds) { - nfds = cout[1]; + FD_SET(cout, &wfds); + if (cout > nfds) { + nfds = cout; } - } else if (cout[1] >= 0) { - close(cout[1]); /* no post data */ - cout[1] = -1; + } else if (cout >= 0) { + close(cout); /* no post data */ + cout = -1; } if (-1 == select(nfds+1, &rfds, &wfds, NULL, NULL)) { break; } - if (FD_ISSET(cin[0], &rfds)) { + if (FD_ISSET(cin, &rfds)) { if (passthru) { + /* Pass everything through verbatim */ size_t len; /* Re-use this big buffer */ @@ -77,72 +78,74 @@ cgi_parent(int cin, int cout) fwrite(cgiheader, 1, len, stdout); size += len; } else { - int ret; + /* Interpret header fields */ + size_t readlen = (sizeof cgiheader) - cgiheaderlen; - ret = read_header(cinf, cgiheader, &cgiheaderlen); - if (0 == ret) { - /* Call read_header again */ - } else if (-1 == ret) { + if (NULL == fgets(cgiheader + cgiheaderlen, readlen, cinf)) { /* EOF or error */ - badrequest(500, "CGI Error", - "CGI output too weird"); - } else { - /* 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); - } + badrequest(500, "CGI Error", "CGI output too weird"); + } + cgiheaderlen = strlen(cgiheader); - 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); + if ('\n' == cgiheader[cgiheaderlen - 1]) { + /* We read a whole line */ + size_t len; + char *val; + + 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 */ - if (post_len) { + if (content_length) { size_t len; 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); if (len < 1) { break; } - post_len -= len; - write(cout[1], buf, len); + content_length -= len; + write(cout, buf, len); } else { - close(cout[1]); + close(cout); } } } - alarm(0); fflush(stdout); - dolog(size); + dolog(200, size); cork(0); } + void serve_cgi(char *relpath) { - size_t size = 0; int pid; - char buf[BUFFER_SIZE]; int cin[2]; int cout[2]; @@ -158,9 +161,14 @@ serve_cgi(char *relpath) close(cin[1]); close(cout[0]); - alarm(CHILD_TIMEOUT); - cgi_parent(cin[0], cout[1]); + /* Eris is not this smart yet */ + keepalive = 0; + + alarm(CGI_TIMEOUT); + cgi_parent(cin[0], cout[1], 0); alarm(0); + + exit(0); } else { close(cwd); close(cout[1]); diff --git a/eris.c b/eris.c index ec312f5..5e56b46 100644 --- a/eris.c +++ b/eris.c @@ -102,7 +102,6 @@ char stdout_buf[BUFFER_SIZE]; #include "strings.c" #include "mime.c" #include "time.c" -#include "cgi.c" /* * 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); } +#include "cgi.c" + void not_found() { @@ -408,12 +409,6 @@ serve_idx(int fd, char *path) fflush(stdout); } -void -serve_cgi(char *path) -{ - DUMP_s(path); -} - void find_serve_file(char *relpath) { diff --git a/test.sh b/test.sh index 685edd6..0e75506 100755 --- a/test.sh +++ b/test.sh @@ -124,7 +124,7 @@ printf 'GET /empty/ HTTP/1.0\r\n\r\n' | $HTTPD_IDX 2>/dev/null | d | grep -Fq '< H "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" 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