第14章 線程機制:併發函數的使用--多線程Web服務器

/* 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
 */

 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <string.h>

 #include <pthread.h>
 #include <stdlib.h>
 #include <unistd.h>

 #include <dirent.h>
 #include <time.h>

 /* 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;
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章