Search code examples
c#iiswebsocketweb-configasp.net-4.5

Runtime access to web.config system.webServer/webSocket section


OK here it goes, posting my first ever SO question after hours of googling. Be gentle please :)

Question: Any ideas how to get past the error: "[System.Configuration.ConfigurationProperty]' is inaccessible due to its protection level", when attempting to modify web.config system.webServer/webSocket section in C#?

Background & what I've tried: I have an ASP.NET Web Application written in C# which utilizes Microsoft SignalR for server<->client function calls. SignalR uses Websocket protocol when possible and falls back to other transport methods when it's not available.

I have this entry in my web.config, within system.webServer section:

<webSocket enabled="true" pingInterval="00:00:10" />

That enables my application to use WebSocket so I need this line. Also, because my users are often operating in difficult network conditions, I've added a rather short pingInterval. This works great, no problem.

It's well known that Windows 2008 R2 / IIS 7.5 (and lower versions) do not support WebSocket. However sometimes I need to run my application on an older Windows Server version. In that case I need to manually remove the line shown above, to avoid nasty IIS errors about incorrect Web.config configuration. This also works just fine, but I don't like the extra work of having to remove this line depending on which server I'm running on.

So, I added code in my Global.asax to detect OS and IIS version, to know if WebSocket is supported or not. Next I want to dynamically add or remove that WebSocket configuration line on runtime (I'm ok with my appdomain rebooting upon change). I need to do this fully programmatically within my application, and by not needing to change anything on IIS or OS level.

I've looked at articles such as this one and this from MS and also many SO posts that come very close to my problem, like this here and this regarding the actual error I get. This is the closest I can get at the moment and I don't know how to get past the error:

System.Configuration.Configuration webConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/MICC");
//At this point I can see that webConfig.FilePath is pointing to correct Web.config file
System.Configuration.ConfigurationElement webSocketElement = webConfig.GetSection("system.webServer/webSocket");
if (webSocketElement == null)
{
  //Not sure how to initialize a new ConfigurationElement, if GetSection() returned null
}
else
{
  //Attempting to change values, but following two lines gives me:
  //[System.Configuration.ConfigurationProperty]' is inaccessible due to its protection level

  webSocketElement["enabled"] = true;
  webSocketElement["pingInterval"] = TimeSpan.Parse("00:00:99"); //Test value
}
webConfig.Save(); //Never getting this far...

I'm also very open to any other suggestions on how to work around this. Meanwhile I'll keep googling...

EDIT: Forgot to mention I'm on .NET 4.5


Solution

  • SOLVED! Well after a couple of more hours of trying I got it to work exactly as I needed. Here is the relevant piece of code, not perhaps the most beautiful code but it works:

    public static bool enableWebSocket()
    {
        System.Configuration.Configuration webConfig = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/my-apps-name-under-my-iis-site");
        System.Configuration.ConfigurationSection webServerSection = webConfig.GetSection("system.webServer");
    
        //This was the "magic" discovery. Just get the whole bunch as raw XML for manual editing:                
        XmlDocument webServerXml = new XmlDocument();
        webServerXml.LoadXml(webServerSection.SectionInformation.GetRawXml());
        //Check if the line is already there:
        XmlNodeList nodes = webServerXml.GetElementsByTagName("webSocket");
        if (nodes.Count > 0)
        {
          return false; //Already there, do nothing...
        }
        else //Node not yet found, so let's add it:
        {
            //Create a new XmlNode with the needed attributes:
            XmlNode webSocket = webServerXml.CreateNode(XmlNodeType.Element, "webSocket", null);
            XmlAttribute attr = webServerXml.CreateAttribute("enabled");
            attr.Value = "true";
            webSocket.Attributes.Append(attr);
            attr = webServerXml.CreateAttribute("pingInterval");
            attr.Value = "00:00:10";
            webSocket.Attributes.Append(attr);
    
            //Append original <system.webServer> section with the new XmlNode:
            webServerXml.DocumentElement.AppendChild(webSocket);
    
            //And finally store the modified <system.webServer> section in Web.config:    
            webServerSection.SectionInformation.SetRawXml(webServerXml.OuterXml);
            webConfig.Save();
            return true; //All done!
        }
    }