PageRenderTime 35ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Microsoft.AspNet.SignalR.Client/Transports/HttpBasedTransport.cs

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