refactor: change project structure
This commit is contained in:
10
include/core/epoll_utils.h
Normal file
10
include/core/epoll_utils.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef CWS_EPOLL_UTILS_H
|
||||||
|
#define CWS_EPOLL_UTILS_H
|
||||||
|
|
||||||
|
int cws_epoll_add(int epfd, int sockfd);
|
||||||
|
|
||||||
|
int cws_epoll_del(int epfd, int sockfd);
|
||||||
|
|
||||||
|
int cws_epoll_create_with_fd(int fd);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -6,8 +6,8 @@
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "utils/utils.h"
|
#include "core/worker.h"
|
||||||
#include "worker.h"
|
#include "utils/net_utils.h"
|
||||||
|
|
||||||
/* Clients max queue */
|
/* Clients max queue */
|
||||||
#define CWS_SERVER_BACKLOG 128
|
#define CWS_SERVER_BACKLOG 128
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#include "../config/config.h"
|
#include "config/config.h"
|
||||||
#include "../utils/utils.h"
|
#include "utils/net_utils.h"
|
||||||
|
|
||||||
extern volatile sig_atomic_t cws_server_run;
|
extern volatile sig_atomic_t cws_server_run;
|
||||||
|
|
||||||
13
include/http/mime.h
Normal file
13
include/http/mime.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef CWS_MIME_H
|
||||||
|
#define CWS_MIME_H
|
||||||
|
|
||||||
|
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
|
|
||||||
|
typedef struct mimetype {
|
||||||
|
const char *ext;
|
||||||
|
const char *type;
|
||||||
|
} mimetype;
|
||||||
|
|
||||||
|
int http_get_content_type(char *location_path, char *content_type);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,11 +5,9 @@
|
|||||||
#include <myclib/mystring.h>
|
#include <myclib/mystring.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
#define CWS_HTTP_CONTENT_TYPE 64
|
||||||
|
|
||||||
#define CWS_HTTP_HEADER_MAX 512
|
#define CWS_HTTP_HEADER_MAX 512
|
||||||
#define CWS_HTTP_HEADER_CONTENT_MAX 1024
|
#define CWS_HTTP_HEADER_CONTENT_MAX 1024
|
||||||
#define CWS_HTTP_CONTENT_TYPE 64
|
|
||||||
|
|
||||||
typedef enum cws_http_method {
|
typedef enum cws_http_method {
|
||||||
HTTP_GET,
|
HTTP_GET,
|
||||||
@@ -26,11 +24,6 @@ typedef enum cws_http_status {
|
|||||||
HTTP_NOT_IMPLEMENTED,
|
HTTP_NOT_IMPLEMENTED,
|
||||||
} cws_http_status_e;
|
} cws_http_status_e;
|
||||||
|
|
||||||
typedef struct mimetype {
|
|
||||||
const char *ext;
|
|
||||||
const char *type;
|
|
||||||
} mimetype;
|
|
||||||
|
|
||||||
typedef struct cws_http {
|
typedef struct cws_http {
|
||||||
int sockfd;
|
int sockfd;
|
||||||
cws_http_method_e method;
|
cws_http_method_e method;
|
||||||
14
include/http/response.h
Normal file
14
include/http/response.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef CWS_RESPONSE_H
|
||||||
|
#define CWS_RESPONSE_H
|
||||||
|
|
||||||
|
#include "http/request.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
size_t http_simple_html(char **response, cws_http_status_e status, char *title, char *description);
|
||||||
|
|
||||||
|
size_t http_response_builder(char **response, cws_http_status_e status, char *content_type,
|
||||||
|
char *body, size_t body_len_bytes);
|
||||||
|
|
||||||
|
void cws_http_send_response(cws_http_s *request, cws_http_status_e status);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#include "utils/utils.h"
|
|
||||||
|
|
||||||
cws_server_ret cws_epoll_add(int epfd, int sockfd);
|
|
||||||
|
|
||||||
cws_server_ret cws_epoll_del(int epfd, int sockfd);
|
|
||||||
|
|
||||||
int cws_epoll_create_with_fd(int fd);
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
#ifndef CWS_COLORS_H
|
#ifndef CWS_COLORS_H
|
||||||
#define CWS_COLORS_H
|
#define CWS_COLORS_H
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* ANSI color escape sequences */
|
/* ANSI color escape sequences */
|
||||||
#define RED "\033[31m"
|
#define RED "\033[31m"
|
||||||
#define GREEN "\033[32m"
|
#define GREEN "\033[32m"
|
||||||
|
|||||||
16
include/utils/hash_utils.h
Normal file
16
include/utils/hash_utils.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#ifndef CWS_HASH_UTILS_H
|
||||||
|
#define CWS_HASH_UTILS_H
|
||||||
|
|
||||||
|
unsigned int my_str_hash_fn(const void *key);
|
||||||
|
|
||||||
|
bool my_str_equal_fn(const void *a, const void *b);
|
||||||
|
|
||||||
|
void my_str_free_fn(void *value);
|
||||||
|
|
||||||
|
unsigned int my_int_hash_fn(const void *key);
|
||||||
|
|
||||||
|
bool my_int_equal_fn(const void *a, const void *b);
|
||||||
|
|
||||||
|
void my_int_free_key_fn(void *key);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -33,13 +33,4 @@ cws_server_ret cws_fd_set_nonblocking(int sockfd);
|
|||||||
|
|
||||||
void cws_utils_get_client_ip(struct sockaddr_storage *sa, char *ip);
|
void cws_utils_get_client_ip(struct sockaddr_storage *sa, char *ip);
|
||||||
|
|
||||||
/* Functions used for hash maps */
|
|
||||||
unsigned int my_str_hash_fn(const void *key);
|
|
||||||
bool my_str_equal_fn(const void *a, const void *b);
|
|
||||||
void my_str_free_fn(void *value);
|
|
||||||
|
|
||||||
unsigned int my_int_hash_fn(const void *key);
|
|
||||||
bool my_int_equal_fn(const void *a, const void *b);
|
|
||||||
void my_int_free_key_fn(void *key);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
32
src/core/epoll_utils.c
Normal file
32
src/core/epoll_utils.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include "core/epoll_utils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
|
||||||
|
int cws_epoll_add(int epfd, int sockfd) {
|
||||||
|
struct epoll_event event;
|
||||||
|
event.events = EPOLLIN;
|
||||||
|
event.data.fd = sockfd;
|
||||||
|
const int status = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cws_epoll_del(int epfd, int sockfd) {
|
||||||
|
const int status = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cws_epoll_create_with_fd(int fd) {
|
||||||
|
int epfd = epoll_create1(0);
|
||||||
|
if (epfd == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cws_epoll_add(epfd, fd) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return epfd;
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "server/server.h"
|
#include "core/server.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -7,10 +7,10 @@
|
|||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "server/epoll_utils.h"
|
#include "core/epoll_utils.h"
|
||||||
#include "server/worker.h"
|
#include "core/worker.h"
|
||||||
#include "utils/debug.h"
|
#include "utils/debug.h"
|
||||||
#include "utils/utils.h"
|
#include "utils/net_utils.h"
|
||||||
|
|
||||||
static void cws_server_setup_hints(struct addrinfo *hints, const char *hostname) {
|
static void cws_server_setup_hints(struct addrinfo *hints, const char *hostname) {
|
||||||
memset(hints, 0, sizeof *hints);
|
memset(hints, 0, sizeof *hints);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "utils/socket.h"
|
#include "core/socket_utils.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "server/worker.h"
|
#include "core/worker.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -7,10 +7,10 @@
|
|||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "http/http.h"
|
#include "core/epoll_utils.h"
|
||||||
#include "server/epoll_utils.h"
|
#include "core/socket_utils.h"
|
||||||
#include "utils/socket.h"
|
#include "http/request.h"
|
||||||
#include "utils/utils.h"
|
#include "utils/net_utils.h"
|
||||||
|
|
||||||
static cws_server_ret cws_worker_setup_epoll(cws_worker_s *worker) {
|
static cws_server_ret cws_worker_setup_epoll(cws_worker_s *worker) {
|
||||||
worker->epfd = epoll_create1(0);
|
worker->epfd = epoll_create1(0);
|
||||||
384
src/http/http.c
384
src/http/http.c
@@ -1,384 +0,0 @@
|
|||||||
#include "http/http.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <myclib/mystring.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "utils/debug.h"
|
|
||||||
#include "utils/socket.h"
|
|
||||||
#include "utils/utils.h"
|
|
||||||
|
|
||||||
static cws_http_s *http_new() {
|
|
||||||
cws_http_s *request = malloc(sizeof(cws_http_s));
|
|
||||||
if (!request) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(request, 0, sizeof *request);
|
|
||||||
|
|
||||||
request->http_version = string_new("", 16);
|
|
||||||
request->location = string_new("", 128);
|
|
||||||
request->location_path = string_new("", 128);
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
static cws_http_method_e http_parse_method(const char *method) {
|
|
||||||
if (strcmp(method, "GET") == 0) {
|
|
||||||
return HTTP_GET;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(method, "POST") == 0) {
|
|
||||||
return HTTP_POST;
|
|
||||||
}
|
|
||||||
|
|
||||||
return HTTP_UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
static mimetype mimetypes[] = {{"html", "text/html"},
|
|
||||||
{"css", "text/css"},
|
|
||||||
{"js", "application/javascript"},
|
|
||||||
{"jpg", "image/jpeg"},
|
|
||||||
{"png", "image/png"}};
|
|
||||||
|
|
||||||
static int http_get_content_type(char *location_path, char *content_type) {
|
|
||||||
/* Find last occurrence of a string */
|
|
||||||
char *ptr = strrchr(location_path, '.');
|
|
||||||
if (ptr == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
ptr += 1;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ARR_SIZE(mimetypes); ++i) {
|
|
||||||
if (!strcmp(ptr, mimetypes[i].ext)) {
|
|
||||||
snprintf(content_type, CWS_HTTP_CONTENT_TYPE - 1, "%s", mimetypes[i].type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *http_status_string(cws_http_status_e status) {
|
|
||||||
switch (status) {
|
|
||||||
case HTTP_OK: {
|
|
||||||
return "200 OK";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HTTP_NOT_FOUND: {
|
|
||||||
return "404 Not Found";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HTTP_NOT_IMPLEMENTED: {
|
|
||||||
return "501 Not Implemented";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "?";
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t file_data(const char *path, char **data) {
|
|
||||||
FILE *file = fopen(path, "rb");
|
|
||||||
if (!file) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Retrieve file size */
|
|
||||||
fseek(file, 0, SEEK_END);
|
|
||||||
const size_t content_length = ftell(file);
|
|
||||||
errno = 0;
|
|
||||||
rewind(file);
|
|
||||||
if (errno != 0) {
|
|
||||||
fclose(file);
|
|
||||||
CWS_LOG_ERROR("Unable to rewind");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Retrieve file data */
|
|
||||||
*data = malloc(content_length);
|
|
||||||
if (!*data) {
|
|
||||||
fclose(file);
|
|
||||||
CWS_LOG_ERROR("Unable to allocate file data");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read file data */
|
|
||||||
size_t read_bytes = fread(*data, 1, content_length, file);
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
if (read_bytes != content_length) {
|
|
||||||
CWS_LOG_ERROR("Partial read from file");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return content_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
static cws_server_ret http_send_resource(cws_http_s *request) {
|
|
||||||
/* Retrieve correct Content-Type */
|
|
||||||
char content_type[CWS_HTTP_CONTENT_TYPE];
|
|
||||||
http_get_content_type(request->location_path->data, content_type);
|
|
||||||
CWS_LOG_DEBUG("content-type: %s", content_type);
|
|
||||||
|
|
||||||
/* TODO: Check for keep-alive */
|
|
||||||
|
|
||||||
char *data = NULL;
|
|
||||||
char *response;
|
|
||||||
size_t content_length = file_data(request->location_path->data, &data);
|
|
||||||
|
|
||||||
size_t response_len =
|
|
||||||
http_response_builder(&response, HTTP_OK, content_type, data, content_length);
|
|
||||||
|
|
||||||
ssize_t sent = cws_send_data(request->sockfd, response, response_len, 0);
|
|
||||||
CWS_LOG_DEBUG("Sent %zd bytes", sent);
|
|
||||||
|
|
||||||
free(response);
|
|
||||||
free(data);
|
|
||||||
|
|
||||||
return CWS_SERVER_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t http_simple_html(char **response, cws_http_status_e status, char *title,
|
|
||||||
char *description) {
|
|
||||||
char body[512];
|
|
||||||
memset(body, 0, sizeof body);
|
|
||||||
|
|
||||||
snprintf(body, sizeof(body),
|
|
||||||
"<html>\n"
|
|
||||||
"<head>\n"
|
|
||||||
" <title>%s</title>\n"
|
|
||||||
"</head>\n"
|
|
||||||
"<body>\n"
|
|
||||||
"<p>%s</p>\n"
|
|
||||||
"</body>"
|
|
||||||
"</html>",
|
|
||||||
title, description);
|
|
||||||
size_t body_len = strlen(body);
|
|
||||||
|
|
||||||
size_t response_len = http_response_builder(response, status, "text/html", body, body_len);
|
|
||||||
|
|
||||||
return response_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
cws_http_s *cws_http_parse(string_s *request_str) {
|
|
||||||
if (!request_str || !request_str->data) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cws_http_s *request = http_new();
|
|
||||||
if (request == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *str = strdup(request_str->data);
|
|
||||||
if (!str) {
|
|
||||||
cws_http_free(request);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
char *str_free = str;
|
|
||||||
|
|
||||||
/* Parse HTTP method */
|
|
||||||
str += strspn(str, " ");
|
|
||||||
if (*str == '\0') {
|
|
||||||
free(str_free);
|
|
||||||
cws_http_free(request);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
size_t len = strcspn(str, " ");
|
|
||||||
if (str[len] == '\0') {
|
|
||||||
free(str_free);
|
|
||||||
cws_http_free(request);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
str[len] = '\0';
|
|
||||||
CWS_LOG_DEBUG("method: %s", str);
|
|
||||||
request->method = http_parse_method(str);
|
|
||||||
str += len + 1;
|
|
||||||
|
|
||||||
/* Parse location */
|
|
||||||
str += strspn(str, " ");
|
|
||||||
if (*str == '\0') {
|
|
||||||
free(str_free);
|
|
||||||
cws_http_free(request);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
len = strcspn(str, " ");
|
|
||||||
if (str[len] == '\0') {
|
|
||||||
free(str_free);
|
|
||||||
cws_http_free(request);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
str[len] = '\0';
|
|
||||||
string_append(request->location, str);
|
|
||||||
str += len + 1;
|
|
||||||
|
|
||||||
/* Adjust location path */
|
|
||||||
/* @TODO: fix path traversal */
|
|
||||||
string_append(request->location_path, "www");
|
|
||||||
if (strcmp(request->location->data, "/") == 0) {
|
|
||||||
string_append(request->location_path, "/index.html");
|
|
||||||
} else {
|
|
||||||
string_append(request->location_path, request->location->data);
|
|
||||||
}
|
|
||||||
CWS_LOG_DEBUG("location path: %s", request->location_path->data);
|
|
||||||
|
|
||||||
/* Parse HTTP version */
|
|
||||||
str += strspn(str, " \t");
|
|
||||||
if (*str == '\0') {
|
|
||||||
free(str_free);
|
|
||||||
cws_http_free(request);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
len = strcspn(str, "\r\n");
|
|
||||||
if (len == 0) {
|
|
||||||
free(str_free);
|
|
||||||
cws_http_free(request);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
str[len] = '\0';
|
|
||||||
CWS_LOG_DEBUG("version: %s", str);
|
|
||||||
string_append(request->http_version, str);
|
|
||||||
str += len;
|
|
||||||
|
|
||||||
/* Parse headers until a blank line (\r\n\r\n) */
|
|
||||||
request->headers =
|
|
||||||
hm_new(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn,
|
|
||||||
sizeof(char) * CWS_HTTP_HEADER_MAX, sizeof(char) * CWS_HTTP_HEADER_CONTENT_MAX);
|
|
||||||
|
|
||||||
str += strspn(str, "\r\n");
|
|
||||||
|
|
||||||
while (*str != '\0' && *str != '\r') {
|
|
||||||
char *line_end = strstr(str, "\r\n");
|
|
||||||
if (!line_end) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
*line_end = '\0';
|
|
||||||
|
|
||||||
char *colon = strchr(str, ':');
|
|
||||||
if (!colon) {
|
|
||||||
str = line_end + 2;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
*colon = '\0';
|
|
||||||
|
|
||||||
char *header_key = str;
|
|
||||||
char *header_value = colon + 1;
|
|
||||||
header_value += strspn(header_value, " \t");
|
|
||||||
|
|
||||||
char hk[CWS_HTTP_HEADER_MAX];
|
|
||||||
char hv[CWS_HTTP_HEADER_CONTENT_MAX];
|
|
||||||
|
|
||||||
strncpy(hk, header_key, sizeof(hk) - 1);
|
|
||||||
hk[sizeof(hk) - 1] = '\0';
|
|
||||||
|
|
||||||
strncpy(hv, header_value, sizeof(hv) - 1);
|
|
||||||
hv[sizeof(hv) - 1] = '\0';
|
|
||||||
|
|
||||||
hm_set(request->headers, hk, hv);
|
|
||||||
|
|
||||||
/* Move to the next line */
|
|
||||||
str = line_end + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(str_free);
|
|
||||||
|
|
||||||
/* TODO: Parse body */
|
|
||||||
/* str is at the beginning of the body */
|
|
||||||
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t http_header_len(char *status_code, char *content_type, size_t body_len) {
|
|
||||||
size_t len = snprintf(NULL, 0,
|
|
||||||
"HTTP/1.1 %s\r\n"
|
|
||||||
"Content-Type: %s\r\n"
|
|
||||||
"Content-Length: %zu\r\n"
|
|
||||||
"Connection: close\r\n"
|
|
||||||
"\r\n",
|
|
||||||
status_code, content_type, body_len);
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t http_response_builder(char **response, cws_http_status_e status, char *content_type,
|
|
||||||
char *body, size_t body_len_bytes) {
|
|
||||||
char *status_code = http_status_string(status);
|
|
||||||
|
|
||||||
size_t header_len = http_header_len(status_code, content_type, body_len_bytes);
|
|
||||||
size_t total_len = header_len + body_len_bytes;
|
|
||||||
|
|
||||||
*response = malloc(total_len + 1);
|
|
||||||
if (*response == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(*response, header_len + 1,
|
|
||||||
"HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n",
|
|
||||||
status_code, content_type, body_len_bytes);
|
|
||||||
|
|
||||||
/* Only append body if we have it */
|
|
||||||
if (body && body_len_bytes > 0) {
|
|
||||||
memcpy(*response + header_len, body, body_len_bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
(*response)[total_len] = '\0';
|
|
||||||
|
|
||||||
return total_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cws_http_send_response(cws_http_s *request, cws_http_status_e status) {
|
|
||||||
char *response = NULL;
|
|
||||||
|
|
||||||
switch (status) {
|
|
||||||
case HTTP_OK:
|
|
||||||
http_send_resource(request);
|
|
||||||
break;
|
|
||||||
case HTTP_NOT_FOUND: {
|
|
||||||
size_t len = http_simple_html(&response, HTTP_NOT_FOUND, "404 Not Found",
|
|
||||||
"Resource not found, 404.");
|
|
||||||
cws_send_data(request->sockfd, response, len, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case HTTP_NOT_IMPLEMENTED: {
|
|
||||||
size_t len = http_simple_html(&response, HTTP_NOT_IMPLEMENTED, "501 Not Implemented",
|
|
||||||
"Method not implemented, 501.");
|
|
||||||
cws_send_data(request->sockfd, response, len, 0);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cws_http_free(cws_http_s *request) {
|
|
||||||
if (!request) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request->headers) {
|
|
||||||
hm_free(request->headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request->http_version) {
|
|
||||||
string_free(request->http_version);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request->location) {
|
|
||||||
string_free(request->location);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request->location_path) {
|
|
||||||
string_free(request->location_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(request);
|
|
||||||
}
|
|
||||||
28
src/http/mime.c
Normal file
28
src/http/mime.c
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#include "http/mime.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "http/request.h"
|
||||||
|
|
||||||
|
static mimetype mimetypes[] = {{"html", "text/html"},
|
||||||
|
{"css", "text/css"},
|
||||||
|
{"js", "application/javascript"},
|
||||||
|
{"jpg", "image/jpeg"},
|
||||||
|
{"png", "image/png"}};
|
||||||
|
|
||||||
|
int http_get_content_type(char *location_path, char *content_type) {
|
||||||
|
/* Find last occurrence of a string */
|
||||||
|
char *ptr = strrchr(location_path, '.');
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
ptr += 1;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARR_SIZE(mimetypes); ++i) {
|
||||||
|
if (!strcmp(ptr, mimetypes[i].ext)) {
|
||||||
|
snprintf(content_type, CWS_HTTP_CONTENT_TYPE - 1, "%s", mimetypes[i].type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
190
src/http/request.c
Normal file
190
src/http/request.c
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
#include "http/request.h"
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <myclib/mystring.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "utils/debug.h"
|
||||||
|
#include "utils/hash_utils.h"
|
||||||
|
|
||||||
|
static cws_http_s *http_new() {
|
||||||
|
cws_http_s *request = malloc(sizeof(cws_http_s));
|
||||||
|
if (!request) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
memset(request, 0, sizeof *request);
|
||||||
|
|
||||||
|
request->http_version = string_new("", 16);
|
||||||
|
request->location = string_new("", 128);
|
||||||
|
request->location_path = string_new("", 128);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
static cws_http_method_e http_parse_method(const char *method) {
|
||||||
|
if (strcmp(method, "GET") == 0) {
|
||||||
|
return HTTP_GET;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(method, "POST") == 0) {
|
||||||
|
return HTTP_POST;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HTTP_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
cws_http_s *cws_http_parse(string_s *request_str) {
|
||||||
|
if (!request_str || !request_str->data) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
cws_http_s *request = http_new();
|
||||||
|
if (request == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *str = strdup(request_str->data);
|
||||||
|
if (!str) {
|
||||||
|
cws_http_free(request);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char *str_free = str;
|
||||||
|
|
||||||
|
/* Parse HTTP method */
|
||||||
|
str += strspn(str, " ");
|
||||||
|
if (*str == '\0') {
|
||||||
|
free(str_free);
|
||||||
|
cws_http_free(request);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
size_t len = strcspn(str, " ");
|
||||||
|
if (str[len] == '\0') {
|
||||||
|
free(str_free);
|
||||||
|
cws_http_free(request);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
str[len] = '\0';
|
||||||
|
CWS_LOG_DEBUG("method: %s", str);
|
||||||
|
request->method = http_parse_method(str);
|
||||||
|
str += len + 1;
|
||||||
|
|
||||||
|
/* Parse location */
|
||||||
|
str += strspn(str, " ");
|
||||||
|
if (*str == '\0') {
|
||||||
|
free(str_free);
|
||||||
|
cws_http_free(request);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
len = strcspn(str, " ");
|
||||||
|
if (str[len] == '\0') {
|
||||||
|
free(str_free);
|
||||||
|
cws_http_free(request);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
str[len] = '\0';
|
||||||
|
string_append(request->location, str);
|
||||||
|
str += len + 1;
|
||||||
|
|
||||||
|
/* Adjust location path */
|
||||||
|
/* @TODO: fix path traversal */
|
||||||
|
string_append(request->location_path, "www");
|
||||||
|
if (strcmp(request->location->data, "/") == 0) {
|
||||||
|
string_append(request->location_path, "/index.html");
|
||||||
|
} else {
|
||||||
|
string_append(request->location_path, request->location->data);
|
||||||
|
}
|
||||||
|
CWS_LOG_DEBUG("location path: %s", request->location_path->data);
|
||||||
|
|
||||||
|
/* Parse HTTP version */
|
||||||
|
str += strspn(str, " \t");
|
||||||
|
if (*str == '\0') {
|
||||||
|
free(str_free);
|
||||||
|
cws_http_free(request);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
len = strcspn(str, "\r\n");
|
||||||
|
if (len == 0) {
|
||||||
|
free(str_free);
|
||||||
|
cws_http_free(request);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
str[len] = '\0';
|
||||||
|
CWS_LOG_DEBUG("version: %s", str);
|
||||||
|
string_append(request->http_version, str);
|
||||||
|
str += len;
|
||||||
|
|
||||||
|
/* Parse headers until a blank line (\r\n\r\n) */
|
||||||
|
request->headers =
|
||||||
|
hm_new(my_str_hash_fn, my_str_equal_fn, my_str_free_fn, my_str_free_fn,
|
||||||
|
sizeof(char) * CWS_HTTP_HEADER_MAX, sizeof(char) * CWS_HTTP_HEADER_CONTENT_MAX);
|
||||||
|
|
||||||
|
str += strspn(str, "\r\n");
|
||||||
|
|
||||||
|
while (*str != '\0' && *str != '\r') {
|
||||||
|
char *line_end = strstr(str, "\r\n");
|
||||||
|
if (!line_end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*line_end = '\0';
|
||||||
|
|
||||||
|
char *colon = strchr(str, ':');
|
||||||
|
if (!colon) {
|
||||||
|
str = line_end + 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
*colon = '\0';
|
||||||
|
|
||||||
|
char *header_key = str;
|
||||||
|
char *header_value = colon + 1;
|
||||||
|
header_value += strspn(header_value, " \t");
|
||||||
|
|
||||||
|
char hk[CWS_HTTP_HEADER_MAX];
|
||||||
|
char hv[CWS_HTTP_HEADER_CONTENT_MAX];
|
||||||
|
|
||||||
|
strncpy(hk, header_key, sizeof(hk) - 1);
|
||||||
|
hk[sizeof(hk) - 1] = '\0';
|
||||||
|
|
||||||
|
strncpy(hv, header_value, sizeof(hv) - 1);
|
||||||
|
hv[sizeof(hv) - 1] = '\0';
|
||||||
|
|
||||||
|
hm_set(request->headers, hk, hv);
|
||||||
|
|
||||||
|
/* Move to the next line */
|
||||||
|
str = line_end + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(str_free);
|
||||||
|
|
||||||
|
/* TODO: Parse body */
|
||||||
|
/* str is at the beginning of the body */
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cws_http_free(cws_http_s *request) {
|
||||||
|
if (!request) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request->headers) {
|
||||||
|
hm_free(request->headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request->http_version) {
|
||||||
|
string_free(request->http_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request->location) {
|
||||||
|
string_free(request->location);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request->location_path) {
|
||||||
|
string_free(request->location_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(request);
|
||||||
|
}
|
||||||
174
src/http/response.c
Normal file
174
src/http/response.c
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
#include "http/response.h"
|
||||||
|
|
||||||
|
#include "http/mime.h"
|
||||||
|
#include "utils/debug.h"
|
||||||
|
#include <core/socket_utils.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
size_t http_simple_html(char **response, cws_http_status_e status, char *title, char *description) {
|
||||||
|
char body[512] = {0};
|
||||||
|
|
||||||
|
snprintf(body, sizeof(body),
|
||||||
|
"<html>\n"
|
||||||
|
"<head>\n"
|
||||||
|
" <title>%s</title>\n"
|
||||||
|
"</head>\n"
|
||||||
|
"<body>\n"
|
||||||
|
"<p>%s</p>\n"
|
||||||
|
"</body>"
|
||||||
|
"</html>",
|
||||||
|
title, description);
|
||||||
|
size_t body_len = strlen(body);
|
||||||
|
|
||||||
|
size_t response_len = http_response_builder(response, status, "text/html", body, body_len);
|
||||||
|
|
||||||
|
return response_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t http_header_len(char *status_code, char *content_type, size_t body_len) {
|
||||||
|
size_t len = snprintf(NULL, 0,
|
||||||
|
"HTTP/1.1 %s\r\n"
|
||||||
|
"Content-Type: %s\r\n"
|
||||||
|
"Content-Length: %zu\r\n"
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n",
|
||||||
|
status_code, content_type, body_len);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *http_status_string(cws_http_status_e status) {
|
||||||
|
switch (status) {
|
||||||
|
case HTTP_OK: {
|
||||||
|
return "200 OK";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HTTP_NOT_FOUND: {
|
||||||
|
return "404 Not Found";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HTTP_NOT_IMPLEMENTED: {
|
||||||
|
return "501 Not Implemented";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t file_data(const char *path, char **data) {
|
||||||
|
FILE *file = fopen(path, "rb");
|
||||||
|
if (!file) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve file size */
|
||||||
|
fseek(file, 0, SEEK_END);
|
||||||
|
const size_t content_length = ftell(file);
|
||||||
|
errno = 0;
|
||||||
|
rewind(file);
|
||||||
|
if (errno != 0) {
|
||||||
|
fclose(file);
|
||||||
|
CWS_LOG_ERROR("Unable to rewind");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve file data */
|
||||||
|
*data = malloc(content_length);
|
||||||
|
if (!*data) {
|
||||||
|
fclose(file);
|
||||||
|
CWS_LOG_ERROR("Unable to allocate file data");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read file data */
|
||||||
|
size_t read_bytes = fread(*data, 1, content_length, file);
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
if (read_bytes != content_length) {
|
||||||
|
CWS_LOG_ERROR("Partial read from file");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return content_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void http_send_resource(cws_http_s *request) {
|
||||||
|
/* Retrieve correct Content-Type */
|
||||||
|
char content_type[CWS_HTTP_CONTENT_TYPE];
|
||||||
|
http_get_content_type(request->location_path->data, content_type);
|
||||||
|
CWS_LOG_DEBUG("content-type: %s", content_type);
|
||||||
|
|
||||||
|
/* TODO: Check for keep-alive */
|
||||||
|
|
||||||
|
char *data = NULL;
|
||||||
|
char *response;
|
||||||
|
size_t content_length = file_data(request->location_path->data, &data);
|
||||||
|
|
||||||
|
size_t response_len =
|
||||||
|
http_response_builder(&response, HTTP_OK, content_type, data, content_length);
|
||||||
|
|
||||||
|
ssize_t sent = cws_send_data(request->sockfd, response, response_len, 0);
|
||||||
|
CWS_LOG_DEBUG("Sent %zd bytes", sent);
|
||||||
|
|
||||||
|
free(response);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t http_response_builder(char **response, cws_http_status_e status, char *content_type,
|
||||||
|
char *body, size_t body_len_bytes) {
|
||||||
|
char *status_code = http_status_string(status);
|
||||||
|
|
||||||
|
size_t header_len = http_header_len(status_code, content_type, body_len_bytes);
|
||||||
|
size_t total_len = header_len + body_len_bytes;
|
||||||
|
|
||||||
|
*response = malloc(total_len + 1);
|
||||||
|
if (*response == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(*response, header_len + 1,
|
||||||
|
"HTTP/1.1 %s\r\nContent-Type: %s\r\nContent-Length: %zu\r\nConnection: close\r\n\r\n",
|
||||||
|
status_code, content_type, body_len_bytes);
|
||||||
|
|
||||||
|
/* Only append body if we have it */
|
||||||
|
if (body && body_len_bytes > 0) {
|
||||||
|
memcpy(*response + header_len, body, body_len_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
(*response)[total_len] = '\0';
|
||||||
|
|
||||||
|
return total_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cws_http_send_response(cws_http_s *request, cws_http_status_e status) {
|
||||||
|
char *response = NULL;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case HTTP_OK:
|
||||||
|
http_send_resource(request);
|
||||||
|
break;
|
||||||
|
case HTTP_NOT_FOUND: {
|
||||||
|
size_t len = http_simple_html(&response, HTTP_NOT_FOUND, "404 Not Found",
|
||||||
|
"Resource not found, 404.");
|
||||||
|
cws_send_data(request->sockfd, response, len, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HTTP_NOT_IMPLEMENTED: {
|
||||||
|
size_t len = http_simple_html(&response, HTTP_NOT_IMPLEMENTED, "501 Not Implemented",
|
||||||
|
"Method not implemented, 501.");
|
||||||
|
cws_send_data(request->sockfd, response, len, 0);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(response);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "server/server.h"
|
#include "core/server.h"
|
||||||
#include "utils/debug.h"
|
#include "utils/debug.h"
|
||||||
|
|
||||||
void cws_signal_handler(int) {
|
void cws_signal_handler(int) {
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
server = files('main.c')
|
server = files('main.c')
|
||||||
server += files(
|
server += files(
|
||||||
'server/epoll_utils.c',
|
'core/epoll_utils.c',
|
||||||
'server/server.c',
|
'core/server.c',
|
||||||
'server/worker.c',
|
'core/socket_utils.c',
|
||||||
|
'core/worker.c',
|
||||||
)
|
)
|
||||||
server += files('config/config.c')
|
server += files('config/config.c')
|
||||||
server += files('utils/socket.c', 'utils/utils.c')
|
server += files('utils/hash_utils.c', 'utils/net_utils.c')
|
||||||
server += files('http/http.c')
|
server += files(
|
||||||
|
'http/mime.c',
|
||||||
|
'http/request.c',
|
||||||
|
'http/response.c',
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
#include "server/epoll_utils.h"
|
|
||||||
|
|
||||||
#include <sys/epoll.h>
|
|
||||||
|
|
||||||
#include "utils/debug.h"
|
|
||||||
#include "utils/utils.h"
|
|
||||||
|
|
||||||
cws_server_ret cws_epoll_add(int epfd, int sockfd) {
|
|
||||||
struct epoll_event event;
|
|
||||||
event.events = EPOLLIN;
|
|
||||||
event.data.fd = sockfd;
|
|
||||||
const int status = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
|
|
||||||
|
|
||||||
if (status != 0) {
|
|
||||||
CWS_LOG_ERROR("epoll_ctl_add()");
|
|
||||||
return CWS_SERVER_EPOLL_ADD_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CWS_SERVER_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
cws_server_ret cws_epoll_del(int epfd, int sockfd) {
|
|
||||||
const int status = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
|
|
||||||
|
|
||||||
if (status != 0) {
|
|
||||||
CWS_LOG_ERROR("epoll_ctl_del()");
|
|
||||||
return CWS_SERVER_EPOLL_DEL_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CWS_SERVER_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cws_epoll_create_with_fd(int fd) {
|
|
||||||
int epfd = epoll_create1(0);
|
|
||||||
if (epfd == -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (cws_epoll_add(epfd, fd) != CWS_SERVER_OK) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return epfd;
|
|
||||||
}
|
|
||||||
51
src/utils/hash_utils.c
Normal file
51
src/utils/hash_utils.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "utils/hash_utils.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
unsigned int my_str_hash_fn(const void *key) {
|
||||||
|
char *key_str = (char *)key;
|
||||||
|
size_t key_len = strlen(key_str);
|
||||||
|
|
||||||
|
int total = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < key_len; ++i) {
|
||||||
|
total += (int)key_str[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return total % 2069;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool my_str_equal_fn(const void *a, const void *b) {
|
||||||
|
if (strcmp((char *)a, (char *)b) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void my_str_free_fn(void *value) {
|
||||||
|
free(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int my_int_hash_fn(const void *key) {
|
||||||
|
return *(int *)key;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool my_int_equal_fn(const void *a, const void *b) {
|
||||||
|
int ai = *(int *)a;
|
||||||
|
int bi = *(int *)b;
|
||||||
|
|
||||||
|
if (ai == bi) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void my_int_free_key_fn(void *key) {
|
||||||
|
int fd = *(int *)key;
|
||||||
|
close(fd);
|
||||||
|
free(key);
|
||||||
|
}
|
||||||
31
src/utils/net_utils.c
Normal file
31
src/utils/net_utils.c
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "utils/net_utils.h"
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
volatile sig_atomic_t cws_server_run = 1;
|
||||||
|
|
||||||
|
static void cws_utils_convert_ip(int family, void *addr, char *ip, size_t ip_len) {
|
||||||
|
inet_ntop(family, addr, ip, ip_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cws_utils_get_client_ip(struct sockaddr_storage *sa, char *ip) {
|
||||||
|
if (sa->ss_family == AF_INET) {
|
||||||
|
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
||||||
|
cws_utils_convert_ip(AF_INET, &sin->sin_addr, ip, INET_ADDRSTRLEN);
|
||||||
|
} else if (sa->ss_family == AF_INET6) {
|
||||||
|
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
||||||
|
cws_utils_convert_ip(AF_INET6, &sin6->sin6_addr, ip, INET6_ADDRSTRLEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cws_server_ret cws_fd_set_nonblocking(int sockfd) {
|
||||||
|
const int status = fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
if (status == -1) {
|
||||||
|
return CWS_SERVER_FD_NONBLOCKING_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CWS_SERVER_OK;
|
||||||
|
}
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#include "utils/utils.h"
|
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
volatile sig_atomic_t cws_server_run = 1;
|
|
||||||
|
|
||||||
static void cws_utils_convert_ip(int family, void *addr, char *ip, size_t ip_len) {
|
|
||||||
inet_ntop(family, addr, ip, ip_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cws_utils_get_client_ip(struct sockaddr_storage *sa, char *ip) {
|
|
||||||
if (sa->ss_family == AF_INET) {
|
|
||||||
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
|
|
||||||
cws_utils_convert_ip(AF_INET, &sin->sin_addr, ip, INET_ADDRSTRLEN);
|
|
||||||
} else if (sa->ss_family == AF_INET6) {
|
|
||||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
|
|
||||||
cws_utils_convert_ip(AF_INET6, &sin6->sin6_addr, ip, INET6_ADDRSTRLEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cws_server_ret cws_fd_set_nonblocking(int sockfd) {
|
|
||||||
const int status = fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
|
||||||
|
|
||||||
if (status == -1) {
|
|
||||||
return CWS_SERVER_FD_NONBLOCKING_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
return CWS_SERVER_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int my_str_hash_fn(const void *key) {
|
|
||||||
char *key_str = (char *)key;
|
|
||||||
size_t key_len = strlen(key_str);
|
|
||||||
|
|
||||||
int total = 0;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < key_len; ++i) {
|
|
||||||
total += (int)key_str[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return total % 2069;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool my_str_equal_fn(const void *a, const void *b) {
|
|
||||||
if (strcmp((char *)a, (char *)b) == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void my_str_free_fn(void *value) {
|
|
||||||
free(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int my_int_hash_fn(const void *key) {
|
|
||||||
return *(int *)key;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool my_int_equal_fn(const void *a, const void *b) {
|
|
||||||
int ai = *(int *)a;
|
|
||||||
int bi = *(int *)b;
|
|
||||||
|
|
||||||
if (ai == bi) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void my_int_free_key_fn(void *key) {
|
|
||||||
int fd = *(int *)key;
|
|
||||||
close(fd);
|
|
||||||
free(key);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user