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; } } }
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; } } } }
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); } }
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; }
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 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); }
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)); }
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); }
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; }
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); } }
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); } } }
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); } } }
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 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 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); }