From b3a5342529e7c5f7612a0c179ffd7c6557afc768 Mon Sep 17 00:00:00 2001 From: Gaspard Kirira Date: Fri, 6 Feb 2026 14:38:31 +0300 Subject: [PATCH] v1.32.0: refresh examples and improve websocket + cli/core --- CMakeLists.txt | 5 +- config/config.json | 2 +- examples/01_run_blocking.cpp | 14 ++ examples/02_listen_background.cpp | 19 +++ examples/03_listen_port_dynamic.cpp | 18 ++ examples/README.md | 10 ++ examples/p2p/server.cpp | 4 +- examples/session/session.cpp | 9 +- examples/websocket/advanced/CMakeLists.txt | 61 ------- .../websocket/advanced/{src => }/client.cpp | 0 .../{advanced_chat.html => index.html} | 0 .../websocket/advanced/{src => }/server.cpp | 0 examples/websocket/index.html | 161 ++++++++++++++++++ examples/websocket/server.cpp | 96 +++++++++++ examples/websocket/simple/CMakeLists.txt | 20 +-- examples/websocket/simple/public/app.js | 4 - examples/websocket/simple/public/style.css | 3 - examples/websocket/simple/src/app_example.cpp | 79 +++++---- examples/websocket/simple/src/index.html | 4 - .../simple/src/minimal_ws_server.cpp | 25 ++- .../websocket/simple/src/simple_client.cpp | 6 +- .../websocket/simple/src/simple_server.cpp | 7 +- modules/cli | 2 +- modules/core | 2 +- modules/websocket | 2 +- 25 files changed, 405 insertions(+), 148 deletions(-) create mode 100644 examples/01_run_blocking.cpp create mode 100644 examples/02_listen_background.cpp create mode 100644 examples/03_listen_port_dynamic.cpp create mode 100644 examples/README.md delete mode 100644 examples/websocket/advanced/CMakeLists.txt rename examples/websocket/advanced/{src => }/client.cpp (100%) rename examples/websocket/advanced/{advanced_chat.html => index.html} (100%) rename examples/websocket/advanced/{src => }/server.cpp (100%) create mode 100644 examples/websocket/index.html create mode 100644 examples/websocket/server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 05c04b1..d5e7597 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -994,7 +994,10 @@ if (VIX_ENABLE_INSTALL) # Install headers if present if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/core/include") install(DIRECTORY modules/core/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h") + FILES_MATCHING + PATTERN "*.hpp" + PATTERN "*.h" + PATTERN "*.inc") endif() if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/modules/utils/include") diff --git a/config/config.json b/config/config.json index 0518df8..f705296 100644 --- a/config/config.json +++ b/config/config.json @@ -3,7 +3,7 @@ "port": 8080, "request_timeout": 2000, "io_threads": 0, - "session_timeout_sec": 120 + "session_timeout_sec": 300 }, "logging": { "async": true, diff --git a/examples/01_run_blocking.cpp b/examples/01_run_blocking.cpp new file mode 100644 index 0000000..c94ec14 --- /dev/null +++ b/examples/01_run_blocking.cpp @@ -0,0 +1,14 @@ +#include + +using namespace vix; + +int main() +{ + App app; + + app.get("/", [](Request &, Response &res) + { res.text("Hello Vix"); }); + + // Bloque le thread courant + app.run(8080); +} diff --git a/examples/02_listen_background.cpp b/examples/02_listen_background.cpp new file mode 100644 index 0000000..2489734 --- /dev/null +++ b/examples/02_listen_background.cpp @@ -0,0 +1,19 @@ +#include +#include + +using namespace vix; + +int main() +{ + App app; + + app.get("/health", [](Request &, Response &res) + { res.json({"ok", true}); }); + + // Lance le serveur en background + app.listen(8080, []() + { std::cout << "Server is ready and accepting connections\n"; }); + + // Attente propre + app.wait(); +} diff --git a/examples/03_listen_port_dynamic.cpp b/examples/03_listen_port_dynamic.cpp new file mode 100644 index 0000000..ea40351 --- /dev/null +++ b/examples/03_listen_port_dynamic.cpp @@ -0,0 +1,18 @@ +#include +#include + +using namespace vix; + +int main() +{ + App app; + + app.get("/", [](Request &, Response &res) + { res.text("Dynamic port example"); }); + + // Port 0 → choisi par l’OS + app.listen_port(0, [](int port) + { std::cout << "Listening on http://localhost:" << port << "\n"; }); + + app.wait(); +} diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..5a3a632 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,10 @@ +# Vix examples + +01_run_blocking.cpp +- Minimal blocking server using `run()` + +02_listen_background.cpp +- Non-blocking server using `listen()` and `wait()` + +03_listen_port_dynamic.cpp +- Dynamic port binding (port = 0) using `listen_port()` diff --git a/examples/p2p/server.cpp b/examples/p2p/server.cpp index 8f9cb36..583ac41 100644 --- a/examples/p2p/server.cpp +++ b/examples/p2p/server.cpp @@ -39,8 +39,8 @@ int main() app.get("/connect", [](Request &, Response &res) { res.file("./public/connect.html"); }); - app.listen(5178, [](const vix::utils::ServerReadyInfo &info) - { console.info("UI API listening on", info.port); }); + app.listen(5178, []() + { console.info("UI API listening on", 5178); }); app.wait(); runtime.stop(); diff --git a/examples/session/session.cpp b/examples/session/session.cpp index 34d8fcd..0753016 100644 --- a/examples/session/session.cpp +++ b/examples/session/session.cpp @@ -7,17 +7,16 @@ int main() App app; app.use(middleware::app::adapt_ctx( - middleware::auth::session({.secret = "dev"}) - )); + middleware::auth::session({.secret = "dev"}))); - app.get("/session", [](Request &req, Response &res){ + app.get("/session", [](Request &req, Response &res) + { auto &s = req.state(); int n = s.get("n") ? std::stoi(*s.get("n")) : 0; s.set("n", std::to_string(++n)); - res.text("n=" + std::to_string(n)); - }); + res.text("n=" + std::to_string(n)); }); app.run(8080); } diff --git a/examples/websocket/advanced/CMakeLists.txt b/examples/websocket/advanced/CMakeLists.txt deleted file mode 100644 index 3eb4ee7..0000000 --- a/examples/websocket/advanced/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -# ------------------------------------------------------------------------------ -# @file CMakeLists.cpp -# @author Gaspard Kirira -# -# Copyright 2025, Gaspard Kirira. All rights reserved. -# https://github.com/vixcpp/vix -# Use of this source code is governed by a MIT license -# that can be found in the License file. -# -# Vix.cpp -# -# Advanced WebSocket Examples for Vix.cpp -# -# This CMake file builds two advanced demonstration executables showcasing -# production-style usage of the Vix.cpp WebSocket module: -# -# 1. vix-ws-advanced-server — a room-based, persistent WebSocket server with: -# - typed JSON protocol ("type" + "payload") -# - room join/leave/broadcast -# - SQLite-backed message persistence + history replay -# - Prometheus-style /metrics endpoint -# -# 2. vix-ws-advanced-client — an interactive CLI WebSocket client with: -# - auto-reconnect -# - heartbeat / ping -# - room switching and structured output -# -# Both executables link only against the umbrella `vix::websocket` target. -# All transitive dependencies (core, utils, Boost, SQLite, etc.) are provided -# by the module itself, keeping these examples clean and focused on behavior, -# not build plumbing. -# -# For quick-start / minimal usage, see: -# examples/simple/ -# ------------------------------------------------------------------------------ - -cmake_minimum_required(VERSION 3.20) -project(vix_ws_advanced LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -# Advanced WebSocket Server -add_executable(vix-ws-advanced-server - src/server.cpp -) - -target_link_libraries(vix-ws-advanced-server - PRIVATE - vix::websocket -) - -# Advanced WebSocket Client -add_executable(vix-ws-advanced-client - src/client.cpp -) - -target_link_libraries(vix-ws-advanced-client - PRIVATE - vix::websocket -) diff --git a/examples/websocket/advanced/src/client.cpp b/examples/websocket/advanced/client.cpp similarity index 100% rename from examples/websocket/advanced/src/client.cpp rename to examples/websocket/advanced/client.cpp diff --git a/examples/websocket/advanced/advanced_chat.html b/examples/websocket/advanced/index.html similarity index 100% rename from examples/websocket/advanced/advanced_chat.html rename to examples/websocket/advanced/index.html diff --git a/examples/websocket/advanced/src/server.cpp b/examples/websocket/advanced/server.cpp similarity index 100% rename from examples/websocket/advanced/src/server.cpp rename to examples/websocket/advanced/server.cpp diff --git a/examples/websocket/index.html b/examples/websocket/index.html new file mode 100644 index 0000000..125ce77 --- /dev/null +++ b/examples/websocket/index.html @@ -0,0 +1,161 @@ + + + + + Vix WebSocket + LongPolling Client + + + +

Vix.cpp Client — WebSocket + Long Polling

+ + + + + + + +
+ + + + +
+ + + + +
+ + + +

Logs

+

+
+    
+  
+
diff --git a/examples/websocket/server.cpp b/examples/websocket/server.cpp
new file mode 100644
index 0000000..4564b01
--- /dev/null
+++ b/examples/websocket/server.cpp
@@ -0,0 +1,96 @@
+#include 
+
+#include 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+using namespace vix;
+
+#include 
+#include 
+#include 
+
+static std::string token_to_string(const vix::json::token &t)
+{
+  // string only
+  if (auto s = std::get_if(&t.v))
+    return *s;
+  return {};
+}
+
+static std::string kvs_get_string(const vix::json::kvs &obj, const std::string &key)
+{
+  const auto &f = obj.flat;
+
+  // Expect pairs: key, value, key, value...
+  for (std::size_t i = 0; i + 1 < f.size(); i += 2)
+  {
+    const std::string k = token_to_string(f[i]);
+    if (k == key)
+      return token_to_string(f[i + 1]);
+  }
+  return {};
+}
+
+int main()
+{
+  vix::serve_http_and_ws([](auto &app, auto &ws)
+                         {
+    static vix::websocket::WebSocketMetrics metrics;
+
+    auto bridge = std::make_shared(
+      &metrics,
+      std::chrono::seconds{60},
+      256
+    );
+
+    ws.attach_long_polling_bridge(bridge);
+
+    app.get("/", [](auto &, auto &res)
+    {
+      nlohmann::json j{
+        {"message", "Hello from Vix.cpp minimal example"},
+        {"framework", "Vix.cpp"}
+      };
+      res.json(j);
+    });
+
+    app.get("/ws/poll", [&ws](auto &req, auto &res)
+    {
+      vix::websocket::http::handle_ws_poll(req, res, ws);
+    });
+
+    app.post("/ws/send", [&ws](auto &req, auto &res)
+    {
+      vix::websocket::http::handle_ws_send(req, res, ws);
+    });
+
+    app.get("/ws/status", [&ws](auto &, auto &res)
+    {
+      auto b = ws.long_polling_bridge();
+      nlohmann::json j{
+        {"bridge_attached", static_cast(b)},
+        {"sessions", b ? static_cast(b->session_count()) : 0}
+      };
+      res.json(j);
+    });
+
+  ws.on_typed_message([&ws](auto &, const std::string &type, const vix::json::kvs &payload)
+  {
+    if (type != "chat.message")
+      return;
+
+    const std::string room = kvs_get_string(payload, "room");
+    if (!room.empty())
+      ws.broadcast_room_json(room, "chat.message", payload);
+    else
+      ws.broadcast_json("chat.message", payload);
+  }); });
+
+  return 0;
+}
diff --git a/examples/websocket/simple/CMakeLists.txt b/examples/websocket/simple/CMakeLists.txt
index 5514bef..34e24f3 100644
--- a/examples/websocket/simple/CMakeLists.txt
+++ b/examples/websocket/simple/CMakeLists.txt
@@ -1,4 +1,12 @@
-# ------------------------------------------------------------------------------
+#  @file CMakeLists.cpp
+#  @author Gaspard Kirira
+#
+#  Copyright 2025, Gaspard Kirira.  All rights reserved.
+#  https://github.com/vixcpp/vix
+#  Use of this source code is governed by a MIT license
+#  that can be found in the License file.
+#
+#  Vix.cpp
 #  CMakeLists.txt — Simple WebSocket Examples for Vix.cpp
 #
 #  This CMake file builds two minimal demonstration executables showcasing
@@ -34,12 +42,6 @@
 #  auto-reconnect, and history replay, see:
 #      examples/advanced/
 #
-# ------------------------------------------------------------------------------
-
-# ------------------------------------------------------------------------------
-#  Simple WebSocket Server
-# ------------------------------------------------------------------------------
-
 add_executable(vix-ws-simple-server
     src/simple_server.cpp
 )
@@ -67,10 +69,6 @@ target_link_libraries(vix-ws-minimal-server
         vix::websocket
 )
 
-# ------------------------------------------------------------------------------
-#  Simple WebSocket Client
-# ------------------------------------------------------------------------------
-
 add_executable(vix-ws-simple-client
     src/simple_client.cpp
 )
diff --git a/examples/websocket/simple/public/app.js b/examples/websocket/simple/public/app.js
index c940b24..3f84f7f 100644
--- a/examples/websocket/simple/public/app.js
+++ b/examples/websocket/simple/public/app.js
@@ -121,8 +121,6 @@ function disconnect() {
   }
 }
 
-// Boutons
-
 connectBtn.addEventListener("click", (e) => {
   e.preventDefault();
   connect();
@@ -133,8 +131,6 @@ disconnectBtn.addEventListener("click", (e) => {
   disconnect();
 });
 
-// Formulaire d'envoi
-
 formEl.addEventListener("submit", (e) => {
   e.preventDefault();
   const text = inputEl.value.trim();
diff --git a/examples/websocket/simple/public/style.css b/examples/websocket/simple/public/style.css
index 737035b..c7ea9ed 100644
--- a/examples/websocket/simple/public/style.css
+++ b/examples/websocket/simple/public/style.css
@@ -66,7 +66,6 @@ body {
 }
 
 /* Layout chat */
-
 .chat {
   display: grid;
   grid-template-columns: 220px 1fr;
@@ -138,7 +137,6 @@ body {
 }
 
 /* Messages */
-
 .message {
   margin-bottom: 8px;
   font-size: 0.9rem;
@@ -160,7 +158,6 @@ body {
 }
 
 /* Form */
-
 .message-form {
   display: flex;
   padding: 8px;
diff --git a/examples/websocket/simple/src/app_example.cpp b/examples/websocket/simple/src/app_example.cpp
index dacb22b..dbf3778 100644
--- a/examples/websocket/simple/src/app_example.cpp
+++ b/examples/websocket/simple/src/app_example.cpp
@@ -1,33 +1,46 @@
-// #include 
-// #include 
-
-// using vix::websocket::App;
-// using vix::websocket::Session;
-
-// void handle_chat(Session &session,
-//                  const std::string &type,
-//                  const vix::json::kvs &payload)
-// {
-//     (void)session;
-
-//     if (type == "chat.message")
-//     {
-//         auto j = vix::websocket::detail::ws_kvs_to_nlohmann(payload);
-
-//         std::string user = j.value("user", "anonymous");
-//         std::string text = j.value("text", "");
-//         std::string room = j.value("room", "general");
-
-//         std::cout << "[chat][" << room << "] " << user << ": " << text << "\n";
-//     }
-// }
-
-// int main()
-// {
-//     App app{"config/config.json"};
-
-//     app.ws("/chat", handle_chat);
-
-//     app.run_blocking();
-//     return 0;
-// }
+/**
+ *
+ *  @file app_example.cpp
+ *  @author Gaspard Kirira
+ *
+ *  Copyright 2025, Gaspard Kirira.  All rights reserved.
+ *  https://github.com/vixcpp/vix
+ *  Use of this source code is governed by a MIT license
+ *  that can be found in the License file.
+ *
+ *  Vix.cpp
+ */
+#include 
+#include 
+
+using vix::websocket::App;
+using vix::websocket::Session;
+
+void handle_chat(
+    Session &session,
+    const std::string &type,
+    const vix::json::kvs &payload)
+{
+  (void)session;
+
+  if (type == "chat.message")
+  {
+    auto j = vix::websocket::detail::ws_kvs_to_nlohmann(payload);
+
+    std::string user = j.value("user", "anonymous");
+    std::string text = j.value("text", "");
+    std::string room = j.value("room", "general");
+
+    std::cout << "[chat][" << room << "] " << user << ": " << text << "\n";
+  }
+}
+
+int main()
+{
+  App app{"config/config.json"};
+
+  (void)app.ws("/chat", handle_chat);
+
+  app.run_blocking();
+  return 0;
+}
diff --git a/examples/websocket/simple/src/index.html b/examples/websocket/simple/src/index.html
index 4a7c7f6..125ce77 100644
--- a/examples/websocket/simple/src/index.html
+++ b/examples/websocket/simple/src/index.html
@@ -55,9 +55,7 @@ 

Logs

el.scrollTop = el.scrollHeight; } - // ------------------------------- // 1) WebSocket client - // ------------------------------- let ws; document.getElementById("connectWS").onclick = () => { @@ -94,9 +92,7 @@

Logs

document.getElementById("msg").value = ""; }; - // ------------------------------- // 2) Long Polling client - // ------------------------------- let sessionId = null; let lpActive = false; diff --git a/examples/websocket/simple/src/minimal_ws_server.cpp b/examples/websocket/simple/src/minimal_ws_server.cpp index 3a2b507..c52bd6b 100644 --- a/examples/websocket/simple/src/minimal_ws_server.cpp +++ b/examples/websocket/simple/src/minimal_ws_server.cpp @@ -9,7 +9,6 @@ * that can be found in the License file. * * Vix.cpp - * */ #include #include @@ -28,20 +27,19 @@ int main() std::cout << "[minimal] WebSocket server starting on port " << server.port() << std::endl; - server.on_open([](ws::Session &session) - { - vix::json::kvs payload{ - "message", std::string{"Welcome to minimal Vix WebSocket 👋"}, - }; - - // { "type": "system.welcome", "payload": { ... } } - std::string text = ws::JsonMessage::serialize("system.welcome", payload); - - session.send_text(text); + server.on_open( + [](ws::Session &session) + { + vix::json::kvs payload{ + "message", std::string{"Welcome to minimal Vix WebSocket 👋"}, + }; - std::cout << "[minimal] New session opened, welcome sent" << std::endl; }); + // { "type": "system.welcome", "payload": { ... } } + std::string text = ws::JsonMessage::serialize("system.welcome", payload); + session.send_text(text); + std::cout << "[minimal] New session opened, welcome sent" << std::endl; }); - (void)app.ws( + [[maybe_unused]] auto &chatRoute = app.ws( "/chat", [&server](ws::Session &session, const std::string &type, @@ -55,7 +53,6 @@ int main() } }); - // 4) Boucle bloquante app.run_blocking(); return 0; diff --git a/examples/websocket/simple/src/simple_client.cpp b/examples/websocket/simple/src/simple_client.cpp index 5a15394..19c476b 100644 --- a/examples/websocket/simple/src/simple_client.cpp +++ b/examples/websocket/simple/src/simple_client.cpp @@ -1,6 +1,6 @@ /** - * @file simple_client.cpp - * @brief Minimal WebSocket client example for Vix.cpp + * + * @file simple_client.cpp * @author Gaspard Kirira * * Copyright 2025, Gaspard Kirira. All rights reserved. @@ -9,7 +9,7 @@ * that can be found in the License file. * * Vix.cpp - * + * @brief Minimal WebSocket client example for Vix.cpp * * This example demonstrates the simplest possible interactive WebSocket * client built with the Vix.cpp WebSocket module. It connects to a server, diff --git a/examples/websocket/simple/src/simple_server.cpp b/examples/websocket/simple/src/simple_server.cpp index 539841b..894224b 100644 --- a/examples/websocket/simple/src/simple_server.cpp +++ b/examples/websocket/simple/src/simple_server.cpp @@ -1,7 +1,7 @@ /** - * @file simple_server.cpp - * @brief Minimal WebSocket server example for Vix.cpp - * @author Gaspard Kirira + * + * @file simple_server.cpp + * @author Gaspard Kirira * * Copyright 2025, Gaspard Kirira. All rights reserved. * https://github.com/vixcpp/vix @@ -9,6 +9,7 @@ * that can be found in the License file. * * Vix.cpp + * @brief Minimal WebSocket server example for Vix.cpp * * This file provides a compact, beginner-friendly demonstration of how to * start a WebSocket server using the Vix.cpp runtime. It serves as the diff --git a/modules/cli b/modules/cli index f0cb00d..a630b62 160000 --- a/modules/cli +++ b/modules/cli @@ -1 +1 @@ -Subproject commit f0cb00d0dca24aa0021db712a7c9a9106e4e28f2 +Subproject commit a630b6279dcc3f52f86407c6d90f2916264f787b diff --git a/modules/core b/modules/core index 0faebd9..0bc457d 160000 --- a/modules/core +++ b/modules/core @@ -1 +1 @@ -Subproject commit 0faebd9e63804d60cbc73b0fee557f6d49110687 +Subproject commit 0bc457da6e94141f9e00a0ce14ab37f7e61b335c diff --git a/modules/websocket b/modules/websocket index 7e7ce73..84911cc 160000 --- a/modules/websocket +++ b/modules/websocket @@ -1 +1 @@ -Subproject commit 7e7ce7347826d5b80aea47a2da438da9eb308605 +Subproject commit 84911cc1eb59e32253fe8a8ddd9e82cc1bd197fb