How does "Require SSL" affect ASP.NET MVC application lifecycle? - asp.net

I have an application that taps into BeginRequest and EndRequest to set up and tear down NHibernate sessions like this:
BeginRequest += delegate
{
CurrentSessionContext.Bind(SessionFactory.OpenSession());
};
EndRequest += delegate
{
var session = CurrentSessionContext.Unbind(SessionFactory);
session.Dispose();
Container.Release(session);
};
This works fine when deployed in IIS, until I check the "Require SSL" box. Once I do this, I get a NullReferenceException at session.Dispose().
I haven't debugged this yet and, yes, the fix is trivial, but I'm just curious about how "Require SSL" affects the lifecycle of a request. Is a session not set up on the server in these cases?
EDIT: Just to clarify, I'm referring to the "Require SSL" option in IIS configuration for the application, not the RequireHttps attribute for controllers.

This one piqued my curiosity so I dug into it a little; sorry for the necromancy.
I created a simple project that wired up notifications for every lifecycle event on the application object, and set breakpoints on each one.
It turns out that when "Require SSL" is set and you access without SSL, most of the events are completely skipped. The first event to fire is LogRequest, followed by PostLogRequest, EndRequest, PreSendRequestContent, and PreSendRequestHeaders (in that order). No other events are fired.
So your code was crashing because the BeginRequest event was never fired, and the EndRequest delegate tried to Dispose() something that had never been created.
What's interesting to me is figuring out why IIS behaves like this. I suspect the reason is that IIS still needs to log invalid connection attempts, as well as send content and headers, even if the requested resource requires SSL. Something has to generate that friendly "forbidden" page, after all. What I don't know is why EndRequest is called at all when they didn't bother calling BeginRequest; I'm guessing there's some IIS/ASP cleanup code that depends on it.
This behavior varies depending on whether the application pool is running in "Integrated" or "Classic" mode. In "Classic" mode, the ASP.NET events all fire "in between" the IIS PreRequestHandlerExecute and PostRequestHandlerExecute events. You didn't say which you were running, but it has to be Integrated; otherwise you'd have seen the behavior you were expecting, i.e. none of your code would have executed at all.
Interestingly, if you try to subscribe to the LogRequest, PostLogRequest, or MapRequestHandler events when in Classic mode, you get a runtime exception; these only "make sense" in the context of the integrated pipeline.

Here is something that might help you
ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
ASP.NET Application Life Cycle Overview for IIS 7.0
You might want to do what you are doing in global.asax instead.

Related

Why is session not created if page is cached?

Question: Why is there no session created (and session cookie) when an aspx page requested is pulled from cache?
Background Information
I've made quite a few google searches, but I can't find anything that indicates that this is the intended behavior. Our desired behavior is that a new session/cookie are always generated regardless of whether the page requested is pulled from cache.
We are caching pages using the following code. (.NET 3.5, IIS 7.5)
Response.Cache.SetExpires(DateTime.Now.AddMonths(1));
Response.Cache.SetCacheability(HttpCacheability.Server);
Response.Cache.SetVaryByCustom("IsLoggedIn");
Response.Cache.VaryByParams["*"] = true;
Response.Cache.SetValidUntilExpires(true);
Response.AddCacheItemDependency("Pages");
Any relevant information would be greatly appreciated.
In order to understand why an output-cached request will not create a session, you need to understand a little bit about the ASP.NET Application Life Cycle, the events . The basics are that are a defined series of HttpApplication events that may fire during each request. These events are typically subscribed to using event handlers by either HttpModule implementations or a Global.asax file.
Not every application event fires on every request, but the events that do fire will always fire in the defined order. The order of events that fire on IIS 7
1. BeginRequest
2. AuthenticateRequest
PostAuthenticateRequest
3. AuthorizeRequest
PostAuthorizeRequest
4. ResolveRequestCache
PostResolveRequestCache
5. MapRequestHandler (Integrated Mode Only)
PostMapRequestHandler
6. AcquireRequestState
PostAcquireRequestState
7. PreRequestHandlerExecute
<--- the IHttpHandler.ProcessRequest() method is called here
PostRequestHandlerExecute
8. ReleaseRequestState
PostReleaseRequestState
9. UpdateRequestCache
PostUpdateRequestCache
10. LogRequest (Integrated Mode Only)
PostLogRequest (Integrated Mode Only)
11. EndRequest
12. PreSendRequestHeaders
13. PreSendRequestContent
How an IHttpModule Works
The IHttpModule interface looks like:
public interface IHttpModule
{
void Init(HttpApplication context);
void Dispose();
}
The Init() method is used to subscribes event handlers to the application events and the Dispose() method cleans up after the module when the application is done with it.
How ASP.NET Sessions Work
System.Web defines an IHttpModule implementation named System.Web.SessionState.SessionStateModule. If the session is not disabled in the web.config, the following event handlers get wired up:
// app is the current HttpApplication ;
app.AddOnAcquireRequestStateAsync(this.BeginAcquireState, this.EndAcquireState);
app.ReleaseRequestState += this.OnReleaseState;
app.EndRequest += this.OnEndRequest;
Sessions work differently depending on what session mode is running but the key thing to understand is that sessions are retrieved and created in the SessionStateModule.BeginAcquireState method and that method is wired up asynchronously to the AcquireRequestState event.
How Output Caching Works
System.Web defines an internal IHttpModule implementation named System.Web.Caching.OutputCacheModule. The Init() method looks like:
void IHttpModule.Init(HttpApplication app)
{
if (RuntimeConfig.GetAppConfig().OutputCache.EnableOutputCache)
{
app.ResolveRequestCache += new EventHandler(this.OnEnter);
app.UpdateRequestCache += new EventHandler(this.OnLeave);
}
}
The OnEnter method evaluates the cache parameters and looks for a cached response that matches the request. The OnLeave method caches cacheable responses. The one thing you need to know is that if the OnEnter method is successful in retrieving a cached response, it calls the CompleteRequest() method on the HttpApplication instance. The documentation for this method reads: "Causes ASP.NET to bypass all events and filtering in the HTTP pipeline chain of execution and directly execute the EndRequest event". So, any events that occur between the time that CompleteRequest() is called and the EndRequest() event are skipped.
Why is session not created if page is cached?
The SessionStateModule subscribes SessionStateModule.BeginAcquireState to the application's AcquireRequestState event, so that is where sessions are created and retrieved.
The OutputCacheModule subscribes OutputCacheModule.OnEnter to the application's ResolveRequestCache event, so that is where cached responses are retrieved and where CompleteRequest() gets called. That means that the following events will never fire for a cached request: MapRequestHandler/PostMapRequestHandler; AcquireRequestState/PostAcquireRequestState; PreRequestHandlerExecute; IHttpHandler.ProcessRequest; PostRequestHandlerExecute; ReleaseRequestState/PostReleaseRequestState; UpdateRequestCache/PostUpdateRequestCache; and, LogRequest/PostLogRequest.
If a cached response is retrieved, then HttpApplication.CompleteRequest() is called, the AcquireRequestState event never fires, and the session is never created.
What to do about it?
Whether disabling output cache on pages in favor of controls is a viable solution depends on: (1) whether the code that uses the session is in the controls or in the pages; (2) how much load your application instance needs to be able to support; and, (3) what other caching mechanisms exist in either the application or its supporting infrastructure. Depending on your infrastructure, there may be other factors to consider. For example, ASP.NET sessions can perform and behave very differently depending on the session state mode and on whether the environment is load balanced or not. Also, if you happened to be running an HTTP accelerator cache like Varnish (for example), enabling sessions on a previously output-cached ASP.NET paged might change the behavior from omitting a session to attaching a stale session that belongs to a different user. This is just a hypothetical example, but the point is that there may be additional factors to consider when making these kinds of decisions.
Apparently this is default .NET behavior. If you create a new app, enable session, cache an aspx page, and then have a new user pull the cached version of that page, they will be given no session/cookie.
As a workaround...I think we'll just disable page outputcache and use more aggressive control caching.

No Error Info in Microsoft.Websockets OnError event handler

I can't get websockets over WCF working at all.
I am using the websocket samples from Paul Batum. (https://github.com/paulbatum/WebSocket-Samples)
I have a Windows 8 operating system, .Net 4.5 and IIS 8 with websockets enabled.
When I run the WCFChat project, what I always get is the onerror followed by the onclose event firing on the client and server.
The onerror event argument on the client doesn't provide any meaning diagnostic info. Using Fiddler does not provide any information, too.
The OnError event handler on the server is the same. There are not event arguments or any other way to see why OnError is being called. "Call Stack" only shows External Code calling OnError method.
The question is how can I debug this?
OK. I had to use WebSocketHandler class. WebSocketHandler has the Error property which I wanted to see.

Cookieless ASP.Net sessions for static pages

I'm using ASP.Net cookieless sessions so that the session ID for the application is tracked by placing it in the URL via a 302 redirect, for example if the user were to access the below URL
http://yourserver/folder/default.aspx
They would then be redirect to a URL similar to the following which would then proceed to serve up the actual page content
http://yourserver/folder/(S(849799d1-7ec0-41dc-962d-a77e1b958b99))/default.aspx
The problem I have is that the entry point for the application is actually a static page (e.g. one with a .html extension), and ASP.Net is not issuing a session ID & redirecting the user for this page. This therefore means that links to ASP.Net hosted content (e.g. links, iframes etc...) each result in a new session ID being created for each of these links. I cannot easily change the pages extension for compatability reasons (although this does fix the problem).
How can I prompt ASP.Net to create a session for my page? I've tried adding an explicit handler mapping to ensure that the page is being handled by the ASP.Net modules, however this has no impact - while debugging I can see that a SessionIDManager instance is being created for this page (implying that the ASP.Net is already handling this page via the integrated pipeline regardless of my handler mapping), however ASP.Net is still not creating a session for this page.
I am using IIS 7, however this also needs to work on IIS 6 (with expicit handler mappings) and IIS 8.
I have discovered from experimentation and decompiling the ASP.Net source that the SessionStateModule decides whether or not to create a session for a request based on whether or not the IHttpHandler for the request implements IRequiresSessionState or IReadOnlySessionState. If neither of those are true then its all down to whether or not someone has used SetSessionStateBehavior to set the session state behaviour to either Required or ReadOnly.
By default the static file handler does not do any of these and so the session is not created for static files - to ensure that the session state behaviour is set for the static files that require session state I simply set the session state behaviour during the BeginRequest event for the application
// In Global.asax.cs
void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Default);
}
I'm sure there are better ways, but this worked for me.

Have Page Method Unhandled Exceptions Behave as Other ASP.Net Unhandled Exceptions

I have a webform that has a single page method. My other web apps log unhandled exceptions to the system event log on the web server. Since the other developers that I work with expect to see entries for app errors in the event log, I would like this app to do the same.
However, I have the app send error emails when an exception is caught from calling code inside the page method. It is not writing to the event log when this occurs. Note: the page method re-throws the exception after calling my email notification method.
From what I've read so far it seems that ASP.Net logs errors to the event log by default. I imagine that the same is not true for Page Methods/WebMethods because they basically throw the exception to the client code calling it.
Is there a trivial way to have that exception bubble up properly so that it writes to the event log? No other apps write to the event log directly from what I've seen so I don't think the application could create a new source since our security people keep a lot of things locked down (with good intentions, yay security).
[WebMethod]
public static object MyPseudoWebMethod()
{
try
{
// My exception spawning unreliable code here
}
catch(Exception ex)
{
// Cleanup ...
this.SendErrorNotification(ex);
throw; // <-- This doesn't bubble up but I'd love for it to!
}
}
Hmm interesting problem. You are right in that WebMethod exceptions do NOT follow normal exception flow.
The Application_Error event is not fired if your web method throws an
exception. The reason for this is that the HTTP handler for XML Web
services consumes any exception that occurs while an XML Web service
is executing and turns it into a SOAP fault prior to the
Application_Error event is called.
(from here)
The above page suggests using a SOAP extension to catch that exception before its swallowed, but here's how I'd do it if you don't want to do that:
1) Make a new 'error recieving' ASPX page that you will build that will take whatever params you want to record in your error log. So for example, have this page take in a POST named "ExceptionDetails" or whatever else you wish to capture. This page will NOT be viewed directly in a browser, so it doesnt need any ASPX controls or anything, but using a MasterPage on it won't hurt anything.
2) In the code behind of this new page, grab whatever POSTS you are sending in and new-up an Exception with whatever details you need. Immediate throw this exception. Doing this means that this exception will follow whatever flow other unhandled exceptions follow in the app (logging, emailing, etc).
3) On the page that calls the WebMethod JS, Wrap the calls to the WebMethod in a try-catch
4) In the catch block, print out whatever message you want in the browser, and initiate a new AJAX post to that new error receiving ASPX page, passing along whatever POST stuff you made that page look for.
The new AJAX call will NOT change ANYTHING in the user's perception by default. The AJAX call fires off a new request to that page, and the ASPX page itself is actually totally unaware that its AJAX and not a normal browser request. Any cookies/session/authentication data that's currently set are available to the AJAXed page as well, if you are recording a user's ID or anything. If you look at the returned response from a tool like Firebug, you will see that its actually the YellowScreenOfDeath's HTML (unless you have a custom 500 page, in which case its that HTML that comes back).
This is simply how the legacy ASMX web services work.
The only workaround is to stop using them (which you should do anyway, unless you're stuck with .NET 2.0). WCF doesn't have this problem.

ASP .NET page request size

I have a ASP .Net web-application running on IIS 7/Win2K8 Standard.
What I would like help with :
As a user makes a request on the client side (browser), I want to examine the request object on the server as soon as it arrives (for logging purposes).
In particular I want to log, at the very least :
Time of the request.
IP address of the request.
Size in bytes of the request.
Similarly I want to log the size of the response being rendered for the same request.
The request may be an entire page request or a partial update (updatepanel) request.
Much obliged for any help provided.
Thanks in anticipation.
Handle the Application_BeginRequest event in your Global.asax file. Something like this:
public void Application_BeginRequest(Object sender, EventArgs e) {
yourLogger.Log(String.Format("Request Time: {0}, Request IP: {1}, Request Size: {2}",
Date.Now.Time,
Request.UserHostAddress,
Request.TotalBytes)
}
Well beside the answer from #just.another.programmer, I would like to recommend another approach
If you want to be completely sure that each request and response will actually be logged even if an unhandled exception ocurrs, then you should consider using the: LogRequest event or the PostLogRequest
From MSDN
Occurs just before ASP.NET performs any logging for the current request.
The LogRequest event is raised even if an error occurs. You can provide an event handler for the LogRequest event to provide custom logging for the request.
In order to use this event, your application must be running in Integrated mode in IIS 7.0 and with the .NET Framework 3.0 or later.
These events occur at the end of the Request life-cycle:
References:
Application Life-cycle in IIS 5 and 6
Application Life-cycle in IIS 7

Resources