diff --git a/http/http_request.c b/http/http_request.c index b548286..dac7a76 100644 --- a/http/http_request.c +++ b/http/http_request.c @@ -40,7 +40,7 @@ void http_request_set(HttpRequest *__req, char *key, char *value) { else if (!strcmp(key, "Upgrade-Insecure-Requests")) __req->upgrade_insecure_requests = value; else if (!strcmp(key, "Accept")) - ; + free(value); // __req->accept = value; else if (!strcmp(key, "Sec-Fetch-Site")) __req->sec_fetch_site = value; @@ -52,9 +52,12 @@ void http_request_set(HttpRequest *__req, char *key, char *value) { __req->accept_encoding = value; else if (!strcmp(key, "Connection")) __req->connection = value; + else + free(value); } HttpRequest *handle_request(char *__req) { + printf("%s\n", __req); HttpRequest *request = malloc(sizeof(HttpRequest)); unsigned int line_count = 0; char *line_start = __req; @@ -62,9 +65,7 @@ HttpRequest *handle_request(char *__req) { while ((line_end = strchr(line_start, '\n')) != NULL) { line_count++; size_t l_length = line_end - line_start; - char *line = malloc(sizeof(char) * l_length); - if (line == NULL) - return NULL; + char line[l_length]; strncpy(line, line_start, l_length); line[l_length - 1] = 0; @@ -79,20 +80,49 @@ HttpRequest *handle_request(char *__req) { request->path = path; } else { char key[100] = {0}; - char *value = malloc(sizeof(char) * 256); char *colon_pos = strchr(line, ':'); if (colon_pos != NULL) { size_t k_length = colon_pos - line; strncpy(key, line, k_length); + size_t v_length = l_length - k_length - 3; + char *value = malloc(sizeof(char) * v_length); strcpy(value, colon_pos + 2); http_request_set(request, key, value); } } // TODO: Analyze line - free(line); line_start = line_end + 1; } return request; } + +void free_request(HttpRequest *__req) { + if (__req == NULL) + return; + if (__req->path != NULL) + free(__req->path); + if (__req->host != NULL) + free(__req->host); + if (__req->sec_fetch_dest != NULL) + free(__req->sec_fetch_dest); + if (__req->user_agent != NULL) + free(__req->user_agent); + if (__req->upgrade_insecure_requests != NULL) + free(__req->upgrade_insecure_requests); + if (__req->accept != NULL) + free(__req->accept); + if (__req->sec_fetch_site != NULL) + free(__req->sec_fetch_site); + if (__req->sec_fetch_mode != NULL) + free(__req->sec_fetch_mode); + if (__req->accept_language != NULL) + free(__req->accept_language); + if (__req->accept_encoding != NULL) + free(__req->accept_encoding); + if (__req->connection != NULL) + free(__req->connection); + + free(__req); +} diff --git a/http/http_request.h b/http/http_request.h index c5cc10e..71fded7 100644 --- a/http/http_request.h +++ b/http/http_request.h @@ -20,5 +20,6 @@ typedef struct { HttpRequest *handle_request(char *__req); void print_request(const HttpRequest *__req); +void free_request(HttpRequest *__req); #endif diff --git a/http/http_response.c b/http/http_response.c index 61fae97..0bb799b 100644 --- a/http/http_response.c +++ b/http/http_response.c @@ -1,5 +1,5 @@ -#include "http_content_type.h" #include "http_response.h" +#include "http_content_type.h" #include "http_status.h" #include #include @@ -31,15 +31,18 @@ bool construct_response(HttpResponse __res, char *out) { return true; } -void http_respond(HttpResponse __res, int clientfd) { +void http_respond(HttpResponse *__res, int clientfd) { + if (__res == NULL) + return; char response[BUFSIZ]; // TODO: Handle return - construct_response(__res, response); + construct_response(*__res, response); send(clientfd, response, strlen(response), 0); } char *read_file(const char *__path) { + // If the file does not exist if (access(__path, F_OK) != 0) { return NULL; } @@ -48,10 +51,12 @@ char *read_file(const char *__path) { if (f == NULL) { return NULL; } + // Get the length of the file fseek(f, 0, SEEK_END); size_t length = ftell(f); rewind(f); + // Initialize a value to get the content char *content = malloc(length * sizeof(char)); if (content == NULL) { @@ -59,6 +64,7 @@ char *read_file(const char *__path) { return NULL; } + // Read the file into content and set the last char to \0 size_t bytesRead = fread(content, sizeof(char), length, f); content[bytesRead] = 0; @@ -72,10 +78,13 @@ HttpResponse *from_file(const char *__path) { if (content == NULL) return NULL; - HttpResponse response = {.status_code = HTTP_OK, - .content_length = strlen(content), - .content_type = HTTP_CT_HTML, - .body = content}; + HttpResponse response = { + .status_code = HTTP_OK, + .content_length = strlen(content), + .content_type = HTTP_CT_HTML, + .body = content, + .body_in_heap = true, + }; HttpResponse *res = malloc(sizeof(response)); *res = response; @@ -84,7 +93,7 @@ HttpResponse *from_file(const char *__path) { } void free_response(HttpResponse *__res) { - if (__res->body != NULL) + if (__res->body != NULL && __res->body_in_heap) free(__res->body); free(__res); diff --git a/http/http_response.h b/http/http_response.h index a0d7cef..746cbc5 100644 --- a/http/http_response.h +++ b/http/http_response.h @@ -11,12 +11,19 @@ typedef struct { HttpContentType content_type; size_t content_length; char *body; + bool body_in_heap; } HttpResponse; +// It will create a string respecting the Hypertext Transfer Protocol. +// +// To then be sent to the client. bool construct_response(HttpResponse __res, char *out); -void http_respond(HttpResponse __res, int clientfd); +// Respond a http response to the client (clientfd); +void http_respond(HttpResponse *__res, int clientfd); +// Read a file from a specified path char *read_file(const char *__path); +// Given a path will return a HttpResponse HttpResponse *from_file(const char *__path); void free_response(HttpResponse *__res); diff --git a/http/http_server.c b/http/http_server.c new file mode 100644 index 0000000..3f26bc3 --- /dev/null +++ b/http/http_server.c @@ -0,0 +1,98 @@ +#include "http_server.h" +#include "http_content_type.h" +#include "http_request.h" +#include "http_response.h" +#include "http_status.h" +#include +#include +#include +#include +#include +#include +#include + +HttpServerRunStatus http_server_setup(HttpServer *s) { + int opt = 1; + + if ((s->server_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return HTTP_SRS_SOCKET_FAILED; + + if (setsockopt(s->server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) + return HTTP_SRS_SETSOCKOPT_FAILED; + + if (s->address == NULL) { + printf("Setting server address\n"); + s->address = malloc(sizeof(*s->address)); + s->address->sin_family = AF_INET; + s->address->sin_addr.s_addr = INADDR_ANY; + s->address->sin_port = htons(s->port); + } + + if (bind(s->server_fd, (struct sockaddr *)s->address, sizeof(*s->address)) < + 0) + return HTTP_SRS_BIND_FAILED; + + if (listen(s->server_fd, s->workers) < 0) + return HTTP_SRS_LISTEN_FAILED; + + return HTTP_SRS_SETUP; +} + +HttpServerRunStatus http_server_run(HttpServer *s) { + int client_fd; + ssize_t valread; + + socklen_t addrlen = sizeof(*s->address); + + if ((client_fd = + accept(s->server_fd, (struct sockaddr *)s->address, &addrlen)) < 0) + return HTTP_SRS_ACCEPT_FAILED; + + char request[BUFSIZ] = {0}; + + if ((valread = read(client_fd, request, 1024 - 1)) < 0) + return HTTP_SRS_READ_FAILED; + + HttpRequest *req = handle_request(request); + if (req == NULL) + return HTTP_SRS_HANDLE_REQUEST_FAILED; + + HttpResponse *res; + + if (!strcmp(req->path, "/")) { + res = from_file("./" DEFAULT_HTML); + } else { + char path[] = "."; + strcat(path, req->path); + res = from_file(path); + } + // print_request(req); + // free_request(req); + if (res == NULL) { + HttpResponse __res = { + .status_code = HTTP_NOT_FOUND, + .body = "", + .content_type = HTTP_CT_PLAIN_TEXT, + .content_length = 1, + .body_in_heap = false, + }; + res = malloc(sizeof(__res)); + *res = __res; + } + + http_respond(res, client_fd); + free_response(res); + close(client_fd); + + return HTTP_SRS_RUNNING; +} + +HttpServerRunStatus http_server_stop(HttpServer *s) { + + close(s->server_fd); + + if (s->address != NULL) + free(s->address); + + return HTTP_SRS_STOPPED; +} diff --git a/http/http_server.h b/http/http_server.h new file mode 100644 index 0000000..3814b06 --- /dev/null +++ b/http/http_server.h @@ -0,0 +1,35 @@ +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +#include +#include + +#ifndef DEFAULT_HTML +#define DEFAULT_HTML "index.html" +#endif + +typedef enum { + HTTP_SRS_RUNNING = 0, + HTTP_SRS_SETUP, + HTTP_SRS_STOPPED, + HTTP_SRS_SOCKET_FAILED, + HTTP_SRS_SETSOCKOPT_FAILED, + HTTP_SRS_BIND_FAILED, + HTTP_SRS_LISTEN_FAILED, + HTTP_SRS_ACCEPT_FAILED, + HTTP_SRS_READ_FAILED, + HTTP_SRS_HANDLE_REQUEST_FAILED, +} HttpServerRunStatus; + +typedef struct { + int port; + int server_fd; + unsigned int workers; + struct sockaddr_in *address; +} HttpServer; + +HttpServerRunStatus http_server_setup(HttpServer *s); +HttpServerRunStatus http_server_run(HttpServer *s); +HttpServerRunStatus http_server_stop(HttpServer *s); + +#endif diff --git a/main.c b/main.c index 758f2d5..e6f8b6e 100644 --- a/main.c +++ b/main.c @@ -1,73 +1,23 @@ -#include "http/http_request.h" -#include "http/http_response.h" +#include "http/http_server.h" #include #include #include -#include #include #include -#define DEFAULT_HTML "index.html" - int main() { + HttpServer server = {.port = 8080, .workers = 3}; + if (http_server_setup(&server) != HTTP_SRS_SETUP) + exit(1); - const int PORT = 8080; + HttpServerRunStatus status; - int serverfd, clientfd; - ssize_t valread; + while ((status = http_server_run(&server)) == HTTP_SRS_RUNNING) + ; - struct sockaddr_in address; - int opt = 1; - socklen_t addrlen = sizeof(address); - char request[BUFSIZ] = {0}; + printf("Status: %d\n", status); - if ((serverfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { - perror("socket failed"); - exit(EXIT_FAILURE); - } - - // Forcefully attaching socket to the port 8080 - if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) { - perror("setsockopt"); - exit(EXIT_FAILURE); - } - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(PORT); - - if (bind(serverfd, (struct sockaddr *)&address, sizeof(address)) < 0) { - perror("bind failed"); - exit(EXIT_FAILURE); - } - if (listen(serverfd, 3) < 0) { - perror("listen"); - exit(EXIT_FAILURE); - } - if ((clientfd = accept(serverfd, (struct sockaddr *)&address, &addrlen)) < - 0) { - perror("accept"); - exit(EXIT_FAILURE); - } - valread = read(clientfd, request, - 1024 - 1); // subtract 1 for the null - // terminator at the end - HttpRequest *req = handle_request(request); - HttpResponse *res; - if (!strcmp(req->path, "/")) { - res = from_file("./" DEFAULT_HTML); - } else { - char path[] = "."; - strcat(path, req->path); - res = from_file(path); - } - free(req); - http_respond(*res, clientfd); - free_response(res); - - // closing the connected socket - close(clientfd); - // closing the listening socket - close(clientfd); + http_server_stop(&server); return 0; } diff --git a/premake5.lua b/premake5.lua index e1cdcd8..6941cb1 100644 --- a/premake5.lua +++ b/premake5.lua @@ -6,6 +6,11 @@ kind "StaticLib" language "C" targetdir "bin/%{cfg.buildcfg}" +filter "configurations:Debug" +symbols "On" +buildoptions { "-g3", "-O0", "-fno-inline" } -- Ensure debug symbols are generated for static library +optimize "Off" + files { "http/*.c", "http/*.h"