|
@@ -1,6 +1,11 @@
|
|
|
|
|
|
import std.stdio;
|
|
import std.stdio;
|
|
import std.conv;
|
|
import std.conv;
|
|
|
|
+import std.range;
|
|
|
|
+
|
|
|
|
+import std.datetime : SysTime, Clock;
|
|
|
|
+import core.sync.mutex : Mutex;
|
|
|
|
+import std.concurrency;
|
|
|
|
|
|
|
|
|
|
import slf4d;
|
|
import slf4d;
|
|
@@ -10,20 +15,135 @@ import handy_httpd.handlers.path_handler;
|
|
import handy_httpd.handlers.file_resolving_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{
|
|
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){
|
|
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
|
|
infoF!"Connection established with id %s"(conn.id); // id 8a4429e7-19b8-4dc4-bca6-14f64a471392
|
|
conn.sendTextMessage("Hey yourself!");
|
|
conn.sendTextMessage("Hey yourself!");
|
|
}
|
|
}
|
|
|
|
|
|
override void onTextMessage(WebSocketTextMessage msg){
|
|
override void onTextMessage(WebSocketTextMessage msg){
|
|
infoF!"Got TEXT: %s"(msg.payload);
|
|
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("Hey yourself!");
|
|
msg.conn.sendTextMessage(msg.payload ~ " :)");
|
|
msg.conn.sendTextMessage(msg.payload ~ " :)");
|
|
}
|
|
}
|
|
|
|
|
|
override void onCloseMessage(WebSocketCloseMessage msg){
|
|
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);
|
|
infoF!"Closed: %d, %s"(msg.statusCode, msg.message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -35,9 +155,13 @@ void main(string[] args){
|
|
cfg.port = args[1].to!ushort;
|
|
cfg.port = args[1].to!ushort;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ userStatesMutex = new Mutex();
|
|
|
|
+
|
|
cfg.workerPoolSize = 3;
|
|
cfg.workerPoolSize = 3;
|
|
cfg.enableWebSockets = true; // Important! Websockets won't work unless `enableWebSockets` is set to true!
|
|
cfg.enableWebSockets = true; // Important! Websockets won't work unless `enableWebSockets` is set to true!
|
|
- WebSocketHandler handler = new WebSocketHandler(new MyWebSocketHandler());
|
|
|
|
|
|
+ //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();
|
|
//new HttpServer(new FileResolvingHandler("public"), cfg).start();
|
|
PathHandler pathHandler = new PathHandler()
|
|
PathHandler pathHandler = new PathHandler()
|