feat: Made big parts of daemon and http modules, new parsing utilities + fixes in logger, Makefile etc.
This commit is contained in:
parent
1dc653f8f3
commit
573e53ef79
10 changed files with 417 additions and 22 deletions
|
|
@ -7,11 +7,14 @@ CFLAGS_DBG = -g
|
||||||
ASAN_DBG_FLAGS = -fsanitize=address
|
ASAN_DBG_FLAGS = -fsanitize=address
|
||||||
|
|
||||||
UTILS_SRCS = src/utils/string/string.c \
|
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 \
|
MODULES_SRCS = src/config/config.c \
|
||||||
src/server/server.c \
|
src/server/server.c \
|
||||||
src/http/http.c \
|
src/http/http.c \
|
||||||
src/logger/logs.c
|
src/logger/logs.c \
|
||||||
|
src/logger/errors.c
|
||||||
SRCS = $(UTILS_SRCS) \
|
SRCS = $(UTILS_SRCS) \
|
||||||
$(MODULES_SRCS) \
|
$(MODULES_SRCS) \
|
||||||
src/main.c
|
src/main.c
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,147 @@
|
||||||
#include "http.h"
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,18 @@
|
||||||
#ifndef HTTP_H
|
#ifndef HTTP_H
|
||||||
#define 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"
|
#include "../utils/string/string.h"
|
||||||
|
|
||||||
// === Enums
|
// === Enums
|
||||||
|
|
@ -8,11 +20,11 @@
|
||||||
enum http_method
|
enum http_method
|
||||||
{
|
{
|
||||||
GET,
|
GET,
|
||||||
POST,
|
// POST,
|
||||||
PUT,
|
// PUT,
|
||||||
DELETE,
|
// DELETE,
|
||||||
// PATCH,
|
// PATCH,
|
||||||
// HEAD,
|
HEAD,
|
||||||
// OPTIONS,
|
// OPTIONS,
|
||||||
// CONNECT,
|
// CONNECT,
|
||||||
// TRACE
|
// TRACE
|
||||||
|
|
@ -32,14 +44,12 @@ struct http_request
|
||||||
enum http_method method;
|
enum http_method method;
|
||||||
struct string *path;
|
struct string *path;
|
||||||
struct string *protocol;
|
struct string *protocol;
|
||||||
struct string *protocol_version;
|
|
||||||
struct http_header *headers; // Headers linked list
|
struct http_header *headers; // Headers linked list
|
||||||
};
|
};
|
||||||
|
|
||||||
struct http_response
|
struct http_response
|
||||||
{
|
{
|
||||||
struct string *protocol;
|
struct string *protocol;
|
||||||
struct string *protocol_version;
|
|
||||||
int status_code;
|
int status_code;
|
||||||
struct string *status_msg;
|
struct string *status_msg;
|
||||||
struct http_header *headers; // Headers linked list
|
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
|
/* @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,
|
* @return A pointer to the structure containing the request infos on success,
|
||||||
* NULL otherwise
|
* NULL otherwise
|
||||||
|
|
@ -58,7 +68,7 @@ struct http_response
|
||||||
|
|
||||||
/* @brief Generates a response to the given request
|
/* @brief Generates a response to the given request
|
||||||
*
|
*
|
||||||
* @params req
|
* @param req
|
||||||
*
|
*
|
||||||
* @return A pointer to the generated response struct on success,
|
* @return A pointer to the generated response struct on success,
|
||||||
* NULL otherwise
|
* NULL otherwise
|
||||||
|
|
@ -68,12 +78,23 @@ struct http_response
|
||||||
/* @brief Formats the given response structure into a valid HTTP response
|
/* @brief Formats the given response structure into a valid HTTP response
|
||||||
* string
|
* string
|
||||||
*
|
*
|
||||||
* @params resp
|
* @param resp
|
||||||
*
|
*
|
||||||
* @return A pointer to the string containing the response on success,
|
* @return A pointer to the string containing the response on success,
|
||||||
* NULL otherwise
|
* NULL otherwise
|
||||||
*/
|
*/
|
||||||
struct string* format_response(struct http_response* resp);
|
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
|
#endif // ! HTTP_H
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "../utils/time/fmt_time.h"
|
||||||
#include "logs.h"
|
#include "logs.h"
|
||||||
|
|
||||||
// === Static variable
|
// === Static variable
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../logger/errors.h"
|
#include "../logger/errors.h"
|
||||||
#include "../logger/logs.h"
|
// #include "../logger/logs.h"
|
||||||
|
|
||||||
// === Definitions
|
// === Definitions
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ static int get_socket(const char *hostname, const char *port)
|
||||||
int sock_fd;
|
int sock_fd;
|
||||||
bool socket_bound = false;
|
bool socket_bound = false;
|
||||||
struct addrinfo *cur_addr = client_addr;
|
struct addrinfo *cur_addr = client_addr;
|
||||||
while (cur_addr != NULL && socket_bound)
|
while (cur_addr != NULL && !socket_bound)
|
||||||
{
|
{
|
||||||
// Create socket
|
// Create socket
|
||||||
sock_fd = socket(cur_addr->ai_family, cur_addr->ai_socktype,
|
sock_fd = socket(cur_addr->ai_family, cur_addr->ai_socktype,
|
||||||
|
|
|
||||||
46
httpd/src/utils/parsing/blanks.c
Normal file
46
httpd/src/utils/parsing/blanks.c
Normal 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;
|
||||||
|
}
|
||||||
38
httpd/src/utils/parsing/blanks.h
Normal file
38
httpd/src/utils/parsing/blanks.h
Normal 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
|
||||||
107
httpd/src/utils/parsing/words.c
Normal file
107
httpd/src/utils/parsing/words.c
Normal 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;
|
||||||
|
}
|
||||||
42
httpd/src/utils/parsing/words.h
Normal file
42
httpd/src/utils/parsing/words.h
Normal 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
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#include "fmt_time.h"
|
#include "fmt_time.h"
|
||||||
|
|
||||||
static char *get_time(void)
|
#include <time.h>
|
||||||
|
|
||||||
|
char *get_time(void)
|
||||||
{
|
{
|
||||||
time_t local_ts = time(NULL);
|
time_t local_ts = time(NULL);
|
||||||
struct tm *gmt_time = gmtime(&local_ts);
|
struct tm *gmt_time = gmtime(&local_ts);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue