ClientWebSocketTest.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. #if NET_4_5
  2. using System;
  3. using System.Net;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using System.Collections.Generic;
  7. using System.Net.WebSockets;
  8. using System.Reflection;
  9. using System.Text;
  10. using NUnit.Framework;
  11. namespace MonoTests.System.Net.WebSockets
  12. {
  13. [TestFixture]
  14. public class ClientWebSocketTest
  15. {
  16. const string EchoServerUrl = "ws://echo.websocket.org";
  17. const int Port = 42123;
  18. HttpListener listener;
  19. ClientWebSocket socket;
  20. MethodInfo headerSetMethod;
  21. [SetUp]
  22. public void Setup ()
  23. {
  24. listener = new HttpListener ();
  25. listener.Prefixes.Add ("http://localhost:" + Port + "/");
  26. listener.Start ();
  27. socket = new ClientWebSocket ();
  28. }
  29. [TearDown]
  30. public void Teardown ()
  31. {
  32. if (listener != null) {
  33. listener.Stop ();
  34. listener = null;
  35. }
  36. if (socket != null) {
  37. if (socket.State == WebSocketState.Open)
  38. socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (2000);
  39. socket.Dispose ();
  40. socket = null;
  41. }
  42. }
  43. [Test]
  44. public void ServerHandshakeReturnCrapStatusCodeTest ()
  45. {
  46. // On purpose,
  47. #pragma warning disable 4014
  48. HandleHttpRequestAsync ((req, resp) => resp.StatusCode = 418);
  49. #pragma warning restore 4014
  50. try {
  51. Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
  52. } catch (AggregateException e) {
  53. AssertWebSocketException (e, WebSocketError.Success, typeof (WebException));
  54. return;
  55. }
  56. Assert.Fail ("Should have thrown");
  57. }
  58. [Test]
  59. public void ServerHandshakeReturnWrongUpgradeHeader ()
  60. {
  61. #pragma warning disable 4014
  62. HandleHttpRequestAsync ((req, resp) => {
  63. resp.StatusCode = 101;
  64. resp.Headers["Upgrade"] = "gtfo";
  65. });
  66. #pragma warning restore 4014
  67. try {
  68. Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
  69. } catch (AggregateException e) {
  70. AssertWebSocketException (e, WebSocketError.Success);
  71. return;
  72. }
  73. Assert.Fail ("Should have thrown");
  74. }
  75. [Test]
  76. public void ServerHandshakeReturnWrongConnectionHeader ()
  77. {
  78. #pragma warning disable 4014
  79. HandleHttpRequestAsync ((req, resp) => {
  80. resp.StatusCode = 101;
  81. resp.Headers["Upgrade"] = "websocket";
  82. // Mono http request doesn't like the forcing, test still valid since the default connection header value is empty
  83. //ForceSetHeader (resp.Headers, "Connection", "Foo");
  84. });
  85. #pragma warning restore 4014
  86. try {
  87. Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
  88. } catch (AggregateException e) {
  89. AssertWebSocketException (e, WebSocketError.Success);
  90. return;
  91. }
  92. Assert.Fail ("Should have thrown");
  93. }
  94. [Test]
  95. public void EchoTest ()
  96. {
  97. const string Payload = "This is a websocket test";
  98. Assert.AreEqual (WebSocketState.None, socket.State);
  99. socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
  100. Assert.AreEqual (WebSocketState.Open, socket.State);
  101. var sendBuffer = Encoding.ASCII.GetBytes (Payload);
  102. Assert.IsTrue (socket.SendAsync (new ArraySegment<byte> (sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None).Wait (5000));
  103. var receiveBuffer = new byte[Payload.Length];
  104. var resp = socket.ReceiveAsync (new ArraySegment<byte> (receiveBuffer), CancellationToken.None).Result;
  105. Assert.AreEqual (Payload.Length, resp.Count);
  106. Assert.IsTrue (resp.EndOfMessage);
  107. Assert.AreEqual (WebSocketMessageType.Text, resp.MessageType);
  108. Assert.AreEqual (Payload, Encoding.ASCII.GetString (receiveBuffer, 0, resp.Count));
  109. Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
  110. Assert.AreEqual (WebSocketState.Closed, socket.State);
  111. }
  112. [Test]
  113. public void CloseOutputAsyncTest ()
  114. {
  115. Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
  116. Assert.AreEqual (WebSocketState.Open, socket.State);
  117. Assert.IsTrue (socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
  118. Assert.AreEqual (WebSocketState.CloseSent, socket.State);
  119. var resp = socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None).Result;
  120. Assert.AreEqual (WebSocketState.Closed, socket.State);
  121. Assert.AreEqual (WebSocketMessageType.Close, resp.MessageType);
  122. Assert.AreEqual (WebSocketCloseStatus.NormalClosure, resp.CloseStatus);
  123. Assert.AreEqual (string.Empty, resp.CloseStatusDescription);
  124. }
  125. [Test]
  126. public void CloseAsyncTest ()
  127. {
  128. Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
  129. Assert.AreEqual (WebSocketState.Open, socket.State);
  130. Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
  131. Assert.AreEqual (WebSocketState.Closed, socket.State);
  132. }
  133. [Test, ExpectedException (typeof (InvalidOperationException))]
  134. public void SendAsyncArgTest_NotConnected ()
  135. {
  136. socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None);
  137. }
  138. [Test, ExpectedException (typeof (ArgumentNullException))]
  139. public void SendAsyncArgTest_NoArray ()
  140. {
  141. Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
  142. socket.SendAsync (new ArraySegment<byte> (), WebSocketMessageType.Text, true, CancellationToken.None);
  143. }
  144. [Test, ExpectedException (typeof (InvalidOperationException))]
  145. public void ReceiveAsyncArgTest_NotConnected ()
  146. {
  147. socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None);
  148. }
  149. [Test, ExpectedException (typeof (ArgumentNullException))]
  150. public void ReceiveAsyncArgTest_NoArray ()
  151. {
  152. Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
  153. socket.ReceiveAsync (new ArraySegment<byte> (), CancellationToken.None);
  154. }
  155. [Test]
  156. public void ReceiveAsyncWrongState_Closed ()
  157. {
  158. try {
  159. Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
  160. Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
  161. Assert.IsTrue (socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None).Wait (5000));
  162. } catch (AggregateException e) {
  163. AssertWebSocketException (e, WebSocketError.Success);
  164. return;
  165. }
  166. Assert.Fail ("Should have thrown");
  167. }
  168. [Test]
  169. public void SendAsyncWrongState_Closed ()
  170. {
  171. try {
  172. Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
  173. Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
  174. Assert.IsTrue (socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None).Wait (5000));
  175. } catch (AggregateException e) {
  176. AssertWebSocketException (e, WebSocketError.Success);
  177. return;
  178. }
  179. Assert.Fail ("Should have thrown");
  180. }
  181. [Test]
  182. public void SendAsyncWrongState_CloseSent ()
  183. {
  184. try {
  185. Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
  186. Assert.IsTrue (socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
  187. Assert.IsTrue (socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None).Wait (5000));
  188. } catch (AggregateException e) {
  189. AssertWebSocketException (e, WebSocketError.Success);
  190. return;
  191. }
  192. Assert.Fail ("Should have thrown");
  193. }
  194. [Test]
  195. [Category ("NotWorking")] // FIXME: test relies on unimplemented HttpListenerContext.AcceptWebSocketAsync (), reenable it when the method is implemented
  196. public void SendAsyncEndOfMessageTest ()
  197. {
  198. var cancellationToken = new CancellationTokenSource (TimeSpan.FromSeconds (30)).Token;
  199. SendAsyncEndOfMessageTest (false, WebSocketMessageType.Text, cancellationToken).Wait (5000);
  200. SendAsyncEndOfMessageTest (true, WebSocketMessageType.Text, cancellationToken).Wait (5000);
  201. SendAsyncEndOfMessageTest (false, WebSocketMessageType.Binary, cancellationToken).Wait (5000);
  202. SendAsyncEndOfMessageTest (true, WebSocketMessageType.Binary, cancellationToken).Wait (5000);
  203. }
  204. public async Task SendAsyncEndOfMessageTest (bool expectedEndOfMessage, WebSocketMessageType webSocketMessageType, CancellationToken cancellationToken)
  205. {
  206. using (var client = new ClientWebSocket ()) {
  207. // Configure the listener.
  208. var serverReceive = HandleHttpWebSocketRequestAsync<WebSocketReceiveResult> (async socket => await socket.ReceiveAsync (new ArraySegment<byte> (new byte[32]), cancellationToken), cancellationToken);
  209. // Connect to the listener and make the request.
  210. await client.ConnectAsync (new Uri ("ws://localhost:" + Port + "/"), cancellationToken);
  211. await client.SendAsync (new ArraySegment<byte> (Encoding.UTF8.GetBytes ("test")), webSocketMessageType, expectedEndOfMessage, cancellationToken);
  212. // Wait for the listener to handle the request and return its result.
  213. var result = await serverReceive;
  214. // Cleanup and check results.
  215. await client.CloseAsync (WebSocketCloseStatus.NormalClosure, "Finished", cancellationToken);
  216. Assert.AreEqual (expectedEndOfMessage, result.EndOfMessage, "EndOfMessage should be " + expectedEndOfMessage);
  217. }
  218. }
  219. async Task<T> HandleHttpWebSocketRequestAsync<T> (Func<WebSocket, Task<T>> action, CancellationToken cancellationToken)
  220. {
  221. var ctx = await this.listener.GetContextAsync ();
  222. var wsContext = await ctx.AcceptWebSocketAsync (null);
  223. var result = await action (wsContext.WebSocket);
  224. await wsContext.WebSocket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, "Finished", cancellationToken);
  225. return result;
  226. }
  227. async Task HandleHttpRequestAsync (Action<HttpListenerRequest, HttpListenerResponse> handler)
  228. {
  229. var ctx = await listener.GetContextAsync ();
  230. handler (ctx.Request, ctx.Response);
  231. ctx.Response.Close ();
  232. }
  233. void AssertWebSocketException (AggregateException e, WebSocketError error, Type inner = null)
  234. {
  235. var wsEx = e.InnerException as WebSocketException;
  236. Console.WriteLine (e.InnerException.ToString ());
  237. Assert.IsNotNull (wsEx, "Not a websocketexception");
  238. Assert.AreEqual (error, wsEx.WebSocketErrorCode);
  239. if (inner != null) {
  240. Assert.IsNotNull (wsEx.InnerException);
  241. Assert.IsTrue (inner.IsInstanceOfType (wsEx.InnerException));
  242. }
  243. }
  244. void ForceSetHeader (WebHeaderCollection headers, string name, string value)
  245. {
  246. if (headerSetMethod == null)
  247. headerSetMethod = typeof (WebHeaderCollection).GetMethod ("AddValue", BindingFlags.NonPublic);
  248. headerSetMethod.Invoke (headers, new[] { name, value });
  249. }
  250. }
  251. }
  252. #endif