Advertisement
CSenshi

httpserver (not finished 2)

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