Compare commits

..

No commits in common. "master" and "dementor3-httpd-2028-1" have entirely different histories.

37 changed files with 318 additions and 985 deletions

View file

@ -6,5 +6,3 @@
*.log *.log
*.core *.core
httpd httpd
__pycache__
env/

View file

@ -31,14 +31,8 @@ TARGET=httpd
$(TARGET): $(OBJS) $(TARGET): $(OBJS)
$(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS) $(CC) -o $@ $(OBJS) $(LDFLAGS) $(LDLIBS)
check: $(TARGET) check:
cp $(TARGET) tests/$(TARGET) dash tests/run.sh
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)
@ -51,7 +45,5 @@ 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)

View file

@ -1,7 +1,6 @@
[global] [global]
log = true log = true
pid_file = /tmp/HTTPd.pid pid_file = /tmp/HTTPd.pid
daemon = start
[[vhosts]] [[vhosts]]
server_name = my_server server_name = my_server

View file

@ -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,19 +180,6 @@ 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[])
@ -240,12 +227,10 @@ 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.\n", argv[0]); printf("%s: Missing mandatory flags, cannot continue.", argv[0]);
config_destroy(config); config_destroy(config);
return NULL; return NULL;
} }

View file

@ -6,9 +6,6 @@
#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
@ -36,7 +33,7 @@ struct config
char *pid_file; char *pid_file;
char *log_file; char *log_file;
bool log; bool log;
char *protocol_version; char* protocol_version;
struct server_config *servers; struct server_config *servers;
enum daemon daemon; enum daemon daemon;

View file

@ -3,10 +3,8 @@
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h>
#include "../server/server.h" #include "../server/server.h"
#include "../utils/files/files.h"
static struct config *config; static struct config *config;
@ -17,7 +15,7 @@ void daemon_init(struct config *cfg)
config = cfg; config = cfg;
} }
int get_pid(void) int get_pid()
{ {
FILE *stream = fopen(config->pid_file, "r"); FILE *stream = fopen(config->pid_file, "r");
if (stream == NULL) if (stream == NULL)
@ -45,21 +43,7 @@ void stop_daemon(void)
int start_daemon(void) int start_daemon(void)
{ {
pid_t pid = fork(); start_server("localhost", config->servers->port);
if (!pid) // Daemon
{
start_server(config);
}
else // Parent
{
// Write pid
int err = write_pid(config->pid_file, pid);
if (err != 0)
{
kill(pid, SIGINT);
return 1;
}
}
return 0; return 0;
} }

View file

@ -7,11 +7,11 @@
* *
* @return * @return
*/ */
int get_pid(void); int get_pid();
/* @brief /* @brief
*/ */
void daemon_init(struct config *cfg); void daemon_init(struct config* cfg);
/* @brief /* @brief
*/ */

View file

@ -5,15 +5,12 @@
#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;
} }
@ -42,12 +39,6 @@ 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;
} }
@ -59,8 +50,7 @@ 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 (i < req->size && req->data[i] != '\n' while (req->data[i] != '\n') // ! Blank line
&& req->data[i] != '\r') // ! Blank line
{ {
if (header == NULL) if (header == NULL)
{ {
@ -80,26 +70,21 @@ 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, i, &header->field); ssize_t nread = read_field(req, offset, &header->field);
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;
// Read value // Read value
nread = read_value(req, i, &header->value); nread = read_value(req, offset, &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;
} }
if (i < req->size && req->data[i] == '\r') return i + 1;
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)

View file

@ -57,7 +57,7 @@ ssize_t parse_headers(struct http_request *res, struct string *req,
* *
* @return * @return
*/ */
struct http_header *get_header(struct http_header *headers, const char *field); struct http_header* get_header(struct http_header *headers, const char* field);
/* /*
* @brief * @brief
@ -67,7 +67,7 @@ struct http_header *get_header(struct http_header *headers, const char *field);
* *
* @return * @return
*/ */
struct http_header *create_header(const char *field, const char *value); struct http_header* create_header(const char* field, const char* value);
/* /*
* @brief * @brief

View file

@ -2,14 +2,13 @@
#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"
@ -31,16 +30,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 || req == NULL) if (res == NULL)
return ERR_HTTP_INTERNAL_ERROR; return ERR_HTTP_INTERNAL_ERROR;
// Method // Method
if (string_compare_n_str(req, "GET", strlen("GET")) == 0) if (strncmp(req->data, "GET", strlen("GET")) == 0)
{ {
res->method = GET; res->method = GET;
i += strlen("GET"); i += strlen("GET");
} }
else if (string_compare_n_str(req, "HEAD", strlen("HEAD")) == 0) else if (strncmp(req->data, "HEAD", strlen("HEAD")) == 0)
{ {
res->method = HEAD; res->method = HEAD;
i += strlen("HEAD"); i += strlen("HEAD");
@ -68,15 +67,9 @@ 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) oh qu'il est casse couilles celui-là // CRLF (EOL)
ssize_t req_size = req->size; // aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah if (req->data[i++] != '\r' && req->data[i++] != '\n')
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;
} }
@ -98,34 +91,25 @@ static void split_target(struct http_request *req)
} }
} }
// Finds a valid path based on the client input and returns the corresponding // Finds a valid path based on the client input
// FILES return value (see files.h) static bool find_target(struct http_request *req)
static int find_target(struct http_request *req)
{ {
// Check filename // Check filename
if (!check_filename(req->target)) if (!check_filename(req->target))
return ERR_FILES_FORBIDDEN; return false;
int err = is_directory(req->target->data);
char *target = string_to_charptr(req->target); if (err == -1)
int err = is_directory(target); return false;
free(target); else if (err == 1)
if (err == FILES_DIR) // Is a directory
{ {
// 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;
} }
return err; return true;
} }
// WARNING allocates result on the heap // WARNING allocates result on the heap
@ -176,51 +160,6 @@ 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
// Oui il y a plus beau, mais on a le temps ou on l'a pas, moi je l'ai pas,
// 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;
// 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
void http_init(struct config *cfg) void http_init(struct config *cfg)
@ -229,7 +168,7 @@ void http_init(struct config *cfg)
} }
// TODO handle logs // TODO handle logs
void handle_request(int client_fd, char *client_ip) void handle_request(int client_fd)
{ {
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);
@ -246,39 +185,27 @@ void handle_request(int client_fd, char *client_ip)
{ {
string_concat_str(str, buffer, nread); string_concat_str(str, buffer, nread);
} }
if (nread > 0) string_concat_str(str, buffer, nread);
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);
print_log_request(method, target, client_ip); log_request(method, target, "127.0.0.1");
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;
@ -301,18 +228,8 @@ 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
method = get_http_method(req->method);
target = string_to_charptr(req->target);
print_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);
@ -331,19 +248,7 @@ 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);
@ -353,10 +258,7 @@ 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;
} }
@ -370,69 +272,35 @@ 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
if (req->status_code == 0)
{
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)
{ {
switch (find_target(req)) if (!find_target(req))
{
case FILES_REG:
res->status_code = 200;
break;
case ERR_FILES_NOT_FOUND:
res->status_code = 404; res->status_code = 404;
break; else
default: res->status_code = 200;
res->status_code = 403;
break;
}
} }
else
res->status_code = req->status_code;
// Check protocol and method // Status msg
if (req->status_code == 0) res->status_msg = generate_status_message(res->status_code);
check_req(req, res);
// 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 }; // (20 ~= log10(2^64)) + 1 (null byte) char buf[21] = { 0 }; // (21 ~= log10(2^64)) + 1 (null byte)
char *target = string_to_charptr(req->target); char *target = string_to_charptr(req->target);
ssize_t cl = get_file_content_size(target); sprintf(buf, "%lu", get_file_content_size(target));
free(target); append_header(&res->headers, create_header("Content-Length", buf));
if (cl >= 0)
{
sprintf(buf, "%lu", cl);
append_header(&res->headers, create_header("Content-Length", buf));
}
else
{
res->status_code = 403;
}
}
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
res->status_msg = generate_status_message(res->status_code);
return res; return res;
} }
@ -458,6 +326,10 @@ struct string *format_response(struct http_response *resp)
string_concat_str(res, "\r\n", 2); string_concat_str(res, "\r\n", 2);
// Time
char *time = get_time();
string_concat_str(res, time, strlen(time));
// Headers // Headers
struct http_header *cur_header = resp->headers; struct http_header *cur_header = resp->headers;
while (cur_header != NULL) while (cur_header != NULL)
@ -465,11 +337,6 @@ struct string *format_response(struct http_response *resp)
string_concat_str(res, cur_header->field->data, string_concat_str(res, cur_header->field->data,
cur_header->field->size); cur_header->field->size);
string_concat_str(res, ": ", 2);
string_concat_str(res, cur_header->value->data,
cur_header->value->size);
string_concat_str(res, "\r\n", 2); string_concat_str(res, "\r\n", 2);
cur_header = cur_header->next; cur_header = cur_header->next;

View file

@ -13,14 +13,13 @@
// === Includes // === Includes
#include "../config/config.h"
#include "../utils/string/string.h" #include "../utils/string/string.h"
#include "../config/config.h"
// === Enums // === Enums
enum http_method enum http_method
{ {
INVALID_METHOD,
GET, GET,
// POST, // POST,
// PUT, // PUT,
@ -67,13 +66,13 @@ struct http_response
* *
* @param cfg * @param cfg
*/ */
void http_init(struct config *cfg); void http_init(struct config* cfg);
/* @brief Reads, parses the request and responds adequately all-in-one /* @brief Reads, parses the request and responds adequately all-in-one
* *
* @param client_fd * @param client_fd
*/ */
void handle_request(int client_fd, char *client_ip); void handle_request(int client_fd);
/* @brief Parses the HTTP request and splits it into a request structure /* @brief Parses the HTTP request and splits it into a request structure
* *
@ -82,7 +81,7 @@ void handle_request(int client_fd, char *client_ip);
* @return A pointer to the structure containing the request infos on success, * @return A pointer to the structure containing the request infos on success,
* NULL otherwise * NULL otherwise
*/ */
struct http_request *parse_request(struct string *req); struct http_request* parse_request(struct string* req);
/* @brief Generates a response to the given request /* @brief Generates a response to the given request
* *
@ -91,7 +90,7 @@ struct http_request *parse_request(struct string *req);
* @return A pointer to the generated response struct on success, * @return A pointer to the generated response struct on success,
* NULL otherwise * NULL otherwise
*/ */
struct http_response *generate_response(struct http_request *req); struct http_response* generate_response(struct http_request* req);
/* @brief Formats the given response structure into a valid HTTP response /* @brief Formats the given response structure into a valid HTTP response
* string * string
@ -101,7 +100,7 @@ struct http_response *generate_response(struct http_request *req);
* @return A pointer to the string containing the response on success, * @return A pointer to the string containing the response on success,
* NULL otherwise * NULL otherwise
*/ */
struct string *format_response(struct http_response *resp); struct string* format_response(struct http_response* resp);
/* @brief Free all allocated memory inside req and req itself /* @brief Free all allocated memory inside req and req itself
* *

View file

@ -4,9 +4,7 @@
#include <errno.h> #include <errno.h>
#include <stdarg.h> #include <stdarg.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"
@ -17,16 +15,13 @@ static struct logs_config config;
// === Functions // === Functions
void errlog_init(bool enabled, int logfile_fd, struct server_config *serv_cfg) void errlog_init(bool enabled, int logfile_fd)
{ {
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) void print_err()
{ {
print_log_err("%s", get_err()); print_log_err("%s", get_err());
} }
@ -37,10 +32,8 @@ void print_log_err(char *format, ...)
return; return;
// Log prefix (time and server name) // Log prefix (time and server name)
char *time = get_time(); dprintf(config.logfile_fd, "%s [%s] ", 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;
@ -55,7 +48,7 @@ void print_log_err(char *format, ...)
fprintf(stderr, "Error: %s", get_err()); fprintf(stderr, "Error: %s", get_err());
} }
char *get_err(void) char *get_err()
{ {
return strerror(errno); return strerror(errno);
} }

View file

@ -4,24 +4,22 @@
#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, struct server_config *serv_cfg); void errlog_init(bool enabled, int logfile_fd);
/* @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); void print_err();
/* @brief Prints error logs, just like print_log() but for errors /* @brief Prints error logs, just like print_log(), and also to stderr
*/ */
void print_log_err(char *format, ...); void print_log_err(char* format, ...);
/* @brief Returns the string corresponding to the last error that happened /* @brief Prints error logs, just like print_log()
*/ */
char *get_err(void); char* get_err();
#endif // ! ERRORS_H #endif // ! ERRORS_H

63
httpd/src/logger/logs.c Normal file
View file

@ -0,0 +1,63 @@
#define _POSIX_C_SOURCE 200809L
#include "logs.h"
#include <fcntl.h>
#include <stdarg.h>
#include "../utils/time/fmt_time.h"
#include "errors.h"
// === Static variables
static struct logs_config config;
// === Functions
int log_init(struct config *global_config)
{
int return_value = 0;
config.enabled = global_config->log;
config.server_cfg = global_config->servers;
config.logfile_fd = open(global_config->log_file, O_WRONLY);
if (config.logfile_fd <= 0)
{
config.enabled = false;
return_value = 1;
}
errlog_init(config.enabled, config.logfile_fd);
return return_value;
}
void print_log(char *format, ...)
{
if (!config.enabled)
return;
// Log prefix (time and server name)
dprintf(config.logfile_fd, "%s [%s] ", get_time(),
config.server_cfg->server_name);
// Print actual log
va_list args;
va_start(args, format);
vdprintf(config.logfile_fd, format, args);
va_end(args);
// New line
dprintf(config.logfile_fd, "\n");
}
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);
}
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,
request_type, target);
}

View file

@ -9,7 +9,7 @@ struct logs_config
{ {
bool enabled; bool enabled;
int logfile_fd; int logfile_fd;
struct server_config *server_cfg; struct server_config* server_cfg;
}; };
/* @brief Initializes the logging module /* @brief Initializes the logging module
@ -18,7 +18,7 @@ struct logs_config
* *
* @return 0 on success, an error code otherwise * @return 0 on success, an error code otherwise
*/ */
int log_init(struct config *config); int log_init(struct config* config);
/* @brief Prints logs (or not) conformly to the config given by the user. /* @brief Prints logs (or not) conformly to the config given by the user.
* Works like printf (because it uses it under the hood) with a * Works like printf (because it uses it under the hood) with a
@ -27,7 +27,7 @@ int log_init(struct config *config);
* @param format * @param format
* @param ... * @param ...
*/ */
void print_log(char *format, ...); void print_log(char* format, ...);
/* @brief Prints request logs with the adequate format in the logfile /* @brief Prints request logs with the adequate format in the logfile
* *
@ -35,7 +35,7 @@ void print_log(char *format, ...);
* @param target * @param target
* @param client_ip * @param client_ip
*/ */
void print_log_request(char *request_type, char *target, char *client_ip); void 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,11 +44,8 @@ void print_log_request(char *request_type, char *target, char *client_ip);
* @param target * @param target
* @param client_ip * @param client_ip
*/ */
void print_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);
/* @brief Gracefully exits the logs module
*/
void log_terminate(void);
#endif // ! LOGS_H #endif // ! LOGS_H

View file

@ -18,13 +18,12 @@ int main(int argc, char **argv)
// Initialize modules // Initialize modules
log_init(config); // Ignore ret val log_init(config); // Ignore ret val
http_init(config); http_init(config);
daemon_init(config);
// Start server // Start server
switch (config->daemon) switch (config->daemon)
{ {
case NO_OPTION: case NO_OPTION:
start_server(config); start_server("localhost", config->servers->port);
break; break;
case START: case START:
@ -43,6 +42,5 @@ int main(int argc, char **argv)
return 2; return 2;
} }
config_destroy(config);
return 0; return 0;
} }

View file

@ -7,25 +7,16 @@
// === Includes // === Includes
#include "server.h" #include "server.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 "../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
@ -101,40 +92,11 @@ static int get_socket(const char *hostname, const char *port)
// } // }
// } // }
// Retrieves client ipv4 address and stores it in res
// WARNING: res must be of size INET_ADDRSTRLEN
static void get_ip(struct sockaddr *client_addr, char *res)
{
// NOTE: exceptionally authorized cast
struct in_addr ipAddr = ((struct sockaddr_in *)client_addr)->sin_addr;
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(struct config *cfg) void start_server(const char *host, const char *port)
{ {
config = cfg; int server_socket = get_socket(host, port);
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;
@ -143,42 +105,19 @@ void start_server(struct config *cfg)
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; int client_fd = accept(server_socket, NULL, 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;
// TODO handle signals to stop // TODO handle signals to stop
// Get ip handle_request(client_fd);
char client_ip[INET_ADDRSTRLEN];
get_ip(&client_addr, client_ip);
handle_request(client_fd, client_ip);
// send_back(client_fd); // send_back(client_fd);
close(client_fd); close(client_fd);
} }
stop_server();
}
void stop_server(void)
{
close(server_socket); close(server_socket);
} }

13
httpd/src/server/server.h Normal file
View file

@ -0,0 +1,13 @@
#ifndef SERVER_H
#define SERVER_H
// #define _POSIX_C_SOURCE 200112L
/* @brief
*
* @param hpstname
* @param port
*/
void start_server(const char* host, const char* port);
#endif // ! SERVER_H

View file

@ -0,0 +1,61 @@
#include "files.h"
#include <stdio.h>
// #include <stdlib.h>
#include <sys/stat.h>
// #include "../string/string.h"
// bool file_exists(const char *path)
// {}
int is_directory(const char *path)
{
struct stat path_stat;
if (stat(path, &path_stat) != 0)
return S_ISDIR(path_stat.st_mode);
else
return -1;
}
// TODO handle logging
// struct string *get_file_content(const char *path)
// {
// // Open file
// FILE *stream = fopen(path, "r");
// if (stream == NULL)
// return NULL;
// // Alloc result
// char buf[BUFFER_SIZE];
// struct string *res = string_create(NULL, 0);
// if (res == NULL)
// {
// return NULL;
// }
// int nread;
// while ((fgets(buf, BUFFER_SIZE, stream)))
// }
// TODO not implemented
bool check_filename(struct string *path)
{
if (path == NULL || path->size <= 0)
return false;
return true;
}
ssize_t get_file_content_size(const char *path)
{
FILE *stream = fopen(path, "r");
if (stream == NULL)
return -2;
fseek(stream, 0, SEEK_END);
ssize_t res = ftell(stream);
fclose(stream);
return res;
}

View file

@ -0,0 +1,82 @@
#ifndef FILES_H
#define FILES_H
// === Definitions
#define BUFFER_SIZE 1024
// === Includes
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
#include "../string/string.h"
// === Functions
/*
* @brief
*
* @param path
*
* @return
*/
// bool file_exists(const char *path);
/*
* @brief
*
* @param path
*
* @return 1 if path is a directory, 0 if it is not and -1 if path is not valid
*/
int is_directory(const char* path);
/*
* @brief
*
* @param path
*
* @return
*/
char* get_file(const char* path);
/*
* @brief
*
* @param path
*
* @return
*/
struct string* get_file_content(const char *path);
/*
* @brief
*
* @param path
*
* @return
*/
bool check_filename(struct string* path);
/*
* @brief
*
* @param filename
*
* @return
*/
bool sanitize_filename(struct string* filename);
/*
* @brief
*
* @param path
*
* @return
*/
ssize_t get_file_content_size(const char* path);
#endif // ! FILES_H

View file

@ -8,9 +8,8 @@
// === Includes // === Includes
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
#include <sys/types.h> #include <sys/types.h>
#include <stddef.h>
#include "../string/string.h" #include "../string/string.h"
// === Functions // === Functions
@ -34,6 +33,6 @@ bool is_blank(char c);
/* /*
* Doc: TODO * Doc: TODO
*/ */
ssize_t skip_blanks(struct string *str, size_t offset); ssize_t skip_blanks(struct string* str, size_t offset);
#endif // ! BLANKS_H #endif // ! BLANKS_H

View file

@ -1,12 +1,12 @@
#ifndef WORDS_H #ifndef WORDS_H
#define WORDS_H #define WORDS_H
// === Includes // === Includes
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdbool.h>
#include <sys/types.h> #include <sys/types.h>
#include "../string/string.h" #include "../string/string.h"
// === Functions // === Functions
@ -19,7 +19,7 @@ bool str_contains(const char *str, char c);
/* /*
* Doc: TODO * Doc: TODO
*/ */
ssize_t read_word(struct string *str, size_t offset, struct string **res); ssize_t read_word(struct string* str, size_t offset, struct string** res);
/* /*
* Doc: TODO * Doc: TODO
@ -30,8 +30,8 @@ ssize_t read_word_delim(struct string *str, size_t offset, struct string **res,
/* /*
* Doc: TODO * Doc: TODO
*/ */
ssize_t read_word_restrict(struct string *str, size_t offset, ssize_t read_word_restrict(struct string *str, size_t offset, struct string **res,
struct string **res, const char *restr); const char *restr);
/* /*
* Doc: TODO * Doc: TODO

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 * sizeof(char)); str->data = realloc(str->data, new_size);
if (str->data == NULL) if (str->data == NULL)
return; // Handle ? return; // Handle ?
} }
@ -78,46 +78,6 @@ 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);
free(tmp);
}
}
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++)
@ -154,22 +114,3 @@ 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;
}

View file

@ -42,16 +42,6 @@ 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
** **
@ -73,17 +63,6 @@ void string_destroy(struct string *str);
** **
** @return a pointer to an allocated memory zone containing the string ** @return a pointer to an allocated memory zone containing the string
*/ */
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 */

View file

@ -0,0 +1,10 @@
#include "fmt_time.h"
#include <time.h>
char *get_time(void)
{
time_t local_ts = time(NULL);
struct tm *gmt_time = gmtime(&local_ts);
return asctime(gmt_time);
}

View file

@ -6,6 +6,6 @@
* *
* @return A NULL-terminated string containing the fromatted GMT time * @return A NULL-terminated string containing the fromatted GMT time
*/ */
char *get_time(); char* get_time();
#endif // ! FMT_TIME_H #endif // ! FMT_TIME_H

View file

@ -1,79 +0,0 @@
#define _POSIX_C_SOURCE 200809L
#include "logs.h"
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include "../utils/time/fmt_time.h"
#include "errors.h"
// === Static variables
static struct logs_config config;
// === Functions
int log_init(struct config *global_config)
{
int return_value = 0;
config.enabled = global_config->log;
config.server_cfg = global_config->servers;
if (global_config->log_file != NULL)
{
config.logfile_fd = open(global_config->log_file, O_WRONLY);
if (config.logfile_fd <= 0)
{
config.enabled = false;
return_value = 1;
}
}
else
{
config.logfile_fd = STDOUT_FILENO;
}
errlog_init(config.enabled, config.logfile_fd, config.server_cfg);
return return_value;
}
void print_log(char *format, ...)
{
if (!config.enabled)
return;
// Log prefix (time and server name)
char *time = get_time();
dprintf(config.logfile_fd, "%s [%s] ", time,
config.server_cfg->server_name);
free(time);
// Print actual log
va_list args;
va_start(args, format);
vdprintf(config.logfile_fd, format, args);
va_end(args);
// New line
dprintf(config.logfile_fd, "\n");
}
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);
}
void print_log_response(int status_code, char *request_type, char *target,
char *client_ip)
{
print_log("responding with %d to %s for %s on '%s'", status_code, client_ip,
request_type, target);
}
void log_terminate(void)
{
if (config.logfile_fd != 0 && config.logfile_fd != STDOUT_FILENO)
close(config.logfile_fd);
}

View file

@ -1,19 +0,0 @@
#ifndef SERVER_H
#define SERVER_H
#include "../config/config.h"
/* @brief Starts the HTTP server
*
* @warn Make sure to initialize modules before calling
*
* @param hostname
* @param port
*/
void start_server(struct config *cfg);
/* @brief Stops the currently running HTTP server
*/
void stop_server(void);
#endif // ! SERVER_H

View file

@ -1,105 +0,0 @@
#define _POSIX_C_SOURCE 200112L
#include "files.h"
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
// #include "../string/string.h"
// int file_exists(const char *path)
// {}
int is_directory(const char *path)
{
struct stat path_stat;
if (lstat(path, &path_stat) == 0)
{
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
{
if (errno == ENOENT) // File not found
return ERR_FILES_NOT_FOUND;
else // Other errors
return ERR_FILES_FORBIDDEN;
}
}
// TODO handle logging
// struct string *get_file_content(const char *path)
// {
// // Open file
// FILE *stream = fopen(path, "r");
// if (stream == NULL)
// return NULL;
// // Alloc result
// char buf[BUFFER_SIZE];
// struct string *res = string_create(NULL, 0);
// if (res == NULL)
// {
// return NULL;
// }
// int nread;
// while ((fgets(buf, BUFFER_SIZE, stream)))
// }
// TODO not implemented
bool check_filename(struct string *path)
{
if (path == NULL || path->size <= 0)
return false;
return true;
}
ssize_t get_file_content_size(const char *path)
{
FILE *stream = fopen(path, "r");
if (stream == NULL)
return -2;
fseek(stream, 0, SEEK_END);
ssize_t res = ftell(stream);
fclose(stream);
return res;
}
int write_to_file(const char *path, struct string *buf)
{
FILE *stream = fopen(path, "w");
if (stream == NULL)
return 1;
fwrite(buf->data, sizeof(char), buf->size, stream);
fclose(stream);
return 0;
}
int write_pid(const char *filepath, int pid)
{
FILE *stream = fopen(filepath, "w");
if (stream == NULL)
return 1;
if (fprintf(stream, "%d", pid) <= 0)
{
fclose(stream);
return 1;
}
fclose(stream);
return 0;
}

View file

@ -1,109 +0,0 @@
#ifndef FILES_H
#define FILES_H
// === Definitions
#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
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
#include "../string/string.h"
// === Functions
/*
* @brief
*
* @param path
*
* @return
*/
// bool file_exists(const char *path);
/*
* @brief
*
* @param path
*
* @return Returns the corresponding return value / error code (see header
* definitions)
*/
int is_directory(const char *path);
/*
* @brief
*
* @param path
*
* @return
*/
// char *get_file(const char *path);
/*
* @brief
*
* @param path
*
* @return
*/
struct string *get_file_content(const char *path);
/*
* @brief
*
* @param path
*
* @return
*/
bool check_filename(struct string *path);
/*
* @brief
*
* @param filename
*
* @return
*/
// bool sanitize_filename(struct string *filename);
/*
* @brief
*
* @param path
*
* @return
*/
ssize_t get_file_content_size(const char *path);
/*
* @brief
*
* @param path
*
* @return 0 on success, the corresponding error code otherwise
*/
int write_to_file(const char *path, struct string *buf);
/*
* @brief
*
* @param path
*
* @return 0 on success, the corresponding error code otherwise
*/
int write_pid(const char *filepath, int pid);
#endif // ! FILES_H

View file

@ -1,15 +0,0 @@
#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);
strftime(buf, 64, "%a, %d %b %Y %H:%M:%S %Z", gmt_time);
return buf;
}

View file

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

View file

@ -1,178 +0,0 @@
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)
@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)

View file

@ -1,40 +0,0 @@
#!/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"