Advertisement
CSenshi

OS - HW2 (httpserver.c)

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