2008-05-15

Filtering a network stream using a wrapper

So not that long ago, someone posted a question asking how to deal with a certain situation. The situation is such that there is a network file stream coming from somewhere, that has certain data you want to keep, and certain data you don't want to keep. Control blocks, extra header information, weirdo protocol, too much data coming back form an API, etc..

My suggestion was to create a simple container object (aka wrapper) to the existing network stream, that operates the same as the network stream, but does the necessary filtering.

Here's an example of how you'd use it, and and example base class implementation of for the filters follows it. In the actual problem case example, he was dealing with a NetworkStream that contained Xml data in irregular chunks, with control blocks as fixed headers. Each header indicates how much XmlData follows. The filter will remove the headers as needed, presenting a simple stream of Xml data to the XmlReader to parse.

I've left out the concrete implementation that actually parses the stream, and here you just have the FilteredNetworkStream base class and an idea of how to use it once you implement it. All that's left for the implementer is to override the abstract method FilterBeforeRead, which contains the customized filtering logic for the particular situation.



using (NetworkStream inputStream = GetNetworkStreamFromSomewhere())
using (StreamWriter outputStream = new StreamWriter(@"C:\Path\To\File.xml", false))
{

XmlReader reader = XmlReader.Create(new FilteredNetworkStream(inputStream));
while (reader.Read())
{

// method returns empty string if current data is discardable
string outputData = GetDesiredDataFromReader(reader);

if (!string.IsNullOrEmpty(outputData))
{

// save desired data to local file
outputStream.Write(outputData);
}
}
}


Here's the base class:


public abstract class FilteredNetworkStream : Stream
{
public FilteredNetworkStream(NetworkStream baseStream)
{
_baseStream = baseStream;
}

protected NetworkStream _baseStream;
public abstract void FilterBeforeRead();

#region Stream Implementation

public override bool CanRead
{
get { return _baseStream.CanRead; }
}

public override bool CanSeek
{
get { return _baseStream.CanSeek; }
}

public override bool CanWrite
{
get { return _baseStream.CanWrite; }
}

public override void Flush()
{
_baseStream.Flush();
}

public override long Length
{
get { return _baseStream.Length; }
}

public override long Position
{
get
{
return _baseStream.Position;
}
set
{
_baseStream.Position = value;
}
}

public override int Read(byte[] buffer, int offset, int count)
{
this.FilterBeforeRead();
return _baseStream.Read(buffer, offset, count);
}

public override long Seek(long offset, SeekOrigin origin)
{
return _baseStream.Seek(offset, origin);
}

public override void SetLength(long value)
{
_baseStream.SetLength(value);
}

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

#endregion
}

No comments: