diff --git a/.idea/editor.xml b/.idea/editor.xml
index 59c76fa..27e844a 100644
--- a/.idea/editor.xml
+++ b/.idea/editor.xml
@@ -99,482 +99,482 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 8a7787d..fa87f4d 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@ And then open the `docs/html/index.html`.
- [ ] IPv6 compatible
- [ ] Request parser
- [x] Serve static files
+ - [ ] Implement Keep-Alive
- [ ] Multithreading
- [ ] Logging
- [ ] Advanced
@@ -46,4 +47,5 @@ And then open the `docs/html/index.html`.
- [Beej's Guide to Network Programming](https://beej.us/guide/bgnet/)
-You can find my journey inside the `notes` directory or on my [blog](https://francescorocca.me/building-an-http-server-in-c/).
+You can find my journey inside the `notes` directory or on
+my [blog](https://francescorocca.me/building-an-http-server-in-c/).
diff --git a/include/http/http.h b/include/http/http.h
index fc30b02..72b7e69 100644
--- a/include/http/http.h
+++ b/include/http/http.h
@@ -19,6 +19,7 @@ enum http_method {
*
*/
typedef struct http {
+ int sockfd; /**< Socket file descriptor */
enum http_method method; /**< HTTP request method */
char location[LOCATION_LEN]; /**< Resource requested */
char location_path[LOCATION_LEN]; /**< Resource path */
@@ -33,14 +34,18 @@ typedef struct http {
/**
* @brief Parses a HTTP request
*
- * @param request[in] The http request sent to the server
+ * @param request_str[in] The http request sent to the server
* @return Returns a http_t pointer to the request
*/
-http_t *http_parse(char *request_str);
+http_t *http_parse(char *request_str, int sockfd);
void http_parse_method(http_t *request, const char *method);
-void http_send_response(http_t *request, int sockfd);
void http_get_content_type(http_t *request, char *content_type);
+
+void http_send_response(http_t *request);
+void http_send_not_found(http_t *request);
+void http_send_not_implemented(http_t *request);
+
void http_free(http_t *request);
#endif
\ No newline at end of file
diff --git a/include/server/server.h b/include/server/server.h
index f41c04e..7635dc0 100644
--- a/include/server/server.h
+++ b/include/server/server.h
@@ -1,18 +1,8 @@
#ifndef CWS_SERVER_H
#define CWS_SERVER_H
-#include
-#include
-#include
#include
-#include
-#include
-#include
-#include
-#include
#include
-#include
-#include
#include "utils/hashmap.h"
@@ -68,9 +58,9 @@ void epoll_ctl_add(int epfd, int sockfd, uint32_t events);
void epoll_ctl_del(int epfd, int sockfd);
/**
- * @brief Makes a file descriptor non blocking
+ * @brief Makes a file descriptor non-blocking
*
- * @param sockfd[in] The file descriptor to make non blocking
+ * @param sockfd[in] The file descriptor to make non-blocking
*/
void setnonblocking(int sockfd);
@@ -87,8 +77,17 @@ int handle_new_client(int sockfd, struct sockaddr_storage *their_sa, socklen_t *
/**
* @brief Closes all the file descriptors opened
*
- * @param map[in] The hash map
+ * @param bucket[in] The hash map
*/
void close_fds(bucket_t *bucket);
+/**
+ * @brief Disconnect a client
+ *
+ * @param epfd[in] Epoll file descriptor
+ * @param client_fd[in] Client file descriptor
+ * @param bucket[in] Clients hash map
+ */
+void close_client(int epfd, int client_fd, bucket_t *bucket);
+
#endif
diff --git a/src/http/http.c b/src/http/http.c
index 3c273d4..d391cf3 100644
--- a/src/http/http.c
+++ b/src/http/http.c
@@ -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"
- "\n"
- "\n"
- " Resource Not Found\n"
- "\n"
- "\n"
- "Resource not found.
\n"
- "\n"
- "";
- 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"
+ "\n"
+ "\n"
+ " 501 Not Implemented\n"
+ "\n"
+ "\n"
+ "501 Not Implemented
\n"
+ "\n"
+ "";
+ 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"
+ "\n"
+ "\n"
+ " 404 Not Found\n"
+ "\n"
+ "\n"
+ "404 Not Found.
\n"
+ "\n"
+ "";
+ const size_t response_len = strlen(response);
+ send(request->sockfd, response, response_len, 0);
}
void http_free(http_t *request) { free(request); }
diff --git a/src/server/server.c b/src/server/server.c
index 832db39..a526abf 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -1,5 +1,16 @@
#include "server/server.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
#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);
+}
\ No newline at end of file
diff --git a/www/cws.jpg b/www/cws.jpg
new file mode 100644
index 0000000..9732493
Binary files /dev/null and b/www/cws.jpg differ
diff --git a/www/index.html b/www/index.html
index b41deea..cc281a9 100644
--- a/www/index.html
+++ b/www/index.html
@@ -4,12 +4,16 @@
- cws
+ CWS
+
+
+
-Hello from cws!
+Hello from CWS!