Exemple #1
0
    public async Task DelegateRequestTest()
    {
        var queueName = Guid.NewGuid().ToString();

        using var receiver = Utilities.CreateHttpServer(out var receiverAddress, async httpContext =>
        {
            await httpContext.Response.WriteAsync(_expectedResponseString);
        },
                                                        options =>
        {
            options.RequestQueueName = queueName;
        });

        DelegationRule destination = default;

        using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
        {
            var delegateFeature = httpContext.Features.Get <IHttpSysRequestDelegationFeature>();
            delegateFeature.DelegateRequest(destination);
            return(Task.CompletedTask);
        });

        var delegationProperty = delegator.Features.Get <IServerDelegationFeature>();

        destination = delegationProperty.CreateDelegationRule(queueName, receiverAddress);

        var responseString = await SendRequestAsync(delegatorAddress);

        Assert.Equal(_expectedResponseString, responseString);
        destination?.Dispose();
    }
Exemple #2
0
    public async Task DelegateAfterWriteToResponseBodyShouldThrowTest()
    {
        var queueName = Guid.NewGuid().ToString();

        using var receiver = Utilities.CreateHttpServer(out var receiverAddress, httpContext =>
        {
            httpContext.Response.StatusCode = StatusCodes.Status418ImATeapot;
            return(Task.CompletedTask);
        },
                                                        options =>
        {
            options.RequestQueueName = queueName;
        });

        DelegationRule destination = default;

        using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, async httpContext =>
        {
            await httpContext.Response.WriteAsync(_expectedResponseString);
            var delegateFeature = httpContext.Features.Get <IHttpSysRequestDelegationFeature>();
            Assert.False(delegateFeature.CanDelegate);
            Assert.Throws <InvalidOperationException>(() => delegateFeature.DelegateRequest(destination));
        });

        var delegationProperty = delegator.Features.Get <IServerDelegationFeature>();

        destination = delegationProperty.CreateDelegationRule(queueName, receiverAddress);

        var responseString = await SendRequestAsync(delegatorAddress);

        Assert.Equal(_expectedResponseString, responseString);
        destination?.Dispose();
    }
Exemple #3
0
    public async Task DelegateAfterRequestBodyReadShouldThrow()
    {
        var queueName = Guid.NewGuid().ToString();

        using var receiver = Utilities.CreateHttpServer(out var receiverAddress, httpContext =>
        {
            httpContext.Response.StatusCode = StatusCodes.Status418ImATeapot;
            return(Task.CompletedTask);
        },
                                                        options =>
        {
            options.RequestQueueName = queueName;
        });

        DelegationRule destination = default;

        using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, async httpContext =>
        {
            var memoryStream = new MemoryStream();
            await httpContext.Request.Body.CopyToAsync(memoryStream);
            var delegateFeature = httpContext.Features.Get <IHttpSysRequestDelegationFeature>();
            Assert.Throws <InvalidOperationException>(() => delegateFeature.DelegateRequest(destination));
        });

        var delegationProperty = delegator.Features.Get <IServerDelegationFeature>();

        destination = delegationProperty.CreateDelegationRule(queueName, receiverAddress);

        _ = await SendRequestWithBodyAsync(delegatorAddress);

        destination?.Dispose();
    }
Exemple #4
0
    public DelegationRule CreateDelegationRule(string queueName, string uri)
    {
        var rule = new DelegationRule(_queue.UrlGroup, queueName, uri, _logger);

        _queue.UrlGroup.SetDelegationProperty(rule.Queue);
        return(rule);
    }
Exemple #5
0
    public async Task DelegateAfterReceiverRestart()
    {
        var queueName = Guid.NewGuid().ToString();

        using var receiver = Utilities.CreateHttpServer(out var receiverAddress, async httpContext =>
        {
            await httpContext.Response.WriteAsync(_expectedResponseString);
        },
                                                        options =>
        {
            options.RequestQueueName = queueName;
        });

        DelegationRule destination = default;

        using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
        {
            var delegateFeature = httpContext.Features.Get <IHttpSysRequestDelegationFeature>();
            delegateFeature.DelegateRequest(destination);
            return(Task.CompletedTask);
        });

        var delegationProperty = delegator.Features.Get <IServerDelegationFeature>();

        destination = delegationProperty.CreateDelegationRule(queueName, receiverAddress);

        var responseString = await SendRequestAsync(delegatorAddress);

        Assert.Equal(_expectedResponseString, responseString);

        // Stop the receiver
        receiver?.Dispose();

        // Start the receiver again but this time we need to attach to the existing queue.
        // Due to https://github.com/dotnet/aspnetcore/issues/40359, we have to manually
        // register URL prefixes and attach the server's queue to them.
        using var receiverRestarted = (MessagePump)Utilities.CreateHttpServer(out receiverAddress, async httpContext =>
        {
            await httpContext.Response.WriteAsync(_expectedResponseString);
        },
                                                                              options =>
        {
            options.RequestQueueName = queueName;
            options.RequestQueueMode = RequestQueueMode.Attach;
            options.UrlPrefixes.Clear();
            options.UrlPrefixes.Add(receiverAddress);
        });
        AttachToUrlGroup(receiverRestarted.Listener.RequestQueue);
        receiverRestarted.Listener.Options.UrlPrefixes.RegisterAllPrefixes(receiverRestarted.Listener.UrlGroup);

        responseString = await SendRequestAsync(delegatorAddress);

        Assert.Equal(_expectedResponseString, responseString);

        destination?.Dispose();
    }
Exemple #6
0
        public async Task CanDelegateOutOfProcess()
        {
            using var _ = StartLog(out var loggerFactory);

            var logger = loggerFactory.CreateLogger("CanDelegateOutOfProcess");

            // https://github.com/dotnet/aspnetcore/issues/8247
#pragma warning disable 0618
            var applicationPath = Path.Combine(TestPathUtilities.GetSolutionRootDirectory("HttpSysServer"), "test", "testassets",
                                               "DelegationSite");
#pragma warning restore 0618

            var deploymentParameters = new DeploymentParameters(
                applicationPath,
                ServerType.HttpSys,
                RuntimeFlavor.CoreClr,
                RuntimeArchitecture.x64)
            {
                EnvironmentName = "Testing",
                TargetFramework = Tfm.Default,
                ApplicationType = ApplicationType.Portable,
                PublishApplicationBeforeDeployment = true,
                StatusMessagesEnabled = true
            };

            var queueName = Guid.NewGuid().ToString();
            deploymentParameters.EnvironmentVariables["queue"] = queueName;

            using var deployer = new SelfHostDeployer(deploymentParameters, loggerFactory);
            var deploymentResult = await deployer.DeployAsync().DefaultTimeout();

            // Make sure the deployment really worked
            var responseString = await deploymentResult.HttpClient.GetStringAsync("").DefaultTimeout();

            Assert.Equal("Hello from delegatee", responseString);

            DelegationRule destination = default;
            using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
            {
                var delegateFeature = httpContext.Features.Get <IHttpSysRequestDelegationFeature>();
                delegateFeature.DelegateRequest(destination);
                return(Task.CompletedTask);
            });

            var delegationProperty = delegator.Features.Get <IServerDelegationFeature>();
            using (destination = delegationProperty.CreateDelegationRule(queueName, deploymentResult.ApplicationBaseUri))
            {
                // Send a request to the delegator that gets transfered to the delegatee in the other process.
                using var client = new HttpClient();
                responseString   = await client.GetStringAsync(delegatorAddress).DefaultTimeout();

                Assert.Equal("Hello from delegatee", responseString);
            }
        }
Exemple #7
0
    public async Task DelegateAfterReceiverRestart()
    {
        var queueName = Guid.NewGuid().ToString();

        using var receiver = Utilities.CreateHttpServer(out var receiverAddress, async httpContext =>
        {
            await httpContext.Response.WriteAsync(_expectedResponseString);
        },
                                                        options =>
        {
            options.RequestQueueName = queueName;
        });

        DelegationRule destination = default;

        using var delegator = Utilities.CreateHttpServer(out var delegatorAddress, httpContext =>
        {
            var delegateFeature = httpContext.Features.Get <IHttpSysRequestDelegationFeature>();
            delegateFeature.DelegateRequest(destination);
            return(Task.CompletedTask);
        });

        var delegationProperty = delegator.Features.Get <IServerDelegationFeature>();

        destination = delegationProperty.CreateDelegationRule(queueName, receiverAddress);

        var responseString = await SendRequestAsync(delegatorAddress);

        Assert.Equal(_expectedResponseString, responseString);

        // Stop the receiver
        receiver?.Dispose();

        // Start the receiver again but this time we need to use CreateOrAttach to attach to the existing queue and setup the UrlPrefixes
        using var receiverRestarted = (MessagePump)Utilities.CreateHttpServer(out receiverAddress, async httpContext =>
        {
            await httpContext.Response.WriteAsync(_expectedResponseString);
        },
                                                                              options =>
        {
            options.RequestQueueName = queueName;
            options.RequestQueueMode = RequestQueueMode.CreateOrAttach;
            options.UrlPrefixes.Clear();
            options.UrlPrefixes.Add(receiverAddress);
        });

        responseString = await SendRequestAsync(delegatorAddress);

        Assert.Equal(_expectedResponseString, responseString);

        destination?.Dispose();
    }
Exemple #8
0
    internal unsafe void Delegate(DelegationRule destination)
    {
        if (destination == null)
        {
            throw new ArgumentNullException(nameof(destination));
        }
        if (Request.HasRequestBodyStarted)
        {
            throw new InvalidOperationException("This request cannot be delegated, the request body has already started.");
        }
        if (Response.HasStarted)
        {
            throw new InvalidOperationException("This request cannot be delegated, the response has already started.");
        }

        var source = Server.RequestQueue;

        uint statusCode;

        fixed(char *uriPointer = destination.UrlPrefix)
        {
            var property = new HttpApiTypes.HTTP_DELEGATE_REQUEST_PROPERTY_INFO()
            {
                PropertyId         = HttpApiTypes.HTTP_DELEGATE_REQUEST_PROPERTY_ID.DelegateRequestDelegateUrlProperty,
                PropertyInfo       = (IntPtr)uriPointer,
                PropertyInfoLength = (uint)System.Text.Encoding.Unicode.GetByteCount(destination.UrlPrefix)
            };

            // Passing 0 for delegateUrlGroupId allows http.sys to find the right group for the
            // URL passed in via the property above. If we passed in the receiver's URL group id
            // instead of 0, then delegation would fail if the receiver restarted.
            statusCode = HttpApi.HttpDelegateRequestEx(source.Handle,
                                                       destination.Queue.Handle,
                                                       Request.RequestId,
                                                       delegateUrlGroupId: 0,
                                                       propertyInfoSetSize: 1,
                                                       &property);
        }

        if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS)
        {
            throw new HttpSysException((int)statusCode);
        }

        Response.MarkDelegated();
    }
 private void DisposeDelegationRule(DelegationRule rule)
 {
     try
     {
         // Note that dispose doesn't properly cleanup everything.
         // This is only an issue if you want to create the same rule again in the same process.
         // Shutdown of the process properly cleans up everything.
         // https://github.com/dotnet/aspnetcore/issues/27126
         rule.Dispose();
     }
     catch (ArgumentNullException ex)
     {
         // There is a bug in rule cleanup that causes a failure
         // https://github.com/dotnet/aspnetcore/issues/26989
         // This failure then causes a null ref bug to get hit
         // https://github.com/dotnet/aspnetcore/issues/26982
         this.logger.LogWarning(ex, "Known issue with disposing delegation rules");
     }
 }
        public Task Invoke(HttpContext context)
        {
            var proxyFeature = context.Features.Get <IReverseProxyFeature>();

            // By always selecting the first destination, we prevent the ability to have multiple destinations
            // but Yarp's load balancing decision happens after this middleware is invoked :/
            DestinationInfo destination = proxyFeature.AvailableDestinations[0];

            DelegationRule delegationRule = this.delegationRuleProvider.GetDelegationRule(destination);

            if (delegationRule != null)
            {
                var feature = context.Features.Get <IHttpSysRequestDelegationFeature>();
                feature.DelegateRequest(delegationRule);

                this.logger.LogDebug($"Request '{context.Request.GetDisplayUrl()}' transfered to: {delegationRule.QueueName} - {delegationRule.UrlPrefix}");

                return(Task.CompletedTask);
            }

            this.logger.LogDebug($"Proxy request '{context.Request.GetDisplayUrl()}'");

            return(this.next(context));
        }
Exemple #11
0
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostApplicationLifetime lifetime, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            ILogger <Startup>        logger    = app.ApplicationServices.GetRequiredService <ILogger <Startup> >();
            IServerDelegationFeature delegator = app.ServerFeatures.Get <IServerDelegationFeature>();

            if (delegator == null)
            {
                throw new NotSupportedException($"The {nameof(IServerDelegationFeature)} is not supported by the current OS.");
            }

            DelegationRule[] rules = app.ApplicationServices.GetRequiredService <IOptions <DelegationConfig> >().Value.Rules
                                     .Where(ruleConfig => ruleConfig.Enabled)
                                     .Select(ruleConfig =>
            {
                var rule = delegator.CreateDelegationRule(ruleConfig.QueueName, ruleConfig.Uri);
                lifetime.ApplicationStopped.Register(() =>
                {
                    try
                    {
                        // Note that dispose doesn't properly cleanup everything.
                        // This is only an issue if you want to create the same rule again in the same process.
                        // Shutdown of the process properly cleans up everything.
                        // https://github.com/dotnet/aspnetcore/issues/27126
                        rule.Dispose();
                    }
                    catch (ArgumentNullException ex)
                    {
                        // There is a bug in rule cleanup that causes a failure
                        // https://github.com/dotnet/aspnetcore/issues/26989
                        // This failure then causes a null ref bug to get hit
                        // https://github.com/dotnet/aspnetcore/issues/26982
                        logger.LogWarning(ex, "Known issue with disposing delegation rules");
                    }
                });

                logger.LogInformation($"Added delegation rule: {ruleConfig.QueueName} - {ruleConfig.Uri}");

                return(rule);
            })
                                     .ToArray();

            uint ruleIndex = uint.MaxValue;

            app.Run(context =>
            {
                int index           = (int)(Interlocked.Increment(ref ruleIndex) % rules.Length);
                DelegationRule rule = rules[index];

                IHttpSysRequestDelegationFeature transferFeature = context.Features.Get <IHttpSysRequestDelegationFeature>();
                transferFeature.DelegateRequest(rule);

                logger.LogDebug($"Request '{context.Request.GetDisplayUrl()}' transfered to: {rule.QueueName} - {rule.UrlPrefix}");

                return(Task.CompletedTask);
            });
        }