NegotiationData.cs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #if !BESTHTTP_DISABLE_SIGNALR
  2. using System;
  3. using System.Collections.Generic;
  4. using BestHTTP.JSON;
  5. namespace BestHTTP.SignalR
  6. {
  7. public sealed class NegotiationData
  8. {
  9. #region Public Negotiate data
  10. /// <summary>
  11. /// Path to the SignalR endpoint. Currently not used by the client.
  12. /// </summary>
  13. public string Url { get; private set; }
  14. /// <summary>
  15. /// WebSocket endpoint.
  16. /// </summary>
  17. public string WebSocketServerUrl { get; private set; }
  18. /// <summary>
  19. /// Connection token assigned by the server. See this article for more details: http://www.asp.net/signalr/overview/security/introduction-to-security#connectiontoken.
  20. /// This value needs to be sent in each subsequent request as the value of the connectionToken parameter
  21. /// </summary>
  22. public string ConnectionToken { get; private set; }
  23. /// <summary>
  24. /// The id of the connection.
  25. /// </summary>
  26. public string ConnectionId { get; private set; }
  27. /// <summary>
  28. /// The amount of time in seconds the client should wait before attempting to reconnect if it has not received a keep alive message.
  29. /// If the server is configured to not send keep alive messages this value is null.
  30. /// </summary>
  31. public TimeSpan? KeepAliveTimeout { get; private set; }
  32. /// <summary>
  33. /// The amount of time within which the client should try to reconnect if the connection goes away.
  34. /// </summary>
  35. public TimeSpan DisconnectTimeout { get; private set; }
  36. /// <summary>
  37. /// Timeout of poll requests.
  38. /// </summary>
  39. public TimeSpan ConnectionTimeout { get; private set; }
  40. /// <summary>
  41. /// Whether the server supports websockets.
  42. /// </summary>
  43. public bool TryWebSockets { get; private set; }
  44. /// <summary>
  45. /// The version of the protocol used for communication.
  46. /// </summary>
  47. public string ProtocolVersion { get; private set; }
  48. /// <summary>
  49. /// The maximum amount of time the client should try to connect to the server using a given transport.
  50. /// </summary>
  51. public TimeSpan TransportConnectTimeout { get; private set; }
  52. /// <summary>
  53. /// The wait time before restablishing a long poll connection after data is sent from the server. The default value is 0.
  54. /// </summary>
  55. public TimeSpan LongPollDelay { get; private set; }
  56. #endregion
  57. #region Event Handlers
  58. /// <summary>
  59. /// Event handler that called when the negotiation data received and parsed successfully.
  60. /// </summary>
  61. public Action<NegotiationData> OnReceived;
  62. /// <summary>
  63. /// Event handler that called when an error happens.
  64. /// </summary>
  65. public Action<NegotiationData, string> OnError;
  66. #endregion
  67. #region Private
  68. private HTTPRequest NegotiationRequest;
  69. private IConnection Connection;
  70. #endregion
  71. public NegotiationData(Connection connection)
  72. {
  73. this.Connection = connection;
  74. }
  75. /// <summary>
  76. /// Start to get the negotiation data.
  77. /// </summary>
  78. public void Start()
  79. {
  80. NegotiationRequest = new HTTPRequest(Connection.BuildUri(RequestTypes.Negotiate), HTTPMethods.Get, true, true, OnNegotiationRequestFinished);
  81. Connection.PrepareRequest(NegotiationRequest, RequestTypes.Negotiate);
  82. NegotiationRequest.Send();
  83. HTTPManager.Logger.Information("NegotiationData", "Negotiation request sent");
  84. }
  85. /// <summary>
  86. /// Abort the negotiation request.
  87. /// </summary>
  88. public void Abort()
  89. {
  90. if (NegotiationRequest != null)
  91. {
  92. OnReceived = null;
  93. OnError = null;
  94. NegotiationRequest.Abort();
  95. }
  96. }
  97. #region Request Event Handler
  98. private void OnNegotiationRequestFinished(HTTPRequest req, HTTPResponse resp)
  99. {
  100. NegotiationRequest = null;
  101. switch (req.State)
  102. {
  103. case HTTPRequestStates.Finished:
  104. if (resp.IsSuccess)
  105. {
  106. HTTPManager.Logger.Information("NegotiationData", "Negotiation data arrived: " + resp.DataAsText);
  107. int idx = resp.DataAsText.IndexOf("{");
  108. if (idx < 0)
  109. {
  110. RaiseOnError("Invalid negotiation text: " + resp.DataAsText);
  111. return;
  112. }
  113. var Negotiation = Parse(resp.DataAsText.Substring(idx));
  114. if (Negotiation == null)
  115. {
  116. RaiseOnError("Parsing Negotiation data failed: " + resp.DataAsText);
  117. return;
  118. }
  119. if (OnReceived != null)
  120. {
  121. OnReceived(this);
  122. OnReceived = null;
  123. }
  124. }
  125. else
  126. RaiseOnError(string.Format("Negotiation request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2} Uri: {3}",
  127. resp.StatusCode,
  128. resp.Message,
  129. resp.DataAsText,
  130. req.CurrentUri));
  131. break;
  132. case HTTPRequestStates.Error:
  133. RaiseOnError(req.Exception != null ? (req.Exception.Message + " " + req.Exception.StackTrace) : string.Empty);
  134. break;
  135. default:
  136. RaiseOnError(req.State.ToString());
  137. break;
  138. }
  139. }
  140. #endregion
  141. #region Helper Methods
  142. private void RaiseOnError(string err)
  143. {
  144. HTTPManager.Logger.Error("NegotiationData", "Negotiation request failed with error: " + err);
  145. if (OnError != null)
  146. {
  147. OnError(this, err);
  148. OnError = null;
  149. }
  150. }
  151. private NegotiationData Parse(string str)
  152. {
  153. bool success = false;
  154. Dictionary<string, object> dict = Json.Decode(str, ref success) as Dictionary<string, object>;
  155. if (!success)
  156. return null;
  157. try
  158. {
  159. this.Url = GetString(dict, "Url");
  160. if (dict.ContainsKey("webSocketServerUrl"))
  161. this.WebSocketServerUrl = GetString(dict, "webSocketServerUrl");
  162. this.ConnectionToken = Uri.EscapeDataString(GetString(dict, "ConnectionToken"));
  163. this.ConnectionId = GetString(dict, "ConnectionId");
  164. if (dict.ContainsKey("KeepAliveTimeout"))
  165. this.KeepAliveTimeout = TimeSpan.FromSeconds(GetDouble(dict, "KeepAliveTimeout"));
  166. this.DisconnectTimeout = TimeSpan.FromSeconds(GetDouble(dict, "DisconnectTimeout"));
  167. if (dict.ContainsKey("ConnectionTimeout"))
  168. this.ConnectionTimeout = TimeSpan.FromSeconds(GetDouble(dict, "ConnectionTimeout"));
  169. else
  170. this.ConnectionTimeout = TimeSpan.FromSeconds(120);
  171. this.TryWebSockets = (bool)Get(dict, "TryWebSockets");
  172. this.ProtocolVersion = GetString(dict, "ProtocolVersion");
  173. this.TransportConnectTimeout = TimeSpan.FromSeconds(GetDouble(dict, "TransportConnectTimeout"));
  174. if (dict.ContainsKey("LongPollDelay"))
  175. this.LongPollDelay = TimeSpan.FromSeconds(GetDouble(dict, "LongPollDelay"));
  176. }
  177. catch(Exception ex)
  178. {
  179. HTTPManager.Logger.Exception("NegotiationData", "Parse", ex);
  180. return null;
  181. }
  182. return this;
  183. }
  184. private static object Get(Dictionary<string, object> from, string key)
  185. {
  186. object value;
  187. if (!from.TryGetValue(key, out value))
  188. throw new System.Exception(string.Format("Can't get {0} from Negotiation data!", key));
  189. return value;
  190. }
  191. private static string GetString(Dictionary<string, object> from, string key)
  192. {
  193. return Get(from, key) as string;
  194. }
  195. private static List<string> GetStringList(Dictionary<string, object> from, string key)
  196. {
  197. List<object> value = Get(from, key) as List<object>;
  198. List<string> result = new List<string>(value.Count);
  199. for (int i = 0; i < value.Count; ++i)
  200. {
  201. string str = value[i] as string;
  202. if (str != null)
  203. result.Add(str);
  204. }
  205. return result;
  206. }
  207. private static int GetInt(Dictionary<string, object> from, string key)
  208. {
  209. return (int)(double)Get(from, key);
  210. }
  211. private static double GetDouble(Dictionary<string, object> from, string key)
  212. {
  213. return (double)Get(from, key);
  214. }
  215. #endregion
  216. }
  217. }
  218. #endif