| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- using System;
- using System.Net;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Collections.Generic;
- using System.Net.WebSockets;
- using System.Reflection;
- using System.Text;
- using NUnit.Framework;
- using MonoTests.Helpers;
- namespace MonoTests.System.Net.WebSockets
- {
- [TestFixture]
- public class ClientWebSocketTest
- {
- const string EchoServerUrl = "ws://corefx-net.cloudapp.net/WebSocket/EchoWebSocket.ashx";
- int Port = NetworkHelpers.FindFreePort ();
- HttpListener _listener;
- HttpListener listener {
- get {
- if (_listener != null)
- return _listener;
- var tmp = new HttpListener ();
- tmp.Prefixes.Add ("http://localhost:" + Port + "/");
- tmp.Start ();
- return _listener = tmp;
- }
- }
- ClientWebSocket _socket;
- ClientWebSocket socket { get { return _socket ?? (_socket = new ClientWebSocket ()); } }
- MethodInfo headerSetMethod;
- [TearDown]
- public void Teardown ()
- {
- if (_listener != null) {
- _listener.Stop ();
- _listener = null;
- }
- if (_socket != null) {
- if (_socket.State == WebSocketState.Open)
- _socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (2000);
- _socket.Dispose ();
- _socket = null;
- }
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void ServerHandshakeReturnCrapStatusCodeTest ()
- {
- // On purpose,
- #pragma warning disable 4014
- HandleHttpRequestAsync ((req, resp) => resp.StatusCode = 418);
- #pragma warning restore 4014
- try {
- Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
- } catch (AggregateException e) {
- AssertWebSocketException (e, WebSocketError.Success, typeof (WebException));
- return;
- }
- Assert.Fail ("Should have thrown");
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void ServerHandshakeReturnWrongUpgradeHeader ()
- {
- #pragma warning disable 4014
- HandleHttpRequestAsync ((req, resp) => {
- resp.StatusCode = 101;
- resp.Headers["Upgrade"] = "gtfo";
- });
- #pragma warning restore 4014
- try {
- Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
- } catch (AggregateException e) {
- AssertWebSocketException (e, WebSocketError.Success);
- return;
- }
- Assert.Fail ("Should have thrown");
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void ServerHandshakeReturnWrongConnectionHeader ()
- {
- #pragma warning disable 4014
- HandleHttpRequestAsync ((req, resp) => {
- resp.StatusCode = 101;
- resp.Headers["Upgrade"] = "websocket";
- // Mono http request doesn't like the forcing, test still valid since the default connection header value is empty
- //ForceSetHeader (resp.Headers, "Connection", "Foo");
- });
- #pragma warning restore 4014
- try {
- Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
- } catch (AggregateException e) {
- AssertWebSocketException (e, WebSocketError.Success);
- return;
- }
- Assert.Fail ("Should have thrown");
- }
- [Test]
- [Category ("MobileNotWorking")] // The test hangs when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void EchoTest ()
- {
- const string Payload = "This is a websocket test";
- Assert.AreEqual (WebSocketState.None, socket.State);
- socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
- Assert.AreEqual (WebSocketState.Open, socket.State);
- var sendBuffer = Encoding.ASCII.GetBytes (Payload);
- Assert.IsTrue (socket.SendAsync (new ArraySegment<byte> (sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None).Wait (5000));
- var receiveBuffer = new byte[Payload.Length];
- var resp = socket.ReceiveAsync (new ArraySegment<byte> (receiveBuffer), CancellationToken.None).Result;
- Assert.AreEqual (Payload.Length, resp.Count);
- Assert.IsTrue (resp.EndOfMessage);
- Assert.AreEqual (WebSocketMessageType.Text, resp.MessageType);
- Assert.AreEqual (Payload, Encoding.ASCII.GetString (receiveBuffer, 0, resp.Count));
- Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
- Assert.AreEqual (WebSocketState.Closed, socket.State);
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void CloseOutputAsyncTest ()
- {
- Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
- Assert.AreEqual (WebSocketState.Open, socket.State);
- Assert.IsTrue (socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
- Assert.AreEqual (WebSocketState.CloseSent, socket.State);
- var resp = socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None).Result;
- Assert.AreEqual (WebSocketState.Closed, socket.State);
- Assert.AreEqual (WebSocketMessageType.Close, resp.MessageType);
- Assert.AreEqual (WebSocketCloseStatus.NormalClosure, resp.CloseStatus);
- Assert.AreEqual (string.Empty, resp.CloseStatusDescription);
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void CloseAsyncTest ()
- {
- Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
- Assert.AreEqual (WebSocketState.Open, socket.State);
- Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
- Assert.AreEqual (WebSocketState.Closed, socket.State);
- }
- [Test, ExpectedException (typeof (InvalidOperationException))]
- public void SendAsyncArgTest_NotConnected ()
- {
- socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None);
- }
- [Test, ExpectedException (typeof (ArgumentNullException))]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void SendAsyncArgTest_NoArray ()
- {
- Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
- socket.SendAsync (new ArraySegment<byte> (), WebSocketMessageType.Text, true, CancellationToken.None);
- }
- [Test, ExpectedException (typeof (InvalidOperationException))]
- public void ReceiveAsyncArgTest_NotConnected ()
- {
- socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None);
- }
- [Test, ExpectedException (typeof (ArgumentNullException))]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void ReceiveAsyncArgTest_NoArray ()
- {
- Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
- socket.ReceiveAsync (new ArraySegment<byte> (), CancellationToken.None);
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void ReceiveAsyncWrongState_Closed ()
- {
- try {
- Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
- Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
- Assert.IsTrue (socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None).Wait (5000));
- } catch (AggregateException e) {
- AssertWebSocketException (e, WebSocketError.Success);
- return;
- }
- Assert.Fail ("Should have thrown");
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void SendAsyncWrongState_Closed ()
- {
- try {
- Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
- Assert.IsTrue (socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
- Assert.IsTrue (socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None).Wait (5000));
- } catch (AggregateException e) {
- AssertWebSocketException (e, WebSocketError.Success);
- return;
- }
- Assert.Fail ("Should have thrown");
- }
- [Test]
- [Category ("MobileNotWorking")] // Fails when ran as part of the entire BCL test suite. Works when only this fixture is ran
- public void SendAsyncWrongState_CloseSent ()
- {
- try {
- Assert.IsTrue (socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait (5000));
- Assert.IsTrue (socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait (5000));
- Assert.IsTrue (socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None).Wait (5000));
- } catch (AggregateException e) {
- AssertWebSocketException (e, WebSocketError.Success);
- return;
- }
- Assert.Fail ("Should have thrown");
- }
- [Test]
- [Category ("NotWorking")] // FIXME: test relies on unimplemented HttpListenerContext.AcceptWebSocketAsync (), reenable it when the method is implemented
- public void SendAsyncEndOfMessageTest ()
- {
- var cancellationToken = new CancellationTokenSource (TimeSpan.FromSeconds (30)).Token;
- SendAsyncEndOfMessageTest (false, WebSocketMessageType.Text, cancellationToken).Wait (5000);
- SendAsyncEndOfMessageTest (true, WebSocketMessageType.Text, cancellationToken).Wait (5000);
- SendAsyncEndOfMessageTest (false, WebSocketMessageType.Binary, cancellationToken).Wait (5000);
- SendAsyncEndOfMessageTest (true, WebSocketMessageType.Binary, cancellationToken).Wait (5000);
- }
- public async Task SendAsyncEndOfMessageTest (bool expectedEndOfMessage, WebSocketMessageType webSocketMessageType, CancellationToken cancellationToken)
- {
- using (var client = new ClientWebSocket ()) {
- // Configure the listener.
- var serverReceive = HandleHttpWebSocketRequestAsync<WebSocketReceiveResult> (async socket => await socket.ReceiveAsync (new ArraySegment<byte> (new byte[32]), cancellationToken), cancellationToken);
- // Connect to the listener and make the request.
- await client.ConnectAsync (new Uri ("ws://localhost:" + Port + "/"), cancellationToken);
- await client.SendAsync (new ArraySegment<byte> (Encoding.UTF8.GetBytes ("test")), webSocketMessageType, expectedEndOfMessage, cancellationToken);
- // Wait for the listener to handle the request and return its result.
- var result = await serverReceive;
- // Cleanup and check results.
- await client.CloseAsync (WebSocketCloseStatus.NormalClosure, "Finished", cancellationToken);
- Assert.AreEqual (expectedEndOfMessage, result.EndOfMessage, "EndOfMessage should be " + expectedEndOfMessage);
- }
- }
- async Task<T> HandleHttpWebSocketRequestAsync<T> (Func<WebSocket, Task<T>> action, CancellationToken cancellationToken)
- {
- var ctx = await this.listener.GetContextAsync ();
- var wsContext = await ctx.AcceptWebSocketAsync (null);
- var result = await action (wsContext.WebSocket);
- await wsContext.WebSocket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, "Finished", cancellationToken);
- return result;
- }
- async Task HandleHttpRequestAsync (Action<HttpListenerRequest, HttpListenerResponse> handler)
- {
- var ctx = await listener.GetContextAsync ();
- handler (ctx.Request, ctx.Response);
- ctx.Response.Close ();
- }
- void AssertWebSocketException (AggregateException e, WebSocketError error, Type inner = null)
- {
- var wsEx = e.InnerException as WebSocketException;
- Console.WriteLine (e.InnerException.ToString ());
- Assert.IsNotNull (wsEx, "Not a websocketexception");
- Assert.AreEqual (error, wsEx.WebSocketErrorCode);
- if (inner != null) {
- Assert.IsNotNull (wsEx.InnerException);
- Assert.IsTrue (inner.IsInstanceOfType (wsEx.InnerException));
- }
- }
- void ForceSetHeader (WebHeaderCollection headers, string name, string value)
- {
- if (headerSetMethod == null)
- headerSetMethod = typeof (WebHeaderCollection).GetMethod ("AddValue", BindingFlags.NonPublic);
- headerSetMethod.Invoke (headers, new[] { name, value });
- }
- }
- }
|