OverHTTP2.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. #if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_ALTERNATE_SSL && !BESTHTTP_DISABLE_HTTP2 && !BESTHTTP_DISABLE_WEBSOCKET
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using BestHTTP.Connections.HTTP2;
  6. using BestHTTP.Extensions;
  7. using BestHTTP.PlatformSupport.Memory;
  8. using BestHTTP.WebSocket.Frames;
  9. using BestHTTP.WebSocket.Implementations.Utils;
  10. namespace BestHTTP.WebSocket
  11. {
  12. /// <summary>
  13. /// Implements RFC 8441 (https://tools.ietf.org/html/rfc8441) to use Websocket over HTTP/2
  14. /// </summary>
  15. public sealed class OverHTTP2 : WebSocketBaseImplementation, IHeartbeat
  16. {
  17. public override int BufferedAmount { get => (int)this.upStream.Length; }
  18. public override bool IsOpen => this.State == WebSocketStates.Open;
  19. public override int Latency { get { return this.Parent.StartPingThread ? base.Latency : (int)this.http2Handler.Latency; } }
  20. private List<WebSocketFrameReader> IncompleteFrames = new List<WebSocketFrameReader>();
  21. private HTTP2Handler http2Handler;
  22. private LockedBufferSegmenStream upStream;
  23. /// <summary>
  24. /// True if we sent out a Close message to the server
  25. /// </summary>
  26. private volatile bool closeSent;
  27. /// <summary>
  28. /// When we sent out the last ping.
  29. /// </summary>
  30. private DateTime lastPing = DateTime.MinValue;
  31. private bool waitingForPong = false;
  32. /// <summary>
  33. /// A circular buffer to store the last N rtt times calculated by the pong messages.
  34. /// </summary>
  35. private CircularBuffer<int> rtts = new CircularBuffer<int>(WebSocketResponse.RTTBufferCapacity);
  36. private PeekableIncomingSegmentStream incomingSegmentStream = new PeekableIncomingSegmentStream();
  37. public OverHTTP2(WebSocket parent, HTTP2Handler handler, Uri uri, string origin, string protocol) : base(parent, uri, origin, protocol)
  38. {
  39. this.http2Handler = handler;
  40. string scheme = "https";
  41. int port = uri.Port != -1 ? uri.Port : 443;
  42. base.Uri = new Uri(scheme + "://" + uri.Host + ":" + port + uri.GetRequestPathAndQueryURL());
  43. }
  44. protected override void CreateInternalRequest()
  45. {
  46. base._internalRequest = new HTTPRequest(base.Uri, HTTPMethods.Connect, OnInternalRequestCallback);
  47. base._internalRequest.Context.Add("WebSocket", this.Parent.Context);
  48. base._internalRequest.SetHeader(":protocol", "websocket");
  49. // The request MUST include a header field with the name |Sec-WebSocket-Key|. The value of this header field MUST be a nonce consisting of a
  50. // randomly selected 16-byte value that has been base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be selected randomly for each connection.
  51. base._internalRequest.SetHeader("sec-webSocket-key", WebSocket.GetSecKey(new object[] { this, InternalRequest, base.Uri, new object() }));
  52. // The request MUST include a header field with the name |Origin| [RFC6454] if the request is coming from a browser client.
  53. // If the connection is from a non-browser client, the request MAY include this header field if the semantics of that client match the use-case described here for browser clients.
  54. // More on Origin Considerations: http://tools.ietf.org/html/rfc6455#section-10.2
  55. if (!string.IsNullOrEmpty(base.Origin))
  56. base._internalRequest.SetHeader("origin", base.Origin);
  57. // The request MUST include a header field with the name |Sec-WebSocket-Version|. The value of this header field MUST be 13.
  58. base._internalRequest.SetHeader("sec-webSocket-version", "13");
  59. if (!string.IsNullOrEmpty(base.Protocol))
  60. base._internalRequest.SetHeader("sec-webSocket-protocol", base.Protocol);
  61. // Disable caching
  62. base._internalRequest.SetHeader("cache-control", "no-cache");
  63. base._internalRequest.SetHeader("pragma", "no-cache");
  64. #if !BESTHTTP_DISABLE_CACHING
  65. base._internalRequest.DisableCache = true;
  66. #endif
  67. base._internalRequest.OnHeadersReceived += OnHeadersReceived;
  68. base._internalRequest.OnStreamingData += OnFrame;
  69. base._internalRequest.StreamChunksImmediately = true;
  70. base._internalRequest.UploadStream = this.upStream = new LockedBufferSegmenStream();
  71. base._internalRequest.UseUploadStreamLength = false;
  72. if (this.Parent.OnInternalRequestCreated != null)
  73. {
  74. try
  75. {
  76. this.Parent.OnInternalRequestCreated(this.Parent, base._internalRequest);
  77. }
  78. catch (Exception ex)
  79. {
  80. HTTPManager.Logger.Exception("OverHTTP2", "CreateInternalRequest", ex, this.Parent.Context);
  81. }
  82. }
  83. }
  84. private void OnHeadersReceived(HTTPRequest req, HTTPResponse resp, Dictionary<string, List<string>> newHeaders)
  85. {
  86. if (resp != null && resp.StatusCode == 200)
  87. {
  88. this.State = WebSocketStates.Open;
  89. if (this.Parent.OnOpen != null)
  90. {
  91. try
  92. {
  93. this.Parent.OnOpen(this.Parent);
  94. }
  95. catch (Exception ex)
  96. {
  97. HTTPManager.Logger.Exception("OverHTTP2", "OnOpen", ex, this.Parent.Context);
  98. }
  99. }
  100. if (this.Parent.StartPingThread)
  101. {
  102. this.LastMessageReceived = DateTime.Now;
  103. SendPing();
  104. }
  105. }
  106. else
  107. req.Abort();
  108. }
  109. private static bool CanReadFullFrame(PeekableIncomingSegmentStream stream)
  110. {
  111. if (stream.Length < 2)
  112. return false;
  113. stream.BeginPeek();
  114. if (stream.PeekByte() == -1)
  115. return false;
  116. int maskAndLength = stream.PeekByte();
  117. if (maskAndLength == -1)
  118. return false;
  119. // The second byte is the Mask Bit and the length of the payload data
  120. var HasMask = (maskAndLength & 0x80) != 0;
  121. // if 0-125, that is the payload length.
  122. var Length = (UInt64)(maskAndLength & 127);
  123. // If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length.
  124. if (Length == 126)
  125. {
  126. byte[] rawLen = BufferPool.Get(2, true);
  127. for (int i = 0; i < 2; i++)
  128. {
  129. int data = stream.PeekByte();
  130. if (data < 0)
  131. return false;
  132. rawLen[i] = (byte)data;
  133. }
  134. if (BitConverter.IsLittleEndian)
  135. Array.Reverse(rawLen, 0, 2);
  136. Length = (UInt64)BitConverter.ToUInt16(rawLen, 0);
  137. BufferPool.Release(rawLen);
  138. }
  139. else if (Length == 127)
  140. {
  141. // If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the
  142. // most significant bit MUST be 0) are the payload length.
  143. byte[] rawLen = BufferPool.Get(8, true);
  144. for (int i = 0; i < 8; i++)
  145. {
  146. int data = stream.PeekByte();
  147. if (data < 0)
  148. return false;
  149. rawLen[i] = (byte)data;
  150. }
  151. if (BitConverter.IsLittleEndian)
  152. Array.Reverse(rawLen, 0, 8);
  153. Length = (UInt64)BitConverter.ToUInt64(rawLen, 0);
  154. BufferPool.Release(rawLen);
  155. }
  156. // Header + Mask&Length
  157. Length += 2;
  158. // 4 bytes for Mask if present
  159. if (HasMask)
  160. Length += 4;
  161. return stream.Length >= (long)Length;
  162. }
  163. private bool OnFrame(HTTPRequest request, HTTPResponse response, byte[] dataFragment, int dataFragmentLength)
  164. {
  165. base.LastMessageReceived = DateTime.Now;
  166. this.incomingSegmentStream.Write(dataFragment, 0, dataFragmentLength);
  167. while (CanReadFullFrame(this.incomingSegmentStream))
  168. {
  169. WebSocketFrameReader frame = new WebSocketFrameReader();
  170. frame.Read(this.incomingSegmentStream);
  171. if (HTTPManager.Logger.Level == Logger.Loglevels.All)
  172. HTTPManager.Logger.Verbose("OverHTTP2", "Frame received: " + frame.Type, this.Parent.Context);
  173. if (!frame.IsFinal)
  174. {
  175. if (this.Parent.OnIncompleteFrame == null)
  176. IncompleteFrames.Add(frame);
  177. else if (this.Parent.OnIncompleteFrame != null)
  178. {
  179. try
  180. {
  181. this.Parent.OnIncompleteFrame(this.Parent, frame);
  182. }
  183. catch (Exception ex)
  184. {
  185. HTTPManager.Logger.Exception("OverHTTP2", "OnIncompleteFrame", ex, this.Parent.Context);
  186. }
  187. }
  188. return false;
  189. }
  190. switch (frame.Type)
  191. {
  192. // For a complete documentation and rules on fragmentation see http://tools.ietf.org/html/rfc6455#section-5.4
  193. // A fragmented Frame's last fragment's opcode is 0 (Continuation) and the FIN bit is set to 1.
  194. case WebSocketFrameTypes.Continuation:
  195. // Do an assemble pass only if OnFragment is not set. Otherwise put it in the CompletedFrames, we will handle it in the HandleEvent phase.
  196. if (this.Parent.OnIncompleteFrame == null)
  197. {
  198. frame.Assemble(IncompleteFrames);
  199. // Remove all incomplete frames
  200. IncompleteFrames.Clear();
  201. // Control frames themselves MUST NOT be fragmented. So, its a normal text or binary frame. Go, handle it as usual.
  202. //goto case WebSocketFrameTypes.Binary;
  203. if (frame.Type == WebSocketFrameTypes.Text)
  204. goto case WebSocketFrameTypes.Text;
  205. else if (frame.Type == WebSocketFrameTypes.Binary)
  206. goto case WebSocketFrameTypes.Binary;
  207. }
  208. else
  209. {
  210. if (this.Parent.OnIncompleteFrame != null)
  211. {
  212. try
  213. {
  214. this.Parent.OnIncompleteFrame(this.Parent, frame);
  215. }
  216. catch (Exception ex)
  217. {
  218. HTTPManager.Logger.Exception("OverHTTP2", "OnIncompleteFrame", ex, this.Parent.Context);
  219. }
  220. }
  221. }
  222. break;
  223. case WebSocketFrameTypes.Text:
  224. frame.DecodeWithExtensions(this.Parent);
  225. if (this.Parent.OnMessage != null)
  226. {
  227. try
  228. {
  229. this.Parent.OnMessage(this.Parent, frame.DataAsText);
  230. }
  231. catch (Exception ex)
  232. {
  233. HTTPManager.Logger.Exception("OverHTTP2", "OnMessage", ex, this.Parent.Context);
  234. }
  235. }
  236. break;
  237. case WebSocketFrameTypes.Binary:
  238. frame.DecodeWithExtensions(this.Parent);
  239. if (this.Parent.OnBinary != null)
  240. {
  241. try
  242. {
  243. this.Parent.OnBinary(this.Parent, frame.Data);
  244. }
  245. catch (Exception ex)
  246. {
  247. HTTPManager.Logger.Exception("OverHTTP2", "OnBinary", ex, this.Parent.Context);
  248. }
  249. }
  250. break;
  251. // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in response, unless it already received a Close frame.
  252. case WebSocketFrameTypes.Ping:
  253. if (!closeSent && !this.upStream.IsClosed)
  254. Send(new WebSocketFrame(this.Parent, WebSocketFrameTypes.Pong, frame.Data));
  255. break;
  256. case WebSocketFrameTypes.Pong:
  257. waitingForPong = false;
  258. try
  259. {
  260. // Get the ticks from the frame's payload
  261. long ticksSent = BitConverter.ToInt64(frame.Data, 0);
  262. // the difference between the current time and the time when the ping message is sent
  263. TimeSpan diff = TimeSpan.FromTicks(this.LastMessageReceived.Ticks - ticksSent);
  264. // add it to the buffer
  265. this.rtts.Add((int)diff.TotalMilliseconds);
  266. // and calculate the new latency
  267. base.Latency = CalculateLatency();
  268. }
  269. catch
  270. {
  271. // https://tools.ietf.org/html/rfc6455#section-5.5
  272. // A Pong frame MAY be sent unsolicited. This serves as a
  273. // unidirectional heartbeat. A response to an unsolicited Pong frame is
  274. // not expected.
  275. }
  276. break;
  277. // If an endpoint receives a Close frame and did not previously send a Close frame, the endpoint MUST send a Close frame in response.
  278. case WebSocketFrameTypes.ConnectionClose:
  279. HTTPManager.Logger.Information("OverHTTP2", "ConnectionClose packet received!", this.Parent.Context);
  280. //CloseFrame = frame;
  281. if (!closeSent)
  282. Send(new WebSocketFrame(this.Parent, WebSocketFrameTypes.ConnectionClose, null));
  283. this.upStream.Close();
  284. UInt16 statusCode = 0;
  285. string msg = string.Empty;
  286. try
  287. {
  288. // If we received any data, we will get the status code and the message from it
  289. if (frame.Data != null && frame.Length >= 2)
  290. {
  291. if (BitConverter.IsLittleEndian)
  292. Array.Reverse(frame.Data, 0, 2);
  293. statusCode = BitConverter.ToUInt16(frame.Data, 0);
  294. if (frame.Data.Length > 2)
  295. msg = System.Text.Encoding.UTF8.GetString(frame.Data, 2, (int)frame.Length - 2);
  296. frame.ReleaseData();
  297. }
  298. }
  299. catch (Exception ex)
  300. {
  301. HTTPManager.Logger.Exception("OverHTTP2", "OnFrame - parsing ConnectionClose data", ex, this.Parent.Context);
  302. }
  303. if (this.Parent.OnClosed != null)
  304. {
  305. try
  306. {
  307. this.Parent.OnClosed(this.Parent, statusCode, msg);
  308. }
  309. catch (Exception ex)
  310. {
  311. HTTPManager.Logger.Exception("OverHTTP2", "OnClosed", ex, this.Parent.Context);
  312. }
  313. this.Parent.OnClosed = null;
  314. }
  315. this.State = WebSocketStates.Closed;
  316. break;
  317. }
  318. }
  319. return false;
  320. }
  321. private void OnInternalRequestCallback(HTTPRequest req, HTTPResponse resp)
  322. {
  323. // If it's already closed, all events are called too.
  324. if (this.State == WebSocketStates.Closed)
  325. return;
  326. if (this.State == WebSocketStates.Connecting && HTTPManager.HTTP2Settings.WebSocketOverHTTP2Settings.EnableImplementationFallback)
  327. {
  328. this.Parent.FallbackToHTTP1();
  329. return;
  330. }
  331. string reason = string.Empty;
  332. switch (req.State)
  333. {
  334. case HTTPRequestStates.Finished:
  335. HTTPManager.Logger.Information("OverHTTP2", string.Format("Request finished. Status Code: {0} Message: {1}", resp.StatusCode.ToString(), resp.Message), this.Parent.Context);
  336. if (resp.StatusCode == 101)
  337. {
  338. // The request upgraded successfully.
  339. return;
  340. }
  341. else
  342. reason = string.Format("Request Finished Successfully, but the server sent an error. Status Code: {0}-{1} Message: {2}",
  343. resp.StatusCode,
  344. resp.Message,
  345. resp.DataAsText);
  346. break;
  347. // The request finished with an unexpected error. The request's Exception property may contain more info about the error.
  348. case HTTPRequestStates.Error:
  349. reason = "Request Finished with Error! " + (req.Exception != null ? ("Exception: " + req.Exception.Message + req.Exception.StackTrace) : string.Empty);
  350. break;
  351. // The request aborted, initiated by the user.
  352. case HTTPRequestStates.Aborted:
  353. reason = "Request Aborted!";
  354. break;
  355. // Connecting to the server is timed out.
  356. case HTTPRequestStates.ConnectionTimedOut:
  357. reason = "Connection Timed Out!";
  358. break;
  359. // The request didn't finished in the given time.
  360. case HTTPRequestStates.TimedOut:
  361. reason = "Processing the request Timed Out!";
  362. break;
  363. default:
  364. return;
  365. }
  366. if (this.State != WebSocketStates.Connecting || !string.IsNullOrEmpty(reason))
  367. {
  368. if (this.Parent.OnError != null)
  369. {
  370. try
  371. {
  372. this.Parent.OnError(this.Parent, reason);
  373. }
  374. catch (Exception ex)
  375. {
  376. HTTPManager.Logger.Exception("OverHTTP2", "OnError", ex, this.Parent.Context);
  377. }
  378. }
  379. else if (!HTTPManager.IsQuitting)
  380. HTTPManager.Logger.Error("OverHTTP2", reason, this.Parent.Context);
  381. }
  382. else if (this.Parent.OnClosed != null)
  383. {
  384. try
  385. {
  386. this.Parent.OnClosed(this.Parent, (ushort)WebSocketStausCodes.NormalClosure, "Closed while opening");
  387. }
  388. catch (Exception ex)
  389. {
  390. HTTPManager.Logger.Exception("OverHTTP2", "OnClosed", ex, this.Parent.Context);
  391. }
  392. }
  393. this.State = WebSocketStates.Closed;
  394. }
  395. public override void StartOpen()
  396. {
  397. if (this.Parent.Extensions != null)
  398. {
  399. try
  400. {
  401. for (int i = 0; i < this.Parent.Extensions.Length; ++i)
  402. {
  403. var ext = this.Parent.Extensions[i];
  404. if (ext != null)
  405. ext.AddNegotiation(base.InternalRequest);
  406. }
  407. }
  408. catch (Exception ex)
  409. {
  410. HTTPManager.Logger.Exception("OverHTTP2", "Open", ex, this.Parent.Context);
  411. }
  412. }
  413. base.InternalRequest.Send();
  414. HTTPManager.Heartbeats.Subscribe(this);
  415. HTTPUpdateDelegator.OnApplicationForegroundStateChanged += OnApplicationForegroundStateChanged;
  416. this.State = WebSocketStates.Connecting;
  417. }
  418. public override void StartClose(ushort code, string message)
  419. {
  420. if (this.State == WebSocketStates.Connecting)
  421. {
  422. if (this.InternalRequest != null)
  423. this.InternalRequest.Abort();
  424. this.State = WebSocketStates.Closed;
  425. if (this.Parent.OnClosed != null)
  426. this.Parent.OnClosed(this.Parent, code, message);
  427. }
  428. else
  429. {
  430. Send(new WebSocketFrame(this.Parent, WebSocketFrameTypes.ConnectionClose, WebSocket.EncodeCloseData(code, message)));
  431. this.State = WebSocketStates.Closing;
  432. }
  433. }
  434. public override void Send(string message)
  435. {
  436. if (message == null)
  437. throw new ArgumentNullException("message must not be null!");
  438. int count = System.Text.Encoding.UTF8.GetByteCount(message);
  439. byte[] data = BufferPool.Get(count, true);
  440. System.Text.Encoding.UTF8.GetBytes(message, 0, message.Length, data, 0);
  441. var frame = new WebSocketFrame(this.Parent, WebSocketFrameTypes.Text, data, 0, (ulong)count, true, true);
  442. var maxFrameSize = this.http2Handler.settings.RemoteSettings[HTTP2Settings.MAX_FRAME_SIZE];
  443. if (frame.Data != null && frame.Data.Length > maxFrameSize)
  444. {
  445. WebSocketFrame[] additionalFrames = frame.Fragment(maxFrameSize);
  446. Send(frame);
  447. if (additionalFrames != null)
  448. for (int i = 0; i < additionalFrames.Length; ++i)
  449. Send(additionalFrames[i]);
  450. }
  451. else
  452. Send(frame);
  453. BufferPool.Release(data);
  454. }
  455. public override void Send(byte[] buffer)
  456. {
  457. if (buffer == null)
  458. throw new ArgumentNullException("data must not be null!");
  459. WebSocketFrame frame = new WebSocketFrame(this.Parent, WebSocketFrameTypes.Binary, buffer);
  460. var maxFrameSize = this.http2Handler.settings.RemoteSettings[HTTP2Settings.MAX_FRAME_SIZE];
  461. if (frame.Data != null && frame.Data.Length > maxFrameSize)
  462. {
  463. WebSocketFrame[] additionalFrames = frame.Fragment(maxFrameSize);
  464. Send(frame);
  465. if (additionalFrames != null)
  466. for (int i = 0; i < additionalFrames.Length; ++i)
  467. Send(additionalFrames[i]);
  468. }
  469. else
  470. Send(frame);
  471. }
  472. public override void Send(byte[] data, ulong offset, ulong count)
  473. {
  474. if (data == null)
  475. throw new ArgumentNullException("data must not be null!");
  476. if (offset + count > (ulong)data.Length)
  477. throw new ArgumentOutOfRangeException("offset + count >= data.Length");
  478. WebSocketFrame frame = new WebSocketFrame(this.Parent, WebSocketFrameTypes.Binary, data, offset, count, true, true);
  479. var maxFrameSize = this.http2Handler.settings.RemoteSettings[HTTP2Settings.MAX_FRAME_SIZE];
  480. if (frame.Data != null && frame.Data.Length > maxFrameSize)
  481. {
  482. WebSocketFrame[] additionalFrames = frame.Fragment(maxFrameSize);
  483. Send(frame);
  484. if (additionalFrames != null)
  485. for (int i = 0; i < additionalFrames.Length; ++i)
  486. Send(additionalFrames[i]);
  487. }
  488. else
  489. Send(frame);
  490. }
  491. public override void Send(WebSocketFrame frame)
  492. {
  493. if (frame == null)
  494. throw new ArgumentNullException("frame is null!");
  495. if (this.upStream.IsClosed || closeSent)
  496. return;
  497. var frameData = frame.Get();
  498. this.upStream.Write(new BufferSegment(frameData.Data, 0, frameData.Length));
  499. this.http2Handler.SignalRunnerThread();
  500. frameData.Data = null;
  501. if (frame.Type == WebSocketFrameTypes.ConnectionClose)
  502. this.closeSent = true;
  503. }
  504. private int CalculateLatency()
  505. {
  506. if (this.rtts.Count == 0)
  507. return 0;
  508. int sumLatency = 0;
  509. for (int i = 0; i < this.rtts.Count; ++i)
  510. sumLatency += this.rtts[i];
  511. return sumLatency / this.rtts.Count;
  512. }
  513. public void OnHeartbeatUpdate(TimeSpan dif)
  514. {
  515. DateTime now = DateTime.Now;
  516. switch (this.State)
  517. {
  518. case WebSocketStates.Connecting:
  519. if (now - this.InternalRequest.Timing.Start >= this.Parent.CloseAfterNoMessage)
  520. {
  521. if (HTTPManager.HTTP2Settings.WebSocketOverHTTP2Settings.EnableImplementationFallback)
  522. {
  523. this.State = WebSocketStates.Closed;
  524. this.InternalRequest.OnHeadersReceived = null;
  525. this.InternalRequest.Callback = null;
  526. this.Parent.FallbackToHTTP1();
  527. }
  528. else
  529. {
  530. CloseWithError("WebSocket Over HTTP/2 Implementation failed to connect in the given time!");
  531. }
  532. }
  533. break;
  534. case WebSocketStates.Open:
  535. if (this.Parent.StartPingThread)
  536. {
  537. if (!waitingForPong && now - LastMessageReceived >= TimeSpan.FromMilliseconds(this.Parent.PingFrequency))
  538. SendPing();
  539. if (waitingForPong && now - lastPing > this.Parent.CloseAfterNoMessage)
  540. {
  541. HTTPManager.Logger.Warning("OverHTTP2",
  542. string.Format("No message received in the given time! Closing WebSocket. LastPing: {0}, PingFrequency: {1}, Close After: {2}, Now: {3}",
  543. this.lastPing, TimeSpan.FromMilliseconds(this.Parent.PingFrequency), this.Parent.CloseAfterNoMessage, now), this.Parent.Context);
  544. CloseWithError("No message received in the given time!");
  545. }
  546. }
  547. break;
  548. case WebSocketStates.Closed:
  549. HTTPManager.Heartbeats.Unsubscribe(this);
  550. HTTPUpdateDelegator.OnApplicationForegroundStateChanged -= OnApplicationForegroundStateChanged;
  551. break;
  552. }
  553. }
  554. private void OnApplicationForegroundStateChanged(bool isPaused)
  555. {
  556. if (!isPaused)
  557. base.LastMessageReceived = DateTime.Now;
  558. }
  559. private void SendPing()
  560. {
  561. HTTPManager.Logger.Information("OverHTTP2", "Sending Ping frame, waiting for a pong...", this.Parent.Context);
  562. lastPing = DateTime.Now;
  563. waitingForPong = true;
  564. long ticks = DateTime.Now.Ticks;
  565. var ticksBytes = BitConverter.GetBytes(ticks);
  566. var pingFrame = new WebSocketFrame(this.Parent, WebSocketFrameTypes.Ping, ticksBytes);
  567. Send(pingFrame);
  568. }
  569. private void CloseWithError(string message)
  570. {
  571. this.State = WebSocketStates.Closed;
  572. this.upStream.Close();
  573. if (this.Parent.OnError != null)
  574. {
  575. try
  576. {
  577. this.Parent.OnError(this.Parent, message);
  578. }
  579. catch (Exception ex)
  580. {
  581. HTTPManager.Logger.Exception("OverHTTP2", "OnError", ex, this.Parent.Context);
  582. }
  583. }
  584. this.InternalRequest.Abort();
  585. }
  586. }
  587. }
  588. #endif