Commit b455dfb5 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent 0f5729d8
...@@ -48,6 +48,8 @@ namespace PcapDotNet.Packets.Test ...@@ -48,6 +48,8 @@ namespace PcapDotNet.Packets.Test
[TestMethod] [TestMethod]
public void HttpParsingTest() public void HttpParsingTest()
{ {
// Console.WriteLine(Encoding.GetEncodings().Where(e => e.Name == "iso-8859-1").Select(e => e.CodePage).SequenceToString(", "));
TestHttpRequest(""); TestHttpRequest("");
TestHttpRequest(" "); TestHttpRequest(" ");
TestHttpRequest("GET", "GET"); TestHttpRequest("GET", "GET");
...@@ -129,6 +131,12 @@ namespace PcapDotNet.Packets.Test ...@@ -129,6 +131,12 @@ namespace PcapDotNet.Packets.Test
HttpVersion.Version11, 200, "OK", HttpVersion.Version11, 200, "OK",
new HttpHeader( new HttpHeader(
new HttpField("Cache-Control", "no-cache"))); new HttpField("Cache-Control", "no-cache")));
TestHttpResponse("HTTP/1.1 200 OK\r\n" +
"Transfer-Encoding: chunked,a, b , c\r\n\t,d , e;f=g;h=\"ijk lmn\"\r\n",
HttpVersion.Version11, 200, "OK",
new HttpHeader(
new HttpTransferEncodingField("chunked", "a", "b", "c", "d", "e;f=g;h=\"ijk lmn\"")));
} }
private static void TestHttpRequest(string httpString, string expectedMethod = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null) private static void TestHttpRequest(string httpString, string expectedMethod = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null)
...@@ -145,9 +153,6 @@ namespace PcapDotNet.Packets.Test ...@@ -145,9 +153,6 @@ namespace PcapDotNet.Packets.Test
HttpRequestDatagram request = (HttpRequestDatagram)http; HttpRequestDatagram request = (HttpRequestDatagram)http;
Assert.AreEqual(expectedMethod, request.Method, "Method " + httpString); Assert.AreEqual(expectedMethod, request.Method, "Method " + httpString);
Assert.AreEqual(expectedUri, request.Uri, "Uri " + httpString); Assert.AreEqual(expectedUri, request.Uri, "Uri " + httpString);
// HttpHeader header = http.Header;
// Assert.IsNotNull(header);
} }
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null, HttpHeader expectedHeader = null) private static void TestHttpResponse(string httpString, HttpVersion expectedVersion = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null, HttpHeader expectedHeader = null)
......
using System; //using System;
using System.Collections.Generic; //using System.Collections.Generic;
using System.Collections.ObjectModel; //using System.Collections.ObjectModel;
using System.Linq; //using System.Linq;
using System.Text; //using System.Text;
using PcapDotNet.Base; //using PcapDotNet.Base;
//
namespace PcapDotNet.Packets.Http //namespace PcapDotNet.Packets.Http
{ //{
public class SequenceEqualityComparator<T> : IEqualityComparer<IEnumerable<T>> // public class SequenceEqualityComparator<T> : IEqualityComparer<IEnumerable<T>>
{ // {
public static SequenceEqualityComparator<T> Instance // public static SequenceEqualityComparator<T> Instance
{ // {
get { return _instance;} // get { return _instance;}
} // }
//
public bool Equals(IEnumerable<T> x, IEnumerable<T> y) // public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{ // {
return x.SequenceEqual(y); // return x.SequenceEqual(y);
} // }
//
public int GetHashCode(IEnumerable<T> obj) // public int GetHashCode(IEnumerable<T> obj)
{ // {
return obj.SequenceGetHashCode(); // return obj.SequenceGetHashCode();
} // }
//
private static readonly SequenceEqualityComparator<T> _instance = new SequenceEqualityComparator<T>(); // private static readonly SequenceEqualityComparator<T> _instance = new SequenceEqualityComparator<T>();
} // }
//
public class HttpCommaSeparatedInnerField : IEquatable<HttpCommaSeparatedInnerField> // public class HttpCommaSeparatedInnerField : IEquatable<HttpCommaSeparatedInnerField>
{ // {
public HttpCommaSeparatedInnerField(string name) // public HttpCommaSeparatedInnerField(string name)
:this(name,null as ReadOnlyCollection<byte>) // :this(name,null as ReadOnlyCollection<byte>)
{ // {
} // }
//
public HttpCommaSeparatedInnerField(string name, ReadOnlyCollection<ReadOnlyCollection<byte>> values) // public HttpCommaSeparatedInnerField(string name, ReadOnlyCollection<ReadOnlyCollection<byte>> values)
{ // {
Name = name; // Name = name;
Values = values; // Values = values;
} // }
//
public HttpCommaSeparatedInnerField(string name, IList<ReadOnlyCollection<byte>> values) // public HttpCommaSeparatedInnerField(string name, IList<ReadOnlyCollection<byte>> values)
:this(name, values.AsReadOnly()) // :this(name, values.AsReadOnly())
{ // {
} // }
//
public HttpCommaSeparatedInnerField(string name, params ReadOnlyCollection<byte>[] values) // public HttpCommaSeparatedInnerField(string name, params ReadOnlyCollection<byte>[] values)
:this(name, (IList<ReadOnlyCollection<byte>>)values) // :this(name, (IList<ReadOnlyCollection<byte>>)values)
{ // {
} // }
//
public bool Equals(HttpCommaSeparatedInnerField other) // public bool Equals(HttpCommaSeparatedInnerField other)
{ // {
return other != null && Name == other.Name && Values.SequenceEqual(other.Values, SequenceEqualityComparator<byte>.Instance); // return other != null && Name == other.Name && Values.SequenceEqual(other.Values, SequenceEqualityComparator<byte>.Instance);
} // }
//
public override string ToString() // public override string ToString()
{ // {
return Values.Select(value => Name + (value == null ? string.Empty : "=" + Encoding.ASCII.GetString(value.ToArray()))).SequenceToString(","); // return Values.Select(value => Name + (value == null ? string.Empty : "=" + Encoding.ASCII.GetString(value.ToArray()))).SequenceToString(",");
} // }
//
public string Name { get; private set; } // public string Name { get; private set; }
public ReadOnlyCollection<ReadOnlyCollection<byte>> Values { get; private set;} // public ReadOnlyCollection<ReadOnlyCollection<byte>> Values { get; private set;}
} // }
} //}
\ No newline at end of file \ No newline at end of file
...@@ -277,14 +277,14 @@ namespace PcapDotNet.Packets.Http ...@@ -277,14 +277,14 @@ namespace PcapDotNet.Packets.Http
HttpParser parser = new HttpParser(Buffer, StartOffset + headerOffsetValue, Length - headerOffsetValue); HttpParser parser = new HttpParser(Buffer, StartOffset + headerOffsetValue, Length - headerOffsetValue);
while (parser.Success) while (parser.Success)
{ {
if (parser.CarraigeReturnLineFeed().Success) if (parser.IsCarriageReturnLineFeed())
{ {
_bodyOffset = parser.Offset - StartOffset; _bodyOffset = parser.Offset + 2 - StartOffset;
break; break;
} }
string fieldName; string fieldName;
IEnumerable<byte> fieldValue; IEnumerable<byte> fieldValue;
parser.Token(out fieldName).Colon().FieldValue(out fieldValue).CarraigeReturnLineFeed(); parser.Token(out fieldName).Colon().FieldValue(out fieldValue).CarriageReturnLineFeed();
if (parser.Success) if (parser.Success)
yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue); yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
} }
......
...@@ -70,7 +70,7 @@ namespace PcapDotNet.Packets.Http ...@@ -70,7 +70,7 @@ namespace PcapDotNet.Packets.Http
public string Name { get; private set; } public string Name { get; private set; }
public ReadOnlyCollection<byte> Value { get; private set; } public ReadOnlyCollection<byte> Value { get; private set; }
public bool Equals(HttpField other) public virtual bool Equals(HttpField other)
{ {
return other != null && Name.Equals(other.Name) && Value.SequenceEqual(other.Value); return other != null && Name.Equals(other.Name) && Value.SequenceEqual(other.Value);
} }
...@@ -82,7 +82,12 @@ namespace PcapDotNet.Packets.Http ...@@ -82,7 +82,12 @@ namespace PcapDotNet.Packets.Http
public override string ToString() public override string ToString()
{ {
return string.Format("{0}: {1}", Name, _defaultEncoding.GetString(Value.ToArray())); return string.Format("{0}: {1}", Name, ValueToString());
}
protected virtual string ValueToString()
{
return _defaultEncoding.GetString(Value.ToArray());
} }
private static readonly Encoding _defaultEncoding = Encoding.GetEncoding(28591); private static readonly Encoding _defaultEncoding = Encoding.GetEncoding(28591);
......
...@@ -185,11 +185,16 @@ namespace PcapDotNet.Packets.Http ...@@ -185,11 +185,16 @@ namespace PcapDotNet.Packets.Http
return this; return this;
} }
public HttpParser CarraigeReturnLineFeed() public HttpParser CarriageReturnLineFeed()
{ {
return Bytes(AsciiBytes.CarriageReturn, AsciiBytes.LineFeed); return Bytes(AsciiBytes.CarriageReturn, AsciiBytes.LineFeed);
} }
public bool IsCarriageReturnLineFeed()
{
return IsNext(AsciiBytes.CarriageReturn) && IsNextNext(AsciiBytes.LineFeed);
}
public HttpParser DecimalNumber(int numDigits, out uint? number) public HttpParser DecimalNumber(int numDigits, out uint? number)
{ {
if (numDigits > 8 || numDigits < 0) if (numDigits > 8 || numDigits < 0)
...@@ -256,7 +261,7 @@ namespace PcapDotNet.Packets.Http ...@@ -256,7 +261,7 @@ namespace PcapDotNet.Packets.Http
else else
break; break;
} }
Console.WriteLine(count); // Console.WriteLine(count);
int reasonPhraseLength = Range.TakeWhile(value => !value.IsControl() || value == AsciiBytes.HorizontalTab).Count(); int reasonPhraseLength = Range.TakeWhile(value => !value.IsControl() || value == AsciiBytes.HorizontalTab).Count();
reasonPhrase = new Datagram(_buffer, _offset, reasonPhraseLength); reasonPhrase = new Datagram(_buffer, _offset, reasonPhraseLength);
_offset += reasonPhraseLength; _offset += reasonPhraseLength;
...@@ -359,14 +364,24 @@ namespace PcapDotNet.Packets.Http ...@@ -359,14 +364,24 @@ namespace PcapDotNet.Packets.Http
return Fail(); return Fail();
} }
private bool IsNext()
{
return _offset < _totalLength;
}
private bool IsNext(byte next)
{
return (IsNext() && Next() == next);
}
private bool IsNextNext() private bool IsNextNext()
{ {
return _offset + 1 < _totalLength; return _offset + 1 < _totalLength;
} }
private bool IsNext() private bool IsNextNext(byte nextNext)
{ {
return _offset < _totalLength; return (IsNextNext() && NextNext() == nextNext);
} }
private byte Next() private byte Next()
...@@ -379,11 +394,6 @@ namespace PcapDotNet.Packets.Http ...@@ -379,11 +394,6 @@ namespace PcapDotNet.Packets.Http
return _buffer[_offset + 1]; return _buffer[_offset + 1];
} }
private bool IsNext(byte next)
{
return (IsNext() && Next() == next);
}
private HttpParser Fail() private HttpParser Fail()
{ {
Success = false; Success = false;
......
using System.Text;
using System.Text.RegularExpressions;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http
{
internal static class HttpRegex
{
public static Regex LinearWhiteSpace
{
get { return _linearWhiteSpaceRegex; }
}
public static Regex Token
{
get { return _tokenRegex;}
}
public static Regex QuotedString
{
get { return _quotedStringRegex; }
}
public static string GetString(byte[] buffer)
{
return _encoding.GetString(buffer);
}
public static Regex Build(string pattern)
{
return new Regex(Bracket(pattern), RegexOptions.Compiled | RegexOptions.Singleline);
}
public static Regex Build(char pattern)
{
return Build(pattern.ToString());
}
public static Regex Concat(params Regex[] regexes)
{
return Build(Bracket(regexes.SequenceToString()));
}
public static Regex Or(params Regex[] regexes)
{
return Build(Bracket(regexes.SequenceToString("|")));
}
public static Regex Optional(Regex regex)
{
return Build(Bracket(string.Format("{0}?", regex)));
}
public static Regex Any(Regex regex)
{
return Build(Bracket(string.Format("{0}*", regex)));
}
public static Regex AtLeastOne(Regex regex)
{
return Build(Bracket(string.Format("{0}+", regex)));
}
public static Regex AtLeast(Regex regex, int minCount)
{
if (minCount <= 0)
return Any(regex);
if (minCount == 1)
return AtLeastOne(regex);
return Build(string.Format("(?:{0}){{{1},}}", regex, minCount));
}
public static Regex CommaSeparatedRegex(Regex element, int minCount)
{
Regex linearWhiteSpacesRegex = Any(LinearWhiteSpace);
Regex regex = Concat(Build(string.Format("{0}{1}", linearWhiteSpacesRegex, element)),
AtLeast(Build(string.Format("{0},{1}{2}", linearWhiteSpacesRegex, linearWhiteSpacesRegex, element)), minCount - 1));
return minCount <= 0 ? Optional(regex) : regex;
}
public static Regex Capture(Regex regex, string captureName)
{
return Build(string.Format("(?<{0}>{1})", captureName, regex));
}
public static Regex MatchEntire(Regex regex)
{
return Build(string.Format("^{0}$", regex));
}
private static string Bracket(string pattern)
{
return string.Format("(?:{0})", pattern);
}
private static readonly Regex _charRegex = Build(@"[\x00-\x127]");
private static readonly Regex _quotedPairRegex = Concat(Build(@"\\"), _charRegex);
private static readonly Regex _linearWhiteSpaceRegex = Concat(Optional(Build(@"\r\n")), AtLeastOne(Build(@"[ \t]")));
private static readonly Regex _qdtextRegex = Or(_linearWhiteSpaceRegex, Build(@"[^\x00-\x31\x127\""]"));
private static readonly Regex _quotedStringRegex = Concat(Build('"'), Any(Or(_qdtextRegex, _quotedPairRegex)), Build('"'));
private static readonly Regex _tokenRegex = AtLeastOne(Build(@"[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E-\x7A\x7C\x7E-\xFE]"));
private static readonly Encoding _encoding = Encoding.GetEncoding(28591);
}
}
\ No newline at end of file
...@@ -35,7 +35,7 @@ namespace PcapDotNet.Packets.Http ...@@ -35,7 +35,7 @@ namespace PcapDotNet.Packets.Http
internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset) internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset)
{ {
HttpParser parser = new HttpParser(Buffer, StartOffset, Length); HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
parser.Token(out _method).Space().RequestUri(out _uri).Space().Version(out version).CarraigeReturnLineFeed(); parser.Token(out _method).Space().RequestUri(out _uri).Space().Version(out version).CarriageReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null; headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
} }
......
...@@ -36,7 +36,7 @@ namespace PcapDotNet.Packets.Http ...@@ -36,7 +36,7 @@ namespace PcapDotNet.Packets.Http
internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset) internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset)
{ {
HttpParser parser = new HttpParser(Buffer, StartOffset, Length); HttpParser parser = new HttpParser(Buffer, StartOffset, Length);
parser.Version(out version).Space().DecimalNumber(3, out _statusCode).Space().ReasonPhrase(out _reasonPhrase).CarraigeReturnLineFeed(); parser.Version(out version).Space().DecimalNumber(3, out _statusCode).Space().ReasonPhrase(out _reasonPhrase).CarriageReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null; headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
public class HttpTransferEncodingField : HttpField public class HttpTransferEncodingField : HttpField, IEquatable<HttpTransferEncodingField>
{ {
public const string Name = "Transfer-Encoding"; public const string Name = "Transfer-Encoding";
private const string RegexTransferCodingGroupName = "TransferCoding";
public HttpTransferEncodingField(byte[] fieldValue) public HttpTransferEncodingField(IList<string> transferCodings)
:base(Name, transferCodings.SequenceToString(","))
{
TransferCodings = transferCodings.AsReadOnly();
}
public HttpTransferEncodingField(params string[] transferCodings)
:this((IList<string>)transferCodings)
{
}
internal HttpTransferEncodingField(byte[] fieldValue)
: base(Name, fieldValue) : base(Name, fieldValue)
{ {
HttpParser parser = new HttpParser(fieldValue); string fieldValueString = HttpRegex.GetString(fieldValue);
List<HttpTransferCoding> transferCodings; Match match = _regex.Match(fieldValueString);
parser.CommaSeparated(parser.TransferCoding, out transferCodings); if (!match.Success)
return;
TransferCodings = match.Groups[RegexTransferCodingGroupName].Captures.Cast<Capture>().Select(capture => capture.Value).ToArray().AsReadOnly();
}
public bool Equals(HttpTransferEncodingField other)
{
return other != null &&
(ReferenceEquals(TransferCodings, other.TransferCodings) ||
TransferCodings != null && other.TransferCodings != null && TransferCodings.SequenceEqual(other.TransferCodings));
}
public ReadOnlyCollection<string> TransferCodings { get; private set; }
public override bool Equals(HttpField other)
{
return Equals(other as HttpTransferEncodingField);
} }
protected override string ValueToString()
{
return TransferCodings == null ? string.Empty : TransferCodings.SequenceToString(",");
}
private static readonly Regex _valueRegex = HttpRegex.Or(HttpRegex.Token, HttpRegex.QuotedString);
private static readonly Regex _attributeRegex = HttpRegex.Token;
private static readonly Regex _parameterRegex = HttpRegex.Concat(_attributeRegex, HttpRegex.Build("="), _valueRegex);
private static readonly Regex _transferExtensionRegex = HttpRegex.Concat(HttpRegex.Token, HttpRegex.Any(HttpRegex.Concat(HttpRegex.Build(";"), _parameterRegex)));
private static readonly Regex _transferCodingRegex = HttpRegex.Capture(HttpRegex.Or(HttpRegex.Build("chunked"), _transferExtensionRegex), RegexTransferCodingGroupName);
private static readonly Regex _regex = HttpRegex.MatchEntire(HttpRegex.CommaSeparatedRegex(_transferCodingRegex, 1));
} }
} }
\ No newline at end of file
...@@ -115,6 +115,7 @@ ...@@ -115,6 +115,7 @@
<Compile Include="Http\HttpField.cs" /> <Compile Include="Http\HttpField.cs" />
<Compile Include="Http\HttpHeader.cs" /> <Compile Include="Http\HttpHeader.cs" />
<Compile Include="Http\HttpParser.cs" /> <Compile Include="Http\HttpParser.cs" />
<Compile Include="Http\HttpRegex.cs" />
<Compile Include="Http\HttpRequestDatagram.cs" /> <Compile Include="Http\HttpRequestDatagram.cs" />
<Compile Include="Http\HttpResponseDatagram.cs" /> <Compile Include="Http\HttpResponseDatagram.cs" />
<Compile Include="Http\HttpTransferEncodingField.cs" /> <Compile Include="Http\HttpTransferEncodingField.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