One of my applications relies on a singleton pattern to create a single instance of a server which processes requests from many different ASP.NET handlers. It is created using pretty much standard Singleton code:
Recently, this server needed to be made aware of whether requests were coming into it via SSL or standard HTTP. The solution that was checked in (and which worked and passes local tests) is to create a new property of Context called IsSecureConnection, and to allow this to be passed into its public constructor like this:
This is pretty much classic Dependency Injection and most of the time is what I would consider the right approach to the problem. However, in this case it fails to take into account how this object is used, because of the fact that it’s using a Singleton. And in this case nothing ever calls this constructor anyway – the actual solution that was applied instead only made use of the IsSecureConnection property (which was read/write).
The client code in the ASP.NET handlers that invokes the Context class looks like this (before adding any support for SSL):
After adding support for SSL, this code became:
Looks fine, right? Run it through some tests – things behave as expected. SSL requests get routed to the Context object with IsSecureConnection = true. Regular HTTP requests get routed with IsSecureConnection = false. Stick a fork in it and ship it…
Global State and Static / Singleton Objects
The problem of course is that many different ASP.NET handlers are talking to the same exact instance of Context. In this particular application, this is taking place tens of times each second. So if you imagine that 49 requests come in during a given second over HTTP and 1 comes in over HTTPS, it’s quite likely that while the Context has had its IsSecureConnection property set to one value, it’s in the middle of processing another request.
The other issue here is that whether or not a given request is using SSL is a lower level concern to the Context object which is in essence a request manager. The only call that the handlers ever send to Context is something like this: