Compare commits
10 commits
9ecc9ed9a7
...
50891b21f2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50891b21f2 | ||
|
|
221e801307 | ||
|
|
f7af2c3850 | ||
|
|
f0127b00a3 | ||
|
|
286730b70d | ||
|
|
24262bffe7 | ||
|
|
3a320421a9 | ||
|
|
7a614bd0d4 | ||
|
|
eee5f940e3 | ||
|
|
f16bdbdf73 |
22 changed files with 434 additions and 89 deletions
1
httpd/.gitignore
vendored
1
httpd/.gitignore
vendored
|
|
@ -7,3 +7,4 @@
|
||||||
*.core
|
*.core
|
||||||
httpd
|
httpd
|
||||||
__pycache__
|
__pycache__
|
||||||
|
env/
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,14 @@ TARGET=httpd
|
||||||
$(TARGET): $(OBJS)
|
$(TARGET): $(OBJS)
|
||||||
$(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
|
$(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
|
||||||
|
|
||||||
check:
|
check: $(TARGET)
|
||||||
dash tests/run.sh
|
cp $(TARGET) tests/$(TARGET)
|
||||||
|
cd tests
|
||||||
|
python3 -m venv env
|
||||||
|
env/bin/python -m pip install requests
|
||||||
|
env/bin/python -m pip install pytest
|
||||||
|
env/bin/python -m pip install pytest-timeout
|
||||||
|
- env/bin/pytest
|
||||||
|
|
||||||
debug: CFLAGS += $(CFLAGS_DBG)
|
debug: CFLAGS += $(CFLAGS_DBG)
|
||||||
debug: $(OBJS)
|
debug: $(OBJS)
|
||||||
|
|
@ -45,5 +51,7 @@ asan: $(OBJS)
|
||||||
$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LDLIBS)
|
$(CC) -o $(TARGET) $(OBJS) $(LDFLAGS) $(LDLIBS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
- pkill -9 $(TARGET)
|
||||||
|
$(RM) tests/$(TARGET) tests/out.log
|
||||||
$(RM) $(TARGET)
|
$(RM) $(TARGET)
|
||||||
$(RM) $(OBJS)
|
$(RM) $(OBJS)
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../utils/string/string.h"
|
// #include "../utils/string/string.h"
|
||||||
#include "bits/getopt_ext.h"
|
// #include "bits/getopt_ext.h"
|
||||||
|
|
||||||
#define ARG_VALID 0
|
#define ARG_VALID 0
|
||||||
#define ARG_INVALID 1
|
#define ARG_INVALID 1
|
||||||
|
|
@ -180,6 +180,19 @@ static void print_arg_error(int err, char **argv, struct option options[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static void apply_default_values(struct config *cfg)
|
||||||
|
// {
|
||||||
|
// // Default file
|
||||||
|
// if (cfg->servers->default_file == NULL)
|
||||||
|
// {
|
||||||
|
// char *default_df = DEFAULT_DF;
|
||||||
|
// cfg->servers->default_file =
|
||||||
|
// malloc((strlen(default_df) + 1) * sizeof(char));
|
||||||
|
// // TODO handle error
|
||||||
|
// strcpy(cfg->servers->default_file, default_df);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// == Main functions
|
// == Main functions
|
||||||
|
|
||||||
struct config *parse_configuration(int argc, char *argv[])
|
struct config *parse_configuration(int argc, char *argv[])
|
||||||
|
|
@ -227,10 +240,12 @@ struct config *parse_configuration(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// apply_default_values(config);
|
||||||
|
|
||||||
// Check config validity
|
// Check config validity
|
||||||
if (check_config(config) != 0)
|
if (check_config(config) != 0)
|
||||||
{
|
{
|
||||||
printf("%s: Missing mandatory flags, cannot continue.", argv[0]);
|
printf("%s: Missing mandatory flags, cannot continue.\n", argv[0]);
|
||||||
config_destroy(config);
|
config_destroy(config);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,9 @@
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
// Default values
|
||||||
|
#define DEFAULT_DF "index.html"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** @brief Enum daemon
|
** @brief Enum daemon
|
||||||
** NO_OPTION if the '--daemon' option is not given
|
** NO_OPTION if the '--daemon' option is not given
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ void daemon_init(struct config *cfg)
|
||||||
config = cfg;
|
config = cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_pid()
|
int get_pid(void)
|
||||||
{
|
{
|
||||||
FILE *stream = fopen(config->pid_file, "r");
|
FILE *stream = fopen(config->pid_file, "r");
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
|
|
@ -48,7 +48,7 @@ int start_daemon(void)
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (!pid) // Daemon
|
if (!pid) // Daemon
|
||||||
{
|
{
|
||||||
start_server(config->servers->ip, config->servers->port);
|
start_server(config);
|
||||||
}
|
}
|
||||||
else // Parent
|
else // Parent
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
int get_pid();
|
int get_pid(void);
|
||||||
|
|
||||||
/* @brief
|
/* @brief
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,15 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "../utils/parsing/words.h"
|
#include "../utils/parsing/words.h"
|
||||||
|
#include "../utils/string/string.h"
|
||||||
|
|
||||||
void destroy_headers(struct http_header *headers)
|
void destroy_headers(struct http_header *headers)
|
||||||
{
|
{
|
||||||
while (headers != NULL)
|
while (headers != NULL)
|
||||||
{
|
{
|
||||||
struct http_header *next = headers->next;
|
struct http_header *next = headers->next;
|
||||||
|
string_destroy(headers->field);
|
||||||
|
string_destroy(headers->value);
|
||||||
free(headers);
|
free(headers);
|
||||||
headers = next;
|
headers = next;
|
||||||
}
|
}
|
||||||
|
|
@ -39,6 +42,12 @@ ssize_t read_value(struct string *str, size_t offset, struct string **res)
|
||||||
if (str->size <= offset + nread || str->data[offset + nread] != '\n')
|
if (str->size <= offset + nread || str->data[offset + nread] != '\n')
|
||||||
return ERR_HTTP_INVALID_INPUT;
|
return ERR_HTTP_INVALID_INPUT;
|
||||||
|
|
||||||
|
// Trim trailing \r
|
||||||
|
if ((*res)->size > 0 && (*res)->data[(*res)->size - 1] == '\r')
|
||||||
|
{
|
||||||
|
(*res)->size--;
|
||||||
|
}
|
||||||
|
|
||||||
return nread;
|
return nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,7 +59,8 @@ ssize_t parse_headers(struct http_request *res, struct string *req,
|
||||||
|
|
||||||
// Yes I know I do one useless allocation but I really don't care at this
|
// Yes I know I do one useless allocation but I really don't care at this
|
||||||
// point
|
// point
|
||||||
while (req->data[i] != '\n') // ! Blank line
|
while (i < req->size && req->data[i] != '\n'
|
||||||
|
&& req->data[i] != '\r') // ! Blank line
|
||||||
{
|
{
|
||||||
if (header == NULL)
|
if (header == NULL)
|
||||||
{
|
{
|
||||||
|
|
@ -70,21 +80,26 @@ ssize_t parse_headers(struct http_request *res, struct string *req,
|
||||||
return ERR_HTTP_OUT_OF_MEMORY;
|
return ERR_HTTP_OUT_OF_MEMORY;
|
||||||
|
|
||||||
// Read field
|
// Read field
|
||||||
ssize_t nread = read_field(req, offset, &header->field);
|
ssize_t nread = read_field(req, i, &header->field);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
return nread; // Contains error code when negative
|
return nread; // Contains error code when negative
|
||||||
|
|
||||||
i += nread;
|
i += nread + 1;
|
||||||
|
|
||||||
// Read value
|
// Read value
|
||||||
nread = read_value(req, offset, &header->value);
|
nread = read_value(req, i, &header->value);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
return nread; // Contains error code when negative
|
return nread; // Contains error code when negative
|
||||||
|
|
||||||
i += nread + 1;
|
i += nread + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i + 1;
|
if (i < req->size && req->data[i] == '\r')
|
||||||
|
i++;
|
||||||
|
if (i < req->size && req->data[i] == '\n')
|
||||||
|
i++;
|
||||||
|
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct http_header *get_header(struct http_header *headers, const char *field)
|
struct http_header *get_header(struct http_header *headers, const char *field)
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,14 @@
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
// #include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include "../config/config.h"
|
||||||
#include "../logger/logs.h"
|
#include "../logger/logs.h"
|
||||||
#include "../utils/files/files.h"
|
#include "../utils/files/files.h"
|
||||||
#include "../utils/parsing/words.h"
|
#include "../utils/parsing/words.h"
|
||||||
|
|
@ -30,16 +31,16 @@ static ssize_t parse_reqline(struct http_request *res, struct string *req)
|
||||||
ssize_t i = 0;
|
ssize_t i = 0;
|
||||||
ssize_t skipped;
|
ssize_t skipped;
|
||||||
|
|
||||||
if (res == NULL)
|
if (res == NULL || req == NULL)
|
||||||
return ERR_HTTP_INTERNAL_ERROR;
|
return ERR_HTTP_INTERNAL_ERROR;
|
||||||
|
|
||||||
// Method
|
// Method
|
||||||
if (strncmp(req->data, "GET", strlen("GET")) == 0)
|
if (string_compare_n_str(req, "GET", strlen("GET")) == 0)
|
||||||
{
|
{
|
||||||
res->method = GET;
|
res->method = GET;
|
||||||
i += strlen("GET");
|
i += strlen("GET");
|
||||||
}
|
}
|
||||||
else if (strncmp(req->data, "HEAD", strlen("HEAD")) == 0)
|
else if (string_compare_n_str(req, "HEAD", strlen("HEAD")) == 0)
|
||||||
{
|
{
|
||||||
res->method = HEAD;
|
res->method = HEAD;
|
||||||
i += strlen("HEAD");
|
i += strlen("HEAD");
|
||||||
|
|
@ -67,9 +68,15 @@ static ssize_t parse_reqline(struct http_request *res, struct string *req)
|
||||||
return ERR_HTTP_INVALID_INPUT;
|
return ERR_HTTP_INVALID_INPUT;
|
||||||
i += skipped;
|
i += skipped;
|
||||||
|
|
||||||
// CRLF (EOL)
|
// CRLF (EOL) oh qu'il est casse couilles celui-là
|
||||||
if (req->data[i++] != '\r' && req->data[i++] != '\n')
|
ssize_t req_size = req->size; // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah
|
||||||
|
if (i < req_size && req->data[i] == '\r')
|
||||||
|
i++;
|
||||||
|
if (i >= req_size || req->data[i] != '\n')
|
||||||
return ERR_HTTP_INVALID_INPUT;
|
return ERR_HTTP_INVALID_INPUT;
|
||||||
|
i++;
|
||||||
|
// Donc 2h de debug pour ça là ? Plutot envie de me tirer une balle si vous
|
||||||
|
// voulez mon avis
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
@ -91,21 +98,19 @@ static void split_target(struct http_request *req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds a valid path based on the client input
|
// Finds a valid path based on the client input and returns the corresponding
|
||||||
static bool find_target(struct http_request *req)
|
// FILES return value (see files.h)
|
||||||
|
static int find_target(struct http_request *req)
|
||||||
{
|
{
|
||||||
// Check filename
|
// Check filename
|
||||||
if (!check_filename(req->target))
|
if (!check_filename(req->target))
|
||||||
return false;
|
return ERR_FILES_FORBIDDEN;
|
||||||
|
|
||||||
char *target = string_to_charptr(req->target);
|
char *target = string_to_charptr(req->target);
|
||||||
int err = is_directory(target);
|
int err = is_directory(target);
|
||||||
free(target);
|
free(target);
|
||||||
err = 0; // TODO fix that bug
|
|
||||||
|
|
||||||
if (err == -1) // Doesn't exists
|
if (err == FILES_DIR) // Is a directory
|
||||||
return false;
|
|
||||||
else if (err == 1)
|
|
||||||
{
|
{
|
||||||
// Append default file if directory
|
// Append default file if directory
|
||||||
if (req->target->data[req->target->size - 1] != '/')
|
if (req->target->data[req->target->size - 1] != '/')
|
||||||
|
|
@ -117,11 +122,10 @@ static bool find_target(struct http_request *req)
|
||||||
target = string_to_charptr(req->target);
|
target = string_to_charptr(req->target);
|
||||||
err = is_directory(target);
|
err = is_directory(target);
|
||||||
free(target);
|
free(target);
|
||||||
return err == 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exists
|
return err;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WARNING allocates result on the heap
|
// WARNING allocates result on the heap
|
||||||
|
|
@ -179,10 +183,42 @@ static void check_req(struct http_request *req, struct http_response *resp)
|
||||||
resp->status_code = 405;
|
resp->status_code = 405;
|
||||||
|
|
||||||
// Protocol
|
// Protocol
|
||||||
char *protocol = string_to_charptr(req->protocol);
|
// Oui il y a plus beau, mais on a le temps ou on l'a pas, moi je l'ai pas,
|
||||||
if (strcmp(protocol, HTTP_VERSION) != 0)
|
// alors si t'es pas content t'as qu'à le modifier toi même vu que tu as
|
||||||
|
// visiblement le code source. <3
|
||||||
|
if (string_compare_strictly_n_str(req->protocol, "HTTP/", strlen("HTTP/"))
|
||||||
|
!= 0)
|
||||||
|
resp->status_code = 400;
|
||||||
|
else if (string_compare_strictly_n_str(req->protocol, "HTTP/1.1",
|
||||||
|
strlen("HTTP/1.1"))
|
||||||
|
!= 0)
|
||||||
resp->status_code = 505;
|
resp->status_code = 505;
|
||||||
free(protocol);
|
|
||||||
|
// Host
|
||||||
|
if (resp->status_code != 400 && resp->status_code != 505)
|
||||||
|
{
|
||||||
|
int host_count = 0;
|
||||||
|
struct http_header *cur = req->headers;
|
||||||
|
while (cur != NULL)
|
||||||
|
{
|
||||||
|
if (cur->field->size == 4
|
||||||
|
&& string_compare_n_str(cur->field, "host", 4) == 0)
|
||||||
|
{
|
||||||
|
host_count++;
|
||||||
|
if (cur->value == NULL || cur->value->size == 0)
|
||||||
|
{
|
||||||
|
resp->status_code = 400;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host_count != 1)
|
||||||
|
resp->status_code = 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
// printf("%s %d\n", req->protocol->data, resp->status_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// === Functions
|
// === Functions
|
||||||
|
|
@ -210,27 +246,39 @@ void handle_request(int client_fd, char *client_ip)
|
||||||
{
|
{
|
||||||
string_concat_str(str, buffer, nread);
|
string_concat_str(str, buffer, nread);
|
||||||
}
|
}
|
||||||
string_concat_str(str, buffer, nread);
|
if (nread > 0)
|
||||||
|
string_concat_str(str, buffer, nread);
|
||||||
|
|
||||||
// Parse request
|
// Parse request
|
||||||
struct http_request *req = parse_request(str);
|
struct http_request *req = parse_request(str);
|
||||||
if (req == NULL)
|
if (req == NULL)
|
||||||
|
{
|
||||||
|
free(str);
|
||||||
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, client_ip);
|
print_log_request(method, target, client_ip);
|
||||||
free(method);
|
free(method);
|
||||||
free(target);
|
free(target);
|
||||||
|
|
||||||
// Generate response
|
// Generate response
|
||||||
struct http_response *resp = generate_response(req);
|
struct http_response *resp = generate_response(req);
|
||||||
if (resp == NULL)
|
if (resp == NULL)
|
||||||
|
{
|
||||||
|
free(str);
|
||||||
|
free(req);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Format response to string
|
// Format response to string
|
||||||
struct string *res = format_response(resp);
|
struct string *res = format_response(resp);
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
|
{
|
||||||
|
free(str);
|
||||||
|
free(req);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Send response
|
// Send response
|
||||||
ssize_t nsent;
|
ssize_t nsent;
|
||||||
|
|
@ -253,12 +301,15 @@ void handle_request(int client_fd, char *client_ip)
|
||||||
int fd = open(target, O_RDONLY);
|
int fd = open(target, O_RDONLY);
|
||||||
if (fd > 0)
|
if (fd > 0)
|
||||||
sendfile(client_fd, fd, 0, atoi(cl_str));
|
sendfile(client_fd, fd, 0, atoi(cl_str));
|
||||||
|
|
||||||
|
free(target);
|
||||||
|
free(cl_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log response
|
// Log response
|
||||||
method = get_http_method(req->method);
|
method = get_http_method(req->method);
|
||||||
target = string_to_charptr(req->target);
|
target = string_to_charptr(req->target);
|
||||||
log_response(resp->status_code, method, target, client_ip);
|
print_log_response(resp->status_code, method, target, client_ip);
|
||||||
free(method);
|
free(method);
|
||||||
free(target);
|
free(target);
|
||||||
|
|
||||||
|
|
@ -280,7 +331,19 @@ struct http_request *parse_request(struct string *req)
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
ssize_t nread = parse_reqline(res, req);
|
ssize_t nread = parse_reqline(res, req);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
return NULL;
|
{
|
||||||
|
if (nread == ERR_HTTP_NOT_IMPLEMENTED)
|
||||||
|
res->status_code = 501;
|
||||||
|
else
|
||||||
|
res->status_code = 400;
|
||||||
|
|
||||||
|
if (res->target == NULL)
|
||||||
|
res->target = string_create("", 0);
|
||||||
|
if (res->protocol == NULL)
|
||||||
|
res->protocol = string_create("HTTP/1.1", 8);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
// Split path and query
|
// Split path and query
|
||||||
split_target(res);
|
split_target(res);
|
||||||
|
|
@ -290,7 +353,10 @@ struct http_request *parse_request(struct string *req)
|
||||||
// Headers
|
// Headers
|
||||||
nread = parse_headers(res, req, i);
|
nread = parse_headers(res, req, i);
|
||||||
if (nread <= 0)
|
if (nread <= 0)
|
||||||
return NULL;
|
{
|
||||||
|
res->status_code = 400;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -304,23 +370,39 @@ struct http_response *generate_response(struct http_request *req)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// Protocol
|
// Protocol
|
||||||
char *protocol = HTTP_VERSION;
|
// char *protocol = HTTP_VERSION;
|
||||||
|
char *protocol = "HTTP/1.1";
|
||||||
res->protocol = string_create(protocol, strlen(protocol));
|
res->protocol = string_create(protocol, strlen(protocol));
|
||||||
|
|
||||||
// Target
|
// Target
|
||||||
str_concat_string(config->servers->root_dir,
|
if (req->status_code == 0)
|
||||||
strlen(config->servers->root_dir), req->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)
|
||||||
{
|
{
|
||||||
if (!find_target(req))
|
switch (find_target(req))
|
||||||
res->status_code = 404;
|
{
|
||||||
else
|
case FILES_REG:
|
||||||
res->status_code = 200;
|
res->status_code = 200;
|
||||||
|
break;
|
||||||
|
case ERR_FILES_NOT_FOUND:
|
||||||
|
res->status_code = 404;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res->status_code = 403;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
res->status_code = req->status_code;
|
||||||
|
|
||||||
check_req(req, res);
|
// Check protocol and method
|
||||||
|
if (req->status_code == 0)
|
||||||
|
check_req(req, res);
|
||||||
|
|
||||||
// Headers
|
// Headers
|
||||||
char *time = get_time();
|
char *time = get_time();
|
||||||
|
|
@ -339,10 +421,13 @@ struct http_response *generate_response(struct http_request *req)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
res->status_code =
|
res->status_code = 403;
|
||||||
404; // TODO replace by 403 once find_target is fixed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
append_header(&res->headers, create_header("Content-Length", "0"));
|
||||||
|
}
|
||||||
append_header(&res->headers, create_header("Connection", "close"));
|
append_header(&res->headers, create_header("Connection", "close"));
|
||||||
|
|
||||||
// Status msg
|
// Status msg
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ void http_init(struct config *cfg);
|
||||||
*
|
*
|
||||||
* @param client_fd
|
* @param client_fd
|
||||||
*/
|
*/
|
||||||
void handle_request(int client_fd, char* client_ip);
|
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
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../utils/time/fmt_time.h"
|
#include "../utils/time/fmt_time.h"
|
||||||
#include "logs.h"
|
#include "logs.h"
|
||||||
|
|
@ -16,13 +17,16 @@ static struct logs_config config;
|
||||||
|
|
||||||
// === Functions
|
// === Functions
|
||||||
|
|
||||||
void errlog_init(bool enabled, int logfile_fd)
|
void errlog_init(bool enabled, int logfile_fd, struct server_config *serv_cfg)
|
||||||
{
|
{
|
||||||
config.enabled = enabled;
|
config.enabled = enabled;
|
||||||
|
if (logfile_fd == STDOUT_FILENO)
|
||||||
|
config.logfile_fd = STDERR_FILENO;
|
||||||
config.logfile_fd = logfile_fd;
|
config.logfile_fd = logfile_fd;
|
||||||
|
config.server_cfg = serv_cfg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void print_err()
|
void print_err(void)
|
||||||
{
|
{
|
||||||
print_log_err("%s", get_err());
|
print_log_err("%s", get_err());
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +55,7 @@ void print_log_err(char *format, ...)
|
||||||
fprintf(stderr, "Error: %s", get_err());
|
fprintf(stderr, "Error: %s", get_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
char *get_err()
|
char *get_err(void)
|
||||||
{
|
{
|
||||||
return strerror(errno);
|
return strerror(errno);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,22 +4,24 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "../config/config.h"
|
||||||
|
|
||||||
/* @brief Initialize the error logging submodule
|
/* @brief Initialize the error logging submodule
|
||||||
* @warning Do not use 'as is', use log_init() instead
|
* @warning Do not use 'as is', use log_init() instead
|
||||||
*/
|
*/
|
||||||
void errlog_init(bool enabled, int logfile_fd);
|
void errlog_init(bool enabled, int logfile_fd, struct server_config *serv_cfg);
|
||||||
|
|
||||||
/* @brief Retrieves the last error with errno and prints the corresponding
|
/* @brief Retrieves the last error with errno and prints the corresponding
|
||||||
* error message in the logs and stderr
|
* error message in the logs and stderr
|
||||||
*/
|
*/
|
||||||
void print_err();
|
void print_err(void);
|
||||||
|
|
||||||
/* @brief Prints error logs, just like print_log(), and also to stderr
|
/* @brief Prints error logs, just like print_log() but for errors
|
||||||
*/
|
*/
|
||||||
void print_log_err(char *format, ...);
|
void print_log_err(char *format, ...);
|
||||||
|
|
||||||
/* @brief Prints error logs, just like print_log()
|
/* @brief Returns the string corresponding to the last error that happened
|
||||||
*/
|
*/
|
||||||
char *get_err();
|
char *get_err(void);
|
||||||
|
|
||||||
#endif // ! ERRORS_H
|
#endif // ! ERRORS_H
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../utils/time/fmt_time.h"
|
#include "../utils/time/fmt_time.h"
|
||||||
#include "errors.h"
|
#include "errors.h"
|
||||||
|
|
@ -20,13 +21,21 @@ int log_init(struct config *global_config)
|
||||||
int return_value = 0;
|
int return_value = 0;
|
||||||
config.enabled = global_config->log;
|
config.enabled = global_config->log;
|
||||||
config.server_cfg = global_config->servers;
|
config.server_cfg = global_config->servers;
|
||||||
config.logfile_fd = open(global_config->log_file, O_WRONLY);
|
if (global_config->log_file != NULL)
|
||||||
if (config.logfile_fd <= 0)
|
|
||||||
{
|
{
|
||||||
config.enabled = false;
|
config.logfile_fd = open(global_config->log_file, O_WRONLY);
|
||||||
return_value = 1;
|
if (config.logfile_fd <= 0)
|
||||||
|
{
|
||||||
|
config.enabled = false;
|
||||||
|
return_value = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
errlog_init(config.enabled, config.logfile_fd);
|
else
|
||||||
|
{
|
||||||
|
config.logfile_fd = STDOUT_FILENO;
|
||||||
|
}
|
||||||
|
|
||||||
|
errlog_init(config.enabled, config.logfile_fd, config.server_cfg);
|
||||||
return return_value;
|
return return_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,14 +60,20 @@ void print_log(char *format, ...)
|
||||||
dprintf(config.logfile_fd, "\n");
|
dprintf(config.logfile_fd, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_request(char *request_type, char *target, char *client_ip)
|
void print_log_request(char *request_type, char *target, char *client_ip)
|
||||||
{
|
{
|
||||||
print_log("received %s, on '%s' from %s", 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,
|
void print_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'", status_code, client_ip,
|
print_log("responding with %d to %s for %s on '%s'", status_code, client_ip,
|
||||||
request_type, target);
|
request_type, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void log_terminate(void)
|
||||||
|
{
|
||||||
|
if (config.logfile_fd != 0 && config.logfile_fd != STDOUT_FILENO)
|
||||||
|
close(config.logfile_fd);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ void print_log(char *format, ...);
|
||||||
* @param target
|
* @param target
|
||||||
* @param client_ip
|
* @param client_ip
|
||||||
*/
|
*/
|
||||||
void log_request(char *request_type, char *target, char *client_ip);
|
void print_log_request(char *request_type, char *target, char *client_ip);
|
||||||
|
|
||||||
/* @brief Prints response logs with the adequate format in the logfile
|
/* @brief Prints response logs with the adequate format in the logfile
|
||||||
*
|
*
|
||||||
|
|
@ -44,7 +44,11 @@ void log_request(char *request_type, char *target, char *client_ip);
|
||||||
* @param target
|
* @param target
|
||||||
* @param client_ip
|
* @param client_ip
|
||||||
*/
|
*/
|
||||||
void log_response(int status_code, char *request_type, char *target,
|
void print_log_response(int status_code, char *request_type, char *target,
|
||||||
char *client_ip);
|
char *client_ip);
|
||||||
|
|
||||||
|
/* @brief Gracefully exits the logs module
|
||||||
|
*/
|
||||||
|
void log_terminate(void);
|
||||||
|
|
||||||
#endif // ! LOGS_H
|
#endif // ! LOGS_H
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ int main(int argc, char **argv)
|
||||||
switch (config->daemon)
|
switch (config->daemon)
|
||||||
{
|
{
|
||||||
case NO_OPTION:
|
case NO_OPTION:
|
||||||
start_server(config->servers->ip, config->servers->port);
|
start_server(config);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case START:
|
case START:
|
||||||
|
|
@ -43,5 +43,6 @@ int main(int argc, char **argv)
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config_destroy(config);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,31 @@
|
||||||
// === Definitions
|
// === Definitions
|
||||||
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#define _POSIX_C_SOURCE 200112L
|
#define _POSIX_C_SOURCE 200112L
|
||||||
|
|
||||||
#define BUFFER_SIZE 1024
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
// === Includes
|
// === Includes
|
||||||
|
#include "server.h"
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../http/http.h"
|
#include "../http/http.h"
|
||||||
#include "../logger/errors.h"
|
#include "../logger/errors.h"
|
||||||
#include "server.h"
|
#include "../logger/logs.h"
|
||||||
// #include "../logger/logs.h"
|
// #include "../logger/logs.h"
|
||||||
|
|
||||||
|
// === Static variables
|
||||||
|
|
||||||
|
static int server_socket = 0;
|
||||||
|
static struct config *config;
|
||||||
|
|
||||||
// === Static functions
|
// === Static functions
|
||||||
|
|
||||||
// Creates and bind the server communication socket
|
// Creates and bind the server communication socket
|
||||||
|
|
@ -102,11 +110,31 @@ static void get_ip(struct sockaddr *client_addr, char *res)
|
||||||
inet_ntop(AF_INET, &ipAddr, res, INET_ADDRSTRLEN);
|
inet_ntop(AF_INET, &ipAddr, res, INET_ADDRSTRLEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void signal_handler(int signal)
|
||||||
|
{
|
||||||
|
print_log("EVENT Signal received: %d", signal);
|
||||||
|
switch (signal)
|
||||||
|
{
|
||||||
|
case SIGINT: {
|
||||||
|
print_log("STOPPING Stopping server...");
|
||||||
|
stop_server();
|
||||||
|
config_destroy(config);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// === Functions
|
// === Functions
|
||||||
|
|
||||||
void start_server(const char *host, const char *port)
|
void start_server(struct config *cfg)
|
||||||
{
|
{
|
||||||
int server_socket = get_socket(host, port);
|
config = cfg;
|
||||||
|
const char *host = config->servers->ip;
|
||||||
|
const char *port = config->servers->port;
|
||||||
|
|
||||||
|
server_socket = get_socket(host, port);
|
||||||
if (server_socket == -1)
|
if (server_socket == -1)
|
||||||
// TODO log that
|
// TODO log that
|
||||||
return;
|
return;
|
||||||
|
|
@ -115,11 +143,24 @@ void start_server(const char *host, const char *port)
|
||||||
if (err == -1)
|
if (err == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Signal handling
|
||||||
|
struct sigaction siga;
|
||||||
|
siga.sa_flags = 0;
|
||||||
|
siga.sa_handler = signal_handler;
|
||||||
|
// initialize mask
|
||||||
|
if (sigemptyset(&siga.sa_mask) < 0)
|
||||||
|
// TODO log that
|
||||||
|
return;
|
||||||
|
if (sigaction(SIGINT, &siga, NULL) == -1
|
||||||
|
|| sigaction(SIGPIPE, &siga, NULL) == -1)
|
||||||
|
return;
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
struct sockaddr client_addr;
|
struct sockaddr client_addr;
|
||||||
int client_fd = accept(server_socket, &client_addr, NULL);
|
socklen_t client_addr_len = sizeof(struct sockaddr);
|
||||||
|
int client_fd = accept(server_socket, &client_addr, &client_addr_len);
|
||||||
if (client_fd == -1)
|
if (client_fd == -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|
@ -134,5 +175,10 @@ void start_server(const char *host, const char *port)
|
||||||
close(client_fd);
|
close(client_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stop_server();
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_server(void)
|
||||||
|
{
|
||||||
close(server_socket);
|
close(server_socket);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
#ifndef SERVER_H
|
#ifndef SERVER_H
|
||||||
#define SERVER_H
|
#define SERVER_H
|
||||||
|
|
||||||
// #define _POSIX_C_SOURCE 200112L
|
#include "../config/config.h"
|
||||||
|
|
||||||
/* @brief
|
/* @brief Starts the HTTP server
|
||||||
*
|
*
|
||||||
* @param hpstname
|
* @warn Make sure to initialize modules before calling
|
||||||
|
*
|
||||||
|
* @param hostname
|
||||||
* @param port
|
* @param port
|
||||||
*/
|
*/
|
||||||
void start_server(const char *host, const char *port);
|
void start_server(struct config *cfg);
|
||||||
|
|
||||||
|
/* @brief Stops the currently running HTTP server
|
||||||
|
*/
|
||||||
|
void stop_server(void);
|
||||||
|
|
||||||
#endif // ! SERVER_H
|
#endif // ! SERVER_H
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "files.h"
|
#include "files.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
|
@ -13,10 +14,22 @@
|
||||||
int is_directory(const char *path)
|
int is_directory(const char *path)
|
||||||
{
|
{
|
||||||
struct stat path_stat;
|
struct stat path_stat;
|
||||||
if (lstat(path, &path_stat) != 0)
|
if (lstat(path, &path_stat) == 0)
|
||||||
return S_ISDIR(path_stat.st_mode);
|
{
|
||||||
|
if (S_ISDIR(path_stat.st_mode)) // Directory
|
||||||
|
return FILES_DIR;
|
||||||
|
else if (S_ISREG(path_stat.st_mode)) // Regular file
|
||||||
|
return FILES_REG;
|
||||||
|
else
|
||||||
|
return FILES_OTHER;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return -1;
|
{
|
||||||
|
if (errno == ENOENT) // File not found
|
||||||
|
return ERR_FILES_NOT_FOUND;
|
||||||
|
else // Other errors
|
||||||
|
return ERR_FILES_FORBIDDEN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle logging
|
// TODO handle logging
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,15 @@
|
||||||
|
|
||||||
#define BUFFER_SIZE 1024
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
// Return codes
|
||||||
|
#define FILES_REG 0
|
||||||
|
#define FILES_DIR 1
|
||||||
|
#define FILES_OTHER 8
|
||||||
|
|
||||||
|
// Errors
|
||||||
|
#define ERR_FILES_NOT_FOUND -1
|
||||||
|
#define ERR_FILES_FORBIDDEN -2
|
||||||
|
|
||||||
// === Includes
|
// === Includes
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
@ -29,7 +38,8 @@
|
||||||
*
|
*
|
||||||
* @param path
|
* @param path
|
||||||
*
|
*
|
||||||
* @return 1 if path is a directory, 0 if it is not and -1 if path is not valid
|
* @return Returns the corresponding return value / error code (see header
|
||||||
|
* definitions)
|
||||||
*/
|
*/
|
||||||
int is_directory(const char *path);
|
int is_directory(const char *path);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@ void str_concat_string(const char *str, size_t size, struct string *to_concat)
|
||||||
|
|
||||||
memcpy(to_concat->data, str, size);
|
memcpy(to_concat->data, str, size);
|
||||||
memcpy(to_concat->data + size, tmp, tmp_size);
|
memcpy(to_concat->data + size, tmp, tmp_size);
|
||||||
|
free(tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,3 +154,22 @@ char *string_to_charptr(struct string *str)
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WARNING takes n as valid, will not stop on '\0'
|
||||||
|
int string_compare_strictly_n_str(const struct string *str1, const char *str2,
|
||||||
|
size_t n)
|
||||||
|
{
|
||||||
|
if (str1->size < n)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
int res = 0;
|
||||||
|
while (i < n)
|
||||||
|
{
|
||||||
|
res += str1->data[i];
|
||||||
|
res -= str2[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,4 +75,15 @@ void string_destroy(struct string *str);
|
||||||
*/
|
*/
|
||||||
char *string_to_charptr(struct string *str);
|
char *string_to_charptr(struct string *str);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** @brief TODO
|
||||||
|
**
|
||||||
|
** @param str1
|
||||||
|
** @param str2
|
||||||
|
** @param n
|
||||||
|
**
|
||||||
|
** @return
|
||||||
|
*/
|
||||||
|
int string_compare_strictly_n_str(const struct string *str1, const char *str2,
|
||||||
|
size_t n);
|
||||||
#endif /* ! STRING_H */
|
#endif /* ! STRING_H */
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ executable = "./httpd"
|
||||||
|
|
||||||
def spawn_httpd(stdout_filename, args=[]):
|
def spawn_httpd(stdout_filename, args=[]):
|
||||||
with open(stdout_filename,"w") as f:
|
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)
|
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)
|
time.sleep(0.2)
|
||||||
|
|
||||||
return httpd_proc
|
return httpd_proc
|
||||||
|
|
@ -21,7 +21,7 @@ def kill_httpd(proc):
|
||||||
#proc.send_signal(sp.SIGINT)
|
#proc.send_signal(sp.SIGINT)
|
||||||
proc.kill()
|
proc.kill()
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_bad_config():
|
def test_bad_config():
|
||||||
proc = spawn_httpd("out.log", ["hello","world"])
|
proc = spawn_httpd("out.log", ["hello","world"])
|
||||||
proc.wait(1)
|
proc.wait(1)
|
||||||
|
|
@ -30,7 +30,7 @@ def test_bad_config():
|
||||||
finally:
|
finally:
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_get_index():
|
def test_get_index():
|
||||||
proc = spawn_httpd("out.log")
|
proc = spawn_httpd("out.log")
|
||||||
req = requests.get(f"http://{host}:{port}/index.html")
|
req = requests.get(f"http://{host}:{port}/index.html")
|
||||||
|
|
@ -41,7 +41,7 @@ def test_get_index():
|
||||||
finally:
|
finally:
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_get_default():
|
def test_get_default():
|
||||||
proc = spawn_httpd("out.log")
|
proc = spawn_httpd("out.log")
|
||||||
req = requests.get(f"http://{host}:{port}/")
|
req = requests.get(f"http://{host}:{port}/")
|
||||||
|
|
@ -52,13 +52,13 @@ def test_get_default():
|
||||||
finally:
|
finally:
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_no_file():
|
def test_no_file():
|
||||||
proc = spawn_httpd("out.log")
|
proc = spawn_httpd("out.log")
|
||||||
req = requests.get(f"http://{host}:{port}/notindex.html")
|
req = requests.get(f"http://{host}:{port}/notindex.html")
|
||||||
assert req.status_code == 404
|
assert req.status_code == 404
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_bad_request():
|
def test_bad_request():
|
||||||
proc = spawn_httpd("out.log")
|
proc = spawn_httpd("out.log")
|
||||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
|
@ -76,7 +76,7 @@ def test_bad_request():
|
||||||
finally:
|
finally:
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_invalid_method():
|
def test_invalid_method():
|
||||||
proc = spawn_httpd("out.log")
|
proc = spawn_httpd("out.log")
|
||||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
|
@ -95,7 +95,7 @@ def test_invalid_method():
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_invalid_version():
|
def test_invalid_version():
|
||||||
proc = spawn_httpd("out.log")
|
proc = spawn_httpd("out.log")
|
||||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
|
@ -113,7 +113,7 @@ def test_invalid_version():
|
||||||
finally:
|
finally:
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
@pytest.mark.timeout(2)
|
# @pytest.mark.timeout(2)
|
||||||
def test_bad_request():
|
def test_bad_request():
|
||||||
proc = spawn_httpd("out.log")
|
proc = spawn_httpd("out.log")
|
||||||
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
|
@ -130,3 +130,49 @@ def test_bad_request():
|
||||||
assert response.status == 400
|
assert response.status == 400
|
||||||
finally:
|
finally:
|
||||||
kill_httpd(proc)
|
kill_httpd(proc)
|
||||||
|
|
||||||
|
@pytest.mark.timeout(2)
|
||||||
|
def test_head_index():
|
||||||
|
proc = spawn_httpd("out.log")
|
||||||
|
try:
|
||||||
|
req = requests.head(f"http://{host}:{port}/index.html")
|
||||||
|
assert req.status_code == 200
|
||||||
|
assert req.text == ""
|
||||||
|
finally:
|
||||||
|
kill_httpd(proc)
|
||||||
|
|
||||||
|
@pytest.mark.timeout(2)
|
||||||
|
def test_missing_host():
|
||||||
|
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.1\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)
|
||||||
|
|
||||||
|
@pytest.mark.timeout(2)
|
||||||
|
def test_directory_traversal():
|
||||||
|
proc = spawn_httpd("out.log")
|
||||||
|
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
|
||||||
|
sock.connect((host,int(port)))
|
||||||
|
|
||||||
|
request = f"GET /../test_suite.py 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 in [400, 403, 404]
|
||||||
|
finally:
|
||||||
|
kill_httpd(proc)
|
||||||
|
|
|
||||||
40
httpd/tests/tests_mieux.sh
Executable file
40
httpd/tests/tests_mieux.sh
Executable file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# Simple test script for HTTP/1.1 Host header compliance
|
||||||
|
# Usage: ./test_host_compliance.sh [IP] [PORT]
|
||||||
|
|
||||||
|
IP=${1:-"127.0.0.1"}
|
||||||
|
PORT=${2:-"6996"}
|
||||||
|
|
||||||
|
echo "Targeting server at $IP:$PORT"
|
||||||
|
|
||||||
|
test_req() {
|
||||||
|
NAME="$1"
|
||||||
|
PAYLOAD="$2"
|
||||||
|
EXPECTED="$3"
|
||||||
|
|
||||||
|
echo -n "Test: $NAME ... "
|
||||||
|
# Send payload, wait max 1s for response
|
||||||
|
RESP=$(printf "$PAYLOAD" | nc -w 1 $IP $PORT 2>/dev/null | head -n 1)
|
||||||
|
|
||||||
|
if echo "$RESP" | grep -q "$EXPECTED"; then
|
||||||
|
echo "PASS"
|
||||||
|
else
|
||||||
|
echo "FAIL (Expected '$EXPECTED', got '$RESP')"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 1. Valid Request
|
||||||
|
test_req "Valid Request" "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n" "200 OK"
|
||||||
|
|
||||||
|
# 2. Missing Host Header
|
||||||
|
test_req "Missing Host" "GET / HTTP/1.1\r\n\r\n" "400 Bad Request"
|
||||||
|
|
||||||
|
# 3. Empty Host Header
|
||||||
|
test_req "Empty Host" "GET / HTTP/1.1\r\nHost:\r\n\r\n" "400 Bad Request"
|
||||||
|
|
||||||
|
# 4. Multiple Host Headers
|
||||||
|
test_req "Multiple Hosts" "GET / HTTP/1.1\r\nHost: a\r\nHost: b\r\n\r\n" "400 Bad Request"
|
||||||
|
|
||||||
|
# 5. Bad Protocol Version (Should be 505 now with the fix)
|
||||||
|
test_req "Bad Protocol (HTTP/1.0)" "GET / HTTP/1.0\r\nHost: localhost\r\n\r\n" "505"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue