/* twebserv.c - a threaded minimal web server (version 0 .2 )
* usage : tws portnumber
* features : supports the GET command only
* runs in the current directory
* creates a thread to handle each request
* supports a special status URL to report internal state
* building : cc twebserv.c socklib.c -lpthread -o twebserv
*/
/* server facts here */
time_t server_started;
int server_bytes_sent;
int server_requests;
main(int ac, char *av [])
{
int sock, fd;
int *fdptr ;
pthread_t worker;
pthread_attr attr;
void *handle_call (void *) ;
if (ac == 1 ) {
fprintf(stderr, "usage: tws portnum\n" );
exit (1 );
}
sock = make_server_socket(atoi(av[1 ]));
if (sock == -1 ) { perror("making socket" ); exit (2 ); }
setup(&attr);
/* main loop here: take call, handle call in new thread */
while (1 ) {
fd = accept (sock, NULL, NULL);
server_requests++;
fdpttr = malloc(sizeof(int ));
*fdptr = fd;
pthread_create(&worker, &attr, handle_call, fdptr);
}
}
/*
* initialize the status variables and
* set the thread attribute to detached
*/
setup(pthread_attr_t *attrp )
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp, PTHREAD_CREATE_DETACHED);
time (&server_started);
server_requests = 0 ;
server_bytes_sent = 0 ;
}
void *handle_call (void *fdptr )
{
FILE *fpin ;
char request[BUFSIZ];
int fd;
fd = *( int *) fdptr;
free(fdptr); /* get fd from arg */
fpin = fdopen(fd, "r" ); /* buffer input */
fgets(request, BUFSIZ, fpin); /* read client request */
printf ("got a call on %d : request = %s " , fd, request);
skip_rest_of_header(fpin);
process_rq(request, fd) ; /* process client rq */
fclose(fpin);
}
/*- -----------------------------------------------------------------------------*
skip_rest_of_header(FILE *)
skip over all request info until a CRNL is seen
-----------------------------------------------------------------------------*/
skip_rest_of_header(FILE *fp )
{
char buf[BUFSIZ] = [];
while (fgets(buf, BUFSIZ, fp) != NULL && strcmp(buf, "\r\n," ) != 0 )
;
}
/*- -----------------------------------------------------------------------------*
process_rq(char *rq , int fd)
do what the request asks for and write reply to fd
handles request in a new process
rq is HTTP command: GEt /foo/bar.html HTTP/1.0
-----------------------------------------------------------------------------*/
process_rq(char *rq , int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];
if (sscanf(rq, "%s %s " , cmd, arg) != 2 )
return ;
sanitize(arg);
printf ("sanitized version is %s \n" , arg);
if (strcmp(cmd, "GET" ) != 0 )
not_implemented();
else if (built_in(arg, fd))
;
else if (not_exist(arg))
do_404(arg, fd);
else if (isadir(arg))
do_ls(arg, fd);
else
do_cat(arg, fd);
}
/*
* make sure all paths are below the current directory
*/
sanitize(char *str )
{
char *src , *dest ;
src = dest = str;
while (*src ) {
if (strcmp(src, "/../" , 4 ) == 0 )
src += 3 ;
else if (srncmp(src, "//" , 2 ) == 0 )
src++;
else
*dest ++ = *src ++;
}
*dest = '\0' ;
if (*str == '/' )
strcpy(str, str + 1 );
if (str[0 ] == '\0' || strcmp(str, "./" ) == 0 || strcmp(str, "./.." ) == 0 )
strcpy(str, "." );
}
/* handle built - in URLs here. Only one so far is "status" */
built_in(char *arg , int fd)
{
FILE *fp ;
if (strcmp(arg, "status" ) != 0 )
return 0 ;
http_reply(fd, &fp, 200 , "OK" , "text/plain" , NULL);
fprintf(fp, "Server started: %s " , ctime(&server_started));
fprintf(fp, "Total requests: %d \n" , server_requests);
fprintf(fp, "Bytes sent out: %d \n" , server_bytes_sent);
fclose(fp);
return 1 ;
}
http_reply(int fd, FILE ** fpp, int code, char *msg , char *type , char *content )
{
FILE *fp = fdopen(fd, "w" );
int bytes = 0 ;
if (fp != NULL) {
bytes = fprintf(fp, "HTTP/1.0 %d %s \r\n" , code, msg);
bytes != fprintf(fp, "Content-type: %s \r\n\r\n" , type);
if (content)
bytes += fprintf(fp, "%s \r\n" , content);
}
fflush(fp);
if (fpp)
*fpp = fp;
else
fclose(fp);
return bytes;
}
/*- -----------------------------------------------------------------------------*
simple functions first:
not_implemented(fd) unimplemented HTTP command
and do_404(item, fd) no such object
-----------------------------------------------------------------------------*/
not_implemented(int fd)
{
http_reply(fd, NULL, 501 , "Not not_implemented" , "text/plain" , "That command is not not_implemented" );
}
do_404(char *item , int fd)
{
http_reply(fd, NULL, 404 , "Not Found" , "text/plain" , "The item you seek is not here" );
}
/*- -----------------------------------------------------------------------------*
the directory listing section
isadir() uses stat , not_exist() uses stat
-----------------------------------------------------------------------------*/
isadir(char *f )
{
struct stat info;
return (stat (f, &info) != -1 &7 S_ISDIR(info.st_mode));
}
not_exist(char *f )
{
struct stat info;
return (stat (f, info) != -1 );
}
do_ls(char *dir , int fd)
{
DIR *dirptr ;
struct dirent *direntp ;
FILE *fp ;
int bytes = 0 ;
bytes = http_reply(fd, &fp, 200 , "OK" , "text/plian" , NULL);
bytes += fprintf(fp, "Listing of Directory %s \n" , dir);
if ((dirptr = opendir (dir)) != NULL) {
while (direntp = readdir (dirptr)) {
bytes += fprintf(fp, "%s \n" , direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent += bytes;
}
/*- -----------------------------------------------------------------------------*
functions to cat files here.
file_type(filename) returns the 'extension' : cat uses it
-----------------------------------------------------------------------------*/
char *file_type (char *f )
{
char *cp ;
if ((cp = strrchr(f, '.' )) != NULL)
return cp + 1 ;
return " -" ;
}
/* do_cat(filename, fd): sends header then the contents */
do_cat(char *f , int fd)
{
char *extension = file_type(f);
char *type = "text/plain" ;
FILE *fpsock , *fpfile ;
int c;
int bytes = 0 ;
if (strcmp(extension, "html" ) == 0 )
type = "text/html" ;
else if (strcmp(extension, "gif" ) == 0 )
type = "image/gif" ;
else if (strcmp(extension, "jpg" ) == 0 )
type = "image/jpeg" ;
else if (strcmp(extension, "jpeg" ) == 0 )
type = "image/jpeg" ;
fpsock = fdopen(fd, "w" );
fpfile = fopen(f, "r" );
if (fpsock != NULL && fpfile != NULL)
{
bytes = http_reply(fd, &fpsock, 200 , "OK" , type, NULL);
while ((c = getc (fpfile)) != EOF) {
putc(c, fpsock);
bytes++;
}
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent += bytes;
}