WssSession.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. using System;
  2. using System.Net.Sockets;
  3. using System.Text;
  4. namespace NetCoreServer
  5. {
  6. /// <summary>
  7. /// WebSocket secure session
  8. /// </summary>
  9. /// <remarks> WebSocket secure session is used to read and write data from the connected WebSocket client. Thread-safe.</remarks>
  10. public class WssSession : HttpsSession, IWebSocket
  11. {
  12. internal readonly WebSocket WebSocket;
  13. /// <summary>
  14. /// Initialize a new WebSocket session
  15. /// </summary>
  16. /// <param name="server">WebSocket server</param>
  17. public WssSession(WssServer server) : base(server) { WebSocket = new WebSocket(this); }
  18. // WebSocket connection methods
  19. public virtual bool Close() => Close(0, Span<byte>.Empty);
  20. public virtual bool Close(int status) => Close(status, Span<byte>.Empty);
  21. public virtual bool Close(int status, string text) => Close(status, Encoding.UTF8.GetBytes(text));
  22. public virtual bool Close(int status, ReadOnlySpan<char> text) => Close(status, Encoding.UTF8.GetBytes(text.ToArray()));
  23. public virtual bool Close(int status, byte[] buffer) => Close(status, buffer.AsSpan());
  24. public virtual bool Close(int status, byte[] buffer, long offset, long size) => Close(status, buffer.AsSpan((int)offset, (int)size));
  25. public virtual bool Close(int status, ReadOnlySpan<byte> buffer) { SendCloseAsync(status, buffer); base.Disconnect(); return true; }
  26. #region WebSocket send text methods
  27. public long SendText(string text) => SendText(Encoding.UTF8.GetBytes(text));
  28. public long SendText(ReadOnlySpan<char> text) => SendText(Encoding.UTF8.GetBytes(text.ToArray()));
  29. public long SendText(byte[] buffer) => SendText(buffer.AsSpan());
  30. public long SendText(byte[] buffer, long offset, long size) => SendText(buffer.AsSpan((int)offset, (int)size));
  31. public long SendText(ReadOnlySpan<byte> buffer)
  32. {
  33. lock (WebSocket.WsSendLock)
  34. {
  35. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_TEXT, false, buffer);
  36. return base.Send(WebSocket.WsSendBuffer.AsSpan());
  37. }
  38. }
  39. public bool SendTextAsync(string text) => SendTextAsync(Encoding.UTF8.GetBytes(text));
  40. public bool SendTextAsync(ReadOnlySpan<char> text) => SendTextAsync(Encoding.UTF8.GetBytes(text.ToArray()));
  41. public bool SendTextAsync(byte[] buffer) => SendTextAsync(buffer.AsSpan());
  42. public bool SendTextAsync(byte[] buffer, long offset, long size) => SendTextAsync(buffer.AsSpan((int)offset, (int)size));
  43. public bool SendTextAsync(ReadOnlySpan<byte> buffer)
  44. {
  45. lock (WebSocket.WsSendLock)
  46. {
  47. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_TEXT, false, buffer);
  48. return base.SendAsync(WebSocket.WsSendBuffer.AsSpan());
  49. }
  50. }
  51. #endregion
  52. #region WebSocket send binary methods
  53. public long SendBinary(string text) => SendBinary(Encoding.UTF8.GetBytes(text));
  54. public long SendBinary(ReadOnlySpan<char> text) => SendBinary(Encoding.UTF8.GetBytes(text.ToArray()));
  55. public long SendBinary(byte[] buffer) => SendBinary(buffer.AsSpan());
  56. public long SendBinary(byte[] buffer, long offset, long size) => SendBinary(buffer.AsSpan((int)offset, (int)size));
  57. public long SendBinary(ReadOnlySpan<byte> buffer)
  58. {
  59. lock (WebSocket.WsSendLock)
  60. {
  61. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_BINARY, false, buffer);
  62. return base.Send(WebSocket.WsSendBuffer.AsSpan());
  63. }
  64. }
  65. public bool SendBinaryAsync(string text) => SendBinaryAsync(Encoding.UTF8.GetBytes(text));
  66. public bool SendBinaryAsync(ReadOnlySpan<char> text) => SendBinaryAsync(Encoding.UTF8.GetBytes(text.ToArray()));
  67. public bool SendBinaryAsync(byte[] buffer) => SendBinaryAsync(buffer.AsSpan());
  68. public bool SendBinaryAsync(byte[] buffer, long offset, long size) => SendBinaryAsync(buffer.AsSpan((int)offset, (int)size));
  69. public bool SendBinaryAsync(ReadOnlySpan<byte> buffer)
  70. {
  71. lock (WebSocket.WsSendLock)
  72. {
  73. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_BINARY, false, buffer);
  74. return base.SendAsync(WebSocket.WsSendBuffer.AsSpan());
  75. }
  76. }
  77. #endregion
  78. #region WebSocket send close methods
  79. public long SendClose(int status, string text) => SendClose(status, Encoding.UTF8.GetBytes(text));
  80. public long SendClose(int status, ReadOnlySpan<char> text) => SendClose(status, Encoding.UTF8.GetBytes(text.ToArray()));
  81. public long SendClose(int status, byte[] buffer) => SendClose(status, buffer.AsSpan());
  82. public long SendClose(int status, byte[] buffer, long offset, long size) => SendClose(status, buffer.AsSpan((int)offset, (int)size));
  83. public long SendClose(int status, ReadOnlySpan<byte> buffer)
  84. {
  85. lock (WebSocket.WsSendLock)
  86. {
  87. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_CLOSE, false, buffer, status);
  88. return base.Send(WebSocket.WsSendBuffer.AsSpan());
  89. }
  90. }
  91. public bool SendCloseAsync(int status, string text) => SendCloseAsync(status, Encoding.UTF8.GetBytes(text));
  92. public bool SendCloseAsync(int status, ReadOnlySpan<char> text) => SendCloseAsync(status, Encoding.UTF8.GetBytes(text.ToArray()));
  93. public bool SendCloseAsync(int status, byte[] buffer) => SendCloseAsync(status, buffer.AsSpan());
  94. public bool SendCloseAsync(int status, byte[] buffer, long offset, long size) => SendCloseAsync(status, buffer.AsSpan((int)offset, (int)size));
  95. public bool SendCloseAsync(int status, ReadOnlySpan<byte> buffer)
  96. {
  97. lock (WebSocket.WsSendLock)
  98. {
  99. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_CLOSE, false, buffer, status);
  100. return base.SendAsync(WebSocket.WsSendBuffer.AsSpan());
  101. }
  102. }
  103. #endregion
  104. #region WebSocket send ping methods
  105. public long SendPing(string text) => SendPing(Encoding.UTF8.GetBytes(text));
  106. public long SendPing(ReadOnlySpan<char> text) => SendPing(Encoding.UTF8.GetBytes(text.ToArray()));
  107. public long SendPing(byte[] buffer) => SendPing(buffer.AsSpan());
  108. public long SendPing(byte[] buffer, long offset, long size) => SendPing(buffer.AsSpan((int)offset, (int)size));
  109. public long SendPing(ReadOnlySpan<byte> buffer)
  110. {
  111. lock (WebSocket.WsSendLock)
  112. {
  113. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PING, false, buffer);
  114. return base.Send(WebSocket.WsSendBuffer.AsSpan());
  115. }
  116. }
  117. public bool SendPingAsync(string text) => SendPingAsync(Encoding.UTF8.GetBytes(text));
  118. public bool SendPingAsync(ReadOnlySpan<char> text) => SendPingAsync(Encoding.UTF8.GetBytes(text.ToArray()));
  119. public bool SendPingAsync(byte[] buffer) => SendPingAsync(buffer.AsSpan());
  120. public bool SendPingAsync(byte[] buffer, long offset, long size) => SendPingAsync(buffer.AsSpan((int)offset, (int)size));
  121. public bool SendPingAsync(ReadOnlySpan<byte> buffer)
  122. {
  123. lock (WebSocket.WsSendLock)
  124. {
  125. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PING, false, buffer);
  126. return base.SendAsync(WebSocket.WsSendBuffer.AsSpan());
  127. }
  128. }
  129. #endregion
  130. #region WebSocket send pong methods
  131. public long SendPong(string text) => SendPong(Encoding.UTF8.GetBytes(text));
  132. public long SendPong(ReadOnlySpan<char> text) => SendPong(Encoding.UTF8.GetBytes(text.ToArray()));
  133. public long SendPong(byte[] buffer) => SendPong(buffer.AsSpan());
  134. public long SendPong(byte[] buffer, long offset, long size) => SendPong(buffer.AsSpan((int)offset, (int)size));
  135. public long SendPong(ReadOnlySpan<byte> buffer)
  136. {
  137. lock (WebSocket.WsSendLock)
  138. {
  139. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PONG, false, buffer);
  140. return base.Send(WebSocket.WsSendBuffer.AsSpan());
  141. }
  142. }
  143. public bool SendPongAsync(string text) => SendPongAsync(Encoding.UTF8.GetBytes(text));
  144. public bool SendPongAsync(ReadOnlySpan<char> text) => SendPongAsync(Encoding.UTF8.GetBytes(text.ToArray()));
  145. public bool SendPongAsync(byte[] buffer) => SendPongAsync(buffer.AsSpan());
  146. public bool SendPongAsync(byte[] buffer, long offset, long size) => SendPongAsync(buffer.AsSpan((int)offset, (int)size));
  147. public bool SendPongAsync(ReadOnlySpan<byte> buffer)
  148. {
  149. lock (WebSocket.WsSendLock)
  150. {
  151. WebSocket.PrepareSendFrame(WebSocket.WS_FIN | WebSocket.WS_PONG, false, buffer);
  152. return base.SendAsync(WebSocket.WsSendBuffer.AsSpan());
  153. }
  154. }
  155. #endregion
  156. #region WebSocket receive methods
  157. public string ReceiveText()
  158. {
  159. Buffer result = new Buffer();
  160. if (!WebSocket.WsHandshaked)
  161. return result.ExtractString(0, result.Data.Length);
  162. Buffer cache = new Buffer();
  163. // Receive WebSocket frame data
  164. while (!WebSocket.WsFinalReceived)
  165. {
  166. while (!WebSocket.WsFrameReceived)
  167. {
  168. long required = WebSocket.RequiredReceiveFrameSize();
  169. cache.Resize(required);
  170. long received = base.Receive(cache.Data, 0, required);
  171. if (received != required)
  172. return result.ExtractString(0, result.Data.Length);
  173. WebSocket.PrepareReceiveFrame(cache.Data, 0, received);
  174. }
  175. if (!WebSocket.WsFinalReceived)
  176. WebSocket.PrepareReceiveFrame(null, 0, 0);
  177. }
  178. // Copy WebSocket frame data
  179. result.Append(WebSocket.WsReceiveFinalBuffer);
  180. WebSocket.PrepareReceiveFrame(null, 0, 0);
  181. return result.ExtractString(0, result.Data.Length);
  182. }
  183. public Buffer ReceiveBinary()
  184. {
  185. Buffer result = new Buffer();
  186. if (!WebSocket.WsHandshaked)
  187. return result;
  188. Buffer cache = new Buffer();
  189. // Receive WebSocket frame data
  190. while (!WebSocket.WsFinalReceived)
  191. {
  192. while (!WebSocket.WsFrameReceived)
  193. {
  194. long required = WebSocket.RequiredReceiveFrameSize();
  195. cache.Resize(required);
  196. long received = base.Receive(cache.Data, 0, required);
  197. if (received != required)
  198. return result;
  199. WebSocket.PrepareReceiveFrame(cache.Data, 0, received);
  200. }
  201. if (!WebSocket.WsFinalReceived)
  202. WebSocket.PrepareReceiveFrame(null, 0, 0);
  203. }
  204. // Copy WebSocket frame data
  205. result.Append(WebSocket.WsReceiveFinalBuffer);
  206. WebSocket.PrepareReceiveFrame(null, 0, 0);
  207. return result;
  208. }
  209. #endregion
  210. #region Session handlers
  211. protected override void OnDisconnecting()
  212. {
  213. if (WebSocket.WsHandshaked)
  214. OnWsDisconnecting();
  215. }
  216. protected override void OnDisconnected()
  217. {
  218. // Disconnect WebSocket
  219. if (WebSocket.WsHandshaked)
  220. {
  221. WebSocket.WsHandshaked = false;
  222. OnWsDisconnected();
  223. }
  224. // Reset WebSocket upgrade HTTP request and response
  225. Request.Clear();
  226. Response.Clear();
  227. // Clear WebSocket send/receive buffers
  228. WebSocket.ClearWsBuffers();
  229. // Initialize new WebSocket random nonce
  230. WebSocket.InitWsNonce();
  231. }
  232. protected override void OnReceived(byte[] buffer, long offset, long size)
  233. {
  234. // Check for WebSocket handshaked status
  235. if (WebSocket.WsHandshaked)
  236. {
  237. // Prepare receive frame
  238. WebSocket.PrepareReceiveFrame(buffer, offset, size);
  239. return;
  240. }
  241. base.OnReceived(buffer, offset, size);
  242. }
  243. protected override void OnReceivedRequestHeader(HttpRequest request)
  244. {
  245. // Check for WebSocket handshaked status
  246. if (WebSocket.WsHandshaked)
  247. return;
  248. // Try to perform WebSocket upgrade
  249. if (!WebSocket.PerformServerUpgrade(request, Response))
  250. {
  251. base.OnReceivedRequestHeader(request);
  252. return;
  253. }
  254. }
  255. protected override void OnReceivedRequest(HttpRequest request)
  256. {
  257. // Check for WebSocket handshaked status
  258. if (WebSocket.WsHandshaked)
  259. {
  260. // Prepare receive frame from the remaining request body
  261. var body = Request.Body;
  262. var data = Encoding.UTF8.GetBytes(body);
  263. WebSocket.PrepareReceiveFrame(data, 0, data.Length);
  264. return;
  265. }
  266. base.OnReceivedRequest(request);
  267. }
  268. protected override void OnReceivedRequestError(HttpRequest request, string error)
  269. {
  270. // Check for WebSocket handshaked status
  271. if (WebSocket.WsHandshaked)
  272. {
  273. OnError(new SocketError());
  274. return;
  275. }
  276. base.OnReceivedRequestError(request, error);
  277. }
  278. #endregion
  279. #region Web socket handlers
  280. public virtual void OnWsConnecting(HttpRequest request) {}
  281. public virtual void OnWsConnected(HttpResponse response) {}
  282. public virtual bool OnWsConnecting(HttpRequest request, HttpResponse response) { return true; }
  283. public virtual void OnWsConnected(HttpRequest request) {}
  284. public virtual void OnWsDisconnecting() {}
  285. public virtual void OnWsDisconnected() {}
  286. public virtual void OnWsReceived(byte[] buffer, long offset, long size) {}
  287. public virtual void OnWsClose(byte[] buffer, long offset, long size, int status = 1000) { Close(); }
  288. public virtual void OnWsPing(byte[] buffer, long offset, long size) { SendPongAsync(buffer, offset, size); }
  289. public virtual void OnWsPong(byte[] buffer, long offset, long size) {}
  290. public virtual void OnWsError(string error) { OnError(SocketError.SocketError); }
  291. public virtual void OnWsError(SocketError error) { OnError(error); }
  292. public void SendUpgrade(HttpResponse response) { SendResponseAsync(response); }
  293. #endregion
  294. }
  295. }