add more content types and improve code

This commit is contained in:
2024-11-27 18:55:15 +01:00
parent 4e8e6ec9a7
commit 3da61ef47c
9 changed files with 413 additions and 318 deletions

View File

@@ -7,9 +7,14 @@
#include "utils/colors.h"
http_t *http_parse(char *request_str) {
http_t *http_parse(char *request_str, int sockfd) {
http_t *request = malloc(sizeof(http_t));
fprintf(stdout, YELLOW "[http] REQUEST:\n%s\n" RESET, request_str);
if (request == NULL) {
return NULL;
}
/* Insert sockfd */
request->sockfd = sockfd;
/* Parse HTTP method */
char *pch = strtok(request_str, " ");
@@ -22,7 +27,8 @@ http_t *http_parse(char *request_str) {
strncpy(request->location, pch, LOCATION_LEN);
/* Parse location path */
/* TODO: fix warnings */
/* TODO: Prevent Path Traversal */
/* TODO: Fix warnings */
if (strcmp(request->location, "/") == 0) {
snprintf(request->location_path, LOCATION_LEN, "%s/index.html", WWW);
} else {
@@ -41,70 +47,119 @@ http_t *http_parse(char *request_str) {
}
void http_parse_method(http_t *request, const char *method) {
if (request == NULL) {
return;
}
if (strcmp(method, "GET") == 0) {
request->method = GET;
return;
}
if (strcmp(method, "POST") == 0) {
request->method = POST;
}
}
void http_send_response(http_t *request, int sockfd) {
FILE *file = fopen(request->location_path, "r");
if (file == NULL) {
/* 404 */
/* TODO: improve error handling */
char response[1024] =
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 216\r\n"
"\r\n"
"<html>\n"
"<head>\n"
" <title>Resource Not Found</title>\n"
"</head>\n"
"<body>\n"
"<p>Resource not found.</p>\n"
"</body>\n"
"</html>";
send(sockfd, response, 1024, 0);
return;
}
http_send_not_implemented(request);
}
void http_send_response(http_t *request) {
FILE *file = fopen(request->location_path, "rb");
if (file == NULL) {
/* 404 */
http_send_not_found(request);
return;
}
/* Retrieve correct Content-Type */
char content_type[1024];
http_get_content_type(request, content_type);
/* Don't care about numbers, they are random */
char line[1024] = {0};
char html_code[32000] = {0};
char response[65535] = {0};
/* Retrieve file size */
fseek(file, 0, SEEK_END);
const size_t content_length = ftell(file); /* Returns the read bytes (we're at the end, so the file size) */
rewind(file);
while (fgets(line, 1024, file)) {
strncat(html_code, line, 32000);
/* Retrieve file data */
char *file_data = malloc(content_length);
if (file_data == NULL) {
fclose(file);
fprintf(stderr, RED "Unable to allocate file data\n");
return;
}
/* Read 1 byte until content_length from file and put in file_data */
fread(file_data, 1, content_length, file);
fclose(file);
const size_t content_length = strlen(html_code);
snprintf(response, sizeof response,
char response_header[1024];
snprintf(response_header, sizeof response_header,
"%s 200 OK\r\n"
"Content-Type: %s\r\n"
"Content-Length: %zu\r\n"
"Connection: close\r\n"
"\r\n"
"%s",
request->http_version, content_type, content_length, html_code);
"\r\n",
request->http_version, content_type, content_length);
send(sockfd, response, strlen(response), 0);
send(request->sockfd, response_header, strlen(response_header), 0);
send(request->sockfd, file_data, content_length, 0);
free(file_data);
}
void http_get_content_type(http_t *request, char *content_type) {
char *ptr = strrchr(request->location_path, '.');
/* TODO: improve content_type (used to test) */
snprintf(content_type, 1024, "text/%s", ptr + 1);
if (ptr == NULL) {
http_send_not_found(request);
return;
}
ptr += 1;
char ct[32];
/* TODO: Improve content_type (used to test) */
if (strcmp(ptr, "html") == 0 || strcmp(ptr, "css") == 0 || strcmp(ptr, "javascript") == 0) {
strncpy(ct, "text", sizeof ct);
}
if (strcmp(ptr, "jpg") == 0 || strcmp(ptr, "png") == 0) {
strncpy(ct, "image", sizeof ct);
}
snprintf(content_type, 1024, "%s/%s", ct, ptr);
}
void http_send_not_implemented(http_t *request) {
const char response[1024] =
"HTTP/1.1 501 Not Implemented\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 216\r\n"
"\r\n"
"<html>\n"
"<head>\n"
" <title>501 Not Implemented</title>\n"
"</head>\n"
"<body>\n"
"<p>501 Not Implemented</p>\n"
"</body>\n"
"</html>";
const size_t response_len = strlen(response);
send(request->sockfd, response, response_len, 0);
}
void http_send_not_found(http_t *request) {
const char response[1024] =
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/html\r\n"
"Content-Length: 216\r\n"
"\r\n"
"<html>\n"
"<head>\n"
" <title>404 Not Found</title>\n"
"</head>\n"
"<body>\n"
"<p>404 Not Found.</p>\n"
"</body>\n"
"</html>";
const size_t response_len = strlen(response);
send(request->sockfd, response, response_len, 0);
}
void http_free(http_t *request) { free(request); }

View File

@@ -1,5 +1,16 @@
#include "server/server.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <unistd.h>
#include "http/http.h"
#include "utils/colors.h"
#include "utils/hashmap.h"
@@ -91,24 +102,32 @@ void handle_clients(int sockfd) {
/* Incoming data */
client_fd = revents[i].data.fd;
const ssize_t bytes_read = recv(client_fd, data, sizeof data, 0);
char ip[INET_ADDRSTRLEN];
bucket_t *client = hm_lookup(clients, client_fd);
get_client_ip(&client->sas, ip);
if (bytes_read == 0) {
/* Client disconnected */
char ip[INET_ADDRSTRLEN];
bucket_t *client = hm_lookup(clients, client_fd);
get_client_ip(&client->sas, ip);
fprintf(stdout, BLUE "[server] Client (%s) disconnected\n" RESET, ip);
epoll_ctl_del(epfd, client_fd);
close(client_fd);
close_client(epfd, client_fd, clients);
continue;
}
if (bytes_read < 0) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
/* Error during read, handle it (close client) */
epoll_ctl_del(epfd, client_fd);
close(client_fd);
}
continue;
}
data[bytes_read] = '\0';
/* Parse HTTP request */
http_t *request = http_parse(data);
http_send_response(request, client_fd);
http_t *request = http_parse(data, client_fd);
http_send_response(request);
fprintf(stdout, BLUE "[server] Client (%s) disconnected\n" RESET, ip);
close_client(epfd, client_fd, clients);
http_free(request);
/* Clear str */
@@ -129,7 +148,7 @@ void epoll_ctl_add(int epfd, int sockfd, uint32_t events) {
struct epoll_event event;
event.events = events;
event.data.fd = sockfd;
int status = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
const int status = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
if (status != 0) {
fprintf(stderr, RED BOLD "[server] epoll_ctl_add(): %s\n" RESET, strerror(errno));
@@ -138,7 +157,7 @@ void epoll_ctl_add(int epfd, int sockfd, uint32_t events) {
}
void epoll_ctl_del(int epfd, int sockfd) {
int status = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
const int status = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
if (status != 0) {
fprintf(stdout, RED BOLD "[server] epoll_ctl_del(): %s\n" RESET, strerror(errno));
@@ -147,7 +166,7 @@ void epoll_ctl_del(int epfd, int sockfd) {
}
void setnonblocking(int sockfd) {
int status = fcntl(sockfd, F_SETFL, O_NONBLOCK);
const int status = fcntl(sockfd, F_SETFL, O_NONBLOCK);
if (status == -1) {
fprintf(stderr, RED BOLD "[server] fcntl(): %s\n" RESET, gai_strerror(status));
@@ -156,7 +175,7 @@ void setnonblocking(int sockfd) {
}
int handle_new_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *theirsa_size) {
int client_fd = accept(sockfd, (struct sockaddr *)their_sa, theirsa_size);
const int client_fd = accept(sockfd, (struct sockaddr *)their_sa, theirsa_size);
if (client_fd == -1) {
if (errno != EWOULDBLOCK) {
@@ -169,14 +188,12 @@ int handle_new_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *
}
void close_fds(bucket_t *bucket) {
bucket_t *p, *next;
for (size_t i = 0; i < HASHMAP_MAX_CLIENTS; ++i) {
close(bucket[i].sockfd);
if (bucket[i].next != NULL) {
/* Close the fds */
p = bucket[i].next;
next = p->next;
bucket_t *p = bucket[i].next;
bucket_t *next = p->next;
do {
close(p->sockfd);
p = next;
@@ -185,3 +202,9 @@ void close_fds(bucket_t *bucket) {
}
}
}
void close_client(int epfd, int client_fd, bucket_t *clients) {
epoll_ctl_del(epfd, client_fd);
hm_remove(clients, client_fd);
close(client_fd);
}