From 9e1abc73b404ca991647d7e7e6e485d84e3e4b6b Mon Sep 17 00:00:00 2001 From: Feiko Date: Thu, 13 Jan 2022 19:21:48 +0100 Subject: [PATCH 1/2] Added support for Sec-WebSocket-Protocol --- WebSockets/WebSocketServer/WebSocketServer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WebSockets/WebSocketServer/WebSocketServer.cs b/WebSockets/WebSocketServer/WebSocketServer.cs index 7c07367..63f9851 100644 --- a/WebSockets/WebSocketServer/WebSocketServer.cs +++ b/WebSockets/WebSocketServer/WebSocketServer.cs @@ -150,6 +150,7 @@ public bool AddWebSocket(HttpListenerContext context) var headerConnection = context.Request.Headers["Connection"]; var headerUpgrade = context.Request.Headers["Upgrade"]; var headerSwk = context.Request.Headers["Sec-WebSocket-Key"]; + var headerSecProtocol = context.Request.Headers["Sec-WebSocket-Protocol"]; WebSocketContext websocketContext = context.GetWebsocketContext(); if (headerConnection == "Upgrade" && headerUpgrade == "websocket" && !string.IsNullOrEmpty(headerSwk)) @@ -166,7 +167,7 @@ public bool AddWebSocket(HttpListenerContext context) string swka = swk + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; //default signature for websocket byte[] swkaSha1 = WebSocketHelpers.ComputeHash(swka); string swkaSha1Base64 = Convert.ToBase64String(swkaSha1); - byte[] response = Encoding.UTF8.GetBytes($"HTTP/1.1 101 Web Socket Protocol Handshake\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {swkaSha1Base64}\r\nServer: {ServerName}\r\nUpgrade: websocket\r\n\r\n"); + byte[] response = Encoding.UTF8.GetBytes($"HTTP/1.1 101 Web Socket Protocol Handshake\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {swkaSha1Base64}\r\nServer: {ServerName}\r\nUpgrade: websocket{(!string.IsNullOrEmpty(headerSecProtocol)? "\r\nSec-WebSocket-Protocol: " + headerSecProtocol : "")}\r\n\r\n"); websocketContext.NetworkStream.Write(response, 0, response.Length); var webSocketClient = new WebSocketServerClient(_options); From 79879a0d9cd9e6cc411c6d74cccf24225d9922fb Mon Sep 17 00:00:00 2001 From: Feiko Date: Sat, 15 Jan 2022 12:39:07 +0100 Subject: [PATCH 2/2] Added option for setting custom client headers, Fix bug where socket is already disposed --- .../System.Net.WebSockets.Client.nfproj | 3 + WebSockets/ClientWebSocket/ClientWebSocket.cs | 21 ++++- .../ClientWebSocket/ClientWebSocketHeaders.cs | 85 +++++++++++++++++++ WebSockets/System.Net.WebSockets.nfproj | 1 + WebSockets/WebSocket.cs | 12 ++- 5 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 WebSockets/ClientWebSocket/ClientWebSocketHeaders.cs diff --git a/WebSockets.Client/System.Net.WebSockets.Client.nfproj b/WebSockets.Client/System.Net.WebSockets.Client.nfproj index b42f938..4cc0112 100644 --- a/WebSockets.Client/System.Net.WebSockets.Client.nfproj +++ b/WebSockets.Client/System.Net.WebSockets.Client.nfproj @@ -38,6 +38,9 @@ ClientWebSocket\ClientWebSocketOptions.cs + + + ClientWebSocket\ClientWebSocketHeaders.cs MessageReceivedEventArgs.cs diff --git a/WebSockets/ClientWebSocket/ClientWebSocket.cs b/WebSockets/ClientWebSocket/ClientWebSocket.cs index 3c6eb24..d20080f 100644 --- a/WebSockets/ClientWebSocket/ClientWebSocket.cs +++ b/WebSockets/ClientWebSocket/ClientWebSocket.cs @@ -111,7 +111,8 @@ public ClientWebSocket(ClientWebSocketOptions options = null) : base(options) /// Connect to a WebSocket server. /// /// The URI of the WebSocket server to connect to. - public void Connect(string uri) + /// Optional for setting custom headers. + public void Connect(string uri, ClientWebSocketHeaders headers = null) { State = WebSocketFrame.WebSocketState.Connecting; @@ -195,7 +196,7 @@ public void Connect(string uri) _networkStream = new NetworkStream(_tcpSocket, true); } - WebSocketClientConnect(ep, prefix, Host); + WebSocketClientConnect(ep, prefix, Host, headers); } catch (SocketException ex) { @@ -212,15 +213,27 @@ private void WebSocket_ConnectionClosed(object sender, EventArgs e) _tcpSocket.Close(); } - private void WebSocketClientConnect(IPEndPoint remoteEndPoint, string prefix = "/", string host = null) + private void WebSocketClientConnect(IPEndPoint remoteEndPoint, string prefix = "/", string host = null, ClientWebSocketHeaders customHeaders = null) { + string customHeaderString = string.Empty; + if (customHeaders != null) + { + var headerKeys = customHeaders.Keys; + foreach (string key in headerKeys) + { + if (!string.IsNullOrEmpty(key)) + { + customHeaderString += $"{key}: {customHeaders[key]}\r\n"; + } + } + } if (prefix[0] != '/') throw new Exception("websocket prefix has to start with '/'"); byte[] keyBuf = new byte[16]; new Random().NextBytes(keyBuf); string swk = Convert.ToBase64String(keyBuf); - byte[] sendBuffer = Encoding.UTF8.GetBytes($"GET {prefix} HTTP/1.1\r\nHost: {(host != null ? host : remoteEndPoint.Address.ToString())}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: {swk}\r\nSec-WebSocket-Version: 13\r\n\r\n"); + byte[] sendBuffer = Encoding.UTF8.GetBytes($"GET {prefix} HTTP/1.1\r\nHost: {(host != null ? host : remoteEndPoint.Address.ToString())}\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: {swk}\r\nSec-WebSocket-Version: 13\r\n{customHeaderString}\r\n"); _networkStream.Write(sendBuffer, 0, sendBuffer.Length); string beginHeader = ($"HTTP/1.1 101".ToLower()); diff --git a/WebSockets/ClientWebSocket/ClientWebSocketHeaders.cs b/WebSockets/ClientWebSocket/ClientWebSocketHeaders.cs new file mode 100644 index 0000000..6c2f747 --- /dev/null +++ b/WebSockets/ClientWebSocket/ClientWebSocketHeaders.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections; +using System.Text; + +namespace System.Net.WebSockets +{ + /// + /// A Dictionary to store custom Http Headers to use with the ClientWebsocket + /// + public class ClientWebSocketHeaders + { + private Hashtable _headers = new Hashtable(); + + /// + /// Gets the number of headers. + /// + public int Count { get { return _headers.Count; } } + + /// + /// Gets all header keys. + /// + public string[] Keys { get { return GetKeys(); } } + + /// + /// Gets all header values. + /// + public string[] Values { get { return GetValues(); } } + + /// + /// Gets a value header or Sets a header value + /// + public string this[string key] + { + get + { + return _headers[key] as string; + } + set + { + + _headers[key] = value; + + } + } + + /// + /// Removes a header from the dictionary + /// + public void Remove(string value) + { + _headers.Remove(value); + + } + + private string[] GetKeys() + { + var keys = _headers.Keys; + string[] returnkeys = new string[keys.Count]; + + int i = 0; + foreach (object key in keys) + { + returnkeys[i] = key as string; + i++; + } + + return returnkeys; + } + + private string[] GetValues() + { + var values = _headers.Values; + string[] returnkeys = new string[values.Count]; + + int i = 0; + foreach (object value in values) + { + returnkeys[i] = value as string; + i++; + } + + return returnkeys; + } + } +} diff --git a/WebSockets/System.Net.WebSockets.nfproj b/WebSockets/System.Net.WebSockets.nfproj index 8d38d06..7a6ccfb 100644 --- a/WebSockets/System.Net.WebSockets.nfproj +++ b/WebSockets/System.Net.WebSockets.nfproj @@ -33,6 +33,7 @@ + diff --git a/WebSockets/WebSocket.cs b/WebSockets/WebSocket.cs index c9400db..eca396c 100644 --- a/WebSockets/WebSocket.cs +++ b/WebSockets/WebSocket.cs @@ -302,9 +302,15 @@ internal void HardClose() ConnectionClosed?.Invoke(this, new EventArgs()); //Let the tcp socket linger for a second so it can try and send all data out before final close. - _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, 1); - - _socket.Close(); + try + { + _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, 1); + _socket.Close(); + } + catch (ObjectDisposedException e) + { + Debug.WriteLine("socket could not be closed properly because it was already disposed"); + } } internal bool QueueMessageToSend(SendMessageFrame frame)