private void WriteResponseInternal(WebSocketHandshake handshake, StreamWriter writer)
        {
            if (handshake == null)
            {
                throw new ArgumentNullException(nameof(handshake));
            }
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            if (!handshake.IsWebSocketRequest)
            {
                handshake.Response.Status = HttpStatusCode.BadRequest;
                SendNegotiationErrorResponse(writer, handshake.Response.Status);
            }
            else if (!handshake.IsVersionSupported)
            {
                handshake.Response.Status = HttpStatusCode.UpgradeRequired;
                SendVersionNegotiationErrorResponse(writer);
            }
            else if (handshake.IsValidWebSocketRequest)
            {
                SendNegotiationResponse(handshake, writer);
            }
            else
            {
                handshake.Response.Status = handshake.Response.Status != HttpStatusCode.SwitchingProtocols ? handshake.Response.Status : HttpStatusCode.BadRequest;
                SendNegotiationErrorResponse(writer, handshake.Response.Status);
            }
        }
        public async Task <WebSocket> ConnectAsync([NotNull] Uri address, Headers <RequestHeader> requestHeaders = null, CancellationToken cancellation = default(CancellationToken))
        {
            try
            {
                cancellation.ThrowIfCancellationRequested();
                if (this.workCancellationSource.IsCancellationRequested)
                {
                    throw new WebSocketException("Client is currently closing or closed.");
                }

                var workCancellation        = this.workCancellationSource.Token;
                var negotiationCancellation = this.negotiationsTimeoutQueue?.GetSubscriptionList().Token ?? CancellationToken.None;

                if (cancellation.CanBeCanceled || workCancellation.CanBeCanceled || negotiationCancellation.CanBeCanceled)
                {
                    cancellation = CancellationTokenSource.CreateLinkedTokenSource(cancellation, workCancellation, negotiationCancellation).Token;
                }

                var request = new WebSocketHttpRequest(HttpRequestDirection.Outgoing)
                {
                    RequestUri = address,
                };

                if (requestHeaders != null)
                {
                    request.Headers.AddMany(requestHeaders);
                }

                var handshake      = new WebSocketHandshake(request);
                var pendingRequest = this.OpenConnectionAsync(handshake, cancellation);

                this.pendingRequests.TryAdd(handshake, pendingRequest);

                var webSocket = await pendingRequest.IgnoreFaultOrCancellation().ConfigureAwait(false);

                if (!workCancellation.IsCancellationRequested && negotiationCancellation.IsCancellationRequested)
                {
                    SafeEnd.Dispose(webSocket, this.log);
                    throw new WebSocketException("Negotiation timeout.");
                }

                if (this.pendingRequests.TryRemove(handshake, out pendingRequest) && this.workCancellationSource.IsCancellationRequested && this.pendingRequests.IsEmpty)
                {
                    this.closeEvent.Set();
                }

                webSocket = await pendingRequest.ConfigureAwait(false);

                this.pingQueue?.GetSubscriptionList().Add(webSocket);

                return(webSocket);
            }
            catch (Exception connectionError)
                when(connectionError.Unwrap() is ThreadAbortException == false &&
                     connectionError.Unwrap() is OperationCanceledException == false &&
                     connectionError.Unwrap() is WebSocketException == false)
                {
                    throw new WebSocketException($"An unknown error occurred while connection to '{address}'. More detailed information in inner exception.", connectionError.Unwrap());
                }
        }
        private void ParseCookies(WebSocketHandshake handshake)
        {
            if (handshake == null)
            {
                throw new ArgumentNullException(nameof(handshake));
            }

            var host = handshake.Request.Headers[RequestHeader.Host];

            foreach (var cookieValue in handshake.Request.Headers.GetValues(RequestHeader.Cookie))
            {
                try
                {
                    foreach (var cookie in CookieParser.Parse(cookieValue))
                    {
                        cookie.Domain = host;
                        cookie.Path   = string.Empty;
                        handshake.Request.Cookies.Add(cookie);
                    }
                }
                catch (Exception ex)
                {
                    throw new WebSocketException("Cannot parse cookie string: '" + (cookieValue ?? "") + "' because: " + ex.Message);
                }
            }
        }
        private void ParseWebSocketProtocol(WebSocketHandshake handshake)
        {
            if (handshake == null)
            {
                throw new ArgumentNullException(nameof(handshake));
            }

            if (!this.options.SubProtocols.Any())
            {
                return;
            }

            if (handshake.Request.Headers.Contains(RequestHeader.WebSocketProtocol))
            {
                foreach (var protocol in handshake.Request.Headers.GetValues(RequestHeader.WebSocketProtocol))
                {
                    if (this.options.SubProtocols.Contains(protocol, StringComparer.OrdinalIgnoreCase) == false)
                    {
                        continue;
                    }

                    handshake.Response.Headers[ResponseHeader.WebSocketProtocol] = protocol;
                    break;
                }
            }
        }
Example #5
0
        private void SendNegotiationResponse(WebSocketHandshake handshake, StreamWriter writer)
        {
            writer.Write("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n");
            if (handshake.Response.Cookies.Count > 0)
            {
                foreach (var cookie in handshake.Response.Cookies)
                {
                    writer.Write("Set-Cookie: ");
                    writer.Write(cookie.ToString());
                    writer.Write("\r\n");
                }
            }
            writer.Write("Sec-WebSocket-Accept: ");
            writer.Write(handshake.GenerateHandshake());

            if (handshake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Protocol))
            {
                writer.Write("\r\nSec-WebSocket-Protocol: ");
                writer.Write(handshake.Response.WebSocketProtocol);
            }

            WriteHandshakeCookies(handshake, writer);

            writer.Write("\r\n\r\n");
        }
        private void ParseWebSocketProtocol(WebSocketHandshake handshake)
        {
            if (!_options.SubProtocols.Any())
            {
                return;
            }

            if (handshake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Protocol))
            {
                var subprotocolRequest = handshake.Request.Headers[WebSocketHeaders.Protocol];

                String[] sp = subprotocolRequest.Split(',');
                AssertArrayIsAtLeast(sp, 1, "Cannot understand the 'Sec-WebSocket-Protocol' header '" + subprotocolRequest + "'");

                for (int i = 0; i < sp.Length; i++)
                {
                    var match = _options.SubProtocols.SingleOrDefault(s => s.Equals(sp[i].Trim(), StringComparison.OrdinalIgnoreCase));
                    if (match != null)
                    {
                        handshake.Response.WebSocketProtocol = match;
                        break;
                    }
                }
            }
        }
Example #7
0
        private void ParseWebSocketProtocol(WebSocketHandshake handshake)
        {
            if (handshake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Protocol))
            {
                var subprotocolRequest = handshake.Request.Headers[WebSocketHeaders.Protocol];

                if (!_options.SubProtocols.Any())
                {
                    handshake.HasSubProtocolMatch = false;
                    throw new WebSocketException("Client is requiring a sub protocol '" + subprotocolRequest + "' but there are not subprotocols defined");
                }

                String[] sp = subprotocolRequest.Split(',');
                AssertArrayIsAtLeast(sp, 1, "Cannot understand the 'Sec-WebSocket-Protocol' header '" + subprotocolRequest + "'");

                for (int i = 0; i < sp.Length; i++)
                {
                    var match = _options.SubProtocols.SingleOrDefault(s => s.Equals(sp[i].Trim(), StringComparison.OrdinalIgnoreCase));
                    if (match != null)
                    {
                        handshake.Response.WebSocketProtocol = match;
                        break;
                    }
                }

                if (String.IsNullOrWhiteSpace(handshake.Response.WebSocketProtocol))
                {
                    handshake.HasSubProtocolMatch = false;
                    throw new WebSocketException("There is no subprotocol defined for '" + subprotocolRequest + "'");
                }
            }
        }
        private void ReadHttpRequest(NetworkConnection clientStream, WebSocketHandshake handshake)
        {
            if (clientStream == null)
            {
                throw new ArgumentNullException(nameof(clientStream));
            }
            if (handshake == null)
            {
                throw new ArgumentNullException(nameof(handshake));
            }

            using (var sr = new StreamReader(clientStream.AsStream(), Encoding.ASCII, false, 1024, true))
            {
                var line = sr.ReadLine();

                ParseGET(line, handshake);

                while (!string.IsNullOrWhiteSpace(line = sr.ReadLine()))
                {
                    handshake.Request.Headers.TryParseAndAdd(line);
                }

                ParseCookies(handshake);
            }
        }
Example #9
0
        private void ConsolidateObjectModel(WebSocketHandshake handshake)
        {
            ParseCookies(handshake);

            ParseWebSocketProtocol(handshake);

            ParseWebSocketExtensions(handshake);
        }
        public async Task<WebSocketHandshake> HandshakeAsync(Stream clientStream)
        {
            WebSocketHandshake handshake = new WebSocketHandshake();
            try
            {
                ReadHttpRequest(clientStream, handshake);
                if (!(handshake.Request.Headers.AllKeys.Contains("Host") &&
                       handshake.Request.Headers.AllKeys.Contains("Upgrade") && "websocket".Equals(handshake.Request.Headers["Upgrade"], StringComparison.InvariantCultureIgnoreCase) &&
                       handshake.Request.Headers.AllKeys.Contains("Connection") &&
                       handshake.Request.Headers.AllKeys.Contains("Sec-WebSocket-Key") && !String.IsNullOrWhiteSpace(handshake.Request.Headers["Sec-WebSocket-Key"]) &&
                       handshake.Request.Headers.AllKeys.Contains("Sec-WebSocket-Version")))
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
                    return handshake;
                }

                handshake.IsWebSocketRequest = true;

                handshake.Factory = _factories.GetWebSocketFactory(handshake.Request);
                if (handshake.Factory == null)
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
                    return handshake;
                }

                handshake.IsVersionSupported = true;

                ConsolidateObjectModel(handshake);

                SelectExtensions(handshake);

                if (_options.OnHttpNegotiation != null)
                {
                    try
                    {
                        _options.OnHttpNegotiation(handshake.Request, handshake.Response);
                    }
                    catch (Exception onNegotiationHandlerError)
                    {
                        handshake.Response.Status = HttpStatusCode.InternalServerError;
                        handshake.Error = ExceptionDispatchInfo.Capture(onNegotiationHandlerError);
                    }
                }

                await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
            }
            catch(Exception ex)
            {
                handshake.Error = ExceptionDispatchInfo.Capture(ex);
                handshake.IsValid = false;
                if (!handshake.IsResponseSent)
                {
                    try { WriteHttpResponse(handshake, clientStream); }
                    catch { };
                }
            }
            return handshake;
        }
Example #11
0
 private void WriteHttpResponse(WebSocketHandshake handshake, Stream clientStream)
 {
     handshake.IsResponseSent = true;
     using (StreamWriter writer = new StreamWriter(clientStream, Encoding.ASCII, 1024, true))
     {
         WriteResponseInternal(handshake, writer);
         writer.Flush();
     }
 }
Example #12
0
 private async Task WriteHttpResponseAsync(WebSocketHandshake handshake, Stream clientStream)
 {
     handshake.IsResponseSent = true;
     using (StreamWriter writer = new StreamWriter(clientStream, Encoding.ASCII, 1024, true))
     {
         WriteResponseInternal(handshake, writer);
         await writer.FlushAsync().ConfigureAwait(false);
     }
 }
 private void WriteHttpResponse(WebSocketHandshake handshake, Stream clientStream)
 {
     handshake.IsResponseSent = true;
     using (var writer = new StreamWriter(clientStream, Encoding.ASCII, _configuration.Options.SendBufferSize, true))
     {
         WriteResponseInternal(handshake, writer);
         writer.Flush();
     }
 }
 private static bool IsHttpHeadersValid(WebSocketHandshake handShake)
 {
     return handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Host) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Upgrade) &&
            "websocket".Equals(handShake.Request.Headers[WebSocketHeaders.Upgrade],
                                      StringComparison.InvariantCultureIgnoreCase) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Connection) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Key) &&
            !String.IsNullOrWhiteSpace(handShake.Request.Headers[WebSocketHeaders.Key]) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Version);
 }
Example #15
0
 private static bool IsHttpHeadersValid(WebSocketHandshake handShake)
 {
     return(handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Host) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Upgrade) &&
            WebSocketHeaders.UpgradeExpectedValue.Equals(handShake.Request.Headers[WebSocketHeaders.Upgrade],
                                                         StringComparison.InvariantCultureIgnoreCase) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Connection) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Key) &&
            !String.IsNullOrWhiteSpace(handShake.Request.Headers[WebSocketHeaders.Key]) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Version));
 }
        private void ConsolidateObjectModel(WebSocketHandshake handshake)
        {
            if (handshake == null)
            {
                throw new ArgumentNullException(nameof(handshake));
            }

            ParseWebSocketProtocol(handshake);

            ParseWebSocketExtensions(handshake);
        }
 private static bool IsWebSocketRequestValid(WebSocketHandshake handShake)
 {
     return(handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Host) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Upgrade) &&
            "websocket".Equals(handShake.Request.Headers[WebSocketHeaders.Upgrade],
                               StringComparison.OrdinalIgnoreCase) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Connection) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Key) &&
            !String.IsNullOrWhiteSpace(handShake.Request.Headers[WebSocketHeaders.Key]) &&
            handShake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Version));
 }
Example #18
0
        public async Task <WebSocketHandshake> HandshakeAsync(Stream clientStream)
        {
            WebSocketHandshake handshake = new WebSocketHandshake();

            try
            {
                ReadHttpRequest(clientStream, handshake);
                if (!(handshake.Request.Headers.HeaderNames.Contains("Host") &&
                      handshake.Request.Headers.HeaderNames.Contains("Upgrade") && "websocket".Equals(handshake.Request.Headers["Upgrade"], StringComparison.OrdinalIgnoreCase) &&
                      handshake.Request.Headers.HeaderNames.Contains("Connection") &&
                      handshake.Request.Headers.HeaderNames.Contains("Sec-WebSocket-Key") && !String.IsNullOrWhiteSpace(handshake.Request.Headers["Sec-WebSocket-Key"]) &&
                      handshake.Request.Headers.HeaderNames.Contains("Sec-WebSocket-Version")))
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);

                    return(handshake);
                }

                handshake.IsWebSocketRequest = true;

                handshake.Factory = _factories.GetWebSocketFactory(handshake.Request);
                if (handshake.Factory == null)
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);

                    return(handshake);
                }

                handshake.IsVersionSupported = true;

                ConsolidateObjectModel(handshake);

                SelectExtensions(handshake);

                RunHttpNegotiationHandler(handshake);

                await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                handshake.Error   = ExceptionDispatchInfo.Capture(ex);
                handshake.IsValid = false;
                if (!handshake.IsResponseSent)
                {
                    try { WriteHttpResponse(handshake, clientStream); }
                    catch (Exception ex2)
                    {
                        DebugLog.Fail("HttpNegotiationQueue.WorkAsync (Writting error esponse)", ex2);
                    };
                }
            }
            return(handshake);
        }
        private void ParseWebSocketExtensions(WebSocketHandshake handshake)
        {
            List <WebSocketExtension> extensionList = new List <WebSocketExtension>();

            if (handshake.Request.Headers.AllKeys.Contains("Sec-WebSocket-Extensions"))
            {
                var header     = handshake.Request.Headers["Sec-WebSocket-Extensions"];
                var extensions = header.Split(',');

                AssertArrayIsAtLeast(extensions, 1, "Cannot parse extension [" + header + "]");

                if (extensions.Any(e => String.IsNullOrWhiteSpace(e)))
                {
                    throw new WebSocketException("Cannot parse a null extension");
                }

                foreach (var extension in extensions)
                {
                    List <WebSocketExtensionOption> extOptions = new List <WebSocketExtensionOption>();
                    var parts = extension.Split(';');
                    AssertArrayIsAtLeast(parts, 1, "Cannot parse extension [" + header + "]");
                    if (parts.Any(e => String.IsNullOrWhiteSpace(e)))
                    {
                        throw new WebSocketException("Cannot parse a null extension part");
                    }
                    foreach (var part in parts.Skip(1))
                    {
                        var optParts = part.Split('=');
                        AssertArrayIsAtLeast(optParts, 1, "Cannot parse extension options [" + header + "]");
                        if (optParts.Any(e => String.IsNullOrWhiteSpace(e)))
                        {
                            throw new WebSocketException("Cannot parse a null extension part option");
                        }
                        if (optParts.Length == 1)
                        {
                            extOptions.Add(new WebSocketExtensionOption()
                            {
                                Name = optParts[0], ClientAvailableOption = true
                            });
                        }
                        else
                        {
                            extOptions.Add(new WebSocketExtensionOption()
                            {
                                Name = optParts[0], Value = optParts[1]
                            });
                        }
                    }
                    extensionList.Add(new WebSocketExtension(parts[0], extOptions));
                }
            }
            handshake.Request.SetExtensions(extensionList);
        }
        private void ParseWebSocketExtensions(WebSocketHandshake handshake)
        {
            List <WebSocketExtension> extensionList = null;

            if (handshake.Request.Headers.Contains(WebSocketHeaders.Extensions))
            {
                var header = handshake.Request.Headers[WebSocketHeaders.Extensions];
                extensionList = extensionList ?? new List <WebSocketExtension>();
                BuildExtensions(extensionList, header, SplitBy(',', header));
            }
            handshake.Request.SetExtensions(extensionList);
        }
Example #21
0
        private void ParseHeader(String line, WebSocketHandshake handshake)
        {
            var separator = line.IndexOf(":");

            if (separator == -1)
            {
                return;
            }
            String key   = line.Substring(0, separator);
            String value = line.Substring(separator + 2, line.Length - (separator + 2));

            handshake.Request.Headers.Add(key.ToLower(), value); // make the keys lowercase
        }
        private async Task WriteRequestAsync(WebSocketHandshake handshake, Stream stream)
        {
            var url        = handshake.Request.RequestUri;
            var nonce      = handshake.GenerateClientNonce();
            var bufferSize = this.options.BufferManager.LargeBufferSize;

            using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize, leaveOpen: true))
            {
                var requestHeaders = handshake.Request.Headers;
                requestHeaders[RequestHeader.Host]             = url.DnsSafeHost;
                requestHeaders[RequestHeader.Upgrade]          = "websocket";
                requestHeaders[RequestHeader.Connection]       = "keep-alive, Upgrade";
                requestHeaders[RequestHeader.WebSocketKey]     = nonce;
                requestHeaders[RequestHeader.WebSocketVersion] = handshake.Factory.Version.ToString();
                requestHeaders[RequestHeader.CacheControl]     = "no-cache";
                requestHeaders[RequestHeader.Pragma]           = "no-cache";
                foreach (var extension in handshake.Factory.MessageExtensions)
                {
                    requestHeaders.Add(RequestHeader.WebSocketExtensions, extension.ToString());
                }
                foreach (var subProtocol in this.options.SubProtocols)
                {
                    requestHeaders.Add(RequestHeader.WebSocketProtocol, subProtocol);
                }

                writer.NewLine = "\r\n";
                await writer.WriteAsync("GET ").ConfigureAwait(false);

                await writer.WriteAsync(url.PathAndQuery).ConfigureAwait(false);

                await writer.WriteLineAsync(" " + WEB_SOCKET_HTTP_VERSION).ConfigureAwait(false);

                foreach (var header in requestHeaders)
                {
                    var headerName = header.Key;
                    foreach (var value in header.Value)
                    {
                        await writer.WriteAsync(headerName).ConfigureAwait(false);

                        await writer.WriteAsync(": ").ConfigureAwait(false);

                        await writer.WriteLineAsync(value).ConfigureAwait(false);
                    }
                }

                await writer.WriteLineAsync().ConfigureAwait(false);

                await writer.FlushAsync().ConfigureAwait(false);
            }
        }
 private void SelectExtensions(WebSocketHandshake handshake)
 {
     foreach (var extRequest in handshake.Request.WebSocketExtensions)
     {
         if (_configuration.MessageExtensions.TryGetExtension(extRequest.Name, out IWebSocketMessageExtension extension))
         {
             if (extension.TryNegotiate(handshake.Request, out WebSocketExtension extensionResponse, out IWebSocketMessageExtensionContext context))
             {
                 handshake.AddExtension(context);
                 handshake.Response.AddExtension(extensionResponse);
             }
         }
     }
 }
        public async Task <WebSocketHandshake> HandshakeAsync(Stream clientStream)
        {
            WebSocketHandshake handshake = new WebSocketHandshake();

            try
            {
                await ReadHttpRequestAsync(clientStream, handshake).ConfigureAwait(false);

                if (!IsHttpHeadersValid(handshake))
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);

                    return(handshake);
                }

                handshake.IsWebSocketRequest = true;

                handshake.Factory = _configuration.Standards.GetWebSocketFactory(handshake.Request);
                if (handshake.Factory == null)
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);

                    return(handshake);
                }

                handshake.IsVersionSupported = true;

                ConsolidateObjectModel(handshake);

                SelectExtensions(handshake);

                await RunHttpNegotiationHandler(handshake).ConfigureAwait(false);

                await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                handshake.Error   = ExceptionDispatchInfo.Capture(ex);
                handshake.IsValid = false;
                if (!handshake.IsResponseSent)
                {
                    try { WriteHttpResponse(handshake, clientStream); }
                    catch (Exception ex2)
                    {
                        Debug.Fail("HttpNegotiationQueue.WorkAsync (Writting error esponse): " + ex2.Message);
                    };
                }
            }
            return(handshake);
        }
 private void SelectExtensions(WebSocketHandshake handshake)
 {
     IWebSocketMessageExtensionContext context;
     WebSocketExtension extensionResponse;
     foreach (var extRequest in handshake.Request.WebSocketExtensions)
     {
         var extension = handshake.Factory.MessageExtensions.SingleOrDefault(x => x.Name.Equals(extRequest.Name, StringComparison.InvariantCultureIgnoreCase));
         if (extension != null && extension.TryNegotiate(handshake.Request, out extensionResponse, out context))
         {
             handshake.NegotiatedMessageExtensions.Add(context);
             handshake.ResponseExtensions.Add(extensionResponse);
         }
     }
 }
        public async Task<WebSocketHandshake> HandshakeAsync(Stream clientStream)
        {
            WebSocketHandshake handshake = new WebSocketHandshake();
            try
            {
                ReadHttpRequest(clientStream, handshake);
                if (!(handshake.Request.Headers.HeaderNames.Contains("Host") &&
                       handshake.Request.Headers.HeaderNames.Contains("Upgrade") && "websocket".Equals(handshake.Request.Headers["Upgrade"], StringComparison.OrdinalIgnoreCase) &&
                       handshake.Request.Headers.HeaderNames.Contains("Connection") &&
                       handshake.Request.Headers.HeaderNames.Contains("Sec-WebSocket-Key") && !String.IsNullOrWhiteSpace(handshake.Request.Headers["Sec-WebSocket-Key"]) &&
                       handshake.Request.Headers.HeaderNames.Contains("Sec-WebSocket-Version")))
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
                    return handshake;
                }

                handshake.IsWebSocketRequest = true;

                handshake.Factory = _factories.GetWebSocketFactory(handshake.Request);
                if (handshake.Factory == null)
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
                    return handshake;
                }

                handshake.IsVersionSupported = true;

                ConsolidateObjectModel(handshake);

                SelectExtensions(handshake);

                RunHttpNegotiationHandler(handshake);

                await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                handshake.Error = ExceptionDispatchInfo.Capture(ex);
                handshake.IsValid = false;
                if (!handshake.IsResponseSent)
                {
                    try { WriteHttpResponse(handshake, clientStream); }
                    catch(Exception ex2) 
                    {
                        DebugLog.Fail("HttpNegotiationQueue.WorkAsync (Writting error esponse)", ex2);
                    };
                }
            }
            return handshake;
        }
Example #27
0
        private void ParseGET(String line, WebSocketHandshake handshake)
        {
            if (String.IsNullOrWhiteSpace(line) || !line.StartsWith("GET"))
            {
                throw new WebSocketException("Not GET request");
            }

            var parts = line.Split(' ');

            handshake.Request.RequestUri = new Uri(parts[1], UriKind.Relative);
            String version = parts[2];

            handshake.Request.HttpVersion = version.EndsWith("1.1") ? HttpVersion.Version11 : HttpVersion.Version10;
        }
        private async Task WriteHttpResponseAsync(WebSocketHandshake handshake, Stream clientStream)
        {
            if (!handshake.IsWebSocketRequest && handshake.IsValidHttpRequest && _options.HttpFallback != null)
            {
                return;
            }

            handshake.IsResponseSent = true;
            using (StreamWriter writer = new StreamWriter(clientStream, Encoding.ASCII, 1024, true))
            {
                WriteResponseInternal(handshake, writer);
                await writer.FlushAsync().ConfigureAwait(false);
            }
        }
Example #29
0
        private void ReadHttpRequest(Stream clientStream, WebSocketHandshake handshake)
        {
            using (var sr = new StreamReader(clientStream, Encoding.ASCII, false, 1024, true))
            {
                String line = sr.ReadLine();

                ParseGET(line, handshake);

                while (!String.IsNullOrWhiteSpace(line = sr.ReadLine()))
                {
                    ParseHeader(line, handshake);
                }
            }
        }
 private async Task RunHttpNegotiationHandler(WebSocketHandshake handshake)
 {
     if (_configuration.Options.OnHttpNegotiation != null)
     {
         try
         {
             await _configuration.Options.OnHttpNegotiation(handshake.Request, handshake.Response).ConfigureAwait(false);
         }
         catch (Exception onNegotiationHandlerError)
         {
             handshake.Response.Status = HttpStatusCode.InternalServerError;
             handshake.Error           = ExceptionDispatchInfo.Capture(onNegotiationHandlerError);
         }
     }
 }
 private void RunHttpNegotiationHandler(WebSocketHandshake handshake)
 {
     if (_options.OnHttpNegotiation != null)
     {
         try
         {
             _options.OnHttpNegotiation(handshake.Request, handshake.Response);
         }
         catch (Exception onNegotiationHandlerError)
         {
             handshake.Response.Status = HttpStatusCode.InternalServerError;
             handshake.Error = ExceptionDispatchInfo.Capture(onNegotiationHandlerError);
         }
     }
 }
Example #32
0
        private void SelectExtensions(WebSocketHandshake handshake)
        {
            IWebSocketMessageExtensionContext context;
            WebSocketExtension extensionResponse;

            foreach (var extRequest in handshake.Request.WebSocketExtensions)
            {
                var extension = handshake.Factory.MessageExtensions.SingleOrDefault(x => x.Name.Equals(extRequest.Name, StringComparison.InvariantCultureIgnoreCase));
                if (extension != null && extension.TryNegotiate(handshake.Request, out extensionResponse, out context))
                {
                    handshake.NegotiatedMessageExtensions.Add(context);
                    handshake.Response.WebSocketExtensions.Add(extensionResponse);
                }
            }
        }
Example #33
0
 private void RunHttpNegotiationHandler(WebSocketHandshake handshake)
 {
     if (_options.OnHttpNegotiation != null)
     {
         try
         {
             _options.OnHttpNegotiation(handshake.Request, handshake.Response);
         }
         catch (Exception onNegotiationHandlerError)
         {
             handshake.Response.Status = HttpStatusCode.InternalServerError;
             handshake.Error           = ExceptionDispatchInfo.Capture(onNegotiationHandlerError);
         }
     }
 }
        private static void WriteHandshakeCookies(WebSocketHandshake handshake, StreamWriter writer)
        {
            if (handshake == null)
            {
                throw new ArgumentNullException(nameof(handshake));
            }
            if (writer == null)
            {
                throw new ArgumentNullException(nameof(writer));
            }

            if (handshake.Response.WebSocketExtensions.Any())
            {
                bool firstExt = true, firstOpt = true;
                writer.Write("\r\nSec-WebSocket-Extensions: ");

                foreach (var extension in handshake.Response.WebSocketExtensions)
                {
                    if (!firstExt)
                    {
                        writer.Write(",");
                    }

                    writer.Write(extension.Name);
                    var serverAcceptedOptions = extension.Options.Where(x => !x.ClientAvailableOption);
                    if (extension.Options.Any())
                    {
                        writer.Write(";");
                        foreach (var extOption in serverAcceptedOptions)
                        {
                            if (!firstOpt)
                            {
                                writer.Write(";");
                            }

                            writer.Write(extOption.Name);
                            if (extOption.Value != null)
                            {
                                writer.Write("=");
                                writer.Write(extOption.Value);
                            }
                            firstOpt = false;
                        }
                        firstExt = false;
                    }
                }
            }
        }
        public async Task <WebSocketHandshake> HandshakeAsync(Stream clientStream, IPEndPoint localEndpoint = null, IPEndPoint remoteEndpoint = null)
        {
            WebSocketHandshake handshake = new WebSocketHandshake(localEndpoint, remoteEndpoint);

            try
            {
                ReadHttpRequest(clientStream, handshake);
                if (!IsWebSocketRequestValid(handshake))
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);

                    return(handshake);
                }

                handshake.IsWebSocketRequest = true;

                handshake.Factory = _factories.GetWebSocketFactory(handshake.Request);
                if (handshake.Factory == null)
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);

                    return(handshake);
                }

                handshake.IsVersionSupported = true;

                ConsolidateObjectModel(handshake);

                SelectExtensions(handshake);

                RunHttpNegotiationHandler(handshake);

                await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
            }
            catch (Exception ex)
            {
                handshake.Error = ExceptionDispatchInfo.Capture(ex);
                if (!handshake.IsResponseSent)
                {
                    try { WriteHttpResponse(handshake, clientStream); }
                    catch (Exception ex2)
                    {
                        DebugLog.Fail("HttpNegotiationQueue.WorkAsync (Writing error esponse)", ex2);
                    };
                }
            }
            return(handshake);
        }
        public async Task<WebSocketHandshake> HandshakeAsync(Stream clientStream)
        {
            WebSocketHandshake handshake = new WebSocketHandshake();
            try
            {
                ReadHttpRequest(clientStream, handshake);
                if (!IsHttpHeadersValid(handshake))
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
                    return handshake;
                }

                handshake.IsWebSocketRequest = true;

                handshake.Factory = _factories.GetWebSocketFactory(handshake.Request);
                if (handshake.Factory == null)
                {
                    await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
                    return handshake;
                }

                handshake.IsVersionSupported = true;

                ConsolidateObjectModel(handshake);

                SelectExtensions(handshake);

                RunHttpNegotiationHandler(handshake);

                await WriteHttpResponseAsync(handshake, clientStream).ConfigureAwait(false);
            }
            catch(Exception ex)
            {
                handshake.Error = ExceptionDispatchInfo.Capture(ex);
                handshake.IsValid = false;
                if (!handshake.IsResponseSent)
                {
                    try { WriteHttpResponse(handshake, clientStream); }
                    catch(Exception ex2) 
                    {
                        DebugLog.Fail("HttpNegotiationQueue.WorkAsync (Writting error esponse)", ex2);
                    };
                }
            }
            return handshake;
        }
        private void ParseWebSocketProtocol(WebSocketHandshake handshake)
        {
            if (!_options.SubProtocols.Any())
                return;

            if (handshake.Request.Headers.HeaderNames.Contains(WebSocketHeaders.Protocol))
            {
                var subprotocolRequest = handshake.Request.Headers[WebSocketHeaders.Protocol];

                String[] sp = subprotocolRequest.Split(',');
                AssertArrayIsAtLeast(sp, 1, "Cannot understand the 'Sec-WebSocket-Protocol' header '" + subprotocolRequest + "'");

                for (int i = 0; i < sp.Length; i++)
                {
                    var match = _options.SubProtocols.SingleOrDefault(s => s.Equals(sp[i].Trim(), StringComparison.OrdinalIgnoreCase));
                    if (match != null)
                    {
                        handshake.Response.WebSocketProtocol = match;
                        break;
                    }
                }
            }
        }
 private void WriteResponseInternal(WebSocketHandshake handshake, StreamWriter writer)
 {
     if (!handshake.IsWebSocketRequest)
     {
         handshake.ResponseCode = HttpStatusCode.BadRequest;
         SendNegotiationErrorResponse(writer);
     }
     else if (!handshake.IsVersionSupported)
     {
         handshake.ResponseCode = HttpStatusCode.UpgradeRequired;
         SendVersionNegotiationErrorResponse(writer);
     }
     else if (handshake.IsValid)
     {
         handshake.ResponseCode = HttpStatusCode.SwitchingProtocols;
         SendNegotiationResponse(handshake, writer);
     }
     else
     {
         handshake.ResponseCode = HttpStatusCode.BadRequest;
         SendNegotiationErrorResponse(writer);
     }
 }
        private void ParseGET(String line, WebSocketHandshake handshake)
        {
            if (String.IsNullOrWhiteSpace(line) || !line.StartsWith("GET"))
                throw new WebSocketException("Not GET request");

            var parts = line.Split(' ');
            handshake.Request.RequestUri = new Uri(parts[1], UriKind.Relative);
            String version = parts[2];
            handshake.Request.HttpVersion = version.EndsWith("1.1") ? HttpVersion.Version11 : HttpVersion.Version10;
        }
        private void SendNegotiationResponse(WebSocketHandshake handshake, StreamWriter writer)
        {
            writer.Write("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n");
            if (handshake.Response.Cookies.Count > 0)
            {
                foreach (var cookie in handshake.Response.Cookies)
                {
                    writer.Write("Set-Cookie: ");
                    writer.Write(cookie.ToString());
                    writer.Write("\r\n");
                }
            }
            writer.Write("Sec-WebSocket-Accept: ");
            writer.Write(handshake.GenerateHandshake());

            // https://tools.ietf.org/html/rfc6455#section-4.2.2
            /* 
              Sec-WebSocket-Protocol
              If the client's handshake did not contain such a header field or if
              the server does not agree to any of the client's requested
              subprotocols, the only acceptable value is null.  The absence
              of such a field is equivalent to the null value (meaning that
              if the server does not wish to agree to one of the suggested
              subprotocols, it MUST NOT send back a |Sec-WebSocket-Protocol|
              header field in its response).
             */
            if (handshake.Response.WebSocketProtocol != null)
            {
                writer.Write("\r\nSec-WebSocket-Protocol: ");
                writer.Write(handshake.Response.WebSocketProtocol);
            }

            WriteHandshakeCookies(handshake, writer);

            writer.Write("\r\n\r\n");
        }
        private void SendNegotiationResponse(WebSocketHandshake handshake, StreamWriter writer)
        {
            writer.Write("HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n");
            if (handshake.Response.Cookies.Count > 0)
            {
                foreach (var cookie in handshake.Response.Cookies)
                {
                    writer.Write("Set-Cookie: ");
                    writer.Write(cookie.ToString());
                    writer.Write("\r\n");
                }
            }
            writer.Write("Sec-WebSocket-Accept: ");
            writer.Write(handshake.GenerateHandshake());

            if (handshake.Request.Headers.HeaderNames.Contains("Sec-WebSocket-Protocol"))
            {
                writer.Write("\r\nSec-WebSocket-Protocol: ");
                writer.Write(handshake.Response.WebSocketProtocol);
            }

            WriteHandshakeCookies(handshake, writer);

            writer.Write("\r\n\r\n");
        }
        private static void WriteHandshakeCookies(WebSocketHandshake handshake, StreamWriter writer)
        {
            if (handshake.Response.WebSocketExtensions.Any())
            {
                Boolean firstExt = true, firstOpt = true;
                writer.Write("\r\nSec-WebSocket-Extensions: ");

                foreach (var extension in handshake.Response.WebSocketExtensions)
                {
                    if (!firstExt)
                        writer.Write(",");

                    writer.Write(extension.Name);
                    var serverAcceptedOptions = extension.Options.Where(x => !x.ClientAvailableOption);
                    if (extension.Options.Any())
                    {
                        writer.Write(";");
                        foreach (var extOption in serverAcceptedOptions)
                        {
                            if (!firstOpt)
                                writer.Write(";");

                            writer.Write(extOption.Name);
                            if (extOption.Value != null)
                            {
                                writer.Write("=");
                                writer.Write(extOption.Value);
                            }
                            firstOpt = false;
                        }
                        firstExt = false;
                    }
                }
            }
        }
        private void SendNegotiationResponse(WebSocketHandshake handshake, StreamWriter writer)
        {
            writer.Write("HTTP/1.1 101 Switching Protocols\r\n");
            writer.Write("Upgrade: websocket\r\n");
            writer.Write("Connection: Upgrade\r\n");
            writer.Write("Sec-WebSocket-Accept: ");
            writer.Write(handshake.GenerateHandshake());

            if (handshake.Request.Headers.AllKeys.Contains("Sec-WebSocket-Protocol"))
            {
                writer.Write("\r\n");
                writer.Write("Sec-WebSocket-Protocol: ");
                writer.Write(handshake.Request.WebSocketProtocol);
            }

            if (handshake.ResponseExtensions.Any())
            {
                Boolean firstExt=true, firstOpt=true;
                writer.Write("\r\n");
                writer.Write("Sec-WebSocket-Extensions: ");
                foreach (var extension in handshake.ResponseExtensions)
                {
                    if(!firstExt)
                        writer.Write(",");

                    writer.Write(extension.Name);
                    var serverAcceptedOptions = extension.Options.Where(x => !x.ClientAvailableOption);
                    if(extension.Options.Any())
                    {
                        writer.Write(";");
                        foreach (var extOption in serverAcceptedOptions)
                        {
                            if(!firstOpt)
                                writer.Write(";");

                            writer.Write(extOption.Name);
                            if (extOption.Value != null)
                            {
                                writer.Write("=");
                                writer.Write(extOption.Value);
                            }
                            firstOpt = false;
                        }
                        firstExt = false;
                    }
                }
            }

            writer.Write("\r\n");
            writer.Write("\r\n");
        }
 private void WriteHttpResponse(WebSocketHandshake handshake, Stream clientStream)
 {
     handshake.IsResponseSent = true;
     using (StreamWriter writer = new StreamWriter(clientStream, Encoding.ASCII, 1024, true))
     {
         WriteResponseInternal(handshake, writer);
         writer.Flush();
     }
 }
        private void ConsolidateObjectModel(WebSocketHandshake handshake)
        {
            ParseCookies(handshake);

            ParseWebSocketProtocol(handshake);

            ParseWebSocketExtensions(handshake);
        }
        private void ReadHttpRequest(Stream clientStream, WebSocketHandshake handshake)
        {
            using (var sr = new StreamReader(clientStream, Encoding.ASCII, false, 1024, true))
            {
                String line = sr.ReadLine();

                ParseGET(line, handshake);

                while (!String.IsNullOrWhiteSpace(line = sr.ReadLine()))
                    ParseHeader(line, handshake);
            }
        }
        private void ParseWebSocketProtocol(WebSocketHandshake handshake)
        {
            if (handshake.Request.Headers.HeaderNames.Contains("Sec-WebSocket-Protocol"))
            {
                var subprotocolRequest = handshake.Request.Headers["Sec-WebSocket-Protocol"];

                if (!_options.SubProtocols.Any())
                {
                    handshake.HasSubProtocolMatch = false;
                    throw new WebSocketException("Client is requiring a sub protocol '" + subprotocolRequest + "' but there are not subprotocols defined");
                }

                String[] sp = subprotocolRequest.Split(',');
                AssertArrayIsAtLeast(sp, 1, "Cannot understand the 'Sec-WebSocket-Protocol' header '" + subprotocolRequest + "'");

                for (int i = 0; i < sp.Length; i++)
                {
                    var match = _options.SubProtocols.SingleOrDefault(s => s.Equals(sp[i].Trim(), StringComparison.OrdinalIgnoreCase));
                    if (match != null)
                    {
                        handshake.Response.WebSocketProtocol = match;
                        break;
                    }
                }

                if (String.IsNullOrWhiteSpace(handshake.Response.WebSocketProtocol))
                {
                    handshake.HasSubProtocolMatch = false;
                    throw new WebSocketException("There is no subprotocol defined for '" + subprotocolRequest + "'");
                }
            }
        }
 private void ParseHeader(String line, WebSocketHandshake handshake)
 {
     var separator = line.IndexOf(":");
     if (separator == -1)
         return;
     String key = line.Substring(0, separator);
     String value = line.Substring(separator + 2, line.Length - (separator + 2));
     handshake.Request.Headers.Add(key,value);
 }
        private void ParseWebSocketExtensions(WebSocketHandshake handshake)
        {
            List<WebSocketExtension> extensionList = new List<WebSocketExtension>();
            if (handshake.Request.Headers.HeaderNames.Contains("Sec-WebSocket-Extensions"))
            {
                var header = handshake.Request.Headers["Sec-WebSocket-Extensions"];
                var extensions = header.Split(',');

                AssertArrayIsAtLeast(extensions, 1, "Cannot parse extension [" + header + "]");

                if (extensions.Any(e => String.IsNullOrWhiteSpace(e)))
                    throw new WebSocketException("Cannot parse a null extension");

                BuildExtensions(extensionList, header, extensions);
            }
            handshake.Request.SetExtensions(extensionList);
        }
        private void ConsolidateObjectModel(WebSocketHandshake handshake)
        {
            if (handshake.Request.Headers.AllKeys.Contains("Cookie"))
                handshake.Request.Cookies.SetCookies(new Uri("http://" + handshake.Request.Headers["Host"]), handshake.Request.Headers["Cookie"]);

            if (handshake.Request.Headers.AllKeys.Contains("Sec-WebSocket-Protocol"))
            {
                var subprotocolRequest = handshake.Request.Headers["Sec-WebSocket-Protocol"];

                if (!_options.SubProtocols.Any())
                {
                    handshake.HasSubProtocolMatch = false;
                    throw new WebSocketException("Client is requiring a sub protocol '" + subprotocolRequest + "' but there are not subprotocols defined");
                }

                String[] sp = subprotocolRequest.Split(',');
                AssertArrayIsAtLeast(sp, 1, "Cannot understand the 'Sec-WebSocket-Protocol' header '" + subprotocolRequest + "'");

                for (int i = 0; i < sp.Length; i++)
                {
                    var match = _options.SubProtocols.SingleOrDefault(s => s.Equals(sp[i].Trim(), StringComparison.OrdinalIgnoreCase));
                    if (match != null)
                    {
                        handshake.Request.WebSocketProtocol = match;
                        break;
                    }
                }

                if (String.IsNullOrWhiteSpace(handshake.Request.WebSocketProtocol))
                {
                    handshake.HasSubProtocolMatch = false;
                    throw new WebSocketException("There is no subprotocol defined for '" + subprotocolRequest + "'");
                }
            }

            List<WebSocketExtension> extensionList = new List<WebSocketExtension>();
            if (handshake.Request.Headers.AllKeys.Contains("Sec-WebSocket-Extensions"))
            {
                var header = handshake.Request.Headers["Sec-WebSocket-Extensions"];
                var extensions = header.Split(',');

                AssertArrayIsAtLeast(extensions, 1, "Cannot parse extension [" + header +"]");

                if (extensions.Any(e => String.IsNullOrWhiteSpace(e)))
                    throw new WebSocketException("Cannot parse a null extension");

                foreach (var extension in extensions)
                {
                    List<WebSocketExtensionOption> extOptions = new List<WebSocketExtensionOption>();
                    var parts = extension.Split(';');
                    AssertArrayIsAtLeast(parts, 1, "Cannot parse extension [" + header + "]");
                    if (parts.Any(e => String.IsNullOrWhiteSpace(e)))
                        throw new WebSocketException("Cannot parse a null extension part");
                    foreach (var part in parts.Skip(1))
                    {
                        var optParts = part.Split('=');
                        AssertArrayIsAtLeast(optParts, 1, "Cannot parse extension options [" + header + "]");
                        if (optParts.Any(e => String.IsNullOrWhiteSpace(e)))
                            throw new WebSocketException("Cannot parse a null extension part option");
                        if(optParts.Length==1)
                            extOptions.Add(new WebSocketExtensionOption() { Name = optParts[0], ClientAvailableOption=true });
                        else
                            extOptions.Add(new WebSocketExtensionOption() { Name = optParts[0], Value = optParts[1]});
                    }
                    extensionList.Add(new WebSocketExtension(parts[0], extOptions));
                }
            }
            handshake.Request.SetExtensions(extensionList);
        }
 private void ParseCookies(WebSocketHandshake handshake)
 {
     if (handshake.Request.Headers.HeaderNames.Contains("Cookie"))
     {
         var cookieString = handshake.Request.Headers["Cookie"];
         try
         {
             CookieParser parser = new CookieParser();
             foreach (var cookie in parser.Parse(cookieString))
             {
                 cookie.Domain = handshake.Request.Headers.Host;
                 cookie.Path = String.Empty;
                 handshake.Request.Cookies.Add(cookie);
             }
         }
         catch (Exception ex)
         {
             throw new WebSocketException("Cannot parse cookie string: '" + (cookieString ?? "") + "' because: " + ex.Message);
         }
     }
 }
 private async Task WriteHttpResponseAsync(WebSocketHandshake handshake, Stream clientStream)
 {
     handshake.IsResponseSent = true;
     using (StreamWriter writer = new StreamWriter(clientStream, Encoding.ASCII, 1024, true))
     {
         WriteResponseInternal(handshake, writer);
         await writer.FlushAsync().ConfigureAwait(false);
     }
 }
        private void ParseWebSocketExtensions(WebSocketHandshake handshake)
        {
            List<WebSocketExtension> extensionList = new List<WebSocketExtension>();
            if (handshake.Request.Headers.AllKeys.Contains("Sec-WebSocket-Extensions"))
            {
                var header = handshake.Request.Headers["Sec-WebSocket-Extensions"];
                var extensions = header.Split(',');

                AssertArrayIsAtLeast(extensions, 1, "Cannot parse extension [" + header + "]");

                if (extensions.Any(e => String.IsNullOrWhiteSpace(e)))
                    throw new WebSocketException("Cannot parse a null extension");

                foreach (var extension in extensions)
                {
                    List<WebSocketExtensionOption> extOptions = new List<WebSocketExtensionOption>();
                    var parts = extension.Split(';');
                    AssertArrayIsAtLeast(parts, 1, "Cannot parse extension [" + header + "]");
                    if (parts.Any(e => String.IsNullOrWhiteSpace(e)))
                        throw new WebSocketException("Cannot parse a null extension part");
                    foreach (var part in parts.Skip(1))
                    {
                        var optParts = part.Split('=');
                        AssertArrayIsAtLeast(optParts, 1, "Cannot parse extension options [" + header + "]");
                        if (optParts.Any(e => String.IsNullOrWhiteSpace(e)))
                            throw new WebSocketException("Cannot parse a null extension part option");
                        if (optParts.Length == 1)
                            extOptions.Add(new WebSocketExtensionOption() { Name = optParts[0], ClientAvailableOption = true });
                        else
                            extOptions.Add(new WebSocketExtensionOption() { Name = optParts[0], Value = optParts[1] });
                    }
                    extensionList.Add(new WebSocketExtension(parts[0], extOptions));
                }
            }
            handshake.Request.SetExtensions(extensionList);
        }