PollingTransport.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449
  1. #if !BESTHTTP_DISABLE_SOCKETIO
  2. using System;
  3. using System.Text;
  4. namespace BestHTTP.SocketIO.Transports
  5. {
  6. internal sealed class PollingTransport : ITransport
  7. {
  8. #region Public (ITransport) Properties
  9. public TransportTypes Type { get { return TransportTypes.Polling; } }
  10. public TransportStates State { get; private set; }
  11. public SocketManager Manager { get; private set; }
  12. public bool IsRequestInProgress { get { return LastRequest != null; } }
  13. public bool IsPollingInProgress { get { return PollRequest != null; } }
  14. #endregion
  15. #region Private Fields
  16. /// <summary>
  17. /// The last POST request we sent to the server.
  18. /// </summary>
  19. private HTTPRequest LastRequest;
  20. /// <summary>
  21. /// Last GET request we sent to the server.
  22. /// </summary>
  23. private HTTPRequest PollRequest;
  24. /// <summary>
  25. /// The last packet with expected binary attachments
  26. /// </summary>
  27. private Packet PacketWithAttachment;
  28. #endregion
  29. private enum PayloadTypes : byte
  30. {
  31. Text,
  32. Binary
  33. }
  34. public PollingTransport(SocketManager manager)
  35. {
  36. Manager = manager;
  37. }
  38. public void Open()
  39. {
  40. string format = "{0}?EIO={1}&transport=polling&t={2}-{3}{5}";
  41. if (Manager.Handshake != null)
  42. format += "&sid={4}";
  43. bool sendAdditionalQueryParams = !Manager.Options.QueryParamsOnlyForHandshake || (Manager.Options.QueryParamsOnlyForHandshake && Manager.Handshake == null);
  44. HTTPRequest request = new HTTPRequest(new Uri(string.Format(format,
  45. Manager.Uri.ToString(),
  46. SocketManager.MinProtocolVersion,
  47. Manager.Timestamp.ToString(),
  48. Manager.RequestCounter++.ToString(),
  49. Manager.Handshake != null ? Manager.Handshake.Sid : string.Empty,
  50. sendAdditionalQueryParams ? Manager.Options.BuildQueryParams() : string.Empty)),
  51. OnRequestFinished);
  52. #if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
  53. // Don't even try to cache it
  54. request.DisableCache = true;
  55. #endif
  56. request.DisableRetry = true;
  57. request.Send();
  58. State = TransportStates.Opening;
  59. }
  60. /// <summary>
  61. /// Closes the transport and cleans up resources.
  62. /// </summary>
  63. public void Close()
  64. {
  65. if (State == TransportStates.Closed)
  66. return;
  67. State = TransportStates.Closed;
  68. /*
  69. if (LastRequest != null)
  70. LastRequest.Abort();
  71. if (PollRequest != null)
  72. PollRequest.Abort();*/
  73. }
  74. #region Packet Sending Implementation
  75. private System.Collections.Generic.List<Packet> lonelyPacketList = new System.Collections.Generic.List<Packet>(1);
  76. public void Send(Packet packet)
  77. {
  78. try
  79. {
  80. lonelyPacketList.Add(packet);
  81. Send(lonelyPacketList);
  82. }
  83. finally
  84. {
  85. lonelyPacketList.Clear();
  86. }
  87. }
  88. public void Send(System.Collections.Generic.List<Packet> packets)
  89. {
  90. if (State != TransportStates.Opening && State != TransportStates.Open)
  91. return;
  92. if (IsRequestInProgress)
  93. throw new Exception("Sending packets are still in progress!");
  94. byte[] buffer = null;
  95. try
  96. {
  97. buffer = packets[0].EncodeBinary();
  98. for (int i = 1; i < packets.Count; ++i)
  99. {
  100. byte[] tmpBuffer = packets[i].EncodeBinary();
  101. Array.Resize(ref buffer, buffer.Length + tmpBuffer.Length);
  102. Array.Copy(tmpBuffer, 0, buffer, buffer.Length - tmpBuffer.Length, tmpBuffer.Length);
  103. }
  104. packets.Clear();
  105. }
  106. catch (Exception ex)
  107. {
  108. (Manager as IManager).EmitError(SocketIOErrors.Internal, ex.Message + " " + ex.StackTrace);
  109. return;
  110. }
  111. LastRequest = new HTTPRequest(new Uri(string.Format("{0}?EIO={1}&transport=polling&t={2}-{3}&sid={4}{5}",
  112. Manager.Uri.ToString(),
  113. SocketManager.MinProtocolVersion,
  114. Manager.Timestamp.ToString(),
  115. Manager.RequestCounter++.ToString(),
  116. Manager.Handshake.Sid,
  117. !Manager.Options.QueryParamsOnlyForHandshake ? Manager.Options.BuildQueryParams() : string.Empty)),
  118. HTTPMethods.Post,
  119. OnRequestFinished);
  120. #if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
  121. // Don't even try to cache it
  122. LastRequest.DisableCache = true;
  123. #endif
  124. LastRequest.SetHeader("Content-Type", "application/octet-stream");
  125. LastRequest.RawData = buffer;
  126. LastRequest.Send();
  127. }
  128. private void OnRequestFinished(HTTPRequest req, HTTPResponse resp)
  129. {
  130. // Clear out the LastRequest variable, so we can start sending out new packets
  131. LastRequest = null;
  132. if (State == TransportStates.Closed)
  133. return;
  134. string errorString = null;
  135. switch (req.State)
  136. {
  137. // The request finished without any problem.
  138. case HTTPRequestStates.Finished:
  139. if (HTTPManager.Logger.Level <= BestHTTP.Logger.Loglevels.All)
  140. HTTPManager.Logger.Verbose("PollingTransport", "OnRequestFinished: " + resp.DataAsText);
  141. if (resp.IsSuccess)
  142. {
  143. // When we are sending data, the response is an 'ok' string
  144. if (req.MethodType != HTTPMethods.Post)
  145. ParseResponse(resp);
  146. }
  147. else
  148. errorString = string.Format("Polling - Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2} Uri: {3}",
  149. resp.StatusCode,
  150. resp.Message,
  151. resp.DataAsText,
  152. req.CurrentUri);
  153. break;
  154. // The request finished with an unexpected error. The request's Exception property may contain more info about the error.
  155. case HTTPRequestStates.Error:
  156. errorString = (req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception");
  157. break;
  158. // The request aborted, initiated by the user.
  159. case HTTPRequestStates.Aborted:
  160. errorString = string.Format("Polling - Request({0}) Aborted!", req.CurrentUri);
  161. break;
  162. // Connecting to the server is timed out.
  163. case HTTPRequestStates.ConnectionTimedOut:
  164. errorString = string.Format("Polling - Connection Timed Out! Uri: {0}", req.CurrentUri);
  165. break;
  166. // The request didn't finished in the given time.
  167. case HTTPRequestStates.TimedOut:
  168. errorString = string.Format("Polling - Processing the request({0}) Timed Out!", req.CurrentUri);
  169. break;
  170. }
  171. if (!string.IsNullOrEmpty(errorString))
  172. (Manager as IManager).OnTransportError(this, errorString);
  173. }
  174. #endregion
  175. #region Polling Implementation
  176. public void Poll()
  177. {
  178. if (PollRequest != null || State == TransportStates.Paused)
  179. return;
  180. PollRequest = new HTTPRequest(new Uri(string.Format("{0}?EIO={1}&transport=polling&t={2}-{3}&sid={4}{5}",
  181. Manager.Uri.ToString(),
  182. SocketManager.MinProtocolVersion,
  183. Manager.Timestamp.ToString(),
  184. Manager.RequestCounter++.ToString(),
  185. Manager.Handshake.Sid,
  186. !Manager.Options.QueryParamsOnlyForHandshake ? Manager.Options.BuildQueryParams() : string.Empty)),
  187. HTTPMethods.Get,
  188. OnPollRequestFinished);
  189. #if !BESTHTTP_DISABLE_CACHING && (!UNITY_WEBGL || UNITY_EDITOR)
  190. // Don't even try to cache it
  191. PollRequest.DisableCache = true;
  192. #endif
  193. PollRequest.DisableRetry = true;
  194. PollRequest.Send();
  195. }
  196. private void OnPollRequestFinished(HTTPRequest req, HTTPResponse resp)
  197. {
  198. // Clear the PollRequest variable, so we can start a new poll.
  199. PollRequest = null;
  200. if (State == TransportStates.Closed)
  201. return;
  202. string errorString = null;
  203. switch (req.State)
  204. {
  205. // The request finished without any problem.
  206. case HTTPRequestStates.Finished:
  207. if (HTTPManager.Logger.Level <= BestHTTP.Logger.Loglevels.All)
  208. HTTPManager.Logger.Verbose("PollingTransport", "OnPollRequestFinished: " + resp.DataAsText);
  209. if (resp.IsSuccess)
  210. ParseResponse(resp);
  211. else
  212. errorString = string.Format("Polling - Request finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2} Uri: {3}",
  213. resp.StatusCode,
  214. resp.Message,
  215. resp.DataAsText,
  216. req.CurrentUri);
  217. break;
  218. // The request finished with an unexpected error. The request's Exception property may contain more info about the error.
  219. case HTTPRequestStates.Error:
  220. errorString = req.Exception != null ? (req.Exception.Message + "\n" + req.Exception.StackTrace) : "No Exception";
  221. break;
  222. // The request aborted, initiated by the user.
  223. case HTTPRequestStates.Aborted:
  224. errorString = string.Format("Polling - Request({0}) Aborted!", req.CurrentUri);
  225. break;
  226. // Connecting to the server is timed out.
  227. case HTTPRequestStates.ConnectionTimedOut:
  228. errorString = string.Format("Polling - Connection Timed Out! Uri: {0}", req.CurrentUri);
  229. break;
  230. // The request didn't finished in the given time.
  231. case HTTPRequestStates.TimedOut:
  232. errorString = string.Format("Polling - Processing the request({0}) Timed Out!", req.CurrentUri);
  233. break;
  234. }
  235. if (!string.IsNullOrEmpty(errorString))
  236. (Manager as IManager).OnTransportError(this, errorString);
  237. }
  238. #endregion
  239. #region Packet Parsing and Handling
  240. /// <summary>
  241. /// Preprocessing and sending out packets to the manager.
  242. /// </summary>
  243. private void OnPacket(Packet packet)
  244. {
  245. if (packet.AttachmentCount != 0 && !packet.HasAllAttachment)
  246. {
  247. PacketWithAttachment = packet;
  248. return;
  249. }
  250. switch (packet.TransportEvent)
  251. {
  252. case TransportEventTypes.Open:
  253. if (this.State != TransportStates.Opening)
  254. HTTPManager.Logger.Warning("PollingTransport", "Received 'Open' packet while state is '" + State.ToString() + "'");
  255. else
  256. State = TransportStates.Open;
  257. goto default;
  258. case TransportEventTypes.Message:
  259. if (packet.SocketIOEvent == SocketIOEventTypes.Connect) //2:40
  260. this.State = TransportStates.Open;
  261. goto default;
  262. default:
  263. (Manager as IManager).OnPacket(packet);
  264. break;
  265. }
  266. }
  267. /// <summary>
  268. /// Will parse the response, and send out the parsed packets.
  269. /// </summary>
  270. private void ParseResponse(HTTPResponse resp)
  271. {
  272. try
  273. {
  274. if (resp != null && resp.Data != null && resp.Data.Length >= 1)
  275. {
  276. // 1.x
  277. //00000000 00 09 07 ff 30 7b 22 73 69 64 22 3a 22 6f 69 48 0{"sid":"oiH
  278. //00000010 34 31 33 73 61 49 4e 52 53 67 37 41 4b 41 41 41 413saINRSg7AKAAA
  279. //00000020 41 22 2c 22 75 70 67 72 61 64 65 73 22 3a 5b 22 A","upgrades":["
  280. //00000030 77 65 62 73 6f 63 6b 65 74 22 5d 2c 22 70 69 6e websocket"],"pin
  281. //00000040 67 49 6e 74 65 72 76 61 6c 22 3a 32 35 30 30 30 gInterval":25000
  282. //00000050 2c 22 70 69 6e 67 54 69 6d 65 6f 75 74 22 3a 36 ,"pingTimeout":6
  283. //00000060 30 30 30 30 7d 0000}
  284. // 2.x
  285. //00000000 39 37 3a 30 7b 22 73 69 64 22 3a 22 73 36 62 5a 97:0{"sid":"s6bZ
  286. //00000010 6c 43 37 66 51 59 6b 4f 46 4f 62 35 41 41 41 41 lC7fQYkOFOb5AAAA
  287. //00000020 22 2c 22 75 70 67 72 61 64 65 73 22 3a 5b 22 77 ","upgrades":["w
  288. //00000030 65 62 73 6f 63 6b 65 74 22 5d 2c 22 70 69 6e 67 ebsocket"],"ping
  289. //00000040 49 6e 74 65 72 76 61 6c 22 3a 32 35 30 30 30 2c Interval":25000,
  290. //00000050 22 70 69 6e 67 54 69 6d 65 6f 75 74 22 3a 36 30 "pingTimeout":60
  291. //00000060 30 30 30 7d 32 3a 34 30 000}2:40
  292. int idx = 0;
  293. while (idx < resp.Data.Length)
  294. {
  295. PayloadTypes type = PayloadTypes.Text;
  296. int length = 0;
  297. if (resp.Data[idx] < '0') {
  298. type = (PayloadTypes)resp.Data[idx++];
  299. byte num = resp.Data[idx++];
  300. while (num != 0xFF) {
  301. length = (length * 10) + num;
  302. num = resp.Data[idx++];
  303. }
  304. }
  305. else {
  306. byte next = resp.Data[idx++];
  307. while (next != ':') {
  308. length = (length * 10) + (next - '0');
  309. next = resp.Data[idx++];
  310. }
  311. }
  312. Packet packet = null;
  313. switch(type)
  314. {
  315. case PayloadTypes.Text:
  316. packet = new Packet(Encoding.UTF8.GetString(resp.Data, idx, length));
  317. break;
  318. case PayloadTypes.Binary:
  319. if (PacketWithAttachment != null)
  320. {
  321. // First byte is the packet type. We can skip it, so we advance our idx and we also have
  322. // to decrease length
  323. idx++;
  324. length--;
  325. byte[] buffer = new byte[length];
  326. Array.Copy(resp.Data, idx, buffer, 0, length);
  327. PacketWithAttachment.AddAttachmentFromServer(buffer, true);
  328. if (PacketWithAttachment.HasAllAttachment)
  329. {
  330. packet = PacketWithAttachment;
  331. PacketWithAttachment = null;
  332. }
  333. }
  334. break;
  335. } // switch
  336. if (packet != null)
  337. {
  338. try
  339. {
  340. OnPacket(packet);
  341. }
  342. catch (Exception ex)
  343. {
  344. HTTPManager.Logger.Exception("PollingTransport", "ParseResponse - OnPacket", ex);
  345. (Manager as IManager).EmitError(SocketIOErrors.Internal, ex.Message + " " + ex.StackTrace);
  346. }
  347. }
  348. idx += length;
  349. }// while
  350. }
  351. }
  352. catch (Exception ex)
  353. {
  354. (Manager as IManager).EmitError(SocketIOErrors.Internal, ex.Message + " " + ex.StackTrace);
  355. HTTPManager.Logger.Exception("PollingTransport", "ParseResponse", ex);
  356. }
  357. }
  358. #endregion
  359. }
  360. }
  361. #endif