WebsocketServerHandler.cs 44 KB

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