OverHTTP2.cs 30 KB

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