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
                                            ));
        }
Exemple #4
0
        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));
            }
Exemple #9
0
        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 }));
        }
Exemple #10
0
        /// <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);
        }
Exemple #11
0
        /// <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
                }));
            }