Skip to content
Snippets Groups Projects
Commit 2cd64030 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

Major rewrite of the test HTTP server to allow more fancy features to make

better tests with the issue12-patch applied.
This change also includes Andrés García's win32-fixes.
Made the logging look better/more readable in sws.log
parent 75e1483e
No related branches found
No related tags found
No related merge requests found
...@@ -38,9 +38,12 @@ ...@@ -38,9 +38,12 @@
#include <time.h> #include <time.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h> #include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> #include <netinet/in.h>
#endif
#ifdef _XOPEN_SOURCE_EXTENDED #ifdef _XOPEN_SOURCE_EXTENDED
/* This define is "almost" required to build on HPUX 11 */ /* This define is "almost" required to build on HPUX 11 */
#include <arpa/inet.h> #include <arpa/inet.h>
...@@ -56,7 +59,35 @@ ...@@ -56,7 +59,35 @@
#define TRUE 1 #define TRUE 1
#endif #endif
int ProcessRequest(char *request); #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <windows.h>
#include <winsock2.h>
#define EINPROGRESS WSAEINPROGRESS
#define EWOULDBLOCK WSAEWOULDBLOCK
#define EISCONN WSAEISCONN
#define ENOTSOCK WSAENOTSOCK
#define ECONNREFUSED WSAECONNREFUSED
#endif
#define REQBUFSIZ 150000
#define REQBUFSIZ_TXT "149999"
struct httprequest {
char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
int offset; /* size of the incoming request */
int testno; /* test number found in the request */
int partno; /* part number found in the request */
int open; /* keep connection open info, as found in the request */
bool auth_req; /* authentication required, don't wait for body unless
there's an Authorization header */
bool auth; /* Authorization header present in the incoming request */
size_t cl; /* Content-Length of the incoming request */
bool digest; /* Authorization digest header found */
bool ntlm; /* Authorization ntlm header found */
};
int ProcessRequest(struct httprequest *req);
void storerequest(char *reqbuf); void storerequest(char *reqbuf);
#define DEFAULT_PORT 8999 #define DEFAULT_PORT 8999
...@@ -72,10 +103,19 @@ void storerequest(char *reqbuf); ...@@ -72,10 +103,19 @@ void storerequest(char *reqbuf);
#define TEST_DATA_PATH "%s/data/test%d" #define TEST_DATA_PATH "%s/data/test%d"
/* very-big-path support */
#define MAXDOCNAMELEN 140000
#define MAXDOCNAMELEN_TXT "139999"
#define REQUEST_KEYWORD_SIZE 256
#define CMD_AUTH_REQUIRED "auth_required"
/* global variable, where to find the 'data' dir */ /* global variable, where to find the 'data' dir */
const char *path="."; const char *path=".";
enum { enum {
DOCNUMBER_NOTHING = -7,
DOCNUMBER_QUIT = -6, DOCNUMBER_QUIT = -6,
DOCNUMBER_BADCONNECT = -5, DOCNUMBER_BADCONNECT = -5,
DOCNUMBER_INTERNAL= -4, DOCNUMBER_INTERNAL= -4,
...@@ -114,32 +154,31 @@ static const char *doc404 = "HTTP/1.1 404 Not Found\n" ...@@ -114,32 +154,31 @@ static const char *doc404 = "HTTP/1.1 404 Not Found\n"
"The requested URL was not found on this server.\n" "The requested URL was not found on this server.\n"
"<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n"; "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
#ifdef HAVE_SIGNAL #ifdef SIGPIPE
static volatile int sigpipe; static volatile int sigpipe;
#endif #endif
static FILE *logfp; static FILE *logfp;
static void logmsg(const char *msg, ...) static void logmsg(const char *msg, ...)
{ {
time_t t = time(NULL); time_t t = time(NULL);
va_list ap; va_list ap;
struct tm *curr_time = localtime(&t); struct tm *curr_time = localtime(&t);
char loctime[80];
char buffer[256]; /* possible overflow if you pass in a huge string */ char buffer[256]; /* possible overflow if you pass in a huge string */
va_start(ap, msg); va_start(ap, msg);
vsprintf(buffer, msg, ap); vsprintf(buffer, msg, ap);
va_end(ap); va_end(ap);
strcpy(loctime, asctime(curr_time)); fprintf(logfp, "%02d:%02d:%02d (%d) %s\n",
loctime[strlen(loctime) - 1] = '\0'; curr_time->tm_hour,
fprintf(logfp, "%s: %d: %s\n", loctime, (int)getpid(), buffer); curr_time->tm_min,
curr_time->tm_sec, (int)getpid(), buffer);
fflush(logfp); fflush(logfp);
} }
#ifdef HAVE_SIGNAL #ifdef SIGPIPE
static void sigpipe_handler(int sig) static void sigpipe_handler(int sig)
{ {
(void)sig; /* prevent warning */ (void)sig; /* prevent warning */
...@@ -147,16 +186,134 @@ static void sigpipe_handler(int sig) ...@@ -147,16 +186,134 @@ static void sigpipe_handler(int sig)
} }
#endif #endif
int ProcessRequest(char *request) #define END_OF_HEADERS "\r\n\r\n"
static char *test2file(int testno)
{ {
char *line=request; static char filename[256];
unsigned long contentlength=0; sprintf(filename, TEST_DATA_PATH, path, testno);
char chunked=FALSE; return filename;
}
#define END_OF_HEADERS "\r\n\r\n"
int ProcessRequest(struct httprequest *req)
{
char *line=req->reqbuf;
char chunked=FALSE;
static char request[REQUEST_KEYWORD_SIZE];
static char doc[MAXDOCNAMELEN];
char logbuf[256];
int prot_major, prot_minor;
char *end; char *end;
end = strstr(request, END_OF_HEADERS); end = strstr(req->reqbuf, END_OF_HEADERS);
/* try to figure out the request characteristics as soon as possible, but
only once! */
if((req->testno == DOCNUMBER_NOTHING) &&
sscanf(line, "%" REQBUFSIZ_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
request,
doc,
&prot_major,
&prot_minor) == 4) {
char *ptr;
/* find the last slash */
ptr = strrchr(doc, '/');
/* get the number after it */
if(ptr) {
FILE *stream;
char *filename;
char *cmd = NULL;
int cmdsize = 0;
if((strlen(doc) + strlen(request)) < 200)
sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
request, doc, prot_major, prot_minor);
else
sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
prot_major, prot_minor);
logmsg(logbuf);
if(!strncmp("/verifiedserver", ptr, 15)) {
logmsg("Are-we-friendly question received");
req->testno = DOCNUMBER_WERULEZ;
return 1; /* done */
}
if(!strncmp("/quit", ptr, 15)) {
logmsg("Request-to-quit received");
req->testno = DOCNUMBER_QUIT;
return 1; /* done */
}
ptr++; /* skip the slash */
req->testno = strtol(ptr, &ptr, 10);
if(req->testno > 10000) {
req->partno = req->testno % 10000;
req->testno /= 10000;
}
else
req->partno = 0;
sprintf(logbuf, "Reqested test number %d part %d",
req->testno, req->partno);
logmsg(logbuf);
filename = test2file(req->testno);
stream=fopen(filename, "rb");
if(!stream) {
logmsg("Couldn't open test file %d", req->testno);
return 0;
}
else {
/* get the custom server control "commands" */
cmd = (char *)spitout(stream, "reply", "servercmd", &cmdsize);
fclose(stream);
if(cmdsize) {
logmsg("Found a reply-servercmd section!");
if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
logmsg("instructed to require authorization header");
req->auth_req = TRUE;
}
}
}
}
else {
if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
doc, &prot_major, &prot_minor) == 3) {
sprintf(logbuf, "Receiced a CONNECT %s HTTP/%d.%d request",
doc, prot_major, prot_minor);
logmsg(logbuf);
if(prot_major*10+prot_minor == 10)
req->open = FALSE; /* HTTP 1.0 closes connection by default */
if(!strncmp(doc, "bad", 3))
/* if the host name starts with bad, we fake an error here */
req->testno = DOCNUMBER_BADCONNECT;
else if(!strncmp(doc, "test", 4)) {
char *ptr = strchr(doc, ':');
if(ptr)
req->testno = atoi(ptr+1);
else
req->testno = DOCNUMBER_CONNECT;
}
else
req->testno = DOCNUMBER_CONNECT;
}
else {
logmsg("Did not find test number in PATH");
req->testno = DOCNUMBER_404;
}
}
}
if(!end) if(!end)
/* we don't have a complete request yet! */ /* we don't have a complete request yet! */
...@@ -174,8 +331,14 @@ int ProcessRequest(char *request) ...@@ -174,8 +331,14 @@ int ProcessRequest(char *request)
*/ */
do { do {
if(!strncasecmp("Content-Length:", line, 15)) { if(!req->cl && !strncasecmp("Content-Length:", line, 15)) {
contentlength = strtol(line+15, &line, 10); /* If we don't ignore content-length, we read it and we read the whole
request including the body before we return. If we've been told to
ignore the content-length, we will return as soon as all headers
have been received */
req->cl = strtol(line+15, &line, 10);
logmsg("Found Content-Legth: %d in the request", req->cl);
break; break;
} }
else if(!strncasecmp("Transfer-Encoding: chunked", line, else if(!strncasecmp("Transfer-Encoding: chunked", line,
...@@ -185,7 +348,7 @@ int ProcessRequest(char *request) ...@@ -185,7 +348,7 @@ int ProcessRequest(char *request)
} }
if(chunked) { if(chunked) {
if(strstr(request, "\r\n0\r\n")) if(strstr(req->reqbuf, "\r\n0\r\n"))
/* end of chunks reached */ /* end of chunks reached */
return 1; /* done */ return 1; /* done */
else else
...@@ -197,8 +360,39 @@ int ProcessRequest(char *request) ...@@ -197,8 +360,39 @@ int ProcessRequest(char *request)
line++; line++;
} while(line); } while(line);
if(contentlength > 0 ) { if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
if(contentlength <= strlen(end+strlen(END_OF_HEADERS))) req->auth = TRUE; /* Authorization: header present! */
if(req->auth_req)
logmsg("Authorization header found, as required");
}
if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
/* If the client is passing this Digest-header, we set the part number
to 1000. Not only to spice up the complexity of this, but to make
Digest stuff to work in the test suite. */
req->partno += 1000;
req->digest = TRUE; /* header found */
logmsg("Received Digest request, sending back data %d", req->partno);
}
else if(!req->ntlm &&
strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
/* If the client is passing this type-3 NTLM header */
req->partno += 1002;
req->ntlm = TRUE; /* NTLM found */
logmsg("Received NTLM type-3, sending back data %d", req->partno);
}
else if(!req->ntlm &&
strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
/* If the client is passing this type-1 NTLM header */
req->partno += 1001;
req->ntlm = TRUE; /* NTLM found */
logmsg("Received NTLM type-1, sending back data %d", req->partno);
}
if(strstr(req->reqbuf, "Connection: close"))
req->open = FALSE; /* close connection after this request */
if(req->cl && (req->auth || !req->auth_req)) {
if(req->cl <= strlen(end+strlen(END_OF_HEADERS)))
return 1; /* done */ return 1; /* done */
else else
return 0; /* not complete yet */ return 0; /* not complete yet */
...@@ -223,29 +417,25 @@ void storerequest(char *reqbuf) ...@@ -223,29 +417,25 @@ void storerequest(char *reqbuf)
} }
} }
/* return 0 on success, non-zero on failure */
static int get_request(int sock, struct httprequest *req)
{
int fail= FALSE;
char *reqbuf = req->reqbuf;
#define REQBUFSIZ 150000 /*** Init the httpreqest structure properly for the upcoming request ***/
#define REQBUFSIZ_TXT "149999"
/* very-big-path support */
#define MAXDOCNAMELEN 140000
#define MAXDOCNAMELEN_TXT "139999"
#define REQUEST_KEYWORD_SIZE 256 memset(req, 0, sizeof(struct httprequest));
static int get_request(int sock, int *part, int *open)
{
static char reqbuf[REQBUFSIZ], doc[MAXDOCNAMELEN];
static char request[REQUEST_KEYWORD_SIZE];
unsigned int offset = 0;
int prot_major, prot_minor;
char logbuf[256];
*part = 0; /* part zero equals none */ /* here's what should not be 0 from the start */
req->testno = DOCNUMBER_NOTHING; /* safe default */
req->open = TRUE; /* connection should remain open and wait for more
commands */
*open = TRUE; /* connection should remain open and wait for more commands */ /*** end of httprequest init ***/
while (offset < REQBUFSIZ) { while (req->offset < REQBUFSIZ) {
int got = sread(sock, reqbuf + offset, REQBUFSIZ - offset); int got = sread(sock, reqbuf + req->offset, REQBUFSIZ - req->offset);
if (got <= 0) { if (got <= 0) {
if (got < 0) { if (got < 0) {
perror("recv"); perror("recv");
...@@ -255,137 +445,31 @@ static int get_request(int sock, int *part, int *open) ...@@ -255,137 +445,31 @@ static int get_request(int sock, int *part, int *open)
logmsg("Connection closed by client"); logmsg("Connection closed by client");
return DOCNUMBER_INTERNAL; return DOCNUMBER_INTERNAL;
} }
offset += got; req->offset += got;
reqbuf[offset] = 0; reqbuf[req->offset] = 0;
if(ProcessRequest(reqbuf)) if(ProcessRequest(req))
break; break;
} }
if (offset >= REQBUFSIZ) { if (req->offset >= REQBUFSIZ) {
logmsg("Request buffer overflow, closing connection"); logmsg("Request buffer overflow, closing connection");
/* dump the request to an external file anyway */
reqbuf[REQBUFSIZ-1]=0; reqbuf[REQBUFSIZ-1]=0;
storerequest(reqbuf); fail = TRUE;
/* dump the request to an external file anyway */
return DOCNUMBER_INTERNAL;
} }
reqbuf[offset]=0; else
reqbuf[req->offset]=0;
/* dump the request to an external file */ /* dump the request to an external file */
storerequest(reqbuf); storerequest(reqbuf);
if(sscanf(reqbuf, "%" REQBUFSIZ_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d", return fail; /* success */
request,
doc,
&prot_major,
&prot_minor) == 4) {
char *ptr;
int test_no=0;
/* find the last slash */
ptr = strrchr(doc, '/');
/* get the number after it */
if(ptr) {
if((strlen(doc) + strlen(request)) < 200)
sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
request, doc, prot_major, prot_minor);
else
sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
prot_major, prot_minor);
logmsg(logbuf);
if(!strncmp("/verifiedserver", ptr, 15)) {
logmsg("Are-we-friendly question received");
return DOCNUMBER_WERULEZ;
}
if(!strncmp("/quit", ptr, 15)) {
logmsg("Request-to-quit received");
return DOCNUMBER_QUIT;
}
ptr++; /* skip the slash */
test_no = strtol(ptr, &ptr, 10);
if(test_no > 10000) {
*part = test_no % 10000;
test_no /= 10000;
}
else
*part = 0;
sprintf(logbuf, "Found test number %d in path", test_no);
logmsg(logbuf);
if(strstr(reqbuf, "Authorization: Digest")) {
/* If the client is passing this Digest-header, we set the part number
to 1000. Not only to spice up the complexity of this, but to make
Digest stuff to work in the test suite. */
*part += 1000;
logmsg("Received Digest request, sending back data %d", *part);
}
else if(strstr(reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
/* If the client is passing this type-3 NTLM header */
*part += 1002;
logmsg("Received NTLM type-3, sending back data %d", *part);
}
else if(strstr(reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
/* If the client is passing this type-1 NTLM header */
*part += 1001;
logmsg("Received NTLM type-1, sending back data %d", *part);
}
if(strstr(reqbuf, "Connection: close"))
*open = FALSE; /* close connection after this request */
}
else {
if(sscanf(reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
doc,
&prot_major, &prot_minor) == 3) {
sprintf(logbuf, "Receiced a CONNECT %s HTTP/%d.%d request",
doc, prot_major, prot_minor);
logmsg(logbuf);
if(prot_major*10+prot_minor == 10)
*open = FALSE; /* HTTP 1.0 closes connection by default */
if(!strncmp(doc, "bad", 3))
/* if the host name starts with bad, we fake an error here */
test_no = DOCNUMBER_BADCONNECT;
else if(!strncmp(doc, "test", 4)) {
char *ptr = strchr(doc, ':');
if(ptr)
test_no = atoi(ptr+1);
else
test_no = DOCNUMBER_CONNECT;
}
else
test_no = DOCNUMBER_CONNECT;
}
else {
logmsg("Did not find test number in PATH");
test_no = DOCNUMBER_404;
}
}
return test_no;
}
logmsg("Got illegal request");
fprintf(stderr, "Got illegal request\n");
return DOCNUMBER_404;
} }
/* returns -1 on failure */ /* returns -1 on failure */
static int send_doc(int sock, static int send_doc(int sock, struct httprequest *req)
int doc,
int part_no,
int *alive) /* keep the connection alive or not */
{ {
int written; int written;
int count; int count;
...@@ -399,13 +483,14 @@ static int send_doc(int sock, ...@@ -399,13 +483,14 @@ static int send_doc(int sock,
static char weare[256]; static char weare[256];
char filename[256];
char partbuf[80]="data"; char partbuf[80]="data";
*alive = FALSE; req->open = FALSE;
logmsg("Send response number %d part %d", req->testno, req->partno);
if(doc < 0) { if(req->testno < 0) {
switch(doc) { switch(req->testno) {
case DOCNUMBER_QUIT: case DOCNUMBER_QUIT:
logmsg("Replying to QUIT"); logmsg("Replying to QUIT");
buffer = docquit; buffer = docquit;
...@@ -440,12 +525,10 @@ static int send_doc(int sock, ...@@ -440,12 +525,10 @@ static int send_doc(int sock,
count = strlen(buffer); count = strlen(buffer);
} }
else { else {
logmsg("Fetch response data, test %d part %d", doc, part_no); char *filename = test2file(req->testno);
if(0 != part_no) if(0 != req->partno)
sprintf(partbuf, "data%d", part_no); sprintf(partbuf, "data%d", req->partno);
sprintf(filename, TEST_DATA_PATH, path, doc);
stream=fopen(filename, "rb"); stream=fopen(filename, "rb");
if(!stream) { if(!stream) {
...@@ -526,19 +609,52 @@ static int send_doc(int sock, ...@@ -526,19 +609,52 @@ static int send_doc(int sock,
if(cmd) if(cmd)
free(cmd); free(cmd);
*alive = persistant; req->open = persistant;
return 0; return 0;
} }
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
static void win32_init(void)
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD(2, 0);
err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
perror("Winsock init failed");
fprintf(logfp, "Error initializing winsock -- aborting\n");
fclose(logfp);
exit(1);
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 0 ) {
WSACleanup();
perror("Winsock init failed");
fprintf(logfp, "No suitable winsock.dll found -- aborting\n");
fclose(logfp);
exit(1);
}
}
static void win32_cleanup(void)
{
WSACleanup();
}
#endif
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
struct sockaddr_in me; struct sockaddr_in me;
int sock, msgsock, flag; int sock, msgsock, flag;
unsigned short port = DEFAULT_PORT; unsigned short port = DEFAULT_PORT;
const char *logfile = DEFAULT_LOGFILE; const char *logfile = DEFAULT_LOGFILE;
int part_no;
FILE *pidfile; FILE *pidfile;
struct httprequest req;
if(argc>1) { if(argc>1) {
port = atoi(argv[1]); port = atoi(argv[1]);
...@@ -553,12 +669,18 @@ int main(int argc, char *argv[]) ...@@ -553,12 +669,18 @@ int main(int argc, char *argv[])
perror(logfile); perror(logfile);
exit(1); exit(1);
} }
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
win32_init();
#endif
#ifdef SIGPIPE
#ifdef HAVE_SIGNAL #ifdef HAVE_SIGNAL
signal(SIGPIPE, sigpipe_handler); signal(SIGPIPE, sigpipe_handler);
#endif #endif
#ifdef HAVE_SIGINTERRUPT #ifdef HAVE_SIGINTERRUPT
siginterrupt(SIGPIPE, 1); siginterrupt(SIGPIPE, 1);
#endif
#endif #endif
sock = socket(AF_INET, SOCK_STREAM, 0); sock = socket(AF_INET, SOCK_STREAM, 0);
...@@ -598,48 +720,48 @@ int main(int argc, char *argv[]) ...@@ -598,48 +720,48 @@ int main(int argc, char *argv[])
listen(sock, 5); listen(sock, 5);
while (1) { while (1) {
int doc;
int open;
int alive;
msgsock = accept(sock, NULL, NULL); msgsock = accept(sock, NULL, NULL);
if (msgsock == -1) if (msgsock == -1)
continue; continue;
logmsg("New client connected"); logmsg("** New client connected");
do { do {
if(get_request(msgsock, &req))
/* non-zero means error, break out of loop */
break;
doc = get_request(msgsock, &part_no, &open); send_doc(msgsock, &req);
logmsg("Received request, now send response number %d part %d",
doc, part_no);
send_doc(msgsock, doc, part_no, &alive);
if((doc < 0) && (doc != DOCNUMBER_CONNECT)) { if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) {
logmsg("special request received, no persistancy"); logmsg("special request received, no persistancy");
break; break;
} }
if(!alive) { if(!req.open) {
logmsg("instructed to close connection after server-reply"); logmsg("instructed to close connection after server-reply");
break; break;
} }
if(open) if(req.open)
logmsg("persistant connection, awaits new request"); logmsg("persistant connection, awaits new request");
/* if we got a CONNECT, loop and get another request as well! */ /* if we got a CONNECT, loop and get another request as well! */
} while(open || (doc == DOCNUMBER_CONNECT)); } while(req.open || (req.testno == DOCNUMBER_CONNECT));
logmsg("Closing client connection"); logmsg("** Closing client connection");
sclose(msgsock); sclose(msgsock);
if (doc == DOCNUMBER_QUIT) if (req.testno == DOCNUMBER_QUIT)
break; break;
} }
sclose(sock); sclose(sock);
fclose(logfp); fclose(logfp);
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
win32_cleanup();
#endif
return 0; return 0;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment