Skip to content

Commit

Permalink
Refactoring the code and adding comments for better understaning of t…
Browse files Browse the repository at this point in the history
…he code
  • Loading branch information
Altaks committed Feb 4, 2024
1 parent 99f5870 commit 9e0b626
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 37 deletions.
27 changes: 23 additions & 4 deletions files/files.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@

struct hashmap * mimeContentTypes = NULL;

/**
* Checks if a path ends with a slash
* @param path the path to check
* @return true if the path ends with a slash
*/
bool endsWithSlash(char* path){
return path[strlen(path) - 1] == '/';
}

int openFile(char* rootDirectory, char* filePath) {

// reserve enough memory
// reserve enough memory for the full path
u_int16_t length = strlen(rootDirectory) + strlen(filePath) + (endsWithSlash(rootDirectory) ? 1 : 2);
char * fullPath = calloc(length, sizeof(char));

Expand All @@ -39,18 +44,23 @@ int openFile(char* rootDirectory, char* filePath) {

void readFile(int fd, char** text_ptr, ssize_t * text_length_ptr) {

// prepare buffers for reading file by 1024 bytes chunks
*text_ptr = NULL;
u_short buffer_size = 1024;
char buffer[buffer_size];

// read the file by chunks and append to the text_ptr
ssize_t bytes_read = 0;
while(*text_ptr == NULL || bytes_read >= buffer_size) {

// clear buffer between reads (just in case)
memset(&buffer, 0, buffer_size);
// read bytes from the file
bytes_read = read(fd, buffer, buffer_size);

// if bytes were read
if(bytes_read != 0){

// if the text_ptr is NULL, allocate memory for it and copy the buffer into it
// otherwise, reallocate memory for the text_ptr and append the buffer to it
if(*text_ptr == NULL){
*text_ptr = calloc(bytes_read + 1, sizeof(char));
strncpy(*text_ptr, buffer, bytes_read);
Expand All @@ -66,6 +76,8 @@ void readFile(int fd, char** text_ptr, ssize_t * text_length_ptr) {


char* getLastModifiedTime(char* rootDirectory, char* path) {

// reserve enough memory for the full path
u_int16_t length = strlen(rootDirectory) + strlen(path) + (endsWithSlash(rootDirectory) ? 1 : 2);
char * fullPath = calloc(length, sizeof(char));

Expand All @@ -76,16 +88,23 @@ char* getLastModifiedTime(char* rootDirectory, char* path) {
}
strcat(fullPath, path);


// get the last modified time of the file using system stat calls
struct stat attrib;
stat(fullPath, &attrib);

// allocate memory for the date
char * date = calloc(30, sizeof(char));

// Format the date to a HTTP valid format (GMT)
strftime(date, 30, "%a, %d %b %Y %H:%M:%S %Z", gmtime(&(attrib.st_mtim.tv_sec)));
return date;
}

void closeFile(int fd) {
// verbose log
server_log(INFO, "Closed file w/ fd: %i", fd);

// close the file
close(fd);
}

Expand Down
22 changes: 14 additions & 8 deletions logging/logging.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,25 @@
* @param ... printf arguments
*/
void server_log(LogLevel logLevel, const char * restrict message, ...) {

// First get the current time
time_t now = time(NULL);
struct tm * time = localtime(&now);

char logLevelstr[16];

// Then get the log level string
char logLevelBuffer[16];
switch (logLevel) {
case FATAL:
sprintf(logLevelstr, "\x1b[1;160m[FATAL]");
sprintf(logLevelBuffer, "\x1b[1;160m[FATAL]");
break;
case ERROR:
sprintf(logLevelstr, "\x1b[0;210m[ERROR]");
sprintf(logLevelBuffer, "\x1b[0;210m[ERROR]");
break;
case WARNING:
sprintf(logLevelstr, "\x1b[0;215m[WARN] ");
sprintf(logLevelBuffer, "\x1b[0;215m[WARN] ");
break;
case INFO:
sprintf(logLevelstr, "\x1b[0;252m[INFO] ");
sprintf(logLevelBuffer, "\x1b[0;252m[INFO] ");
break;
}

Expand All @@ -38,15 +40,19 @@ void server_log(LogLevel logLevel, const char * restrict message, ...) {
switch (logLevel) {
case FATAL:
case ERROR:
fprintf(stderr, "[%02d/%02d/%d %02d:%02d:%02d] %s | ", time->tm_mday, time->tm_mon + 1, time->tm_year + 1900, time->tm_hour, time->tm_min, time->tm_sec, logLevelstr);
// Print to stderr if the log level is FATAL or ERROR
fprintf(stderr, "[%02d/%02d/%d %02d:%02d:%02d] %s | ", time->tm_mday, time->tm_mon + 1, time->tm_year + 1900, time->tm_hour, time->tm_min, time->tm_sec, logLevelBuffer);
vfprintf(stderr, message, args);
fprintf(stderr, "\x1b[0m\n");
break;
default:
printf("[%02d/%02d/%d %02d:%02d:%02d] %s | ", time->tm_mday, time->tm_mon + 1, time->tm_year + 1900, time->tm_hour, time->tm_min, time->tm_sec, logLevelstr);
// Print to stdout if the log level is WARNING or INFO
printf("[%02d/%02d/%d %02d:%02d:%02d] %s | ", time->tm_mday, time->tm_mon + 1, time->tm_year + 1900, time->tm_hour, time->tm_min, time->tm_sec, logLevelBuffer);
vprintf(message, args);
printf("\x1b[0m\n");
break;
}

// Clean up the stack
va_end(args);
}
9 changes: 8 additions & 1 deletion logging/logging.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
#ifndef LOGGING_H
#define LOGGING_H

/**
* Represents a log level
*/
typedef enum LogLevel LogLevel;
enum LogLevel {
FATAL, ERROR, WARNING, INFO
};

void server_log(LogLevel, const char*, ...);
/**
* Logs the message same as printf but adds time and log level to the beginning of the message
* @param ... printf arguments
*/
void server_log(LogLevel, const char *, ...);

#endif
55 changes: 47 additions & 8 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,22 @@
#include "logging/logging.h"
#include "network/address.h"

// The maximum amount of TCP connections the server can handle
const int TCP_STACK = 5;

// The root directory of the server
char * rootDirectory = NULL;

[[noreturn]] int main(int argc, char** argv) {
server_log(INFO, "Booting the server...");
server_log(INFO, "Initializing MIME file types hash table...");

// Initialize the MIME file types hash table
int mimeContentTypesInsertedAmount = 0;
initMimeContentTypes(&mimeContentTypesInsertedAmount);
server_log(INFO, "MIME file types hash table initialized, %i files types supported", mimeContentTypesInsertedAmount);

struct sockaddr_in server;

// Check if the server has been launched with the correct amount of arguments
if(argc != 3){
server_log(ERROR, "Usage : %s port rootDirectoryPath", argv[0]);
exit(EXIT_FAILURE);
Expand All @@ -50,6 +53,7 @@ char * rootDirectory = NULL;

server_log(INFO, "Server launched on port %i and root directory %s", port, rootDirectory);

// Create the virtual socket
int sock = socket(AF_INET, SOCK_STREAM, 0);

if(sock == -1){
Expand All @@ -59,13 +63,16 @@ char * rootDirectory = NULL;
server_log(INFO, "Created socket for server with file descriptor %i", sock);
}

// Internet address with port "port" converted as network value and IP chosen by kernel
// Prepare the server address
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_port = htons(port); // Internet address with port "port" converted as network value and IP chosen by kernel
server.sin_addr.s_addr = INADDR_ANY;

// Bind the server address to the socket
int bind_rtrn = bind(sock, (const struct sockaddr *) &server, sizeof(server));

// Check if the binding was successful
if(bind_rtrn < 0){
server_log(FATAL, "Could not bind server address [port:%i] to socket %i due to error : %s", port, sock, strerror(errno));
exit(EXIT_FAILURE);
Expand All @@ -75,64 +82,95 @@ char * rootDirectory = NULL;
if(addr != NULL) free(addr);
}

// Make the socket listen to incoming connections with a maximum of TCP_STACK connections in the stack
int listen_rtrn = listen(sock, TCP_STACK);

// Check if the socket is now in listen mode
if(listen_rtrn != 0){
server_log(FATAL, "Could not make the socket listen due to error : %s", strerror(errno));
exit(EXIT_FAILURE);
} else {
server_log(INFO, "Successfully made the socket go in listen mode");
}

int connection_id = 0;

while(true){

// Prepare the client address structure
struct sockaddr_in client;
socklen_t len = sizeof(client);
int dialog_socket;

// Accept the connection from the client socket
dialog_socket = accept(sock, (struct sockaddr *) &client, &len);

// Check if the connection was successful
if(dialog_socket < 0){
server_log(ERROR, "An error occurred while accepting the connection : %s", strerror(errno));
exit(EXIT_FAILURE);
}

// Convert the client address to a human-readable string for logging purposes
char* client_addr = addressToString(client);
connection_id++;

// Prepare request reading buffer for 1024 bytes chunks reading
ushort buff_len = 1024;
char buff_read[buff_len];

// Allocate memory for the request
char * request = NULL;

// Read the request from the client
ssize_t bytes_received = 0;
while(request == NULL || bytes_received > 0) {

// Read bytes from the connection
bytes_received = recv(dialog_socket, buff_read, buff_len, 0);

// Check if an error occurred while reading bytes
if(bytes_received < 0) {
server_log(ERROR, "An error occurred while reading bytes from connection : %s", strerror(errno));
exit(EXIT_FAILURE);
} else if(bytes_received > 0){

// Allocate memory for the request if it's the first chunk, else expand the allocated memory chunk
if(request == NULL) {

// Allocate memory for the request and copy the first chunk of bytes
request = calloc(bytes_received + 1, sizeof(char));
strncpy(request, buff_read, bytes_received);
} else {

// Expand the allocated memory chunk and concatenate the next chunk of bytes
request = reallocarray(request, strlen(request) + strlen(buff_read) + 1, sizeof(char));
if(request == NULL){
printf("Reallocation of buffer at address %p as failed during expanding resources expansion", request);
continue;
}
strcat(request, buff_read);
}

// Check if the request has been fully read.
if(strstr(request, "\r\n\r\n") || strstr(request, "\n\n")){
break;
}
}
}

// Build the response for the response algorithm
HTTPResponse httpResponse = buildResponse(rootDirectory, request);

// Convert the response to a HTTP valid string
char * responseStr = responseToStr(httpResponse);

// Send the response to the client if the response is valid
if(responseStr != NULL){
send(dialog_socket, responseStr, strlen(responseStr), 0);
} else {
// TODO : NYI
server_log(ERROR, "Response is NULL ???");
}

// Close the connection with the client
close(dialog_socket);

// Freeing http response manual memory allocations
Expand All @@ -146,6 +184,7 @@ char * rootDirectory = NULL;
if(request != NULL) free(request);
if(responseStr != NULL) free(responseStr);

server_log(INFO, "Server connection %i with client on socket %i has been closed and resources have been freed", connection_id, sock);
// Log the connection closure
server_log(INFO, "Server connection with client on socket %i has been closed and resources have been freed", sock);
}
}
6 changes: 6 additions & 0 deletions network/address.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@
#include "address.h"

char* addressToString(struct sockaddr_in address){

// Allocates enough memory for the IPvX address to be written in
char * addr = malloc(sizeof(char) * (INET6_ADDRSTRLEN > INET_ADDRSTRLEN ? INET6_ADDRSTRLEN : INET_ADDRSTRLEN) + 1);

// Depending on the address type, write to the buffer the address using native methods.
switch (((struct sockaddr *) &address)->sa_family) {
case AF_INET: {
inet_ntop(AF_INET, &(address.sin_addr), addr, INET_ADDRSTRLEN);
}
case AF_INET6: {
// Here we need to cast the struct as an IPv6 related one.
inet_ntop(AF_INET6, &((struct sockaddr_in6 *) &address)->sin6_addr, addr, INET6_ADDRSTRLEN);
}
}

return addr;
}
Loading

0 comments on commit 9e0b626

Please sign in to comment.