fix: Lots of late fixes. Hope I pass this time, otherwise I'm more likely to kill myself before christmas than having to continue like that all year long

This commit is contained in:
Gu://em_ 2025-11-28 00:39:43 +01:00
parent c675e3253b
commit 8ec545e126
15 changed files with 270 additions and 30 deletions

1
httpd/.gitignore vendored
View file

@ -6,3 +6,4 @@
*.log *.log
*.core *.core
httpd httpd
__pycache__

View file

@ -48,9 +48,9 @@ int start_daemon(void)
pid_t pid = fork(); pid_t pid = fork();
if (!pid) // Daemon if (!pid) // Daemon
{ {
start_server("localhost", config->servers->port); start_server(config->servers->ip, config->servers->port);
} }
else else // Parent
{ {
// Write pid // Write pid
int err = write_pid(config->pid_file, pid); int err = write_pid(config->pid_file, pid);
@ -59,8 +59,6 @@ int start_daemon(void)
kill(pid, SIGINT); kill(pid, SIGINT);
return 1; return 1;
} }
exit(1);
} }
return 0; return 0;
} }

View file

@ -97,18 +97,30 @@ static bool find_target(struct http_request *req)
// Check filename // Check filename
if (!check_filename(req->target)) if (!check_filename(req->target))
return false; return false;
int err = is_directory(req->target->data);
if (err == -1) char *target = string_to_charptr(req->target);
int err = is_directory(target);
free(target);
err = 0; // TODO fix that bug
if (err == -1) // Doesn't exists
return false; return false;
else if (err == 1) else if (err == 1)
{ {
// Append default file if directory
if (req->target->data[req->target->size - 1] != '/') if (req->target->data[req->target->size - 1] != '/')
string_concat_str(req->target, "/", 1); string_concat_str(req->target, "/", 1);
string_concat_str(req->target, config->servers->default_file, string_concat_str(req->target, config->servers->default_file,
strlen(config->servers->default_file)); strlen(config->servers->default_file));
// Recheck
target = string_to_charptr(req->target);
err = is_directory(target);
free(target);
return err == 0;
} }
// Exists
return true; return true;
} }
@ -160,6 +172,19 @@ static struct string *generate_status_message(int status_code)
return string_create(message, strlen(message)); return string_create(message, strlen(message));
} }
static void check_req(struct http_request *req, struct http_response *resp)
{
// Method
if (req->method == INVALID_METHOD)
resp->status_code = 405;
// Protocol
char *protocol = string_to_charptr(req->protocol);
if (strcmp(protocol, HTTP_VERSION) != 0)
resp->status_code = 505;
free(protocol);
}
// === Functions // === Functions
void http_init(struct config *cfg) void http_init(struct config *cfg)
@ -168,7 +193,7 @@ void http_init(struct config *cfg)
} }
// TODO handle logs // TODO handle logs
void handle_request(int client_fd) void handle_request(int client_fd, char *client_ip)
{ {
char buffer[BUFFER_SIZE]; // Declared in server.h char buffer[BUFFER_SIZE]; // Declared in server.h
struct string *str = string_create(NULL, 0); struct string *str = string_create(NULL, 0);
@ -193,7 +218,7 @@ void handle_request(int client_fd)
return; return;
char *method = get_http_method(req->method); char *method = get_http_method(req->method);
char *target = string_to_charptr(req->target); char *target = string_to_charptr(req->target);
log_request(method, target, "127.0.0.1"); log_request(method, target, client_ip);
free(method); free(method);
free(target); free(target);
@ -230,6 +255,13 @@ void handle_request(int client_fd)
sendfile(client_fd, fd, 0, atoi(cl_str)); sendfile(client_fd, fd, 0, atoi(cl_str));
} }
// Log response
method = get_http_method(req->method);
target = string_to_charptr(req->target);
log_response(resp->status_code, method, target, client_ip);
free(method);
free(target);
// Free // Free
string_destroy(str); string_destroy(str);
string_destroy(res); string_destroy(res);
@ -275,6 +307,10 @@ struct http_response *generate_response(struct http_request *req)
char *protocol = HTTP_VERSION; char *protocol = HTTP_VERSION;
res->protocol = string_create(protocol, strlen(protocol)); res->protocol = string_create(protocol, strlen(protocol));
// Target
str_concat_string(config->servers->root_dir,
strlen(config->servers->root_dir), req->target);
// Status code // Status code
if (req->status_code == 0) if (req->status_code == 0)
{ {
@ -284,23 +320,34 @@ struct http_response *generate_response(struct http_request *req)
res->status_code = 200; res->status_code = 200;
} }
// Status msg check_req(req, res);
res->status_msg = generate_status_message(res->status_code);
// Headers // Headers
char *time = get_time(); char *time = get_time();
append_header(&res->headers, create_header("Date", time)); append_header(&res->headers, create_header("Date", time));
// free(time); // Yes, the one that completely disapeared this year free(time); // Yes, the one that completely disapeared this year
// Oopa
if (res->status_code == 200) if (res->status_code == 200)
{ {
char buf[21] = { 0 }; // (21 ~= log10(2^64)) + 1 (null byte) char buf[21] = { 0 }; // (20 ~= log10(2^64)) + 1 (null byte)
char *target = string_to_charptr(req->target); char *target = string_to_charptr(req->target);
sprintf(buf, "%lu", get_file_content_size(target)); ssize_t cl = get_file_content_size(target);
append_header(&res->headers, create_header("Content-Length", buf)); free(target);
if (cl >= 0)
{
sprintf(buf, "%lu", cl);
append_header(&res->headers, create_header("Content-Length", buf));
}
else
{
res->status_code =
404; // TODO replace by 403 once find_target is fixed
}
} }
append_header(&res->headers, create_header("Connection", "close")); append_header(&res->headers, create_header("Connection", "close"));
// Status msg
res->status_msg = generate_status_message(res->status_code);
return res; return res;
} }

View file

@ -20,6 +20,7 @@
enum http_method enum http_method
{ {
INVALID_METHOD,
GET, GET,
// POST, // POST,
// PUT, // PUT,
@ -72,7 +73,7 @@ void http_init(struct config *cfg);
* *
* @param client_fd * @param client_fd
*/ */
void handle_request(int client_fd); void handle_request(int client_fd, char* client_ip);
/* @brief Parses the HTTP request and splits it into a request structure /* @brief Parses the HTTP request and splits it into a request structure
* *

View file

@ -4,6 +4,7 @@
#include <errno.h> #include <errno.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include "../utils/time/fmt_time.h" #include "../utils/time/fmt_time.h"
@ -32,8 +33,10 @@ void print_log_err(char *format, ...)
return; return;
// Log prefix (time and server name) // Log prefix (time and server name)
dprintf(config.logfile_fd, "%s [%s] ", get_time(), char *time = get_time();
dprintf(config.logfile_fd, "%s [%s] ERROR ", time,
config.server_cfg->server_name); config.server_cfg->server_name);
free(time);
// Print actual log // Print actual log
va_list args; va_list args;

View file

@ -4,6 +4,7 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h>
#include "../utils/time/fmt_time.h" #include "../utils/time/fmt_time.h"
#include "errors.h" #include "errors.h"
@ -35,8 +36,10 @@ void print_log(char *format, ...)
return; return;
// Log prefix (time and server name) // Log prefix (time and server name)
dprintf(config.logfile_fd, "%s [%s] ", get_time(), char *time = get_time();
dprintf(config.logfile_fd, "%s [%s] ", time,
config.server_cfg->server_name); config.server_cfg->server_name);
free(time);
// Print actual log // Print actual log
va_list args; va_list args;
@ -50,14 +53,12 @@ void print_log(char *format, ...)
void log_request(char *request_type, char *target, char *client_ip) void log_request(char *request_type, char *target, char *client_ip)
{ {
print_log("received %s, on '%s' from %s", get_time(), print_log("received %s, on '%s' from %s", request_type, target, client_ip);
config.server_cfg->server_name, request_type, target, client_ip);
} }
void log_response(int status_code, char *request_type, char *target, void log_response(int status_code, char *request_type, char *target,
char *client_ip) char *client_ip)
{ {
print_log("responding with %d to %s for %s on '%s'", get_time(), print_log("responding with %d to %s for %s on '%s'", status_code, client_ip,
config.server_cfg->server_name, status_code, client_ip,
request_type, target); request_type, target);
} }

View file

@ -24,7 +24,7 @@ int main(int argc, char **argv)
switch (config->daemon) switch (config->daemon)
{ {
case NO_OPTION: case NO_OPTION:
start_server("localhost", config->servers->port); start_server(config->servers->ip, config->servers->port);
break; break;
case START: case START:

View file

@ -114,7 +114,7 @@ void start_server(const char *host, const char *port)
// TODO handle signals to stop // TODO handle signals to stop
handle_request(client_fd); handle_request(client_fd, "127.0.0.1");
// send_back(client_fd); // send_back(client_fd);
close(client_fd); close(client_fd);
} }

View file

@ -1,3 +1,5 @@
#define _POSIX_C_SOURCE 200112L
#include "files.h" #include "files.h"
#include <stdio.h> #include <stdio.h>
@ -5,13 +7,13 @@
// #include "../string/string.h" // #include "../string/string.h"
// bool file_exists(const char *path) // int file_exists(const char *path)
// {} // {}
int is_directory(const char *path) int is_directory(const char *path)
{ {
struct stat path_stat; struct stat path_stat;
if (stat(path, &path_stat) != 0) if (lstat(path, &path_stat) != 0)
return S_ISDIR(path_stat.st_mode); return S_ISDIR(path_stat.st_mode);
else else
return -1; return -1;

View file

@ -40,7 +40,7 @@ int is_directory(const char *path);
* *
* @return * @return
*/ */
char *get_file(const char *path); // char *get_file(const char *path);
/* /*
* @brief * @brief
@ -85,7 +85,7 @@ ssize_t get_file_content_size(const char *path);
* *
* @return 0 on success, the corresponding error code otherwise * @return 0 on success, the corresponding error code otherwise
*/ */
int write_to_file(const char *path, struct string* buf); int write_to_file(const char *path, struct string *buf);
/* /*
* @brief * @brief

View file

@ -67,7 +67,7 @@ void string_concat_str(struct string *str, const char *to_concat, size_t size)
} }
else else
{ {
str->data = realloc(str->data, new_size); str->data = realloc(str->data, new_size * sizeof(char));
if (str->data == NULL) if (str->data == NULL)
return; // Handle ? return; // Handle ?
} }
@ -78,6 +78,45 @@ void string_concat_str(struct string *str, const char *to_concat, size_t size)
} }
} }
void str_concat_string(const char *str, size_t size, struct string *to_concat)
{
size_t new_size = to_concat->size + size;
size_t tmp_size = to_concat->size;
if (new_size == 0)
return;
to_concat->size = new_size;
if (tmp_size == 0)
{
to_concat->data = malloc(new_size);
if (to_concat->data == NULL)
return; // Handle ?
}
else
{
// Temporary buffer
char *tmp = malloc(tmp_size * sizeof(char));
if (tmp == NULL)
return; // Handle ?
// (Duplicate)
memcpy(tmp, to_concat->data, tmp_size);
// Reallocate string
char *new_data = realloc(to_concat->data, new_size * sizeof(char));
if (to_concat->data == NULL)
{
to_concat->size = tmp_size; // Restore (original ptr still valid)
return; // Handle ?
}
to_concat->data = new_data;
memcpy(to_concat->data, str, size);
memcpy(to_concat->data + size, tmp, tmp_size);
}
}
void string_to_lowercase(struct string *str) void string_to_lowercase(struct string *str)
{ {
for (size_t i = 0; i < str->size; i++) for (size_t i = 0; i < str->size; i++)

View file

@ -42,6 +42,16 @@ int string_compare_n_str(const struct string *str1, const char *str2, size_t n);
*/ */
void string_concat_str(struct string *str, const char *to_concat, size_t size); void string_concat_str(struct string *str, const char *to_concat, size_t size);
/*
** @brief Similar to string_concat_str but with str at the beginning of the
* result string
**
** @param str
** @param to_concat
** @param size
*/
void str_concat_string(const char *str, size_t size, struct string *to_concat);
/* /*
** @brief Concat a char * with its size in a struct string ** @brief Concat a char * with its size in a struct string
** **

View file

@ -1,10 +1,15 @@
#include "fmt_time.h" #include "fmt_time.h"
#include <stdlib.h>
#include <time.h> #include <time.h>
char *get_time(void) char *get_time(void)
{ {
char *buf = malloc(64 * sizeof(char)); // Oui, 64
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);
return asctime(gmt_time);
// return asctime(gmt_time);
strftime(buf, 64, "%a, %d %b %Y %H:%M:%S %Z", gmt_time);
return buf;
} }

View file

@ -0,0 +1 @@
<h1> YEAAH </h1>

132
httpd/tests/test_suite.py Normal file
View file

@ -0,0 +1,132 @@
import subprocess as sp
import http
import requests
import socket
import pytest
import time
host = "127.0.0.1"
port = "6994"
executable = "./httpd"
def spawn_httpd(stdout_filename, args=[]):
with open(stdout_filename,"w") as f:
httpd_proc = sp.Popen([executable,"--pid_file","/tmp/HTTPd.pid","--ip",host,"--port", port, "--root_dir","./test_root_dir/","--server_name","httpd"] if args == [] else [executable] + args, stdout=f,stderr=sp.PIPE,bufsize=0)
time.sleep(0.2)
return httpd_proc
def kill_httpd(proc):
#proc.send_signal(sp.SIGINT)
proc.kill()
@pytest.mark.timeout(2)
def test_bad_config():
proc = spawn_httpd("out.log", ["hello","world"])
proc.wait(1)
try:
assert proc.returncode == 2
finally:
kill_httpd(proc)
@pytest.mark.timeout(2)
def test_get_index():
proc = spawn_httpd("out.log")
req = requests.get(f"http://{host}:{port}/index.html")
assert req.status_code == 200
with open("./test_root_dir/index.html","r") as f:
try:
assert f.read() == req.text
finally:
kill_httpd(proc)
@pytest.mark.timeout(2)
def test_get_default():
proc = spawn_httpd("out.log")
req = requests.get(f"http://{host}:{port}/")
assert req.status_code == 200
with open("./test_root_dir/index.html","r") as f:
try:
assert f.read() == req.text
finally:
kill_httpd(proc)
@pytest.mark.timeout(2)
def test_no_file():
proc = spawn_httpd("out.log")
req = requests.get(f"http://{host}:{port}/notindex.html")
assert req.status_code == 404
@pytest.mark.timeout(2)
def test_bad_request():
proc = spawn_httpd("out.log")
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,int(port)))
request = f"GET /index.html FTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
sock.sendall(request.encode())
resp = sock.recv(1024)
resp_decoded = resp.decode()
try:
assert "400 Bad Request" in resp_decoded
finally:
kill_httpd(proc)
@pytest.mark.timeout(2)
def test_invalid_method():
proc = spawn_httpd("out.log")
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,int(port)))
request = f"PUT /index.html HTTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
sock.sendall(request.encode())
response = http.client.HTTPResponse(sock)
response.begin()
try:
assert response.status == 405
finally:
kill_httpd(proc)
@pytest.mark.timeout(2)
def test_invalid_version():
proc = spawn_httpd("out.log")
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,int(port)))
request = f"GET /index.html HTTP/1.2\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
sock.sendall(request.encode())
response = http.client.HTTPResponse(sock)
response.begin()
try:
assert response.status == 505
finally:
kill_httpd(proc)
@pytest.mark.timeout(2)
def test_bad_request():
proc = spawn_httpd("out.log")
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,int(port)))
request = f"GET /index.html FTP/1.1\r\nHOST: {host}:{port}\r\nConnection: close\r\n\r\n"
sock.sendall(request.encode())
response = http.client.HTTPResponse(sock)
response.begin()
try:
assert response.status == 400
finally:
kill_httpd(proc)