WebsocketServerHandler.cs 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.IO;
  30. using System.Net;
  31. using System.Security.Cryptography;
  32. using System.Text;
  33. using System.Threading;
  34. using OSHttpServer;
  35. namespace OpenSim.Framework.Servers.HttpServer
  36. {
  37. // Sealed class. If you're going to unseal it, implement IDisposable.
  38. /// <summary>
  39. /// This class implements websockets. It grabs the network context from C#Webserver and utilizes it directly as a tcp streaming service
  40. /// </summary>
  41. public sealed class WebSocketHttpServerHandler : BaseRequestHandler
  42. {
  43. private class WebSocketState
  44. {
  45. public List<byte> ReceivedBytes;
  46. public int ExpectedBytes;
  47. public WebsocketFrameHeader Header;
  48. public bool FrameComplete;
  49. public WebSocketFrame ContinuationFrame;
  50. }
  51. /// <summary>
  52. /// Binary Data will trigger this event
  53. /// </summary>
  54. public event DataDelegate OnData;
  55. /// <summary>
  56. /// Textual Data will trigger this event
  57. /// </summary>
  58. public event TextDelegate OnText;
  59. /// <summary>
  60. /// A ping request form the other side will trigger this event.
  61. /// This class responds to the ping automatically. You shouldn't send a pong.
  62. /// it's informational.
  63. /// </summary>
  64. public event PingDelegate OnPing;
  65. /// <summary>
  66. /// This is a response to a ping you sent.
  67. /// </summary>
  68. public event PongDelegate OnPong;
  69. /// <summary>
  70. /// This is a regular HTTP Request... This may be removed in the future.
  71. /// </summary>
  72. // public event RegularHttpRequestDelegate OnRegularHttpRequest;
  73. /// <summary>
  74. /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired
  75. /// </summary>
  76. public event UpgradeCompletedDelegate OnUpgradeCompleted;
  77. /// <summary>
  78. /// If the upgrade failed, this will be fired
  79. /// </summary>
  80. public event UpgradeFailedDelegate OnUpgradeFailed;
  81. /// <summary>
  82. /// When the websocket is closed, this will be fired.
  83. /// </summary>
  84. public event CloseDelegate OnClose;
  85. /// <summary>
  86. /// Set this delegate to allow your module to validate the origin of the
  87. /// Websocket request. Primary line of defense against cross site scripting
  88. /// </summary>
  89. public ValidateHandshake HandshakeValidateMethodOverride = null;
  90. private ManualResetEvent _receiveDone = new ManualResetEvent(false);
  91. private OSHttpRequest _request;
  92. private HTTPNetworkContext _networkContext;
  93. private IHttpClientContext _clientContext;
  94. private int _pingtime = 0;
  95. private byte[] _buffer;
  96. private int _bufferPosition;
  97. private int _bufferLength;
  98. private bool _closing;
  99. private bool _upgraded;
  100. private int _maxPayloadBytes = 41943040;
  101. private int _initialMsgTimeout = 0;
  102. private int _defaultReadTimeout = 10000;
  103. private const string HandshakeAcceptText =
  104. "HTTP/1.1 101 Switching Protocols\r\n" +
  105. "upgrade: websocket\r\n" +
  106. "Connection: Upgrade\r\n" +
  107. "sec-websocket-accept: {0}\r\n\r\n";// +
  108. //"{1}";
  109. private const string HandshakeDeclineText =
  110. "HTTP/1.1 {0} {1}\r\n" +
  111. "Connection: close\r\n\r\n";
  112. /// <summary>
  113. /// Mysterious constant defined in RFC6455 to append to the client provided security key
  114. /// </summary>
  115. private const string WebsocketHandshakeAcceptHashConstant = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  116. public WebSocketHttpServerHandler(OSHttpRequest preq, int bufferlen)
  117. : base(preq.HttpMethod, preq.Url.OriginalString)
  118. {
  119. _request = preq;
  120. _networkContext = preq.IHttpClientContext.GiveMeTheNetworkStreamIKnowWhatImDoing();
  121. _networkContext.Stream.ReadTimeout = _defaultReadTimeout;
  122. _clientContext = preq.IHttpClientContext;
  123. _bufferLength = bufferlen;
  124. _buffer = new byte[_bufferLength];
  125. }
  126. // Sealed class implments destructor and an internal dispose method. complies with C# unmanaged resource best practices.
  127. ~WebSocketHttpServerHandler()
  128. {
  129. Dispose();
  130. }
  131. /// <summary>
  132. /// Sets the length of the stream buffer
  133. /// </summary>
  134. /// <param name="pChunk">Byte length.</param>
  135. public void SetChunksize(int pChunk)
  136. {
  137. if (!_upgraded)
  138. {
  139. _buffer = new byte[pChunk];
  140. }
  141. else
  142. {
  143. throw new InvalidOperationException("You must set the chunksize before the connection is upgraded");
  144. }
  145. }
  146. /// <summary>
  147. /// This is the famous nagle.
  148. /// </summary>
  149. public bool NoDelay_TCP_Nagle
  150. {
  151. get
  152. {
  153. if (_networkContext != null && _networkContext.Socket != null)
  154. {
  155. return _networkContext.Socket.NoDelay;
  156. }
  157. else
  158. {
  159. throw new InvalidOperationException("The socket has been shutdown");
  160. }
  161. }
  162. set
  163. {
  164. if (_networkContext != null && _networkContext.Socket != null)
  165. _networkContext.Socket.NoDelay = value;
  166. else
  167. {
  168. throw new InvalidOperationException("The socket has been shutdown");
  169. }
  170. }
  171. }
  172. /// <summary>
  173. /// This triggers the websocket to start the upgrade process...
  174. /// This is a Generalized Networking 'common sense' helper method. Some people expect to call Start() instead
  175. /// of the more context appropriate HandshakeAndUpgrade()
  176. /// </summary>
  177. public void Start()
  178. {
  179. HandshakeAndUpgrade();
  180. }
  181. /// <summary>
  182. /// Max Payload Size in bytes. Defaults to 40MB, but could be set upon connection before calling handshake and upgrade.
  183. /// </summary>
  184. public int MaxPayloadSize
  185. {
  186. get { return _maxPayloadBytes; }
  187. set { _maxPayloadBytes = value; }
  188. }
  189. /// <summary>
  190. /// Set this to the maximum amount of milliseconds to wait for the first complete message to be sent or received on the websocket after upgrading
  191. /// Default, it waits forever. Use this when your Websocket consuming code opens a connection and waits for a message from the other side to avoid a Denial of Service vector.
  192. /// </summary>
  193. public int InitialMsgTimeout
  194. {
  195. get { return _initialMsgTimeout; }
  196. set { _initialMsgTimeout = value; }
  197. }
  198. /// <summary>
  199. /// This triggers the websocket start the upgrade process
  200. /// </summary>
  201. public void HandshakeAndUpgrade()
  202. {
  203. string webOrigin = string.Empty;
  204. string websocketKey = string.Empty;
  205. string acceptKey = string.Empty;
  206. string accepthost = string.Empty;
  207. if (!string.IsNullOrEmpty(_request.Headers["origin"]))
  208. webOrigin = _request.Headers["origin"];
  209. if (!string.IsNullOrEmpty(_request.Headers["sec-websocket-key"]))
  210. websocketKey = _request.Headers["sec-websocket-key"];
  211. if (!string.IsNullOrEmpty(_request.Headers["host"]))
  212. accepthost = _request.Headers["host"];
  213. if (string.IsNullOrEmpty(_request.Headers["upgrade"]))
  214. {
  215. FailUpgrade(HttpStatusCode.BadRequest, "no upgrade request submitted");
  216. }
  217. string connectionheader = _request.Headers["upgrade"];
  218. if (connectionheader.ToLower() != "websocket")
  219. {
  220. FailUpgrade(HttpStatusCode.BadRequest, "no connection upgrade request submitted");
  221. }
  222. // If the object consumer provided a method to validate the origin, we should call it and give the client a success or fail.
  223. // If not.. we should accept any. The assumption here is that there would be no Websocket handlers registered in baseHTTPServer unless
  224. // Something asked for it...
  225. if (HandshakeValidateMethodOverride != null)
  226. {
  227. if (HandshakeValidateMethodOverride(webOrigin, websocketKey, accepthost))
  228. {
  229. acceptKey = GenerateAcceptKey(websocketKey);
  230. string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
  231. SendUpgradeSuccess(rawaccept);
  232. }
  233. else
  234. {
  235. FailUpgrade(HttpStatusCode.Forbidden, "Origin Validation Failed");
  236. }
  237. }
  238. else
  239. {
  240. acceptKey = GenerateAcceptKey(websocketKey);
  241. string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
  242. SendUpgradeSuccess(rawaccept);
  243. }
  244. }
  245. public IPEndPoint GetRemoteIPEndpoint()
  246. {
  247. return _request.RemoteIPEndPoint;
  248. }
  249. /// <summary>
  250. /// Generates a handshake response key string based on the client's
  251. /// provided key to prove to the client that we're allowing the Websocket
  252. /// upgrade of our own free will and we were not coerced into doing it.
  253. /// </summary>
  254. /// <param name="key">Client provided security key</param>
  255. /// <returns></returns>
  256. private static string GenerateAcceptKey(string key)
  257. {
  258. if (string.IsNullOrEmpty(key))
  259. return string.Empty;
  260. string acceptkey = key + WebsocketHandshakeAcceptHashConstant;
  261. SHA1 hashobj = SHA1.Create();
  262. string ret = Convert.ToBase64String(hashobj.ComputeHash(Encoding.UTF8.GetBytes(acceptkey)));
  263. hashobj.Clear();
  264. return ret;
  265. }
  266. /// <summary>
  267. /// Informs the otherside that we accepted their upgrade request
  268. /// </summary>
  269. /// <param name="pHandshakeResponse">The HTTP 1.1 101 response that says Yay \o/ </param>
  270. private void SendUpgradeSuccess(string pHandshakeResponse)
  271. {
  272. // Create a new websocket state so we can keep track of data in between network reads.
  273. WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true};
  274. byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse);
  275. try
  276. {
  277. if (_initialMsgTimeout > 0)
  278. {
  279. _receiveDone.Reset();
  280. }
  281. // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream.
  282. _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState);
  283. // Write the upgrade handshake success message
  284. _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
  285. _networkContext.Stream.Flush();
  286. _upgraded = true;
  287. UpgradeCompletedDelegate d = OnUpgradeCompleted;
  288. if (d != null)
  289. d(this, new UpgradeCompletedEventArgs());
  290. if (_initialMsgTimeout > 0)
  291. {
  292. if (!_receiveDone.WaitOne(TimeSpan.FromMilliseconds(_initialMsgTimeout)))
  293. Close(string.Empty);
  294. }
  295. }
  296. catch (IOException)
  297. {
  298. Close(string.Empty);
  299. }
  300. catch (ObjectDisposedException)
  301. {
  302. Close(string.Empty);
  303. }
  304. }
  305. /// <summary>
  306. /// The server has decided not to allow the upgrade to a websocket for some reason. The Http 1.1 response that says Nay >:(
  307. /// </summary>
  308. /// <param name="pCode">HTTP Status reflecting the reason why</param>
  309. /// <param name="pMessage">Textual reason for the upgrade fail</param>
  310. private void FailUpgrade(HttpStatusCode pCode, string pMessage )
  311. {
  312. string handshakeResponse = string.Format(HandshakeDeclineText, (int)pCode, pMessage.Replace("\n", string.Empty).Replace("\r", string.Empty));
  313. byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(handshakeResponse);
  314. _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
  315. _networkContext.Stream.Flush();
  316. _networkContext.Stream.Dispose();
  317. UpgradeFailedDelegate d = OnUpgradeFailed;
  318. if (d != null)
  319. d(this,new UpgradeFailedEventArgs());
  320. }
  321. /// <summary>
  322. /// This is our ugly Async OnReceive event handler.
  323. /// This chunks the input stream based on the length of the provided buffer and processes out
  324. /// as many frames as it can. It then moves the unprocessed data to the beginning of the buffer.
  325. /// </summary>
  326. /// <param name="ar">Our Async State from beginread</param>
  327. private void OnReceive(IAsyncResult ar)
  328. {
  329. WebSocketState _socketState = ar.AsyncState as WebSocketState;
  330. try
  331. {
  332. int bytesRead = _networkContext.Stream.EndRead(ar);
  333. if (bytesRead == 0)
  334. {
  335. // Do Disconnect
  336. _networkContext.Stream.Dispose();
  337. _networkContext = null;
  338. return;
  339. }
  340. _bufferPosition += bytesRead;
  341. if (_bufferPosition > _bufferLength)
  342. {
  343. // Message too big for chunksize.. not sure how this happened...
  344. //Close(string.Empty);
  345. }
  346. int offset = 0;
  347. bool headerread = true;
  348. int headerforwardposition = 0;
  349. while (headerread && offset < bytesRead)
  350. {
  351. if (_socketState.FrameComplete)
  352. {
  353. WebsocketFrameHeader pheader = WebsocketFrameHeader.ZeroHeader;
  354. headerread = WebSocketReader.TryReadHeader(_buffer, offset, _bufferPosition - offset, out pheader,
  355. out headerforwardposition);
  356. offset += headerforwardposition;
  357. if (headerread)
  358. {
  359. _socketState.FrameComplete = false;
  360. if (pheader.PayloadLen > (ulong) _maxPayloadBytes)
  361. {
  362. Close("Invalid Payload size");
  363. return;
  364. }
  365. if (pheader.PayloadLen > 0)
  366. {
  367. if ((int) pheader.PayloadLen > _bufferPosition - offset)
  368. {
  369. byte[] writebytes = new byte[_bufferPosition - offset];
  370. Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition - offset);
  371. _socketState.ExpectedBytes = (int) pheader.PayloadLen;
  372. _socketState.ReceivedBytes.AddRange(writebytes);
  373. _socketState.Header = pheader; // We need to add the header so that we can unmask it
  374. offset += (int) _bufferPosition - offset;
  375. }
  376. else
  377. {
  378. byte[] writebytes = new byte[pheader.PayloadLen];
  379. Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) pheader.PayloadLen);
  380. WebSocketReader.Mask(pheader.Mask, writebytes);
  381. pheader.IsMasked = false;
  382. _socketState.FrameComplete = true;
  383. _socketState.ReceivedBytes.AddRange(writebytes);
  384. _socketState.Header = pheader;
  385. offset += (int) pheader.PayloadLen;
  386. }
  387. }
  388. else
  389. {
  390. pheader.Mask = 0;
  391. _socketState.FrameComplete = true;
  392. _socketState.Header = pheader;
  393. }
  394. if (_socketState.FrameComplete)
  395. {
  396. ProcessFrame(_socketState);
  397. _socketState.Header.SetDefault();
  398. _socketState.ReceivedBytes.Clear();
  399. _socketState.ExpectedBytes = 0;
  400. }
  401. }
  402. }
  403. else
  404. {
  405. WebsocketFrameHeader frameHeader = _socketState.Header;
  406. int bytesleft = _socketState.ExpectedBytes - _socketState.ReceivedBytes.Count;
  407. if (bytesleft > _bufferPosition)
  408. {
  409. byte[] writebytes = new byte[_bufferPosition];
  410. Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
  411. _socketState.ReceivedBytes.AddRange(writebytes);
  412. _socketState.Header = frameHeader; // We need to add the header so that we can unmask it
  413. offset += (int) _bufferPosition;
  414. }
  415. else
  416. {
  417. byte[] writebytes = new byte[_bufferPosition];
  418. Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
  419. _socketState.FrameComplete = true;
  420. _socketState.ReceivedBytes.AddRange(writebytes);
  421. _socketState.Header = frameHeader;
  422. offset += (int) _bufferPosition;
  423. }
  424. if (_socketState.FrameComplete)
  425. {
  426. ProcessFrame(_socketState);
  427. _socketState.Header.SetDefault();
  428. _socketState.ReceivedBytes.Clear();
  429. _socketState.ExpectedBytes = 0;
  430. // do some processing
  431. }
  432. }
  433. }
  434. if (offset > 0)
  435. {
  436. // If the buffer is maxed out.. we can just move the cursor. Nothing to move to the beginning.
  437. if (offset <_buffer.Length)
  438. Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bufferPosition - offset);
  439. _bufferPosition -= offset;
  440. }
  441. if (_networkContext.Stream != null && _networkContext.Stream.CanRead && !_closing)
  442. {
  443. _networkContext.Stream.BeginRead(_buffer, _bufferPosition, _bufferLength - _bufferPosition, OnReceive,
  444. _socketState);
  445. }
  446. else
  447. {
  448. // We can't read the stream anymore...
  449. }
  450. }
  451. catch (IOException)
  452. {
  453. Close(string.Empty);
  454. }
  455. catch (ObjectDisposedException)
  456. {
  457. Close(string.Empty);
  458. }
  459. }
  460. /// <summary>
  461. /// Sends a string to the other side
  462. /// </summary>
  463. /// <param name="message">the string message that is to be sent</param>
  464. public void SendMessage(string message)
  465. {
  466. if (_initialMsgTimeout > 0)
  467. {
  468. _receiveDone.Set();
  469. _initialMsgTimeout = 0;
  470. }
  471. byte[] messagedata = Encoding.UTF8.GetBytes(message);
  472. WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata };
  473. textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text;
  474. textMessageFrame.Header.IsEnd = true;
  475. SendSocket(textMessageFrame.ToBytes());
  476. }
  477. public void SendData(byte[] data)
  478. {
  479. if (_initialMsgTimeout > 0)
  480. {
  481. _receiveDone.Set();
  482. _initialMsgTimeout = 0;
  483. }
  484. WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data};
  485. dataMessageFrame.Header.IsEnd = true;
  486. dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary;
  487. SendSocket(dataMessageFrame.ToBytes());
  488. }
  489. /// <summary>
  490. /// Writes raw bytes to the websocket. Unframed data will cause disconnection
  491. /// </summary>
  492. /// <param name="data"></param>
  493. private void SendSocket(byte[] data)
  494. {
  495. if (!_closing)
  496. {
  497. try
  498. {
  499. _networkContext.Stream.Write(data, 0, data.Length);
  500. }
  501. catch (IOException)
  502. {
  503. }
  504. }
  505. }
  506. /// <summary>
  507. /// Sends a Ping check to the other side. The other side SHOULD respond as soon as possible with a pong frame. This interleaves with incoming fragmented frames.
  508. /// </summary>
  509. public void SendPingCheck()
  510. {
  511. if (_initialMsgTimeout > 0)
  512. {
  513. _receiveDone.Set();
  514. _initialMsgTimeout = 0;
  515. }
  516. WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] };
  517. pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping;
  518. pingFrame.Header.IsEnd = true;
  519. _pingtime = Util.EnvironmentTickCount();
  520. SendSocket(pingFrame.ToBytes());
  521. }
  522. /// <summary>
  523. /// Closes the websocket connection. Sends a close message to the other side if it hasn't already done so.
  524. /// </summary>
  525. /// <param name="message"></param>
  526. public void Close(string message)
  527. {
  528. if (_initialMsgTimeout > 0)
  529. {
  530. _receiveDone.Set();
  531. _initialMsgTimeout = 0;
  532. }
  533. if (_networkContext == null)
  534. return;
  535. if (_networkContext.Stream != null)
  536. {
  537. if (_networkContext.Stream.CanWrite)
  538. {
  539. byte[] messagedata = Encoding.UTF8.GetBytes(message);
  540. WebSocketFrame closeResponseFrame = new WebSocketFrame()
  541. {
  542. Header = WebsocketFrameHeader.HeaderDefault(),
  543. WebSocketPayload = messagedata
  544. };
  545. closeResponseFrame.Header.Opcode = WebSocketReader.OpCode.Close;
  546. closeResponseFrame.Header.PayloadLen = (ulong) messagedata.Length;
  547. closeResponseFrame.Header.IsEnd = true;
  548. SendSocket(closeResponseFrame.ToBytes());
  549. }
  550. }
  551. CloseDelegate closeD = OnClose;
  552. if (closeD != null)
  553. {
  554. closeD(this, new CloseEventArgs());
  555. }
  556. _closing = true;
  557. }
  558. /// <summary>
  559. /// Processes a websocket frame and triggers consumer events
  560. /// </summary>
  561. /// <param name="psocketState">We need to modify the websocket state here depending on the frame</param>
  562. private void ProcessFrame(WebSocketState psocketState)
  563. {
  564. if (psocketState.Header.IsMasked)
  565. {
  566. byte[] unmask = psocketState.ReceivedBytes.ToArray();
  567. WebSocketReader.Mask(psocketState.Header.Mask, unmask);
  568. psocketState.ReceivedBytes = new List<byte>(unmask);
  569. }
  570. if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0)
  571. {
  572. _receiveDone.Set();
  573. _initialMsgTimeout = 0;
  574. }
  575. switch (psocketState.Header.Opcode)
  576. {
  577. case WebSocketReader.OpCode.Ping:
  578. PingDelegate pingD = OnPing;
  579. if (pingD != null)
  580. {
  581. pingD(this, new PingEventArgs());
  582. }
  583. WebSocketFrame pongFrame = new WebSocketFrame(){Header = WebsocketFrameHeader.HeaderDefault(),WebSocketPayload = new byte[0]};
  584. pongFrame.Header.Opcode = WebSocketReader.OpCode.Pong;
  585. pongFrame.Header.IsEnd = true;
  586. SendSocket(pongFrame.ToBytes());
  587. break;
  588. case WebSocketReader.OpCode.Pong:
  589. PongDelegate pongD = OnPong;
  590. if (pongD != null)
  591. {
  592. pongD(this, new PongEventArgs(){PingResponseMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),_pingtime)});
  593. }
  594. break;
  595. case WebSocketReader.OpCode.Binary:
  596. if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
  597. {
  598. psocketState.ContinuationFrame = new WebSocketFrame
  599. {
  600. Header = psocketState.Header,
  601. WebSocketPayload =
  602. psocketState.ReceivedBytes.ToArray()
  603. };
  604. }
  605. else
  606. {
  607. // Send Done Event!
  608. DataDelegate dataD = OnData;
  609. if (dataD != null)
  610. {
  611. dataD(this,new WebsocketDataEventArgs(){Data = psocketState.ReceivedBytes.ToArray()});
  612. }
  613. }
  614. break;
  615. case WebSocketReader.OpCode.Text:
  616. if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
  617. {
  618. psocketState.ContinuationFrame = new WebSocketFrame
  619. {
  620. Header = psocketState.Header,
  621. WebSocketPayload =
  622. psocketState.ReceivedBytes.ToArray()
  623. };
  624. }
  625. else
  626. {
  627. TextDelegate textD = OnText;
  628. if (textD != null)
  629. {
  630. textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(psocketState.ReceivedBytes.ToArray()) });
  631. }
  632. // Send Done Event!
  633. }
  634. break;
  635. case WebSocketReader.OpCode.Continue: // Continuation. Multiple frames worth of data for one message. Only valid when not using Control Opcodes
  636. //Console.WriteLine("currhead " + psocketState.Header.IsEnd);
  637. //Console.WriteLine("Continuation! " + psocketState.ContinuationFrame.Header.IsEnd);
  638. byte[] combineddata = new byte[psocketState.ReceivedBytes.Count+psocketState.ContinuationFrame.WebSocketPayload.Length];
  639. byte[] newdata = psocketState.ReceivedBytes.ToArray();
  640. Buffer.BlockCopy(psocketState.ContinuationFrame.WebSocketPayload, 0, combineddata, 0, psocketState.ContinuationFrame.WebSocketPayload.Length);
  641. Buffer.BlockCopy(newdata, 0, combineddata,
  642. psocketState.ContinuationFrame.WebSocketPayload.Length, newdata.Length);
  643. psocketState.ContinuationFrame.WebSocketPayload = combineddata;
  644. psocketState.Header.PayloadLen = (ulong)combineddata.Length;
  645. if (psocketState.Header.IsEnd)
  646. {
  647. if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Text)
  648. {
  649. // Send Done event
  650. TextDelegate textD = OnText;
  651. if (textD != null)
  652. {
  653. textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(combineddata) });
  654. }
  655. }
  656. else if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Binary)
  657. {
  658. // Send Done event
  659. DataDelegate dataD = OnData;
  660. if (dataD != null)
  661. {
  662. dataD(this, new WebsocketDataEventArgs() { Data = combineddata });
  663. }
  664. }
  665. else
  666. {
  667. // protocol violation
  668. }
  669. psocketState.ContinuationFrame = null;
  670. }
  671. break;
  672. case WebSocketReader.OpCode.Close:
  673. Close(string.Empty);
  674. break;
  675. }
  676. psocketState.Header.SetDefault();
  677. psocketState.ReceivedBytes.Clear();
  678. psocketState.ExpectedBytes = 0;
  679. }
  680. public void Dispose()
  681. {
  682. if (_initialMsgTimeout > 0)
  683. {
  684. _receiveDone.Set();
  685. _initialMsgTimeout = 0;
  686. }
  687. if (_networkContext != null && _networkContext.Stream != null)
  688. {
  689. if (_networkContext.Stream.CanWrite)
  690. _networkContext.Stream.Flush();
  691. _networkContext.Stream.Close();
  692. _networkContext.Stream.Dispose();
  693. _networkContext.Stream = null;
  694. }
  695. if (_request != null && _request.InputStream != null)
  696. {
  697. _request.InputStream.Close();
  698. _request.InputStream.Dispose();
  699. _request = null;
  700. }
  701. if (_clientContext != null)
  702. {
  703. _clientContext.Close();
  704. _clientContext = null;
  705. }
  706. }
  707. }
  708. /// <summary>
  709. /// Reads a byte stream and returns Websocket frames.
  710. /// </summary>
  711. public class WebSocketReader
  712. {
  713. /// <summary>
  714. /// Bit to determine if the frame read on the stream is the last frame in a sequence of fragmented frames
  715. /// </summary>
  716. private const byte EndBit = 0x80;
  717. /// <summary>
  718. /// These are the Frame Opcodes
  719. /// </summary>
  720. public enum OpCode
  721. {
  722. // Data Opcodes
  723. Continue = 0x0,
  724. Text = 0x1,
  725. Binary = 0x2,
  726. // Control flow Opcodes
  727. Close = 0x8,
  728. Ping = 0x9,
  729. Pong = 0xA
  730. }
  731. /// <summary>
  732. /// Masks and Unmasks data using the frame mask. Mask is applied per octal
  733. /// Note: Frames from clients MUST be masked
  734. /// Note: Frames from servers MUST NOT be masked
  735. /// </summary>
  736. /// <param name="pMask">Int representing 32 bytes of mask data. Mask is applied per octal</param>
  737. /// <param name="pBuffer"></param>
  738. public static void Mask(int pMask, byte[] pBuffer)
  739. {
  740. byte[] maskKey = BitConverter.GetBytes(pMask);
  741. int currentMaskIndex = 0;
  742. for (int i = 0; i < pBuffer.Length; i++)
  743. {
  744. pBuffer[i] = (byte)(pBuffer[i] ^ maskKey[currentMaskIndex]);
  745. if (currentMaskIndex == 3)
  746. {
  747. currentMaskIndex = 0;
  748. }
  749. else
  750. {
  751. currentMaskIndex++;
  752. }
  753. }
  754. }
  755. /// <summary>
  756. /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader,
  757. /// and an int to move the buffer forward when it reads a header. False when it can't read a header
  758. /// </summary>
  759. /// <param name="pBuffer">Bytes read from the stream</param>
  760. /// <param name="pOffset">Starting place in the stream to begin trying to read from</param>
  761. /// <param name="length">Lenth in the stream to try and read from. Provided for cases where the
  762. /// buffer's length is larger then the data in it</param>
  763. /// <param name="oHeader">Outputs the read WebSocket frame header</param>
  764. /// <param name="moveBuffer">Informs the calling stream to move the buffer forward</param>
  765. /// <returns>True if it got a header, False if it didn't get a header</returns>
  766. public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader,
  767. out int moveBuffer)
  768. {
  769. oHeader = WebsocketFrameHeader.ZeroHeader;
  770. int minumheadersize = 2;
  771. if (length > pBuffer.Length - pOffset)
  772. throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied");
  773. if (length < minumheadersize)
  774. {
  775. moveBuffer = 0;
  776. return false;
  777. }
  778. byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3
  779. byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block
  780. oHeader = new WebsocketFrameHeader();
  781. oHeader.SetDefault();
  782. if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit)
  783. {
  784. oHeader.IsEnd = true;
  785. }
  786. else
  787. {
  788. oHeader.IsEnd = false;
  789. }
  790. //Opcode
  791. oHeader.Opcode = (WebSocketReader.OpCode)nibble2;
  792. //Mask
  793. oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7);
  794. // Payload length
  795. oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F);
  796. int index = 2; // LargerPayload length starts at byte 3
  797. switch (oHeader.PayloadLen)
  798. {
  799. case 126:
  800. minumheadersize += 2;
  801. if (length < minumheadersize)
  802. {
  803. moveBuffer = 0;
  804. return false;
  805. }
  806. Array.Reverse(pBuffer, pOffset + index, 2); // two bytes
  807. oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index);
  808. index += 2;
  809. break;
  810. case 127: // we got more this is a bigger frame
  811. // 8 bytes - uint64 - most significant bit 0 network byte order
  812. minumheadersize += 8;
  813. if (length < minumheadersize)
  814. {
  815. moveBuffer = 0;
  816. return false;
  817. }
  818. Array.Reverse(pBuffer, pOffset + index, 8);
  819. oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index);
  820. index += 8;
  821. break;
  822. }
  823. //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation
  824. if (oHeader.IsMasked)
  825. {
  826. minumheadersize += 4;
  827. if (length < minumheadersize)
  828. {
  829. moveBuffer = 0;
  830. return false;
  831. }
  832. oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index);
  833. index += 4;
  834. }
  835. moveBuffer = index;
  836. return true;
  837. }
  838. }
  839. /// <summary>
  840. /// RFC6455 Websocket Frame
  841. /// </summary>
  842. public class WebSocketFrame
  843. {
  844. /*
  845. * RFC6455
  846. nib 0 1 2 3 4 5 6 7
  847. byt 0 1 2 3
  848. dec 0 1 2 3
  849. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  850. +-+-+-+-+-------+-+-------------+-------------------------------+
  851. |F|R|R|R| opcode|M| Payload len | Extended payload length |
  852. |I|S|S|S| (4) |A| (7) | (16/64) +
  853. |N|V|V|V| |S| | (if payload len==126/127) |
  854. | |1|2|3| |K| | +
  855. +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
  856. | Extended payload length continued, if payload len == 127 |
  857. + - - - - - - - - - - - - - - - +-------------------------------+
  858. | |Masking-key, if MASK set to 1 |
  859. +-------------------------------+-------------------------------+
  860. | Masking-key (continued) | Payload Data |
  861. +-------------------------------- - - - - - - - - - - - - - - - +
  862. : Payload Data continued ... :
  863. + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
  864. | Payload Data continued ... |
  865. +---------------------------------------------------------------+
  866. * When reading these, the frames are possibly fragmented and interleaved with control frames
  867. * the fragmented frames are not interleaved with data frames. Just control frames
  868. */
  869. public static readonly WebSocketFrame DefaultFrame = new WebSocketFrame(){Header = new WebsocketFrameHeader(),WebSocketPayload = new byte[0]};
  870. public WebsocketFrameHeader Header;
  871. public byte[] WebSocketPayload;
  872. public byte[] ToBytes()
  873. {
  874. Header.PayloadLen = (ulong)WebSocketPayload.Length;
  875. return Header.ToBytes(WebSocketPayload);
  876. }
  877. }
  878. public struct WebsocketFrameHeader
  879. {
  880. //public byte CurrentMaskIndex;
  881. /// <summary>
  882. /// The last frame in a sequence of fragmented frames or the one and only frame for this message.
  883. /// </summary>
  884. public bool IsEnd;
  885. /// <summary>
  886. /// Returns whether the payload data is masked or not. Data from Clients MUST be masked, Data from Servers MUST NOT be masked
  887. /// </summary>
  888. public bool IsMasked;
  889. /// <summary>
  890. /// A set of cryptologically sound random bytes XoR-ed against the payload octally. Looped
  891. /// </summary>
  892. public int Mask;
  893. /*
  894. byt 0 1 2 3
  895. 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  896. +---------------+---------------+---------------+---------------+
  897. | Octal 1 | Octal 2 | Octal 3 | Octal 4 |
  898. +---------------+---------------+---------------+---------------+
  899. */
  900. public WebSocketReader.OpCode Opcode;
  901. public UInt64 PayloadLen;
  902. //public UInt64 PayloadLeft;
  903. // Payload is X + Y
  904. //public UInt64 ExtensionDataLength;
  905. //public UInt64 ApplicationDataLength;
  906. public static readonly WebsocketFrameHeader ZeroHeader = WebsocketFrameHeader.HeaderDefault();
  907. public void SetDefault()
  908. {
  909. //CurrentMaskIndex = 0;
  910. IsEnd = true;
  911. IsMasked = true;
  912. Mask = 0;
  913. Opcode = WebSocketReader.OpCode.Close;
  914. // PayloadLeft = 0;
  915. PayloadLen = 0;
  916. // ExtensionDataLength = 0;
  917. // ApplicationDataLength = 0;
  918. }
  919. /// <summary>
  920. /// Returns a byte array representing the Frame header
  921. /// </summary>
  922. /// <param name="payload">This is the frame data payload. The header describes the size of the payload.
  923. /// If payload is null, a Zero sized payload is assumed</param>
  924. /// <returns>Returns a byte array representing the frame header</returns>
  925. public byte[] ToBytes(byte[] payload)
  926. {
  927. List<byte> result = new List<byte>();
  928. // Squeeze in our opcode and our ending bit.
  929. result.Add((byte)((byte)Opcode | (IsEnd?0x80:0x00) ));
  930. // Again with the three different byte interpretations of size..
  931. //bytesize
  932. if (PayloadLen <= 125)
  933. {
  934. result.Add((byte) PayloadLen);
  935. } //Uint16
  936. else if (PayloadLen <= ushort.MaxValue)
  937. {
  938. result.Add(126);
  939. byte[] payloadLengthByte = BitConverter.GetBytes(Convert.ToUInt16(PayloadLen));
  940. Array.Reverse(payloadLengthByte);
  941. result.AddRange(payloadLengthByte);
  942. } //UInt64
  943. else
  944. {
  945. result.Add(127);
  946. byte[] payloadLengthByte = BitConverter.GetBytes(PayloadLen);
  947. Array.Reverse(payloadLengthByte);
  948. result.AddRange(payloadLengthByte);
  949. }
  950. // Only add a payload if it's not null
  951. if (payload != null)
  952. {
  953. result.AddRange(payload);
  954. }
  955. return result.ToArray();
  956. }
  957. /// <summary>
  958. /// A Helper method to define the defaults
  959. /// </summary>
  960. /// <returns></returns>
  961. public static WebsocketFrameHeader HeaderDefault()
  962. {
  963. return new WebsocketFrameHeader
  964. {
  965. //CurrentMaskIndex = 0,
  966. IsEnd = false,
  967. IsMasked = true,
  968. Mask = 0,
  969. Opcode = WebSocketReader.OpCode.Close,
  970. //PayloadLeft = 0,
  971. PayloadLen = 0,
  972. // ExtensionDataLength = 0,
  973. // ApplicationDataLength = 0
  974. };
  975. }
  976. }
  977. public delegate void DataDelegate(object sender, WebsocketDataEventArgs data);
  978. public delegate void TextDelegate(object sender, WebsocketTextEventArgs text);
  979. public delegate void PingDelegate(object sender, PingEventArgs pingdata);
  980. public delegate void PongDelegate(object sender, PongEventArgs pongdata);
  981. public delegate void RegularHttpRequestDelegate(object sender, RegularHttpRequestEvnetArgs request);
  982. public delegate void UpgradeCompletedDelegate(object sender, UpgradeCompletedEventArgs completeddata);
  983. public delegate void UpgradeFailedDelegate(object sender, UpgradeFailedEventArgs faileddata);
  984. public delegate void CloseDelegate(object sender, CloseEventArgs closedata);
  985. public delegate bool ValidateHandshake(string pWebOrigin, string pWebSocketKey, string pHost);
  986. public class WebsocketDataEventArgs : EventArgs
  987. {
  988. public byte[] Data;
  989. }
  990. public class WebsocketTextEventArgs : EventArgs
  991. {
  992. public string Data;
  993. }
  994. public class PingEventArgs : EventArgs
  995. {
  996. /// <summary>
  997. /// The ping event can arbitrarily contain data
  998. /// </summary>
  999. public byte[] Data;
  1000. }
  1001. public class PongEventArgs : EventArgs
  1002. {
  1003. /// <summary>
  1004. /// The pong event can arbitrarily contain data
  1005. /// </summary>
  1006. public byte[] Data;
  1007. public int PingResponseMS;
  1008. }
  1009. public class RegularHttpRequestEvnetArgs : EventArgs
  1010. {
  1011. }
  1012. public class UpgradeCompletedEventArgs : EventArgs
  1013. {
  1014. }
  1015. public class UpgradeFailedEventArgs : EventArgs
  1016. {
  1017. }
  1018. public class CloseEventArgs : EventArgs
  1019. {
  1020. }
  1021. }