7

What I'm trying to do is create (or perhaps one already exists) a HTTPHandler that will filter the HTML generated ASP.NET to use the content delivery network (CDN). For example, I want to rewrite references such as this:

/Portals/_default/default.css

to

http://cdn.example.com/Portals/_default/default.css

I'm perfectly happy using RegEx to match the initial strings. Such a regex patterns might be:

href=['"](/Portals/.+\.css)

or

src=['"](/Portals/.+\.(css|gif|jpg|jpeg))

This is a dotnetnuke site and I don't really have control over all the HTML generated so that's why I want to do it with an HTTPHandler. That way the changes can be done post-page generation.

Keltex
  • 26,220
  • 11
  • 79
  • 111

1 Answers1

13

You could write a response filter which can be registered in a custom HTTP module and which will modify the generated HTML of all pages running the regex you showed.

For example:

public class CdnFilter : MemoryStream
{
    private readonly Stream _outputStream;
    public CdnFilter(Stream outputStream)
    {
        _outputStream = outputStream;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        var contentInBuffer = Encoding.UTF8.GetString(buffer);

        contentInBuffer = Regex.Replace(
            contentInBuffer, 
            @"href=(['""])(/Portals/.+\.css)",
            m => string.Format("href={0}http://cdn.example.com{1}", m.Groups[1].Value, m.Groups[2].Value)
        );

        contentInBuffer = Regex.Replace(
            contentInBuffer,
            @"src=(['""])(/Portals/.+\.(css|gif|jpg|jpeg))",
            m => string.Format("href={0}http://cdn.example.com{1}", m.Groups[1].Value, m.Groups[2].Value)
        );

        _outputStream.Write(Encoding.UTF8.GetBytes(contentInBuffer), offset, Encoding.UTF8.GetByteCount(contentInBuffer));
    }
}

and then write a module:

public class CdnModule : IHttpModule
{
    void IHttpModule.Dispose()
    {
    }

    void IHttpModule.Init(HttpApplication context)
    {
        context.ReleaseRequestState += new EventHandler(context_ReleaseRequestState);
    }

    void context_ReleaseRequestState(object sender, EventArgs e)
    {
        HttpContext.Current.Response.Filter = new CdnFilter(HttpContext.Current.Response.Filter);
    }
}

and register in web.config:

<httpModules>
  <add name="CdnModule" type="MyApp.CdnModule, MyApp"/>
</httpModules>
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • That's what I will have to do, but I'm hoping that there's something like that already out there. – Keltex May 19 '11 at 15:49
  • Worked! I had to play around with the regular expressions a bit (they matched too much), but the basic concept works perfectly! Thanks. I also defined the regex as static / compiled at the class level for better performance. – Keltex May 19 '11 at 17:54
  • 1
    @Keltex, perfect, I hope you are well aware about [parsing HTML with regex](http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454) but if it fits your needs you should be OK. Otherwise you might also consider using [Html Agility Pack](http://htmlagilitypack.codeplex.com/) which is a full blown parser. – Darin Dimitrov May 19 '11 at 17:56
  • For the purposes of this site then Regex solution looks like it works fine. Sure somebody could type in src="/portals/dummy.jpg" in the middle of the document, but I think that's unlikely enough that I'll take my chances. I would rather get the site a lot quicker and take some of the load off the server. Thanks again! – Keltex May 19 '11 at 18:00
  • wanted to ask one thing... from the performance perspective, will this approach affect the performance since we are altering the response content before sending to browser? I am also planning to make this change, wanted to confirm before i make the change. – nimi Dec 18 '13 at 05:25
  • @nimi, of course that it will affect the performance. You are making additional processing on every request. How bad it will hurt it will depend on the size of the pages. Your best bet is to perform extensive load tests and compare the results. – Darin Dimitrov Dec 18 '13 at 07:07
  • Thanks for the solution, I was looking to do this to help with resource caching issues after a new release - a version can be added to the end of all javascript and css without having to find all instances of the resources. – DanCaveman Oct 02 '15 at 18:46