From 457a52a5858ec20ac4c47f273ee7256292d76997 Mon Sep 17 00:00:00 2001 From: Dm1tr1y147 Date: Tue, 23 Jun 2020 17:02:05 +0500 Subject: [PATCH] finished static files hosting. Added mime type definition and file sending functions. Finished send_response and changed header compose function. Some small fixes --- include/file.h | 10 +++- include/mime.h | 1 + include/mine.h | 1 - include/request.h | 9 +++- include/utils.h | 4 +- src/file.c | 61 +++++++++++++++++++++-- src/mime.c | 65 +++++++++++++++++++++++++ src/mine.c | 5 -- src/request.c | 120 +++++++++++++++++++++++++--------------------- src/server.c | 10 ++-- src/utils.c | 52 +++++++++++++++++++- 11 files changed, 265 insertions(+), 73 deletions(-) create mode 100644 include/mime.h delete mode 100644 include/mine.h create mode 100644 src/mime.c delete mode 100644 src/mine.c diff --git a/include/file.h b/include/file.h index 3beebd5..178358a 100644 --- a/include/file.h +++ b/include/file.h @@ -1,2 +1,10 @@ +struct file_s +{ + char *path; + off_t size; + int fd; +}; + char *gen_file_path(char *path); -int send_file(int fd, char *path); \ No newline at end of file +int send_file(int cli_fd, struct file_s *file); +struct file_s *get_file_info(char *file_path); \ No newline at end of file diff --git a/include/mime.h b/include/mime.h new file mode 100644 index 0000000..aec5cf4 --- /dev/null +++ b/include/mime.h @@ -0,0 +1 @@ +char *get_mime_type(char *file_path); \ No newline at end of file diff --git a/include/mine.h b/include/mine.h deleted file mode 100644 index c28a0e7..0000000 --- a/include/mine.h +++ /dev/null @@ -1 +0,0 @@ -char *get_mine_type(char *file_path); \ No newline at end of file diff --git a/include/request.h b/include/request.h index 318d1f2..9c7edbb 100644 --- a/include/request.h +++ b/include/request.h @@ -1,5 +1,10 @@ +struct header_s { + char * str; + size_t size; +}; + char *get_path(char *request); -void gen_header(int status_code, size_t file_size, char *mine_type); -int send_response(int fd, char *path); +struct header_s *gen_header(int status_code, size_t file_size, char *mime_type); +int send_response(int fd, char *req_path); void handle_post_request(int fd, char *request); void handle_get_request(int fd, char *request); \ No newline at end of file diff --git a/include/utils.h b/include/utils.h index f5e415d..04566f5 100644 --- a/include/utils.h +++ b/include/utils.h @@ -1,2 +1,4 @@ void err_msg(char *msg); -char *add_to_front(char **str1, char *str2); \ No newline at end of file +char *add_to_front(char **str1, char *str2); +char *get_status_message(int status_code); +char *to_lower(char *str); \ No newline at end of file diff --git a/src/file.c b/src/file.c index 77e27d3..032eab9 100644 --- a/src/file.c +++ b/src/file.c @@ -1,9 +1,19 @@ #include #include +#include +#include +#include +#include #include "../include/file.h" #include "../include/utils.h" +/** + * @brief Generate file path from request path provided + * + * @param req_path + * @return char* + */ char *gen_file_path(char *req_path) { char *path = (char *)malloc(sizeof req_path); @@ -19,7 +29,7 @@ char *gen_file_path(char *req_path) path = strcat(path, "index.html"); } - char *webroot = "../static"; + char *webroot = "static"; path = realloc(path, sizeof path + sizeof webroot); path = add_to_front(&path, webroot); @@ -27,9 +37,52 @@ char *gen_file_path(char *req_path) return path; } -int send_file(int fd, char *req_path) +/** + * @brief Send file to client + * + * @param fd + * @param file_path + * @return int + */ +int send_file(int cli_fd, struct file_s *file) { - char *path = gen_file_path(req_path); + off_t offset = 0; + if (sendfile(cli_fd, file->fd, &offset, file->size+2) < 0) + { + return -1; + } - + close(file->fd); + free(file); + return 0; +} + +/** + * @brief Get the file info + * + * @param file_path + * @return struct file_s* + */ +struct file_s *get_file_info(char *file_path) +{ + struct file_s *file = (struct file_s *)malloc(sizeof(struct file_s)); + + if (file == NULL) + { + err_msg("couldn't create file structure"); + return file; + } + + struct stat stat_buf; + + file->fd = open(file_path, O_RDONLY); + if (file->fd < 0) + { + return file; + } + + fstat(file->fd, &stat_buf); + file->size = stat_buf.st_size; + + return file; } \ No newline at end of file diff --git a/src/mime.c b/src/mime.c new file mode 100644 index 0000000..e8c5923 --- /dev/null +++ b/src/mime.c @@ -0,0 +1,65 @@ +#include + +#include "../include/mime.h" +#include "../include/utils.h" + +#define DEFAULT_mime_TYPE "application/octet-stream" + +/** + * @brief Get the mime type of file + * + * @param file_path + * @return char* + */ +char *get_mime_type(char *file_path) +{ + char *ext = strchr(file_path, '.'); + + if (ext == NULL) + { + return DEFAULT_mime_TYPE; + } + + ext++; + + ext = to_lower(ext); + + if (strcmp(ext, "html") == 0 || strcmp(ext, "htm") == 0) + { + return "text/html"; + } + if (strcmp(ext, "jpeg") == 0 || strcmp(ext, "jpg") == 0) + { + return "image/jpg"; + } + if (strcmp(ext, "css") == 0) + { + return "text/css"; + } + if (strcmp(ext, "js") == 0) + { + return "application/javascript"; + } + if (strcmp(ext, "json") == 0) + { + return "application/json"; + } + if (strcmp(ext, "txt") == 0) + { + return "text/plain"; + } + if (strcmp(ext, "gif") == 0) + { + return "image/gif"; + } + if (strcmp(ext, "png") == 0) + { + return "image/png"; + } + if (strcmp(ext, "ico") == 0) + { + return "image/x-icon"; + } + + return DEFAULT_mime_TYPE; +} \ No newline at end of file diff --git a/src/mine.c b/src/mine.c deleted file mode 100644 index 49983de..0000000 --- a/src/mine.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "../include/mine.h" - -char *get_mine_type(char *file_path) { - -} \ No newline at end of file diff --git a/src/request.c b/src/request.c index aa012ce..fda892b 100644 --- a/src/request.c +++ b/src/request.c @@ -3,46 +3,15 @@ #include #include #include +#include -#include -#include #include #include #include "../include/request.h" #include "../include/utils.h" - -/** - * @brief Get the status message object - * - * @param status_code - * @return char* - */ -char *get_status_message(int status_code) -{ - switch (status_code) - { - case 200: - return "OK"; - case 400: - return "Internal Server Error"; - case 401: - return "Unauthorized"; - case 403: - return "Forbidden"; - case 404: - return "NOT FOUND"; - case 500: - return "Internal Server Error"; - case 501: - return "Not Implemented"; - case 502: - return "Bad Gateway"; - default: - return "None"; - break; - } -} +#include "../include/file.h" +#include "../include/mime.h" /** * @brief Get the path object @@ -63,10 +32,10 @@ char *get_path(char *request) * * @param status_code * @param file_size - * @param mine_type + * @param mime_type * @return char* */ -char *gen_header(int status_code, size_t file_size, char *mine_type) +struct header_s *gen_header(int status_code, size_t file_size, char *mime_type) { char *status_message = get_status_message(status_code); @@ -74,34 +43,78 @@ char *gen_header(int status_code, size_t file_size, char *mine_type) struct tm *ptm = gmtime(&now); char *curr_date = asctime(ptm); - int header_length = snprintf(NULL, 0, "HTTP/1.1 %d %s\nDate: %s\nConnection: close\nContent-Length: %d\nContent-Type: %s\n\n", status_code, status_message, curr_date, file_size, mine_type); - char *header = (char *)malloc(header_length + 1); - snprintf(header, header_length + 1, "HTTP/1.1 %d %s\nDate: %s\nConnection: close\nContent-Length: %d\nContent-Type: %s\n\n", status_code, status_message, curr_date, file_size, mine_type); - printf("%s\n", header); + int header_length = snprintf(NULL, 0, "HTTP/1.1 %d %s\nDate: %sConnection: close\nContent-Length: %ld\nContent-Type: %s\r\n\r\n", status_code, status_message, curr_date, file_size, mime_type) + 1; + char *header_string = (char *)malloc(header_length); + snprintf(header_string, header_length, "HTTP/1.1 %d %s\nDate: %sConnection: close\nContent-Length: %ld\nContent-Type: %s\r\n\r\n", status_code, status_message, curr_date, file_size, mime_type); + + struct header_s *header = (struct header_s *)malloc(header_length + sizeof(size_t)); + + header->size = header_length; + header->str = header_string; + + return header; } /** * @brief Send HTTP response * * @param fd - * @param file_path + * @param req_path * @return int */ -int send_response(int fd, char *path) +int send_response(int fd, char *req_path) { - int file_fd; - struct stat stat_buf; - file_fd = open(file_path, O_RDONLY); - fstat(file_fd, &stat_buf); - off_t f_size = stat_buf.st_size; - off_t offset = 0; + char *file_path = gen_file_path(req_path); - char *mine_type; + struct file_s *file = get_file_info(file_path); + if (file == NULL) + { + char *msg = "Server error"; - char *header = gen_header(200, f_size, mine_type); + struct header_s *header = gen_header(500, sizeof(msg), "text/plain"); + send(fd, header->str, header->size - 1, 0); - sendfile(fd, file_fd, &offset, f_size); - close(file_fd); + send(fd, msg, sizeof(msg), 0); + close(fd); + + return 0; + } + if (file->fd < 0) + { + char *msg = "\n404, Not Found. Return to home? Home"; + + struct header_s *header = gen_header(404, strlen(msg), "text/html"); + send(fd, header->str, header->size - 1, 0); + + send(fd, msg, strlen(msg), 0); + close(fd); + + return 0; + } + + char *mime_type = get_mime_type(file_path); + + struct header_s *header = gen_header(200, file->size, mime_type); + + printf("\n---\n%s\n%ld\n---\n", header->str, header->size); + + int rv = send(fd, header->str, header->size - 1, 0); + + if (rv < 0) + { + err_msg("couldn't send header"); + return -1; + } + + if (send_file(fd, file) < 0) + { + err_msg("couldn't send file"); + return -1; + } + + close(fd); + + return 0; } /** @@ -128,8 +141,7 @@ void handle_get_request(int fd, char *request) char *path = get_path(request); printf("Client accessed path: %s\n", path); - int rv = send_response(fd, path); - if (rv < 0) + if (send_response(fd, path) < 0) { err_msg("couldn't send response"); } diff --git a/src/server.c b/src/server.c index 7add287..c144f42 100644 --- a/src/server.c +++ b/src/server.c @@ -12,6 +12,11 @@ #include "../include/utils.h" #include "../include/request.h" +/** + * @brief Handle client connection + * + * @param fd + */ void handle_connection(int fd) { const size_t request_buffer_size = 512; @@ -89,9 +94,6 @@ int main(int argc, char *argv[]) inet_ntop(cli_addr.ss_family, get_in_addr((struct sockaddr *)&cli_addr), s, sizeof s); printf("\nGot connection from %s\n", s); - // | Doesn't work properly yet - // v - int pid = fork(); if (pid < 0) @@ -100,7 +102,7 @@ int main(int argc, char *argv[]) { close(listenfd); - handle_connection(client_fd); // Implement + handle_connection(client_fd); exit(0); } else diff --git a/src/utils.c b/src/utils.c index c8e183d..c73921a 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,7 @@ #include +#include +#include +#include #include "../include/utils.h" @@ -12,14 +15,61 @@ void err_msg(char *msg) fprintf(stderr, "Error: %s\n", msg); } +/** + * @brief Add str2 in front of str1 + * + * @param str1 + * @param str2 + * @return char* + */ char *add_to_front(char **str1, char *str2) { char *tmp = strdup(*str1); - + strcpy(*str1, str2); strcat(*str1, tmp); free(tmp); return *str1; +} + +/** + * @brief Get the status message object + * + * @param status_code + * @return char* + */ +char *get_status_message(int status_code) +{ + switch (status_code) + { + case 200: + return "OK"; + case 400: + return "Internal Server Error"; + case 401: + return "Unauthorized"; + case 403: + return "Forbidden"; + case 404: + return "NOT FOUND"; + case 500: + return "Internal Server Error"; + case 501: + return "Not Implemented"; + case 502: + return "Bad Gateway"; + default: + return "None"; + break; + } +} + +char *to_lower(char *str) +{ + for (char *p = str; *p != '\0'; p++) + *p = tolower(*p); + + return str; } \ No newline at end of file