PageRenderTime 36ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/SignalR/Transports/LongPollingTransport.cs

https://github.com/kpmrafeeq/SignalR
C# | 256 lines | 202 code | 45 blank | 9 comment | 27 complexity | 2ca47bcc64e98a638d51c6f9bc0358ad MD5 | raw file
Possible License(s): MIT
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using SignalR.Infrastructure;
  6. namespace SignalR.Transports
  7. {
  8. public class LongPollingTransport : TransportDisconnectBase, ITransport
  9. {
  10. private IJsonSerializer _jsonSerializer;
  11. public LongPollingTransport(HostContext context, IDependencyResolver resolver)
  12. : this(context,
  13. resolver.Resolve<IJsonSerializer>(),
  14. resolver.Resolve<ITransportHeartBeat>())
  15. {
  16. }
  17. public LongPollingTransport(HostContext context, IJsonSerializer jsonSerializer, ITransportHeartBeat heartBeat)
  18. : base(context, jsonSerializer, heartBeat)
  19. {
  20. _jsonSerializer = jsonSerializer;
  21. }
  22. // Static events intended for use when measuring performance
  23. public static event Action<string> Sending;
  24. public static event Action<string> Receiving;
  25. /// <summary>
  26. /// The number of milliseconds to tell the browser to wait before restablishing a
  27. /// long poll connection after data is sent from the server. Defaults to 0.
  28. /// </summary>
  29. public static long LongPollDelay
  30. {
  31. get;
  32. set;
  33. }
  34. public override TimeSpan DisconnectThreshold
  35. {
  36. get { return TimeSpan.FromMilliseconds(LongPollDelay); }
  37. }
  38. protected override bool IsConnectRequest
  39. {
  40. get
  41. {
  42. return Context.Request.Url.LocalPath.EndsWith("/connect", StringComparison.OrdinalIgnoreCase);
  43. }
  44. }
  45. private bool IsReconnectRequest
  46. {
  47. get
  48. {
  49. return Context.Request.Url.LocalPath.EndsWith("/reconnect", StringComparison.OrdinalIgnoreCase);
  50. }
  51. }
  52. private bool IsJsonp
  53. {
  54. get
  55. {
  56. return !String.IsNullOrEmpty(JsonpCallback);
  57. }
  58. }
  59. private bool IsSendRequest
  60. {
  61. get
  62. {
  63. return Context.Request.Url.LocalPath.EndsWith("/send", StringComparison.OrdinalIgnoreCase);
  64. }
  65. }
  66. private string MessageId
  67. {
  68. get
  69. {
  70. return Context.Request.QueryString["messageId"];
  71. }
  72. }
  73. private string JsonpCallback
  74. {
  75. get
  76. {
  77. return Context.Request.QueryString["callback"];
  78. }
  79. }
  80. public override bool SupportsKeepAlive
  81. {
  82. get
  83. {
  84. return false;
  85. }
  86. }
  87. public Func<string, Task> Received { get; set; }
  88. public Func<Task> TransportConnected { get; set; }
  89. public Func<Task> Connected { get; set; }
  90. public Func<Task> Reconnected { get; set; }
  91. public Func<Exception, Task> Error { get; set; }
  92. public Task ProcessRequest(ITransportConnection connection)
  93. {
  94. Connection = connection;
  95. if (IsSendRequest)
  96. {
  97. return ProcessSendRequest();
  98. }
  99. else if (IsAbortRequest)
  100. {
  101. return Connection.Abort();
  102. }
  103. else
  104. {
  105. if (IsConnectRequest)
  106. {
  107. return ProcessConnectRequest(connection);
  108. }
  109. else if (MessageId != null)
  110. {
  111. if (IsReconnectRequest && Reconnected != null)
  112. {
  113. // Return a task that completes when the reconnected event task & the receive loop task are both finished
  114. return TaskAsyncHelper.Interleave(ProcessReceiveRequest, Reconnected, connection, Completed);
  115. }
  116. return ProcessReceiveRequest(connection);
  117. }
  118. }
  119. return null;
  120. }
  121. public virtual Task Send(PersistentResponse response)
  122. {
  123. HeartBeat.MarkConnection(this);
  124. AddTransportData(response);
  125. return Send((object)response);
  126. }
  127. public virtual Task Send(object value)
  128. {
  129. var payload = _jsonSerializer.Stringify(value);
  130. if (IsJsonp)
  131. {
  132. payload = Json.CreateJsonpCallback(JsonpCallback, payload);
  133. }
  134. if (Sending != null)
  135. {
  136. Sending(payload);
  137. }
  138. Context.Response.ContentType = IsJsonp ? Json.JsonpMimeType : Json.MimeType;
  139. return Context.Response.EndAsync(payload);
  140. }
  141. private Task ProcessSendRequest()
  142. {
  143. string data = IsJsonp ? Context.Request.QueryString["data"] : Context.Request.Form["data"];
  144. if (Receiving != null)
  145. {
  146. Receiving(data);
  147. }
  148. if (Received != null)
  149. {
  150. return Received(data);
  151. }
  152. return TaskAsyncHelper.Empty;
  153. }
  154. private Task ProcessConnectRequest(ITransportConnection connection)
  155. {
  156. if (Connected != null)
  157. {
  158. bool newConnection = HeartBeat.AddConnection(this);
  159. // Return a task that completes when the connected event task & the receive loop task are both finished
  160. return TaskAsyncHelper.Interleave(ProcessReceiveRequestWithoutTracking, () =>
  161. {
  162. if (newConnection)
  163. {
  164. return Connected();
  165. }
  166. return TaskAsyncHelper.Empty;
  167. },
  168. connection, Completed);
  169. }
  170. return ProcessReceiveRequest(connection);
  171. }
  172. private Task ProcessReceiveRequest(ITransportConnection connection, Action postReceive = null)
  173. {
  174. HeartBeat.AddConnection(this);
  175. return ProcessReceiveRequestWithoutTracking(connection, postReceive);
  176. }
  177. private Task ProcessReceiveRequestWithoutTracking(ITransportConnection connection, Action postReceive = null)
  178. {
  179. if (TransportConnected != null)
  180. {
  181. TransportConnected().Catch();
  182. }
  183. // ReceiveAsync() will async wait until a message arrives then return
  184. var receiveTask = IsConnectRequest ?
  185. connection.ReceiveAsync(ConnectionEndToken) :
  186. connection.ReceiveAsync(MessageId, ConnectionEndToken);
  187. if (postReceive != null)
  188. {
  189. postReceive();
  190. }
  191. return receiveTask.Then(response =>
  192. {
  193. response.TimedOut = IsTimedOut;
  194. if (response.Aborted)
  195. {
  196. // If this was a clean disconnect then raise the event
  197. OnDisconnect();
  198. }
  199. return Send(response);
  200. });
  201. }
  202. private PersistentResponse AddTransportData(PersistentResponse response)
  203. {
  204. if (response != null)
  205. {
  206. response.TransportData["LongPollDelay"] = LongPollDelay;
  207. }
  208. return response;
  209. }
  210. }
  211. }