This is a continuation of this issue.
Find 2 different FREE ports for 2 kestrel servers
I was able to start 2 kestrel servers from Console app using dynamic ports and on some computers it works fine, but sometimes this approach fails.
TLDR
For testing purposes, I created Firewall rule that opens all ports for all protocols and all apps, and then completely disabled Firewall itself. For clarity, I'm not using IIS and start Kestrel manually from C# Console application as local server.
Issues
public static IWebHost Run<T>(ServerOptions options = null) where T : class
{
var configuration = new ConfigurationBuilder().Build();
var urls = new[]
{
"http://0.0.0.0:0",
"https://0.0.0.0:0"
};
var environment = WebHost
.CreateDefaultBuilder(new string[0])
.ConfigureServices(o => o.AddSingleton(options))
.UseConfiguration(configuration)
.UseUrls(urls)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
//.UseIISIntegration()
.UseStartup<T>()
.Build();
environment.RunAsync();
return environment;
}
// Issue #1
// On some computers
// webAddresses = ["http://0.0.0.0:50210", "https://0.0.0.0:50220"]
// On other computers and even for 2 users on the same Windows server it doesn't create HTTPS
// webAddresses = ["http://0.0.0.0:50210"]
var webEnvironment = Server.Run<Startup>();
var webAddresses = webEnvironment.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
Hacky solution
There is an actual incompatibility between Windows Server 2012 and Kestrel. To make them compatible, I had to enforce Kestrel to use HTTP/1 protocol instead of HTTP/2 set by default.
https://stackoverflow.com/a/59756935/437393
Final Version for ASP Core 3.1 and Windows Server 2012
public static IWebHost Run<T>(ServerOptions options = null) where T : class
{
//var urls = new[]
//{
// "http://127.0.0.1:0",
// "https://127.0.0.1:0"
//};
var configuration = new ConfigurationBuilder().Build();
var environment = WebHost
.CreateDefaultBuilder(new string[0])
.ConfigureServices(o => o.AddSingleton(options))
.UseConfiguration(configuration)
//.UseUrls(urls)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel(o =>
{
o.Listen(IPAddress.Loopback, 0, v => v.Protocols = HttpProtocols.Http1); // FIX!!!
//o.Listen(IPAddress.Loopback, 0, v =>
//{
// v.UseHttps();
// v.Protocols = HttpProtocols.Http1;
//});
})
.UseStartup<T>()
.Build();
environment.RunAsync();
return environment;
}
Issue with inability to establish HTTPS connection is still open, because when I use hacky solution above and try to create HTTPS instead of HTTP I get an exception
Cannot access a disposed object. Object name: 'IServiceProvider'
In this line
var serviceEnvironment = Server.Run<ServiceStartup>();
var webEnvironment = Server.Run<WebStartup>(); // Exception happens here
Maybe, because Kestrel is trying to reuse existing connection from the first server...
Update
As far as I can create only one connection, decided to always use HTTPS with auto-generated certificate for localhost and disabled SSL verification in CefSharp.
public static IWebHost Run<T>(ServerOptions options = null) where T : class
{
var configuration = new ConfigurationBuilder().Build();
var environment = WebHost
.CreateDefaultBuilder(new string[0])
.ConfigureServices(o => o.AddSingleton(options))
.UseConfiguration(configuration)
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 0, o =>
{
o.UseHttps("Certificate.pfx", "SomePassword");
o.Protocols = HttpProtocols.Http1;
});
})
.UseStartup<T>()
.Build();
environment.StartAsync();
return environment;
}
// Disable errors about invalid local certificate in CefSharp
var browserOptions = new BrowserSettings
{
WebSecurity = CefState.Disabled,
FileAccessFromFileUrls = CefState.Enabled,
UniversalAccessFromFileUrls = CefState.Enabled
};
Browser.BrowserSettings = browserOptions;