zoukankan      html  css  js  c++  java
  • Tinyhttpd

    #include <stdio.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    #include <ctype.h>
    #include <strings.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <pthread.h>
    #include <sys/wait.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <assert.h>
    
    #define ISspace(x) isspace((int)(x))
    
    #define SERVER_STRING "Server: xxrlhttpd/0.1.0
    "
    #define STDIN	0
    #define STDOUT	1
    #define STDERR	2
    #define String  const char *
    
    const int BUF_SIZE = 1024;
    const int CONTENT_SIZE = 255;
    
    // 头信息
    struct HeaderInfo
    {
        int length;
        String *head_str;
    };
    
    void accept_request(void *);
    void headers(int, struct HeaderInfo *);
    void cat(int, FILE *);
    void error_die(String);
    void execute_cgi(int, String, String , String);
    int get_line(int, char *, int);
    void serve_file(int, String);
    int startup(u_short *);
    
    struct HeaderInfo accpetInfo, notfoundInfo, badrequestInfo, unimplementedInfo, cannotexecuteInfo;
    
    /**
     * 初始化头信息
    */
    void init_headers() {
        accpetInfo.head_str = (String*)malloc(sizeof(String) * 4);
        accpetInfo.head_str[0] = "HTTP/1.0 200 OK
    ";
        accpetInfo.head_str[1] = SERVER_STRING;
        accpetInfo.head_str[2] = "Content-Type: text/html
    ";
        accpetInfo.head_str[3] = "
    ";
        accpetInfo.length = 4;
    
        notfoundInfo.head_str = (String *)malloc(sizeof(String) * 9);
        notfoundInfo.head_str[0] = "HTTP/1.0 404 NOT FOUND
    ";
        notfoundInfo.head_str[1] = SERVER_STRING;
        notfoundInfo.head_str[2] = "Content-Type: text/html
    ";
        notfoundInfo.head_str[3] = "
    ";
        notfoundInfo.head_str[4] = "<HTML><TITLE>Not Found</TITLE>
    ";
        notfoundInfo.head_str[5] = "<BODY><P>The server could not fulfill
    ";
        notfoundInfo.head_str[6] = "your request because the resorce specified
    ";
        notfoundInfo.head_str[7] = "is unavailable or nonexistent.</P>
    ";
        notfoundInfo.head_str[8] = "</BODY></HTML>
    ";
        notfoundInfo.length = 9;
    
        badrequestInfo.head_str = (String *)malloc(sizeof(String) * 6);
        badrequestInfo.head_str[0] = "HTTP/1.0 400 BAD REQUEST
    ";
        badrequestInfo.head_str[1] = SERVER_STRING;
        badrequestInfo.head_str[2] = "Content-type: text/html
    ";
        badrequestInfo.head_str[3] = "
    ";
        badrequestInfo.head_str[4] = "<P>Your browser send a bad request, ";
        badrequestInfo.head_str[5] = "such as a POST without a Content-Length.</P>
    ";
        badrequestInfo.length = 6;
    
        unimplementedInfo.head_str = (String *)malloc(sizeof(String) * 6);
        unimplementedInfo.head_str[0] = "HTTP/1.0 501 Method Not Implemented
    ";
        unimplementedInfo.head_str[1] = SERVER_STRING;
        unimplementedInfo.head_str[2] = "Content-Type: text/html
    ";
        unimplementedInfo.head_str[3] = "
    ";
        unimplementedInfo.head_str[4] = "<HTML><HEAD><TITLE>Method Not Implemented
    ";
        unimplementedInfo.head_str[5] = "</BODY></HTML>
    ";
        unimplementedInfo.length = 6;
    
        cannotexecuteInfo.head_str = (String *)malloc(sizeof(String) * 4);
        cannotexecuteInfo.head_str[0] = "HTTP/1.0 500 Internal Server Error
    ";
        cannotexecuteInfo.head_str[1] = "Content-Type: text/html
    ";
        cannotexecuteInfo.head_str[2] = "
    ";
        cannotexecuteInfo.head_str[3] = "<P>Error prohibited CGI execution.</P>
    ";
        cannotexecuteInfo.length = 4;
    }
    
    // 日志文件
    FILE *logFile;
    
    /**
     * 处理一次请求
     * 
    */
    void accept_request(void *arg)
    {
    	int client = (intptr_t)arg;
    	char buf[BUF_SIZE];
    	size_t numchars;
        char method[CONTENT_SIZE];
        char url[CONTENT_SIZE];
        char path[512];
    	size_t i, j;
    	struct stat st;
    	int cgi = 0;
        char *query_string = NULL;
    
        numchars = get_line(client, buf, sizeof(buf));
    	i = 0, j = 0;
    	while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
    	{
    		method[i] = buf[i];
    		i++;
    	}
    	j = i;
    	method[i] = '';
    
    	if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
    	{
    		headers(client, &unimplementedInfo);
    		return;
    	}
    
    	if (strcasecmp(method, "POST") == 0)
    		cgi = 1;
    
    	i = 0;
    	while (ISspace(buf[j]) && (j < numchars))
    		j++;
    	while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars))
    	{
    		url[i] = buf[j];
    		i++, j++;
    	}
    	url[i] = '';
    
    	if (strcasecmp(method, "GET") == 0)
    	{
    		query_string = url;
    		while ((*query_string != '?') && (*query_string != ''))
    			query_string++;
    		if (*query_string == '?')
    		{
    			cgi = 1;
    			*query_string = '';
    			query_string++;
    		}
    	}
    
    	sprintf(path, "htpdocs%s", url);
    	if (path[strlen(path) - 1] == '/')
    		strcat(path, "index.html");
    	if (stat(path, &st) == -1) {
    		while ((numchars > 0) && strcmp("
    ", buf))
    			numchars = get_line(client, buf, sizeof(buf));
    		headers(client, &notfoundInfo);
    	}
    	else {
    		if ((st.st_mode & S_IFMT) == S_IFDIR)
    			strcat(path, "/index.html");
    		if ((st.st_mode & S_IXUSR) || 
    				(st.st_mode & S_IXGRP) ||
    				(st.st_mode & S_IXOTH) )
    			cgi = 1;
    		if (!cgi)
    			serve_file(client, path);
    		else 
    			execute_cgi(client, path, method, query_string);
    	}
    
        fprintf(logFile, "Accept a request, path: %s	 query:%s
    ", path, query_string);
        fflush(logFile);
    
        close(client);
    }
    
    /**
     * 处理cgi请求
     * @param:
     *  client: 客户端 描述符
     *  path: 请求的路径
     *  method: 请求的方法
     *  query_string: 参数字符串
    */
    void execute_cgi(int client, String path, String method, String query_string)
    {
        char buf[BUF_SIZE];
        int cgi_output[2];
        int cgi_input[2];
        pid_t pid;
        int status;
        int i;
        char c;
        int numchars = 1;
        int content_length = -1;
    
        buf[0] = 'A';
        buf[1] = '';
        if (strcasecmp(method, "GET") == 0)
            while ((numchars > 0) && strcmp("
    ", buf))
                numchars = get_line(client, buf, sizeof(buf));
        else if (strcasecmp(method, "POST") == 0)
        {
            numchars = get_line(client, buf, sizeof(buf));
            while ((numchars > 0) && strcmp("
    ", buf))
            {
                buf[15] = '';
                if (strcasecmp(buf, "Content-Length:") == 0)
                    content_length = atoi(&(buf[16]));
                numchars = get_line(client, buf, sizeof(buf));
            }
            if (content_length == -1)
            {
                headers(client, &badrequestInfo);
                return;
            }
        }
        else
        {
            // other request
        }
    
        // 创建管道和子进程
        if (pipe(cgi_output) < 0 || pipe(cgi_input) < 0 || (pid = fork()) < 0)
        {
            headers(client, &cannotexecuteInfo);
            return;
        }
    
        sprintf(buf, "HTTP/1.0 200 OK
    ");
        send(client, buf, strlen(buf), 0);
        if (pid == 0) /*child: CGI script*/
        {
            char meth_env[CONTENT_SIZE];
            char query_env[CONTENT_SIZE];
            char length_env[CONTENT_SIZE];
            // 重定向标准输入输出, 子进程在output向父进程输出, 在input获取输入
            dup2(cgi_output[1], STDOUT);
            dup2(cgi_input[0], STDIN);
            close(cgi_output[0]);
            close(cgi_input[1]);
            sprintf(meth_env, "REQUEST_METHOD=%s", method);
            putenv(meth_env);
            if (strcasecmp(method, "GET") == 0)
            {
                sprintf(query_env, "QUERY_STRING=%s", query_string);
                putenv(query_env);
            }
            else
            {
                sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
                putenv(length_env);
            }
            // 执行脚本, 退出子进程
            execl(path, NULL);
            exit(0);
        }
        else
        { /* parent */
            // 父进程 input向子进程输出, 在output获取输入
            close(cgi_output[1]);
            close(cgi_input[0]);
            if (strcasecmp(method, "POST") == 0)
                // 如果是post, 就向子进程写入长度为 content_length的数据 
                for (i = 0; i < content_length; ++i)
                {
                    recv(client, &c, 1, 0);
                    write(cgi_input[1], &c, 1);
                }
            // 读取子进程的输出, 发送给socket
            while (read(cgi_output[0], &c, 1))
                send(client, &c, 1, 0);
    
            // io完毕关闭描述符,等待子进程退出
            close(cgi_output[0]);
            close(cgi_input[1]);
            waitpid(pid, &status, 0);
        }
    }
    
    /**
     * 响应文件
     * @param:
     *  client: 客户端 描述符
     *  filename: 文件路径
    */
    void serve_file(int client, String filename)
    {
        FILE *resource = NULL;
        int numchars = 1;
        char buf[BUF_SIZE];
    
        // 获取 client 的数据
        buf[0] = 'A';
        buf[1] = '';
        while ((numchars > 0) && strcmp("
    ", buf))
            numchars = get_line(client, buf, sizeof(buf));
    
        // 向流中写入 文件
        resource = fopen(filename, "r");
        if (resource == NULL)
            headers(client, &notfoundInfo);
        else
        {
            headers(client, &accpetInfo);
            cat(client, resource);
        }
        fclose(resource);
    }
    
    /**
     * 将文件中的数据传送到流中
     * @param:
     *  client: 客户端 描述符
     *  resource: 文件
    */
    void cat(int client, FILE* resource)
    {
        char buf[BUF_SIZE];
    
        fgets(buf, sizeof(buf), resource);
        while (!feof(resource))
        {
            send(client, buf, strlen(buf), 0);
            fgets(buf, sizeof(buf), resource);
        }
    }
    
    /**
     * 出错后失败退出
     * @param:
     *  sc: 错误信息
    */
    void error_die(String sc)
    {
        perror(sc);
        exit(1);
    }
    
    /**
     * sock 流中获取一行
     * @param:
     *  sock: sockfd 描述符
     *  buf: 缓冲流
     *  size: 最长读取长度
     * @return:
     *  读取到的字节数
    */
    int get_line(int sock, char *buf, int size)
    {
        int i = 0;
        char c = '';
        int n;
    
        while ((i < size - 1) && (c != '
    '))
        {
            n = recv(sock, &c, 1, 0);
            if (n > 0)
            {
                // 
     or 
     => 
    
                if (c == '
    ')
                {
                    n = recv(sock, &c, 1, MSG_PEEK);
                    if ((n > 0) && (c == '
    '))
                        recv(sock, &c, 1, 0);
                    else
                        c = '
    ';
                }
                buf[i] = c;
                i++;
            }
            else
                break;
        }
        buf[i] = '';
    
        return i;
    }
    
    /**
     * 根据headerInfo 写入header信息
     * @param:
     *  client: 客户端 描述符
     *  headerInfo: 响应的头信息
     * 
    */
    void headers(int client, struct HeaderInfo *headerInfo)
    {
        char buf[BUF_SIZE];
    
        int i = 0;
        for (i = 0; i < headerInfo->length; ++i)
        {
            sprintf(buf, headerInfo->head_str[i]);
            send(client, buf, strlen(buf), 0);
        }
    }
    
    /**
     * 创建socket 绑定到指定端口
     * @param:
     *  port 端口号
     * @return:
     *  socket 描述符
    */
    int startup(u_short *port)
    {
        int httpd = 0;
        int on = 1;
        struct sockaddr_in name;
    
        httpd = socket(PF_INET, SOCK_STREAM, 0);
        if (httpd == -1)
            error_die("socket");
        memset(&name, 0, sizeof(name));
        name.sin_family = AF_INET;
        name.sin_port = htons(*port);
        name.sin_addr.s_addr = htonl(INADDR_ANY);
        if ((setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0)
            error_die("setsockopt failed");
        if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
            error_die("bind");
        if (*port == 0)
        {
            socklen_t namelen = sizeof(name);
            if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
                error_die("getsockname");
            *port = ntohs(name.sin_port);
        }
        if (listen(httpd, 5) < 0)
            error_die("listen");
        return httpd;
    }
    
    int main(void)
    {
        int server_sock = -1;
        u_short port = 4000;
        int client_sock = -1;
        struct sockaddr_in client_name;
        socklen_t client_name_len = sizeof(client_name);
        pthread_t newthread;
    
        //prepare to start
        init_headers();
        server_sock = startup(&port);
        printf("httpd running on port %d
    ", port);
        logFile = fopen("log", "w");
    
        // main loop
        while (1)
        {
            // accpet a new request
            client_sock = accept(server_sock,
                (struct sockaddr *)&client_name,
                &client_name_len);
            if (client_sock == -1)
                error_die("accept");
            // send to a new thread
            if (pthread_create(&newthread, NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
                perror("pthread_create");
        }
        // will never reach
        fclose(logFile);
        close(server_sock);
    
        printf("Program exit.");
        return 0;
    }
    

    more detail in

  • 相关阅读:
    14.1.1 使用InnoDB 表的好处:
    7.5.1 Point-in-Time Recovery Using Event Times 使用Event Times 基于时间点恢复
    7.5 Point-in-Time (Incremental) Recovery Using the Binary Log 使用binay log 基于时间点恢复
    7.4.1 Dumping Data in SQL Format with mysqldump
    7.3.2 Using Backups for Recovery 使用备份用于恢复
    7.3.1 Establishing a Backup Policy
    RR 和RC隔离问题
    mark
    weblogic12
    转一篇对EJB理解的文章
  • 原文地址:https://www.cnblogs.com/xxrlz/p/13550736.html
Copyright © 2011-2022 走看看