123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- import std.stdio;
- import std.conv;
- import std.range;
- import std.datetime : SysTime, Clock;
- import core.sync.mutex : Mutex;
- import std.concurrency;
- import slf4d;
- import handy_httpd;
- import handy_httpd.components.websocket;
- import handy_httpd.handlers.path_handler;
- import handy_httpd.handlers.file_resolving_handler;
- struct UserState{
- string client_id;
- SysTime last_online_at;
- }
- UserState[string] userStates;
- Mutex userStatesMutex;
- class MyWebSocketRequestHandler : HttpRequestHandler{
- private Mutex userStatesMutex;
-
- this(Mutex mutex){
- this.userStatesMutex = mutex;
- writeln("DEBUG: MyWebSocketRequestHandler created");
- }
-
- //WebSocketMessageHandler factory;
-
- //this(WebSocketMessageHandler factory){
- // this.factory = factory;
- //}
-
- void handle(ref HttpRequestContext ctx){
- writeln("DEBUG: MyWebSocketRequestHandler.handle(...) called");
- bool isWebSocketUpgrade = false;
- string client_id = "";
-
- //writeln("ctx.request.headers = ", ctx.request.headers);
- //writeln("ctx.request = ", ctx.request);
- //writeln("ctx.request.queryParams = ", ctx.request.queryParams);
- //writeln("ctx.request.queryParams[\"client_id\"] = ", ctx.request.queryParams["client_id"]);
-
- if(ctx.request.queryParams.contains("client_id")){
- client_id = ctx.request.queryParams["client_id"];
- isWebSocketUpgrade = true;
- }
-
- //writeln("ctx.request.url = ", ctx.request.url);
-
- /*
- auto secWsKey = ctx.request.headers.getFirst("Sec-WebSocket-Key");
- if( (!(client_id.empty)) && (secWsKey !is null) && (secWsKey.get().length > 0) ){
- isWebSocketUpgrade = true;
- }
- */
-
- /*
- if(!isWebSocketUpgrade){
- throw new HttpStatusException(HttpStatus.UPGRADE_REQUIRED);
- }
-
- if(client_id.empty){
- throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Missing X-User-ID header");
- }
- */
-
- if(!isWebSocketUpgrade || client_id.empty){
- throw new HttpStatusException(HttpStatus.BAD_REQUEST, "Invalid WebSocket handshake");
- }
-
- writeln("DEBUG: Valid WS upgrade for client: ", client_id);
- auto handler = new MyWebSocketHandler(client_id, this.userStatesMutex);
- ctx.server.getWebSocketManager().registerConnection(ctx, handler);
- }
- }
- class MyWebSocketHandler : WebSocketMessageHandler{
- private string client_id;
- private Mutex userStatesMutex;
-
- this(){
- this.client_id = "not_found";
- this.userStatesMutex = null;
- writeln("DEBUG: MyWebSocketHandler created with client_id: ", this.client_id);
- }
-
- this(string client_id, Mutex mutex){
- this.client_id = client_id.idup;
- this.userStatesMutex = mutex;
- writeln("DEBUG: MyWebSocketHandler created with client_id: ", this.client_id);
- }
-
- override void onConnectionEstablished(WebSocketConnection conn, in HttpRequest request){
- if(this.userStatesMutex is null){
- writeln("ERROR: this.userStatesMutex not initialized!");
- //assert(false);
- return;
- }
- this.userStatesMutex.lock();
- scope(exit) this.userStatesMutex.unlock();
-
- if(client_id in userStates){
- writeln("Reconnect detected for client: ", client_id);
- }else{
- writeln("New connection for client: ", client_id);
- userStates[client_id] = UserState(client_id, Clock.currTime());
- //existingState = new UserState();
- //existingState.client_id = client_id.idup;
- //existingState.connectedAt = Clock.currTime();
- }
-
- writeln("DEBUG: onConnectionEstablished(...) called for client: ", client_id);
- infoF!"Connection established with id %s"(conn.id); // id 8a4429e7-19b8-4dc4-bca6-14f64a471392
- conn.sendTextMessage("Hey yourself!");
- }
-
- override void onTextMessage(WebSocketTextMessage msg){
- infoF!"Got TEXT: %s"(msg.payload);
-
- writeln("Message from client: ", client_id);
- //writeln("Message from client: ", userStates[client_id]);
-
- //msg.conn.sendTextMessage("Hey yourself!");
- msg.conn.sendTextMessage(msg.payload ~ " :)");
- }
-
- override void onCloseMessage(WebSocketCloseMessage msg){
- /*
- this.userStatesMutex.lock();
- scope(exit) this.userStatesMutex.unlock();
- if(client_id in userStates){
- userStates.remove(client_id);
- }
- */
-
- infoF!"Closed: %d, %s"(msg.statusCode, msg.message);
- }
- }
- void main(string[] args){
- ServerConfig cfg;
- if(args.length > 1){
- cfg.port = args[1].to!ushort;
- }
-
- userStatesMutex = new Mutex();
-
- cfg.workerPoolSize = 3;
- cfg.enableWebSockets = true; // Important! Websockets won't work unless `enableWebSockets` is set to true!
- //WebSocketHandler handler = new WebSocketHandler(new MyWebSocketHandler());
- //auto handler = new WebSocketHandler(new MyWebSocketHandler("123"));
- auto handler = new MyWebSocketRequestHandler(userStatesMutex);
-
- //new HttpServer(new FileResolvingHandler("public"), cfg).start();
- PathHandler pathHandler = new PathHandler()
- .addMapping(Method.GET, "/ws", handler)
- .addMapping("/**", new FileResolvingHandler("public"));
-
- new HttpServer(pathHandler, cfg).start();
- }
|