private async Task <WebSocketNegotiationResult> NegotiateWebSocket(Socket client) { var result = new WebSocketNegotiationResult(); try { var timeoutTask = Task.Delay(_options.NegotiationTimeout); ConfigureSocket(client); Stream stream = new NetworkStream(client, FileAccess.ReadWrite, true); foreach (var conExt in ConnectionExtensions) { var extTask = conExt.ExtendConnectionAsync(stream); await Task.WhenAny(timeoutTask, extTask).ConfigureAwait(false); if (timeoutTask.IsCompleted) { throw new WebSocketException("Negotiation timeout (Extension: " + conExt.GetType().Name + ")"); } stream = await extTask; } var handshakeTask = _handShaker.HandshakeAsync(stream); await Task.WhenAny(timeoutTask, handshakeTask).ConfigureAwait(false); if (timeoutTask.IsCompleted) { throw new WebSocketException("Negotiation timeout"); } var handshake = await handshakeTask; if (handshake.IsValid) { result.Result = handshake.Factory.CreateWebSocket(stream, _options, (IPEndPoint)client.LocalEndPoint, (IPEndPoint)client.RemoteEndPoint, handshake.Request, handshake.Response, handshake.NegotiatedMessageExtensions); } else { result.Error = handshake.Error; } } catch (Exception ex) { FinishSocket(client); result.Error = ExceptionDispatchInfo.Capture(ex); result.Result = null; } return(result); }
public void WebSocketHandshaker_CanDoSimpleHandshake() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions()); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII,1024,true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5;"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); Assert.IsTrue(result.IsWebSocketRequest); Assert.IsTrue(result.IsVersionSupported); Assert.AreEqual(new Uri("http://example.com"), result.Request.Headers.Origin); Assert.AreEqual("server.example.com", result.Request.Headers[HttpRequestHeader.Host]); Assert.AreEqual(@"/chat", result.Request.RequestUri.ToString()); Assert.AreEqual(1, result.Request.Cookies.Count); var cookie = result.Request.Cookies.GetCookies(result.Request.Headers.Host)[0]; Assert.AreEqual("key", cookie.Name); Assert.AreEqual(@"W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5", cookie.Value); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 101 Switching Protocols"); sb.AppendLine(@"Upgrade: websocket"); sb.AppendLine(@"Connection: Upgrade"); sb.AppendLine(@"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk="); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }
public void WebSocketHandshaker_CanDetectReturnCookieErrors() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions() { OnHttpNegotiation = (req,res) => { throw new Exception("dummy"); } }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5;"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); Assert.IsFalse(result.IsValid); Assert.IsNotNull(result.Error); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 500 Internal Server Error"); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }
public void WebSocketHandshaker_CanIndicateANonSupportedVersion() { var extension = new Mock<IWebSocketMessageExtension>(); extension.Setup(x => x.Name).Returns("test-extension"); WebSocketExtension ext = new WebSocketExtension("test-extension", new List<WebSocketExtensionOption>(new[] { new WebSocketExtensionOption() { ClientAvailableOption = false, Name = "optionA" } })); IWebSocketMessageExtensionContext ctx; extension.Setup(x => x.TryNegotiate(It.IsAny<WebSocketHttpRequest>(), out ext, out ctx)) .Returns(true); var factory = new WebSocketFactoryRfc6455(); factory.MessageExtensions.RegisterExtension(extension.Object); var factories = new WebSocketFactoryCollection(); factories.RegisterStandard(factory); WebSocketHandshaker handshaker = new WebSocketHandshaker(factories, new WebSocketListenerOptions() { SubProtocols = new[] { "superchat" } }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5;"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Protocol: chat, superchat"); sw.WriteLine(@"Sec-WebSocket-Extensions: test-extension;optionA"); sw.WriteLine(@"Sec-WebSocket-Version: 14"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); Assert.IsTrue(result.IsWebSocketRequest); Assert.IsFalse(result.IsVersionSupported); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 426 Upgrade Required"); sb.AppendLine(@"Sec-WebSocket-Version: 13"); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }
public void WebSocketHandshaker_FailWhenSubProtocolRequestedButNotOffered() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions()); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5;"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Protocol: chat, superchat"); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); Assert.IsTrue(result.IsWebSocketRequest); Assert.IsTrue(result.IsVersionSupported); Assert.IsNotNull(result.Error); Assert.IsFalse(result.IsValid); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 404 Bad Request"); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }
public void WebSocketHandshaker_CanNegotiateAndIgnoreAnExtension() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions() { SubProtocols = new[] { "superchat" } }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5;"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Protocol: chat, superchat"); sw.WriteLine(@"Sec-WebSocket-Extensions: test-extension"); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); Assert.IsTrue(result.IsWebSocketRequest); Assert.AreEqual(new Uri("http://example.com"), result.Request.Headers.Origin); Assert.AreEqual("superchat", result.Request.WebSocketProtocol); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 101 Switching Protocols"); sb.AppendLine(@"Upgrade: websocket"); sb.AppendLine(@"Connection: Upgrade"); sb.AppendLine(@"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk="); sb.AppendLine(@"Sec-WebSocket-Protocol: superchat"); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }
public void WebSocketHandshaker_CanIndicateANonWebSocketConnection() { var extension = new Mock<IWebSocketMessageExtension>(); extension.Setup(x => x.Name).Returns("test-extension"); WebSocketExtension ext = new WebSocketExtension("test-extension", new List<WebSocketExtensionOption>(new[] { new WebSocketExtensionOption() { ClientAvailableOption = false, Name = "optionA" } })); IWebSocketMessageExtensionContext ctx; extension.Setup(x => x.TryNegotiate(It.IsAny<WebSocketHttpRequest>(), out ext, out ctx)) .Returns(true); var factory = new WebSocketFactoryRfc6455(); factory.MessageExtensions.RegisterExtension(extension.Object); var factories = new WebSocketFactoryCollection(); factories.RegisterStandard(factory); WebSocketHandshaker handshaker = new WebSocketHandshaker(factories, new WebSocketListenerOptions() { SubProtocols = new[] { "superchat" } }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); Assert.IsFalse(result.IsWebSocketRequest); Assert.IsFalse(result.IsVersionSupported); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 404 Bad Request"); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }
WebSocketHandshake GenerateSimpleHandshake() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions()); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } ms.Seek(0, SeekOrigin.Begin); return handshaker.HandshakeAsync(ms).Result; } }
public void WebSocketHandshaker_CanUnderstandEncodedCookies() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions() { }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=This%20is%20encoded."); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); Assert.IsTrue(result.IsValid); Assert.AreEqual(1, result.Request.Cookies.Count); Assert.AreEqual("This is encoded.", result.Request.Cookies[0].Value); } }
public void WebSocketHandshaker_CanReturnCookies() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions() { OnHttpNegotiation = (request, response) => { response.Cookies.Add(new Cookie("name1", "value1")); response.Cookies.Add(new Cookie("name2", "value2")); } }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5;"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.IsNotNull(result); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 101 Switching Protocols"); sb.AppendLine(@"Upgrade: websocket"); sb.AppendLine(@"Connection: Upgrade"); sb.AppendLine(@"Set-Cookie: name1=value1"); sb.AppendLine(@"Set-Cookie: name2=value2"); sb.AppendLine(@"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk="); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }
public void WebSocketHandshaker_CanParseMultipleCookie() { WebSocketHandshaker handshaker = new WebSocketHandshaker(_factories, new WebSocketListenerOptions() { }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: cookie1=uno; cookie2=dos"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.NotNull(result); Assert.AreEqual(2, result.Request.Cookies.Count); } }
public void WebSocketHandshaker_CanNegotiateAnExtension() { var extension = new Mock<IWebSocketMessageExtension>(); extension.Setup(x=>x.Name).Returns("test-extension"); WebSocketExtension ext = new WebSocketExtension("test-extension"); IWebSocketMessageExtensionContext ctx; extension.Setup(x => x.TryNegotiate(It.IsAny<WebSocketHttpRequest>(), out ext, out ctx)) .Returns(true); var factory = new WebSocketFactoryRfc6455(); factory.MessageExtensions.RegisterExtension(extension.Object); var factories = new WebSocketFactoryCollection(); factories.RegisterStandard(factory); WebSocketHandshaker handshaker = new WebSocketHandshaker(factories, new WebSocketListenerOptions() { SubProtocols = new[] { "superchat" } }); using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms, Encoding.ASCII, 1024, true)) { sw.WriteLine(@"GET /chat HTTP/1.1"); sw.WriteLine(@"Host: server.example.com"); sw.WriteLine(@"Upgrade: websocket"); sw.WriteLine(@"Connection: Upgrade"); sw.WriteLine(@"Cookie: key=W9g/8FLW8RAFqSCWBvB9Ag==#5962c0ace89f4f780aa2a53febf2aae5;"); sw.WriteLine(@"Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw=="); sw.WriteLine(@"Sec-WebSocket-Protocol: chat, superchat"); sw.WriteLine(@"Sec-WebSocket-Extensions: test-extension"); sw.WriteLine(@"Sec-WebSocket-Version: 13"); sw.WriteLine(@"Origin: http://example.com"); } var position = ms.Position; ms.Seek(0, SeekOrigin.Begin); var result = handshaker.HandshakeAsync(ms).Result; Assert.NotNull(result); Assert.True(result.IsWebSocketRequest); Assert.AreEqual(new Uri("http://example.com"), result.Request.Headers.Origin); Assert.AreEqual("superchat", result.Response.WebSocketProtocol); ms.Seek(position, SeekOrigin.Begin); StringBuilder sb = new StringBuilder(); sb.AppendLine(@"HTTP/1.1 101 Switching Protocols"); sb.AppendLine(@"Upgrade: websocket"); sb.AppendLine(@"Connection: Upgrade"); sb.AppendLine(@"Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk="); sb.AppendLine(@"Sec-WebSocket-Protocol: superchat"); sb.AppendLine(@"Sec-WebSocket-Extensions: test-extension"); sb.AppendLine(); using (var sr = new StreamReader(ms)) { var s = sr.ReadToEnd(); Assert.AreEqual(sb.ToString(), s); } } }