• tinyhttpd源码学习2


    直接进入主题吧,代码量不大

    /* J. David's webserver */
    /* This is a simple webserver.
     * Created November 1999 by J. David Blackstone.
     * CSE 4344 (Network concepts), Prof. Zeigler
     * University of Texas at Arlington
     */
    /* This program compiles for Sparc Solaris 2.6.
     * To compile for Linux:
     *  1) Comment out the #include <pthread.h> line.
     *  2) Comment out the line that defines the variable newthread.
     *  3) Comment out the two lines that run pthread_create().
     *  4) Uncomment the line that runs accept_request().
     *  5) Remove -lsocket from the Makefile.
     */
    #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>
    
    #define ISspace(x) isspace((int)(x))
    
    #define SERVER_STRING "Server: jdbhttpd/0.1.0
    "
    
    void accept_request(int);
    void bad_request(int);
    void cat(int, FILE *);
    void cannot_execute(int);
    void error_die(const char *);
    void execute_cgi(int, const char *, const char *, const char *);
    int get_line(int, char *, int);
    void headers(int, const char *);
    void not_found(int);
    void serve_file(int, const char *);
    int startup(u_short *);
    void unimplemented(int);
    
    /**********************************************************************/
    /* A request has caused a call to accept() on the server port to
     * return.  Process the request appropriately.
     * Parameters: the socket connected to the client */
    /**********************************************************************/
    void accept_request(int client)
    {
     char buf[1024];
     int numchars;
     char method[255];
     char url[255];
     char path[512];
     size_t i, j;
     struct stat st;
     int cgi = 0;      /* becomes true if server decides this is a CGI
                        * program */
     char *query_string = NULL;
    
     numchars = get_line(client, buf, sizeof(buf));
     i = 0; j = 0;
     while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
     {
      method[i] = buf[j];
      i++; j++;
     }
     method[i] = '';
    
     if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
     {
      unimplemented(client);
      return;
     }
    
     if (strcasecmp(method, "POST") == 0)
      cgi = 1;
    
     i = 0;
     while (ISspace(buf[j]) && (j < sizeof(buf)))
      j++;
     while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
     {
      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, "htdocs%s", url);
     if (path[strlen(path) - 1] == '/')
      strcat(path, "index.html");
     if (stat(path, &st) == -1) {
      while ((numchars > 0) && strcmp("
    ", buf))  /* read & discard headers */
       numchars = get_line(client, buf, sizeof(buf));
      not_found(client);
     }
     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);
     }
    
     close(client);
    }
    
    /**********************************************************************/
    /* Inform the client that a request it has made has a problem.
     * Parameters: client socket */
    /**********************************************************************/
    void bad_request(int client)
    {
     char buf[1024];
    
     sprintf(buf, "HTTP/1.0 400 BAD REQUEST
    ");
     send(client, buf, sizeof(buf), 0);
     sprintf(buf, "Content-type: text/html
    ");
     send(client, buf, sizeof(buf), 0);
     sprintf(buf, "
    ");
     send(client, buf, sizeof(buf), 0);
     sprintf(buf, "<P>Your browser sent a bad request, ");
     send(client, buf, sizeof(buf), 0);
     sprintf(buf, "such as a POST without a Content-Length.
    ");
     send(client, buf, sizeof(buf), 0);
    }
    
    /**********************************************************************/
    /* Put the entire contents of a file out on a socket.  This function
     * is named after the UNIX "cat" command, because it might have been
     * easier just to do something like pipe, fork, and exec("cat").
     * Parameters: the client socket descriptor
     *             FILE pointer for the file to cat */
    /**********************************************************************/
    void cat(int client, FILE *resource)
    {
     char buf[1024];
    
     fgets(buf, sizeof(buf), resource);
     while (!feof(resource))
     {
      send(client, buf, strlen(buf), 0);
      fgets(buf, sizeof(buf), resource);
     }
    }
    
    /**********************************************************************/
    /* Inform the client that a CGI script could not be executed.
     * Parameter: the client socket descriptor. */
    /**********************************************************************/
    void cannot_execute(int client)
    {
     char buf[1024];
    
     sprintf(buf, "HTTP/1.0 500 Internal Server Error
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "Content-type: text/html
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "<P>Error prohibited CGI execution.
    ");
     send(client, buf, strlen(buf), 0);
    }
    
    /**********************************************************************/
    /* Print out an error message with perror() (for system errors; based
     * on value of errno, which indicates system call errors) and exit the
     * program indicating an error. */
    /**********************************************************************/
    void error_die(const char *sc)
    {
     perror(sc);
     exit(1);
    }
    
    /**********************************************************************/
    /* Execute a CGI script.  Will need to set environment variables as
     * appropriate.
     * Parameters: client socket descriptor
     *             path to the CGI script */
    /**********************************************************************/
    void execute_cgi(int client, const char *path,
                     const char *method, const char *query_string)
    {
     char buf[1024];
     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))  /* read & discard headers */
       numchars = get_line(client, buf, sizeof(buf));
     else    /* POST */
     {
      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) {
       bad_request(client);
       return;
      }
     }
    
     sprintf(buf, "HTTP/1.0 200 OK
    ");
     send(client, buf, strlen(buf), 0);
    
     if (pipe(cgi_output) < 0) {
      cannot_execute(client);
      return;
     }
     if (pipe(cgi_input) < 0) {
      cannot_execute(client);
      return;
     }
    
     if ( (pid = fork()) < 0 ) {
      cannot_execute(client);
      return;
     }
     if (pid == 0)  /* child: CGI script */
     {
      char meth_env[255];
      char query_env[255];
      char length_env[255];
    
      dup2(cgi_output[1], 1);
      dup2(cgi_input[0], 0);
      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 {   /* POST */
       sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
       putenv(length_env);
      }
      execl(path, path, NULL);
      exit(0);
     } else {    /* parent */
      close(cgi_output[1]);
      close(cgi_input[0]);
      if (strcasecmp(method, "POST") == 0)
       for (i = 0; i < content_length; i++) {
        recv(client, &c, 1, 0);
        write(cgi_input[1], &c, 1);
       }
      while (read(cgi_output[0], &c, 1) > 0)
       send(client, &c, 1, 0);
    
      close(cgi_output[0]);
      close(cgi_input[1]);
      waitpid(pid, &status, 0);
     }
    }
    
    /**********************************************************************/
    /* Get a line from a socket, whether the line ends in a newline,
     * carriage return, or a CRLF combination.  Terminates the string read
     * with a null character.  If no newline indicator is found before the
     * end of the buffer, the string is terminated with a null.  If any of
     * the above three line terminators is read, the last character of the
     * string will be a linefeed and the string will be terminated with a
     * null character.
     * Parameters: the socket descriptor
     *             the buffer to save the data in
     *             the size of the buffer
     * Returns: the number of bytes stored (excluding null) */
    /**********************************************************************/
    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);
      /* DEBUG printf("%02X
    ", c); */
      if (n > 0)
      {
       if (c == '
    ')
       {
        n = recv(sock, &c, 1, MSG_PEEK);
        /* DEBUG printf("%02X
    ", c); */
        if ((n > 0) && (c == '
    '))
         recv(sock, &c, 1, 0);
        else
         c = '
    ';
       }
       buf[i] = c;
       i++;
      }
      else
       c = '
    ';
     }
     buf[i] = '';
     
     return(i);
    }
    
    /**********************************************************************/
    /* Return the informational HTTP headers about a file. */
    /* Parameters: the socket to print the headers on
     *             the name of the file */
    /**********************************************************************/
    void headers(int client, const char *filename)
    {
     char buf[1024];
     (void)filename;  /* could use filename to determine file type */
    
     strcpy(buf, "HTTP/1.0 200 OK
    ");
     send(client, buf, strlen(buf), 0);
     strcpy(buf, SERVER_STRING);
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "Content-Type: text/html
    ");
     send(client, buf, strlen(buf), 0);
     strcpy(buf, "
    ");
     send(client, buf, strlen(buf), 0);
    }
    
    /**********************************************************************/
    /* Give a client a 404 not found status message. */
    /**********************************************************************/
    void not_found(int client)
    {
     char buf[1024];
    
     sprintf(buf, "HTTP/1.0 404 NOT FOUND
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, SERVER_STRING);
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "Content-Type: text/html
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "<HTML><TITLE>Not Found</TITLE>
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "<BODY><P>The server could not fulfill
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "your request because the resource specified
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "is unavailable or nonexistent.
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "</BODY></HTML>
    ");
     send(client, buf, strlen(buf), 0);
    }
    
    /**********************************************************************/
    /* Send a regular file to the client.  Use headers, and report
     * errors to client if they occur.
     * Parameters: a pointer to a file structure produced from the socket
     *              file descriptor
     *             the name of the file to serve */
    /**********************************************************************/
    void serve_file(int client, const char *filename)
    {
     FILE *resource = NULL;
     int numchars = 1;
     char buf[1024];
    
     buf[0] = 'A'; buf[1] = '';
     while ((numchars > 0) && strcmp("
    ", buf))  /* read & discard headers */
      numchars = get_line(client, buf, sizeof(buf));
    
     resource = fopen(filename, "r");
     if (resource == NULL)
      not_found(client);
     else
     {
      headers(client, filename);
      cat(client, resource);
     }
     fclose(resource);
    }
    
    /**********************************************************************/
    /* This function starts the process of listening for web connections
     * on a specified port.  If the port is 0, then dynamically allocate a
     * port and modify the original port variable to reflect the actual
     * port.
     * Parameters: pointer to variable containing the port to connect on
     * Returns: the socket */
    /**********************************************************************/
    int startup(u_short *port)
    {
     int httpd = 0;
     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 (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
      error_die("bind");
     if (*port == 0)  /* if dynamically allocating a port */
     {
      int 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);
    }
    
    /**********************************************************************/
    /* Inform the client that the requested web method has not been
     * implemented.
     * Parameter: the client socket */
    /**********************************************************************/
    void unimplemented(int client)
    {
     char buf[1024];
    
     sprintf(buf, "HTTP/1.0 501 Method Not Implemented
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, SERVER_STRING);
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "Content-Type: text/html
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "</TITLE></HEAD>
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "<BODY><P>HTTP request method not supported.
    ");
     send(client, buf, strlen(buf), 0);
     sprintf(buf, "</BODY></HTML>
    ");
     send(client, buf, strlen(buf), 0);
    }
    
    /**********************************************************************/
    
    int main(void)
    {
     int server_sock = -1;
     u_short port = 0;
     int client_sock = -1;
     struct sockaddr_in client_name;
     int client_name_len = sizeof(client_name);
     pthread_t newthread;
    
     server_sock = startup(&port);
     printf("httpd running on port %d
    ", port);
    
     while (1)
     {
      client_sock = accept(server_sock,
                           (struct sockaddr *)&client_name,
                           &client_name_len);
      if (client_sock == -1)
       error_die("accept");
     /* accept_request(client_sock); */
     if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0)
       perror("pthread_create");
     }
    
     close(server_sock);
    
     return(0);
    }
    

    再添加一个clinet测试程序

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
     int sockfd;
     int len;
     struct sockaddr_in address;
     int result;
     char ch = 'A';
    
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     address.sin_family = AF_INET;
     address.sin_addr.s_addr = inet_addr("127.0.0.1");
     address.sin_port = htons(9734);
     len = sizeof(address);
     result = connect(sockfd, (struct sockaddr *)&address, len);
    
     if (result == -1)
     {
      perror("oops: client1");
      exit(1);
     }
     write(sockfd, &ch, 1);
     read(sockfd, &ch, 1);
     printf("char from server = %c
    ", ch);
     close(sockfd);
     exit(0);
    }
    
    短小精悍,教材式的代码,学习整个框架和模式绝对的标本

    难点应该没有,原理性的实现

    每天早上叫醒你的不是闹钟,而是心中的梦~
  • 相关阅读:
    Docker 中国官方镜像加速
    mysql主从同步
    hadoop相关
    Mac上Ultra Edit的激活
    ansible安装应用软件
    Docker学习第二天-容器
    wwnjld团队第二轮迭代成员分数
    第二轮迭代发布报告
    wwnjld第二轮迭代测试报告
    12.24会议记录
  • 原文地址:https://www.cnblogs.com/vintion/p/4116812.html
Copyright © 2020-2023  润新知