mirror of https://github.com/nealey/eris.git
CGI beginning to work again
This commit is contained in:
parent
688d7c9b7e
commit
03d532240a
3
CHANGES
3
CHANGES
|
@ -1,4 +1,7 @@
|
|||
2.0:
|
||||
Replace poll with select, which is more portable and may be
|
||||
slightly faster; however, it's only called for CGI and by
|
||||
that point you've lost quite a bit in terms of speed
|
||||
Remove Accept header parsing: it was broken and the result was
|
||||
that the Accept header had no effect
|
||||
Remove the .gz trick: I never used it, but I would not be averse
|
||||
|
|
|
@ -98,6 +98,13 @@ title "Second MIME-Type"
|
|||
ls / > /dev/null # Delay required to work around test #3
|
||||
printf 'GET /a HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\n\r\n') | $HTTPD 2>/dev/null | grep -q 'text/plain\|application/octet-stream' && pass || fail
|
||||
|
||||
# 6. Should consume POST data; instead tries to read POST data as second request
|
||||
title "POST to static HTML"
|
||||
(printf 'POST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\n';
|
||||
ls / > /dev/null
|
||||
printf 'aPOST / HTTP/1.1\r\nHost: a\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nContent-Length: 1\r\n\r\na') | $HTTPD 2>/dev/null | grep -c '^HTTP/1.' | grep -q 2 && pass || fail
|
||||
|
||||
|
||||
cat <<EOD
|
||||
-----------------------------------------
|
||||
$successes of $tests tests passed ($failures failed).
|
||||
|
|
305
eris.c
305
eris.c
|
@ -14,7 +14,7 @@
|
|||
#include <sys/wait.h>
|
||||
#include <grp.h>
|
||||
#include <errno.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
@ -22,16 +22,14 @@
|
|||
#include <sys/mman.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Some things I use for debugging
|
||||
*/
|
||||
#define XXNODUMP
|
||||
|
||||
#ifndef NODUMP
|
||||
#define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args)
|
||||
#else
|
||||
#define DUMPf(fmt, args...)
|
||||
#endif
|
||||
#define DUMP() DUMPf("")
|
||||
#define DUMP_d(v) DUMPf("%s = %d", #v, v)
|
||||
#define DUMP_u(v) DUMPf("%s = %u", #v, v)
|
||||
|
@ -55,33 +53,39 @@
|
|||
|
||||
#define CGI_TIMEOUT (5*60) /* 5 minutes time-out for CGI to complete */
|
||||
|
||||
#ifdef __linux__
|
||||
/*
|
||||
* defining USE_SENDFILE enables zero-copy TCP on Linux for static files.
|
||||
* I measured over 320 meg per second with apache bench over localhost
|
||||
* with sendfile and keep-alive. However, sendfile does not work with
|
||||
* large files and may be considered cheating ;-) Also, sendfile is a
|
||||
* blocking operation. Thus, no timeout handling.
|
||||
* I (Fefe) measured over 320 meg per second with apache bench over localhost
|
||||
* with sendfile and keep-alive.
|
||||
*/
|
||||
#define USE_SENDFILE
|
||||
|
||||
#ifndef __linux__
|
||||
#undef USE_SENDFILE
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENDFILE
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Memory-mapping may result in a performance boost. thttpd does it,
|
||||
* but for a different reason (to cache frequently-accessed files).
|
||||
* XXX: Some performance testing is in order here.
|
||||
*/
|
||||
#ifdef _POSIX_MAPPED_FILES
|
||||
#define USE_MMAP
|
||||
#ifndef _POSIX_MAPPED_FILES
|
||||
#undef USE_MMAP
|
||||
#endif
|
||||
|
||||
enum { UNKNOWN, GET, HEAD, POST } method;
|
||||
/*
|
||||
* TCP_CORK is a Linux extension to work around a TCP problem.
|
||||
* http://www.baus.net/on-tcp_cork has a good description.
|
||||
* XXX: Since we do our own buffering, TCP_CORK may not be helping
|
||||
* with anything. This needs testing.
|
||||
*/
|
||||
|
||||
#ifdef TCP_CORK
|
||||
static int corked;
|
||||
#endif
|
||||
|
||||
enum { UNKNOWN, GET, HEAD, POST } method;
|
||||
|
||||
static long retcode = 404; /* used for logging code */
|
||||
char *host = "?"; /* Host: header */
|
||||
char *port; /* also Host: header, :80 part */
|
||||
|
@ -100,8 +104,6 @@ char *uri; /* copy of url before demangling */
|
|||
char *content_type;
|
||||
char *content_len;
|
||||
char *auth_type;
|
||||
char *post_miss;
|
||||
unsigned long post_mlen;
|
||||
unsigned long post_len = 0;
|
||||
|
||||
#if _FILE_OFFSET_BITS == 64
|
||||
|
@ -123,8 +125,8 @@ char *remote_ip;
|
|||
char *remote_port;
|
||||
char *remote_ident;
|
||||
|
||||
#define BUFFER_OUTSIZE 8192
|
||||
char stdout_buf[BUFFER_OUTSIZE];
|
||||
#define BUFFER_SIZE 8192
|
||||
char stdout_buf[BUFFER_SIZE];
|
||||
|
||||
static void
|
||||
sanitize(char *ua)
|
||||
|
@ -226,20 +228,31 @@ elen(register const char *const *e)
|
|||
static int
|
||||
read_header(FILE *f, char *buf, size_t * buflen)
|
||||
{
|
||||
/*
|
||||
* I'm not crazy about all these static variables. But the idea,
|
||||
* which seems to work, is that you pass in things like it was
|
||||
* a read call, and then just keep passing that same stuff in over
|
||||
* and over until it returns 0.
|
||||
*
|
||||
* Further down this winds up looking pretty nice. In here, sort
|
||||
* of gross.
|
||||
*/
|
||||
|
||||
static char *lastbuf = NULL;
|
||||
static int found = 0;
|
||||
static int bare = 1; /* LF here would be bare */
|
||||
int bufsize = *buflen;
|
||||
static int bufsize = 0;
|
||||
|
||||
if (lastbuf != buf) {
|
||||
lastbuf = buf;
|
||||
bare = 1;
|
||||
found = 0;
|
||||
}
|
||||
bufsize = *buflen;
|
||||
*buflen = 0;
|
||||
}
|
||||
|
||||
while (*buflen + bare < bufsize) {
|
||||
int c = getchar();
|
||||
int c = fgetc(f);
|
||||
|
||||
switch (c) {
|
||||
case EOF:
|
||||
|
@ -384,10 +397,12 @@ do_cgi(const char *pathinfo, const char *const *envp)
|
|||
char tmp[PATH_MAX];
|
||||
|
||||
i = strrchr(url, '/') - url;
|
||||
if (i) {
|
||||
strncpy(tmp, url + 1, i);
|
||||
tmp[i] = 0;
|
||||
chdir(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char tmp[PATH_MAX];
|
||||
|
@ -425,175 +440,117 @@ cgi_child(int sig)
|
|||
signal(SIGCHLD, cgi_child);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert bare \n to \r\n in header. Return 0 if header is over.
|
||||
*/
|
||||
static int
|
||||
cgi_send_correct_http(const char *s, unsigned int sl)
|
||||
{
|
||||
unsigned int i;
|
||||
int newline = 0;
|
||||
|
||||
for (i = 0; i < sl; i += 1) {
|
||||
switch (s[i]) {
|
||||
case '\r':
|
||||
if (s[i + 1] == '\n') {
|
||||
i += 1;
|
||||
case '\n':
|
||||
printf("\r\n");
|
||||
if (newline) {
|
||||
fwrite(s + i + 1, sl - i - 1, 1, stdout);
|
||||
return 0;
|
||||
} else {
|
||||
newline = 1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
default:
|
||||
newline = 0;
|
||||
putchar(s[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
start_cgi(int nph, const char *pathinfo, const char *const *envp)
|
||||
{
|
||||
// XXX: Is it safe to reuse headerbuf from main?
|
||||
size_t size = 0;
|
||||
int n;
|
||||
int pid;
|
||||
char ibuf[8192],
|
||||
obuf[8192];
|
||||
char cgiheader[BUFFER_SIZE];
|
||||
size_t cgiheaderlen = BUFFER_SIZE;
|
||||
int fd[2],
|
||||
df[2];
|
||||
FILE *cin;
|
||||
|
||||
if (pipe(fd) || pipe(df)) {
|
||||
if (pipe(fd) || pipe(df) || !(cin = fdopen(fd[0], "r"))) {
|
||||
badrequest(500, "Internal Server Error",
|
||||
"Server Resource problem.");
|
||||
}
|
||||
|
||||
if ((pid = fork())) {
|
||||
if (pid > 0) {
|
||||
struct pollfd pfd[2];
|
||||
int nr = 1;
|
||||
int startup = 1;
|
||||
int nfds;
|
||||
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]);
|
||||
|
||||
pfd[0].fd = fd[0];
|
||||
pfd[0].events = POLLIN;
|
||||
pfd[0].revents = 0;
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(fd[0], &rfds);
|
||||
nfds = fd[0];
|
||||
|
||||
pfd[1].fd = df[1];
|
||||
pfd[1].events = POLLOUT;
|
||||
pfd[1].revents = 0;
|
||||
|
||||
if (post_len)
|
||||
++nr; /* have post data */
|
||||
else
|
||||
if (post_len) {
|
||||
/* have post data */
|
||||
FD_SET(df[0], &wfds);
|
||||
if (df[0] > nfds) {
|
||||
nfds = df[0];
|
||||
}
|
||||
} else if (df[1] >= 0) {
|
||||
close(df[1]); /* no post data */
|
||||
df[1] = -1;
|
||||
}
|
||||
|
||||
while (poll(pfd, nr, -1) != -1) {
|
||||
/*
|
||||
* read from cgi
|
||||
*/
|
||||
if (pfd[0].revents & POLLIN) {
|
||||
while (select(nfds+1, &rfds, &wfds, NULL, NULL) != -1) {
|
||||
if (FD_ISSET(fd[0], &rfds)) {
|
||||
if (passthru) {
|
||||
size_t len;
|
||||
|
||||
if (startup) {
|
||||
/*
|
||||
* XXX: could block :<
|
||||
*/
|
||||
// len = read_header(fd[0], ibuf, sizeof ibuf,
|
||||
// NULL);
|
||||
len = 0;
|
||||
} else {
|
||||
len = read(fd[0], ibuf, sizeof ibuf);
|
||||
}
|
||||
DUMP_u((unsigned int) len);
|
||||
|
||||
/* Re-use this big buffer */
|
||||
len = fread(cgiheader, 1, sizeof cgiheader, cin);
|
||||
if (0 == len) {
|
||||
/* CGI is done */
|
||||
break;
|
||||
}
|
||||
if (len == -1) {
|
||||
goto cgi_500;
|
||||
}
|
||||
fwrite(cgiheader, 1, len, stdout);
|
||||
size += len;
|
||||
} else {
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* startup
|
||||
ret = read_header(cin, 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 {
|
||||
/* 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 (startup) {
|
||||
if (nph) { /* NPH-CGI */
|
||||
startup = 0;
|
||||
printf("%.*s", (int) len, ibuf);
|
||||
/*
|
||||
* skip HTTP/x.x
|
||||
*/
|
||||
retcode = strtoul(ibuf + 9, NULL, 10);
|
||||
} else { /* CGI */
|
||||
if (memcmp(ibuf, "Location: ", 10) == 0) {
|
||||
if (memcmp(cgiheader, "Location: ", 10) == 0) {
|
||||
retcode = 302;
|
||||
printf
|
||||
("HTTP/1.0 302 CGI-Redirect\r\nConnection: close\r\n");
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
cgi_send_correct_http(ibuf, n);
|
||||
fflush(stdout);
|
||||
fwrite(cgiheader, 1, cgiheaderlen, stdout);
|
||||
dolog(0);
|
||||
exit(0);
|
||||
} else {
|
||||
}
|
||||
|
||||
retcode = 200;
|
||||
printf("HTTP/1.0 200 OK\r\nServer: "
|
||||
FNORD
|
||||
"\r\nPragma: no-cache\r\nConnection: close\r\n");
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
cgi_send_correct_http(ibuf, len);
|
||||
startup = 0;
|
||||
fwrite(cgiheader, 1, cgiheaderlen, stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* non startup
|
||||
*/
|
||||
else {
|
||||
fwrite(ibuf, len, 1, stdout);
|
||||
}
|
||||
size += len;
|
||||
if (pfd[0].revents & POLLHUP)
|
||||
break;
|
||||
}
|
||||
} else if (FD_ISSET(df[1], &wfds)) {
|
||||
/*
|
||||
* write to cgi the post data
|
||||
*/
|
||||
else if (nr > 1 && pfd[1].revents & POLLOUT) {
|
||||
if (post_miss) {
|
||||
write(df[1], post_miss, post_mlen);
|
||||
post_miss = 0;
|
||||
} else if (post_mlen < post_len) {
|
||||
n = read(0, obuf, sizeof(obuf));
|
||||
if (n < 1)
|
||||
goto cgi_500;
|
||||
post_mlen += n;
|
||||
write(df[1], obuf, n);
|
||||
} else {
|
||||
--nr;
|
||||
close(df[1]);
|
||||
if (post_len) {
|
||||
size_t len;
|
||||
char buf[BUFFER_SIZE];
|
||||
size_t nmemb = min(BUFFER_SIZE, post_len);
|
||||
|
||||
len = fread(buf, 1, nmemb, stdin);
|
||||
if (len < 1) {
|
||||
break;
|
||||
}
|
||||
} else if (pfd[0].revents & POLLHUP)
|
||||
break;
|
||||
else {
|
||||
cgi_500:if (startup)
|
||||
badrequest(500, "Internal Server Error",
|
||||
"Looks like the CGI crashed.");
|
||||
else {
|
||||
printf("\n\nLooks like the CGI crashed.\n\n");
|
||||
break;
|
||||
post_len -= len;
|
||||
write(df[1], buf, len);
|
||||
} else {
|
||||
close(df[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1204,36 +1161,25 @@ static int
|
|||
serve_read_write(int fd)
|
||||
{
|
||||
char tmp[4096];
|
||||
struct pollfd duh;
|
||||
time_t now,
|
||||
fini;
|
||||
char *tmp2;
|
||||
int len;
|
||||
off_t todo = rangeend - rangestart;
|
||||
duh.fd = 1;
|
||||
duh.events = POLLOUT;
|
||||
if (rangestart)
|
||||
lseek(fd, rangestart, SEEK_SET);
|
||||
|
||||
while (todo > 0) {
|
||||
int olen;
|
||||
fini = time(&now) + WRITETIMEOUT;
|
||||
len = read(fd, tmp, todo > 4096 ? 4096 : todo);
|
||||
|
||||
olen = len;
|
||||
tmp2 = tmp;
|
||||
while (len > 0) {
|
||||
int written;
|
||||
switch (poll(&duh, 1, (fini - now) * 1000)) {
|
||||
case 0:
|
||||
if (now < fini)
|
||||
continue; /* fall through */
|
||||
case -1:
|
||||
return 1; /* timeout or error */
|
||||
}
|
||||
if ((written = write(1, tmp2, len)) < 0)
|
||||
|
||||
if ((written = write(1, tmp2, len)) < 0) {
|
||||
return -1;
|
||||
}
|
||||
len -= written;
|
||||
tmp2 += written;
|
||||
time(&now);
|
||||
}
|
||||
todo -= olen;
|
||||
}
|
||||
|
@ -1248,9 +1194,7 @@ serve_mmap(int fd)
|
|||
unsigned long mapofs;
|
||||
char *map,
|
||||
*tmp2;
|
||||
struct pollfd duh;
|
||||
time_t now,
|
||||
fini;
|
||||
|
||||
mapstart = rangestart & (~(off_t) 0xfff); /* round down to 4k page */
|
||||
maplen = rangeend - mapstart;
|
||||
mapofs = rangestart - mapstart;
|
||||
|
@ -1274,27 +1218,21 @@ serve_mmap(int fd)
|
|||
} else
|
||||
return serve_read_write(fd);
|
||||
}
|
||||
duh.fd = 1;
|
||||
duh.events = POLLOUT;
|
||||
|
||||
while (rangestart < rangeend) {
|
||||
int len;
|
||||
fini = time(&now) + WRITETIMEOUT;
|
||||
|
||||
len = maplen - mapofs;
|
||||
tmp2 = map + mapofs;
|
||||
while (len > 0) {
|
||||
int written;
|
||||
switch (poll(&duh, 1, (fini - now) * 1000)) {
|
||||
case 0:
|
||||
if (now < fini)
|
||||
continue; /* fall through */
|
||||
case -1:
|
||||
return 1; /* timeout or error */
|
||||
}
|
||||
if ((written = write(1, tmp2, len)) < 0)
|
||||
|
||||
if ((written = write(1, tmp2, len)) < 0) {
|
||||
alarm(0);
|
||||
return -1;
|
||||
}
|
||||
len -= written;
|
||||
tmp2 += written;
|
||||
time(&now);
|
||||
}
|
||||
rangestart += maplen - mapofs;
|
||||
mapstart += maplen;
|
||||
|
@ -1433,6 +1371,7 @@ main(int argc, char *argv[], const char *const *envp)
|
|||
alarm(READTIMEOUT);
|
||||
|
||||
headerlen = sizeof headerbuf;
|
||||
// XXX: I need a way to signal that this is a new read with the same buffer.
|
||||
switch (read_header(stdin, headerbuf, &headerlen)) {
|
||||
case -1:
|
||||
return 0;
|
||||
|
@ -1781,7 +1720,13 @@ main(int argc, char *argv[], const char *const *envp)
|
|||
}
|
||||
fputs("\r\n", stdout);
|
||||
if (method == GET || method == POST) {
|
||||
switch (serve_static_data(fd)) {
|
||||
int ret;
|
||||
|
||||
alarm(WRITETIMEOUT);
|
||||
ret = serve_static_data(fd);
|
||||
alarm(0);
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
break;
|
||||
case -1:
|
||||
|
|
|
@ -96,18 +96,18 @@ class CGITests(BasicTests):
|
|||
|
||||
def testSet(self):
|
||||
so, se = self.get('/cgi/set.cgi', 'default')
|
||||
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\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')
|
||||
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: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 218 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.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\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')
|
||||
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: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 243 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.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.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\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
|
||||
# (it probably won't discard content-length octets)
|
||||
|
|
Loading…
Reference in New Issue