Very, very basic server responses
This commit is contained in:
@@ -5,17 +5,19 @@ project(SimpleHttpServer VERSION 1.0
|
|||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
|
|
||||||
add_library(
|
add_library(
|
||||||
httpserverlib
|
httpserverlib
|
||||||
STATIC
|
STATIC
|
||||||
src/httpserver.h
|
src/httpserver.h
|
||||||
src/httpserver.c
|
src/httpserver.c
|
||||||
src/utils/hashmap.c
|
src/utils/hashmap.c
|
||||||
src/utils/hashmap.h
|
src/utils/hashmap.h
|
||||||
|
src/utils/string_builder.c
|
||||||
|
src/utils/string_builder.h
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(
|
add_executable(
|
||||||
httpserver
|
httpserver
|
||||||
app/main.c
|
app/main.c
|
||||||
app/main.h
|
app/main.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
22
app/main.c
22
app/main.c
@@ -7,7 +7,27 @@
|
|||||||
|
|
||||||
http_response_t* handle_request(http_request_t* request)
|
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;
|
return http_response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ void start_http_server(http_server_t* http_server, const char *addr, const short
|
|||||||
close(server_fd);
|
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);
|
const int socket_d = accept(server_fd, address, addr_len);
|
||||||
if (socket_d == -1)
|
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;
|
enum http_request_parsing_stage request_parsing_stage = START_LINE_METHOD;
|
||||||
http_request_t http_request = {};
|
http_request_t http_request = {};
|
||||||
|
|
||||||
char method_str[8];
|
char *method_str = malloc(8);
|
||||||
size_t method_str_len = 0;
|
size_t method_str_len = 0;
|
||||||
|
|
||||||
char http_path[2048];
|
char *http_path = malloc(2048);
|
||||||
size_t http_path_len = 0;
|
size_t http_path_len = 0;
|
||||||
|
|
||||||
char http_name[16];
|
char *http_name = malloc(16);
|
||||||
size_t http_name_len = 0;
|
size_t http_name_len = 0;
|
||||||
|
|
||||||
char header_key[256];
|
char *header_key = malloc(256);
|
||||||
size_t header_key_length = 0;
|
size_t header_key_length = 0;
|
||||||
char header_val[1024];
|
char *header_val = malloc(1024);
|
||||||
size_t header_val_length = 0;
|
size_t header_val_length = 0;
|
||||||
|
|
||||||
http_request.http_content = nullptr;
|
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));
|
char* val = calloc(header_val_length + 1, sizeof(char));
|
||||||
strncpy(val, header_val, header_val_length);
|
strncpy(val, header_val, header_val_length);
|
||||||
|
|
||||||
printf(
|
// printf(
|
||||||
"Setting header '%s' (hash %d per cap %d): '%s'\n",
|
// "Setting header '%s' (hash %d per cap %d): '%s'\n",
|
||||||
key, hashcode(http_request.http_headers, key),
|
// key, hashcode(http_request.http_headers, key),
|
||||||
http_request.http_headers->cap, val);
|
// http_request.http_headers->cap, val);
|
||||||
|
|
||||||
set(http_request.http_headers, key, val);
|
set(http_request.http_headers, key, val);
|
||||||
header_key[0] = '\0';
|
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);
|
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_hash_map(http_request.http_headers);
|
||||||
free(http_response);
|
free(http_response);
|
||||||
|
|
||||||
@@ -250,4 +288,13 @@ enum http_method parse_method_str(const char* str, const size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return UNKNOWN;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include "utils/hashmap.h"
|
#include "utils/hashmap.h"
|
||||||
|
#include "utils/string_builder.h"
|
||||||
|
|
||||||
typedef struct http_version
|
typedef struct http_version
|
||||||
{
|
{
|
||||||
@@ -65,7 +66,7 @@ typedef struct http_request
|
|||||||
typedef struct http_response
|
typedef struct http_response
|
||||||
{
|
{
|
||||||
http_version_t http_version;
|
http_version_t http_version;
|
||||||
u_short http_status_code;
|
unsigned short http_status_code;
|
||||||
char* http_status_message;
|
char* http_status_message;
|
||||||
size_t http_status_message_len;
|
size_t http_status_message_len;
|
||||||
hashmap_t* http_headers;
|
hashmap_t* http_headers;
|
||||||
@@ -79,7 +80,8 @@ typedef struct http_server
|
|||||||
} http_server_t;
|
} http_server_t;
|
||||||
|
|
||||||
void start_http_server(http_server_t* http_server, const char *addr, short port);
|
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);
|
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
|
#endif //SIMPLEHTTPSERVER_H
|
||||||
163
src/utils/string_builder.c
Normal file
163
src/utils/string_builder.c
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
//
|
||||||
|
// Created by nazar on 13.09.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "string_builder.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
43
src/utils/string_builder.h
Normal file
43
src/utils/string_builder.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// Created by nazar on 13.09.2025.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SIMPLEHTTPSERVER_STRING_BUILDER_H
|
||||||
|
#define SIMPLEHTTPSERVER_STRING_BUILDER_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user