Commit b455dfb5 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent 0f5729d8
......@@ -48,6 +48,8 @@ namespace PcapDotNet.Packets.Test
[TestMethod]
public void HttpParsingTest()
{
// Console.WriteLine(Encoding.GetEncodings().Where(e => e.Name == "iso-8859-1").Select(e => e.CodePage).SequenceToString(", "));
TestHttpRequest("");
TestHttpRequest(" ");
TestHttpRequest("GET", "GET");
......@@ -129,6 +131,12 @@ namespace PcapDotNet.Packets.Test
HttpVersion.Version11, 200, "OK",
new HttpHeader(
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)
......@@ -145,9 +153,6 @@ namespace PcapDotNet.Packets.Test
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 = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null, HttpHeader expectedHeader = null)
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http
{
public class SequenceEqualityComparator<T> : IEqualityComparer<IEnumerable<T>>
{
public static SequenceEqualityComparator<T> Instance
{
get { return _instance;}
}
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<T> obj)
{
return obj.SequenceGetHashCode();
}
private static readonly SequenceEqualityComparator<T> _instance = new SequenceEqualityComparator<T>();
}
public class HttpCommaSeparatedInnerField : IEquatable<HttpCommaSeparatedInnerField>
{
public HttpCommaSeparatedInnerField(string name)
:this(name,null as ReadOnlyCollection<byte>)
{
}
public HttpCommaSeparatedInnerField(string name, ReadOnlyCollection<ReadOnlyCollection<byte>> values)
{
Name = name;
Values = values;
}
public HttpCommaSeparatedInnerField(string name, IList<ReadOnlyCollection<byte>> values)
:this(name, values.AsReadOnly())
{
}
public HttpCommaSeparatedInnerField(string name, params ReadOnlyCollection<byte>[] values)
:this(name, (IList<ReadOnlyCollection<byte>>)values)
{
}
public bool Equals(HttpCommaSeparatedInnerField other)
{
return other != null && Name == other.Name && Values.SequenceEqual(other.Values, SequenceEqualityComparator<byte>.Instance);
}
public override string ToString()
{
return Values.Select(value => Name + (value == null ? string.Empty : "=" + Encoding.ASCII.GetString(value.ToArray()))).SequenceToString(",");
}
public string Name { get; private set; }
public ReadOnlyCollection<ReadOnlyCollection<byte>> Values { get; private set;}
}
}
\ No newline at end of file
//using System;
//using System.Collections.Generic;
//using System.Collections.ObjectModel;
//using System.Linq;
//using System.Text;
//using PcapDotNet.Base;
//
//namespace PcapDotNet.Packets.Http
//{
// public class SequenceEqualityComparator<T> : IEqualityComparer<IEnumerable<T>>
// {
// public static SequenceEqualityComparator<T> Instance
// {
// get { return _instance;}
// }
//
// public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
// {
// return x.SequenceEqual(y);
// }
//
// public int GetHashCode(IEnumerable<T> obj)
// {
// return obj.SequenceGetHashCode();
// }
//
// private static readonly SequenceEqualityComparator<T> _instance = new SequenceEqualityComparator<T>();
// }
//
// public class HttpCommaSeparatedInnerField : IEquatable<HttpCommaSeparatedInnerField>
// {
// public HttpCommaSeparatedInnerField(string name)
// :this(name,null as ReadOnlyCollection<byte>)
// {
// }
//
// public HttpCommaSeparatedInnerField(string name, ReadOnlyCollection<ReadOnlyCollection<byte>> values)
// {
// Name = name;
// Values = values;
// }
//
// public HttpCommaSeparatedInnerField(string name, IList<ReadOnlyCollection<byte>> values)
// :this(name, values.AsReadOnly())
// {
// }
//
// public HttpCommaSeparatedInnerField(string name, params ReadOnlyCollection<byte>[] values)
// :this(name, (IList<ReadOnlyCollection<byte>>)values)
// {
// }
//
// public bool Equals(HttpCommaSeparatedInnerField other)
// {
// return other != null && Name == other.Name && Values.SequenceEqual(other.Values, SequenceEqualityComparator<byte>.Instance);
// }
//
// public override string ToString()
// {
// return Values.Select(value => Name + (value == null ? string.Empty : "=" + Encoding.ASCII.GetString(value.ToArray()))).SequenceToString(",");
// }
//
// public string Name { get; private set; }
// public ReadOnlyCollection<ReadOnlyCollection<byte>> Values { get; private set;}
// }
//}
\ No newline at end of file
......@@ -277,14 +277,14 @@ namespace PcapDotNet.Packets.Http
HttpParser parser = new HttpParser(Buffer, StartOffset + headerOffsetValue, Length - headerOffsetValue);
while (parser.Success)
{
if (parser.CarraigeReturnLineFeed().Success)
if (parser.IsCarriageReturnLineFeed())
{
_bodyOffset = parser.Offset - StartOffset;
_bodyOffset = parser.Offset + 2 - StartOffset;
break;
}
string fieldName;
IEnumerable<byte> fieldValue;
parser.Token(out fieldName).Colon().FieldValue(out fieldValue).CarraigeReturnLineFeed();
parser.Token(out fieldName).Colon().FieldValue(out fieldValue).CarriageReturnLineFeed();
if (parser.Success)
yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
}
......
......@@ -70,7 +70,7 @@ namespace PcapDotNet.Packets.Http
public string Name { 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);
}
......@@ -82,7 +82,12 @@ namespace PcapDotNet.Packets.Http
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);
......
......@@ -185,11 +185,16 @@ namespace PcapDotNet.Packets.Http
return this;
}
public HttpParser CarraigeReturnLineFeed()
public HttpParser CarriageReturnLineFeed()
{
return Bytes(AsciiBytes.CarriageReturn, AsciiBytes.LineFeed);
}
public bool IsCarriageReturnLineFeed()
{
return IsNext(AsciiBytes.CarriageReturn) && IsNextNext(AsciiBytes.LineFeed);
}
public HttpParser DecimalNumber(int numDigits, out uint? number)
{
if (numDigits > 8 || numDigits < 0)
......@@ -256,7 +261,7 @@ namespace PcapDotNet.Packets.Http
else
break;
}
Console.WriteLine(count);
// Console.WriteLine(count);
int reasonPhraseLength = Range.TakeWhile(value => !value.IsControl() || value == AsciiBytes.HorizontalTab).Count();
reasonPhrase = new Datagram(_buffer, _offset, reasonPhraseLength);
_offset += reasonPhraseLength;
......@@ -359,14 +364,24 @@ namespace PcapDotNet.Packets.Http
return Fail();
}
private bool IsNext()
{
return _offset < _totalLength;
}
private bool IsNext(byte next)
{
return (IsNext() && Next() == next);
}
private bool IsNextNext()
{
return _offset + 1 < _totalLength;
}
private bool IsNext()
private bool IsNextNext(byte nextNext)
{
return _offset < _totalLength;
return (IsNextNext() && NextNext() == nextNext);
}
private byte Next()
......@@ -379,11 +394,6 @@ namespace PcapDotNet.Packets.Http
return _buffer[_offset + 1];
}
private bool IsNext(byte next)
{
return (IsNext() && Next() == next);
}
private HttpParser Fail()
{
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
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();
parser.Token(out _method).Space().RequestUri(out _uri).Space().Version(out version).CarriageReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
}
......
......@@ -36,7 +36,7 @@ namespace PcapDotNet.Packets.Http
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();
parser.Version(out version).Space().DecimalNumber(3, out _statusCode).Space().ReasonPhrase(out _reasonPhrase).CarriageReturnLineFeed();
headerOffset = parser.Success ? (int?)(parser.Offset - StartOffset) : null;
}
......
using System;
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
{
public class HttpTransferEncodingField : HttpField
public class HttpTransferEncodingField : HttpField, IEquatable<HttpTransferEncodingField>
{
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)
{
HttpParser parser = new HttpParser(fieldValue);
List<HttpTransferCoding> transferCodings;
parser.CommaSeparated(parser.TransferCoding, out transferCodings);
string fieldValueString = HttpRegex.GetString(fieldValue);
Match match = _regex.Match(fieldValueString);
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 @@
<Compile Include="Http\HttpField.cs" />
<Compile Include="Http\HttpHeader.cs" />
<Compile Include="Http\HttpParser.cs" />
<Compile Include="Http\HttpRegex.cs" />
<Compile Include="Http\HttpRequestDatagram.cs" />
<Compile Include="Http\HttpResponseDatagram.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