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:
parent
c675e3253b
commit
8ec545e126
15 changed files with 270 additions and 30 deletions
1
httpd/.gitignore
vendored
1
httpd/.gitignore
vendored
|
|
@ -6,3 +6,4 @@
|
|||
*.log
|
||||
*.core
|
||||
httpd
|
||||
__pycache__
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
1
httpd/tests/test_root_dir/index.html
Normal file
1
httpd/tests/test_root_dir/index.html
Normal file
|
|
@ -0,0 +1 @@
|
|||
<h1> YEAAH </h1>
|
||||
132
httpd/tests/test_suite.py
Normal file
132
httpd/tests/test_suite.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue