Browse Source

Code for notification server-side

Kenric Nugteren 2 years ago
parent
commit
18ee657957

+ 7 - 7
InABox.Client.Local/LocalClient.cs

@@ -4,9 +4,9 @@ using InABox.WebSocket.Shared;
 
 namespace InABox.Clients
 {
-    class LocalNotifier : Notifier
+    class LocalNotifier : INotifier
     {
-        protected override IEnumerable<Guid> GetUserSessions(Guid userID)
+        public IEnumerable<Guid> GetUserSessions(Guid userID)
         {
             if(userID == ClientFactory.UserGuid)
             {
@@ -15,7 +15,7 @@ namespace InABox.Clients
             return Array.Empty<Guid>();
         }
 
-        protected override IEnumerable<Guid> GetSessions(Platform platform)
+        public IEnumerable<Guid> GetSessions(Platform platform)
         {
             if (platform == Platform.Wpf)
             {
@@ -24,15 +24,15 @@ namespace InABox.Clients
             return Array.Empty<Guid>();
         }
 
-        protected override void NotifyAll<TNotification>(TNotification notification)
+        public void NotifyAll<TNotification>(TNotification notification) where TNotification : BaseObject
         {
             ClientFactory.Notifications.Notify(typeof(TNotification), notification);
         }
 
-        protected override void NotifySession<TNotification>(Guid session, TNotification notification) =>
-            NotifySession(session, typeof(TNotification), notification);
+        public void NotifySession<TNotification>(Guid session, TNotification notification) where TNotification : BaseObject
+            => NotifySession(session, typeof(TNotification), notification);
 
-        protected override void NotifySession(Guid session, Type TNotification, BaseObject notification)
+        public void NotifySession(Guid session, Type TNotification, BaseObject notification)
         {
             if (session == ClientFactory.SessionID)
             {

+ 5 - 70
InABox.Client.RPC/RPCClient.cs

@@ -16,7 +16,6 @@ namespace InABox.Rpc
         
         public RpcClient(IRpcClientTransport transport)
         {
-            
             _transport = transport;
             _transport.OnMessage += Transport_Message;
             if (!_transport.IsConnected())
@@ -38,72 +37,8 @@ namespace InABox.Rpc
             RaiseLogEvent(LogType.Error, "", "Message received: ({0}) -> {1}", e.Message.Command, e.Message.Payload);
         }
 
-        public TResult Send<TCommand, TParameters, TResult>(TParameters parameters) 
-            where TCommand : IRpcCommand<TParameters,TResult>
-            where TParameters : ISerializeBinary
-            where TResult : ISerializeBinary, new()
-        {
-            var request = new RpcMessage()
-            {
-                Id = new Guid(),
-                Command = typeof(TCommand).Name,
-                Payload = parameters.WriteBinary(BinarySerializationSettings.Latest)
-            };
-            var response = Send(request);
-            if (response.Error != RpcError.NONE)
-                throw new Exception($"Exception in {typeof(TCommand).Name}({request.Id}): {response.Error}");
-            var result = Serialization.ReadBinary<TResult>(response.Payload, BinarySerializationSettings.Latest);
-            if (result == null)
-                throw new Exception($"{typeof(TCommand).Name}({request.Id}) returned NULL");
-            return result;
-        }
-        
-        public RpcMessage Send(RpcMessage request, int timeout = DefaultRequestTimeout)
-        {
-            var start = DateTime.Now;
-            var ev = Queue(request.Id);
-            _transport.Send(request);
-            var result = GetResult(request.Id, ev, timeout);
-            return result;
-        }
-
-        public ManualResetEventSlim Queue(Guid id)
-        {
-            var ev = new ManualResetEventSlim();
-            _events[id] = ev;
-            return ev;
-        }
-
-        public RpcMessage GetResult(Guid id, ManualResetEventSlim ev, int timeout)
-        {
-            if (_responses.TryGetValue(id, out var result))
-            {
-                _responses.Remove(id, out result);
-                _events.Remove(id, out ev);
-                return result;
-            }
-
-            try
-            {
-                if (!ev.Wait(timeout))
-                {
-                    return new RpcMessage() { Id = id, Error = RpcError.TIMEOUT };
-                }
-            }
-            catch (Exception e)
-            {
-                RaiseLogEvent(LogType.Error, "", e.Message);
-                throw;
-            }
-            
-            _responses.Remove(id, out result);
-            _events.Remove(id, out ev);
-            return result ?? new RpcMessage() { Id =id, Error = RpcError.UNKNOWN };
-        }
-        
         #endregion
         
-        
         #region Client Interface
 
         public override DatabaseInfo Info()
@@ -204,13 +139,13 @@ namespace InABox.Rpc
 
         protected override TEntity[] DoLoad(Filter<TEntity>? filter = null, SortOrder<TEntity>? sort = null)
         {
-            return DoQuery(filter, null, sort).Rows.Select(r => r.ToObject<TEntity>()).ToArray();
+            return DoQuery(filter, null, sort).ToObjects<TEntity>().ToArray();
         }
         
         
         protected override Dictionary<string, CoreTable> DoQueryMultiple(Dictionary<string, IQueryDef> queries)
         {
-            var result = new Dictionary<String, CoreTable>();
+            var result = new Dictionary<string, CoreTable>();
             
             var parameters = new RpcQueryParameters()
             {
@@ -247,7 +182,7 @@ namespace InABox.Rpc
                 Items = items
             };
             var result = _transport.Send<RpcSaveCommand, RpcSaveParameters, RpcSaveResult>(parameters);
-            for (int i=0; i< result.Deltas.Length; i++)
+            for (int i = 0; i < result.Deltas.Length; i++)
             {
                 items[i].SetObserving(false);
                 foreach (var (key, value) in result.Deltas[i])
@@ -270,7 +205,7 @@ namespace InABox.Rpc
             var parameters = new RpcDeleteParameters()
             {
                 Type = typeof(TEntity),
-                IDs = entities.Select(x=>x.ID).ToArray(),
+                IDs = entities.Select(x => x.ID).ToArray(),
                 AuditNote = auditnote
             };
             _transport.Send<RpcDeleteCommand, RpcDeleteParameters, RpcDeleteResult>(parameters);
@@ -284,7 +219,7 @@ namespace InABox.Rpc
                 _transport.Send<RpcPingCommand, RpcPingParameters, RpcPingResult>(new RpcPingParameters());
                 return true;
             }
-            catch (Exception e)
+            catch (Exception)
             {
                 return false;
             }

+ 10 - 12
InABox.Client.RPC/Transports/RPCClientTransport.cs

@@ -72,16 +72,14 @@ namespace InABox.Rpc
             
             Send(request);
 
-            var response = GetResponse(request.Id, ev, DefaultRequestTimeout);
-            if (response == null)
-                throw new Exception($"{typeof(TCommand).Name}({request.Id}) returned NULL");
-            
+            var response = GetResponse(request.Id, ev, DefaultRequestTimeout)
+                ?? throw new Exception($"{typeof(TCommand).Name}({request.Id}) returned NULL");
+
             if (response.Error != RpcError.NONE)
-                throw new Exception($"Exception in {typeof(TCommand).Name}({request.Id}): {response.Error}");
+                throw new RpcException($"Exception in {typeof(TCommand).Name}({request.Id})", response.Error);
             
-            var result = Serialization.ReadBinary<TResult>(response.Payload, BinarySerializationSettings.Latest);
-            if (result == null)
-                throw new Exception($"Cannot Deserialize {typeof(TCommand).Name}({request.Id})");
+            var result = Serialization.ReadBinary<TResult>(response.Payload, BinarySerializationSettings.Latest)
+                ?? throw new Exception($"Cannot Deserialize {typeof(TCommand).Name}({request.Id})");
             
             return result;
         }
@@ -141,19 +139,19 @@ namespace InABox.Rpc
 
         public DatabaseInfo? Info()
         {
-            DatabaseInfo? result = null;
             try
             {
                 var transport = Clone();
                 transport.Connect();
-                result = transport.Send<RpcInfoCommand,RpcInfoParameters,RpcInfoResult>(new RpcInfoParameters()).Info;
+                var result = transport.Send<RpcInfoCommand,RpcInfoParameters,RpcInfoResult>(new RpcInfoParameters()).Info;
                 transport.Disconnect();
+
+                return result;
             }
-            catch (Exception e)
+            catch (Exception)
             {
                 return null;
             }
-            return result;
         }
 
     }

+ 3 - 3
InABox.Client.RPC/Transports/Socket/RPCClientSocketTransport.cs

@@ -8,7 +8,7 @@ namespace InABox.Rpc
     public class RpcClientSocketTransport : RpcClientTransport, IDisposable
     {
         private WebSocket _socket;
-        private String _url;
+        private string _url;
         public RpcClientSocketTransport(string url)
         {
             _url = url;
@@ -22,9 +22,9 @@ namespace InABox.Rpc
         private void Socket_OnMessage(object? sender, MessageEventArgs e)
         {
             RpcMessage? message = null;
-            if ((e.IsBinary) && (e.RawData != null))
+            if (e.IsBinary && (e.RawData != null))
                 message = Serialization.ReadBinary<RpcMessage>(e.RawData, BinarySerializationSettings.Latest);
-            else if ((e.IsText) && !String.IsNullOrWhiteSpace(e.Data))
+            else if (e.IsText && !string.IsNullOrWhiteSpace(e.Data))
                 message = Serialization.Deserialize<RpcMessage>(e.Data);
      
             Accept(message);

+ 6 - 6
InABox.Core/Notifications/Notifier.cs

@@ -36,13 +36,13 @@ namespace InABox.Core
     }
 
 
-    public abstract class Notifier
+    public interface INotifier
     {
-        protected abstract void NotifyAll<TNotification>(TNotification notification) where TNotification : BaseObject;
-        protected abstract void NotifySession<TNotification>(Guid session, TNotification notification) where TNotification : BaseObject;
-        protected abstract void NotifySession(Guid session, Type TNotification, BaseObject notification);
-        protected abstract IEnumerable<Guid> GetUserSessions(Guid user);
-        protected abstract IEnumerable<Guid> GetSessions(Platform platform);
+        void NotifyAll<TNotification>(TNotification notification) where TNotification : BaseObject;
+        void NotifySession<TNotification>(Guid session, TNotification notification) where TNotification : BaseObject;
+        void NotifySession(Guid session, Type TNotification, BaseObject notification);
+        IEnumerable<Guid> GetUserSessions(Guid user);
+        IEnumerable<Guid> GetSessions(Platform platform);
 
         public void Push<TNotification>(TNotification notification)
             where TNotification : BaseObject

+ 2 - 2
InABox.Core/Notifications/Notify.cs

@@ -8,11 +8,11 @@ namespace InABox.Core
     {
         private static List<IPollHandler> Handlers = new List<IPollHandler>();
 
-        private static List<Notifier> Notifiers { get; set; } = new List<Notifier>();
+        private static List<INotifier> Notifiers { get; set; } = new List<INotifier>();
 
         private Notify() { }
 
-        public static void AddNotifier(Notifier notifier) =>
+        public static void AddNotifier(INotifier notifier) =>
             Notifiers.Add(notifier);
 
         public static void Push<TNotification>(TNotification notification) where TNotification : BaseObject => 

+ 29 - 18
InABox.Core/Serialization.cs

@@ -662,20 +662,13 @@ namespace InABox.Core
                 }
             }
         }
-
-        /// <summary>
-        /// An implementation of binary serialising a <typeparamref name="TObject"/>; this is the inverse of <see cref="ReadObject{TObject}(CoreBinaryReader)"/>.
-        /// </summary>
-        /// <remarks>
-        /// Also serialises the names of properties along with the values.
-        /// </remarks>
-        /// <typeparam name="TObject"></typeparam>
-        /// <param name="writer"></param>
-        /// <param name="entity"></param>
-        public static void WriteObject<TObject>(this CoreBinaryWriter writer, TObject entity)
+        public static void WriteObject<TObject>(this CoreBinaryWriter writer, TObject entity, Type type)
             where TObject : BaseObject
         {
-            var properties = SerializableProperties(typeof(TObject)).ToList();
+            if (!type.IsSubclassOf(typeof(TObject)))
+                throw new Exception($"{type.EntityName()} is not a subclass of {typeof(TObject).EntityName()}");
+
+            var properties = SerializableProperties(type).ToList();
 
             writer.Write(properties.Count);
             foreach (var property in properties)
@@ -687,15 +680,24 @@ namespace InABox.Core
         }
 
         /// <summary>
-        /// The inverse of <see cref="WriteObject{TObject}(CoreBinaryWriter, TObject)"/>.
+        /// An implementation of binary serialising a <typeparamref name="TObject"/>; this is the inverse of <see cref="ReadObject{TObject}(CoreBinaryReader)"/>.
         /// </summary>
+        /// <remarks>
+        /// Also serialises the names of properties along with the values.
+        /// </remarks>
         /// <typeparam name="TObject"></typeparam>
-        /// <param name="reader"></param>
-        /// <returns></returns>
-        public static TObject ReadObject<TObject>(this CoreBinaryReader reader)
-            where TObject : BaseObject, new()
+        /// <param name="writer"></param>
+        /// <param name="entity"></param>
+        public static void WriteObject<TObject>(this CoreBinaryWriter writer, TObject entity)
+            where TObject : BaseObject, new() => WriteObject(writer, entity, typeof(TObject));
+
+        public static TObject ReadObject<TObject>(this CoreBinaryReader reader, Type type)
+            where TObject : BaseObject
         {
-            var obj = new TObject();
+            if (!type.IsSubclassOf(typeof(TObject)))
+                throw new Exception($"{type.EntityName()} is not a subclass of {typeof(TObject).EntityName()}");
+
+            var obj = (Activator.CreateInstance(type) as BaseObject)!;
             obj.SetObserving(false);
 
             var nProps = reader.ReadInt32();
@@ -712,6 +714,15 @@ namespace InABox.Core
             return obj;
         }
 
+        /// <summary>
+        /// The inverse of <see cref="WriteObject{TObject}(CoreBinaryWriter, TObject)"/>.
+        /// </summary>
+        /// <typeparam name="TObject"></typeparam>
+        /// <param name="reader"></param>
+        /// <returns></returns>
+        public static TObject ReadObject<TObject>(this CoreBinaryReader reader)
+            where TObject : BaseObject, new() => reader.ReadObject<TObject>(typeof(TObject));
+
         /// <summary>
         /// An implementation of binary serialising multiple <typeparamref name="TObject"/>s;
         /// this is the inverse of <see cref="ReadObjects{TObject}(CoreBinaryReader)"/>.

+ 1 - 0
InABox.RPC.Shared/IRPCSession.cs

@@ -9,5 +9,6 @@ namespace InABox.Rpc
         string? Version { get; set; }
 
         string UserID { get; set; }
+        Guid UserGuid { get; set; }
     }
 }

+ 11 - 3
InABox.RPC.Shared/RPCMessage.cs

@@ -16,10 +16,18 @@ namespace InABox.Rpc
         {
             Id = Guid.NewGuid();
             Command = "";
-            Payload = new byte[] { };
+            Payload = Array.Empty<byte>();
             Error = RpcError.NONE;
         }
-        
+
+        public RpcMessage(Guid id, string command, byte[] payload)
+        {
+            Id = id;
+            Command = command;
+            Payload = payload;
+            Error = RpcError.NONE;
+        }
+
         public void SerializeBinary(CoreBinaryWriter writer)
         {
             writer.Write(Id);
@@ -32,7 +40,7 @@ namespace InABox.Rpc
         {
             Id = reader.ReadGuid();
             Command = reader.ReadString();
-            Payload = reader.ReadBinaryValue<byte[]>() ?? new byte[] { };
+            Payload = reader.ReadBinaryValue<byte[]>();
             if (Enum.TryParse<RpcError>(reader.ReadString(), out var error))
                 Error = error;
         }

+ 38 - 0
InABox.RPC.Shared/RpcNotification.cs

@@ -0,0 +1,38 @@
+using InABox.Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace InABox.Rpc
+{
+    public class RpcNotification : ISerializeBinary
+    {
+        public Type Type { get; set; }
+
+        public BaseObject Object { get; set; }
+
+        public void SerializeBinary(CoreBinaryWriter writer)
+        {
+            writer.Write(Type.EntityName());
+            writer.WriteObject(Object, Type);
+        }
+
+        public void DeserializeBinary(CoreBinaryReader reader)
+        {
+            Type = CoreUtils.GetEntity(reader.ReadString());
+            Object = reader.ReadObject<BaseObject>(Type);
+        }
+
+        public static RpcNotification Create<TNotification>(TNotification notification)
+            where TNotification : BaseObject
+        {
+            return new RpcNotification
+            {
+                Type = typeof(TNotification),
+                Object = notification
+            };
+        }
+    }
+}

+ 16 - 0
InABox.Server/CredentialsCache.cs

@@ -3,6 +3,7 @@ using System.Security.Cryptography;
 using InABox.API;
 using InABox.Core;
 using InABox.Database;
+using Microsoft.Exchange.WebServices.Data;
 
 namespace InABox.API
 {
@@ -97,6 +98,21 @@ namespace InABox.API
             return _cache.FirstOrDefault(x => x.ID == session.User);
         }
 
+        /// <summary>
+        /// Validate a given session, and refresh the session expiry if valid; use for database queries that need to refresh the user's expiry time.
+        /// </summary>
+        /// <param name="sessionID"></param>
+        /// <returns></returns>
+        public static User? ValidateAndRefresh(Guid sessionID)
+        {
+            var user = Validate(sessionID);
+            if(user is not null)
+            {
+                RefreshSessionExpiry(sessionID);
+            }
+            return user;
+        }
+
         public static User? ValidateUser(string? pin)
         {
             if (String.IsNullOrWhiteSpace(pin))

+ 6 - 6
InABox.Server/IPC/IPCNotifier.cs

@@ -3,7 +3,7 @@ using InABox.Core;
 
 namespace InABox.IPC
 {
-    public class IPCNotifier : Notifier
+    public class IPCNotifier : INotifier
     {
         IPCNotifyState NotifyState { get; set; }
 
@@ -18,17 +18,17 @@ namespace InABox.IPC
             Notify.Poll(session.SessionID);
         }
 
-        protected override IEnumerable<Guid> GetSessions(Platform platform)
+        public IEnumerable<Guid> GetSessions(Platform platform)
         {
             return NotifyState.SessionMap.Where(x => x.Value.Platform == platform).Select(x => x.Key);
         }
 
-        protected override IEnumerable<Guid> GetUserSessions(Guid userID)
+        public IEnumerable<Guid> GetUserSessions(Guid userID)
         {
             return CredentialsCache.GetUserSessions(userID);
         }
 
-        protected override void NotifyAll<TNotification>(TNotification notification)
+        public void NotifyAll<TNotification>(TNotification notification) where TNotification : BaseObject
         {
             foreach(var session in NotifyState.SessionMap.Values)
             {
@@ -42,7 +42,7 @@ namespace InABox.IPC
             }
         }
 
-        protected override void NotifySession<TNotification>(Guid sessionID, TNotification notification)
+        public void NotifySession<TNotification>(Guid sessionID, TNotification notification) where TNotification : BaseObject
         {
             if(NotifyState.SessionMap.TryGetValue(sessionID, out var session))
             {
@@ -56,7 +56,7 @@ namespace InABox.IPC
             }
         }
 
-        protected override void NotifySession(Guid sessionID, Type TNotification, BaseObject notification)
+        public void NotifySession(Guid sessionID, Type TNotification, BaseObject notification)
         {
             if(NotifyState.SessionMap.TryGetValue(sessionID, out var session))
             {

+ 1 - 1
InABox.Server/RPC/Handlers/Delete.cs

@@ -10,7 +10,7 @@ namespace InABox.Rpc
     {
         protected override RpcDeleteResult Execute(IRpcSession session, RpcDeleteParameters parameters)
         {
-            var user = CredentialsCache.Validate(session.ID)
+            var user = CredentialsCache.ValidateAndRefresh(session.ID)
                 ?? throw new RpcException("User not found in Credentials Cache!", RpcError.UNAUTHENTICATED);
 
             var store = DbFactory.FindStore(parameters.Type, user.ID, user.UserID, session.Platform, session.Version ?? "");

+ 1 - 1
InABox.Server/RPC/Handlers/Query.cs

@@ -9,7 +9,7 @@ namespace InABox.Rpc
     {
         protected override RpcQueryResult Execute(IRpcSession session, RpcQueryParameters parameters)
         {
-            var user = CredentialsCache.Validate(session.ID)
+            var user = CredentialsCache.ValidateAndRefresh(session.ID)
                 ?? throw new RpcException("User not found in Credentials Cache!", RpcError.UNAUTHENTICATED);
 
             var response = new RpcQueryResult();

+ 1 - 1
InABox.Server/RPC/Handlers/Save.cs

@@ -9,7 +9,7 @@ namespace InABox.Rpc
     {
         protected override RpcSaveResult Execute(IRpcSession session, RpcSaveParameters parameters)
         {
-            var user = CredentialsCache.Validate(session.ID)
+            var user = CredentialsCache.ValidateAndRefresh(session.ID)
                 ?? throw new RpcException("User not found in Credentials Cache!", RpcError.UNAUTHENTICATED);
 
             var response = new RpcSaveResult();

+ 3 - 2
InABox.Server/RPC/RPCServer.cs

@@ -7,6 +7,7 @@ using InABox.IPC;
 
 namespace InABox.Rpc
 {
+
     public class RpcServer<TTransport> : IDisposable, IRpcServer where TTransport : IRpcServerTransport
     {
         private TTransport _transport;
@@ -15,9 +16,9 @@ namespace InABox.Rpc
         
         public bool IsSecure() => _transport?.IsSecure() ?? false;
 
-        public RpcServer(Func<TTransport> transport)
+        public RpcServer(TTransport transport)
         {
-            _transport = transport();
+            _transport = transport;
 
             _transport.OnOpen += Transport_OnOpen;
             _transport.OnClose += Transport_OnClose;

+ 1 - 0
InABox.Server/RPC/RPCServerSession.cs

@@ -8,5 +8,6 @@ namespace InABox.Rpc
         public Platform Platform { get; set; }
         public string? Version { get; set; }
         public string UserID { get; set; }
+        public Guid UserGuid { get; set; }
     }
 }

+ 5 - 0
InABox.Server/RPC/Transports/Pipe/RPCServerPipeTransport.cs

@@ -66,6 +66,11 @@ namespace InABox.Rpc
             });
         }
 
+        public override void Send(PipeConnection<RpcMessage?> connection, RpcMessage message)
+        {
+            connection.WriteAsync(message);
+        }
+
         private void Transport_OnDisconnected(object? sender, H.Pipes.Args.ConnectionEventArgs<RpcMessage?> e)
         {
             DoClose(e.Connection, RpcTransportCloseEventType.Closed);

+ 56 - 3
InABox.Server/RPC/Transports/RPCServerTransport.cs

@@ -2,9 +2,9 @@ using InABox.Core;
 
 namespace InABox.Rpc
 {
-    public abstract class RpcServerTransport<TConnection> : IRpcServerTransport where TConnection : notnull
-    {
 
+    public abstract class RpcServerTransport<TConnection> : INotifier, IRpcServerTransport where TConnection : notnull
+    {
         public abstract bool IsSecure();
         
         private Dictionary<TConnection, RpcServerSession> _sessions = new Dictionary<TConnection, RpcServerSession>();
@@ -81,6 +81,12 @@ namespace InABox.Rpc
             AfterMessage?.Invoke(this, new RpcTransportMessageArgs(session,message));
         }
         
+        /// <summary>
+        /// Handle a message from a client.
+        /// </summary>
+        /// <param name="connection">The client connection.</param>
+        /// <param name="message">The message to be handled.</param>
+        /// <returns>The response to be sent back to the client.</returns>
         public RpcMessage? DoMessage(TConnection? connection, RpcMessage? message)
         {
             var session = GetSession(connection);
@@ -119,6 +125,53 @@ namespace InABox.Rpc
 
             return response;
         }
-        
+
+        /// <summary>
+        /// Send a message to a particular client connection.
+        /// </summary>
+        /// <param name="connection">The connection to send to.</param>
+        /// <param name="message">The message to send.</param>
+        public abstract void Send(TConnection connection, RpcMessage message);
+
+        public void NotifyAll<TNotification>(TNotification notification) where TNotification : BaseObject
+        {
+            var message = new RpcMessage(Guid.NewGuid(), "Notification", RpcNotification.Create(notification).WriteBinary(BinarySerializationSettings.Latest)));
+            foreach (var connection in _sessions.Keys)
+            {
+                Send(connection, message);
+            }
+        }
+
+        public void NotifySession<TNotification>(Guid session, TNotification notification) where TNotification : BaseObject
+        {
+            var message = new RpcMessage(Guid.NewGuid(), "Notification", RpcNotification.Create(notification).WriteBinary(BinarySerializationSettings.Latest)));
+
+            var sessionConnection = _sessions.FirstOrDefault(x => x.Value.ID == session).Key;
+            if(sessionConnection is not null)
+            {
+                Send(sessionConnection, message);
+            }
+        }
+
+        public void NotifySession(Guid session, Type TNotification, BaseObject notification)
+        {
+            var message = new RpcMessage(Guid.NewGuid(), "Notification", new RpcNotification
+            {
+                Object = notification,
+                Type = TNotification
+            }.WriteBinary(BinarySerializationSettings.Latest));
+
+            var sessionConnection = _sessions.FirstOrDefault(x => x.Value.ID == session).Key;
+            if (sessionConnection is not null)
+            {
+                Send(sessionConnection, message);
+            }
+        }
+
+        public IEnumerable<Guid> GetUserSessions(Guid user) =>
+            _sessions.Values.Where(x => x.UserGuid == user).Select(x => x.ID);
+
+        public IEnumerable<Guid> GetSessions(Platform platform) =>
+            _sessions.Values.Where(x => x.Platform == platform).Select(x => x.ID);
     }
 }

+ 7 - 2
InABox.Server/RPC/Transports/Socket/RPCServerSocketConnection.cs

@@ -34,9 +34,9 @@ namespace InABox.Rpc
             Task.Run(() =>
             {
                 RpcMessage? request = null;
-                if ((e.IsBinary) && (e.RawData != null))
+                if (e.IsBinary && (e.RawData != null))
                     request = Serialization.ReadBinary<RpcMessage>(e.RawData, BinarySerializationSettings.Latest);
-                else if ((e.IsText) && !String.IsNullOrWhiteSpace(e.Data))
+                else if (e.IsText && !String.IsNullOrWhiteSpace(e.Data))
                     request = Serialization.Deserialize<RpcMessage>(e.Data);
                 
                 RpcMessage? response = Transport?.DoMessage(this, request);
@@ -55,5 +55,10 @@ namespace InABox.Rpc
 
         }
 
+        public void Send(RpcMessage message)
+        {
+            Send(Serialization.WriteBinary(message, BinarySerializationSettings.Latest));
+        }
+
     }
 }

+ 5 - 0
InABox.Server/RPC/Transports/Socket/RPCServerSocketTransport.cs

@@ -40,6 +40,11 @@ namespace InABox.Rpc
             _server?.Start();
         }
 
+        public override void Send(RpcServerSocketConnection connection, RpcMessage message)
+        {
+            connection.Send(message);
+        }
+
         public override void Stop()
         {
             _server?.Stop();

+ 6 - 6
InABox.Server/Rest/RestListener.cs

@@ -447,7 +447,7 @@ namespace InABox.API
         }
     }
 
-    class RestNotifier : Notifier
+    class RestNotifier : INotifier
     {
         private WebSocketServer SocketServer;
 
@@ -474,27 +474,27 @@ namespace InABox.API
             SocketServer.Stop();
         }
 
-        protected override void NotifyAll<TNotification>(TNotification notification)
+        public void NotifyAll<TNotification>(TNotification notification) where TNotification : BaseObject
         {
             SocketServer.Push(notification);
         }
 
-        protected override void NotifySession(Guid session, Type TNotification, BaseObject notification)
+        public void NotifySession(Guid session, Type TNotification, BaseObject notification)
         {
             SocketServer.Push(session, TNotification, notification);
         }
 
-        protected override void NotifySession<TNotification>(Guid session, TNotification notification)
+        public void NotifySession<TNotification>(Guid session, TNotification notification) where TNotification : BaseObject
         {
             SocketServer.Push(session, notification);
         }
 
-        protected override IEnumerable<Guid> GetUserSessions(Guid userID)
+        public IEnumerable<Guid> GetUserSessions(Guid userID)
         {
             return CredentialsCache.GetUserSessions(userID);
         }
 
-        protected override IEnumerable<Guid> GetSessions(Platform platform)
+        public IEnumerable<Guid> GetSessions(Platform platform)
         {
             return SocketServer.GetSessions(platform);
         }

+ 0 - 1
inabox.client.ipc/IPCClientTransport.cs

@@ -46,7 +46,6 @@ namespace InABox.Client.IPC
 
         public IPCMessage Send(IPCMessage request, int timeout = DefaultRequestTimeout)
         {
-            var start = DateTime.Now;
             var ev = Queue(request.RequestID);
             Client.WriteAsync(request);
             var result = GetResult(request.RequestID, ev, timeout);