diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b4866c..75cf17a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,17 +5,19 @@ project(SimpleHttpServer VERSION 1.0 LANGUAGES C) add_library( - httpserverlib - STATIC - src/httpserver.h - src/httpserver.c + httpserverlib + STATIC + src/httpserver.h + src/httpserver.c src/utils/hashmap.c src/utils/hashmap.h + src/utils/string_builder.c + src/utils/string_builder.h ) add_executable( - httpserver - app/main.c + httpserver + app/main.c app/main.h ) diff --git a/app/main.c b/app/main.c index 95c6ca8..a201529 100644 --- a/app/main.c +++ b/app/main.c @@ -7,7 +7,27 @@ http_response_t* handle_request(http_request_t* request) { - http_response_t* http_response = {}; + http_response_t* http_response = malloc(sizeof *http_response); + http_response->http_version.http_name = "HTTP\0"; + http_response->http_version.http_name_len = 4; + http_response->http_version.major = 1; + http_response->http_version.minor = 1; + + http_response->http_status_code = 200; + http_response->http_status_message = "OK\0"; + http_response->http_status_message_len = 2; + + char *length_str = malloc(20); + sprintf(length_str, "%ld", request->http_path_len); + + http_response->http_headers = new_hash_map(); + set(http_response->http_headers, "Server", "SimpleHttpServer"); + set(http_response->http_headers, "Content-Length", length_str); + set(http_response->http_headers, "Content-Type", "text/plain"); + + http_response->http_content = request->http_path; + http_response->http_content_len = request->http_path_len; + return http_response; } diff --git a/src/httpserver.c b/src/httpserver.c index 5139904..71880ac 100644 --- a/src/httpserver.c +++ b/src/httpserver.c @@ -60,7 +60,7 @@ void start_http_server(http_server_t* http_server, const char *addr, const short close(server_fd); } -void process_conn(http_server_t* http_server, const int server_fd, struct sockaddr* address, socklen_t* addr_len) +void process_conn(const http_server_t* http_server, const int server_fd, struct sockaddr* address, socklen_t* addr_len) { const int socket_d = accept(server_fd, address, addr_len); if (socket_d == -1) @@ -92,18 +92,18 @@ void process_conn(http_server_t* http_server, const int server_fd, struct sockad enum http_request_parsing_stage request_parsing_stage = START_LINE_METHOD; http_request_t http_request = {}; - char method_str[8]; + char *method_str = malloc(8); size_t method_str_len = 0; - char http_path[2048]; + char *http_path = malloc(2048); size_t http_path_len = 0; - char http_name[16]; + char *http_name = malloc(16); size_t http_name_len = 0; - char header_key[256]; + char *header_key = malloc(256); size_t header_key_length = 0; - char header_val[1024]; + char *header_val = malloc(1024); size_t header_val_length = 0; http_request.http_content = nullptr; @@ -203,10 +203,10 @@ void process_conn(http_server_t* http_server, const int server_fd, struct sockad char* val = calloc(header_val_length + 1, sizeof(char)); strncpy(val, header_val, header_val_length); - printf( - "Setting header '%s' (hash %d per cap %d): '%s'\n", - key, hashcode(http_request.http_headers, key), - http_request.http_headers->cap, val); + // printf( + // "Setting header '%s' (hash %d per cap %d): '%s'\n", + // key, hashcode(http_request.http_headers, key), + // http_request.http_headers->cap, val); set(http_request.http_headers, key, val); header_key[0] = '\0'; @@ -231,8 +231,46 @@ void process_conn(http_server_t* http_server, const int server_fd, struct sockad http_response_t* http_response = http_server->handle_request(&http_request); - // TODO: Send response + string_builder_t* response_sb = new_string_builder(); + append_fmt( + response_sb, + "%s/%d.%d %d %s\r\n", + http_response->http_version.http_name, + http_response->http_version.major, + http_response->http_version.minor, + http_response->http_status_code, + http_response->http_status_message + ); + + for (int i = 0; i < http_response->http_headers->cap; i++) + { + const pair_t* iter = http_response->http_headers->list[i]; + while (iter != nullptr) + { + append_fmt(response_sb, "%s: %s\r\n", iter->key, iter->val); + iter = iter->next; + } + } + + append_crlf(response_sb, ""); + append_nstr(response_sb, http_response->http_content, http_response->http_content_len); + + char* response_str = string_builder_to_string(response_sb); + + printf("\nServer response (%ld):\n", response_sb->char_count); + for (int i = 0; i < response_sb->char_count; i++) + { + if (response_str[i] != '\r') + putchar(response_str[i]); + } + putchar('\n'); + fflush(stdout); + + write(socket_d, response_str, response_sb->char_count); + + free(response_str); + free_string_builder(response_sb); free_hash_map(http_request.http_headers); free(http_response); @@ -250,4 +288,13 @@ enum http_method parse_method_str(const char* str, const size_t len) } return UNKNOWN; -} \ No newline at end of file +} + +const char* http_method_to_string(const enum http_method m) +{ + for (size_t i = 0; i < sizeof(method_map)/sizeof(method_map[0]); i++) { + if (method_map[i].method == m) + return method_map[i].name; + } + return "UNKNOWN"; +} diff --git a/src/httpserver.h b/src/httpserver.h index c61cd22..28f894b 100644 --- a/src/httpserver.h +++ b/src/httpserver.h @@ -16,6 +16,7 @@ #include #include #include "utils/hashmap.h" +#include "utils/string_builder.h" typedef struct http_version { @@ -65,7 +66,7 @@ typedef struct http_request typedef struct http_response { http_version_t http_version; - u_short http_status_code; + unsigned short http_status_code; char* http_status_message; size_t http_status_message_len; hashmap_t* http_headers; @@ -79,7 +80,8 @@ typedef struct http_server } http_server_t; void start_http_server(http_server_t* http_server, const char *addr, short port); -void process_conn(http_server_t* http_server, int server_fd, struct sockaddr* address, socklen_t* addr_len); +void process_conn(const http_server_t* http_server, int server_fd, struct sockaddr* address, socklen_t* addr_len); enum http_method parse_method_str(const char* str, size_t len); +const char* http_method_to_string(enum http_method m); #endif //SIMPLEHTTPSERVER_H \ No newline at end of file diff --git a/src/utils/string_builder.c b/src/utils/string_builder.c new file mode 100644 index 0000000..fb3f712 --- /dev/null +++ b/src/utils/string_builder.c @@ -0,0 +1,163 @@ +// +// Created by nazar on 13.09.2025. +// + +#include "string_builder.h" + +#include +#include + +string_builder_t* new_string_builder() +{ + string_builder_t* sb = malloc(sizeof *sb); + sb->first = nullptr; + sb->last = nullptr; + sb->char_count = 0; + return sb; +} + +char* string_builder_to_string(const string_builder_t* sb) +{ + char* str = malloc(sb->char_count + 1); + if (!str) return nullptr; + + char* p = str; + + for (const string_builder_part_t* part = sb->first; part; part = part->next) { + memcpy(p, part->data, part->len); + p += part->len; + } + + *p = '\0'; + return str; +} + +void free_string_builder(string_builder_t* sb) +{ + string_builder_part_t* iter = sb->first; + while (iter != nullptr) + { + string_builder_part_t* prev = iter; + iter = iter->next; + free(prev->data); + free(prev); + } + + sb->first = nullptr; + sb->last = nullptr; + free(sb); +} + +void append_part(string_builder_t* sb, string_builder_part_t* part) +{ + sb->char_count += part->len; + if (sb->last == nullptr) + { + sb->first = part; + sb->last = part; + return; + } + + sb->last->next = part; + sb->last = part; +} + +void append_char(string_builder_t* sb, const char c) +{ + char* str = malloc(2); + sprintf(str, "%c", c); + + string_builder_part_t* part = malloc(sizeof *part); + part->data = str; + part->len = 1; + + append_part(sb, part); +} + +void append_int(string_builder_t* sb, const int i) +{ + char* str = malloc(20); + sprintf(str, "%d", i); + + string_builder_part_t* part = malloc(sizeof *part); + part->data = str; + part->len = strlen(str); + + append_part(sb, part); +} + +void append_str(string_builder_t* sb, char* str) +{ + append_nstr(sb, str, strlen(str)); +} + +void append_nstr(string_builder_t* sb, char* str, size_t len) +{ + string_builder_part_t* part = malloc(sizeof *part); + part->data = str; + part->len = len; + + append_part(sb, part); +} + +void append_fmt(string_builder_t* sb, const char* fmt, ...) { + va_list args = {}; + va_start(args, fmt); + + va_list args_copy; + va_copy(args_copy, args); + const int len = vsnprintf(nullptr, 0, fmt, args_copy); + va_end(args_copy); + + if (len < 0) { + va_end(args); + return; + } + + char* buf = malloc(len + 1); + if (!buf) { + va_end(args); + return; + } + + vsnprintf(buf, len + 1, fmt, args); + va_end(args); + + append_nstr(sb, buf, len); +} + +void append_lf(string_builder_t* sb, char* str) +{ + const size_t len = strlen(str); + append_nlf(sb, str, len); +} + +void append_nlf(string_builder_t* sb, char* str, size_t len) +{ + char* l = malloc(len + 2); + sprintf(l, "%s\n", str); + + string_builder_part_t* part = malloc(sizeof *part); + part->data = l; + part->len = len + 1; + + append_part(sb, part); +} + +void append_crlf(string_builder_t* sb, char* str) +{ + const size_t len = strlen(str); + append_ncrlf(sb, str, len); +} + +void append_ncrlf(string_builder_t* sb, char* str, size_t len) +{ + char* l = malloc(len + 3); + sprintf(l, "%s\r\n", str); + + string_builder_part_t* part = malloc(sizeof *part); + part->data = l; + part->len = len + 2; + + append_part(sb, part); +} \ No newline at end of file diff --git a/src/utils/string_builder.h b/src/utils/string_builder.h new file mode 100644 index 0000000..1345d8e --- /dev/null +++ b/src/utils/string_builder.h @@ -0,0 +1,43 @@ +// +// Created by nazar on 13.09.2025. +// + +#ifndef SIMPLEHTTPSERVER_STRING_BUILDER_H +#define SIMPLEHTTPSERVER_STRING_BUILDER_H + +#include +#include +#include +#include + +typedef struct string_builder_part string_builder_part_t; + +struct string_builder_part +{ + char* data; + size_t len; + string_builder_part_t* next; +}; + +typedef struct string_builder +{ + string_builder_part_t* first; + string_builder_part_t* last; + size_t char_count; +} string_builder_t; + +string_builder_t* new_string_builder(); +char* string_builder_to_string(const string_builder_t* sb); +void free_string_builder(string_builder_t* sb); +void append_part(string_builder_t* sb, string_builder_part_t* part); +void append_char(string_builder_t* sb, char c); +void append_int(string_builder_t* sb, int i); +void append_str(string_builder_t* sb, char* str); +void append_nstr(string_builder_t* sb, char* str, size_t len); +void append_fmt(string_builder_t* sb, const char* fmt, ...); +void append_lf(string_builder_t* sb, char* str); +void append_nlf(string_builder_t* sb, char* str, size_t len); +void append_crlf(string_builder_t* sb, char* str); +void append_ncrlf(string_builder_t* sb, char* str, size_t len); + +#endif //SIMPLEHTTPSERVER_STRING_BUILDER_H \ No newline at end of file