diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 5316267..0000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index 79ee123..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/cws.iml b/.idea/cws.iml deleted file mode 100644 index 55d7baf..0000000 --- a/.idea/cws.iml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/.idea/editor.xml b/.idea/editor.xml deleted file mode 100644 index 27e844a..0000000 --- a/.idea/editor.xml +++ /dev/null @@ -1,580 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 683c996..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 2ce65a9..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 9497029..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 5b91413..99d10f2 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ # cws -A minimal web server written in C. This is a personal project; it is not intended to be a production-ready tool, nor -will it ever be. Use it at your own risk. +A minimal web server. This is a personal project; it is not intended to be a production-ready tool, nor will it ever be. Use it at your own risk. ## Requirements - [meson](https://mesonbuild.com/index.html) -- [doxygen](https://www.doxygen.nl/) - - Optional, just to build the docs. - libcyaml - libyaml +- [doxygen](https://www.doxygen.nl/) + - Optional, just to build the docs. ## How to build @@ -24,7 +23,6 @@ And then run `cws`! ## Docs ```bash -# inside the cws directory $ git submodule update --init $ doxygen ``` @@ -33,23 +31,14 @@ And then open the `docs/html/index.html`. ## Roadmap -- [x] Understanding basic web server concepts -- [x] Basic server -- [ ] CLI args -- [ ] Enhance web server - - [ ] IPv6 compatible - - [ ] Request parser - - [x] Serve static files - - [ ] Implement Keep-Alive - - [ ] Multithreading to handle concurrent requests - - [ ] Logging -- [ ] Advanced Features - - [ ] HTTPS support with TLS - - [ ] Compression (Gzip) - - [ ] Support for virtual hosting +- Request parser +- Implement Keep-Alive +- Support for virtual hosting +- HTTPS support with TLS -## Resources - -- [Beej's Guide to Network Programming](https://beej.us/guide/bgnet/) - -You can find my journey inside the `notes` directory. +## Future +- CLI args +- IPv6 compatible +- Multithreading to handle concurrent requests +- Logging +- Compression (Gzip) diff --git a/include/utils/colors.h b/include/utils/colors.h index c48e679..c648910 100644 --- a/include/utils/colors.h +++ b/include/utils/colors.h @@ -9,4 +9,28 @@ #define BOLD "\033[1m" #define RESET "\033[0m" +#ifdef USE_COLORS +#define _ERR RED BOLD "[ERROR]" RESET +#define _WARNING YELLOW "[WARNING]" RESET +#define _INFO GREEN "[INFO]" RESET +#define _DEBUG BLUE "[DEBUG]" RESET +#else +#define _ERR "[ERROR]" +#define _WARNING "[WARNING]" +#define _INFO "[INFO]" +#define _DEBUG "[DEBUG]" +#endif + +#ifdef EVELOPER +#define CWS_LOG_DEBUG(msg, ...) fprintf(stdout, _DEBUG " " msg "\n", ##__VA_ARGS__) +#else +#define CWS_LOG_DEBUG(msg, ...) +#endif + +#define CWS_LOG(level, msg, ...) + +#define CWS_LOG_ERROR(msg, ...) fprintf(stderr, _ERR " " msg "\n", ##__VA_ARGS__) +#define CWS_LOG_WARNING(msg, ...) fprintf(stdout, _WARNING " " msg "\n", ##__VA_ARGS__) +#define CWS_LOG_INFO(msg, ...) fprintf(stdout, _INFO " " msg "\n", ##__VA_ARGS__) + #endif \ No newline at end of file diff --git a/meson.build b/meson.build index b807b0f..32e5c82 100644 --- a/meson.build +++ b/meson.build @@ -7,4 +7,7 @@ incdir = include_directories('include') libyaml = dependency('yaml-0.1') libcyaml = dependency('libcyaml') +add_global_arguments('-DUSE_COLORS', language : 'c') +add_global_arguments('-DEVELOPER', language : 'c') + executable('cws', server, include_directories : incdir, dependencies : [libyaml, libcyaml]) diff --git a/notes/advanced-techniques.md b/notes/advanced-techniques.md deleted file mode 100644 index 496d4cb..0000000 --- a/notes/advanced-techniques.md +++ /dev/null @@ -1,96 +0,0 @@ -# Advanced Techniques - -- [Advanced Techniques](#advanced-techniques) - - [Blocking](#blocking) - - [poll() - Synchronous I/O Multiplexing](#poll---synchronous-io-multiplexing) - - [epoll() - I/O Event Notification (Async)](#epoll---io-event-notification-async) - -### Blocking - -All the Unix networking functions are **blocking**. What does it mean? It means that if you write `accept()` or `recv()` -it will wait until some data appears. So how we can avoid this? Making the socket non blocking so we can poll the socket -for info: - -```c -fcntl(sockfd, F_SETFL, O_NONBLOCK); -``` - -`F_SETFL`: set the file descriptor flags -`O_NONBLOCK`: make the fd non blocking - -### poll() - Synchronous I/O Multiplexing - -The plan is to have a `struct pollfd` with the info about which sockets fd we want to monitor. The Operating System will -block on the `poll()` call until one event occurs (e.g. "socket ready to write/read"). - -```c -#include - -int poll(struct pollfd fds[], nfds_t nfds, int timeout); -``` - -`fds` is our array of info (which sockets to monitor) -`nfds` the elements in the array -`timeout` in milliseconds (-1 to wait forever) - -```c -struct pollfd { - int fd; // the socket descriptor - short events; // bitmap of events we're interested in - short revents; // when poll() returns, bitmap of events that occurred -}; -``` - -`events` is a bitmap of the following values: - -- `POLLIN` (alert when I can read data) -- `POLLOUT` (alert when I can send data) - -**I won't continue this section, read below** - -### epoll() - I/O Event Notification (Async) - -It is similar to `poll()` but more efficient when dealing with lots of fds. The array is in the kernel space, no further -copies. Nice explaination [here](https://copyconstruct.medium.com/the-method-to-epolls-madness-d9d2d6378642). - -```c -#include - -int epoll_create1(int flags); -``` - -Just pass 0 for the `flags`, it is an improved version of the `epoll_create()`. It creates a new epoll instance and -returns the fd of that instance. - -```c -#include - -int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); -int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); -``` - -- `epoll_ctl()` is used to register new fds and add them to the interest list: - - `epfd` epoll file descriptor - - `op` the operation: `EPOLL_CTL_ADD`, `EPOLL_CTL_MOD` or `EPOLL_CTL_DEL` - - `fd` the file descriptor to check - - `event` a pointer to the `epoll_event` struct - -- `epoll_wait()` waits for I/O events, blocking the calling thread if no events are availables. - - `epfd` epoll file descriptor - - `events` array where there will be saved ready events - - `maxevents` max events per array - - `timeout` in milliseconds (-1 forever) - -```c -struct epoll_event { - uint32_t events; // bitmap - epoll_data_t data; -}; - -union epoll_data { - void *ptr; - int fd; // just use this - uint32_t u32; - uint64_t u64; -}; -``` diff --git a/notes/basic-concepts.md b/notes/basic-concepts.md deleted file mode 100644 index fecd042..0000000 --- a/notes/basic-concepts.md +++ /dev/null @@ -1,279 +0,0 @@ -# Basic Concepts - -Before reading, this document could contain errors, please check everything you read. - -- [Basic Concepts](#basic-concepts) - - [Socket](#socket) - - [Internet sockets](#internet-sockets) - - [Byte Order](#byte-order) - - [Structs](#structs) - - [struct addrinfo](#struct-addrinfo) - - [struct sockaddr](#struct-sockaddr) - - [struct sockaddr\_in](#struct-sockaddr_in) - - [struct sockaddr\_storage](#struct-sockaddr_storage) - - [IP Addresses](#ip-addresses) - - [getaddrinfo()](#getaddrinfo) - - [socket()](#socket-1) - - [bind()](#bind) - - [connect()](#connect) - - [listen()](#listen) - - [accept()](#accept) - - [send() and recv()](#send-and-recv) - - [sendto() and recvfrom()](#sendto-and-recvfrom) - - [close() and shutdown()](#close-and-shutdown) - - [getpeername()](#getpeername) - - [gethostname()](#gethostname) - -### Socket - -When Unix programs do some I/O they do it reading/writing to a **file descriptor**. A file descriptor is an integer -associated with an open file, it can be anything. To communicate over the internet using a file descriptor we'll make a -call to the `socket()` system routine. - -### Internet sockets - -There are two types of Internet sockets: - -1. Stream Sockets (SOCK_STREAM) - error-free and a realiable two-way communication (TCP) -2. Datagram Sockets (SOCK_DGRAM) - connectionless (UDP) - -### Byte Order - -- Big-Endian (also called **Network Byte Order**) -- Little-Endian - -Before making any transmission we have to convert the byte order to a Network Byte Order, we can do this with simple -functions: - -| Function | Description | -|----------|-----------------------| -| htons() | Host to Network Short | -| htonl() | Host to Network Long | -| ntohs() | Network to Host Short | -| ntohl() | Network to Host Long | - -And convert the answer to the host byte order. - -### Structs - -#### struct addrinfo - -This struct prepares the socket address strcutures for subsequent use. - -```c -struct addrinfo { - int ai_flags; // AI_PASSIVE, AI_CANONNAME, etc. - int ai_family; // AF_INET, AF_INET6, AF_UNSPEC - int ai_socktype; // SOCK_STREAM, SOCK_DGRAM - int ai_protocol; // use 0 for "any" - size_t ai_addrlen; // size of ai_addr in bytes - struct sockaddr *ai_addr; // struct sockaddr_in or _in6 - char *ai_canonname; // full canonical hostname - - struct addrinfo *ai_next; // linked list, next node -}; -``` - -#### struct sockaddr - -Inside the struct we can see there is a pointer to the `struct sockaddr`, that is defined as follows: - -```c -struct sockaddr { - unsigned short sa_family; // address family, AF_xxx - char sa_data[14]; // 14 bytes of protocol address -}; -``` - -#### struct sockaddr_in - -But, we can avoid to pack manually the stuff inside this struct and use the `struct sockaddr_in` (or -`struct sockaddr_in6` for IPv6) with a fast cast that is made for the Internet: - -```c -struct sockaddr_in { - short int sin_family; // Address family, AF_INET - unsigned short int sin_port; // Port number - struct in_addr sin_addr; // Internet address - unsigned char sin_zero[8]; // Same size as struct sockaddr -}; -``` - -`sin_zero` is used to pad the struct to the length of a sockaddr and it should be set to all zeros (`memset()`). - -#### struct sockaddr_storage - -This struct is designed to storage both IPv4 and IPv6 structures. Example, when a client is going to connect to your -server you don't know if it is a IPv4 or IPv6 so you use this struct and then cast to what you need (check the -`ss_family` first). - -```c -struct sockaddr_storage { - sa_family_t ss_family; // address family - - // all this is padding, implementation specific, ignore it: - char __ss_pad1[_SS_PAD1SIZE]; - int64_t __ss_align; - char __ss_pad2[_SS_PAD2SIZE]; -}; -``` - -### IP Addresses - -Here's a way to convert an IP address string into a struct. - -```c -struct sockaddr_in sa; -struct sockaddr_in6 sa6; - -inet_pton(AF_INET, "192.168.0.1", &(sa.sin_addr)); -inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); -``` - -There is a very easy function called `inet_pton()`, *pton* stands for **Presentation to network**. If you want to do the -same but from binary to string you have `inet_ntop()`. - -### getaddrinfo() - -```c -#include -#include -#include - -int getaddrinfo(const char *node, // e.g. "www.example.com" or IP - const char *service, // e.g. "http" or port number - const struct addrinfo *hints, - struct addrinfo **res); -``` - -The `node` could be a host name or IP address. `service` could be a port number or a service found in `/etc/services` ( -e.g. "http" or "ftp" or "telnet"). -`hints` points to a struct you already filled and `res` contains a linked list of results. - -### socket() - -```c -#include -#include - -int socket(int domain, int type, int protocol); -``` - -`domain` could be `PF_INET` or `PF_INET6`, `type` instead TCP or UDP and `protocol` to 0. `PF_INET` is very close to -`AF_INET`! However, we can avoid to put the stuff manually and use the results from `getaddrinfo()`. - -### bind() - -Do this if you're going to listen on a port. The port is used by the kernel to match an incoming packet to a socket -descriptor. - -```c -#include -#include - -int bind(int sockfd, struct sockaddr *my_addr, int addrlen); -``` - -### connect() - -```c -#include -#include - -int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); -``` - -### listen() - -```c -int listen(int sockfd, int backlog); -``` - -`backlog` is the amount of max clients allowed on the incoming queue. A client will wait in the queue until you accept -it. - -### accept() - -```c -#include -#include - -int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); -``` - -After you accept a client, the function will return a new socket file descriptor used to communicate. - -### send() and recv() - -```c -int send(int sockfd, const void *msg, int len, int flags); -``` - -Just put `flags` to 0. It will return the bytes sent, but sometimes it could not match the len of the data sent, it's up -to you to send the rest of the string (it should sent 1K of data without splitting). - -```c -int recv(int sockfd, void *buf, int len, int flags); -``` - -Put 0 at `flags` (see the man page for more info). The `sockfd` is the file descriptor to read from. The function could -return 0 (this means the remote side has closed the connection). - -### sendto() and recvfrom() - -It's the DGRAM equivalent of STREAM. Marked as *TODO*. - -### close() and shutdown() - -To close the connection just use the regular Unix file descriptor `close()` function: - -```c -close(sockfd); -``` - -If you want more control over how the socket closes there is `shutdown()`: - -```c -int shutdown(int sockfd, int how); -``` - -`how` is one of the following: -| how | Effect | -| --- | ------------------------ | -| 0 | No future receives | -| 1 | No future sends | -| 2 | No future receives/sends | -The 2 is like `close()`, use `close()`. - -### getpeername() - -This function is quite simple. It will tell you who is in the other side of the connection. - -```c -#include - -int getpeername(int sockfd, struct sockaddr *addr, int *addrlen); -``` - -Example of getting client's IP: - -```c -struct sockaddr_storage their; -socklen_t their_len = sizeof their; - -int clientfd = accept(sockfd, (struct sockaddr *)&their, &their_len); -getpeername(clientfd, (struct sockaddr *)&their, &their_len); -struct sockaddr_in *client = (struct sockaddr_in *)&their; -char client_ip[INET_ADDRSTRLEN]; -inet_ntop(AF_INET, &client->sin_addr, client_ip, INET_ADDRSTRLEN); -``` - -### gethostname() - -```c -#include - -int gethostname(char *hostname, size_t size); -``` - -Returns the name of the computer that your program is running on. diff --git a/notes/hash-table.md b/notes/hash-table.md deleted file mode 100644 index e10e9b4..0000000 --- a/notes/hash-table.md +++ /dev/null @@ -1,25 +0,0 @@ -# Hash Table - -Well, the moment has come. I never made a Hash Map algorithm, but in this scenario I have to save both fd and sockaddr ( -I could make a simply linked list, but it's a way to learn new things). - -Let's start with a little bit of theory. - -A *Hash Table* is a data structure also called **dictionary** or **map**. It maps *keys* to *values* thanks to a **hash -function** that computes and *index* (**hash code**) into an array of **buckets**. - -One problem could be the *hash collision* where the hash function computes the same index for different values. A fix -could be the **chaining** method, where for the same hash code you can make a linked list and append the index. Then the -lookup function will go through the list and find the key. - -In a Hash Map you can insert, delete and lookup (simply search). - -### Hash function - -The easiest way... don't judge me. - -- Integer keys: - $$ hash(\text{key}) = \text{key} \mod \text{table\_dim} $$ - -- String keys: - $$ hash(key) = \sum_{i=0}^{len(key) - 1} ascii\_value(key[i]) * prime\_number$$ diff --git a/notes/http-request.md b/notes/http-request.md deleted file mode 100644 index 6d53c58..0000000 --- a/notes/http-request.md +++ /dev/null @@ -1,33 +0,0 @@ -# HTTP Request - -This is an example of a basic HTTP request made from the browser: - -```bash -GET / HTTP/1.1 -User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT) -Host: www.tutorialspoint.com -Accept-Language: en-us -Accept-Encoding: gzip, deflate -Connection: Keep-Alive -``` - -> Thanks tutorialspoint - -The first line is a *request line*. It has: - -- Method (GET, POST, HEAD, ...) -- Location (the request resource, file) -- HTTP version - -# HTTP Response - -```bash -HTTP/1.1 200 OK\r\n -Content-Type: text/html\r\n -Content-Length: 88\r\n -Connection: close\r\n -\r\n - -``` - -`Content-Length` is the length of the body (in the response case is the length of html page) \ No newline at end of file diff --git a/src/http/http.c b/src/http/http.c index 60a2994..2f4466e 100644 --- a/src/http/http.c +++ b/src/http/http.c @@ -1,6 +1,6 @@ #include "http/http.h" -#include /* Debug */ +#include #include #include #include @@ -21,7 +21,7 @@ cws_http *cws_http_parse(char *request_str, int sockfd) { if (pch == NULL) { return NULL; } - printf("[client::http] method: %s\n", pch); + CWS_LOG_DEBUG("[client::http] method: %s", pch); cws_http_parse_method(request, pch); /* Parse location */ @@ -29,7 +29,7 @@ cws_http *cws_http_parse(char *request_str, int sockfd) { if (pch == NULL) { return NULL; } - printf("[client::http] location: %s\n", pch); + CWS_LOG_DEBUG("[client::http] location: %s", pch); strncpy(request->location, pch, CWS_HTTP_LOCATION_LEN); /* Parse location path */ @@ -38,14 +38,14 @@ cws_http *cws_http_parse(char *request_str, int sockfd) { } else { snprintf(request->location_path, CWS_HTTP_LOCATION_PATH_LEN, "%s%s", CWS_WWW, request->location); } - fprintf(stdout, "[client::http] location path: %s\n", request->location_path); + CWS_LOG_DEBUG("[client::http] location path: %s", request->location_path); /* Parse HTTP version */ pch = strtok(NULL, " \r\n"); if (pch == NULL) { return NULL; } - printf("[client::http] version: %s\n", pch); + CWS_LOG_DEBUG("[client::http] version: %s", pch); strncpy(request->http_version, pch, CWS_HTTP_VERSION_LEN); /* Parse other stuff... */ @@ -90,7 +90,7 @@ void cws_http_send_response(cws_http *request) { char *file_data = malloc(content_length); if (file_data == NULL) { fclose(file); - fprintf(stderr, RED "Unable to allocate file data\n"); + CWS_LOG_ERROR("Unable to allocate file data"); return; } diff --git a/src/main.c b/src/main.c index 6b8bcec..e978220 100644 --- a/src/main.c +++ b/src/main.c @@ -1,36 +1,31 @@ #include -#include #include +#include #include #include "server/server.h" #include "utils/colors.h" #include "utils/config.h" -void cws_signal_handler(int signo) { - fprintf(stdout, BLUE "[server] Cleaning up resources...\n" RESET); - cws_server_run = false; -} +void cws_signal_handler(int signo) { cws_server_run = false; } int main(int argc, char **argv) { int ret; cws_config *config = cws_config_init(); if (config == NULL) { - fprintf(stderr, RED BOLD "[server] Unable to read config file\n" RESET); + CWS_LOG_ERROR("[server] Unable to read config file"); return 1; } - struct sigaction act = { - .sa_handler = cws_signal_handler - }; + struct sigaction act = {.sa_handler = cws_signal_handler}; ret = sigaction(SIGINT, &act, NULL); - fprintf(stdout, BOLD GREEN "[server] Running cws on http://%s:%s...\n" RESET, config->host, config->port); + CWS_LOG_INFO("[server] Running cws on http://%s:%s...", config->host, config->port); ret = cws_server_start(config->host, config->port); if (ret < 0) { - fprintf(stderr, BOLD RED "[server] Unable to start web server\n" RESET); + CWS_LOG_ERROR("[server] Unable to start web server"); } cws_config_free(config); diff --git a/src/server/server.c b/src/server/server.c index 34a6c00..0138eb2 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -26,7 +26,7 @@ int cws_server_start(const char *hostname, const char *service) { int status = getaddrinfo(hostname, service, &hints, &res); if (status != 0) { - fprintf(stderr, RED BOLD "[server] getaddrinfo() error: %s\n" RESET, gai_strerror(status)); + CWS_LOG_ERROR("[server] getaddrinfo() error: %s", gai_strerror(status)); exit(EXIT_FAILURE); } @@ -35,19 +35,19 @@ int cws_server_start(const char *hostname, const char *service) { const int opt = 1; status = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt); if (status != 0) { - fprintf(stderr, RED BOLD "[server] setsockopt(): %s\n" RESET, strerror(errno)); + CWS_LOG_ERROR("[server] setsockopt(): %s", strerror(errno)); exit(EXIT_FAILURE); } status = bind(sockfd, res->ai_addr, res->ai_addrlen); if (status != 0) { - fprintf(stderr, RED BOLD "[server] bind(): %s\n" RESET, strerror(errno)); + CWS_LOG_ERROR("[server] bind(): %s", strerror(errno)); exit(EXIT_FAILURE); } status = listen(sockfd, CWS_SERVER_BACKLOG); if (status != 0) { - fprintf(stderr, RED BOLD "[server] listen(): %s\n" RESET, gai_strerror(status)); + CWS_LOG_ERROR("[server] listen(): %s", gai_strerror(status)); exit(EXIT_FAILURE); } @@ -61,10 +61,13 @@ int cws_server_start(const char *hostname, const char *service) { void cws_server_setup_hints(struct addrinfo *hints, size_t len, const char *hostname) { memset(hints, 0, len); + /* IPv4 or IPv6 */ hints->ai_family = AF_UNSPEC; + /* TCP */ hints->ai_socktype = SOCK_STREAM; + if (hostname == NULL) { /* Fill in IP for me */ hints->ai_flags = AI_PASSIVE; @@ -94,13 +97,14 @@ void cws_server_loop(int sockfd) { char ip[INET_ADDRSTRLEN]; client_fd = cws_server_accept_client(sockfd, &their_sa, &theirsa_size); cws_utils_get_client_ip(&their_sa, ip); - fprintf(stdout, BLUE "[server] Client (%s) connected\n" RESET, ip); + CWS_LOG_INFO("[server] Client (%s) connected", ip); cws_fd_set_nonblocking(client_fd); cws_epoll_add(epfd, client_fd, EPOLLIN); cws_hm_push(clients, client_fd, &their_sa); } else { char data[4096] = {0}; + /* Incoming data */ client_fd = revents[i].data.fd; const ssize_t bytes_read = recv(client_fd, data, sizeof data, 0); @@ -110,7 +114,7 @@ void cws_server_loop(int sockfd) { if (bytes_read == 0) { /* Client disconnected */ - fprintf(stdout, BLUE "[server] Client (%s) disconnected\n" RESET, ip); + CWS_LOG_INFO("[server] Client (%s) disconnected", ip); cws_server_close_client(epfd, client_fd, clients); continue; } @@ -135,7 +139,7 @@ void cws_server_loop(int sockfd) { } cws_http_send_response(request); - fprintf(stdout, BLUE "[server] Client (%s) disconnected\n" RESET, ip); + CWS_LOG_INFO("[server] Client (%s) disconnected", ip); cws_server_close_client(epfd, client_fd, clients); cws_http_free(request); @@ -150,7 +154,7 @@ void cws_server_loop(int sockfd) { close(epfd); cws_server_close_all_fds(clients); cws_hm_free(clients); - fprintf(stdout, BLUE "[server] Closing...\n" RESET); + CWS_LOG_INFO("[server] Closing..."); } void cws_epoll_add(int epfd, int sockfd, uint32_t events) { @@ -160,7 +164,7 @@ void cws_epoll_add(int epfd, int sockfd, uint32_t events) { 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)); + CWS_LOG_ERROR("[server] epoll_ctl_add(): %s", strerror(errno)); exit(EXIT_FAILURE); } } @@ -169,7 +173,7 @@ void cws_epoll_del(int epfd, int sockfd) { 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)); + CWS_LOG_ERROR("[server] epoll_ctl_del(): %s", strerror(errno)); exit(EXIT_FAILURE); } } @@ -178,7 +182,7 @@ void cws_fd_set_nonblocking(int sockfd) { const int status = fcntl(sockfd, F_SETFL, O_NONBLOCK); if (status == -1) { - fprintf(stderr, RED BOLD "[server] fcntl(): %s\n" RESET, gai_strerror(status)); + CWS_LOG_ERROR("[server] fcntl(): %s", gai_strerror(status)); exit(EXIT_FAILURE); } } @@ -188,7 +192,7 @@ int cws_server_accept_client(int sockfd, struct sockaddr_storage *their_sa, sock if (client_fd == -1) { if (errno != EWOULDBLOCK) { - fprintf(stderr, RED BOLD "[server] accept(): %s\n" RESET, strerror(errno)); + CWS_LOG_ERROR("[server] accept(): %s", strerror(errno)); } return -1; } diff --git a/src/utils/utils.c b/src/utils/utils.c index fc3880e..b9aa4a4 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -17,7 +17,7 @@ void cws_utils_print_ips(const char *hostname, const char *port) { int status = getaddrinfo(hostname, port, &ai, &res); if (status < 0) { - fprintf(stderr, RED "getaddrinfo(): %s\n" RESET, gai_strerror(status)); + CWS_LOG_ERROR("getaddrinfo(): %s", gai_strerror(status)); exit(1); } @@ -28,11 +28,11 @@ void cws_utils_print_ips(const char *hostname, const char *port) { if (p->ai_family == AF_INET) { struct sockaddr_in *sin = (struct sockaddr_in *)p->ai_addr; inet_ntop(AF_INET, &sin->sin_addr, ipv4, INET_ADDRSTRLEN); - fprintf(stdout, BLUE "%s\n" RESET, ipv4); + CWS_LOG_INFO("%s", ipv4); } else if (p->ai_family == AF_INET6) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)p->ai_addr; inet_ntop(AF_INET6, &sin6->sin6_addr, ipv6, INET6_ADDRSTRLEN); - fprintf(stdout, BLUE "%s\n" RESET, ipv6); + CWS_LOG_INFO("%s", ipv6); } }