Commit fb777528 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent d6b96212
......@@ -44,16 +44,74 @@ namespace PcapDotNet.Packets.Test
#endregion
[TestMethod]
public void RandomHttpTest()
public void HttpParsingTest()
{
TestHttpRequest("", null, null, null);
TestHttpRequest(" ", null, null, null);
TestHttpRequest("GET", "GET", null, null);
TestHttpRequest("GET /url", "GET", "/url", null);
TestHttpRequest("GET /url HTTP/1.0", "GET", "/url", HttpVersion.Version10);
TestHttpRequest("GET /url HTTP/1.1", "GET", "/url", HttpVersion.Version11);
TestHttpRequest("GET /url HTTP/1.1", "GET", "/url", null);
TestHttpRequest("GET HTTP/1.1", "GET", "", HttpVersion.Version11);
TestHttpResponse("HTTP/", null, null, null);
TestHttpResponse("HTTP/1", null, null, null);
TestHttpResponse("HTTP/1.", null, null, null);
TestHttpResponse("HTTP/1.0", HttpVersion.Version10, null, null);
TestHttpResponse("HTTP/1.0 ", HttpVersion.Version10, null, null);
TestHttpResponse("HTTP/1.0 A", HttpVersion.Version10, null, null);
TestHttpResponse("HTTP/1.0 200", HttpVersion.Version10, 200, null);
TestHttpResponse("HTTP/1.0 200 ", HttpVersion.Version10, 200, "");
TestHttpResponse("HTTP/1.0 200 OK", HttpVersion.Version10, 200, "OK");
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, "OK");
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, null, null);
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, " OK");
}
private static void TestHttpRequest(string httpString, string expectedMethod, string expectedUri, HttpVersion expectedVersion)
{
Packet packet = BuildPacket(httpString);
// HTTP
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
Assert.IsTrue(http.IsRequest, "IsRequest " + httpString);
Assert.IsFalse(http.IsResponse, "IsResponse " + httpString);
Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString);
HttpRequestDatagram request = (HttpRequestDatagram)http;
Assert.AreEqual(expectedMethod, request.Method, "Method " + httpString);
Assert.AreEqual(expectedUri, request.Uri, "Uri " + httpString);
// HttpHeader header = http.Header;
// Assert.IsNotNull(header);
}
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion, uint? expectedStatusCode, string expectedReasonPhrase)
{
Packet packet = BuildPacket(httpString);
// HTTP
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
Assert.IsFalse(http.IsRequest, "IsRequest " + httpString);
Assert.IsTrue(http.IsResponse, "IsResponse " + httpString);
Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString);
HttpResponseDatagram response = (HttpResponseDatagram)http;
Assert.AreEqual(expectedStatusCode, response.StatusCode, "StatusCode " + httpString);
Assert.AreEqual(expectedReasonPhrase == null ? null : new Datagram(Encoding.ASCII.GetBytes(expectedReasonPhrase)), response.ReasonPhrase, "ReasonPhrase " + httpString);
}
private static Packet BuildPacket(string httpString)
{
MacAddress ethernetSource = new MacAddress("00:01:02:03:04:05");
MacAddress ethernetDestination = new MacAddress("A0:A1:A2:A3:A4:A5");
EthernetLayer ethernetLayer = new EthernetLayer
{
Source = ethernetSource,
Destination = ethernetDestination
};
{
Source = ethernetSource,
Destination = ethernetDestination
};
Random random = new Random();
......@@ -61,36 +119,30 @@ namespace PcapDotNet.Packets.Test
ipV4Layer.HeaderChecksum = null;
TcpLayer tcpLayer = random.NextTcpLayer();
for (int i = 0; i != 1000; ++i)
PayloadLayer payloadLayer = new PayloadLayer
{
PayloadLayer payloadLayer = new PayloadLayer
{
Data = new Datagram(Encoding.ASCII.GetBytes("GET /url HTTP/1.1"))
};
Packet packet = new PacketBuilder(ethernetLayer, ipV4Layer, tcpLayer, payloadLayer).Build(DateTime.Now);
Assert.IsTrue(packet.IsValid);
// Ethernet
ethernetLayer.EtherType = EthernetType.IpV4;
Assert.AreEqual(ethernetLayer, packet.Ethernet.ExtractLayer(), "Ethernet Layer");
// IpV4
ipV4Layer.Protocol = IpV4Protocol.Tcp;
ipV4Layer.HeaderChecksum = ((IpV4Layer)packet.Ethernet.IpV4.ExtractLayer()).HeaderChecksum;
Assert.AreEqual(ipV4Layer, packet.Ethernet.IpV4.ExtractLayer(), "IP Layer");
ipV4Layer.HeaderChecksum = null;
// TCP
tcpLayer.Checksum = packet.Ethernet.IpV4.Tcp.Checksum;
Assert.AreEqual(tcpLayer, packet.Ethernet.IpV4.Tcp.ExtractLayer(), "TCP Layer");
// HTTP
HttpDatagram http = packet.Ethernet.IpV4.Tcp.Http;
HttpHeader header = http.Header;
Assert.IsNotNull(header);
}
Data = new Datagram(Encoding.ASCII.GetBytes(httpString))
};
Packet packet = new PacketBuilder(ethernetLayer, ipV4Layer, tcpLayer, payloadLayer).Build(DateTime.Now);
Assert.IsTrue(packet.IsValid);
// Ethernet
ethernetLayer.EtherType = EthernetType.IpV4;
Assert.AreEqual(ethernetLayer, packet.Ethernet.ExtractLayer(), "Ethernet Layer");
// IpV4
ipV4Layer.Protocol = IpV4Protocol.Tcp;
ipV4Layer.HeaderChecksum = ((IpV4Layer)packet.Ethernet.IpV4.ExtractLayer()).HeaderChecksum;
Assert.AreEqual(ipV4Layer, packet.Ethernet.IpV4.ExtractLayer(), "IP Layer");
ipV4Layer.HeaderChecksum = null;
// TCP
tcpLayer.Checksum = packet.Ethernet.IpV4.Tcp.Checksum;
Assert.AreEqual(tcpLayer, packet.Ethernet.IpV4.Tcp.ExtractLayer(), "TCP Layer");
return packet;
}
}
}
\ No newline at end of file
......@@ -148,6 +148,11 @@ namespace PcapDotNet.Packets
return Length.GetHashCode() ^ this.BytesSequenceGetHashCode();
}
public override string ToString()
{
return _buffer.Range(StartOffset, Length).BytesSequenceToHexadecimalString();
}
internal void Write(byte[] buffer, int offset)
{
_buffer.BlockCopy(StartOffset, buffer, offset, Length);
......
......@@ -200,22 +200,24 @@ namespace PcapDotNet.Packets.Http
public abstract bool IsRequest { get; }
public bool IsResponse { get { return !IsRequest; } }
public HttpHeader Header
public HttpVersion Version
{
get
{
Parse();
return _header;
}
}
private void Parse()
{
HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
ParseFirstLine(parser);
_header = new HttpHeader(ParseHeader(parser));
ParseFirstLine();
return _version;
}
}
// public HttpHeader Header
// {
// get
// {
// Parse();
// return _header;
// }
// }
internal static HttpDatagram CreateDatagram(byte[] buffer, int offset, int length)
{
if (length >= _httpSlash.Length && buffer.SequenceEqual(offset, _httpSlash, 0, _httpSlash.Length))
......@@ -228,22 +230,50 @@ namespace PcapDotNet.Packets.Http
{
}
internal abstract void ParseFirstLine(HttpParser parser);
private IEnumerable<KeyValuePair<string, IEnumerable<byte>>> ParseHeader(HttpParser parser)
internal void ParseFirstLine()
{
while (parser.Success)
{
string fieldName;
IEnumerable<byte> fieldValue;
parser.Token(out fieldName).Colon().FieldValue(out fieldValue);
if (parser.Success)
yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
}
if (_isParsedFirstLine)
return;
ParseSpecificFirstLine(out _version, out _headerOffset);
_isParsedFirstLine = true;
}
internal abstract void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset);
// private void ParseHeader()
// {
// ParseFirstLine();
// if (_headerStart == null)
// return;
//
// int headerStartValue = _headerStart.Value;
// HttpParser parser = new HttpParser(Buffer, StartOffset + headerStartValue, Length - headerStartValue);
// if (_isParsed)
// return;
// HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
// HttpVersion version;
// ParseFirstLine(parser, out _version);
// _header = new HttpHeader(ParseHeader(parser));
// }
// private IEnumerable<KeyValuePair<string, IEnumerable<byte>>> ParseHeader(HttpParser parser)
// {
// while (parser.Success)
// {
// string fieldName;
// IEnumerable<byte> fieldValue;
// parser.Token(out fieldName).Colon().FieldValue(out fieldValue);
// if (parser.Success)
// yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
// }
// }
private static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/");
private bool _isParsedFirstLine;
private int? _headerOffset;
private HttpVersion _version;
private HttpHeader _header;
}
......
......@@ -18,6 +18,11 @@ namespace PcapDotNet.Packets.Http
public bool Success { get; private set; }
public int Offset
{
get { return _offset; }
}
public HttpParser Token(out string token)
{
if (!Success)
......@@ -156,10 +161,6 @@ namespace PcapDotNet.Packets.Http
get { return _buffer.Range(_offset, _totalLength - _offset); }
}
private readonly byte[] _buffer;
private int _offset;
private readonly int _totalLength;
public HttpParser RequestUri(out string uri)
{
if (!Success)
......@@ -177,10 +178,10 @@ namespace PcapDotNet.Packets.Http
public HttpParser Version(out HttpVersion version)
{
uint major;
uint minor;
uint? major;
uint? minor;
Bytes(_httpSlash).DecimalNumber(out major).Dot().DecimalNumber(out minor);
version = new HttpVersion(major, minor);
version = major != null && minor != null ? new HttpVersion(major.Value, minor.Value) : null;
return this;
}
......@@ -189,18 +190,21 @@ namespace PcapDotNet.Packets.Http
return Bytes(AsciiBytes.CarriageReturn, AsciiBytes.LineFeed);
}
public HttpParser DecimalNumber(int numDigits, out uint number)
public HttpParser DecimalNumber(int numDigits, out uint? number)
{
if (numDigits > 8 || numDigits < 0)
throw new ArgumentOutOfRangeException("numDigits", numDigits, "Only between 0 and 8 digits are supported");
if (!Success)
{
number = 0;
number = null;
return this;
}
var digits = Range.Take(numDigits).TakeWhile(value => value.IsDigit());
if (digits.Count() != numDigits)
{
number = 0;
number = null;
return Fail();
}
number = digits.Select(value => (uint)(value - AsciiBytes.Zero)).Aggregate<uint, uint>(0, (accumulated, value) => 10 * accumulated + value);
......@@ -208,28 +212,35 @@ namespace PcapDotNet.Packets.Http
return this;
}
public HttpParser DecimalNumber(out uint number)
public HttpParser DecimalNumber(out uint? number)
{
if (!Success)
{
number = 0;
number = null;
return this;
}
var digits = Range.TakeWhile(value => value.IsDigit());
if (!digits.Any())
{
number = 0;
number = null;
return Fail();
}
int numDigits = digits.Count();
number = digits.Select(value => (uint)(value - AsciiBytes.Zero)).Aggregate<uint, uint>(0, (accumulated, value) => 10 * accumulated + value);
int numInsignificantDigits = digits.TakeWhile(value => value == AsciiBytes.Zero).Count();
uint numberValue = digits.Select(value => (uint)(value - AsciiBytes.Zero)).Aggregate<uint, uint>(0, (accumulated, value) => 10 * accumulated + value);
if (numDigits - numInsignificantDigits >= 8)
{
number = null;
return Fail();
}
number = numberValue;
_offset += numDigits;
return this;
}
public HttpParser ReasonPhrase(out IEnumerable<byte> reasonPhrase)
public HttpParser ReasonPhrase(out Datagram reasonPhrase)
{
if (!Success)
{
......@@ -237,23 +248,16 @@ namespace PcapDotNet.Packets.Http
return this;
}
reasonPhrase = Range.TakeWhile(value => !value.IsControl() || value == AsciiBytes.HorizontalTab);
_offset += reasonPhrase.Count();
int reasonPhraseLength = Range.Count(value => !value.IsControl() || value == AsciiBytes.HorizontalTab);
reasonPhrase = new Datagram(_buffer, _offset, reasonPhraseLength);
_offset += reasonPhraseLength;
return this;
}
private static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/");
}
public class HttpVersion
{
public HttpVersion(uint major, uint minor)
{
Major = major;
Minor = minor;
}
public uint Major { get; private set; }
public uint Minor { get; private set; }
private readonly byte[] _buffer;
private int _offset;
private readonly int _totalLength;
}
}
\ No newline at end of file
......@@ -14,12 +14,32 @@ namespace PcapDotNet.Packets.Http
get { return true; }
}
internal override void ParseFirstLine(HttpParser parser)
public string Method
{
string method;
string uri;
HttpVersion version;
parser.Token(out method).Space().RequestUri(out uri).Space().Version(out version).CarraigeReturnLineFeed();
get
{
ParseFirstLine();
return _method;
}
}
public string Uri
{
get
{
ParseFirstLine();
return _uri;
}
}
internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset)
{
HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
parser.Token(out _method).Space().RequestUri(out _uri).Space().Version(out version).CarraigeReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
}
private string _uri;
private string _method;
}
}
\ No newline at end of file
......@@ -15,12 +15,32 @@ namespace PcapDotNet.Packets.Http
get { return false; }
}
internal override void ParseFirstLine(HttpParser parser)
public uint? StatusCode
{
uint statusCode;
HttpVersion version;
IEnumerable<byte> reasonPhrase;
parser.Version(out version).Space().DecimalNumber(3, out statusCode).Space().ReasonPhrase(out reasonPhrase).CarraigeReturnLineFeed();
get
{
ParseFirstLine();
return _statusCode;
}
}
public Datagram ReasonPhrase
{
get
{
ParseFirstLine();
return _reasonPhrase;
}
}
internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset)
{
HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
parser.Version(out version).Space().DecimalNumber(3, out _statusCode).Space().ReasonPhrase(out _reasonPhrase).CarraigeReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
}
private uint? _statusCode;
private Datagram _reasonPhrase;
}
}
\ No newline at end of file
using System;
namespace PcapDotNet.Packets.Http
{
public class HttpVersion : IEquatable<HttpVersion>
{
public HttpVersion(uint major, uint minor)
{
Major = major;
Minor = minor;
}
public static HttpVersion Version10 { get { return _version10; } }
public static HttpVersion Version11 { get { return _version11; } }
public uint Major { get; private set; }
public uint Minor { get; private set; }
public override string ToString()
{
return string.Format("HTTP/{0}.{1}", Major, Minor);
}
public bool Equals(HttpVersion other)
{
return other != null &&
Major == other.Major &&
Minor == other.Minor;
}
public override bool Equals(object obj)
{
return Equals(obj as HttpVersion);
}
private static readonly HttpVersion _version10 = new HttpVersion(1,0);
private static readonly HttpVersion _version11 = new HttpVersion(1,1);
}
}
\ No newline at end of file
......@@ -114,6 +114,7 @@
<Compile Include="Http\HttpParser.cs" />
<Compile Include="Http\HttpRequestDatagram.cs" />
<Compile Include="Http\HttpResponseDatagram.cs" />
<Compile Include="Http\HttpVersion.cs" />
<Compile Include="Icmp\IcmpAddressMaskReplyDatagram.cs" />
<Compile Include="Icmp\IcmpAddressMaskReplyLayer.cs" />
<Compile Include="Icmp\IcmpAddressMaskRequestLayer.cs" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment