========================================================================== CS 311 - Assignment 4 Name: Jacob Lundberg Student ID: 541.215.251 Email: jacob@chaos2.org Name: Student ID: Email: Name: Student ID: Email: Tue Nov 28 20:31:43 PST 2000 SunOS az.ENGR.ORST.EDU 5.7 Generic_106541-12 sun4u sparc SUNW,Ultra-5_10 lundberg ========================================================================== -------------------------------------------------------------------------- Source File: Makefile # # Makefile for Jacob's http daemon. # Yes, it's gnu-make specific. # # The symbol DEBUG turns on or off debug symbols and output. # Levels are (0) off # (1) gdb symbols # (2) gdb symbols and debug output DEBUG=0 AR=ar CC=gcc CFLAGS=-I/usr/include -O2 LDLIBS=-lsocket -lnsl BASE=/usr VERSION=1.0 CFLAGS+=-DVERSION=\"$(VERSION)\" -DDEBUG=$(DEBUG) DISTDIR=server-$(VERSION) OBJFILS=parse.o server.o signals.o sockio.o ifneq ($(DEBUG), 0) CFLAGS+=-Wall -ggdb endif all: server server: $(OBJFILS) $(CC) $(CFLAGS) $(LDLIBS) -o server $(OBJFILS) install: server install -c -m 0755 server $(BASE)/server depend: makedepend -- $(CFLAGS) -- *.c clean: rm -rf *.o *.bak *~ distclean: clean rm -rf server-[0-9].[0-9]* server distlist: distclean DISTLIST=$(shell find $(PWD) -mindepth 1) distdir: distlist mkdir $(DISTDIR) cp -a $(DISTLIST) $(DISTDIR) dist: distdir tar chozf $(DISTDIR).tar.gz $(DISTDIR) rm -rf $(DISTDIR) -------------------------------------------------------------------------- Source File: parse.c /* * parse.c * File Revision 0 * Parsing of http requests for server. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.11 Initial implementation */ #include #include #include #include #include #include #include "response.h" #include "server.h" /* Location of symbols for DBG_PRN. */ #define WHENCE "parse.c" /* Maximum number of chars in a time field. */ #define TIME_SIZE_MAX 64 static void timeform(char *buffer, time_t timeval) { /* * timeform() * Convert timeval into a string value stored on buffer. * It's big, and it's ugly, but it's mostly table lookups. */ struct tm *gmt; DBG_PRN("timeform: timeval = %li\n", WHENCE, timeval); /* Get the UTC version of our timestamp. */ gmt = gmtime(&timeval); /* Set the day of the week. */ switch(gmt->tm_wday) { case 0: strcpy(buffer, "Sun"); break; case 1: strcpy(buffer, "Mon"); break; case 2: strcpy(buffer, "Tue"); break; case 3: strcpy(buffer, "Wed"); break; case 4: strcpy(buffer, "Thu"); break; case 5: strcpy(buffer, "Fri"); break; case 6: strcpy(buffer, "Sat"); break; } /* Month day. */ sprintf(buffer + strlen(buffer), ", %02i ", gmt->tm_mday); /* Month name. */ switch(gmt->tm_mon) { case 0: strcat(buffer, "Jan"); break; case 1: strcat(buffer, "Feb"); break; case 2: strcat(buffer, "Mar"); break; case 3: strcat(buffer, "Apr"); break; case 4: strcat(buffer, "May"); break; case 5: strcat(buffer, "Jun"); break; case 6: strcat(buffer, "Jul"); break; case 7: strcat(buffer, "Aug"); break; case 8: strcat(buffer, "Sep"); break; case 9: strcat(buffer, "Oct"); break; case 10: strcat(buffer, "Nov"); break; case 11: strcat(buffer, "Dec"); break; } /* The four-digit year. */ sprintf(buffer + strlen(buffer), " %04i ", 1900 + gmt->tm_year); /* Hours! Minutes! Seconds! (and GMT as required by RFC 1945) */ sprintf(buffer + strlen(buffer), "%02i:%02i:%02i GMT", gmt->tm_hour, gmt->tm_min + (gmt->tm_sec / 60), gmt->tm_sec % 60); } bool parse_request(sv_req *request, char *buffer) { /* * parse_request() * Slurp out the request information. */ time_t timeval; char *full, *holder; char curtime[TIME_SIZE_MAX]; bool parse_success = true; if(request == NULL || buffer == NULL) { DBG_PRN("parse_request: NULL buffer!\n", WHENCE); return(false); } else DBG_PRN("parse_request\n", WHENCE); /* Remember the full form of the request. */ full = buffer; /* Get the current date and time. */ time(&timeval); timeform(curtime, timeval); /* Clip \n from the top of the buffer. */ while(buffer[0] == '\n') ++buffer; /* Determine the request type. */ if(!strncmp(buffer, "HEAD", 4) && strchr(" \t", buffer[4])) { request->req_type = SV_REQ_HEAD; buffer += 4; } else if(!strncmp(buffer, "GET", 3) && strchr(" \t", buffer[3])) { request->req_type = SV_REQ_GET; buffer += 3; } else { /* Request type wasn't valid. *sniff* */ request->req_type = SV_REQ_NONE; while(!memchr(" \t\n", buffer[0], 4)) ++buffer; parse_success = false; /* 501 deferred until below because 400 takes precedence. */ } /* Advance to the next word in the buffer. */ while(strchr(" \t", buffer[0])) ++buffer; /* Get the file name from the buffer. */ holder = buffer; while(!memchr(" \t\n", buffer[0], 4)) ++buffer; request->filename = (char *)malloc((buffer - holder + 2) * sizeof(char)); memcpy(request->filename + 1, holder, buffer - holder); request->filename[buffer - holder + 1] = '\0'; request->filename[0] = '.'; if(buffer - holder <= 0) { parse_success = false; /* 400, Bad request. */ snprintf(sv_req_buffer, SV_REQ_BUF_SIZE - 2, HEAD_400 BODY_400, curtime, full); action_write(sv_req_buffer, strlen(sv_req_buffer)); } else if(!parse_success) { /* 501, Method not implemented. */ snprintf(sv_req_buffer, SV_REQ_BUF_SIZE - 2, HEAD_501 BODY_501, curtime, full); action_write(sv_req_buffer, strlen(sv_req_buffer)); } /* Advance to the next word in the buffer. */ while(strchr(" \t", buffer[0])) ++buffer; /* Check the request class. */ if(parse_success && (strncmp(buffer, "HTTP/1.", 7) || !strchr("01", buffer[7]))) { parse_success = false; /* 400, Bad request. */ snprintf(sv_req_buffer, SV_REQ_BUF_SIZE - 2, HEAD_400 BODY_400, curtime, full); action_write(sv_req_buffer, strlen(sv_req_buffer)); } return(parse_success); } void write_file(int file) { /* * write_file() * Helper for fill_request(). * Copies an entire file to the communications socket. */ int inlen = 0; DBG_PRN("write_file: file = %i\n", WHENCE, file); /* Loop buffering data from the file until an empty read. */ do { inlen = read(file, sv_req_buffer, SV_REQ_BUF_SIZE - 2); action_write(sv_req_buffer, inlen); } while(inlen > 0); if(inlen < 0) DBG_PRN("write_file: short read due to error %i\n", WHENCE, errno); } const char *get_ctype(sv_req *request) { /* * get_ctype() * Get the Content-Type of a request. */ /* This is not a thread-safe buffer. */ static char buffer[64]; /* We need to know how long the filename is in several places. */ int namelen = strlen(request->filename); DBG_PRN("get_ctype\n", WHENCE); /* Lookup table of known types. */ if(namelen < 4) strcpy(buffer, "application/x-unknown"); else if(!strncmp(request->filename + namelen - 4, ".txt", 4) || (namelen >= 5 && !strncmp(request->filename + namelen - 5, ".text", 5))) strcpy(buffer, "text/plain"); else if(!strncmp(request->filename + namelen - 4, ".htm", 4) || (namelen >= 5 && !strncmp(request->filename + namelen - 5, ".html", 5))) strcpy(buffer, "text/html"); else if(!strncmp(request->filename + namelen - 3, "jpg", 3)) strcpy(buffer, "image/jpeg"); else if(!strncmp(request->filename + namelen - 3, "gif", 3)) strcpy(buffer, "image/gif"); else if(!strncmp(request->filename + namelen - 3, "png", 3)) strcpy(buffer, "image/png"); else strcpy(buffer, "application/x-unknown"); return(buffer); } void fill_request(sv_req *request) { /* * fill_request() * Toss a file out for the hungry dogs to chew on. */ char curtime[TIME_SIZE_MAX], flmtime[TIME_SIZE_MAX]; time_t timeval; struct stat fs; DBG_PRN("fill_request: type = %i\n", WHENCE, request->req_type); /* Get the current date and time. */ time(&timeval); timeform(curtime, timeval); /* Stat the file. */ stat(request->filename, &fs); /* If it's a directory, scan for contents to revise to. */ if(S_ISDIR(fs.st_mode)) { request->filename = (char *)realloc(request->filename, (strlen(request->filename) + 12) * sizeof(char)); /* Yeah, we don't search too hard just yet. */ if(request->filename[strlen(request->filename) - 1] != '/') strcat(request->filename, "/"); strcat(request->filename, "index.html"); stat(request->filename, &fs); } /* Try and open the input source for reading. */ request->file = open(request->filename, O_RDONLY, S_IRUSR); if(request->file < 0) { /* Write out a failure response. */ switch(errno) { case EPERM: case EACCES: /* 403, Permissions do not allow. */ snprintf(sv_req_buffer, SV_REQ_BUF_SIZE - 2, HEAD_403 BODY_403, curtime, request->filename + 1); action_write(sv_req_buffer, strlen(sv_req_buffer)); break; case ENOENT: case ENXIO: case EBADF: case EAGAIN: case EBUSY: case ENODEV: case EISDIR: /* 404, File not found or not available. */ snprintf(sv_req_buffer, SV_REQ_BUF_SIZE - 2, HEAD_404 BODY_404, curtime, request->filename + 1); action_write(sv_req_buffer, strlen(sv_req_buffer)); break; default: /* 500, Server error. */ snprintf(sv_req_buffer, SV_REQ_BUF_SIZE - 2, HEAD_500 BODY_500, curtime, errno); action_write(sv_req_buffer, strlen(sv_req_buffer)); break; } } else { /* Duplicate the contents of the file out to the socket. */ timeform(flmtime, fs.st_mtime); snprintf(sv_req_buffer, SV_REQ_BUF_SIZE - 2, HEAD_200, curtime, flmtime, fs.st_size, get_ctype(request)); action_write(sv_req_buffer, strlen(sv_req_buffer)); if(request->req_type == SV_REQ_GET) write_file(request->file); close(request->file); } } -------------------------------------------------------------------------- Source File: response.h /* * response.h * File Revision 0 * Strings used in responses. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.27 Initial implementation */ #ifndef __RESPONSE_H__ #define __RESPONSE_H__ /* Buffering for request printing. */ #define SV_REQ_BUF_SIZE 4096 static char sv_req_buffer[SV_REQ_BUF_SIZE]; /* 200 no errors */ #define HEAD_200 \ "HTTP/1.1 200 OK Date: %s Server: " SERVER_ID " Last-Modified: %s Accept-Ranges: bytes Content-Length: %li Connection: close Content-Type: %s " /* 400 bad request */ #define HEAD_400 \ "HTTP/1.1 400 Bad Request Date: %s Server: " SERVER_ID " Connection: close Content-Type: text/html " #define BODY_400 \ " 400 Bad Request

Bad Request

Your browser sent a request that this server could not understand:

%s
" /* 403 forbidden */ #define HEAD_403 \ "HTTP/1.1 403 Forbidden Date: %s Server: " SERVER_ID " Connection: close Content-Type: text/html " #define BODY_403 \ " 403 Forbidden

Forbidden

You don't have permission to access %s on this server.

" /* 404 not found */ #define HEAD_404 \ "HTTP/1.1 404 Not Found Date: %s Server: " SERVER_ID " Connection: close Content-Type: text/html " #define BODY_404 \ " 404 Not Found

Not Found

The requested URL %s was not found on this server.

" /* 500 internal server error */ #define HEAD_500 \ "HTTP/1.1 500 Internal Server Error Date: %s Server: " SERVER_ID " Connection: close Content-Type: text/html " #define BODY_500 \ " 500 Internal Server Error

Internal Server Error

The server encountered an internal error
and was unable to process your request.

The server recieved error number %i.
Please contact the server administrator. " /* 501 method not implemented */ #define HEAD_501 \ "HTTP/1.1 501 Method Not Implemented Date: %s Server: " SERVER_ID " Connection: close Content-Type: text/html " #define BODY_501 \ " 501 Method Not Implemented

Method Not Implemented

Invalid method in request:

%s
" #endif /* ndef __RESPONSE_H__ */ -------------------------------------------------------------------------- Source File: server.c /* * server.c * File Revision 0 * Server flow for http server. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.10 Initial implementation */ #include "server.h" /* Location of symbols for DBG_PRN. */ #define WHENCE "server.c" /* We track the fork depth for shared functions. */ int sv_fork_level = 0; /* The socket descriptors are global so signal handlers can close them. */ int sv_sock_lstn = -1; int sv_sock_comm = -1; int main(int argc, char **argv) { /* * main() * Loop doing nothing. */ /* Let the user know what (s)he started. */ printf(SERVER_ID " starting...\n"); fflush(stdout); /* Set up signal handling. */ handle_signals(); /* Open the socket for listening. */ setup_socket(); /* Bind to the socket. */ setup_bind((argc > 1) ? atoi(argv[1]) : SV_DEFAULT_PORT); /* Listen on the socket. */ setup_listen(SV_LISTEN_BACKLOG); /* Loop forever! (almost) */ while(true) { /* Accept connections on our socket. */ action_accept(); /* Fork off a child to handle a new connection. */ action_fork(); /* Close the connection so we can accept again. */ close(sv_sock_comm); sv_sock_comm = -1; /* Slurp up any zombie escapees. */ while(waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0) /* reap */; } /* Yeah, so we'll never return. But gcc likes it. */ do_exit(0); return(0); } void action_fork(void) { /* * action_fork() * Fork off a child to handle a new connection. */ pid_t pid; DBG_PRN("action_fork: sv_sock_comm = %i\n", WHENCE, sv_sock_comm); /* Fork! */ pid = fork(); /* Everybody look what's goin' down! */ if(pid == 0) { /* The child serves the new connection, then closes it and terminates. */ /* Top priority is to mark that we're a child. */ ++sv_fork_level; /* Then mark that we mustn't shutdown() our parent's listening socket. */ sv_sock_lstn = -1; close(sv_sock_lstn); /* Install our own signal handlers. */ handle_signals(); /* Serve the request that we were forked to serve. */ action_serve(); /* Exit, which will also close our communications socket. */ do_exit(0); } else if(pid < 0) { fprintf(stderr, "server: unable to fork new process\n"); do_exit(1); } } void action_serve() { /* * action_serve() * Serve a request. */ char *buffer = NULL; sv_req request; DBG_PRN("action_serve: sv_sock_comm = %i\n", WHENCE, sv_sock_comm); /* Setup for proper memory deallocation. */ request.filename = NULL; /* Read in an http request. */ buffer = action_read(buffer); /* Parse the request info. */ if(parse_request(&request, buffer)) { /* Write the request. */ fill_request(&request); } /* Local cleanups. */ free(request.filename); free(buffer); } void do_exit(int exit_val) { /* * do_exit() * Time to exit, so wait on our children's deaths. */ DBG_PRN("do_exit: exit_val = %i\n", WHENCE, exit_val); /* Ignore signals since we're exiting already. */ ignore_signals(); /* Close our listening socket. */ if(sv_sock_lstn >= 0) { shutdown(sv_sock_lstn, SHUT_RDWR); close(sv_sock_lstn); sv_sock_lstn = -1; } /* Close our communication socket. */ if(sv_sock_comm >= 0) { shutdown(sv_sock_comm, SHUT_RDWR); close(sv_sock_comm); sv_sock_comm = -1; } /* Wait for any children to exit. */ while(waitpid(-1, NULL, 0) > 0) /* reap */; /* Time to leave. */ exit(exit_val); } -------------------------------------------------------------------------- Source File: server.h /* * server.h * File Revision 0 * Primary header for http server. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.10 Initial implementation */ #ifndef __SERVER_H__ #define __SERVER_H__ #include #include #include #include #include #include #include #include #include /* Versioning. */ #define SERVER_ID "Indigo Simple Server/" VERSION " (Unix)" /* * boolean logic * We provide a form that gcc will accept by default. However, it isn't * strictly correct to define arbitrary values for both members of an enum * so unless we know we're compiling with a GNU C compiler we'll refrain. */ #ifdef __GNUC__ typedef enum boolean { false = 0, true = !false } bool; #else /* def __GNUC__ */ typedef int bool; #define false 0 #define true !false #endif /* def __GNUC__ */ /* * Debug output, not strictly ANSI (ellipsi). * DEBUG should be set in the Makefile. */ #if defined( __GNUC__ ) && defined( DEBUG ) && ( DEBUG > 1 ) #define DBG_PRN(A, B...) printf("%5i - %s - " A, getpid(), ##B); \ fflush(stdout) #else /* DEBUG > 1 */ #define DBG_PRN(A, B...) /* nothing */ #endif /* DEBUG > 1 */ /* * Single Unix names flags for shutdown(). Since I'm using them, * I provide them here for systems that don't define them for me. */ #ifndef SHUT_RD #define SHUT_RD 0 #endif /* ndef SHUT_RD */ #ifndef SHUT_WR #define SHUT_WR 1 #endif /* ndef SHUT_WR */ #ifndef SHUT_RDWR #define SHUT_RDWR 2 #endif /* ndef SHUT_RDWR */ /* Default port the server listens on if no port is specified. */ #define SV_DEFAULT_PORT 80 /* Give the listen max backlog. BSD may lower it to 5 though. */ #define SV_LISTEN_BACKLOG 16 /* Tuning for buffer allocations in sockio.c. */ #define SV_BUFFER_GROW_SIZE 512 #define SV_BUFFER_MAX_GROWTHS 32 /* Enumerate the request types. */ typedef enum _sv_req_type { SV_REQ_NONE = false, /* bad request type */ SV_REQ_GET, /* GET method */ SV_REQ_HEAD, /* HEAD method */ } sv_req_type; /* Define a struct to store information about a request in. */ typedef struct _sv_req { sv_req_type req_type; /* type of request */ char *filename; /* file name requested */ int file; /* file opened (if one is) */ } sv_req; /* Am I the parent or the child? */ extern int sv_fork_level; /* Global socket descriptors. */ extern int sv_sock_lstn; extern int sv_sock_comm; /* parse.c */ void fill_request(sv_req *request); bool parse_request(sv_req *request, char *buffer); /* server.c */ void action_fork(void); void action_serve(void); void do_exit(int exit_val); /* signals.c */ void handle_signals(void); void ignore_signals(void); /* sockio.c */ void action_accept(void); char *action_read(char *buffer); void action_write(char *buffer, int size); void setup_bind(unsigned short port); void setup_listen(int backlog); void setup_socket(void); #endif /* ndef __SERVER_H__ */ -------------------------------------------------------------------------- Source File: signals.c /* * signals.c * File Revision 0 * Signal handling in http server. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.12 Initial implementation */ #include "server.h" /* Location of symbols for DBG_PRN. */ #define WHENCE "signals.c" void handle_sigint(int signo) { /* * handle_sigint() * We'll exit on ^C methinks. */ DBG_PRN("handle_sigint: signo = %i\n", WHENCE, signo); do_exit(1); } void handle_sigquit(int signo) { /* * handle_sigquit() * Close everything down nice'n'orderly. */ DBG_PRN("handle_sigquit: signo = %i\n", WHENCE, signo); do_exit(0); } void handle_signals(void) { /* * handle_signals() * Arrange to trap the signals we need. * We can check sv_fork_level to see if we're parent or child. */ DBG_PRN("handle_signals: sv_fork_level = %i\n", WHENCE, sv_fork_level); /* Kill children and reap zombies on 2 (child and parent). */ signal(SIGINT, handle_sigint); /* Parent terminates the listen socket and waits for children on 3. */ if(sv_fork_level == 0) signal(SIGQUIT, handle_sigquit); /* Child ignores 3 and completes its current transaction. */ else signal(SIGQUIT, SIG_IGN); /* Parent terminates the listen socket and waits for children on 15. */ if(sv_fork_level == 0) signal(SIGTERM, handle_sigquit); /* Child ignores 15 and completes its current transaction. */ else signal(SIGTERM, SIG_IGN); } void ignore_signals(void) { /* * ignore_signals() * During the exit sequence we ignore signals. */ DBG_PRN("ignore_signals\n", WHENCE); signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, SIG_IGN); } -------------------------------------------------------------------------- Source File: sockio.c /* * sockio.c * File Revision 0 * Socket I/O for http server. * (c) 2000 Jacob Lundberg, jacob@chaos2.org */ /* * 2000.11.10 Initial implementation */ #include "server.h" /* Location of symbols for DBG_PRN. */ #define WHENCE "sockio.c" void setup_socket(void) { /* * setup_socket() * Open a socket to listen on. */ DBG_PRN("setup_socket\n", WHENCE); /* Open the socket for listening. */ sv_sock_lstn = socket(AF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto); /* Done reading protocol information. */ endprotoent(); /* Make sure a socket was opened. */ if(sv_sock_lstn < 0) { fprintf(stderr, "server: unable to establish tcp socket\n"); do_exit(1); } } void setup_bind(unsigned short port) { /* * setup_bind() * Bind an open socket. */ /* Name our socket (and make sure port isn't swabbed). */ struct sockaddr_in address = {AF_INET, htons(port), {INADDR_ANY}}; DBG_PRN("setup_bind: sv_sock_lstn = %i, port = %i\n", WHENCE, sv_sock_lstn, port); /* Try to bind the socket. */ if(bind(sv_sock_lstn, (struct sockaddr *)&address, sizeof(struct sockaddr_in))) { fprintf(stderr, "server: unable to bind tcp socket\n"); do_exit(1); } } void setup_listen(int backlog) { /* * setup_listen() * Start to listen on a bound socket. */ DBG_PRN("setup_listen: sv_sock_lstn = %i, backlog = %i\n", WHENCE, sv_sock_lstn, backlog); if(listen(sv_sock_lstn, backlog)) { fprintf(stderr, "server: unable to listen on tcp socket\n"); do_exit(1); } } void action_accept() { /* * action_accept() * Wait on new connections. */ struct sockaddr_in address; int sockaddr_size = sizeof(struct sockaddr_in); DBG_PRN("action_accept: sv_sock_lstn = %i\n", WHENCE, sv_sock_lstn); /* * Accept connections on our socket. * Different OSen want different struct types here. * The type I've used makes Linux happy but generates * a warning on SunOS. There is no operational impact. */ if((sv_sock_comm = accept(sv_sock_lstn, &address, &sockaddr_size)) < 0) { fprintf(stderr, "server: unable to accept on tcp socket\n"); do_exit(1); } } char *action_read(char *buffer) { /* * action_read() * Read in a request into a newly realloc'd buffer. */ int resizes = 1; size_t size_total = 0, size_add = 0; DBG_PRN("action_read: buffer = %p\n", WHENCE, buffer); /* Size the buffer to the basic growth size. */ buffer = (char *)realloc(buffer, SV_BUFFER_GROW_SIZE); /* Loop and get more data. */ do { if(size_total > SV_BUFFER_GROW_SIZE * (resizes - 1)) { /* Buffer is within the last region, so increase its size. */ buffer = (char *)realloc(buffer, SV_BUFFER_GROW_SIZE * ++resizes); } /* Try to read some more data. */ size_add = read(sv_sock_comm, buffer + size_total, SV_BUFFER_GROW_SIZE); /* Detect end-of-input conditions. */ if(size_add == 0) break; if(size_add > 0) size_total += size_add; if(buffer[size_total - 2] == '\n' && buffer[size_total - 1] == '\n') break; if(buffer[size_total - 4] == '\r' && buffer[size_total - 3] == '\n' && buffer[size_total - 2] == '\r' && buffer[size_total - 1] == '\n') break; } while(resizes < SV_BUFFER_MAX_GROWTHS); /* Make sure the string is NULL-terminated. */ if(size_total >= SV_BUFFER_MAX_GROWTHS * SV_BUFFER_GROW_SIZE) size_total = SV_BUFFER_MAX_GROWTHS * SV_BUFFER_GROW_SIZE - 1; if(size_total >= SV_BUFFER_GROW_SIZE * resizes) buffer = (char *)realloc(buffer, SV_BUFFER_GROW_SIZE * ++resizes); buffer[size_total] = '\0'; return(buffer); } void action_write(char *buffer, int size) { /* * action_write() * Write out a string of chars to the socket. */ int written; if(buffer == NULL) { DBG_PRN("action_write: NULL buffer\n", WHENCE); return; } else DBG_PRN("action_write: size = %i\n", WHENCE, size); /* Write out some data. Length 0 writes are no-ops for portability. */ while(size > 0) { written = write(sv_sock_comm, buffer, size * sizeof(char)); if(written < 0) { DBG_PRN("action_write: socket write error\n", WHENCE); return; } else { size -= written * sizeof(char); buffer += written; } } } -------------------------------------------------------------------------- Building Test Program -------------------------------------------------------------------------- Creating Test Files total 266 -rwxr-xr-x 1 lundberg user 10188 Nov 28 20:31 client -rw-r--r-- 1 lundberg user 2858 Nov 28 20:31 client.c -rw-r--r-- 1 lundberg user 85 Nov 28 20:31 file1.html -rw-r--r-- 1 lundberg user 47 Nov 28 20:31 file1.txt -rw-r--r-- 1 lundberg user 4899 Nov 28 20:31 file2.html -rw-r--r-- 1 lundberg user 97980 Nov 28 20:31 file3.html -rwxr-xr-x 1 lundberg user 17324 Nov 28 20:31 server -------------------------------------------------------------------------- Computing Port 11212 -------------------------------------------------------------------------- Starting server -------------------------------------------------------------------------- PS PID TTY TIME CMD 29241 pts/8 0:00 server 27969 pts/8 0:00 tcsh 29212 pts/8 0:00 test.sh -------------------------------------------------------------------------- Small Text File HTTP/1.1 200 OK Date: Wed, 29 Nov 2000 04:31:50 GMT Server: Indigo Simple Server/1.0 (Unix) Last-Modified: Wed, 29 Nov 2000 04:31:48 GMT Accept-Ranges: bytes Content-Length: 47 Connection: close Content-Type: text/plain This is a test text file. It is rather small. ./client: transfered 268 bytes -------------------------------------------------------------------------- Small HTML File HTTP/1.1 200 OK Date: Wed, 29 Nov 2000 04:31:50 GMT Server: Indigo Simple Server/1.0 (Unix) Last-Modified: Wed, 29 Nov 2000 04:31:48 GMT Accept-Ranges: bytes Content-Length: 85 Connection: close Content-Type: text/html

This is a test html file. It is rather small.

./client: transfered 305 bytes -------------------------------------------------------------------------- PS PID TTY TIME CMD 29241 pts/8 0:00 server 27969 pts/8 0:00 tcsh 29212 pts/8 0:00 test.sh -------------------------------------------------------------------------- Bad File HTTP/1.1 404 Not Found Date: Wed, 29 Nov 2000 04:31:50 GMT Server: Indigo Simple Server/1.0 (Unix) Connection: close Content-Type: text/html 404 Not Found

Not Found

The requested URL /bad_file was not found on this server.

./client: transfered 293 bytes -------------------------------------------------------------------------- Bad Command HTTP/1.1 501 Method Not Implemented Date: Wed, 29 Nov 2000 04:31:51 GMT Server: Indigo Simple Server/1.0 (Unix) Connection: close Content-Type: text/html 501 Method Not Implemented

Method Not Implemented

Invalid method in request:

PUT /file1.txt HTTP/1.0

./client: transfered 338 bytes -------------------------------------------------------------------------- PS PID TTY TIME CMD 29241 pts/8 0:00 server 27969 pts/8 0:00 tcsh 29212 pts/8 0:00 test.sh -------------------------------------------------------------------------- Big HTML File ./client: transfered 98203 bytes Diff Original and Received -------------------------- 0a1,9 > HTTP/1.1 200 OK > Date: Wed, 29 Nov 2000 04:31:51 GMT > Server: Indigo Simple Server/1.0 (Unix) > Last-Modified: Wed, 29 Nov 2000 04:31:48 GMT > Accept-Ranges: bytes > Content-Length: 97980 > Connection: close > Content-Type: text/html > -------------------------------------------------------------------------- 20 Simultaneous Connections ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes Diff Original and Received -------------------------- 0a1,9 > HTTP/1.1 200 OK > Date: Wed, 29 Nov 2000 04:31:51 GMT > Server: Indigo Simple Server/1.0 (Unix) > Last-Modified: Wed, 29 Nov 2000 04:31:48 GMT > Accept-Ranges: bytes > Content-Length: 4899 > Connection: close > Content-Type: text/html > Comparing Received Files -------------------------- file2.1.out file2.2.out differ: char 47, line 2 file2.1.out file2.3.out differ: char 47, line 2 file2.1.out file2.4.out differ: char 47, line 2 file2.1.out file2.5.out differ: char 47, line 2 file2.1.out file2.6.out differ: char 47, line 2 file2.1.out file2.7.out differ: char 47, line 2 file2.1.out file2.8.out differ: char 47, line 2 file2.1.out file2.9.out differ: char 47, line 2 file2.1.out file2.10.out differ: char 47, line 2 file2.1.out file2.11.out differ: char 47, line 2 file2.1.out file2.12.out differ: char 47, line 2 file2.1.out file2.13.out differ: char 47, line 2 file2.1.out file2.14.out differ: char 47, line 2 file2.1.out file2.15.out differ: char 47, line 2 file2.1.out file2.16.out differ: char 47, line 2 file2.1.out file2.17.out differ: char 47, line 2 file2.1.out file2.18.out differ: char 47, line 2 file2.1.out file2.19.out differ: char 47, line 2 file2.1.out file2.20.out differ: char 47, line 2 -------------------------------------------------------------------------- 50 Simultaneous Connections ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes ./client: transfered 5121 bytes Diff Original and Received -------------------------- 0a1,9 > HTTP/1.1 200 OK > Date: Wed, 29 Nov 2000 04:32:30 GMT > Server: Indigo Simple Server/1.0 (Unix) > Last-Modified: Wed, 29 Nov 2000 04:31:48 GMT > Accept-Ranges: bytes > Content-Length: 4899 > Connection: close > Content-Type: text/html > Comparing Received Files -------------------------- file2.21.out file2.25.out differ: char 47, line 2 file2.21.out file2.26.out differ: char 47, line 2 file2.21.out file2.27.out differ: char 47, line 2 file2.21.out file2.28.out differ: char 47, line 2 file2.21.out file2.29.out differ: char 47, line 2 file2.21.out file2.30.out differ: char 47, line 2 file2.21.out file2.31.out differ: char 47, line 2 file2.21.out file2.32.out differ: char 47, line 2 file2.21.out file2.33.out differ: char 47, line 2 file2.21.out file2.34.out differ: char 47, line 2 file2.21.out file2.35.out differ: char 47, line 2 file2.21.out file2.36.out differ: char 47, line 2 file2.21.out file2.37.out differ: char 47, line 2 file2.21.out file2.38.out differ: char 47, line 2 file2.21.out file2.39.out differ: char 47, line 2 file2.21.out file2.40.out differ: char 47, line 2 file2.21.out file2.41.out differ: char 47, line 2 file2.21.out file2.42.out differ: char 47, line 2 file2.21.out file2.43.out differ: char 47, line 2 file2.21.out file2.44.out differ: char 47, line 2 file2.21.out file2.45.out differ: char 47, line 2 file2.21.out file2.46.out differ: char 47, line 2 file2.21.out file2.47.out differ: char 47, line 2 file2.21.out file2.48.out differ: char 47, line 2 file2.21.out file2.49.out differ: char 47, line 2 file2.21.out file2.50.out differ: char 47, line 2 file2.21.out file2.51.out differ: char 47, line 2 file2.21.out file2.52.out differ: char 47, line 2 file2.21.out file2.53.out differ: char 47, line 2 file2.21.out file2.54.out differ: char 46, line 2 file2.21.out file2.55.out differ: char 46, line 2 file2.21.out file2.56.out differ: char 46, line 2 file2.21.out file2.57.out differ: char 46, line 2 file2.21.out file2.58.out differ: char 46, line 2 file2.21.out file2.59.out differ: char 46, line 2 file2.21.out file2.60.out differ: char 46, line 2 file2.21.out file2.61.out differ: char 46, line 2 file2.21.out file2.62.out differ: char 46, line 2 file2.21.out file2.63.out differ: char 46, line 2 file2.21.out file2.64.out differ: char 46, line 2 file2.21.out file2.65.out differ: char 46, line 2 file2.21.out file2.66.out differ: char 46, line 2 file2.21.out file2.67.out differ: char 46, line 2 file2.21.out file2.68.out differ: char 46, line 2 file2.21.out file2.69.out differ: char 46, line 2 file2.21.out file2.70.out differ: char 46, line 2 -------------------------------------------------------------------------- Server Output Indigo Simple Server/1.0 (Unix) starting... -------------------------------------------------------------------------- Kill Server 29241 Killed