ClientWebSocketTest.cs 11 KB

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