只有一个C语言文件简单的 Web 服务器

jopen 11年前
只有一个C语言文件简单的 Web 服务器
只有一个C语言文件简单的 Web 服务器
/*   * WebServer.c   *   *  Created on: Nov 3, 2012   *      Author: pavithra   *   * A web server in C language using only the standard libraries.   * The port number is passed as an argument.   *   */    #include <stdio.h>  #include <unistd.h>  #include <stdlib.h>  #include <string.h>  #include <sys/types.h>  #include <sys/socket.h>  #include <netinet/in.h>  #include <fcntl.h>  #include <errno.h>    #define EOL "\r\n"  #define EOL_SIZE 2    typedef struct {   char *ext;   char *mediatype;  } extn;    //Possible media types  extn extensions[] ={   {"gif", "image/gif" },   {"txt", "text/plain" },   {"jpg", "image/jpg" },   {"jpeg","image/jpeg"},   {"png", "image/png" },   {"ico", "image/ico" },   {"zip", "image/zip" },   {"gz",  "image/gz"  },   {"tar", "image/tar" },   {"htm", "text/html" },   {"html","text/html" },   {"php", "text/html" },   {"pdf","application/pdf"},   {"zip","application/octet-stream"},   {"rar","application/octet-stream"},   {0,0} };    /*   A helper function   */  void error(const char *msg) {   perror(msg);   exit(1);  }    /*   A helper function   */  int get_file_size(int fd) {   struct stat stat_struct;   if (fstat(fd, &stat_struct) == -1)    return (1);   return (int) stat_struct.st_size;  }    /*   A helper function   */  void send_new(int fd, char *msg) {   int len = strlen(msg);   if (send(fd, msg, len, 0) == -1) {    printf("Error in send\n");   }  }    /*   This function recieves the buffer   until an "End of line(EOL)" byte is recieved   */  int recv_new(int fd, char *buffer) {   char *p = buffer; // Use of a pointer to the buffer rather than dealing with the buffer directly   int eol_matched = 0; // Use to check whether the recieved byte is matched with the buffer byte or not   while (recv(fd, p, 1, 0) != 0) // Start receiving 1 byte at a time   {    if (*p == EOL[eol_matched]) // if the byte matches with the first eol byte that is '\r'      {     ++eol_matched;     if (eol_matched == EOL_SIZE) // if both the bytes matches with the EOL     {      *(p + 1 - EOL_SIZE) = '\0'; // End the string      return (strlen(buffer)); // Return the bytes recieved     }    } else {     eol_matched = 0;    }    p++; // Increment the pointer to receive next byte   }   return (0);  }    /*   A helper function: Returns the   web root location.   */  char* webroot() {   // open the file "conf" for reading   FILE *in = fopen("conf", "rt");   // read the first line from the file   char buff[1000];   fgets(buff, 1000, in);   // close the stream   fclose(in);   char* nl_ptr = strrchr(buff, '\n');   if (nl_ptr != NULL)    *nl_ptr = '\0';   return strdup(buff);  }    /*   Handles php requests   */  void php_cgi(char* script_path, int fd) {   send_new(fd, "HTTP/1.1 200 OK\n Server: Web Server in C\n Connection: close\n");   dup2(fd, STDOUT_FILENO);   char script[500];   strcpy(script, "SCRIPT_FILENAME=");   strcat(script, script_path);   putenv("GATEWAY_INTERFACE=CGI/1.1");   putenv(script);   putenv("QUERY_STRING=");   putenv("REQUEST_METHOD=GET");   putenv("REDIRECT_STATUS=true");   putenv("SERVER_PROTOCOL=HTTP/1.1");   putenv("REMOTE_HOST=127.0.0.1");   execl("/usr/bin/php-cgi", "php-cgi", NULL);  }    /*   This function parses the HTTP requests,   arrange resource locations,   check for supported media types,   serves files in a web root,   sends the HTTP error codes.   */  int connection(int fd) {   char request[500], resource[500], *ptr;   int fd1, length;   if (recv_new(fd, request) == 0) {    printf("Recieve Failed\n");   }   printf("%s\n", request);   // Check for a valid browser request   ptr = strstr(request, " HTTP/");   if (ptr == NULL) {    printf("NOT HTTP !\n");   } else {    *ptr = 0;    ptr = NULL;      if (strncmp(request, "GET ", 4) == 0) {     ptr = request + 4;    }    if (ptr == NULL) {     printf("Unknown Request ! \n");    } else {     if (ptr[strlen(ptr) - 1] == '/') {      strcat(ptr, "index.html");     }     strcpy(resource, webroot());     strcat(resource, ptr);     char* s = strchr(ptr, '.');     int i;     for (i = 0; extensions[i].ext != NULL; i++) {      if (strcmp(s + 1, extensions[i].ext) == 0) {       fd1 = open(resource, O_RDONLY, 0);       printf("Opening \"%s\"\n", resource);       if (fd1 == -1) {        printf("404 File not found Error\n");        send_new(fd, "HTTP/1.1 404 Not Found\r\n");        send_new(fd, "Server : Web Server in C\r\n\r\n");        send_new(fd, "<html><head><title>404 Not Found</head></title>");        send_new(fd, "<body><p>404 Not Found: The requested resource could not be found!</p></body></html>");        //Handling php requests       } else if (strcmp(extensions[i].ext, "php") == 0) {        php_cgi(resource, fd);        sleep(1);        close(fd);        exit(1);       } else {        printf("200 OK, Content-Type: %s\n\n",          extensions[i].mediatype);        send_new(fd, "HTTP/1.1 200 OK\r\n");        send_new(fd, "Server : Web Server in C\r\n\r\n");        if (ptr == request + 4) // if it is a GET request          {         if ((length = get_file_size(fd1)) == -1)          printf("Error in getting size !\n");         size_t total_bytes_sent = 0;         ssize_t bytes_sent;         while (total_bytes_sent < length) {          //Zero copy optimization          if ((bytes_sent = sendfile(fd, fd1, 0,            length - total_bytes_sent)) <= 0) {           if (errno == EINTR || errno == EAGAIN) {            continue;           }           perror("sendfile");           return -1;          }          total_bytes_sent += bytes_sent;         }          }       }       break;      }      int size = sizeof(extensions) / sizeof(extensions[0]);      if (i == size - 2) {       printf("415 Unsupported Media Type\n");       send_new(fd, "HTTP/1.1 415 Unsupported Media Type\r\n");       send_new(fd, "Server : Web Server in C\r\n\r\n");       send_new(fd, "<html><head><title>415 Unsupported Media Type</head></title>");       send_new(fd, "<body><p>415 Unsupported Media Type!</p></body></html>");      }     }       close(fd);    }   }   shutdown(fd, SHUT_RDWR);  }    int main(int argc, char *argv[]) {   int sockfd, newsockfd, portno, pid;   socklen_t clilen;   struct sockaddr_in serv_addr, cli_addr;     if (argc < 2) {    fprintf(stderr, "ERROR, no port provided\n");    exit(1);   }   sockfd = socket(AF_INET, SOCK_STREAM, 0);   if (sockfd < 0)    error("ERROR opening socket");   bzero((char *) &serv_addr, sizeof(serv_addr));   portno = atoi(argv[1]);   serv_addr.sin_family = AF_INET;   serv_addr.sin_addr.s_addr = INADDR_ANY;   serv_addr.sin_port = htons(portno);   if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)    error("ERROR on binding");   listen(sockfd, 5);   clilen = sizeof(cli_addr);   /*    Server runs forever, forking off a separate    process for each connection.    */   while (1) {    newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);    if (newsockfd < 0)     error("ERROR on accept");    pid = fork();    if (pid < 0)     error("ERROR on fork");    if (pid == 0) {     close(sockfd);     connection(newsockfd);     exit(0);    } else     close(newsockfd);   } /* end of while */   close(sockfd);   return 0; /* we never get here */  }