Commit 36d1d759 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent fb777528
using System.Collections.Generic;
namespace PcapDotNet.Base
{
public static class IDictionaryExtensions
{
public static bool DictionaryEquals<TKey, TValue>(this IDictionary<TKey, TValue> dictionary1, IDictionary<TKey, TValue> dictionary2, IEqualityComparer<TValue> valueComparer)
{
if (dictionary1 == null || dictionary2 == null)
return dictionary1 == null && dictionary2 == null;
if (dictionary1.Count != dictionary2.Count)
return false;
foreach (var pair in dictionary1)
{
TValue otherValue;
if (!dictionary2.TryGetValue(pair.Key, out otherValue))
return false;
if (!valueComparer.Equals(pair.Value, otherValue))
return false;
}
return true;
}
public static bool DictionaryEquals<TKey, TValue>(this IDictionary<TKey, TValue> dictionary1, IDictionary<TKey, TValue> dictionary2)
{
return dictionary1.DictionaryEquals(dictionary2, EqualityComparer<TValue>.Default);
}
}
}
\ No newline at end of file
......@@ -86,6 +86,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="FuncExtensions.cs" />
<Compile Include="IDictionaryExtensions.cs" />
<Compile Include="IEnumerableExtensions.cs" />
<Compile Include="IListExtensions.cs" />
<Compile Include="PropertyInfoExtensions.cs" />
......
......@@ -46,30 +46,33 @@ namespace PcapDotNet.Packets.Test
[TestMethod]
public void HttpParsingTest()
{
TestHttpRequest("", null, null, null);
TestHttpRequest(" ", null, null, null);
TestHttpRequest("GET", "GET", null, null);
TestHttpRequest("GET /url", "GET", "/url", null);
TestHttpRequest("");
TestHttpRequest(" ");
TestHttpRequest("GET", "GET");
TestHttpRequest("GET /url", "GET", "/url");
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 /url HTTP/1.1", "GET", "/url");
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);
TestHttpRequest("GET /url HTTP/1.1\r\nCache-Control: no-cache", "GET", "/url", HttpVersion.Version11,
new HttpHeader(HttpField.Create("Cache-Control", Encoding.ASCII.GetBytes("no-cache"))));
TestHttpResponse("HTTP/");
TestHttpResponse("HTTP/1");
TestHttpResponse("HTTP/1.");
TestHttpResponse("HTTP/1.0", HttpVersion.Version10);
TestHttpResponse("HTTP/1.0 ", HttpVersion.Version10);
TestHttpResponse("HTTP/1.0 A", HttpVersion.Version10);
TestHttpResponse("HTTP/1.0 200", HttpVersion.Version10, 200);
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);
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, " OK");
}
private static void TestHttpRequest(string httpString, string expectedMethod, string expectedUri, HttpVersion expectedVersion)
private static void TestHttpRequest(string httpString, string expectedMethod = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null)
{
Packet packet = BuildPacket(httpString);
......@@ -78,6 +81,7 @@ namespace PcapDotNet.Packets.Test
Assert.IsTrue(http.IsRequest, "IsRequest " + httpString);
Assert.IsFalse(http.IsResponse, "IsResponse " + httpString);
Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString);
Assert.AreEqual(expectedHeader, http.Header, "Header " + httpString);
HttpRequestDatagram request = (HttpRequestDatagram)http;
Assert.AreEqual(expectedMethod, request.Method, "Method " + httpString);
......@@ -87,7 +91,7 @@ namespace PcapDotNet.Packets.Test
// Assert.IsNotNull(header);
}
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion, uint? expectedStatusCode, string expectedReasonPhrase)
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null)
{
Packet packet = BuildPacket(httpString);
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using PcapDotNet.Base;
using IListExtensions = PcapDotNet.Base.IListExtensions;
namespace PcapDotNet.Packets.Http
{
public class HttpCommaSeparatedField : HttpField, IEnumerable<HttpCommaSeparatedInnerField>, IEquatable<HttpCommaSeparatedField>
{
internal HttpCommaSeparatedField(string name, params HttpCommaSeparatedInnerField[] value)
:this(name, (IEnumerable<HttpCommaSeparatedInnerField>)value)
{
}
internal HttpCommaSeparatedField(string name, IEnumerable<HttpCommaSeparatedInnerField> value = null)
:base(name)
{
_innerFields = value == null ? null : value.ToDictionary(innerField => innerField.Name, innerField => innerField);
}
public bool Equals(HttpCommaSeparatedField other)
{
return base.Equals(other) && _innerFields.DictionaryEquals(other._innerFields);
}
public override string ToString()
{
return base.ToString() + (_innerFields == null ? string.Empty : _innerFields.Values.SequenceToString(","));
}
internal HttpCommaSeparatedField(string name, IEnumerable<byte> value)
:base(name)
{
var innerFields = new Dictionary<string, List<ReadOnlyCollection<byte>>>();
foreach (var commaValue in CommaSeparate(value))
{
string innerFieldName = commaValue.Key;
List<ReadOnlyCollection<byte>> innerFieldValues;
if (!innerFields.TryGetValue(innerFieldName, out innerFieldValues))
{
innerFieldValues = new List<ReadOnlyCollection<byte>>();
innerFields.Add(innerFieldName, innerFieldValues);
}
innerFieldValues.Add(commaValue.Value == null ? null : commaValue.Value.ToArray().AsReadOnly());
}
_innerFields = innerFields.ToDictionary(field => field.Key, field => new HttpCommaSeparatedInnerField(field.Key, field.Value));
}
private static IEnumerable<KeyValuePair<string, IEnumerable<byte>>> CommaSeparate(IEnumerable<byte> buffer)
{
do
{
string key = Encoding.ASCII.GetString(buffer.TakeWhile(b => b.IsToken()).ToArray());
if (key.Length == 0)
yield break;
buffer = buffer.Skip(key.Length);
if (!buffer.Any())
{
yield return new KeyValuePair<string, IEnumerable<byte>>(key, null);
continue;
}
switch (buffer.First())
{
case AsciiBytes.EqualsSign:
buffer = buffer.Skip(1);
IEnumerable<byte> value = buffer;
int count = 0;
if (buffer.Any())
{
byte valueByte = buffer.First();
if (valueByte.IsToken()) // Token value
count = 1 + buffer.Skip(1).TakeWhile(b => b.IsToken()).Count();
else if (!TryExtractQuotedString(buffer, out count)) // Not Quoted value - Illegal value
yield break;
buffer = buffer.Skip(count);
}
yield return new KeyValuePair<string, IEnumerable<byte>>(key, value.Take(count));
if (!buffer.Any())
yield break;
switch (buffer.First())
{
case AsciiBytes.Comma:
case AsciiBytes.Space:
buffer = buffer.Skip(1).SkipWhile(b => b == AsciiBytes.Space || b == AsciiBytes.Comma);
break;
default:
yield break;
}
break;
case AsciiBytes.Comma:
case AsciiBytes.Space:
yield return new KeyValuePair<string, IEnumerable<byte>>(key, null);
buffer = buffer.Skip(1).SkipWhile(b => b == AsciiBytes.Space || b == AsciiBytes.Comma);
break;
default:
yield break;
}
} while (buffer.Any());
}
private static bool TryExtractQuotedString(IEnumerable<byte> buffer, out int count)
{
count = 0;
if (!buffer.Any() || buffer.First() != AsciiBytes.DoubleQuotationMark)
return false;
++count;
buffer = buffer.Skip(1);
while (buffer.Any())
{
++count;
byte current = buffer.First();
if (current == AsciiBytes.DoubleQuotationMark)
return true;
buffer = buffer.Skip(1);
if (current == AsciiBytes.BackSlash && buffer.Any())
{
current = buffer.First();
if (current.IsChar())
{
buffer = buffer.Skip(1);
++count;
}
}
}
return false;
}
public IEnumerator<HttpCommaSeparatedInnerField> GetEnumerator()
{
return _innerFields.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private readonly Dictionary<string, HttpCommaSeparatedInnerField> _innerFields;
}
}
\ 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
......@@ -209,14 +209,14 @@ namespace PcapDotNet.Packets.Http
}
}
// public HttpHeader Header
// {
// get
// {
// Parse();
// return _header;
// }
// }
public HttpHeader Header
{
get
{
ParseHeader();
return _header;
}
}
internal static HttpDatagram CreateDatagram(byte[] buffer, int offset, int length)
{
......@@ -234,44 +234,44 @@ namespace PcapDotNet.Packets.Http
{
if (_isParsedFirstLine)
return;
_isParsedFirstLine = true;
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 void ParseHeader()
{
if (_isParsedHeader)
return;
_isParsedHeader = true;
// 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);
// }
// }
ParseFirstLine();
if (_headerOffset == null)
return;
_header = new HttpHeader(GetHeaderFields());
}
private IEnumerable<KeyValuePair<string, IEnumerable<byte>>> GetHeaderFields()
{
int headerOffsetValue = _headerOffset.Value;
HttpParser parser = new HttpParser(Buffer, StartOffset + headerOffsetValue, Length - headerOffsetValue);
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 bool _isParsedHeader;
private int? _headerOffset;
private HttpVersion _version;
private HttpHeader _header;
......
using System;
using System.Collections.Generic;
namespace PcapDotNet.Packets.Http
{
public class HttpField : IEquatable<HttpField>
{
public static HttpField Create(string name, IEnumerable<byte> value)
{
switch (name)
{
// general-header
case "Cache-Control":
return new HttpCommaSeparatedField(name, value);
case "Connection":
case "Date":
case "Pragma":
case "Trailer":
case "Transfer-Encoding":
case "Upgrade":
case "Via":
case "Warning":
break;
}
return new HttpField(name);
}
public string Name { get; private set; }
public bool Equals(HttpField other)
{
return other != null && Name.Equals(other.Name);
}
public override bool Equals(object obj)
{
return Equals(obj as HttpField);
}
public override string ToString()
{
return string.Format("{0}: ", Name);
}
internal HttpField(string name)
{
Name = name;
}
}
}
\ No newline at end of file
using System.Collections.Generic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http
{
public class HttpHeader
public class HttpHeader : IEnumerable<HttpField>, IEquatable<HttpHeader>
{
public HttpHeader(IEnumerable<HttpField> fields)
{
_fields = fields.ToDictionary(field => field.Name, field => field);
}
public HttpHeader(params HttpField[] fields)
:this((IEnumerable<HttpField>)fields)
{
}
public bool Equals(HttpHeader other)
{
return other != null &&
_fields.DictionaryEquals(other._fields);
}
public override bool Equals(object obj)
{
return Equals(obj as HttpHeader);
}
public override string ToString()
{
return this.SequenceToString("\r\n");
}
internal HttpHeader(IEnumerable<KeyValuePair<string, IEnumerable<byte>>> fields)
{
// int totalLength = offset + length;
// Datagram data = new Datagram(buffer, offset, totalLength - offset);
// Parse field-name = token
// string fieldName;
// if (!TryParseToken(buffer, offset, totalLength - offset, out fieldName))
// return;
// offset += fieldName.Length;
// data = new Datagram(buffer, offset, totalLength - offset);
// Parse ":"
// if (data.FirstOrDefault() != AsciiBytes.Colon)
// return;
//
// ++offset;
// Parse field-value
// if (!TryParseFieldValue(buffer, offset, totalLength - offset))
// return;
// data = new Datagram(buffer, offset, totalLength - offset);
// Parse field-value = *( field-content | LWS )
// IEnumerable<byte> fieldValue = new byte[0];
//
// int lwsCount = data.CountLinearWhiteSpaces();
// if (lwsCount != 0)
// {
// offset += lwsCount;
// data = new Datagram(buffer, offset, totalLength - offset);
// }
//
// int fieldContentCount;
// IEnumerable<byte> fieldContent = data.TakeFieldContent(out fieldContentCount);
//
// if (fieldContentCount != 0)
// {
// if (fieldValue.Any())
// fieldValue = fieldValue.Concat(AsciiBytes.Space);
// fieldValue = fieldValue.Concat(fieldContent);
// offset += fieldContentCount;
// }
// data.SkipWhile
// buffer..Take(count).TakeWhile(value => value.IsToken()).Count))
// Encoding.ASCII.GetString()
}
// private HttpHeaderPart _generalHeader;
// private HttpHeaderPart _requestHeader;
// private HttpHeaderPart _responseHeader;
// private HttpHeaderPart _entityHeader;
// private Dictionary<string, >
var mergedFields = new Dictionary<string, IEnumerable<byte>>();
foreach (var field in fields)
{
string fieldName = field.Key;
IEnumerable<byte> fieldValue;
if (!mergedFields.TryGetValue(fieldName, out fieldValue))
{
fieldValue = field.Value;
mergedFields.Add(fieldName, fieldValue);
}
else
mergedFields[fieldName] = fieldValue.Concat(AsciiBytes.Space).Concat(fieldValue);
}
_fields = mergedFields.ToDictionary(field => field.Key, field => HttpField.Create(field.Key, field.Value));
}
public IEnumerator<HttpField> GetEnumerator()
{
return _fields.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private readonly Dictionary<string, HttpField> _fields = new Dictionary<string, HttpField>();
}
}
\ No newline at end of file
}
......@@ -109,7 +109,10 @@
<Compile Include="Gre\GreVersion.cs" />
<Compile Include="Http\AsciiBytes.cs" />
<Compile Include="Http\ByteExtensions.cs" />
<Compile Include="Http\HttpCommaSeparatedField.cs" />
<Compile Include="Http\HttpCommaSeparatedInnerField.cs" />
<Compile Include="Http\HttpDatagram.cs" />
<Compile Include="Http\HttpField.cs" />
<Compile Include="Http\HttpHeader.cs" />
<Compile Include="Http\HttpParser.cs" />
<Compile Include="Http\HttpRequestDatagram.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