using System.IO; using System.Text; using System.Text.RegularExpressions; namespace ChadSoft.Web.ResponseFilters { /// /// Base filter class that takes care of the dirty work of reading in /// the response data, calling the abstract ProcessResponseText() method, /// then streaming the processed text back into the response. /// public abstract class TextProcessingResponseFilter : Stream { private static readonly Regex ClosingHtmlTag = new Regex("", RegexOptions.IgnoreCase); private readonly Stream _stream; private readonly StringBuilder _responseBody; #region Properites public override bool CanRead { get { return true; } } public override bool CanSeek { get { return true; } } public override bool CanWrite { get { return true; } } public override void Flush() { _stream.Flush(); } public override long Length { get { return 0; } } public override long Position { get; set; } #endregion protected TextProcessingResponseFilter(Stream stream) { _stream = stream; _responseBody = new StringBuilder(); } protected abstract string ProcessResponseText(string responseText); #region Methods public override int Read(byte[] buffer, int offset, int count) { return _stream.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return _stream.Seek(offset, origin); } public override void SetLength(long value) { _stream.SetLength(value); } public override void Close() { _stream.Close(); } public override void Write(byte[] buffer, int offset, int count) { var bufferContent = Encoding.UTF8.GetString(buffer, offset, count); _responseBody.Append(bufferContent); // If we're not at the end of the page, just keep collecting the response data. // NOTE: We're checking the buffer content here, not the full request. // This is to avoid the _responseBody.ToString() with every chunk. // I suppose it is possible for the to be split between two chunks... if (!ClosingHtmlTag.IsMatch(bufferContent)) return; var responseText = ProcessResponseText(_responseBody.ToString()); var responseBytes = Encoding.UTF8.GetBytes(responseText); _stream.Write(responseBytes, 0, responseBytes.Length); } #endregion } }