Internet Information Service (IIS) is a pain for developers and may cause more troubles for them on a shared host where they don't have full access. One of the usual problems that I encounter is the IIS Application Pool's Idle Timeout which I can't update the settings due to insufficient permissions. Let's see which problem that the Idle Timeout causes and how to deal with it.
1. The Problem of IIS Idle Timeout
If your application implements Session or Cookie Authentication which persists user's login data and is hosted on a Windows Server by IIS, you may notice the application forced you to logout every 20 minutes even when you set the expiration date to be much more later than that. This is because of the IIS Idle Timeout is set to 20 minutes by default which causes the worker process to shutdown after the set interval time.
In my application, I used .Net Core Cookie Authentication and keep the data persistent for a year if user login and select Remember Me option.
AccountController.cs
[HttpPost] public ActionResult Login(LoginViewModel model) { try { bool valid = false; if (ModelState.IsValid) valid = _userManager.Login(model); if (valid) { var userClaims = new List<Claim>() { new Claim(ClaimTypes.Name, "Lucas") }; var identity = new ClaimsIdentity(userClaims, CookieAuthenticationDefaults.AuthenticationScheme); HttpContext.SignInAsync(new ClaimsPrincipal(identity), new AuthenticationProperties { IsPersistent = model.RememberMe, ExpiresUtc = model.RememberMe ? DateTime.UtcNow.AddDays(365) : DateTime.UtcNow.AddSeconds(5) }); } else ModelState.AddModelError("", "Incorrect Password!"); return PartialView("_Login", model); } catch { ModelState.AddModelError("", "Something Wrong. Please Try Again!"); return PartialView("_Login", model); } }
StartUp.cs
public void ConfigureServices(IServiceCollection services) { //Omitted Code... //... services.Configure<SecurityStampValidatorOptions>(options => { options.ValidationInterval = TimeSpan.FromDays(365); }); services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(config => { config.Cookie.Name = "__MyBlogCookie__"; config.LoginPath = "/Home/Login"; config.SlidingExpiration = true; config.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest; config.Cookie.Expiration = TimeSpan.FromSeconds(10); config.ExpireTimeSpan = TimeSpan.FromSeconds(10); }); services.AddAuthorization(); //... }
However, the application keeps asking me to login again after every 20 minutes of being idle. We know it's the problem of IIS Idle Timeout and will see how to resolve it in 2 case scenarios: when you have and when you don't have permission to update IIS App Pool's settings.
2. Resolve IIS Idle Timeout Problem When You Have Permission to IIS Settings
It's an easy task if you are server administrator and able to update IIS App Pool Settings. You just need to go to Advanced Settings and either extend the Idle Timeout value to be more than 20 or set it to 0 so that there won't be any Idle Timeout period.
3. Resolve IIS Idle Timeout Problem When You Don't Have Permission to IIS Settings
It's me in this case because I purchased a shared GoDaddy Windows host server which doesn't allow me to update IIS Settings. Unfortunately, the App Pool Idle Timeout is set to be 20 by default so my application keeps asking me to login again after 20 minutes.
So, I need to come up with a solution to automatically make a request to the server every 1 minute. By doing so, the worker process will be kept busy and won't be shutdown by IIS. Let see how I did it in my .Net Core MVC project.
Step 1: Create a Thread Process
The idea is to keep a background running process which can be invoked every 1 minute since the application starts. A thread process can be used to approach this implementation. Create a static class called MyAppContext
MyAppContext.cs
public static class MyAppContext { private static IMemoryCache _cache; public static void Configure(IMemoryCache cache) { _cache = cache; } public static void SetupRefreshJob() { Action remove = _cache.Get("Refresh") as Action; if (remove is Action) { _cache.Remove("Refresh"); remove.EndInvoke(null); } //get the worker Action work = () => { while (true) { Thread.Sleep(60000); WebClient refresh = new WebClient(); try { refresh.UploadString("https://lucasology.com", string.Empty); } catch (Exception ex) { //Log Error here } finally { refresh.Dispose(); } } }; Task.Run(() => work.Invoke()); //add this job to cache _cache.Set("Refresh", work, new MemoryCacheEntryOptions() { AbsoluteExpiration = DateTimeOffset.MaxValue, SlidingExpiration = TimeSpan.FromDays(365), Priority = CacheItemPriority.Normal }); } }
In this class, we create a work delegate function which is invoked every 1 minute. This function just does 1 simple job which is uploading an empty string to the server. That's how to keep the worker process from being shutdown and from being idle.
Step 2: Call SetupRefreshJob function in StartUp.cs
We need to fire this refresh job once the application starts. That is when we need to look at StartUp class where we can inject dependency for IMemoryCache as well as start the SetupRefreshJob.
StartUp.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { //Omitted Code... //... MyAppContext.Configure(app.ApplicationServices.GetRequiredService<IMemoryCache>()); MyAppContext.SetupRefreshJob(); //... }
Done! So now your application can keep the IIS App Pool busy every minute and avoid being shutdown. I hope that this post is helpful to you. Please let me know your thoughts in the comment section below. See you next time!