123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534 |
- #if !BESTHTTP_DISABLE_SOCKETIO
- using System;
- namespace BestHTTP.SocketIO3
- {
- using BestHTTP;
- using BestHTTP.Logger;
- using BestHTTP.SocketIO3.Events;
- public delegate void SocketIOCallback(Socket socket, IncomingPacket packet, params object[] args);
- public delegate void SocketIOAckCallback(Socket socket, IncomingPacket packet, params object[] args);
- public struct EmitBuilder
- {
- private Socket socket;
- internal bool isVolatile;
- internal int id;
- internal EmitBuilder(Socket s)
- {
- this.socket = s;
- this.isVolatile = false;
- this.id = -1;
- }
- public EmitBuilder ExpectAcknowledgement(Action callback)
- {
- this.id = this.socket.Manager.NextAckId;
- string name = IncomingPacket.GenerateAcknowledgementNameFromId(this.id);
- this.socket.TypedEventTable.Register(name, null, _ => callback(), true);
- return this;
- }
- public EmitBuilder ExpectAcknowledgement<T>(Action<T> callback)
- {
- this.id = this.socket.Manager.NextAckId;
- string name = IncomingPacket.GenerateAcknowledgementNameFromId(this.id);
- this.socket.TypedEventTable.Register(name, new Type[] { typeof(T) }, (args) => callback((T)args[0]), true);
- return this;
- }
- public EmitBuilder Volatile()
- {
- this.isVolatile = true;
- return this;
- }
- public Socket Emit(string eventName, params object[] args)
- {
- bool blackListed = EventNames.IsBlacklisted(eventName);
- if (blackListed)
- throw new ArgumentException("Blacklisted event: " + eventName);
- var packet = this.socket.Manager.Parser.CreateOutgoing(this.socket, SocketIOEventTypes.Event, this.id, eventName, args);
- packet.IsVolatile = this.isVolatile;
- (this.socket.Manager as IManager).SendPacket(packet);
- return this.socket;
- }
- }
- /// <summary>
- /// This class represents a Socket.IO namespace.
- /// </summary>
- public sealed class Socket : ISocket
- {
- #region Public Properties
- /// <summary>
- /// The SocketManager instance that created this socket.
- /// </summary>
- public SocketManager Manager { get; private set; }
- /// <summary>
- /// The namespace that this socket is bound to.
- /// </summary>
- public string Namespace { get; private set; }
- /// <summary>
- /// Unique Id of the socket.
- /// </summary>
- public string Id { get; private set; }
- /// <summary>
- /// True if the socket is connected and open to the server. False otherwise.
- /// </summary>
- public bool IsOpen { get; private set; }
- public IncomingPacket CurrentPacket { get { return this.currentPacket; } }
- public LoggingContext Context { get; private set; }
- #endregion
- internal TypedEventTable TypedEventTable;
- private IncomingPacket currentPacket = IncomingPacket.Empty;
- /// <summary>
- /// Internal constructor.
- /// </summary>
- internal Socket(string nsp, SocketManager manager)
- {
- this.Context = new LoggingContext(this);
- this.Context.Add("nsp", nsp);
- this.Namespace = nsp;
- this.Manager = manager;
- this.IsOpen = false;
- this.TypedEventTable = new TypedEventTable(this);
- this.On<ConnectResponse>(EventNames.GetNameFor(SocketIOEventTypes.Connect), OnConnected);
- }
- private void OnConnected(ConnectResponse resp)
- {
- this.Id = resp.sid;
- this.IsOpen = true;
- }
- #region Socket Handling
- /// <summary>
- /// Internal function to start opening the socket.
- /// </summary>
- void ISocket.Open()
- {
- HTTPManager.Logger.Information("Socket", string.Format("Open - Manager.State = {0}", Manager.State), this.Context);
- // The transport already established the connection
- if (Manager.State == SocketManager.States.Open)
- OnTransportOpen();
- else if (Manager.Options.AutoConnect && Manager.State == SocketManager.States.Initial)
- Manager.Open();
- }
- /// <summary>
- /// Disconnects this socket/namespace.
- /// </summary>
- public void Disconnect()
- {
- (this as ISocket).Disconnect(true);
- }
- /// <summary>
- /// Disconnects this socket/namespace.
- /// </summary>
- void ISocket.Disconnect(bool remove)
- {
- // Send a disconnect packet to the server
- if (IsOpen)
- {
- var packet = this.Manager.Parser.CreateOutgoing(this, SocketIOEventTypes.Disconnect, -1, null, null);
- (Manager as IManager).SendPacket(packet);
- // IsOpen must be false, because in the OnPacket preprocessing the packet would call this function again
- IsOpen = false;
- (this as ISocket).OnPacket(new IncomingPacket(TransportEventTypes.Message, SocketIOEventTypes.Disconnect, this.Namespace, -1));
- }
- if (remove)
- {
- this.TypedEventTable.Clear();
-
- (Manager as IManager).Remove(this);
- }
- }
- #endregion
- #region Emit Implementations
- /// <summary>
- /// By emitting a volatile event, if the transport isn't ready the event is going to be discarded.
- /// </summary>
- public EmitBuilder Volatile()
- {
- return new EmitBuilder(this) { isVolatile = true };
- }
- public EmitBuilder ExpectAcknowledgement(Action callback)
- {
- return new EmitBuilder(this).ExpectAcknowledgement(callback);
- }
- public EmitBuilder ExpectAcknowledgement<T>(Action<T> callback)
- {
- return new EmitBuilder(this).ExpectAcknowledgement<T>(callback);
- }
- public Socket Emit(string eventName, params object[] args)
- {
- return new EmitBuilder(this).Emit(eventName, args);
- }
- public Socket EmitAck(params object[] args)
- {
- return EmitAck(this.currentPacket, args);
- }
- public Socket EmitAck(IncomingPacket packet, params object[] args)
- {
- if (packet.Equals(IncomingPacket.Empty))
- throw new ArgumentNullException("currentPacket");
- if (packet.Id < 0 || (packet.SocketIOEvent != SocketIOEventTypes.Event && packet.SocketIOEvent != SocketIOEventTypes.BinaryEvent))
- throw new ArgumentException("Wrong packet - you can't send an Ack for a packet with id < 0 or SocketIOEvent != Event or SocketIOEvent != BinaryEvent!");
- var eventType = packet.SocketIOEvent == SocketIOEventTypes.Event ? SocketIOEventTypes.Ack : SocketIOEventTypes.BinaryAck;
- (Manager as IManager).SendPacket(this.Manager.Parser.CreateOutgoing(this, eventType, packet.Id, null, args));
- return this;
- }
- #endregion
- #region On Implementations
- public void On(SocketIOEventTypes eventType, Action callback)
- {
- this.TypedEventTable.Register(EventNames.GetNameFor(eventType), null, _ => callback());
- }
- public void On<T>(SocketIOEventTypes eventType, Action<T> callback)
- {
- string eventName = EventNames.GetNameFor(eventType);
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T) }, (args) =>
- {
- T arg = default(T);
- try
- {
- arg = (T)args[0];
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", String.Format("On<{0}>('{1}') - cast failed", typeof(T).Name, eventName), ex, this.Context);
- }
- callback(arg);
- });
- }
- public void On(string eventName, Action callback)
- {
- this.TypedEventTable.Register(eventName, null, _ => callback());
- }
- public void On<T>(string eventName, Action<T> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T) }, (args) => {
- T arg = default(T);
- try
- {
- arg = (T)args[0];
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", String.Format("On<{0}>('{1}') - cast failed", typeof(T).Name, eventName), ex, this.Context);
- return;
- }
- callback(arg);
- });
- }
- public void On<T1, T2>(string eventName, Action<T1, T2> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2) }, (args) => {
- T1 arg1 = default(T1);
- T2 arg2 = default(T2);
- try
- {
- arg1 = (T1)args[0];
- arg2 = (T2)args[1];
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", String.Format("On<{0}, {1}>('{2}') - cast failed", typeof(T1).Name, typeof(T2).Name, eventName), ex, this.Context);
- return;
- }
- callback(arg1, arg2);
- });
- }
- public void On<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3) }, (args) => {
- T1 arg1 = default(T1);
- T2 arg2 = default(T2);
- T3 arg3 = default(T3);
- try
- {
- arg1 = (T1)args[0];
- arg2 = (T2)args[1];
- arg3 = (T3)args[2];
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", String.Format("On<{0}, {1}, {2}>('{3}') - cast failed", typeof(T1).Name, typeof(T2).Name, typeof(T3).Name, eventName), ex, this.Context);
- return;
- }
- callback(arg1, arg2, arg3);
- });
- }
- public void On<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, (args) => {
- T1 arg1 = default(T1);
- T2 arg2 = default(T2);
- T3 arg3 = default(T3);
- T4 arg4 = default(T4);
- try
- {
- arg1 = (T1)args[0];
- arg2 = (T2)args[1];
- arg3 = (T3)args[2];
- arg4 = (T4)args[3];
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", String.Format("On<{0}, {1}, {2}, {3}>('{4}') - cast failed", typeof(T1).Name, typeof(T2).Name, typeof(T3).Name, typeof(T4).Name, eventName), ex, this.Context);
- return;
- }
- callback(arg1, arg2, arg3, arg4);
- });
- }
- public void On<T1, T2, T3, T4, T5>(string eventName, Action<T1, T2, T3, T4, T5> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, (args) => {
- T1 arg1 = default(T1);
- T2 arg2 = default(T2);
- T3 arg3 = default(T3);
- T4 arg4 = default(T4);
- T5 arg5 = default(T5);
- try
- {
- arg1 = (T1)args[0];
- arg2 = (T2)args[1];
- arg3 = (T3)args[2];
- arg4 = (T4)args[3];
- arg5 = (T5)args[4];
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", String.Format("On<{0}, {1}, {2}, {3}, {4}>('{5}') - cast failed", typeof(T1).Name, typeof(T2).Name, typeof(T3).Name, typeof(T4).Name, typeof(T5).Name, eventName), ex, this.Context);
- return;
- }
- callback(arg1, arg2, arg3, arg4, arg5);
- });
- }
- #endregion
- #region Once Implementations
- public void Once(string eventName, Action callback)
- {
- this.TypedEventTable.Register(eventName, null, _ => callback(), true);
- }
- public void Once<T>(string eventName, Action<T> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T) }, (args) => callback((T)args[0]), true);
- }
- public void Once<T1, T2>(string eventName, Action<T1, T2> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2) }, (args) => callback((T1)args[0], (T2)args[1]), true);
- }
- public void Once<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3) }, (args) => callback((T1)args[0], (T2)args[1], (T3)args[2]), true);
- }
- public void Once<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, (args) => callback((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3]), true);
- }
- public void Once<T1, T2, T3, T4, T5>(string eventName, Action<T1, T2, T3, T4, T5> callback)
- {
- this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, (args) => callback((T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4]), true);
- }
- #endregion
- #region Off Implementations
- /// <summary>
- /// Remove all callbacks for all events.
- /// </summary>
- public void Off()
- {
- this.TypedEventTable.Clear();
- }
- /// <summary>
- /// Removes all callbacks to the given event.
- /// </summary>
- public void Off(string eventName)
- {
- this.TypedEventTable.Unregister(eventName);
- }
- /// <summary>
- /// Removes all callbacks to the given event.
- /// </summary>
- public void Off(SocketIOEventTypes type)
- {
- Off(EventNames.GetNameFor(type));
- }
- #endregion
- #region Packet Handling
- /// <summary>
- /// Last call of the OnPacket chain(Transport -> Manager -> Socket), we will dispatch the event if there is any callback
- /// </summary>
- void ISocket.OnPacket(IncomingPacket packet)
- {
- // Some preprocessing of the packet
- switch(packet.SocketIOEvent)
- {
- case SocketIOEventTypes.Connect:
- break;
- case SocketIOEventTypes.Disconnect:
- if (IsOpen)
- {
- IsOpen = false;
- this.TypedEventTable.Call(packet);
- Disconnect();
- }
- break;
- }
- try
- {
- this.currentPacket = packet;
- // Dispatch the event to all subscriber
- this.TypedEventTable.Call(packet);
- }
- finally
- {
- this.currentPacket = IncomingPacket.Empty;
- }
- }
- #endregion
- public Subscription GetSubscription(string name)
- {
- return this.TypedEventTable.GetSubscription(name);
- }
- /// <summary>
- /// Emits an internal packet-less event to the user level.
- /// </summary>
- void ISocket.EmitEvent(SocketIOEventTypes type, params object[] args)
- {
- (this as ISocket).EmitEvent(EventNames.GetNameFor(type), args);
- }
- /// <summary>
- /// Emits an internal packet-less event to the user level.
- /// </summary>
- void ISocket.EmitEvent(string eventName, params object[] args)
- {
- if (!string.IsNullOrEmpty(eventName))
- this.TypedEventTable.Call(eventName, args);
- }
- void ISocket.EmitError(string msg)
- {
- var outcoming = this.Manager.Parser.CreateOutgoing(this, SocketIOEventTypes.Error, -1, null, new Error(msg));
- IncomingPacket packet = IncomingPacket.Empty;
- if (outcoming.IsBinary)
- packet = this.Manager.Parser.Parse(this.Manager, outcoming.PayloadData);
- else
- packet = this.Manager.Parser.Parse(this.Manager, outcoming.Payload);
- (this as ISocket).EmitEvent(SocketIOEventTypes.Error, packet.DecodedArg ?? packet.DecodedArgs);
- }
- #region Private Helper Functions
- /// <summary>
- /// Called when the underlying transport is connected
- /// </summary>
- internal void OnTransportOpen()
- {
- HTTPManager.Logger.Information("Socket", "OnTransportOpen - IsOpen: " + this.IsOpen, this.Context);
- if (this.IsOpen)
- return;
- object authData = null;
- try
- {
- authData = this.Manager.Options.Auth != null ? this.Manager.Options.Auth(this.Manager, this) : null;
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", "OnTransportOpen - Options.Auth", ex, this.Context);
- }
- try
- {
- (Manager as IManager).SendPacket(this.Manager.Parser.CreateOutgoing(this, SocketIOEventTypes.Connect, -1, null, authData));
- }
- catch (Exception ex)
- {
- HTTPManager.Logger.Exception("Socket", "OnTransportOpen", ex, this.Context);
- }
- }
- #endregion
- }
- }
- #endif
|