Exemplo n.º 1
0
        public async Task Invoke_PropagatesExceptions()
        {
            var             ex          = new Exception("Kaboom!");
            bool            nextInvoked = false;
            RequestDelegate next        = (ctxt) =>
            {
                nextInvoked = true;
                throw ex;
            };
            var options = new HttpOptions
            {
                MaxOutstandingRequests = 10,
                MaxConcurrentRequests  = 5
            };
            var middleware = new HttpThrottleMiddleware(next, _loggerFactory, TimeSpan.FromSeconds(1));

            var resultEx = await Assert.ThrowsAsync <Exception>(async() =>
            {
                var httpContext = new DefaultHttpContext();
                await middleware.Invoke(httpContext, new OptionsWrapper <HttpOptions>(_httpOptions), _requestQueue, _performanceManager.Object, _metricsLogger.Object);
            });

            Assert.True(nextInvoked);
            Assert.Same(ex, resultEx);
        }
Exemplo n.º 2
0
        public HttpThrottleMiddlewareTests()
        {
            _functionDescriptor = new FunctionDescriptor("Test", null, null, new Collection <ParameterDescriptor>(), null, null, null);
            _scriptHost         = new Mock <IScriptJobHost>(MockBehavior.Strict);
            _metricsLogger      = new Mock <IMetricsLogger>(MockBehavior.Strict);
            _metricsLogger.Setup(p => p.LogEvent(MetricEventNames.FunctionInvokeThrottled, null, null)).Callback(() =>
            {
                Interlocked.Increment(ref _throttleMetricCount);
            });
            var environment          = SystemEnvironment.Instance;
            var mockServiceProvider  = new Mock <IServiceProvider>(MockBehavior.Strict);
            var healthMonitorOptions = new HostHealthMonitorOptions();

            _performanceManager = new Mock <HostPerformanceManager>(MockBehavior.Strict, environment, new OptionsWrapper <HostHealthMonitorOptions>(healthMonitorOptions), mockServiceProvider.Object);
            _httpOptions        = new HttpOptions();
            _loggerFactory      = new LoggerFactory();
            _loggerProvider     = new TestLoggerProvider();
            _loggerFactory.AddProvider(_loggerProvider);
            RequestDelegate next = (ctxt) =>
            {
                ctxt.Response.StatusCode = (int)HttpStatusCode.Accepted;
                return(Task.CompletedTask);
            };

            _middleware   = new HttpThrottleMiddleware(next, _loggerFactory, TimeSpan.FromSeconds(1));
            _requestQueue = new HttpRequestQueue(new OptionsWrapper <HttpOptions>(_httpOptions));
        }
Exemplo n.º 3
0
        public void ShouldEnable_ReturnsExpectedValue()
        {
            IOptions <HttpOptions> optionsWrapper = null;
            var scriptHostManagerMock             = new Mock <IScriptHostManager>(MockBehavior.Strict);
            var hostServiceProviderMock           = scriptHostManagerMock.As <IServiceProvider>();

            hostServiceProviderMock.Setup(p => p.GetService(typeof(IOptions <HttpOptions>))).Returns(() => optionsWrapper);
            var rootServiceProvider = new Mock <IServiceProvider>(MockBehavior.Strict);

            rootServiceProvider.Setup(p => p.GetService(typeof(IScriptHostManager))).Returns(scriptHostManagerMock.Object);

            Assert.False(HttpThrottleMiddleware.ShouldEnable(null));
            Assert.False(HttpThrottleMiddleware.ShouldEnable(rootServiceProvider.Object));

            var httpOptions = new HttpOptions();

            optionsWrapper = new OptionsWrapper <HttpOptions>(httpOptions);
            Assert.False(HttpThrottleMiddleware.ShouldEnable(rootServiceProvider.Object));

            httpOptions.MaxConcurrentRequests = 5;
            Assert.True(HttpThrottleMiddleware.ShouldEnable(rootServiceProvider.Object));

            httpOptions.MaxConcurrentRequests   = -1;
            httpOptions.DynamicThrottlesEnabled = true;
            Assert.True(HttpThrottleMiddleware.ShouldEnable(rootServiceProvider.Object));
        }
Exemplo n.º 4
0
        public async Task Invoke_MaxOutstandingRequestsExceeded_RequestsAreRejected()
        {
            int maxParallelism = 1;
            int maxQueueLength = 10;

            _httpOptions = new HttpOptions
            {
                MaxOutstandingRequests = maxQueueLength,
                MaxConcurrentRequests  = maxParallelism
            };
            _requestQueue = new HttpRequestQueue(new OptionsWrapper <HttpOptions>(_httpOptions));

            RequestDelegate next = async(ctxt) =>
            {
                await Task.Delay(100);

                ctxt.Response.StatusCode = (int)HttpStatusCode.Accepted;
            };

            var middleware = new HttpThrottleMiddleware(next, _loggerFactory, TimeSpan.FromSeconds(1));

            // expect requests past the threshold to be rejected
            var tasks        = new List <Task>();
            var httpContexts = new List <HttpContext>();

            for (int i = 0; i < 25; i++)
            {
                var httpContext = new DefaultHttpContext();
                httpContexts.Add(httpContext);
                tasks.Add(middleware.Invoke(httpContext, new OptionsWrapper <HttpOptions>(_httpOptions), _requestQueue, _performanceManager.Object, _metricsLogger.Object));
            }
            await Task.WhenAll(tasks);

            int countSuccess = httpContexts.Count(p => (HttpStatusCode)p.Response.StatusCode == HttpStatusCode.Accepted);

            Assert.Equal(maxQueueLength, countSuccess);
            int rejectCount = 25 - countSuccess;

            Assert.Equal(rejectCount, httpContexts.Count(p => p.Response.StatusCode == 429));

            IEnumerable <LogMessage> logMessages = _loggerProvider.GetAllLogMessages();

            Assert.Equal(rejectCount, logMessages.Count());
            Assert.True(logMessages.All(p => string.Compare("Http request queue limit of 10 has been exceeded.", p.FormattedMessage) == 0));

            // send a number of requests not exceeding the limit
            // expect all to succeed
            tasks        = new List <Task>();
            httpContexts = new List <HttpContext>();
            for (int i = 0; i < maxQueueLength; i++)
            {
                var httpContext = new DefaultHttpContext();
                httpContexts.Add(httpContext);
                tasks.Add(middleware.Invoke(httpContext, new OptionsWrapper <HttpOptions>(_httpOptions), _requestQueue, _performanceManager.Object, _metricsLogger.Object));
            }
            await Task.WhenAll(tasks);

            Assert.True(httpContexts.All(p => (HttpStatusCode)p.Response.StatusCode == HttpStatusCode.Accepted));
        }
Exemplo n.º 5
0
        public async Task Invoke_NoThrottle_DispatchesDirectly()
        {
            bool            nextInvoked = false;
            RequestDelegate next        = (ctxt) =>
            {
                nextInvoked = true;
                ctxt.Response.StatusCode = (int)HttpStatusCode.Accepted;
                return(Task.CompletedTask);
            };
            var options    = new HttpOptions();
            var middleware = new HttpThrottleMiddleware(next, _loggerFactory, TimeSpan.FromSeconds(1));

            var httpContext = new DefaultHttpContext();
            await middleware.Invoke(httpContext, new OptionsWrapper <HttpOptions>(_httpOptions), _requestQueue, _performanceManager.Object, _metricsLogger.Object);

            Assert.True(nextInvoked);
            Assert.Equal(HttpStatusCode.Accepted, (HttpStatusCode)httpContext.Response.StatusCode);
        }
        public async Task Invoke_MaxParallelism_RequestsAreThrottled()
        {
            int maxParallelism = 3;

            _httpOptions = new HttpOptions
            {
                MaxConcurrentRequests = maxParallelism
            };
            _requestQueue = new HttpRequestQueue(new OptionsWrapper <HttpOptions>(_httpOptions));

            int             count = 0;
            RequestDelegate next  = async(ctxt) =>
            {
                if (Interlocked.Increment(ref count) > maxParallelism)
                {
                    throw new Exception($"Max parallelism of {maxParallelism} exceeded. Current parallelism: {count}");
                }

                await Task.Delay(100);

                Interlocked.Decrement(ref count);
                ctxt.Response.StatusCode = (int)HttpStatusCode.Accepted;
            };

            var middleware = new HttpThrottleMiddleware(next, _loggerFactory, TimeSpan.FromSeconds(1));

            // expect all requests to succeed
            var tasks        = new List <Task>();
            var httpContexts = new List <HttpContext>();

            for (int i = 0; i < 20; i++)
            {
                var httpContext = new DefaultHttpContext();
                httpContexts.Add(httpContext);
                tasks.Add(middleware.Invoke(httpContext, new OptionsWrapper <HttpOptions>(_httpOptions), _requestQueue, _performanceManager.Object, _metricsLogger.Object));
            }
            await Task.WhenAll(tasks);

            Assert.True(httpContexts.All(p => (HttpStatusCode)p.Response.StatusCode == HttpStatusCode.Accepted));
        }
Exemplo n.º 7
0
        public async Task Invoke_HostIsOverloaded_RequestsAreRejected()
        {
            _httpOptions = new HttpOptions
            {
                DynamicThrottlesEnabled = true
            };
            _requestQueue = new HttpRequestQueue(new OptionsWrapper <HttpOptions>(_httpOptions));

            bool isOverloaded = false;

            _performanceManager.Setup(p => p.PerformanceCountersExceeded(It.IsAny <Collection <string> >(), It.IsAny <ILogger>())).Returns(() => isOverloaded);

            RequestDelegate next = async(ctxt) =>
            {
                await Task.Delay(100);

                ctxt.Response.StatusCode = (int)HttpStatusCode.Accepted;
            };
            var middleware = new HttpThrottleMiddleware(next, _loggerFactory, TimeSpan.FromMilliseconds(50));

            var tasks        = new List <Task>();
            var httpContexts = new List <HttpContext>();

            for (int i = 0; i < 10; i++)
            {
                if (i == 7)
                {
                    isOverloaded = true;
                }
                var httpContext = new DefaultHttpContext();
                httpContexts.Add(httpContext);
                await middleware.Invoke(httpContext, new OptionsWrapper <HttpOptions>(_httpOptions), _requestQueue, _performanceManager.Object, _metricsLogger.Object);
            }
            await Task.WhenAll(tasks);

            Assert.Equal(7, httpContexts.Count(p => (HttpStatusCode)p.Response.StatusCode == HttpStatusCode.Accepted));
            Assert.Equal(3, httpContexts.Count(p => (HttpStatusCode)p.Response.StatusCode == HttpStatusCode.TooManyRequests));
        }
Exemplo n.º 8
0
        public static IApplicationBuilder UseWebJobsScriptHost(this IApplicationBuilder builder, IApplicationLifetime applicationLifetime, Action <WebJobsRouteBuilder> routes)
        {
            IEnvironment environment = builder.ApplicationServices.GetService <IEnvironment>() ?? SystemEnvironment.Instance;
            IOptionsMonitor <StandbyOptions>         standbyOptions         = builder.ApplicationServices.GetService <IOptionsMonitor <StandbyOptions> >();
            IOptionsMonitor <HttpBodyControlOptions> httpBodyControlOptions = builder.ApplicationServices.GetService <IOptionsMonitor <HttpBodyControlOptions> >();
            IServiceProvider serviceProvider = builder.ApplicationServices;

            builder.UseMiddleware <HttpRequestBodySizeMiddleware>();
            builder.UseMiddleware <SystemTraceMiddleware>();
            builder.UseMiddleware <HostnameFixupMiddleware>();
            if (environment.IsLinuxConsumption())
            {
                builder.UseMiddleware <EnvironmentReadyCheckMiddleware>();
            }

            if (standbyOptions.CurrentValue.InStandbyMode)
            {
                builder.UseMiddleware <PlaceholderSpecializationMiddleware>();
            }

            // Specialization can change the CompatMode setting, so this must run later than
            // the PlaceholderSpecializationMiddleware
            builder.UseWhen(context => httpBodyControlOptions.CurrentValue.AllowSynchronousIO || context.Request.IsAdminDownloadRequest(), config =>
            {
                config.UseMiddleware <AllowSynchronousIOMiddleware>();
            });

            // This middleware must be registered before we establish the request service provider.
            builder.UseWhen(context => !context.Request.IsAdminRequest(), config =>
            {
                config.UseMiddleware <HostAvailabilityCheckMiddleware>();
            });

            builder.UseWhen(context => HostWarmupMiddleware.IsWarmUpRequest(context.Request, standbyOptions.CurrentValue.InStandbyMode, environment), config =>
            {
                config.UseMiddleware <HostWarmupMiddleware>();
            });

            // This middleware must be registered before any other middleware depending on
            // JobHost/ScriptHost scoped services.
            builder.UseMiddleware <ScriptHostRequestServiceProviderMiddleware>();

            if (environment.IsLinuxConsumption())
            {
                builder.UseMiddleware <AppServiceHeaderFixupMiddleware>();
            }

            builder.UseMiddleware <ExceptionMiddleware>();
            builder.UseWhen(HomepageMiddleware.IsHomepageRequest, config =>
            {
                config.UseMiddleware <HomepageMiddleware>();
            });
            builder.UseWhen(context => !context.Request.IsAdminRequest() && HttpThrottleMiddleware.ShouldEnable(serviceProvider), config =>
            {
                config.UseMiddleware <HttpThrottleMiddleware>();
            });

            builder.UseMiddleware <JobHostPipelineMiddleware>();
            builder.UseMiddleware <FunctionInvocationMiddleware>();

            // Register /admin/vfs, and /admin/zip to the VirtualFileSystem middleware.
            builder.UseWhen(VirtualFileSystemMiddleware.IsVirtualFileSystemRequest, config => config.UseMiddleware <VirtualFileSystemMiddleware>());

            // MVC routes (routes defined by Controllers like HostController, FunctionsController, ... must be added before functions/proxy routes so they are matched first and can not be overridden by functions or proxy routes)
            // source here: https://github.com/aspnet/Mvc/blob/master/src/Microsoft.AspNetCore.Mvc.Core/Builder/MvcApplicationBuilderExtensions.cs
            builder.UseMvc();

            // Ensure the HTTP binding routing is registered after all middleware
            builder.UseHttpBindingRouting(applicationLifetime, routes);

            return(builder);
        }
        public static IApplicationBuilder UseWebJobsScriptHost(this IApplicationBuilder builder, IApplicationLifetime applicationLifetime, Action <WebJobsRouteBuilder> routes)
        {
            IEnvironment environment = builder.ApplicationServices.GetService <IEnvironment>() ?? SystemEnvironment.Instance;
            IOptionsMonitor <StandbyOptions>         standbyOptions         = builder.ApplicationServices.GetService <IOptionsMonitor <StandbyOptions> >();
            IOptionsMonitor <HttpBodyControlOptions> httpBodyControlOptions = builder.ApplicationServices.GetService <IOptionsMonitor <HttpBodyControlOptions> >();
            IOptionsMonitor <HttpOptions>            httpOptions            = builder.ApplicationServices.GetService <IOptionsMonitor <HttpOptions> >();

            builder.UseMiddleware <SystemTraceMiddleware>();
            builder.UseMiddleware <HostnameFixupMiddleware>();
            if (environment.IsLinuxConsumption())
            {
                builder.UseMiddleware <EnvironmentReadyCheckMiddleware>();
            }

            if (standbyOptions.CurrentValue.InStandbyMode)
            {
                builder.UseMiddleware <PlaceholderSpecializationMiddleware>();
            }

            // Specialization can change the CompatMode setting, so this must run later than
            // the PlaceholderSpecializationMiddleware
            builder.UseWhen(context => httpBodyControlOptions.CurrentValue.AllowSynchronousIO || context.Request.IsAdminDownloadRequest(), config =>
            {
                config.UseMiddleware <AllowSynchronousIOMiddleware>();
            });

            // This middleware must be registered before we establish the request service provider.
            builder.UseWhen(context => !context.Request.IsAdminRequest(), config =>
            {
                config.UseMiddleware <HostAvailabilityCheckMiddleware>();
            });

            builder.UseWhen(context => HostWarmupMiddleware.IsWarmUpRequest(context.Request, standbyOptions.CurrentValue.InStandbyMode, environment), config =>
            {
                config.UseMiddleware <HostWarmupMiddleware>();
            });

            // This middleware must be registered before any other middleware depending on
            // JobHost/ScriptHost scoped services.
            builder.UseMiddleware <ScriptHostRequestServiceProviderMiddleware>();

            if (environment.IsLinuxConsumption())
            {
                builder.UseMiddleware <AppServiceHeaderFixupMiddleware>();
            }

            builder.UseMiddleware <ExceptionMiddleware>();
            builder.UseWhen(HomepageMiddleware.IsHomepageRequest, config =>
            {
                config.UseMiddleware <HomepageMiddleware>();
            });
            builder.UseWhen(context => HttpThrottleMiddleware.ShouldEnable(httpOptions.CurrentValue) && !context.Request.IsAdminRequest(), config =>
            {
                config.UseMiddleware <HttpThrottleMiddleware>();
            });
            builder.UseMiddleware <ResponseContextItemsCheckMiddleware>();
            builder.UseMiddleware <JobHostPipelineMiddleware>();
            builder.UseMiddleware <FunctionInvocationMiddleware>();

            // Register /admin/vfs, and /admin/zip to the VirtualFileSystem middleware.
            builder.UseWhen(VirtualFileSystemMiddleware.IsVirtualFileSystemRequest, config => config.UseMiddleware <VirtualFileSystemMiddleware>());

            // Ensure the HTTP binding routing is registered after all middleware
            builder.UseHttpBindingRouting(applicationLifetime, routes);

            builder.UseMvc();

            return(builder);
        }