Skip to content
This repository was archived by the owner on Mar 23, 2025. It is now read-only.

Commit 4d787b0

Browse files
Merged in changes from the radio project, since a number of changes had been made which fixed various bugs in the web server
1 parent 16ea16b commit 4d787b0

20 files changed

+560
-248
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ build
44
.config
55
.cache
66
.clangd
7-
compile_commands.json
7+
compile_commands.json
8+
**/vendor

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
[submodule "src/vendor/readerwriterqueue"]
22
path = src/vendor/readerwriterqueue
33
url = https://github.com/cameron314/readerwriterqueue
4+
[submodule "src/vendor/json"]
5+
path = src/vendor/json
6+
url = https://github.com/nlohmann/json.git

server

1.58 MB
Binary file not shown.

src/CMakeLists.txt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ project(webserver)
44

55
set(CMAKE_CXX_STANDARD 11)
66

7-
# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
8-
# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
7+
# set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -Og -fsanitize=address,undefined")
8+
# set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -Og -fsanitize=address,undefined")
9+
10+
# set (CMAKE_CXX_FLAGS_RELEASE "-03")
911

1012
set( SOURCE_FILE_LIST ${SOURCE_FILES} ) # SOURCE_FILES is a space separated list of files passed in on the command line
1113
separate_arguments(SOURCE_FILE_LIST) # the string list is made into an actual list
1214

1315
add_executable(webserver ${SOURCE_FILE_LIST}) # the list is passed here to actually set the source files
14-
#SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG=1")
15-
target_link_libraries(webserver -luring -lcrypto -lwolfssl -lpthread)
16+
# SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_GLIBCXX_DEBUG=1")
17+
target_link_libraries(webserver -luring -lcrypto -lwolfssl -lpthread -lcurl)

src/header/data_store.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <unordered_set>
66

77
#include <iostream>
8+
#include <queue>
89

910
// If using this across multiple threads, only call free_item/allocate_item on one thread,
1011
// and only those, after you're sure that (when deleting) the ptr you're using is definitely
@@ -21,7 +22,7 @@ namespace data_store_namespace {
2122
if(--item.second == 0){
2223
free(data_vec[idx].first);
2324
data_vec[idx] = { nullptr, -1 };
24-
free_idxs.insert(idx);
25+
free_idxs.insert(free_idxs.end(), idx);
2526
}
2627
}
2728

@@ -61,7 +62,7 @@ namespace data_store_namespace {
6162

6263
class data_store {
6364
std::vector<std::pair<std::vector<char>, int>> data_vec{};
64-
std::unordered_set<int> free_idxs{};
65+
std::queue<int> free_idxs{};
6566

6667
struct buff {
6768
void *ptr{};
@@ -80,15 +81,23 @@ namespace data_store_namespace {
8081
auto &item = data_vec[idx];
8182
if(--item.second == 0){
8283
data_vec[idx] = { {}, -1 };
83-
free_idxs.insert(idx);
84+
free_idxs.push(idx);
8485
}
8586
}
8687

88+
size_t const size() { return data_vec.size() - free_idxs.size(); }
89+
size_t const full_size() {
90+
size_t all_size{};
91+
for(std::pair<std::vector<char>, int> &item : data_vec)
92+
all_size += item.first.size();
93+
return all_size;
94+
}
95+
8796
buff_idx_pair make_item(size_t size, int uses){ // used to allocate and insert an item
8897
int idx = 0;
8998
if(free_idxs.size() > 0){
90-
idx = *free_idxs.cbegin();
91-
free_idxs.erase(idx);
99+
idx = free_idxs.front();
100+
free_idxs.pop();
92101
}else{
93102
data_vec.emplace_back();
94103
idx = data_vec.size() - 1;
@@ -101,8 +110,8 @@ namespace data_store_namespace {
101110
buff_idx_pair insert_item(std::vector<char> &&buff, int uses){ // inserts and from then on assume the buff belongs to this data store
102111
int idx = 0;
103112
if(free_idxs.size() > 0){
104-
idx = *free_idxs.cbegin();
105-
free_idxs.erase(idx);
113+
idx = free_idxs.front();
114+
free_idxs.pop();
106115
}else{
107116
data_vec.emplace_back();
108117
idx = data_vec.size() - 1;

src/header/server.h

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
#include <sys/syscall.h> //syscall stuff parameters (as in like __NR_io_uring_enter/__NR_io_uring_setup)
1010
#include <sys/mman.h> //for mmap
11-
#include <sys/eventfd.h> // for eventfd
12-
#include <sys/timerfd.h> // for timerfd
11+
#include <sys/eventfd.h> // for eventfd=
1312

1413
#include <liburing.h> //for liburing
1514

@@ -61,7 +60,7 @@ namespace tcp_tls_server {
6160
// fields used for any request
6261
event_type event;
6362
int client_idx = -1;
64-
int ID = -1;
63+
int ID = 0;
6564

6665
// fields used for write requests
6766
size_t written{}; //how much written so far
@@ -71,6 +70,7 @@ namespace tcp_tls_server {
7170
// fields used for read requests
7271
std::vector<char> read_data{};
7372
size_t read_amount{}; //how much has been read (in case of multi read requests)
73+
bool auto_retry = false; // whether or not to use custom_read_req_continued
7474

7575
// extra
7676
int64_t custom_info{}; //any custom info you want to attach to the request
@@ -124,7 +124,7 @@ namespace tcp_tls_server {
124124
};
125125

126126
struct client_base {
127-
int id = -1;
127+
int id = 0; // id is only used to ensure the connection is unique
128128
int sockfd = -1;
129129
std::queue<write_data> send_data{};
130130

@@ -162,20 +162,17 @@ namespace tcp_tls_server {
162162
std::set<int> freed_indexes{}; //using a set to store free indexes instead
163163
std::vector<client<T>> clients{};
164164

165-
int timerfd = timerfd_create(CLOCK_MONOTONIC, 0); // used for pinging connections
166-
167165
void add_tcp_accept_req();
168166

169167
//need it protected rather than private, since need to access from children
170168
int add_write_req(int client_idx, event_type event, const char *buffer, unsigned int length); //this is for the case you want to write a buffer rather than a vector
171169
//used internally for sending messages
172170
int add_read_req(int client_idx, event_type event); //adds a read request to the io_uring ring
173-
//arms the timerfd
174-
void add_timerfd_read_req();
175171

176172
void custom_read_req_continued(request *req, size_t last_read); //to finish off partial reads
177173

178174
int setup_client(int client_idx);
175+
void clean_up_client_resources(int client_idx, bool trigger_callback = true); // used for cleaning up client resources
179176

180177
void event_read(int event_fd, event_type event); //will set a read request for the eventfd
181178

@@ -203,12 +200,14 @@ namespace tcp_tls_server {
203200
void read_connection(int client_idx);
204201

205202
//to read for a custom fd and be notified via the CUSTOM_READ event
206-
void custom_read_req(int fd, size_t to_read, int client_idx = -1, std::vector<char> &&buff = {}, size_t read_amount = 0);
203+
void custom_read_req(int fd, size_t to_read, bool auto_retry = false, int client_idx = -1, std::vector<char> &&buff = {}, size_t read_amount = 0); // auto_retry is for calling custom_read_req_continued
207204

208205
void notify_event();
209206
void kill_server(); // will kill the server
210207

211208
bool is_active = true; // is the server active (only false once it received an exit signal)
209+
210+
std::string get_ip_address(int client_idx);
212211
};
213212

214213
template<>
@@ -250,7 +249,7 @@ namespace tcp_tls_server {
250249
}
251250

252251
template<typename U>
253-
void broadcast_message(U begin, U end, int num_clients, const char *buff, size_t length, uint64_t custom_info = 0){ //if the buff pointer is ever invalidated, it will just fail to write - so sort of unsafe on its own
252+
void broadcast_message(U begin, U end, int num_clients, const char *buff, size_t length, uint64_t custom_info = -1){ //if the buff pointer is ever invalidated, it will just fail to write - so sort of unsafe on its own
254253
if(num_clients > 0){
255254
for(auto client_idx_ptr = begin; client_idx_ptr != end; client_idx_ptr++){
256255
auto &client = clients[(int)*client_idx_ptr];
@@ -265,7 +264,9 @@ namespace tcp_tls_server {
265264

266265
void write_connection(int client_idx, std::vector<char> &&buff); //writing depends on TLS or SSL, unlike read
267266
void write_connection(int client_idx, char *buff, size_t length); //writing but using a char pointer, doesn't do anything to the data
268-
void close_connection(int client_idx); //closing depends on what resources need to be freed
267+
268+
void start_closing_connection(int client_idx); //closing depends on what resources need to be freed
269+
void finish_closing_connection(int client_idx); //closing depends on what resources need to be freed
269270
};
270271

271272
template<>
@@ -317,7 +318,7 @@ namespace tcp_tls_server {
317318
}
318319

319320
template<typename U>
320-
void broadcast_message(U begin, U end, int num_clients, const char *buff, size_t length, uint64_t custom_info = 0){ //if the buff pointer is ever invalidated, it will just fail to write - so sort of unsafe on its own
321+
void broadcast_message(U begin, U end, int num_clients, const char *buff, size_t length, uint64_t custom_info = -1){ //if the buff pointer is ever invalidated, it will just fail to write - so sort of unsafe on its own
321322
if(num_clients > 0){
322323
for(auto client_idx_ptr = begin; client_idx_ptr != end; client_idx_ptr++){
323324
auto &client = clients[(int)*client_idx_ptr];
@@ -332,10 +333,12 @@ namespace tcp_tls_server {
332333

333334
void write_connection(int client_idx, std::vector<char> &&buff); //writing depends on TLS or SSL, unlike read
334335
void write_connection(int client_idx, char *buff, size_t length); //writing but using a char pointer, doesn't do anything to the data
335-
void close_connection(int client_idx); //closing depends on what resources need to be freed
336+
337+
void start_closing_connection(int client_idx); //closing depends on what resources need to be freed
338+
void finish_closing_connection(int client_idx); //closing depends on what resources need to be freed
336339
};
337340

338341
#include "../tcp_server/server_base.tcc" //template implementation file
339342
}
340343

341-
#endif
344+
#endif

src/header/server_metadata.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
#ifndef SERVER_ENUMS
22
#define SERVER_ENUMS
33

4+
#include <linux/limits.h>
45
#include <vector> //for vectors
56

67
enum class server_type { TLS, NON_TLS };
78

9+
constexpr size_t inotify_read_size = 8192; // to read many events, for large moves for example
10+
811
// for use elsewhere too
912
constexpr int QUEUE_DEPTH = 256; //the maximum number of events which can be submitted to the io_uring submission queue ring at once, you can have many more pending requests though
1013

1114
namespace tcp_tls_server {
12-
enum class event_type{ ACCEPT, ACCEPT_READ, ACCEPT_WRITE, READ, WRITE, NOTIFICATION, CUSTOM_READ, TIMERFD, KILL };
15+
enum class event_type{ ACCEPT, ACCEPT_READ, ACCEPT_WRITE, READ, READ_FINAL, WRITE, NOTIFICATION, CUSTOM_READ, KILL };
1316

1417
constexpr int BACKLOG = 10; //max number of connections pending acceptance
1518
constexpr int READ_SIZE = 8192; //how much one read request should read
@@ -28,6 +31,6 @@ namespace tcp_tls_server {
2831
#define READ_CB_PARAMS int client_idx, char* buffer, unsigned int length, tcp_tls_server::server<T> *tcp_server, void *custom_obj
2932
#define WRITE_CB_PARAMS int client_idx, int broadcast_additional_info, tcp_tls_server::server<T> *tcp_server, void *custom_obj
3033
#define EVENT_CB_PARAMS tcp_tls_server::server<T> *tcp_server, void *custom_obj
31-
#define CUSTOM_READ_CB_PARAMS int client_idx, int fd, std::vector<char> &&buff, tcp_tls_server::server<T> *tcp_server, void *custom_obj
34+
#define CUSTOM_READ_CB_PARAMS int client_idx, int fd, std::vector<char> &&buff, size_t read_bytes, tcp_tls_server::server<T> *tcp_server, void *custom_obj
3235

33-
#endif
36+
#endif

src/header/utility.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <unistd.h> //read
1414
#include <fcntl.h> //open
1515
#include <sys/types.h> //O_RDONLY
16+
#include <sys/timerfd.h> //for the timerfd
1617

1718
typedef struct stat stat_struct;
1819

@@ -21,6 +22,17 @@ namespace utility {
2122
uint64_t get_file_size(int file_fd); //gets file size of the file descriptor passed in
2223
void sigint_handler(int sig_number); //handler used in main for handling SIGINT
2324

25+
void log_helper_function(std::string msg, bool cerr_or_not);
26+
27+
std::string replace(std::string s1, std::string s2, std::string pattern);
28+
std::string remove_from_slash_string(std::string slash_string, std::string remove_string); // removes ABCD from `123/abc/ABCD/efg`, making `123/abc/efg`
29+
30+
void set_timerfd_interval(int timerfd, int ms);
31+
32+
uint64_t random_number(uint64_t min, uint64_t max);
33+
34+
std::string to_web_name(std::string name); // simple function to make the input lower case, and replace spaces with underscores
35+
2436
//removes first n elements from a vector
2537
template <typename T>
2638
void remove_first_n_elements(std::vector<T> &data, int num_elements_to_remove){ //deals correctly with overlaps

src/header/web_server/cache.h

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
#ifndef CACHE
22
#define CACHE
33

4+
#include <iostream>
45
#include <unordered_set>
56
#include <unordered_map>
67
#include <vector>
78

9+
#include "../utility.h"
10+
811
#include <sys/inotify.h>
912

1013
#include "common_structs_enums.h"
@@ -16,7 +19,7 @@ namespace web_cache {
1619
int next_item_idx = -1;
1720
int prev_item_idx = -1;
1821
bool outdated = false; //if true, then removed from cache
19-
int watch{};
22+
int watch = -1;
2023
};
2124

2225
struct cache_fetch_item {
@@ -43,10 +46,13 @@ namespace web_cache {
4346

4447
cache(){ //only works for cache's which are greater than 1 in size
4548
for(int i = 0; i < cache_buffer.size(); i++)
46-
free_idxs.insert(i);
49+
free_idxs.insert(free_idxs.end(), i);
4750
}
4851

4952
cache_fetch_item fetch_item(const std::string &filepath, int client_idx, web_server::tcp_client &client){
53+
if(client_idx < 0) // for the off chance that an invalid client ID is supplied
54+
return { false, nullptr };
55+
5056
if(filepath_to_cache_idx.count(filepath)) {
5157
auto current_idx = filepath_to_cache_idx[filepath];
5258
auto &item = cache_buffer[current_idx];
@@ -67,7 +73,7 @@ namespace web_cache {
6773

6874
inotify_rm_watch(inotify_fd, item.watch); //remove the watcher for this item
6975
watch_to_cache_idx.erase(item.watch);
70-
free_idxs.insert(current_idx);
76+
free_idxs.insert(free_idxs.end(), current_idx);
7177
item = cache_item();
7278

7379
return { false, nullptr };
@@ -90,7 +96,7 @@ namespace web_cache {
9096
if(outdated_file){
9197
inotify_rm_watch(inotify_fd, item.watch); //remove the watcher for this item
9298
watch_to_cache_idx.erase(item.watch);
93-
free_idxs.insert(current_idx);
99+
free_idxs.insert(free_idxs.end(), current_idx);
94100
item = cache_item();
95101

96102
return { false, nullptr };
@@ -108,7 +114,7 @@ namespace web_cache {
108114
}
109115
}
110116

111-
bool try_insert_item(int client_idx, const std::string &filepath, std::vector<char> &&buff){
117+
bool try_insert_item(const std::string &filepath, std::vector<char> &&buff){
112118
int current_idx = -1;
113119

114120
if(free_idxs.size()){ //if free idxs available
@@ -145,9 +151,9 @@ namespace web_cache {
145151
filepath_to_cache_idx[filepath] = current_idx;
146152
cache_idx_to_filepath[current_idx] = filepath;
147153

148-
current_item.watch = inotify_add_watch(inotify_fd, filepath.c_str(), IN_MODIFY);
154+
current_item.watch = inotify_add_watch(inotify_fd, filepath.c_str(), IN_MODIFY); // doesn't watch for deleted/created files in the cache, only modified
149155
watch_to_cache_idx[current_item.watch] = current_idx;
150-
156+
151157
current_item.prev_item_idx = highest_idx; //promote to highest
152158
current_item.next_item_idx = -1;
153159
highest_idx = current_idx; //new highest position
@@ -168,6 +174,11 @@ namespace web_cache {
168174
}
169175

170176
void finished_with_item(int client_idx, web_server::tcp_client &client){ //requires a pointer to the client object, for the using_file stuff - to ensure it's not decremented too many times
177+
if(client_idx < 0){ // for the off chance that an invalid client ID is supplied
178+
utility::log_helper_function(std::string(__func__) + " ## " + std::to_string(__LINE__) + " ## " + std::string(__FILE__) + " ## Client idx: " + std::to_string(client_idx) + " ## " + std::to_string(client.using_file), true);
179+
return;
180+
}
181+
171182
if(client.using_file){
172183
const auto cache_idx = client_idx_to_cache_idx[client_idx];
173184
cache_buffer[cache_idx].lock_number--;

0 commit comments

Comments
 (0)