diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props
index 067ba79788..e274d05a71 100644
--- a/eng/ProjectReferences.props
+++ b/eng/ProjectReferences.props
@@ -79,6 +79,7 @@
+
diff --git a/src/Middleware/RequestQueue/ref/Microsoft.AspNetCore.RequestQueue.csproj b/src/Middleware/RequestQueue/ref/Microsoft.AspNetCore.RequestQueue.csproj
new file mode 100644
index 0000000000..05f6a42637
--- /dev/null
+++ b/src/Middleware/RequestQueue/ref/Microsoft.AspNetCore.RequestQueue.csproj
@@ -0,0 +1,14 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Middleware/RequestQueue/ref/Microsoft.AspNetCore.RequestQueue.netcoreapp3.0.cs b/src/Middleware/RequestQueue/ref/Microsoft.AspNetCore.RequestQueue.netcoreapp3.0.cs
new file mode 100644
index 0000000000..dc22bca076
--- /dev/null
+++ b/src/Middleware/RequestQueue/ref/Microsoft.AspNetCore.RequestQueue.netcoreapp3.0.cs
@@ -0,0 +1,51 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Builder
+{
+ public static partial class HstsBuilderExtensions
+ {
+ public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseHsts(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) { throw null; }
+ }
+ public static partial class HstsServicesExtensions
+ {
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddHsts(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configureOptions) { throw null; }
+ }
+ public static partial class HttpsPolicyBuilderExtensions
+ {
+ public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseHttpsRedirection(this Microsoft.AspNetCore.Builder.IApplicationBuilder app) { throw null; }
+ }
+ public static partial class HttpsRedirectionServicesExtensions
+ {
+ public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddHttpsRedirection(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configureOptions) { throw null; }
+ }
+}
+namespace Microsoft.AspNetCore.HttpsPolicy
+{
+ public partial class HstsMiddleware
+ {
+ public HstsMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.Extensions.Options.IOptions options) { }
+ public HstsMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.Extensions.Options.IOptions options, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
+ public System.Threading.Tasks.Task Invoke(Microsoft.AspNetCore.Http.HttpContext context) { throw null; }
+ }
+ public partial class HstsOptions
+ {
+ public HstsOptions() { }
+ public System.Collections.Generic.IList ExcludedHosts { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
+ public bool IncludeSubDomains { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public System.TimeSpan MaxAge { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public bool Preload { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ }
+ public partial class HttpsRedirectionMiddleware
+ {
+ public HttpsRedirectionMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.Extensions.Options.IOptions options, Microsoft.Extensions.Configuration.IConfiguration config, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
+ public HttpsRedirectionMiddleware(Microsoft.AspNetCore.Http.RequestDelegate next, Microsoft.Extensions.Options.IOptions options, Microsoft.Extensions.Configuration.IConfiguration config, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature serverAddressesFeature) { }
+ public System.Threading.Tasks.Task Invoke(Microsoft.AspNetCore.Http.HttpContext context) { throw null; }
+ }
+ public partial class HttpsRedirectionOptions
+ {
+ public HttpsRedirectionOptions() { }
+ public int? HttpsPort { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public int RedirectStatusCode { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ }
+}
diff --git a/src/Middleware/RequestQueue/sample/RequestQueueSample.csproj b/src/Middleware/RequestQueue/sample/RequestQueueSample.csproj
new file mode 100644
index 0000000000..57892c2249
--- /dev/null
+++ b/src/Middleware/RequestQueue/sample/RequestQueueSample.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
diff --git a/src/Middleware/RequestQueue/sample/Startup.cs b/src/Middleware/RequestQueue/sample/Startup.cs
new file mode 100644
index 0000000000..5768d045e6
--- /dev/null
+++ b/src/Middleware/RequestQueue/sample/Startup.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace RequestQueueSample
+{
+ public class Startup
+ {
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
+ {
+ }
+
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
+ {
+ app.Run(async context =>
+ {
+ await context.Response.WriteAsync("Hello world!");
+ });
+ }
+
+ // Entry point for the application.
+ public static void Main(string[] args)
+ {
+ var host = new WebHostBuilder()
+ .UseKestrel()
+ .UseContentRoot(Directory.GetCurrentDirectory()) // for the cert file
+ .ConfigureLogging(factory =>
+ {
+ factory.SetMinimumLevel(LogLevel.Debug);
+ factory.AddConsole();
+ })
+ .UseStartup()
+ .Build();
+
+ host.Run();
+ }
+ }
+}
diff --git a/src/Middleware/RequestQueue/src/Microsoft.AspNetCore.RequestQueue.csproj b/src/Middleware/RequestQueue/src/Microsoft.AspNetCore.RequestQueue.csproj
new file mode 100644
index 0000000000..ddc77f3682
--- /dev/null
+++ b/src/Middleware/RequestQueue/src/Microsoft.AspNetCore.RequestQueue.csproj
@@ -0,0 +1,19 @@
+
+
+
+ ASP.NET Core middleware for queuing incoming HTTP requests, to avoid threadpool starvation.
+ netcoreapp3.0
+ $(NoWarn);CS1591
+ true
+ true
+ $(WarningsNotAsErrors);CS1591
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Middleware/RequestQueue/src/SemaphoreWrapper.cs b/src/Middleware/RequestQueue/src/SemaphoreWrapper.cs
new file mode 100644
index 0000000000..aff2f66919
--- /dev/null
+++ b/src/Middleware/RequestQueue/src/SemaphoreWrapper.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.AspNetCore.RequestQueue
+{
+ internal class SemaphoreWrapper
+ {
+ private static SemaphoreSlim semaphore;
+
+ public SemaphoreWrapper(int qlength)
+ {
+ semaphore = new SemaphoreSlim(qlength);
+ }
+
+ public Task EnterQueue()
+ {
+ return semaphore.WaitAsync();
+ }
+
+ public void LeaveQueue()
+ {
+ semaphore.Release();
+ }
+
+ public int SpotsLeft
+ {
+ get => semaphore.CurrentCount;
+ }
+ }
+}
diff --git a/src/Middleware/RequestQueue/test/Microsoft.AspNetCore.RequestQueue.Tests.csproj b/src/Middleware/RequestQueue/test/Microsoft.AspNetCore.RequestQueue.Tests.csproj
new file mode 100644
index 0000000000..b1559a97fe
--- /dev/null
+++ b/src/Middleware/RequestQueue/test/Microsoft.AspNetCore.RequestQueue.Tests.csproj
@@ -0,0 +1,10 @@
+
+
+
+ netcoreapp3.0
+
+
+
+
+
+
diff --git a/src/Middleware/RequestQueue/test/SemaphoreWrapperTests.cs b/src/Middleware/RequestQueue/test/SemaphoreWrapperTests.cs
new file mode 100644
index 0000000000..33bf6ea861
--- /dev/null
+++ b/src/Middleware/RequestQueue/test/SemaphoreWrapperTests.cs
@@ -0,0 +1,56 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Xunit;
+using System.Threading;
+using System.Threading.Tasks;
+using System;
+using System.Runtime.CompilerServices;
+using Microsoft.AspNetCore.Testing;
+
+namespace Microsoft.AspNetCore.RequestQueue.Tests
+{
+ public class SemaphoreWrapperTests
+ {
+ [Fact]
+ public async Task TestBehavior()
+ {
+ var s = new SemaphoreWrapper(1);
+ Assert.Equal(1, s.SpotsLeft);
+
+ await s.EnterQueue();
+ Assert.Equal(0, s.SpotsLeft);
+
+ s.LeaveQueue();
+ Assert.Equal(1, s.SpotsLeft);
+ }
+
+ [Fact]
+ public void TestQueueLength()
+ {
+ var s = new SemaphoreWrapper(2);
+
+ var t1 = s.EnterQueue();
+ Assert.True(t1.IsCompleted);
+
+ var t2 = s.EnterQueue();
+ Assert.True(t2.IsCompleted);
+
+ var t3 = s.EnterQueue();
+ Assert.False(t3.IsCompleted);
+ }
+
+ [Fact]
+ public async Task TestWaiting()
+ {
+ var s = new SemaphoreWrapper(1);
+ await s.EnterQueue();
+
+ var waitingTask = s.EnterQueue();
+ Assert.False(waitingTask.IsCompleted);
+
+ s.LeaveQueue();
+ await waitingTask.TimeoutAfter(TimeSpan.FromSeconds(1));
+ }
+ }
+}
diff --git a/src/Middleware/build.cmd b/src/Middleware/build.cmd
new file mode 100644
index 0000000000..033fe6f614
--- /dev/null
+++ b/src/Middleware/build.cmd
@@ -0,0 +1,3 @@
+@ECHO OFF
+SET RepoRoot=%~dp0..\..
+%RepoRoot%\build.cmd -projects %~dp0\**\*.*proj %*