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
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../utils/time/fmt_time.h"
|
||||
#include "logs.h"
|
||||
|
||||
// === Static variable
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
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"
|
||||
|
||||
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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue