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
*.core
httpd
__pycache__

View file

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

View file

@ -97,18 +97,30 @@ static bool find_target(struct http_request *req)
// Check filename
if (!check_filename(req->target))
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;
else if (err == 1)
{
// Append default file if directory
if (req->target->data[req->target->size - 1] != '/')
string_concat_str(req->target, "/", 1);
string_concat_str(req->target, 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;
}
@ -160,6 +172,19 @@ static struct string *generate_status_message(int status_code)
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
void http_init(struct config *cfg)
@ -168,7 +193,7 @@ void http_init(struct config *cfg)
}
// 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
struct string *str = string_create(NULL, 0);
@ -193,7 +218,7 @@ void handle_request(int client_fd)
return;
char *method = get_http_method(req->method);
char *target = string_to_charptr(req->target);
log_request(method, target, "127.0.0.1");
log_request(method, target, client_ip);
free(method);
free(target);
@ -230,6 +255,13 @@ void handle_request(int client_fd)
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
string_destroy(str);
string_destroy(res);
@ -275,6 +307,10 @@ struct http_response *generate_response(struct http_request *req)
char *protocol = HTTP_VERSION;
res->protocol = string_create(protocol, strlen(protocol));
// Target
str_concat_string(config->servers->root_dir,
strlen(config->servers->root_dir), req->target);
// Status code
if (req->status_code == 0)
{
@ -284,23 +320,34 @@ struct http_response *generate_response(struct http_request *req)
res->status_code = 200;
}
// Status msg
res->status_msg = generate_status_message(res->status_code);
check_req(req, res);
// Headers
char *time = get_time();
append_header(&res->headers, create_header("Date", time));
// free(time); // Yes, the one that completely disapeared this year
// Oopa
free(time); // Yes, the one that completely disapeared this year
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);
sprintf(buf, "%lu", get_file_content_size(target));
append_header(&res->headers, create_header("Content-Length", buf));
ssize_t cl = get_file_content_size(target);
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"));
// Status msg
res->status_msg = generate_status_message(res->status_code);
return res;
}

View file

@ -20,6 +20,7 @@
enum http_method
{
INVALID_METHOD,
GET,
// POST,
// PUT,
@ -72,7 +73,7 @@ void http_init(struct config *cfg);
*
* @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
*

View file

@ -4,6 +4,7 @@
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "../utils/time/fmt_time.h"
@ -32,8 +33,10 @@ void print_log_err(char *format, ...)
return;
// 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);
free(time);
// Print actual log
va_list args;

View file

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

View file

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

View file

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

View file

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

View file

@ -40,7 +40,7 @@ int is_directory(const char *path);
*
* @return
*/
char *get_file(const char *path);
// char *get_file(const char *path);
/*
* @brief
@ -85,7 +85,7 @@ ssize_t get_file_content_size(const char *path);
*
* @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

View file

@ -67,7 +67,7 @@ void string_concat_str(struct string *str, const char *to_concat, size_t size)
}
else
{
str->data = realloc(str->data, new_size);
str->data = realloc(str->data, new_size * sizeof(char));
if (str->data == NULL)
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)
{
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);
/*
** @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
**

View file

@ -1,10 +1,15 @@
#include "fmt_time.h"
#include <stdlib.h>
#include <time.h>
char *get_time(void)
{
char *buf = malloc(64 * sizeof(char)); // Oui, 64
time_t local_ts = time(NULL);
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)