1 2 module mars.client; 3 4 import std.datetime, 5 std.experimental.logger; 6 7 import vibe.core.log; 8 import vibe.data.json; 9 import vibe.http.websockets; 10 11 import mars.msg; 12 import mars.server; 13 import mars.websocket; 14 import mars.protomars : MarsProxyStoC; // XXX per instanziare la variabile per il socket... dovrei fare un interfaccia? 15 // nel momento in cui instanzio il client, non ho ancora il MarsProxy, che invece 16 // instanzio nel protoMars. Dovrei instanziare il client nel protoMars? Maybe yes 17 // visto che è li che, dopo aver stabilito che è un client mars, instanzio il socket. 18 19 import mars.sync; 20 import mars.pgsql; 21 22 struct MarsClient 23 { 24 void connected() in { 25 assert( connectionEvents.length ==0 || connectionEvents[$-1].type == ConnessionEvent.disconnected ); 26 } body { 27 connectionEvents ~= ConnessionEvent(ConnessionEvent.connected, Clock.currTime); 28 } 29 void disconnected() in { 30 assert( connectionEvents[$-1].type == ConnessionEvent.connected ); 31 } body { 32 connectionEvents ~= ConnessionEvent(ConnessionEvent.disconnected, Clock.currTime); 33 } 34 35 bool isConnected(){ return connectionEvents.length >0 && connectionEvents[$-1].type == ConnessionEvent.connected; } 36 37 SysTime[] reconnections() { 38 import std.algorithm : filter, map; 39 import std.array : array; 40 41 return connectionEvents 42 .filter!( (e) => e.type == ConnessionEvent.connected ) 43 .map!"a.when" 44 .array; 45 } 46 47 private { 48 struct ConnessionEvent { 49 enum { connected, disconnected } 50 int type; 51 SysTime when; 52 } 53 ConnessionEvent[] connectionEvents; 54 } 55 56 this(string id, const DatabaseService databaseService){ 57 this.id_ = id; 58 this.databaseService = databaseService; 59 } 60 61 /** 62 * Push a forward-only message to the client, a reply is not expected. */ 63 void sendBroadcast(M)(M msg) in { assert(isConnected); } body 64 { 65 socket.sendRequest(0, msg); // ... this is really a proxy to the websocket 66 } 67 68 /** 69 * Push a new message, from the server to the client. Used by the server to inform clients about events. */ 70 void sendRequest(M)(M msg) in { assert(isConnected); } body 71 { 72 bool sent = socket.sendRequest(nextId++, msg); 73 if( ! sent ){ 74 disconnected(); 75 } 76 } 77 private int nextId = 1; 78 79 auto receiveReply(M)() in { assert(isConnected); } body 80 { 81 auto msg = socket.receiveMsg!M(); 82 if( msg.status == msg.channelDropped ) disconnected(); 83 return msg; 84 } 85 86 /** 87 * The Helo protocol will wire the active socket here, and will set this to null when disconnecting. */ 88 void wireSocket(MarsProxyStoC!WebSocket socket) 89 { 90 this.socket = socket; 91 } 92 93 /** 94 * Returns true if the 'server to client' socket was opened and wired to us. */ 95 bool socketWired() { return this.socket != this.socket.init; } 96 97 /** 98 * Called by the authentication protocol. 99 * 100 * Returns: false if PostgreSQL is offline or user in not authorised, or true. */ 101 AuthoriseError authoriseUser(string username, string pgpassword) in { 102 assert(username && pgpassword); 103 } body { 104 this.username = username; 105 106 AuthoriseError err; 107 if( databaseService.host == "" ){ 108 logWarn("S --- C | the database host is not specified, we are operating in offline mode"); 109 err = AuthoriseError.authorised; 110 } 111 else { 112 db = databaseService.connect(username, pgpassword, err ); 113 } 114 return err; 115 } 116 void discardAuthorisation() { this.username = ""; } 117 118 bool authorised() { return this.username != ""; } 119 string id() { return id_; } 120 121 string callServerMethod(string method, Json parameters){ 122 if( serverSideMethods !is null ){ 123 return serverSideMethods(this, method, parameters); 124 } 125 assert(false); // catch on server side; 126 } 127 128 immutable(ubyte)[][2] vueInsertRecord(int statementIndex, immutable(ubyte)[] record, ref InsertError err){ 129 immutable(ubyte)[][2] inserted = marsServer.tables[statementIndex].insertRecord(db, record, err); 130 return inserted; 131 } 132 133 immutable(ubyte)[] vueDeleteRecord(int tableIndex, immutable(ubyte)[] record, ref DeleteError err){ 134 immutable(ubyte)[] deleted = marsServer.tables[tableIndex].deleteRecord(db, record, err); 135 return deleted; 136 } 137 138 private { 139 string id_; 140 141 string username = ""; 142 //string password; 143 string seed; 144 145 MarsProxyStoC!WebSocket socket; 146 147 public typeof(MarsServer.serverSideMethods) serverSideMethods; 148 DatabaseService databaseService; 149 public Database db; 150 } 151 }