using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.IO; namespace Clarius { /// /// A simple that buffers the response in-memory via the /// stream property and then appends /// Google Analytics (https://www.google.com/analytics/) script /// before the page body closing tag. /// /// The app settings key ( /// must be present in the web.config file in order for the module to kick-in. If it's not, /// the filter will not be added. /// /// /// /// <configuration> /// <appSettings> /// <add key="GoogleAnalyticsAccount" value="[Google Analytics Account #"/> /// </appSettings> /// <httpModules> /// <add name="GoogleAnalytics" type="Clarius.GoogleAnalyticsModule"/> /// </httpModules> /// </configuration> /// /// public class GoogleAnalyticsModule : IHttpModule { public const string AppSettingsKey = "GoogleAnalyticsAccount"; const string GoogleScript = @" "; HttpApplication application; string accountNumber; public void Dispose() { context.BeginRequest -= new EventHandler(OnBeginRequest); } public void Init(HttpApplication context) { accountNumber = ConfigurationManager.AppSettings[AppSettingsKey]; if (!String.IsNullOrEmpty(accountNumber)) { context.BeginRequest += new EventHandler(OnBeginRequest); application = context; } } void OnBeginRequest(object sender, EventArgs e) { application.Response.Filter = new AnalyticsStream(application.Response.Filter, accountNumber); } class AnalyticsStream : Stream { Stream innerStream; string accountNumber; MemoryStream memory = new MemoryStream(); public AnalyticsStream(Stream innerStream, string accountNumber) { this.innerStream = innerStream; this.accountNumber = accountNumber; } public override void Close() { memory.Position = 0; using (StreamWriter writer = new StreamWriter(innerStream)) { using (StreamReader reader = new StreamReader(memory)) { while (!reader.EndOfStream) { // Find if (MatchesOrWrite(reader, writer, '<', null) && MatchesOrWrite(reader, writer, '/', "<") && MatchesOrWrite(reader, writer, 'b', "', ""; writer.Write(script); while (!reader.EndOfStream) writer.Write((char)reader.Read()); } } } } base.Close(); } private bool MatchesOrWrite(StreamReader reader, StreamWriter writer, char target, string buffered) { if (!reader.EndOfStream) { char current = (char)reader.Read(); if (current == target) { return true; } else { writer.Write(buffered); writer.Write(current); } } else { writer.Write(buffered); } return false; } public override bool CanRead { get { return memory.CanRead; } } public override bool CanSeek { get { return memory.CanSeek; } } public override bool CanWrite { get { return memory.CanWrite; } } public override void Flush() { memory.Flush(); } public override long Length { get { return memory.Length; } } public override long Position { get { return memory.Position; } set { memory.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { return memory.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); } public override void SetLength(long value) { memory.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { memory.Write(buffer, offset, count); } } } }