diff --git a/' b/' new file mode 100644 index 0000000..5125314 --- /dev/null +++ b/' @@ -0,0 +1,118 @@ +#include "include/DateTime.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +struct PerSocketData +{ + std::chrono::system_clock::time_point created_at; + std::string remote_addr; +}; + +std::string full_message(PerSocketData &user_data, std::string_view message) +{ + std::cout << user_data.remote_addr << ": " << message << std::endl; + std::stringstream ss; + ss << user_data.remote_addr << ": " << message; + return ss.str(); +} + +int main() +{ + std::cout << "Welcome to the homechat server!\n"; + + std::mutex message_cache_mutex; + std::unique_ptr message_cache = std::make_unique(); + + /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. + * You may swap to using uWS:App() if you don't need SSL */ + uWS::App({/* There are example certificates in uWebSockets.js repo */ + .key_file_name = "misc/key.pem", + .cert_file_name = "misc/cert.pem", + .passphrase = "1234"}) + .ws( + "/*", + {/* Settings */ + .compression = uWS::CompressOptions(uWS::DEDICATED_COMPRESSOR_4KB | uWS::DEDICATED_DECOMPRESSOR), + .maxPayloadLength = 100 * 1024 * 1024, + .idleTimeout = 16, + .maxBackpressure = 100 * 1024 * 1024, + .closeOnBackpressureLimit = false, + .resetIdleTimeoutOnSend = false, + .sendPingsAutomatically = true, + /* Handlers */ + .upgrade = nullptr, + .open = + [&](uWS::WebSocket *ws) { + auto user_data = ws->getUserData(); + user_data->created_at = DateTime::now(); + user_data->remote_addr = ws->getRemoteAddressAsText(); + + DateTime::outputTime("Client connected at ", user_data->created_at); + if (!ws->subscribe("*", true)) + { + std::cout << "Failed to subscribe to * channel\n"; + ws->close(); + } + + std::lock_guard lock(message_cache_mutex); + auto messages = message_cache->last_ten_messages(); + ws->send(std::string("Welcome to the homechat server! Here are the last 10 messages: \n"), + uWS::OpCode::TEXT, false); + for (auto &message : messages) + { + ws->send(full_message(*user_data, message), uWS::OpCode::TEXT, false); + } + }, + .message = + [&](uWS::WebSocket *ws, std::string_view message, uWS::OpCode opCode) { + /* This is the opposite of what you probably want; compress if message is LARGER than 16 kb + * the reason we do the opposite here; compress if SMALLER than 16 kb is to allow for + * benchmarking of large message sending without compression */ + std::cout << "Message received: " << message << std::endl; + ws->send(message, opCode, false); + ws->publish("*", message, opCode, false); + + std::lock_guard lock(message_cache_mutex); + if (!message_cache->add_message(std::string(message))) + { + std::cerr << "Failed to add message to cache" << std::endl; + } + }, + .dropped = + [](auto * /*ws*/, std::string_view /*message*/, uWS::OpCode /*opCode*/) { + /* A message was dropped due to set maxBackpressure and closeOnBackpressureLimit limit */ + }, + .drain = + [](auto * /*ws*/) { + /* Check ws->getBufferedAmount() here */ + }, + .ping = + [](auto * /*ws*/, std::string_view) { + /* Not implemented yet */ + }, + .pong = + [](auto * /*ws*/, std::string_view) { + /* Not implemented yet */ + }, + .close = + [](auto * /*ws*/, int /*code*/, std::string_view /*message*/) { + /* You may access ws->getUserData() here */ + std::cout << "Client disconnected\n"; + }}) + .listen(9001, + [](auto *listen_socket) { + if (listen_socket) + { + std::cout << "Listening on port " << 9001 << std::endl; + } + }) + .run(); + + return 0; +} diff --git a/client/.cache/clangd/index/Main.cpp.59B438EDC5609780.idx b/client/.cache/clangd/index/Main.cpp.59B438EDC5609780.idx index bb9b014..baf90ba 100644 Binary files a/client/.cache/clangd/index/Main.cpp.59B438EDC5609780.idx and b/client/.cache/clangd/index/Main.cpp.59B438EDC5609780.idx differ diff --git a/client/.cache/clangd/index/colors.h.9CBF675E9831E6F4.idx b/client/.cache/clangd/index/colors.h.9CBF675E9831E6F4.idx new file mode 100644 index 0000000..0cdf12b Binary files /dev/null and b/client/.cache/clangd/index/colors.h.9CBF675E9831E6F4.idx differ diff --git a/server/.cache/clangd/index/MessageCache.cpp.C00C31C15AF0039C.idx b/server/.cache/clangd/index/MessageCache.cpp.C00C31C15AF0039C.idx new file mode 100644 index 0000000..1c355ea Binary files /dev/null and b/server/.cache/clangd/index/MessageCache.cpp.C00C31C15AF0039C.idx differ diff --git a/server/.cache/clangd/index/MessageCache.hpp.EA225B528CC272FD.idx b/server/.cache/clangd/index/MessageCache.hpp.EA225B528CC272FD.idx new file mode 100644 index 0000000..ac92228 Binary files /dev/null and b/server/.cache/clangd/index/MessageCache.hpp.EA225B528CC272FD.idx differ diff --git a/server/src/Main.cpp b/server/src/Main.cpp index 37bd6d9..de93267 100644 --- a/server/src/Main.cpp +++ b/server/src/Main.cpp @@ -4,10 +4,29 @@ #include #include #include +#include #include +#include #include +#include +#include #include +struct PerSocketData +{ + std::chrono::system_clock::time_point created_at; + std::string username; +}; + +std::string full_message(PerSocketData *&user_data, std::string_view message) +{ + std::cout << user_data->username << ": " << message << std::endl; + + std::stringstream ss; + ss << user_data->username << ": " << message; + return ss.str(); +} + int main() { std::cout << "Welcome to the homechat server!\n"; @@ -15,12 +34,6 @@ int main() std::mutex message_cache_mutex; std::unique_ptr message_cache = std::make_unique(); - /* ws->getUserData returns one of these */ - struct PerSocketData - { - std::chrono::system_clock::time_point created_at; - }; - /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. * You may swap to using uWS:App() if you don't need SSL */ uWS::App({/* There are example certificates in uWebSockets.js repo */ @@ -38,14 +51,31 @@ int main() .resetIdleTimeoutOnSend = false, .sendPingsAutomatically = true, /* Handlers */ - .upgrade = nullptr, + .upgrade = + [](uWS::HttpResponse *res, uWS::HttpRequest *req, us_socket_context_t *ctx) { + std::string_view name = req->getHeader("username"); + if (name.empty()) + { + std::cout << "No username provided\n"; + res->writeStatus("401 Unauthorized: add `username` header")->end(); + return; + } + + res->template upgrade( + { + .created_at = DateTime::now(), + .username = std::string(name), + }, + req->getHeader("sec-websocket-key"), req->getHeader("sec-websocket-protocol"), + req->getHeader("sec-websocket-extensions"), ctx); + }, .open = [&](uWS::WebSocket *ws) { auto user_data = ws->getUserData(); - user_data->created_at = DateTime::now(); - DateTime::outputTime("Client connected at ", user_data->created_at); - if (!ws->subscribe("*")) + auto desc = user_data->username + " connected at: "; + DateTime::outputTime(desc.c_str(), user_data->created_at); + if (!ws->subscribe("*", true)) { std::cout << "Failed to subscribe to * channel\n"; ws->close(); @@ -53,10 +83,11 @@ int main() std::lock_guard lock(message_cache_mutex); auto messages = message_cache->last_ten_messages(); - ws->send(std::string("Welcome to the homechat server! Here are the last 10 messages: \n"), uWS::OpCode::TEXT, false); - for (auto &message : messages) + ws->send(std::string("Welcome to the homechat server! Here are the last 10 messages: \n"), + uWS::OpCode::TEXT, false); + for (auto &full_message : messages) { - ws->send(message, uWS::OpCode::TEXT, false); + ws->send(full_message, uWS::OpCode::TEXT, false); } }, .message = @@ -65,11 +96,13 @@ int main() * the reason we do the opposite here; compress if SMALLER than 16 kb is to allow for * benchmarking of large message sending without compression */ std::cout << "Message received: " << message << std::endl; - ws->send(message, opCode, false); - ws->publish("*", message, opCode, false); + auto user_data = ws->getUserData(); + auto full_message_formed = full_message(user_data, message); + ws->send(full_message_formed, opCode, false); + ws->publish("*", full_message_formed, opCode, false); std::lock_guard lock(message_cache_mutex); - if (!message_cache->add_message(std::string(message))) + if (!message_cache->add_message(full_message_formed)) { std::cerr << "Failed to add message to cache" << std::endl; } diff --git a/server/src/include/MessageCache.hpp b/server/src/include/MessageCache.hpp index ffd96d5..7b9696d 100644 --- a/server/src/include/MessageCache.hpp +++ b/server/src/include/MessageCache.hpp @@ -1,6 +1,7 @@ #include #include +// LRUCache for messages class MessageCache { public: