4 using System.Threading;
5 using System.Threading.Tasks;
6 using System.Collections.Generic;
7 using System.Net.WebSockets;
8 using System.Reflection;
11 using NUnit.Framework;
14 namespace MonoTests.System.Net.WebSockets
17 public class ClientWebSocketTest
19 const string EchoServerUrl = "ws://echo.websocket.org";
20 const int Port = 42123;
21 HttpListener listener;
22 ClientWebSocket socket;
23 MethodInfo headerSetMethod;
28 listener = new HttpListener ();
29 listener.Prefixes.Add ("http://localhost:" + Port + "/");
31 socket = new ClientWebSocket ();
35 public void Teardown ()
37 if (listener != null) {
42 if (socket.State == WebSocketState.Open)
43 socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait ();
50 public void ServerHandshakeReturnCrapStatusCodeTest ()
52 HandleHttpRequestAsync ((req, resp) => resp.StatusCode = 418);
54 socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait ();
55 } catch (AggregateException e) {
56 AssertWebSocketException (e, WebSocketError.Success, typeof (WebException));
59 Assert.Fail ("Should have thrown");
63 public void ServerHandshakeReturnWrongUpgradeHeader ()
65 HandleHttpRequestAsync ((req, resp) => {
66 resp.StatusCode = 101;
67 resp.Headers["Upgrade"] = "gtfo";
70 socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait ();
71 } catch (AggregateException e) {
72 AssertWebSocketException (e, WebSocketError.Success);
75 Assert.Fail ("Should have thrown");
79 public void ServerHandshakeReturnWrongConnectionHeader ()
81 HandleHttpRequestAsync ((req, resp) => {
82 resp.StatusCode = 101;
83 resp.Headers["Upgrade"] = "websocket";
84 // Mono http request doesn't like the forcing, test still valid since the default connection header value is empty
85 //ForceSetHeader (resp.Headers, "Connection", "Foo");
88 socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait ();
89 } catch (AggregateException e) {
90 AssertWebSocketException (e, WebSocketError.Success);
93 Assert.Fail ("Should have thrown");
97 public void EchoTest ()
99 const string Payload = "This is a websocket test";
101 Assert.AreEqual (WebSocketState.None, socket.State);
102 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
103 Assert.AreEqual (WebSocketState.Open, socket.State);
105 var sendBuffer = Encoding.ASCII.GetBytes (Payload);
106 socket.SendAsync (new ArraySegment<byte> (sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None).Wait ();
108 var receiveBuffer = new byte[Payload.Length];
109 var resp = socket.ReceiveAsync (new ArraySegment<byte> (receiveBuffer), CancellationToken.None).Result;
111 Assert.AreEqual (Payload.Length, resp.Count);
112 Assert.IsTrue (resp.EndOfMessage);
113 Assert.AreEqual (WebSocketMessageType.Text, resp.MessageType);
114 Assert.AreEqual (Payload, Encoding.ASCII.GetString (receiveBuffer, 0, resp.Count));
116 socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait ();
117 Assert.AreEqual (WebSocketState.Closed, socket.State);
121 public void CloseOutputAsyncTest ()
123 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
124 Assert.AreEqual (WebSocketState.Open, socket.State);
126 socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait ();
127 Assert.AreEqual (WebSocketState.CloseSent, socket.State);
129 var resp = socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None).Result;
130 Assert.AreEqual (WebSocketState.Closed, socket.State);
131 Assert.AreEqual (WebSocketMessageType.Close, resp.MessageType);
132 Assert.AreEqual (WebSocketCloseStatus.NormalClosure, resp.CloseStatus);
133 Assert.AreEqual (string.Empty, resp.CloseStatusDescription);
137 public void CloseAsyncTest ()
139 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
140 Assert.AreEqual (WebSocketState.Open, socket.State);
142 socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait ();
143 Assert.AreEqual (WebSocketState.Closed, socket.State);
146 [Test, ExpectedException (typeof (InvalidOperationException))]
147 public void SendAsyncArgTest_NotConnected ()
149 socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None);
152 [Test, ExpectedException (typeof (ArgumentNullException))]
153 public void SendAsyncArgTest_NoArray ()
155 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
156 socket.SendAsync (new ArraySegment<byte> (), WebSocketMessageType.Text, true, CancellationToken.None);
159 [Test, ExpectedException (typeof (InvalidOperationException))]
160 public void ReceiveAsyncArgTest_NotConnected ()
162 socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None);
165 [Test, ExpectedException (typeof (ArgumentNullException))]
166 public void ReceiveAsyncArgTest_NoArray ()
168 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
169 socket.ReceiveAsync (new ArraySegment<byte> (), CancellationToken.None);
173 public void ReceiveAsyncWrongState_Closed ()
176 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
177 socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait ();
178 socket.ReceiveAsync (new ArraySegment<byte> (new byte[0]), CancellationToken.None).Wait ();
179 } catch (AggregateException e) {
180 AssertWebSocketException (e, WebSocketError.Success);
183 Assert.Fail ("Should have thrown");
187 public void SendAsyncWrongState_Closed ()
190 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
191 socket.CloseAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait ();
192 socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None).Wait ();
193 } catch (AggregateException e) {
194 AssertWebSocketException (e, WebSocketError.Success);
197 Assert.Fail ("Should have thrown");
201 public void SendAsyncWrongState_CloseSent ()
204 socket.ConnectAsync (new Uri (EchoServerUrl), CancellationToken.None).Wait ();
205 socket.CloseOutputAsync (WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).Wait ();
206 socket.SendAsync (new ArraySegment<byte> (new byte[0]), WebSocketMessageType.Text, true, CancellationToken.None).Wait ();
207 } catch (AggregateException e) {
208 AssertWebSocketException (e, WebSocketError.Success);
211 Assert.Fail ("Should have thrown");
214 async Task HandleHttpRequestAsync (Action<HttpListenerRequest, HttpListenerResponse> handler)
216 var ctx = await listener.GetContextAsync ();
217 handler (ctx.Request, ctx.Response);
218 ctx.Response.Close ();
221 void AssertWebSocketException (AggregateException e, WebSocketError error, Type inner = null)
223 var wsEx = e.InnerException as WebSocketException;
224 Console.WriteLine (e.InnerException.ToString ());
225 Assert.IsNotNull (wsEx, "Not a websocketexception");
226 Assert.AreEqual (error, wsEx.WebSocketErrorCode);
228 Assert.IsNotNull (wsEx.InnerException);
229 Assert.IsInstanceOfType (inner, wsEx.InnerException);
233 void ForceSetHeader (WebHeaderCollection headers, string name, string value)
235 if (headerSetMethod == null)
236 headerSetMethod = typeof (WebHeaderCollection).GetMethod ("AddValue", BindingFlags.NonPublic);
237 headerSetMethod.Invoke (headers, new[] { name, value });