2

I am new to the IIS module and C# area.

I need to modify the content of static HTML files in a specific directory of a website before the website sends the page content to client. The modification includes adding the banner, footer, etc.

Based on my research, I should be able to achieve my goal through IIS Module (correct?). Here is my code:

namespace MyProject
{
    public class MyModule : IHttpModule
    {
        #region IHttpModule Members

        public void Dispose()
        {

        }

        public void Init(HttpApplication context)
        {
            context.PreSendRequestContent +=
                new EventHandler(onPreSendRequestContent);
        }

        #endregion

        public void onPreSendRequestContent(Object source, EventArgs e)
        {
            HttpApplication app = (HttpApplication)source;
            HttpRequest request = app.Context.Request;
            HttpResponse response = app.Context.Response;

            if (request.Path.Contains("my_specific_directory"))
            {
                //add banner and footer to the page content
                //which class, methods, or properties to use?
            }
        }
    }
}

I am not sure whether PreSendRequestContent is the right event to start modifying page content. Could someone point me to the right way to get page content retrieved by IIS?

Thanks and regards.

curious1
  • 14,155
  • 37
  • 130
  • 231

1 Answers1

5

I think it's better to use the MVC Framework because It's easy to maintain and you can do anything. But here's the process if you still want to modify static HTMLs by a IIS HTTP Module. Hope this helps.

At the first, Add a handler & a build provider to process static HTML files by IIS.

Web.config:

<system.webServer>
    <handlers>
        <add name="html" verb="GET,HEAD,POST,DEBUG" path="*.html" type="System.Web.UI.PageHandlerFactory" />
    </handlers>
</system.webServer>

<system.web>
    <compilation>
        <buildProviders>
            <add extension=".html" type="System.Web.Compilation.PageBuildProvider" />
        </buildProviders>
    </compilation>
</system.web>

Next, Write a HTTP Module & a filter to modify your HTML contents.

HTTP Module:

class ModifyContentModule : IHttpModule
{
    public void Dispose() { }

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (o, e) =>
        {
            context.Response.Filter = new ModifyContentStream(context.Response.Filter);
        };
    }
}

Filter:

public class ModifyContentStream : Stream
{
    private Stream _base;
    private MemoryStream _memoryBase = new MemoryStream();

    public ModifyContentStream(Stream stream)
    {
        _base = stream;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _memoryBase.Write(buffer, offset, count);
    }

    public override void Flush()
    {
        // Get static HTML code
        string html = Encoding.UTF8.GetString(_memoryBase.GetBuffer());

        // Modify your HTML
        // Sample: Replace absolute links to relative
        Regex regex = new Regex("(href=\")http:\\/\\/www\\.example\\.com(\\/[^\"']+\\.[^\"']+\")");
        Match match = regex.Match(html);
        while (match.Success)
        {
            string oldValue = match.Value;
            string newValue = match.Groups[1].Value + match.Groups[2].Value;
            html = html.Replace(oldValue, newValue);

            match = match.NextMatch();
        }

        // Flush modified HTML
        byte[] buffer = Encoding.UTF8.GetBytes(html);
        _base.Write(buffer, 0, buffer.Length);
        _base.Flush();
    }

    #region Rest of the overrides
}

}

Finally, Add the HTTP Module to your Web.config

Web.config

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <remove name="ModifyContent" />
        <add name="ModifyContent" type="ModifyContentModule.ModifyContentModule, ModifyContentModule" />
    </modules>
    <handlers>
        <add name="html" verb="GET,HEAD,POST,DEBUG" path="*.html" type="System.Web.UI.PageHandlerFactory" />
    </handlers>
</system.webServer>
Tsukuru
  • 104
  • 1
  • 4
  • Thanks for your chiming in! – curious1 Jun 29 '16 at 12:57
  • 1
    What about the other overrides in `ModifyContentStream`? Do we just leave them as `throw new NotImplementedException();`? – Blaise Aug 03 '16 at 16:19
  • How do you build and deploy the HTTP module if your site is not an MVC website? – GGirard Dec 20 '17 at 14:36
  • 1
    @GGirard, you could just have this module as a standalone class library that you build once into a DLL, then as long as you're hosting on IIS and the feature is enabled you just need to have the DLL in a bin folder and a web.config with the config above in it. – benmccallum Jan 27 '18 at 04:59
  • @benmccallum Does that mean ISS load any DLLs it founds in the bin folder by default, regardless it is a dependency of the currently running app? If yes, looks like a big security hole to me as it means an attacker just has to upload a DLL to get it run by the host! – GGirard Jan 29 '18 at 17:43
  • @GGirard, hmm, good q. In my experience yes but I could be wrong. I remember once I had a DLL with a self-registering module (a Glimpse plugin) and it had me messed up for ages wondering why this thing was running when I thought I'd removed it. Certainly write access to the bin should be heavily restricted to say an admin Windows user and def not your w3wp process! https://haacked.com/archive/2005/10/31/writing-to-the-aspnet-bin-directory.aspx/ – benmccallum Jan 29 '18 at 23:06
  • In my case ModifyContentStream.Write is called 3 times and the html page is written multiple times... Any ideas? – Rick Jan 16 '19 at 11:13