Skip to content

Instantly share code, notes, and snippets.

@thecodejunkie
Created March 7, 2011 08:06
Show Gist options
  • Select an option

  • Save thecodejunkie/858210 to your computer and use it in GitHub Desktop.

Select an option

Save thecodejunkie/858210 to your computer and use it in GitHub Desktop.
Read HTTP multipart/form-data formatted streams
public class Boundary
{
private readonly SubStream boundaryStream;
private const byte LF = (byte)'\n';
private const byte CR = (byte)'\r';
public Boundary(SubStream boundaryStream)
{
this.boundaryStream = boundaryStream;
this.ExtractHeaders();
}
public string ContentType { get; set; }
public string Filename { get; set; }
public string Name { get; set; }
public Stream Value { get; set; }
private void ExtractHeaders()
{
while(true)
{
var header =
this.ReadLineFromStream();
if (string.IsNullOrEmpty(header))
{
break;
}
if (header.StartsWith("Content-Disposition", StringComparison.CurrentCultureIgnoreCase))
{
this.Name = Regex.Match(header, @"name=""(?<name>[^\""]*)", RegexOptions.IgnoreCase).Groups["name"].Value;
this.Filename = Regex.Match(header, @"filename=""(?<filename>[^\""]*)", RegexOptions.IgnoreCase).Groups["filename"].Value;
}
if (header.StartsWith("Content-Type", StringComparison.InvariantCultureIgnoreCase))
{
this.ContentType = header.Split(new[] { ' ' }).Last().Trim();
}
}
this.boundaryStream.PositionStartAtCurrentLocation();
}
private string ReadLineFromStream()
{
var readBuffer = new StringBuilder();
while (true)
{
var byteReadFromStream = this.boundaryStream.ReadByte();
if (byteReadFromStream == -1)
return null;
if (byteReadFromStream.Equals(LF))
break;
readBuffer.Append((char)byteReadFromStream);
}
var lineReadFromStream =
readBuffer.ToString().Trim(new[] { (char)CR });
return lineReadFromStream;
}
}
public class Buffer
{
private readonly byte[] boundaryAsBytes;
private readonly byte[] buffer;
private int position;
public Buffer(byte[] boundaryAsBytes)
{
this.boundaryAsBytes = boundaryAsBytes;
this.buffer = new byte[this.boundaryAsBytes.Length];
}
public bool IsBoundary
{
get { return this.buffer.SequenceEqual(this.boundaryAsBytes); }
}
public bool IsFull
{
get { return this.position.Equals(this.buffer.Length); }
}
public int Length
{
get { return this.buffer.Length; }
}
public void Clear()
{
this.position = 0;
}
public void Insert(byte value)
{
this.buffer[this.position++] = value;
}
}
public class Multipart
{
private const byte LF = (byte)'\n';
private readonly byte[] boundaryAsBytes;
private Buffer readBuffer;
private readonly Stream requestStream;
public Multipart(Stream requestStream, string boundary)
{
this.requestStream = requestStream;
this.boundaryAsBytes = GetBoundaryAsBytes(boundary);
this.readBuffer = new Buffer(this.boundaryAsBytes);
}
public IEnumerable<Boundary> GetBoundaries()
{
return
from boundaryStream in this.GetBoundarySubStreams()
select new Boundary(boundaryStream);
}
private IEnumerable<SubStream> GetBoundarySubStreams()
{
var boundarySubStreams = new List<SubStream>();
var boundaryStart = this.GetNextBoundaryPosition();
while (boundaryStart > -1)
{
var boundaryEnd = this.GetNextBoundaryPosition();
boundarySubStreams.Add(new SubStream(
this.requestStream,
boundaryStart,
this.GetActualEndOfBoundary(boundaryEnd)));
boundaryStart = boundaryEnd;
}
return boundarySubStreams;
}
private long GetActualEndOfBoundary(long boundaryEnd)
{
if (this.CheckIfFoundEndOfStream())
{
return this.requestStream.Position;
}
return boundaryEnd - (this.readBuffer.Length + 2);
}
private bool CheckIfFoundEndOfStream()
{
return this.requestStream.Position.Equals(this.requestStream.Length);
}
private static byte[] GetBoundaryAsBytes(string boundary)
{
var boundaryBuilder = new StringBuilder();
boundaryBuilder.Append("--");
boundaryBuilder.Append(boundary);
boundaryBuilder.Append('\r');
boundaryBuilder.Append('\n');
var bytes =
Encoding.ASCII.GetBytes(boundaryBuilder.ToString());
return bytes;
}
private long GetNextBoundaryPosition()
{
this.readBuffer.Clear();
while(true)
{
var byteReadFromStream = this.requestStream.ReadByte();
if (byteReadFromStream == -1)
{
return -1;
}
this.readBuffer.Insert((byte)byteReadFromStream);
if (this.readBuffer.IsFull && this.readBuffer.IsBoundary)
{
return this.requestStream.Position;
}
if (byteReadFromStream.Equals(LF) || this.readBuffer.IsFull)
{
this.readBuffer.Clear();
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment