15

I'm currently running my application under either Kestrel (locally) or IIS InProcess (production).

return WebHost.CreateDefaultBuilder(args)
    .ConfigureKestrel(options => options.AddServerHeader = false)
    .UseIIS()
    .UseStartup<Startup>();

I'd like to be able to get the hosting server name at runtime in a controller so I can achieve the following:

if (hostingServer == "kestrel")
{
    DoSomething();
}
else
{
    DoSomethingElse();
}

In this specific case it's to get around the fact that non-ascii characters are not supported in response headers with Kestrel. Ideally I would remove the non-ascii header but currently it's required for legacy interoperability.

Any help would be massively appreciated.

Charlie
  • 393
  • 3
  • 10
  • Could you not use `environment.IsDevelopment()` in the startup to achieve that? – Greg Apr 25 '19 at 15:47
  • That's what I'm currently doing as a workaround but our acceptance tests run using Kestrel under Production (potentially should be staging) – Charlie Apr 25 '19 at 15:52

4 Answers4

5

The easiest way is probably reading System.Diagnostics.Process.GetCurrentProcess().ProcessName. If it is w3wp or iisexpress you know the host is IIS/IIS Express, while dotnet (or other names when you use self-contained deployment) indicates Kestrel. This will only work for an in process deployment. If you are out of process, this will not work. Learn more at https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module

Example:

/// <summary>
/// Check if this process is running on Windows in an in process instance in IIS
/// </summary>
/// <returns>True if Windows and in an in process instance on IIS, false otherwise</returns>
public static bool IsRunningInProcessIIS()
{
    if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
    {
        return false;
    }

    string processName = Path.GetFileNameWithoutExtension(Process.GetCurrentProcess().ProcessName);
    return (processName.Contains("w3wp", StringComparison.OrdinalIgnoreCase) ||
        processName.Contains("iisexpress", StringComparison.OrdinalIgnoreCase));
}
jjxtra
  • 20,415
  • 16
  • 100
  • 140
Lex Li
  • 60,503
  • 9
  • 116
  • 147
  • @jjxtra thanks for the edit, but clearly the question itself says "or IIS InProcess (production)", and I was answering that. – Lex Li Dec 28 '19 at 19:05
  • Thanks, this was very useful to me because I needed to detect if I was hosted in IIS/IIS Express in-process for finding out if I was under IIS MaxStackSize limit (256 KB for 32-bit, and 512 KB for 64-bit). If I am hosted out-of-process with IIS/IIS Express, this does not matter because the exe/dll who started the process is important for determining stack size. So if the process is not w3wp.exe or iisexpress.exe, I can ensure that I am running on normal CLR MaxStackSize (1MB for 32-bit and 4MB for 64-bit) because those specific exe files has the stack size restriction in their header. – gtdev Jan 22 '20 at 12:15
  • As @soniCue pointed out this also did not work for me. It could be from the app pool being set to No Managed Code or the Visual Studio debugger attach process running `bin\IISSupport\VSIISExeLauncher.exe` to launch/attach – mlhDev Jul 20 '23 at 18:06
5

Checking for process name didn't work for me, even when hosting in IIS with InProcess it still proxies to the dotnet process (my guess is that you would need to get parent process to get the w3wp process).

Internally .NET Core calls IsAspNetCoreModuleLoaded() in NativeMethods.cs as can be found in WebHostBuilderIISExtensions.cs. So checking for IIS can be done with the following code.

internal static class NativeMethods
{
    internal const string AspNetCoreModuleDll = "aspnetcorev2_inprocess.dll";

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    public static bool IsAspNetCoreModuleLoaded()
    {
        return GetModuleHandle(AspNetCoreModuleDll) != IntPtr.Zero;
    }
}
SoniCue
  • 319
  • 4
  • 6
  • if it's true, is that IIS? – Markus May 28 '20 at 17:19
  • Yes, it is. The application is then hosted within the IIS process. – SoniCue Jun 04 '20 at 18:21
  • this always returns true for me, I suppose because I am using AspNetCore... – Christian O. Nov 16 '22 at 15:03
  • @ChristianO. This depends on whether you use Kestrel, IIS express or IIS. The above code works when hosting in IIS using in-process hosting. Also note that in-process hosting has been enabled by default since ASP.NET Core 3.0. So it will return true unless you explicitly opt-out using the _AspNetCoreHostingModel_ property. https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/iis/in-process-hosting – SoniCue Dec 15 '22 at 09:54
0

When the application starts the hosting method can be exposed in IApplicationBuilder.ServerFeatures. Through here you can find items that reference Kestrel vs reverse proxy configuration.

The IApplicationBuilder available in the Startup.Configure method exposes the ServerFeatures property of type IFeatureCollection. Kestrel and HTTP.sys only expose a single feature each, IServerAddressesFeature, but different server implementations may expose additional functionality. IServerAddressesFeature can be used to find out which port the server implementation has bound at runtime.

The property is a collection, so you'll need to filter for specific hosting methods that pertain to IIS Reverse Proxy and Kestrel.

Greg
  • 11,302
  • 2
  • 48
  • 79
  • `IApplicationBuilder` is available during the `Configure()` all but if you are a service in `ConfigureServices()` you only have an `IServiceCollection` – mlhDev Jul 20 '23 at 18:03
0

Another approach would be testing for the IServer implementation type. For IIS In-Proc it will be IISHttpServer, and for IIS Out-of-Proc and for pure Kestrel it will be KestrelServerImpl. Unfortunately, these types are internal, so you need to check the string name, which is no more reliable than checking process name:

var server = serviceProvider.GetRequiredService<Microsoft.AspNetCore.Hosting.Server.IServer>();

if (server.GetType().Name.Equals("IISHttpServer", StringComparison.OrdinalIgnoreCase))
 {
   // IIS In-Proc
 }
 else
 {
   // Kestrel (including IIS Out-of-Proc)
 }

To make it somewhat more reliable, instead of ordinal comparison you can search the type name for something like "iis" which is more likely to remain in the type name after code refactoring.

sich
  • 505
  • 6
  • 10