1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
// Glaze Library
// For the license information refer to glaze.hpp
// Complete example showing Glaze HTTP server with WebSocket support
#include <iostream>
#include <mutex>
#include <set>
#include "glaze/net/http_server.hpp"
#include "glaze/net/websocket_connection.hpp"
using namespace glz;
std::string read_file(const std::string& path)
{
std::string full_path = std::string{SOURCE_DIR} + "/" + path;
std::ifstream file(full_path, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
std::cerr << "Failed to open " << full_path << ", current directory: " << std::filesystem::current_path().string()
<< "\n";
return "";
}
// Get the file size from the current position (since we used std::ios::ate)
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
std::string buffer;
buffer.resize(size);
if (file.read(buffer.data(), size)) {
return buffer;
}
return "";
}
int main()
{
std::cout << "Starting Glaze HTTP + WebSocket Server\n";
std::cout << "=====================================\n";
// Show which SHA-1 implementation is being used
#if defined(GLZ_ENABLE_OPENSSL) && defined(GLZ_HAS_OPENSSL)
std::cout << "✅ Using OpenSSL for WebSocket handshake\n";
#else
std::cout << "⚠️ Using fallback SHA-1 implementation\n";
#endif
// Create HTTP server
http_server server;
// Create WebSocket server
auto ws_server = std::make_shared<websocket_server>();
// Thread-safe storage for connected clients
std::set<std::shared_ptr<websocket_connection_interface>> clients;
std::mutex clients_mutex;
// WebSocket event handlers
ws_server->on_validate([](const request& req) -> bool {
// Optional: Add authentication/validation logic here
std::cout << "📋 Validating WebSocket connection from: " << req.remote_ip << std::endl;
return true; // Accept all connections for this example
});
ws_server->on_open([&clients, &clients_mutex](auto conn, const request&) {
std::lock_guard<std::mutex> lock(clients_mutex);
clients.insert(conn);
std::cout << "🔗 WebSocket opened: " << conn->remote_address() << " (Total clients: " << clients.size() << ")"
<< std::endl;
// Send welcome message
conn->send_text("Welcome! You are connected to the Glaze WebSocket server.");
// Notify other clients about the new connection
std::string join_msg = "User from " + conn->remote_address() + " joined the chat";
for (auto& client : clients) {
if (client != conn) {
client->send_text("📢 " + join_msg);
}
}
});
ws_server->on_message([&clients, &clients_mutex](auto conn, std::string_view message, ws_opcode opcode) {
std::cout << "💬 Message from " << conn->remote_address() << ": " << message << std::endl;
if (opcode == ws_opcode::text) {
std::string msg(message);
// Handle special commands
if (msg == "/ping") {
conn->send_ping("server-ping");
conn->send_text("🏓 Ping sent!");
return;
}
if (msg == "/clients") {
std::lock_guard<std::mutex> lock(clients_mutex);
conn->send_text("👥 Connected clients: " + std::to_string(clients.size()));
return;
}
if (msg.starts_with("/echo ")) {
std::string echo_msg = msg.substr(6);
conn->send_text("🔄 Echo: " + echo_msg);
return;
}
// Broadcast message to all clients
std::lock_guard<std::mutex> lock(clients_mutex);
std::string broadcast_msg = "[" + conn->remote_address() + "]: " + msg;
for (auto& client : clients) {
client->send_text(broadcast_msg);
}
}
else if (opcode == ws_opcode::binary) {
std::cout << "📦 Binary message received (" << message.size() << " bytes)" << std::endl;
conn->send_binary("Binary echo: " + std::string(message));
}
});
ws_server->on_close([&clients, &clients_mutex](auto conn, ws_close_code code, std::string_view reason) {
std::lock_guard<std::mutex> lock(clients_mutex);
clients.erase(conn);
std::cout << "❌ WebSocket closed: " << conn->remote_address() << " (code=" << static_cast<int>(code);
if (!reason.empty()) {
std::cout << ", reason=" << reason;
}
std::cout << ", remaining clients: " << clients.size() << ")" << std::endl;
// Notify remaining clients
std::string leave_msg = "User from " + conn->remote_address() + " left the chat";
for (auto& client : clients) {
client->send_text("📢 " + leave_msg);
}
});
ws_server->on_error([](auto conn, std::error_code ec) {
std::cout << "🚨 WebSocket error for " << conn->remote_address() << ": " << ec.message() << std::endl;
});
// Register WebSocket endpoint
server.websocket("/ws", ws_server);
// HTTP Routes
server.get("/", [](const request&, response& res) { res.content_type("text/html").body(read_file("index.html")); });
// API endpoints
server.get("/api/status", [&clients, &clients_mutex](const request&, response& res) {
std::lock_guard<std::mutex> lock(clients_mutex);
res.json({{"server", "Glaze WebSocket + HTTP Server"},
{"websocket_clients", clients.size()},
{"implementation",
#if defined(GLZ_ENABLE_OPENSSL) && defined(GLZ_HAS_OPENSSL)
"OpenSSL"
#else
"fallback_sha1"
#endif
},
{"status", "running"}});
});
server.get("/api/broadcast", [&clients, &clients_mutex](const request& req, response& res) {
auto it = req.params.find("message");
if (it == req.params.end()) {
res.status(400).json({{"error", "Missing message parameter"}});
return;
}
std::lock_guard<std::mutex> lock(clients_mutex);
std::string broadcast_msg = "📢 Server broadcast: " + it->second;
for (auto& client : clients) {
client->send_text(broadcast_msg);
}
res.json({{"message", "Broadcast sent"}, {"recipients", clients.size()}});
});
// Enable CORS for browser access
server.enable_cors();
try {
// Start server
server.bind(8080).with_signals(); // Enable signal handling for graceful shutdown
std::cout << "\nServer running on http://localhost:8080\n";
std::cout << "WebSocket endpoint: ws://localhost:8080/ws\n";
std::cout << "Web interface: http://localhost:8080\n";
std::cout << "Status API: http://localhost:8080/api/status\n";
std::cout << "\nPress Ctrl+C to gracefully shut down the server\n\n";
server.start();
// Wait for shutdown signal (blocks until server stops)
server.wait_for_signal();
std::cout << "👋 Server stopped gracefully\n";
}
catch (const std::exception& e) {
std::cerr << "❌ Server error: " << e.what() << std::endl;
return 1;
}
return 0;
}
|