Advertisement
CSenshi

httpserver (not finished)

Apr 12th, 2019
282
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.76 KB | None | 0 0
  1. #include <arpa/inet.h>
  2. #include <dirent.h>
  3. #include <errno.h>
  4. #include <fcntl.h>
  5. #include <netdb.h>
  6. #include <netinet/in.h>
  7. #include <pthread.h>
  8. #include <signal.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <sys/socket.h>
  13. #include <sys/stat.h>
  14. #include <sys/types.h>
  15. #include <unistd.h>
  16. #include <unistd.h>
  17.  
  18. #include "libhttp.h"
  19. #include "wq.h"
  20.  
  21. /*
  22.  * Global configuration variables.
  23.  * You need to use these in your implementation of handle_files_request and
  24.  * handle_proxy_request. Their values are set up in main() using the
  25.  * command line arguments (already implemented for you).
  26.  */
  27. wq_t work_queue;
  28. int num_threads;
  29. int server_port;
  30. char *server_files_directory;
  31. char *server_proxy_hostname;
  32. int server_proxy_port;
  33.  
  34. char *str_error_not_found = "<center>"
  35.                             "<h1>404 Not Found</h1>"
  36.                             "<hr>"
  37.                             "<p>Requested Domani Not Found!!!</p>"
  38.                             "</center>";
  39.  
  40. char * H_html_start = "<html>";
  41. char * H_html_end = "</html>";
  42. char * H_ul_start = "<ul>";
  43. char * H_ul_end = "<ul>";
  44. char * H_li_start = "<li>";
  45. char * H_li_end = "</li>";
  46. char * H_href = "<a href=\"%s\">%s</a>";
  47.  
  48.  
  49. char *read_from_file(char *path, int *data_size)
  50. {
  51.   char *data = malloc(1);
  52.   int file_fd = open(path, O_RDONLY, 0644);
  53.   if (file_fd == -1)
  54.   {
  55.     perror("");
  56.   }
  57.   else
  58.   {
  59.     int BUF_SIZE = 1024;
  60.     char buff[BUF_SIZE];
  61.     int bytes_read = 0;
  62.     while (bytes_read = read(file_fd, buff, BUF_SIZE), bytes_read > 0)
  63.     {
  64.       data = (char *)realloc(data, *data_size + bytes_read);
  65.       memcpy(data + *data_size, buff, bytes_read);
  66.       *data_size += bytes_read;
  67.     }
  68.   }
  69.   return data;
  70. }
  71.  
  72. char *read_from_dir(char *path, int *data_size){
  73.   char * html = malloc(strlen(H_html_start) + strlen(H_html_end) + strlen(H_ul_start) + strlen(H_ul_end));
  74.   strcpy(html, H_html_start);
  75.  
  76.   DIR *d = opendir(path);
  77.   if (d)
  78.   {
  79.     struct dirent *dir;
  80.     strcat(html, H_ul_start);
  81.     while ((dir = readdir(d)) != NULL)
  82.     {
  83.       char * cur_file = malloc(strlen(H_href) + 2*strlen(dir->d_name) + strlen(H_li_start) + strlen(H_li_end));
  84.       sprintf(cur_file, H_href, dir->d_name, dir->d_name);
  85.       strcat(html, H_li_start);
  86.       strcat(html, cur_file);
  87.       strcat(html, H_li_end);
  88.     }
  89.     closedir(d);
  90.   }
  91.   strcat(html, H_ul_end);
  92.   strcat(html, H_html_end);
  93.   *data_size = strlen(html);
  94.   return html;
  95. }
  96.  
  97. /*
  98.  * Reads an HTTP request from stream (fd), and writes an HTTP response
  99.  * containing:
  100.  *
  101.  *   1) If user requested an existing file, respond with the file
  102.  *   2) If user requested a directory and index.html exists in the directory,
  103.  *      send the index.html file.
  104.  *   3) If user requested a directory and index.html doesn't exist, send a list
  105.  *      of files in the directory with links to each.
  106.  *   4) Send a 404 Not Found response.
  107.  */
  108. void handle_files_request(int fd)
  109. {
  110.   struct http_request *request = http_request_parse(fd);
  111.  
  112.   // get full path of file
  113.   char *full_path = malloc(strlen(server_files_directory) + strlen(request->path));
  114.   strcpy(full_path, server_files_directory);
  115.   strcat(full_path, request->path);
  116.  
  117.   // file check struct
  118.   struct stat stat_file;
  119.   int stat_res = stat(full_path, &stat_file);
  120.  
  121.   // data to send in http
  122.   int status_code;
  123.   char *content_type;
  124.   char *data;
  125.   int data_size = 0;
  126.  
  127.   if (stat_res == -1) // error 404
  128.   {
  129.     content_type = "text/html";
  130.     status_code = 404;
  131.     data = str_error_not_found;
  132.     data_size = strlen(data);
  133.   }
  134.   else
  135.   {
  136.     status_code = 200;
  137.     char *index = "index.html";
  138.     if (S_ISDIR(stat_file.st_mode))
  139.     {
  140.       // check if index.html exists in given folder
  141.       char *full_path_index = malloc(strlen(full_path) + strlen(index) + 1);
  142.       strcpy(full_path_index, full_path);
  143.       strcat(full_path_index, "/");
  144.       strcat(full_path_index, index);
  145.      
  146.       stat_res = stat(full_path_index, &stat_file);
  147.       if (stat_res == -1) // does not exists
  148.       {
  149.         content_type = "text/html";
  150.         data = read_from_dir(full_path, &data_size);
  151.       }
  152.       else // index.html exists
  153.       {
  154.         full_path = full_path_index;
  155.       }
  156.     }
  157.  
  158.     if (S_ISREG(stat_file.st_mode))
  159.     {
  160.       content_type = http_get_mime_type(full_path);
  161.       data = read_from_file(full_path, &data_size);
  162.     }
  163.   }
  164.   char data_size_str[100] = {'\0'};
  165.   sprintf(data_size_str, "%d", data_size);
  166.  
  167.   http_start_response(fd, status_code);
  168.   http_send_header(fd, "Content-Type", content_type);
  169.   http_send_header(fd, "Content-Length", data_size_str);
  170.   http_end_headers(fd);
  171.   http_send_data(fd, data, data_size);
  172. }
  173.  
  174. /*
  175.  * Opens a connection to the proxy target (hostname=server_proxy_hostname and
  176.  * port=server_proxy_port) and relays traffic to/from the stream fd and the
  177.  * proxy target. HTTP requests from the client (fd) should be sent to the
  178.  * proxy target, and HTTP responses from the proxy target should be sent to
  179.  * the client (fd).
  180.  *
  181.  *   +--------+     +------------+     +--------------+
  182.  *   | client | <-> | httpserver | <-> | proxy target |
  183.  *   +--------+     +------------+     +--------------+
  184.  */
  185. void handle_proxy_request(int fd)
  186. {
  187.  
  188.   /*
  189.   * The code below does a DNS lookup of server_proxy_hostname and
  190.   * opens a connection to it. Please do not modify.
  191.   */
  192.  
  193.   struct sockaddr_in target_address;
  194.   memset(&target_address, 0, sizeof(target_address));
  195.   target_address.sin_family = AF_INET;
  196.   target_address.sin_port = htons(server_proxy_port);
  197.  
  198.   struct hostent *target_dns_entry = gethostbyname2(server_proxy_hostname, AF_INET);
  199.  
  200.   int client_socket_fd = socket(PF_INET, SOCK_STREAM, 0);
  201.   if (client_socket_fd == -1)
  202.   {
  203.     fprintf(stderr, "Failed to create a new socket: error %d: %s\n", errno, strerror(errno));
  204.     exit(errno);
  205.   }
  206.  
  207.   if (target_dns_entry == NULL)
  208.   {
  209.     fprintf(stderr, "Cannot find host: %s\n", server_proxy_hostname);
  210.     exit(ENXIO);
  211.   }
  212.  
  213.   char *dns_address = target_dns_entry->h_addr_list[0];
  214.  
  215.   memcpy(&target_address.sin_addr, dns_address, sizeof(target_address.sin_addr));
  216.   int connection_status = connect(client_socket_fd, (struct sockaddr *)&target_address,
  217.                                   sizeof(target_address));
  218.  
  219.   if (connection_status < 0)
  220.   {
  221.     /* Dummy request parsing, just to be compliant. */
  222.     http_request_parse(fd);
  223.  
  224.     http_start_response(fd, 502);
  225.     http_send_header(fd, "Content-Type", "text/html");
  226.     http_end_headers(fd);
  227.     http_send_string(fd, "<center><h1>502 Bad Gateway</h1><hr></center>");
  228.     return;
  229.   }
  230.  
  231.   /*
  232.   * TODO: Your solution for task 3 belongs here!
  233.   */
  234. }
  235.  
  236. void init_thread_pool(int num_threads, void (*request_handler)(int))
  237. {
  238.   /*
  239.    * TODO: Part of your solution for Task 2 goes here!
  240.    */
  241. }
  242.  
  243. /*
  244.  * Opens a TCP stream socket on all interfaces with port number PORTNO. Saves
  245.  * the fd number of the server socket in *socket_number. For each accepted
  246.  * connection, calls request_handler with the accepted fd number.
  247.  */
  248. void serve_forever(int *socket_number, void (*request_handler)(int))
  249. {
  250.  
  251.   struct sockaddr_in server_address, client_address;
  252.   size_t client_address_length = sizeof(client_address);
  253.   int client_socket_number;
  254.  
  255.   *socket_number = socket(PF_INET, SOCK_STREAM, 0);
  256.   if (*socket_number == -1)
  257.   {
  258.     perror("Failed to create a new socket");
  259.     exit(errno);
  260.   }
  261.  
  262.   int socket_option = 1;
  263.   if (setsockopt(*socket_number, SOL_SOCKET, SO_REUSEADDR, &socket_option,
  264.                  sizeof(socket_option)) == -1)
  265.   {
  266.     perror("Failed to set socket options");
  267.     exit(errno);
  268.   }
  269.  
  270.   memset(&server_address, 0, sizeof(server_address));
  271.   server_address.sin_family = AF_INET;
  272.   server_address.sin_addr.s_addr = INADDR_ANY;
  273.   server_address.sin_port = htons(server_port);
  274.  
  275.   if (bind(*socket_number, (struct sockaddr *)&server_address,
  276.            sizeof(server_address)) == -1)
  277.   {
  278.     perror("Failed to bind on socket");
  279.     exit(errno);
  280.   }
  281.  
  282.   if (listen(*socket_number, 1024) == -1)
  283.   {
  284.     perror("Failed to listen on socket");
  285.     exit(errno);
  286.   }
  287.  
  288.   printf("Listening on port %d...\n", server_port);
  289.  
  290.   init_thread_pool(num_threads, request_handler);
  291.  
  292.   while (1)
  293.   {
  294.     client_socket_number = accept(*socket_number,
  295.                                   (struct sockaddr *)&client_address,
  296.                                   (socklen_t *)&client_address_length);
  297.     if (client_socket_number < 0)
  298.     {
  299.       perror("Error accepting socket");
  300.       continue;
  301.     }
  302.  
  303.     printf("Accepted connection from %s on port %d\n",
  304.            inet_ntoa(client_address.sin_addr),
  305.            client_address.sin_port);
  306.  
  307.     // TODO: Change me?
  308.     request_handler(client_socket_number);
  309.     close(client_socket_number);
  310.  
  311.     printf("Accepted connection from %s on port %d\n",
  312.            inet_ntoa(client_address.sin_addr),
  313.            client_address.sin_port);
  314.   }
  315.  
  316.   shutdown(*socket_number, SHUT_RDWR);
  317.   close(*socket_number);
  318. }
  319.  
  320. int server_fd;
  321. void signal_callback_handler(int signum)
  322. {
  323.   printf("Caught signal %d: %s\n", signum, strsignal(signum));
  324.   printf("Closing socket %d\n", server_fd);
  325.   if (close(server_fd) < 0)
  326.     perror("Failed to close server_fd (ignoring)\n");
  327.   exit(0);
  328. }
  329.  
  330. char *USAGE =
  331.     "Usage: ./httpserver --files www_directory/ --port 8000 [--num-threads 5]\n"
  332.     "       ./httpserver --proxy inst.eecs.berkeley.edu:80 --port 8000 [--num-threads 5]\n";
  333.  
  334. void exit_with_usage()
  335. {
  336.   fprintf(stderr, "%s", USAGE);
  337.   exit(EXIT_SUCCESS);
  338. }
  339.  
  340. int main(int argc, char **argv)
  341. {
  342.   signal(SIGINT, signal_callback_handler);
  343.  
  344.   /* Default settings */
  345.   server_port = 8000;
  346.   void (*request_handler)(int) = NULL;
  347.  
  348.   int i;
  349.   for (i = 1; i < argc; i++)
  350.   {
  351.     if (strcmp("--files", argv[i]) == 0)
  352.     {
  353.       request_handler = handle_files_request;
  354.       free(server_files_directory);
  355.       server_files_directory = argv[++i];
  356.       if (!server_files_directory)
  357.       {
  358.         fprintf(stderr, "Expected argument after --files\n");
  359.         exit_with_usage();
  360.       }
  361.     }
  362.     else if (strcmp("--proxy", argv[i]) == 0)
  363.     {
  364.       request_handler = handle_proxy_request;
  365.  
  366.       char *proxy_target = argv[++i];
  367.       if (!proxy_target)
  368.       {
  369.         fprintf(stderr, "Expected argument after --proxy\n");
  370.         exit_with_usage();
  371.       }
  372.  
  373.       char *colon_pointer = strchr(proxy_target, ':');
  374.       if (colon_pointer != NULL)
  375.       {
  376.         *colon_pointer = '\0';
  377.         server_proxy_hostname = proxy_target;
  378.         server_proxy_port = atoi(colon_pointer + 1);
  379.       }
  380.       else
  381.       {
  382.         server_proxy_hostname = proxy_target;
  383.         server_proxy_port = 80;
  384.       }
  385.     }
  386.     else if (strcmp("--port", argv[i]) == 0)
  387.     {
  388.       char *server_port_string = argv[++i];
  389.       if (!server_port_string)
  390.       {
  391.         fprintf(stderr, "Expected argument after --port\n");
  392.         exit_with_usage();
  393.       }
  394.       server_port = atoi(server_port_string);
  395.     }
  396.     else if (strcmp("--num-threads", argv[i]) == 0)
  397.     {
  398.       char *num_threads_str = argv[++i];
  399.       if (!num_threads_str || (num_threads = atoi(num_threads_str)) < 1)
  400.       {
  401.         fprintf(stderr, "Expected positive integer after --num-threads\n");
  402.         exit_with_usage();
  403.       }
  404.     }
  405.     else if (strcmp("--help", argv[i]) == 0)
  406.     {
  407.       exit_with_usage();
  408.     }
  409.     else
  410.     {
  411.       fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
  412.       exit_with_usage();
  413.     }
  414.   }
  415.  
  416.   if (server_files_directory == NULL && server_proxy_hostname == NULL)
  417.   {
  418.     fprintf(stderr, "Please specify either \"--files [DIRECTORY]\" or \n"
  419.                     "                      \"--proxy [HOSTNAME:PORT]\"\n");
  420.     exit_with_usage();
  421.   }
  422.  
  423.   serve_forever(&server_fd, request_handler);
  424.  
  425.   return EXIT_SUCCESS;
  426. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement