public async Task <MazeSocket> AcceptAsync() { var key = string.Join(", ", _context.Request.Headers[MazeSocketHeaders.SecWebSocketKey]); var responseHeaders = HandshakeHelpers.GenerateResponseHeaders(key); foreach (var headerPair in responseHeaders) { _context.Response.Headers[headerPair.Key] = headerPair.Value; } var stream = await _upgradeFeature.UpgradeAsync(); await Task.Delay(500); string result; using (var streamReader = new StreamReader(stream)) { result = await streamReader.ReadLineAsync(); } Debug.Print("Result Server: " + result); using (var streamWriter = new StreamWriter(stream)) { await streamWriter.WriteLineAsync("Hey other"); } return(new MazeSocket(stream, _options.KeepAliveInterval)); }
public async Task <WebSocket> AcceptAsync(WebSocketAcceptContext acceptContext) { if (!IsWebSocketRequest) { throw new InvalidOperationException("Not a WebSocket request."); // TODO: LOC } string?subProtocol = null; if (acceptContext != null) { subProtocol = acceptContext.SubProtocol; } TimeSpan keepAliveInterval = _options.KeepAliveInterval; var advancedAcceptContext = acceptContext as ExtendedWebSocketAcceptContext; if (advancedAcceptContext != null) { if (advancedAcceptContext.KeepAliveInterval.HasValue) { keepAliveInterval = advancedAcceptContext.KeepAliveInterval.Value; } } string key = _context.Request.Headers[HeaderNames.SecWebSocketKey]; HandshakeHelpers.GenerateResponseHeaders(key, subProtocol, _context.Response.Headers); Stream opaqueTransport = await _upgradeFeature.UpgradeAsync(); // Sets status code to 101 return(WebSocket.CreateFromStream(opaqueTransport, isServer: true, subProtocol: subProtocol, keepAliveInterval: keepAliveInterval)); }
private async Task <WebSocket> CreateCompressionWebSocket(WebSocketAcceptContext acceptContext, string subProtocol) { WebSocketCompressionAcceptContext compressionAcceptContext = acceptContext as WebSocketCompressionAcceptContext; return(new CompressionWebSocket(await _upgradeFeature.UpgradeAsync(), subProtocol, compressionAcceptContext?.KeepAliveInterval ?? _options.KeepAliveInterval, compressionAcceptContext?.ReceiveBufferSize ?? _options.ReceiveBufferSize )); }
public async Task <Stream> UpgradeAsync() { var response = _context.Response; response.Headers[WebSocketHeaders.Connection] = "Upgrade"; response.Headers[WebSocketHeaders.ConnectionUpgrade] = "websocket"; response.Headers[WebSocketHeaders.SecWebSocketAccept] = CreateResponseKey(); return(await _upgradeFeature.UpgradeAsync()); }
private async Task <IWebSocketConnection> AcceptWebSocketConnectionCoreAsync(string subProtocol) { _logger.LogDebug("Upgrading connection to WebSockets"); var opaqueTransport = await _upgradeFeature.UpgradeAsync(); var connection = new WebSocketConnection( opaqueTransport.AsReadableChannel(), _channelFactory.MakeWriteableChannel(opaqueTransport), subProtocol: subProtocol); return(connection); }
public async Task <SpdySession> AcceptAsync() { if (!IsSpdyRequest) { throw new InvalidOperationException("Not a spdy request."); } GenerateResponseHeaders(_context.Response.Headers); var transport = await _upgradeFeature.UpgradeAsync() .ConfigureAwait(false); // Sets status code to 101 return(SpdySession.CreateServer(new StreamingNetworkClient(transport))); }
public async Task <WebSocket> AcceptAsync(WebSocketAcceptContext acceptContext) { if (!IsWebSocketRequest) { throw new InvalidOperationException("Not a WebSocket request."); // TODO: LOC } string subProtocol = null; if (acceptContext != null) { subProtocol = acceptContext.SubProtocol; } TimeSpan keepAliveInterval = _options.KeepAliveInterval; int receiveBufferSize = _options.ReceiveBufferSize; var advancedAcceptContext = acceptContext as ExtendedWebSocketAcceptContext; if (advancedAcceptContext != null) { if (advancedAcceptContext.ReceiveBufferSize.HasValue) { receiveBufferSize = advancedAcceptContext.ReceiveBufferSize.Value; } if (advancedAcceptContext.KeepAliveInterval.HasValue) { keepAliveInterval = advancedAcceptContext.KeepAliveInterval.Value; } } string key = string.Join(", ", _context.Request.Headers[Constants.Headers.SecWebSocketKey]); var responseHeaders = HandshakeHelpers.GenerateResponseHeaders(key, subProtocol); foreach (var headerPair in responseHeaders) { _context.Response.Headers[headerPair.Key] = headerPair.Value; } Stream opaqueTransport = await _upgradeFeature.UpgradeAsync(); // Sets status code to 101 // Allocate a buffer for receive (default is 4k) var buffer = new byte[receiveBufferSize]; return(WebSocketProtocol.CreateFromStream(opaqueTransport, isServer: true, subProtocol: subProtocol, keepAliveInterval: keepAliveInterval, buffer: buffer)); }
public async Task <WebSocket> AcceptAsync(IWebSocketAcceptContext acceptContext) { if (!IsWebSocketRequest) { throw new InvalidOperationException("Not a WebSocket request."); // TODO: LOC } string subProtocol = null; if (acceptContext != null) { subProtocol = acceptContext.SubProtocol; } TimeSpan keepAliveInterval = _options.KeepAliveInterval; int receiveBufferSize = _options.ReceiveBufferSize; var advancedAcceptContext = acceptContext as WebSocketAcceptContext; if (advancedAcceptContext != null) { if (advancedAcceptContext.ReceiveBufferSize.HasValue) { receiveBufferSize = advancedAcceptContext.ReceiveBufferSize.Value; } if (advancedAcceptContext.KeepAliveInterval.HasValue) { keepAliveInterval = advancedAcceptContext.KeepAliveInterval.Value; } } string key = string.Join(", ", _context.Request.Headers[Constants.Headers.SecWebSocketKey]); var responseHeaders = HandshakeHelpers.GenerateResponseHeaders(key, subProtocol); foreach (var headerPair in responseHeaders) { _context.Response.Headers[headerPair.Key] = headerPair.Value; } Stream opaqueTransport = await _upgradeFeature.UpgradeAsync(); // Sets status code to 101 return(CommonWebSocket.CreateServerWebSocket(opaqueTransport, subProtocol, keepAliveInterval, receiveBufferSize)); }
public async Task <WebSocket> AcceptAsync(WebSocketAcceptContext context) { Debug.Assert(IsWebSocketRequest); var response = Context.Response; var deflateOptions = ParseDeflateOptions(Context.Request.Headers[WebSocketHeaders.SecWebSocketExtensions].FirstOrDefault() ?? string.Empty, out var responseExtensions); if (deflateOptions is not null) { response.Headers[WebSocketHeaders.SecWebSocketExtensions] = responseExtensions; } response.Headers[WebSocketHeaders.Connection] = "Upgrade"; response.Headers[WebSocketHeaders.ConnectionUpgrade] = "websocket"; response.Headers[WebSocketHeaders.SecWebSocketAccept] = CreateResponseKey(); // Sets status code to 101 var stream = await _upgradeFeature.UpgradeAsync(); if (stream == null) { Context.Abort(); throw new WebSocketException("Failed to upgrade websocket connection."); } s_managedWebSocketType ??= typeof(WebSocket).Assembly.GetType("System.Net.WebSockets.ManagedWebSocket", throwOnError: true); s_webSocketCtor ??= s_managedWebSocketType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).First(x => x.GetParameters().Length == 2); s_webSocketOptionsType ??= typeof(WebSocket).Assembly.GetType("System.Net.WebSockets.WebSocketCreationOptions", throwOnError: true); object options = Activator.CreateInstance(s_webSocketOptionsType); ((dynamic)options).IsServer = true; ((dynamic)options).KeepAliveInterval = TimeSpan.Zero; options.GetType().GetProperty("DangerousDeflateOptions").SetValue(options, deflateOptions); return((WebSocket)s_webSocketCtor.Invoke(new object[] { stream, options })); }
/// <summary> /// Proxies an upgradable request to the upstream server, treating the upgraded stream as an opaque duplex channel. /// </summary> /// <remarks> /// Upgradable request proxying comprises the following steps: /// (1) Create outgoing HttpRequestMessage /// (2) Copy request headers Downstream ---► Proxy ---► Upstream /// (3) Send the outgoing request using HttpMessageInvoker Downstream ---► Proxy ---► Upstream /// (4) Copy response status line Downstream ◄--- Proxy ◄--- Upstream /// (5) Copy response headers Downstream ◄--- Proxy ◄--- Upstream /// Scenario A: upgrade with upstream worked (got 101 response) /// (A-6) Upgrade downstream channel (also sends response headers) Downstream ◄--- Proxy ◄--- Upstream /// (A-7) Copy duplex streams Downstream ◄--► Proxy ◄--► Upstream /// ---- or ---- /// Scenario B: upgrade with upstream failed (got non-101 response) /// (B-6) Send response headers Downstream ◄--- Proxy ◄--- Upstream /// (B-7) Copy response body Downstream ◄--- Proxy ◄--- Upstream /// /// This takes care of WebSockets as well as any other upgradable protocol. /// </remarks> private async Task UpgradableProxyAsync( HttpContext context, IHttpUpgradeFeature upgradeFeature, Uri targetUri, HttpMessageInvoker httpClient, ProxyTelemetryContext proxyTelemetryContext, CancellationToken shortCancellation, CancellationToken longCancellation) { Contracts.CheckValue(context, nameof(context)); Contracts.CheckValue(upgradeFeature, nameof(upgradeFeature)); Contracts.CheckValue(targetUri, nameof(targetUri)); Contracts.CheckValue(httpClient, nameof(httpClient)); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 1: Create outgoing HttpRequestMessage var upstreamRequest = new HttpRequestMessage(HttpUtilities.GetHttpMethod(context.Request.Method), targetUri) { // Default to HTTP/1.1 for proxying upgradable requests. This is already the default as of .NET Core 3.1 Version = new Version(1, 1), }; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 2: Copy request headers Downstream --► Proxy --► Upstream CopyHeadersToUpstream(context, upstreamRequest); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 3: Send the outgoing request using HttpMessageInvoker var upstreamResponse = await httpClient.SendAsync(upstreamRequest, shortCancellation); var upgraded = upstreamResponse.StatusCode == HttpStatusCode.SwitchingProtocols && upstreamResponse.Content != null; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 4: Copy response status line Downstream ◄-- Proxy ◄-- Upstream context.Response.StatusCode = (int)upstreamResponse.StatusCode; context.Features.Get <IHttpResponseFeature>().ReasonPhrase = upstreamResponse.ReasonPhrase; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 5: Copy response headers Downstream ◄-- Proxy ◄-- Upstream CopyHeadersToDownstream(upstreamResponse, context.Response.Headers); if (!upgraded) { // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step B-6: Send response headers Downstream ◄-- Proxy ◄-- Upstream // This is important to avoid any extra delays in sending response headers // e.g. if the upstream server is slow to provide its response body. await context.Response.StartAsync(shortCancellation); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step B-7: Copy response body Downstream ◄-- Proxy ◄-- Upstream await CopyBodyDownstreamAsync(upstreamResponse.Content, context.Response.Body, proxyTelemetryContext, longCancellation); return; } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step A-6: Upgrade the downstream channel. This will send all response headers too. using var downstreamStream = await upgradeFeature.UpgradeAsync(); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step A-7: Copy duplex streams var upstreamStream = await upstreamResponse.Content.ReadAsStreamAsync(); var upstreamCopier = new StreamCopier( _metrics, new StreamCopyTelemetryContext( direction: "upstream", backendId: proxyTelemetryContext.BackendId, routeId: proxyTelemetryContext.RouteId, destinationId: proxyTelemetryContext.DestinationId)); var upstreamTask = upstreamCopier.CopyAsync(downstreamStream, upstreamStream, longCancellation); var downstreamCopier = new StreamCopier( _metrics, new StreamCopyTelemetryContext( direction: "downstream", backendId: proxyTelemetryContext.BackendId, routeId: proxyTelemetryContext.RouteId, destinationId: proxyTelemetryContext.DestinationId)); var downstreamTask = downstreamCopier.CopyAsync(upstreamStream, downstreamStream, longCancellation); await Task.WhenAll(upstreamTask, downstreamTask); }
/// <summary> /// Proxies an upgradable request to the upstream server, treating the upgraded stream as an opaque duplex channel. /// </summary> /// <remarks> /// Upgradable request proxying comprises the following steps: /// (1) Create outgoing HttpRequestMessage /// (2) Copy request headers Downstream ---► Proxy ---► Upstream /// (3) Send the outgoing request using HttpMessageInvoker Downstream ---► Proxy ---► Upstream /// (4) Copy response status line Downstream ◄--- Proxy ◄--- Upstream /// (5) Copy response headers Downstream ◄--- Proxy ◄--- Upstream /// Scenario A: upgrade with upstream worked (got 101 response) /// (A-6) Upgrade downstream channel (also sends response headers) Downstream ◄--- Proxy ◄--- Upstream /// (A-7) Copy duplex streams Downstream ◄--► Proxy ◄--► Upstream /// ---- or ---- /// Scenario B: upgrade with upstream failed (got non-101 response) /// (B-6) Send response headers Downstream ◄--- Proxy ◄--- Upstream /// (B-7) Copy response body Downstream ◄--- Proxy ◄--- Upstream /// /// This takes care of WebSockets as well as any other upgradable protocol. /// </remarks> private async Task UpgradableProxyAsync( HttpContext context, IHttpUpgradeFeature upgradeFeature, HttpRequestMessage upstreamRequest, HttpMessageInvoker httpClient, CancellationToken shortCancellation, CancellationToken longCancellation, Action <HttpRequestMessage> requestAction = null) { _ = context ?? throw new ArgumentNullException(nameof(context)); _ = upgradeFeature ?? throw new ArgumentNullException(nameof(upgradeFeature)); _ = upstreamRequest ?? throw new ArgumentNullException(nameof(upstreamRequest)); _ = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 2: Copy request headers Downstream --► Proxy --► Upstream CopyHeadersToUpstream(context, upstreamRequest); if (requestAction != null) { requestAction(upstreamRequest); } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 3: Send the outgoing request using HttpMessageInvoker var upstreamResponse = await httpClient.SendAsync(upstreamRequest, shortCancellation); var upgraded = upstreamResponse.StatusCode == HttpStatusCode.SwitchingProtocols && upstreamResponse.Content != null; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 4: Copy response status line Downstream ◄-- Proxy ◄-- Upstream context.Response.StatusCode = (int)upstreamResponse.StatusCode; context.Features.Get <IHttpResponseFeature>().ReasonPhrase = upstreamResponse.ReasonPhrase; // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step 5: Copy response headers Downstream ◄-- Proxy ◄-- Upstream CopyHeadersToDownstream(upstreamResponse, context); if (!upgraded) { // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step B-6: Send response headers Downstream ◄-- Proxy ◄-- Upstream // This is important to avoid any extra delays in sending response headers // e.g. if the upstream server is slow to provide its response body. await context.Response.StartAsync(shortCancellation); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step B-7: Copy response body Downstream ◄-- Proxy ◄-- Upstream await CopyBodyDownstreamAsync(upstreamResponse.Content, context.Response.Body, longCancellation); return; } // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step A-6: Upgrade the downstream channel. This will send all response headers too. using var downstreamStream = await upgradeFeature.UpgradeAsync(); // ::::::::::::::::::::::::::::::::::::::::::::: // :: Step A-7: Copy duplex streams var upstreamStream = await upstreamResponse.Content.ReadAsStreamAsync(); var upstreamCopier = new StreamCopier(); var upstreamTask = upstreamCopier.CopyAsync(downstreamStream, upstreamStream, longCancellation); var downstreamCopier = new StreamCopier(); var downstreamTask = downstreamCopier.CopyAsync(upstreamStream, downstreamStream, longCancellation); await Task.WhenAll(upstreamTask, downstreamTask); }
public async Task <WebSocket> AcceptAsync(WebSocketAcceptContext acceptContext) { if (!IsWebSocketRequest) { throw new InvalidOperationException("Not a WebSocket request."); // TODO: LOC } string? subProtocol = null; bool enableCompression = false; bool serverContextTakeover = true; int serverMaxWindowBits = 15; TimeSpan keepAliveInterval = _options.KeepAliveInterval; if (acceptContext != null) { subProtocol = acceptContext.SubProtocol; enableCompression = acceptContext.DangerousEnableCompression; serverContextTakeover = !acceptContext.DisableServerContextTakeover; serverMaxWindowBits = acceptContext.ServerMaxWindowBits; keepAliveInterval = acceptContext.KeepAliveInterval ?? keepAliveInterval; } #pragma warning disable CS0618 // Type or member is obsolete if (acceptContext is ExtendedWebSocketAcceptContext advancedAcceptContext) #pragma warning restore CS0618 // Type or member is obsolete { if (advancedAcceptContext.KeepAliveInterval.HasValue) { keepAliveInterval = advancedAcceptContext.KeepAliveInterval.Value; } } var key = _context.Request.Headers.SecWebSocketKey.ToString(); HandshakeHelpers.GenerateResponseHeaders(key, subProtocol, _context.Response.Headers); WebSocketDeflateOptions?deflateOptions = null; if (enableCompression) { var ext = _context.Request.Headers.SecWebSocketExtensions; if (ext.Count != 0) { // loop over each extension offer, extensions can have multiple offers, we can accept any foreach (var extension in _context.Request.Headers.GetCommaSeparatedValues(HeaderNames.SecWebSocketExtensions)) { if (extension.AsSpan().TrimStart().StartsWith("permessage-deflate", StringComparison.Ordinal)) { if (HandshakeHelpers.ParseDeflateOptions(extension.AsSpan().TrimStart(), serverContextTakeover, serverMaxWindowBits, out var parsedOptions, out var response)) { Log.CompressionAccepted(_logger, response); deflateOptions = parsedOptions; // If more extension types are added, this would need to be a header append // and we wouldn't want to break out of the loop _context.Response.Headers.SecWebSocketExtensions = response; break; } } } if (deflateOptions is null) { Log.CompressionNotAccepted(_logger); } } } Stream opaqueTransport = await _upgradeFeature.UpgradeAsync(); // Sets status code to 101 return(WebSocket.CreateFromStream(opaqueTransport, new WebSocketCreationOptions() { IsServer = true, KeepAliveInterval = keepAliveInterval, SubProtocol = subProtocol, DangerousDeflateOptions = deflateOptions })); }