Socket.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. #if !BESTHTTP_DISABLE_SOCKETIO
  2. using System;
  3. namespace BestHTTP.SocketIO3
  4. {
  5. using BestHTTP;
  6. using BestHTTP.Logger;
  7. using BestHTTP.SocketIO3.Events;
  8. public delegate void SocketIOCallback(Socket socket, IncomingPacket packet, params object[] args);
  9. public delegate void SocketIOAckCallback(Socket socket, IncomingPacket packet, params object[] args);
  10. public struct EmitBuilder
  11. {
  12. private Socket socket;
  13. internal bool isVolatile;
  14. internal int id;
  15. internal EmitBuilder(Socket s)
  16. {
  17. this.socket = s;
  18. this.isVolatile = false;
  19. this.id = -1;
  20. }
  21. public EmitBuilder ExpectAcknowledgement(Action callback)
  22. {
  23. this.id = this.socket.Manager.NextAckId;
  24. string name = IncomingPacket.GenerateAcknowledgementNameFromId(this.id);
  25. this.socket.TypedEventTable.Register(name, null, _ => callback(), true);
  26. return this;
  27. }
  28. public EmitBuilder ExpectAcknowledgement<T>(Action<T> callback)
  29. {
  30. this.id = this.socket.Manager.NextAckId;
  31. string name = IncomingPacket.GenerateAcknowledgementNameFromId(this.id);
  32. this.socket.TypedEventTable.Register(name, new Type[] { typeof(T) }, (args) => callback((T)args[0]), true);
  33. return this;
  34. }
  35. public EmitBuilder Volatile()
  36. {
  37. this.isVolatile = true;
  38. return this;
  39. }
  40. public Socket Emit(string eventName, params object[] args)
  41. {
  42. bool blackListed = EventNames.IsBlacklisted(eventName);
  43. if (blackListed)
  44. throw new ArgumentException("Blacklisted event: " + eventName);
  45. var packet = this.socket.Manager.Parser.CreateOutgoing(this.socket, SocketIOEventTypes.Event, this.id, eventName, args);
  46. packet.IsVolatile = this.isVolatile;
  47. (this.socket.Manager as IManager).SendPacket(packet);
  48. return this.socket;
  49. }
  50. }
  51. /// <summary>
  52. /// This class represents a Socket.IO namespace.
  53. /// </summary>
  54. public sealed class Socket : ISocket
  55. {
  56. #region Public Properties
  57. /// <summary>
  58. /// The SocketManager instance that created this socket.
  59. /// </summary>
  60. public SocketManager Manager { get; private set; }
  61. /// <summary>
  62. /// The namespace that this socket is bound to.
  63. /// </summary>
  64. public string Namespace { get; private set; }
  65. /// <summary>
  66. /// Unique Id of the socket.
  67. /// </summary>
  68. public string Id { get; private set; }
  69. /// <summary>
  70. /// True if the socket is connected and open to the server. False otherwise.
  71. /// </summary>
  72. public bool IsOpen { get; private set; }
  73. public IncomingPacket CurrentPacket { get { return this.currentPacket; } }
  74. public LoggingContext Context { get; private set; }
  75. #endregion
  76. internal TypedEventTable TypedEventTable;
  77. private IncomingPacket currentPacket = IncomingPacket.Empty;
  78. /// <summary>
  79. /// Internal constructor.
  80. /// </summary>
  81. internal Socket(string nsp, SocketManager manager)
  82. {
  83. this.Context = new LoggingContext(this);
  84. this.Context.Add("nsp", nsp);
  85. this.Namespace = nsp;
  86. this.Manager = manager;
  87. this.IsOpen = false;
  88. this.TypedEventTable = new TypedEventTable(this);
  89. this.On<ConnectResponse>(EventNames.GetNameFor(SocketIOEventTypes.Connect), OnConnected);
  90. }
  91. private void OnConnected(ConnectResponse resp)
  92. {
  93. this.Id = resp.sid;
  94. this.IsOpen = true;
  95. }
  96. #region Socket Handling
  97. /// <summary>
  98. /// Internal function to start opening the socket.
  99. /// </summary>
  100. void ISocket.Open()
  101. {
  102. HTTPManager.Logger.Information("Socket", string.Format("Open - Manager.State = {0}", Manager.State), this.Context);
  103. // The transport already established the connection
  104. if (Manager.State == SocketManager.States.Open)
  105. OnTransportOpen();
  106. else if (Manager.Options.AutoConnect && Manager.State == SocketManager.States.Initial)
  107. Manager.Open();
  108. }
  109. /// <summary>
  110. /// Disconnects this socket/namespace.
  111. /// </summary>
  112. public void Disconnect()
  113. {
  114. (this as ISocket).Disconnect(true);
  115. }
  116. /// <summary>
  117. /// Disconnects this socket/namespace.
  118. /// </summary>
  119. void ISocket.Disconnect(bool remove)
  120. {
  121. // Send a disconnect packet to the server
  122. if (IsOpen)
  123. {
  124. var packet = this.Manager.Parser.CreateOutgoing(this, SocketIOEventTypes.Disconnect, -1, null, null);
  125. (Manager as IManager).SendPacket(packet);
  126. // IsOpen must be false, because in the OnPacket preprocessing the packet would call this function again
  127. IsOpen = false;
  128. (this as ISocket).OnPacket(new IncomingPacket(TransportEventTypes.Message, SocketIOEventTypes.Disconnect, this.Namespace, -1));
  129. }
  130. if (remove)
  131. {
  132. this.TypedEventTable.Clear();
  133. (Manager as IManager).Remove(this);
  134. }
  135. }
  136. #endregion
  137. #region Emit Implementations
  138. /// <summary>
  139. /// By emitting a volatile event, if the transport isn't ready the event is going to be discarded.
  140. /// </summary>
  141. public EmitBuilder Volatile()
  142. {
  143. return new EmitBuilder(this) { isVolatile = true };
  144. }
  145. public EmitBuilder ExpectAcknowledgement(Action callback)
  146. {
  147. return new EmitBuilder(this).ExpectAcknowledgement(callback);
  148. }
  149. public EmitBuilder ExpectAcknowledgement<T>(Action<T> callback)
  150. {
  151. return new EmitBuilder(this).ExpectAcknowledgement<T>(callback);
  152. }
  153. public Socket Emit(string eventName, params object[] args)
  154. {
  155. return new EmitBuilder(this).Emit(eventName, args);
  156. }
  157. public Socket EmitAck(params object[] args)
  158. {
  159. return EmitAck(this.currentPacket, args);
  160. }
  161. public Socket EmitAck(IncomingPacket packet, params object[] args)
  162. {
  163. if (packet.Equals(IncomingPacket.Empty))
  164. throw new ArgumentNullException("currentPacket");
  165. if (packet.Id < 0 || (packet.SocketIOEvent != SocketIOEventTypes.Event && packet.SocketIOEvent != SocketIOEventTypes.BinaryEvent))
  166. throw new ArgumentException("Wrong packet - you can't send an Ack for a packet with id < 0 or SocketIOEvent != Event or SocketIOEvent != BinaryEvent!");
  167. var eventType = packet.SocketIOEvent == SocketIOEventTypes.Event ? SocketIOEventTypes.Ack : SocketIOEventTypes.BinaryAck;
  168. (Manager as IManager).SendPacket(this.Manager.Parser.CreateOutgoing(this, eventType, packet.Id, null, args));
  169. return this;
  170. }
  171. #endregion
  172. #region On Implementations
  173. public void On(SocketIOEventTypes eventType, Action callback)
  174. {
  175. this.TypedEventTable.Register(EventNames.GetNameFor(eventType), null, _ => callback());
  176. }
  177. public void On<T>(SocketIOEventTypes eventType, Action<T> callback)
  178. {
  179. string eventName = EventNames.GetNameFor(eventType);
  180. this.TypedEventTable.Register(eventName, new Type[] { typeof(T) }, (args) =>
  181. {
  182. T arg = default(T);
  183. try
  184. {
  185. arg = (T)args[0];
  186. }
  187. catch (Exception ex)
  188. {
  189. HTTPManager.Logger.Exception("Socket", String.Format("On<{0}>('{1}') - cast failed", typeof(T).Name, eventName), ex, this.Context);
  190. }
  191. callback(arg);
  192. });
  193. }
  194. public void On(string eventName, Action callback)
  195. {
  196. this.TypedEventTable.Register(eventName, null, _ => callback());
  197. }
  198. public void On<T>(string eventName, Action<T> callback)
  199. {
  200. this.TypedEventTable.Register(eventName, new Type[] { typeof(T) }, (args) => {
  201. T arg = default(T);
  202. try
  203. {
  204. arg = (T)args[0];
  205. }
  206. catch (Exception ex)
  207. {
  208. HTTPManager.Logger.Exception("Socket", String.Format("On<{0}>('{1}') - cast failed", typeof(T).Name, eventName), ex, this.Context);
  209. return;
  210. }
  211. callback(arg);
  212. });
  213. }
  214. public void On<T1, T2>(string eventName, Action<T1, T2> callback)
  215. {
  216. this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2) }, (args) => {
  217. T1 arg1 = default(T1);
  218. T2 arg2 = default(T2);
  219. try
  220. {
  221. arg1 = (T1)args[0];
  222. arg2 = (T2)args[1];
  223. }
  224. catch (Exception ex)
  225. {
  226. HTTPManager.Logger.Exception("Socket", String.Format("On<{0}, {1}>('{2}') - cast failed", typeof(T1).Name, typeof(T2).Name, eventName), ex, this.Context);
  227. return;
  228. }
  229. callback(arg1, arg2);
  230. });
  231. }
  232. public void On<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
  233. {
  234. this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3) }, (args) => {
  235. T1 arg1 = default(T1);
  236. T2 arg2 = default(T2);
  237. T3 arg3 = default(T3);
  238. try
  239. {
  240. arg1 = (T1)args[0];
  241. arg2 = (T2)args[1];
  242. arg3 = (T3)args[2];
  243. }
  244. catch (Exception ex)
  245. {
  246. 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);
  247. return;
  248. }
  249. callback(arg1, arg2, arg3);
  250. });
  251. }
  252. public void On<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
  253. {
  254. this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4) }, (args) => {
  255. T1 arg1 = default(T1);
  256. T2 arg2 = default(T2);
  257. T3 arg3 = default(T3);
  258. T4 arg4 = default(T4);
  259. try
  260. {
  261. arg1 = (T1)args[0];
  262. arg2 = (T2)args[1];
  263. arg3 = (T3)args[2];
  264. arg4 = (T4)args[3];
  265. }
  266. catch (Exception ex)
  267. {
  268. 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);
  269. return;
  270. }
  271. callback(arg1, arg2, arg3, arg4);
  272. });
  273. }
  274. public void On<T1, T2, T3, T4, T5>(string eventName, Action<T1, T2, T3, T4, T5> callback)
  275. {
  276. this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, (args) => {
  277. T1 arg1 = default(T1);
  278. T2 arg2 = default(T2);
  279. T3 arg3 = default(T3);
  280. T4 arg4 = default(T4);
  281. T5 arg5 = default(T5);
  282. try
  283. {
  284. arg1 = (T1)args[0];
  285. arg2 = (T2)args[1];
  286. arg3 = (T3)args[2];
  287. arg4 = (T4)args[3];
  288. arg5 = (T5)args[4];
  289. }
  290. catch (Exception ex)
  291. {
  292. 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);
  293. return;
  294. }
  295. callback(arg1, arg2, arg3, arg4, arg5);
  296. });
  297. }
  298. #endregion
  299. #region Once Implementations
  300. public void Once(string eventName, Action callback)
  301. {
  302. this.TypedEventTable.Register(eventName, null, _ => callback(), true);
  303. }
  304. public void Once<T>(string eventName, Action<T> callback)
  305. {
  306. this.TypedEventTable.Register(eventName, new Type[] { typeof(T) }, (args) => callback((T)args[0]), true);
  307. }
  308. public void Once<T1, T2>(string eventName, Action<T1, T2> callback)
  309. {
  310. this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2) }, (args) => callback((T1)args[0], (T2)args[1]), true);
  311. }
  312. public void Once<T1, T2, T3>(string eventName, Action<T1, T2, T3> callback)
  313. {
  314. this.TypedEventTable.Register(eventName, new Type[] { typeof(T1), typeof(T2), typeof(T3) }, (args) => callback((T1)args[0], (T2)args[1], (T3)args[2]), true);
  315. }
  316. public void Once<T1, T2, T3, T4>(string eventName, Action<T1, T2, T3, T4> callback)
  317. {
  318. 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);
  319. }
  320. public void Once<T1, T2, T3, T4, T5>(string eventName, Action<T1, T2, T3, T4, T5> callback)
  321. {
  322. 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);
  323. }
  324. #endregion
  325. #region Off Implementations
  326. /// <summary>
  327. /// Remove all callbacks for all events.
  328. /// </summary>
  329. public void Off()
  330. {
  331. this.TypedEventTable.Clear();
  332. }
  333. /// <summary>
  334. /// Removes all callbacks to the given event.
  335. /// </summary>
  336. public void Off(string eventName)
  337. {
  338. this.TypedEventTable.Unregister(eventName);
  339. }
  340. /// <summary>
  341. /// Removes all callbacks to the given event.
  342. /// </summary>
  343. public void Off(SocketIOEventTypes type)
  344. {
  345. Off(EventNames.GetNameFor(type));
  346. }
  347. #endregion
  348. #region Packet Handling
  349. /// <summary>
  350. /// Last call of the OnPacket chain(Transport -> Manager -> Socket), we will dispatch the event if there is any callback
  351. /// </summary>
  352. void ISocket.OnPacket(IncomingPacket packet)
  353. {
  354. // Some preprocessing of the packet
  355. switch(packet.SocketIOEvent)
  356. {
  357. case SocketIOEventTypes.Connect:
  358. break;
  359. case SocketIOEventTypes.Disconnect:
  360. if (IsOpen)
  361. {
  362. IsOpen = false;
  363. this.TypedEventTable.Call(packet);
  364. Disconnect();
  365. }
  366. break;
  367. }
  368. try
  369. {
  370. this.currentPacket = packet;
  371. // Dispatch the event to all subscriber
  372. this.TypedEventTable.Call(packet);
  373. }
  374. finally
  375. {
  376. this.currentPacket = IncomingPacket.Empty;
  377. }
  378. }
  379. #endregion
  380. public Subscription GetSubscription(string name)
  381. {
  382. return this.TypedEventTable.GetSubscription(name);
  383. }
  384. /// <summary>
  385. /// Emits an internal packet-less event to the user level.
  386. /// </summary>
  387. void ISocket.EmitEvent(SocketIOEventTypes type, params object[] args)
  388. {
  389. (this as ISocket).EmitEvent(EventNames.GetNameFor(type), args);
  390. }
  391. /// <summary>
  392. /// Emits an internal packet-less event to the user level.
  393. /// </summary>
  394. void ISocket.EmitEvent(string eventName, params object[] args)
  395. {
  396. if (!string.IsNullOrEmpty(eventName))
  397. this.TypedEventTable.Call(eventName, args);
  398. }
  399. void ISocket.EmitError(string msg)
  400. {
  401. var outcoming = this.Manager.Parser.CreateOutgoing(this, SocketIOEventTypes.Error, -1, null, new Error(msg));
  402. IncomingPacket packet = IncomingPacket.Empty;
  403. if (outcoming.IsBinary)
  404. packet = this.Manager.Parser.Parse(this.Manager, outcoming.PayloadData);
  405. else
  406. packet = this.Manager.Parser.Parse(this.Manager, outcoming.Payload);
  407. (this as ISocket).EmitEvent(SocketIOEventTypes.Error, packet.DecodedArg ?? packet.DecodedArgs);
  408. }
  409. #region Private Helper Functions
  410. /// <summary>
  411. /// Called when the underlying transport is connected
  412. /// </summary>
  413. internal void OnTransportOpen()
  414. {
  415. HTTPManager.Logger.Information("Socket", "OnTransportOpen - IsOpen: " + this.IsOpen, this.Context);
  416. if (this.IsOpen)
  417. return;
  418. object authData = null;
  419. try
  420. {
  421. authData = this.Manager.Options.Auth != null ? this.Manager.Options.Auth(this.Manager, this) : null;
  422. }
  423. catch (Exception ex)
  424. {
  425. HTTPManager.Logger.Exception("Socket", "OnTransportOpen - Options.Auth", ex, this.Context);
  426. }
  427. try
  428. {
  429. (Manager as IManager).SendPacket(this.Manager.Parser.CreateOutgoing(this, SocketIOEventTypes.Connect, -1, null, authData));
  430. }
  431. catch (Exception ex)
  432. {
  433. HTTPManager.Logger.Exception("Socket", "OnTransportOpen", ex, this.Context);
  434. }
  435. }
  436. #endregion
  437. }
  438. }
  439. #endif