feat: Made big parts of daemon and http modules, new parsing utilities + fixes in logger, Makefile etc.

This commit is contained in:
Gu://em_ 2025-11-24 20:43:30 +01:00
parent 1dc653f8f3
commit 573e53ef79
10 changed files with 417 additions and 22 deletions

View file

@ -7,11 +7,14 @@ CFLAGS_DBG = -g
ASAN_DBG_FLAGS = -fsanitize=address
UTILS_SRCS = src/utils/string/string.c \
src/utils/time/fmt_time.c
src/utils/time/fmt_time.c \
src/utils/parsing/words.c \
src/utils/parsing/blanks.c
MODULES_SRCS = src/config/config.c \
src/server/server.c \
src/http/http.c \
src/logger/logs.c
src/logger/logs.c \
src/logger/errors.c
SRCS = $(UTILS_SRCS) \
$(MODULES_SRCS) \
src/main.c

View file

@ -1,10 +1,147 @@
#include "http.h"
struct http_request *parse_request(struct string *req)
{}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct http_response *generate_response(struct http_request *req)
{}
#include "../utils/parsing/words.h"
struct string *format_response(struct http_response *resp)
{}
// === Static functions
static void destroy_headers(struct http_header *headers)
{
while (headers != NULL)
{
struct http_header *next = headers->next;
free(headers);
headers = next;
}
}
static ssize_t read_field(struct string *str, size_t offset,
struct string **res)
{
ssize_t nread = read_word_delim(str, offset, res, ":\n");
if (nread <= 0)
return ERR_HTTP_INVALID_INPUT;
if (str->size <= offset + nread || str->data[offset + nread] != ':')
return ERR_HTTP_INVALID_INPUT;
return nread;
}
static ssize_t read_value(struct string *str, size_t offset,
struct string **res)
{
ssize_t nread = read_word_delim(str, offset, res, "\n");
if (nread <= 0)
return ERR_HTTP_INVALID_INPUT;
if (str->size <= offset + nread || str->data[offset + nread] != '\n')
return ERR_HTTP_INVALID_INPUT;
return nread;
}
// Parses the status line of req, stores the result in res and returns the
// number of read characters or a negative number on error
// WARNING res must be pre allocated
// (See the header for error codes)
static ssize_t parse_reqline(struct http_request *res, struct string *req)
{
ssize_t i = 0;
ssize_t skipped;
if (res == NULL)
return ERR_HTTP_INTERNAL_ERROR;
// Method
if (strncmp(req->data, "GET", strlen("GET")) == 0)
{
res->method = GET;
i += strlen("GET");
}
else if (strncmp(req->data, "HEAD", strlen("HEAD")) == 0)
{
res->method = HEAD;
i += strlen("HEAD");
}
else
return ERR_HTTP_NOT_IMPLEMENTED;
// Skip space
if (req->data[i++] != ' ')
return ERR_HTTP_INVALID_INPUT;
// Target (path)
skipped += read_word(req, i, &res->path);
if (skipped <= 0)
return ERR_HTTP_INVALID_INPUT;
// Skip space
if (req->data[i++] != ' ')
return ERR_HTTP_INVALID_INPUT;
// Protocol
skipped += read_word(req, i, &res->protocol);
if (skipped <= 0)
return ERR_HTTP_INVALID_INPUT;
// EOL
if (req->data[i++] != '\n')
return ERR_HTTP_INVALID_INPUT;
return i;
}
static ssize_t parse_headers(struct http_header *res, struct string *req,
size_t offset)
{
size_t i = offset;
while (req->data[i] != '\n') // ! Blank line
{
// Read field
ssize_t nread = read_field(req, offset, &res->field);
if (nread <= 0)
return nread; // Contains error code when negative
i += nread;
// Read value
nread = read_value(req, offset, &res->value);
if (nread <= 0)
return nread; // Contains error code when negative
i += nread + 1;
}
return i + 1;
}
// === Functions
// struct http_request *parse_request(struct string *req)
// {}
// struct http_response *generate_response(struct http_request *req)
// {}
// struct string *format_response(struct http_response *resp)
// {}
void destroy_request(struct http_request *req)
{
if (req != NULL)
{
if (req->path != NULL)
string_destroy(req->path);
if (req->protocol != NULL)
string_destroy(req->protocol);
destroy_headers(req->headers);
free(req);
}
}

View file

@ -1,6 +1,18 @@
#ifndef HTTP_H
#define HTTP_H
// === Definitions
#define _POSIX_C_SOURCE 200809L
// Error codes
#define ERR_HTTP_INVALID_INPUT -1
#define ERR_HTTP_NOT_IMPLEMENTED -2
#define ERR_HTTP_OUT_OF_MEMORY -4
#define ERR_HTTP_INTERNAL_ERROR -5
// === Includes
#include "../utils/string/string.h"
// === Enums
@ -8,11 +20,11 @@
enum http_method
{
GET,
POST,
PUT,
DELETE,
// POST,
// PUT,
// DELETE,
// PATCH,
// HEAD,
HEAD,
// OPTIONS,
// CONNECT,
// TRACE
@ -32,14 +44,12 @@ struct http_request
enum http_method method;
struct string *path;
struct string *protocol;
struct string *protocol_version;
struct http_header *headers; // Headers linked list
};
struct http_response
{
struct string *protocol;
struct string *protocol_version;
int status_code;
struct string *status_msg;
struct http_header *headers; // Headers linked list
@ -49,7 +59,7 @@ struct http_response
/* @brief Parses the HTTP request and splits it into a request structure
*
* @params req
* @param req
*
* @return A pointer to the structure containing the request infos on success,
* NULL otherwise
@ -58,7 +68,7 @@ struct http_response
/* @brief Generates a response to the given request
*
* @params req
* @param req
*
* @return A pointer to the generated response struct on success,
* NULL otherwise
@ -68,12 +78,23 @@ struct http_response
/* @brief Formats the given response structure into a valid HTTP response
* string
*
* @params resp
* @param resp
*
* @return A pointer to the string containing the response on success,
* NULL otherwise
*/
struct string* format_response(struct http_response* resp);
/* @brief Free all allocated memory inside req and req itself
*
* @param req
*/
void destroy_request(struct http_request *req);
/* @brief Free all allocated memory inside resp and resp itself
*
* @param resp
*/
void destroy_response(struct response *resp);
#endif // ! HTTP_H

View file

@ -4,6 +4,7 @@
#include <stdarg.h>
#include <string.h>
#include "../utils/time/fmt_time.h"
#include "logs.h"
// === Static variable

View file

@ -9,7 +9,7 @@
#include <unistd.h>
#include "../logger/errors.h"
#include "../logger/logs.h"
// #include "../logger/logs.h"
// === Definitions
@ -39,7 +39,7 @@ static int get_socket(const char *hostname, const char *port)
int sock_fd;
bool socket_bound = false;
struct addrinfo *cur_addr = client_addr;
while (cur_addr != NULL && socket_bound)
while (cur_addr != NULL && !socket_bound)
{
// Create socket
sock_fd = socket(cur_addr->ai_family, cur_addr->ai_socktype,

View file

@ -0,0 +1,46 @@
#include "blanks.h"
bool is_space(char c)
{
switch (c)
{
case ' ':
case '\t':
return true;
default:
return false;
}
}
bool is_blank(char c)
{
switch (c)
{
case '\f':
case '\n':
case '\r':
case '\t':
case '\v':
case ' ':
return true;
default:
return false;
}
}
ssize_t skip_blanks(struct string *str, size_t offset)
{
size_t i = offset;
while (i < str->size)
{
if (!is_space(str->data[i]))
return i;
i++;
}
// Reached EOL
return i;
}

View file

@ -0,0 +1,38 @@
#ifndef BLANKS_H
#define BLANKS_H
// === Definitions
#define BLANKS_LIST "\f\n\r\t\v "
// === Includes
#include <stdbool.h>
#include <sys/types.h>
#include <stddef.h>
#include "../string/string.h"
// === Functions
/*
* NOTE:
* As ispace(3) and isblank(3) functions from stdlib can be confusing and not
* always best suited for what I want to do, I decided to reimplement them.
* So be warned, they won't have the same behavior as their libc counterparts.
*/
/*
* Doc: TODO
*/
bool is_space(char c);
/*
* Doc: TODO
*/
bool is_blank(char c);
/*
* Doc: TODO
*/
ssize_t skip_blanks(struct string* str, size_t offset);
#endif // ! BLANKS_H

View file

@ -0,0 +1,107 @@
#include "words.h"
#include "blanks.h"
bool str_contains(const char *str, char c)
{
int i = 0;
while (str[i] != '\0')
{
if (str[i] == c)
return true;
i++;
}
return false;
}
ssize_t read_word(struct string *str, size_t offset, struct string **res)
{
size_t i = offset;
while (i < str->size)
{
if (is_blank(str->data[i]))
break;
i++;
}
*res = string_create(str->data + offset, i - offset);
if (res == NULL)
return -1;
return i - offset;
}
ssize_t read_word_delim(struct string *str, size_t offset, struct string **res,
const char *delims)
{
size_t i = offset;
bool found = false;
while (i < str->size && !found)
{
if (str_contains(delims, str->data[i]))
{
found = true;
break;
}
i++;
}
*res = string_create(str->data + offset, i - offset);
if (res == NULL)
return -1;
return i - offset;
}
ssize_t read_word_restrict(struct string *str, size_t offset,
struct string **res, const char *restr)
{
size_t i = offset;
bool found = false;
while (i < str->size && !found)
{
if (!str_contains(restr, str->data[i]))
{
found = true;
break;
}
i++;
}
*res = string_create(str->data + offset, i - offset);
if (res == NULL)
return -1;
return i - offset;
}
ssize_t read_word_predicate(struct string *str, size_t offset,
struct string **res, bool (*predicate)(char c))
{
size_t i = offset;
bool found = false;
while (i < str->size && !found)
{
if (predicate(str->data[i]))
{
found = true;
break;
}
i++;
}
*res = string_create(str->data + offset, i - offset);
if (res == NULL)
return -1;
return i - offset;
}

View file

@ -0,0 +1,42 @@
#ifndef WORDS_H
#define WORDS_H
// === Includes
#include <stddef.h>
#include <stdbool.h>
#include <sys/types.h>
#include "../string/string.h"
// === Functions
/*
* Doc: TODO
*/
bool str_contains(const char *str, char c);
/*
* Doc: TODO
*/
ssize_t read_word(struct string* str, size_t offset, struct string** res);
/*
* Doc: TODO
*/
ssize_t read_word_delim(struct string *str, size_t offset, struct string **res,
const char *delims);
/*
* Doc: TODO
*/
ssize_t read_word_restrict(struct string *str, size_t offset, struct string **res,
const char *restr);
/*
* Doc: TODO
*/
ssize_t read_word_predicate(struct string *str, size_t offset,
struct string **res, bool (*predicate)(char c));
#endif // ! WORDS_H

View file

@ -1,8 +1,8 @@
#include <time.h>
#include "fmt_time.h"
static char *get_time(void)
#include <time.h>
char *get_time(void)
{
time_t local_ts = time(NULL);
struct tm *gmt_time = gmtime(&local_ts);