public UpgradeHandshake(
     HttpContext context,
     IHttpUpgradeFeature upgradeFeature)
 {
     _context        = context;
     _upgradeFeature = upgradeFeature;
 }
Exemple #2
0
        public WebSocketRequest(HttpContext context, IHttpUpgradeFeature upgradeFeature)
        {
            Context            = context;
            IsWebSocketRequest = upgradeFeature.IsUpgradableRequest && CheckSupportedWebSocketRequest();

            _upgradeFeature = upgradeFeature;
        }
 public WebSocketConnectionFeature(HttpContext context, ChannelFactory channelFactory, IHttpUpgradeFeature upgradeFeature, ILoggerFactory loggerFactory)
 {
     _channelFactory = channelFactory;
     _context        = context;
     _upgradeFeature = upgradeFeature;
     _logger         = loggerFactory.CreateLogger <WebSocketConnectionFeature>();
 }
 public UpgradeHandshake(HttpContext context, IHttpUpgradeFeature upgradeFeature, WebSocketOptions options, ILogger logger)
 {
     _context        = context;
     _upgradeFeature = upgradeFeature;
     _options        = options;
     _logger         = logger;
 }
 public UpgradeFeatureLoggingDecorator(IHttpUpgradeFeature innerUpgradeFeature, HttpResponse response, HttpLoggingOptions options, ILogger logger)
 {
     _innerUpgradeFeature = innerUpgradeFeature ?? throw new ArgumentNullException(nameof(innerUpgradeFeature));
     _response            = response ?? throw new ArgumentNullException(nameof(response));
     _options             = options ?? throw new ArgumentNullException(nameof(options));
     _logger = logger ?? throw new ArgumentNullException(nameof(logger));
 }
 public MazeSocketUpgrader(HttpContext context, IHttpUpgradeFeature upgradeFeature,
                           MazeSocketOptions options)
 {
     _context        = context;
     _upgradeFeature = upgradeFeature;
     _options        = options;
 }
        public WebSocketRequest(HttpContext context, IHttpUpgradeFeature upgradeFeature, WebSocketServerOptions options, ILogger logger)
        {
            Context            = context;
            IsWebSocketRequest = upgradeFeature.IsUpgradableRequest && CheckSupportedWebSocketRequest();

            m_upgradeFeature = upgradeFeature;
            m_options        = options;
            m_logger         = logger;
        }
Exemple #8
0
        public WebSocketRequest(HttpContext context, IHttpUpgradeFeature upgradeFeature)
        {
            _context        = context;
            _upgradeFeature = upgradeFeature;

            if (upgradeFeature.IsUpgradableRequest)
            {
                IsWebSocketRequest = CheckSupportedWebSocketRequest();
            }
        }
Exemple #9
0
        /// <summary>
        /// Process an individual request.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns>The task object representing the asynchronous operation.</returns>
        public Task Invoke(HttpContext context)
        {
            IHttpUpgradeFeature upgradeFeature = context.Features.Get <IHttpUpgradeFeature>();

            if (upgradeFeature != null)
            {
                context.Features.Set <IHttpWebSocketFeature>(new HttpWebSocketCompressionFeature(context, upgradeFeature, _options));
            }

            return(_next(context));
        }
Exemple #10
0
    public async Task OriginalUpgradeFeatureIsRestoredBeforeMiddlewareCompletes(HttpLoggingFields loggingFields)
    {
        var options = CreateOptionsAccessor();

        options.CurrentValue.LoggingFields = loggingFields;

        var letBodyFinish = new TaskCompletionSource();

        var httpContext = new DefaultHttpContext();

        var upgradeFeatureMock = new Mock <IHttpUpgradeFeature>();

        upgradeFeatureMock.Setup(m => m.IsUpgradableRequest).Returns(true);
        upgradeFeatureMock.Setup(m => m.UpgradeAsync()).ReturnsAsync(Stream.Null);
        httpContext.Features.Set <IHttpUpgradeFeature>(upgradeFeatureMock.Object);

        IHttpUpgradeFeature upgradeFeature = null;

        var middleware = new HttpLoggingMiddleware(
            async c =>
        {
            upgradeFeature = c.Features.Get <IHttpUpgradeFeature>();
            await letBodyFinish.Task;
        },
            options,
            LoggerFactory.CreateLogger <HttpLoggingMiddleware>());

        var middlewareTask = middleware.Invoke(httpContext);

        Assert.True(upgradeFeature is UpgradeFeatureLoggingDecorator);

        letBodyFinish.SetResult();
        await middlewareTask;

        Assert.False(httpContext.Features.Get <IHttpUpgradeFeature>() is UpgradeFeatureLoggingDecorator);
    }
        private bool IsMazeSocketRequest(HttpContext context, IHttpUpgradeFeature upgradeFeature)
        {
            if (!upgradeFeature.IsUpgradableRequest)
            {
                return(false);
            }

            if (!string.Equals(context.Request.Method, "GET", StringComparison.OrdinalIgnoreCase))
            {
                return(false);
            }

            var headers = new List <KeyValuePair <string, string> >();

            foreach (var headerName in HandshakeHelpers.NeededHeaders)
            {
                foreach (var value in context.Request.Headers.GetCommaSeparatedValues(headerName))
                {
                    headers.Add(new KeyValuePair <string, string>(headerName, value));
                }
            }

            return(HandshakeHelpers.CheckSupportedRequest(headers));
        }
 internal HttpWebSocketCompressionFeature(HttpContext context, IHttpUpgradeFeature upgradeFeature, WebSocketCompressionOptions options)
 {
     _context        = context ?? throw new ArgumentNullException(nameof(context));
     _upgradeFeature = upgradeFeature ?? throw new ArgumentNullException(nameof(upgradeFeature));
     _options        = options ?? throw new ArgumentNullException(nameof(options));
 }
Exemple #13
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);
        }
 public UpgradeHandshake(HttpContext context, IHttpUpgradeFeature upgradeFeature, WebSocketOptions options)
 {
     _context        = context;
     _upgradeFeature = upgradeFeature;
     _options        = options;
 }
Exemple #15
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 HttpUpgradeFeatureWrapper(IClock clock, HttpContext httpContext, IHttpUpgradeFeature upgradeFeature)
 {
     _clock              = clock ?? throw new ArgumentNullException(nameof(clock));
     HttpContext         = httpContext ?? throw new ArgumentNullException(nameof(httpContext));
     InnerUpgradeFeature = upgradeFeature ?? throw new ArgumentNullException(nameof(upgradeFeature));
 }
 public UpgradeHandshake(HttpContext context, IHttpUpgradeFeature upgradeFeature, WebSocketOptions options)
 {
     _context = context;
     _upgradeFeature = upgradeFeature;
     _options = options;
 }
        private async Task DoWebSocket(HttpContext context, IHttpUpgradeFeature upgrade)
        {
            var service  = context.Features.Get <IService>();
            var endPoint = service.IpEndPoint;
            var socket   = await System.IO.Pipelines.Networking.Sockets.SocketConnection.ConnectAsync(endPoint, _factory);

            try
            {
                var writer = socket.Output.Alloc();

                writer.Append(context.Request.Method, TextEncoder.Utf8);
                writer.Write(_space);
                writer.Append(context.Request.Path.Value, TextEncoder.Utf8);
                writer.Write(_space);
                writer.Append(context.Request.Protocol, TextEncoder.Utf8);
                writer.Write(_endOfLine);
                foreach (var header in context.Request.Headers)
                {
                    writer.Append(header.Key, TextEncoder.Utf8);
                    writer.Write(_headerSplit);
                    writer.Append(string.Join(", ", (IEnumerable <string>)header.Value), TextEncoder.Utf8);
                    writer.Write(_endOfLine);
                }
                writer.Write(_endOfLine);
                await writer.FlushAsync();

                while (true)
                {
                    var reader = await socket.Input.ReadAsync();

                    var buffer = reader.Buffer;
                    try
                    {
                        if (!buffer.TrySliceTo(_headersEnd, out ReadableBuffer headers, out ReadCursor cursor))
                        {
                            continue;
                        }
                        buffer = buffer.Slice(cursor).Slice(_headersEnd.Length);
                        if (!headers.TrySliceTo(_endOfLine, out ReadableBuffer line, out cursor))
                        {
                            throw new InvalidOperationException();
                        }
                        headers = headers.Slice(cursor).Slice(_endOfLine.Length);
                        while (headers.Length > 0)
                        {
                            if (!headers.TrySliceTo(_endOfLine, out ReadableBuffer headerLine, out cursor))
                            {
                                headerLine = headers;
                                headers    = headers.Slice(headers.Length);
                            }
                            else
                            {
                                headers = headers.Slice(cursor).Slice(_endOfLine.Length);
                            }
                            if (!headerLine.TrySliceTo(_headerSplit, out ReadableBuffer key, out cursor))
                            {
                                throw new NotImplementedException();
                            }
                            var values = headerLine.Slice(cursor).Slice(_headerSplit.Length).GetUtf8String().Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
                            context.Response.Headers[key.GetUtf8String()] = new Microsoft.Extensions.Primitives.StringValues(values);
                        }
                        break;
                    }
                    finally
                    {
                        socket.Input.Advance(buffer.Start, buffer.End);
                    }
                }