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(); }
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(); }
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(); }
public DelegationRule CreateDelegationRule(string queueName, string uri) { var rule = new DelegationRule(_queue.UrlGroup, queueName, uri, _logger); _queue.UrlGroup.SetDelegationProperty(rule.Queue); return(rule); }
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(); }
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); } }
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(); }
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)); }
// 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); }); }