PageRenderTime 44ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/SignalR.Client/Transports/HttpBasedTransport.cs

https://github.com/kpmrafeeq/SignalR
C# | 270 lines | 210 code | 51 blank | 9 comment | 24 complexity | 843ce272b15452f8e60f9a3ccd8ae6a9 MD5 | raw file
Possible License(s): MIT
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading;
  7. using System.Threading.Tasks;
  8. using Newtonsoft.Json;
  9. using Newtonsoft.Json.Linq;
  10. using SignalR.Client.Http;
  11. namespace SignalR.Client.Transports
  12. {
  13. public abstract class HttpBasedTransport : IClientTransport
  14. {
  15. // The send query string
  16. private const string _sendQueryString = "?transport={0}&connectionId={1}{2}";
  17. // The transport name
  18. protected readonly string _transport;
  19. protected const string HttpRequestKey = "http.Request";
  20. protected readonly IHttpClient _httpClient;
  21. public HttpBasedTransport(IHttpClient httpClient, string transport)
  22. {
  23. _httpClient = httpClient;
  24. _transport = transport;
  25. }
  26. public Task<NegotiationResponse> Negotiate(IConnection connection)
  27. {
  28. return GetNegotiationResponse(_httpClient, connection);
  29. }
  30. internal static Task<NegotiationResponse> GetNegotiationResponse(IHttpClient httpClient, IConnection connection)
  31. {
  32. #if SILVERLIGHT || WINDOWS_PHONE
  33. string negotiateUrl = connection.Url + "negotiate?" + GetNoCacheUrlParam();
  34. #else
  35. string negotiateUrl = connection.Url + "negotiate";
  36. #endif
  37. return httpClient.GetAsync(negotiateUrl, connection.PrepareRequest).Then(response =>
  38. {
  39. string raw = response.ReadAsString();
  40. if (raw == null)
  41. {
  42. throw new InvalidOperationException("Server negotiation failed.");
  43. }
  44. return JsonConvert.DeserializeObject<NegotiationResponse>(raw);
  45. });
  46. }
  47. public Task Start(IConnection connection, string data)
  48. {
  49. var tcs = new TaskCompletionSource<object>();
  50. OnStart(connection, data, () => tcs.TrySetResult(null), exception => tcs.TrySetException(exception));
  51. return tcs.Task;
  52. }
  53. protected abstract void OnStart(IConnection connection, string data, Action initializeCallback, Action<Exception> errorCallback);
  54. public Task<T> Send<T>(IConnection connection, string data)
  55. {
  56. string url = connection.Url + "send";
  57. string customQueryString = GetCustomQueryString(connection);
  58. url += String.Format(_sendQueryString, _transport, connection.ConnectionId, customQueryString);
  59. var postData = new Dictionary<string, string> {
  60. { "data", data }
  61. };
  62. return _httpClient.PostAsync(url, connection.PrepareRequest, postData).Then(response =>
  63. {
  64. string raw = response.ReadAsString();
  65. if (String.IsNullOrEmpty(raw))
  66. {
  67. return default(T);
  68. }
  69. return JsonConvert.DeserializeObject<T>(raw);
  70. });
  71. }
  72. protected string GetReceiveQueryString(IConnection connection, string data)
  73. {
  74. // ?transport={0}&connectionId={1}&messageId={2}&groups={3}&connectionData={4}{5}
  75. var qsBuilder = new StringBuilder();
  76. qsBuilder.Append("?transport=" + _transport)
  77. .Append("&connectionId=" + Uri.EscapeDataString(connection.ConnectionId));
  78. if (connection.MessageId != null)
  79. {
  80. qsBuilder.Append("&messageId=" + connection.MessageId);
  81. }
  82. if (connection.Groups != null && connection.Groups.Any())
  83. {
  84. qsBuilder.Append("&groups=" + Uri.EscapeDataString(JsonConvert.SerializeObject(connection.Groups)));
  85. }
  86. if (data != null)
  87. {
  88. qsBuilder.Append("&connectionData=" + data);
  89. }
  90. string customQuery = GetCustomQueryString(connection);
  91. if (!String.IsNullOrEmpty(customQuery))
  92. {
  93. qsBuilder.Append("&")
  94. .Append(customQuery);
  95. }
  96. #if SILVERLIGHT || WINDOWS_PHONE
  97. qsBuilder.Append("&").Append(GetNoCacheUrlParam());
  98. #endif
  99. return qsBuilder.ToString();
  100. }
  101. private static string GetNoCacheUrlParam()
  102. {
  103. return "noCache=" + Guid.NewGuid().ToString();
  104. }
  105. protected virtual Action<IRequest> PrepareRequest(IConnection connection)
  106. {
  107. return request =>
  108. {
  109. // Setup the user agent along with any other defaults
  110. connection.PrepareRequest(request);
  111. connection.Items[HttpRequestKey] = request;
  112. };
  113. }
  114. public void Stop(IConnection connection)
  115. {
  116. var httpRequest = connection.GetValue<IRequest>(HttpRequestKey);
  117. if (httpRequest != null)
  118. {
  119. try
  120. {
  121. OnBeforeAbort(connection);
  122. // Abort the server side connection
  123. AbortConnection(connection);
  124. // Now abort the client connection
  125. httpRequest.Abort();
  126. }
  127. catch (NotImplementedException)
  128. {
  129. // If this isn't implemented then do nothing
  130. }
  131. }
  132. }
  133. private void AbortConnection(IConnection connection)
  134. {
  135. string url = connection.Url + "abort" + String.Format(_sendQueryString, _transport, connection.ConnectionId, null);
  136. try
  137. {
  138. // Attempt to perform a clean disconnect, but only wait 2 seconds
  139. _httpClient.PostAsync(url, connection.PrepareRequest).Wait(TimeSpan.FromSeconds(2));
  140. }
  141. catch (Exception ex)
  142. {
  143. // Swallow any exceptions, but log them
  144. Debug.WriteLine("Clean disconnect failed. " + ex.Unwrap().Message);
  145. }
  146. }
  147. protected virtual void OnBeforeAbort(IConnection connection)
  148. {
  149. }
  150. protected static void ProcessResponse(IConnection connection, string response, out bool timedOut, out bool disconnected)
  151. {
  152. timedOut = false;
  153. disconnected = false;
  154. if (String.IsNullOrEmpty(response))
  155. {
  156. return;
  157. }
  158. try
  159. {
  160. var result = JValue.Parse(response);
  161. if (!result.HasValues)
  162. {
  163. return;
  164. }
  165. timedOut = result.Value<bool>("TimedOut");
  166. disconnected = result.Value<bool>("Disconnect");
  167. if (disconnected)
  168. {
  169. return;
  170. }
  171. var messages = result["Messages"] as JArray;
  172. if (messages != null)
  173. {
  174. foreach (JToken message in messages)
  175. {
  176. try
  177. {
  178. connection.OnReceived(message);
  179. }
  180. catch (Exception ex)
  181. {
  182. #if NET35
  183. Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Failed to process message: {0}", ex));
  184. #else
  185. Debug.WriteLine("Failed to process message: {0}", ex);
  186. #endif
  187. connection.OnError(ex);
  188. }
  189. }
  190. connection.MessageId = result["MessageId"].Value<string>();
  191. var transportData = result["TransportData"] as JObject;
  192. if (transportData != null)
  193. {
  194. var groups = (JArray)transportData["Groups"];
  195. if (groups != null)
  196. {
  197. connection.Groups = groups.Select(token => token.Value<string>());
  198. }
  199. }
  200. }
  201. }
  202. catch (Exception ex)
  203. {
  204. #if NET35
  205. Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Failed to response: {0}", ex));
  206. #else
  207. Debug.WriteLine("Failed to response: {0}", ex);
  208. #endif
  209. connection.OnError(ex);
  210. }
  211. }
  212. private static string GetCustomQueryString(IConnection connection)
  213. {
  214. return String.IsNullOrEmpty(connection.QueryString)
  215. ? ""
  216. : "&" + connection.QueryString;
  217. }
  218. }
  219. }