第12章 连接和协议:编写Web服务器

/*
 * webserv.c - a minimal web server (version 0.2)
 * usage:   ws portnumber
 * features:    supports the GET command only
 *              runs in the current directory
 *              forks a new child to handle each request
 *              has MAJOR security holes, for demo purposes only
 *              has many other weaknesses, but is a good start
 *  build:      cc webserv.c socklib.c -o webserv
 */

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

main(int ac, char *av[])
{
    int     sock, fd;
    FILE    *fpin;
    char    request[BUFSIZ];

    if (ac == 1) {
        fprintf(stderr, "usage: ws portnum\n");
        exit(1);
    }
    sock = make_server_socket(atoi(av[1]));
    if (sock == -1)
        exit(2);

    /* main loop here */

    while (1) {
        /* take a call and buffer it */
        fd = accept(sock, NULL, NULL);
        fpin = fdopen(fd, "r");

        /* read request */
        fgets(request, BUFSIZ, fpin);
        printf("got a call: request = %s", request);
        read_til_crnl(fpin);

        /* do what client asks */
        process_rq(request, fd);

        fclose(fpin);
    }
}

/* ----------------------------------------------------------------------------------- *
 * read_til_crnl(FILE *)
 * skip over all request info until a CRNL is seen
 * ----------------------------------------------------------------------------------- */

read_til_crnl(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];

    /* create a new process and return if not the child */
    if (fork() != 0)
        return;

    strcpy(arg, "./");                      /* precedure args with ./ */
    if (sscanf(rq, "%s%s", cmd, arg + 2) != 2)
        return;

    if (strcmp(cmd, "GET") != 0)
        cannot_do(fd);
    else if (not_exist(arg))
        do_404(arg, fd);
    else if (isadir(arg))
        do_ls(arg, fd);
    else if (ends_in_cgi(arg))
        do_exec(arg, fd);
    else
        do_cat(arg, fd);
}

/* ------------------------------------------------------------------------------------ *
 * the reply header thind: all functions need one
 * if content_type is NULL then don'g send content type
 * ------------------------------------------------------------------------------------ */

header(FILE *fp, char *content_type)
{
    fprintf(fp, "HTTP/1.0 200 OK\r\n");
    if (content_type)
        fprintf(fp, "Content-type: %s\r\n", content_type);
}

/* ----------------------------------------------------------------------------------- *
 * simple function first:
 * cannot_do(fd)            unimplemented HTTP command
 * and do_404(item, fd)     no such object
 * ----------------------------------------------------------------------------------- */

cannot_do(int fd)
{
    FILE *fp = fdopen(fd, "w");

    fprintf(fp, "HTTP/1.0 501 Not Implemented\r\n");
    fprintf(fp, "Content-type: text/plain\r\n");
    fprintf(fp, "\r\n");

    fprintf(fp, "That command is not yet implemented\r\n");
    fclose(fp);
}

do_404(char *item, int fd)
{
    FILE *fp = fdopen(fd, "w");

    fprintf(fp, "HTTP/1.0 404 Not Found\r\n");
    fprintf(fp, "Content-type: text/plain\r\n");
    fprintf(fp, "\r\n");

    fprintf(fp, "The item you requested: %s\r\nis not found\r\n", item);
    fclose(fp);
}

/* ----------------------------------------------------------------------------------- *
 * the directory listing section
 * isadir() uses stat, not_exist() uses stat
 * do_ls runs ls. It should not
 * ----------------------------------------------------------------------------------- */

isadir(char *f)
{
    struct stat info;
    return (stat(f, &info) != -1 && S_ISDIR(info.st_mode));
}

not_exist(char *f)
{
    struct stat info;
    return (stat(f, &info) == -1);
}

do_ls(char *dir, int fd)
{
    FILE *fp;

    fp = fdopen(fd, "w");
    header(fp, "text/plain");
    fprintf(fp, "\r\n");
    fflush(fp);

    dup2(fd, 1);
    dup2(fd, 2);
    close(fd);
    execlp("ls", "ls", "-1", dir, NULL);
    perror(dir);
    exit(1);
}

/* ----------------------------------------------------------------------------------- *
 * the cgi stuff. function to check extension and
 * one to run the program.
 * ----------------------------------------------------------------------------------- */

char *file_type(char *f)
/* returns 'extension' of file */
{
    char *cp;
    if ((cp = strrchr(f, '.')) != NULL)
        return cp + 1;
    return "";
}

ends_in_cgi(char *f)
{
    return (strcmp(file_type(f), "cgi") == 0);
}

do_exec(char *prog, int fd)
{
    FILE *fp;

    fp = fdopen(fd, "w");
    header(fp, NULL);
    fflush(fp);
    dup2(fd, 1);
    dup2(fd, 2);
    close(fd);
    execl(prog, prog, NULL);
    perror(prog);
}

/* ------------------------------------------------------------------------------------- *
 * do_cat(filename, fd)
 * send back contents after a header
 * ------------------------------------------------------------------------------------- */

do_cat(char *f, int fd)
{
    char    *extension = file_type(f);
    char    *content = "text/plain";
    FILE    *fpsock, *fpfile;
    int     c;

    if (strcmp(extension, "html") == 0)
        content = "tent/html";
    else if (strcmp(extension, "gif") == 0)
        content = "tent/gif";
    else if (strcmp(extension, "jpg") == 0)
        content = "tent/jpg";
    else if (strcmp(extension, "jpeg") == 0)
        content = "tent/jpeg";

    fpsock = fdopen(fd, "w");
    fpfile = fopen(f, "r");
    if (fpsock != NULL && fpfile != NULL)
    {
        header(fpsock, content);
        fprintf(fpsock, "\r\n");
        while (c = getc(fpfile) != EOF)
            putc(c, fpsock);
        fclose(fpfile);
        fclose(fpsock);
    }
    exit(0);
}

/*
 * socklib.c
 *
 * This file contains functions used lot when writing internet
 * client/server programs. The two main functions here are:
 *
 * int make_server_socket( portnum ) returns a server socket
 * or -1 if error
 *
 * int make_server_socket_q(portnum, backlog)
 *
 * int connect_to_server(char *hostname, int portnum)
 *
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <time.h>
#include <strings.h>

#define HOSTLEN 256
#define BACKLOG 1

int make_server_socket_q(int, int);

int make_server_socket(int portnum)
{
    return make_server_socket_q(portnum, BACKLOG);
}

int make_server_socket_q(int portnum, int backlog)
{
    struct  sockaddr_in saddr;                          /* build our address here */
    struct  hostent *hp;                                /* this is part of our */
    char    hostname[HOSTLEN];                          /* address */
    int     sock_id;                                    /* the socket */

    sock_id = socket(PF_INET, SOCK_STREAM, 0);          /* get a socket */
    if (sock_id == -1)
        return -1;

    /* * build address and bind it to socket * */

    bzero((void *)&saddr, sizeof(saddr));               /* clear out struct */
    gethostname(hostname, HOSTLEN);                     /* where am I ? */
    hp = gethostbyname(hostname);                       /* get info about host */
                                                        /* fill in host part */
    bcopy((void *)hp->h_addr, (void *)&saddr.sin_addr,
            hp->h_length);
    saddr.sin_port = htons(portnum);                    /* fill in socket port */
    saddr.sin_family = AF_INET;                         /* fill in addr family */
    if (bind(sock_id, (struct sockaddr *)&saddr,
                sizeof(saddr)) != 0)
        return -1;

    /* * arrange for incoming calls * */

    if (listen(sock_id, backlog) != 0)
        return -1;
    return sock_id;
}

int connect_to_server(char *host, int portnum)
{
    int sock;
    struct sockaddr_in servadd;                         /* the number to call */
    struct hostent      *hp;                            /* used to get number */

    /* * Step1: Get a socket * */                       

    sock = socket( AF_INET, SOCK_STREAM, 0 );           /* get a line */
    if ( sock == -1 )
        return -1;

    /* * Step2: connect to server * */

    bzero(&servadd, sizeof(servadd));                   /* zero the address */
    hp = gethostbyname( host );                         /* lookup host's ip # */
    if (hp == NULL)
        return -1;
    bcopy(hp->h_addr, (struct sockaddr *)&servadd.sin_addr,
            hp->h_length);
    servadd.sin_port = htons(portnum);                  /* fill in port number */
    servadd.sin_family = AF_INET;                       /* fill in socket type */

    if ( connect(sock, (struct sockaddr *)&servadd,
                sizeof(servadd)) != 0)
        return -1;
    return sock;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值