Commit f0e4f8f6 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent 5b45d41c
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Linq;
namespace PcapDotNet.Base
{
public static class MatchExtensions
{
public static IEnumerable<string> GroupCapturesValues(this Match match, string groupName)
{
return match.Groups[groupName].Captures.Cast<Capture>().Select(capture => capture.Value);
}
}
}
\ No newline at end of file
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
<Compile Include="IDictionaryExtensions.cs" /> <Compile Include="IDictionaryExtensions.cs" />
<Compile Include="IEnumerableExtensions.cs" /> <Compile Include="IEnumerableExtensions.cs" />
<Compile Include="IListExtensions.cs" /> <Compile Include="IListExtensions.cs" />
<Compile Include="MatchExtensions.cs" />
<Compile Include="PropertyInfoExtensions.cs" /> <Compile Include="PropertyInfoExtensions.cs" />
<Compile Include="TimeSpanExtensions.cs" /> <Compile Include="TimeSpanExtensions.cs" />
<Compile Include="TypeExtensions.cs" /> <Compile Include="TypeExtensions.cs" />
......
...@@ -85,7 +85,7 @@ namespace PcapDotNet.Packets.Test ...@@ -85,7 +85,7 @@ namespace PcapDotNet.Packets.Test
"\r\n" + "\r\n" +
"A: B\r\n", "A: B\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader()); new HttpHeader(), Datagram.Empty);
TestHttpRequest("GET /url HTTP/1.1\r\n" + TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" + "A: B\r\n" +
...@@ -93,7 +93,7 @@ namespace PcapDotNet.Packets.Test ...@@ -93,7 +93,7 @@ namespace PcapDotNet.Packets.Test
"B: C\r\n", "B: C\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader( new HttpHeader(
new HttpField("A", "B"))); new HttpField("A", "B")), Datagram.Empty);
TestHttpRequest("GET /url HTTP/1.1\r\n" + TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" + "A: B\r\n" +
...@@ -161,7 +161,7 @@ namespace PcapDotNet.Packets.Test ...@@ -161,7 +161,7 @@ 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);
Assert.AreEqual(expectedBody, request.Body); Assert.AreEqual(expectedBody, request.Body, "Body " + httpString);
} }
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null, HttpHeader expectedHeader = null, Datagram expectedBody = null) private static void TestHttpResponse(string httpString, HttpVersion expectedVersion = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null, HttpHeader expectedHeader = null, Datagram expectedBody = null)
......
...@@ -28,6 +28,27 @@ namespace PcapDotNet.Packets ...@@ -28,6 +28,27 @@ namespace PcapDotNet.Packets
return array.Compare(offset, other, otherOffset, count) == 0; return array.Compare(offset, other, otherOffset, count) == 0;
} }
public static int Find(this byte[] array, int offset, int count, byte[] other, int otherOffset, int otherCount)
{
if (otherCount > count)
return array.Length;
int maxOffset = offset + count - otherCount;
while (offset < maxOffset)
{
if (Compare(array, offset, other, otherOffset, otherCount) == 0)
return offset;
++offset;
}
return array.Length;
}
public static int Find(this byte[] array, int offset, int count, byte[] other)
{
return array.Find(offset, count, other, 0, other.Length);
}
/// <summary> /// <summary>
/// Copies a specified number of bytes from a source array starting at a particular offset to a destination array starting at a particular offset. /// Copies a specified number of bytes from a source array starting at a particular offset to a destination array starting at a particular offset.
/// </summary> /// </summary>
......
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text.RegularExpressions;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http
{
public class HttpContentTypeField : HttpField, IEquatable<HttpContentTypeField>
{
public const string Name = "Content-Type";
public bool Equals(HttpContentTypeField other)
{
return other != null &&
MediaType == other.MediaType &&
MediaSubType == other.MediaSubType &&
Parameters == other.Parameters;
}
public string MediaType { get; private set; }
public string MediaSubType { get; private set; }
public HttpFieldParameters Parameters { get; private set;}
public override bool Equals(HttpField other)
{
return Equals(other as HttpContentTypeField);
}
internal HttpContentTypeField(byte[] fieldValue)
: base(Name, fieldValue)
{
string fieldValueString = HttpRegex.GetString(fieldValue);
Match match = _regex.Match(fieldValueString);
if (!match.Success)
return;
MediaType = match.Groups[MediaTypeGroupName].Captures.Cast<Capture>().First().Value;
MediaSubType = match.Groups[MediaSubTypeGroupName].Captures.Cast<Capture>().First().Value;
Parameters = new HttpFieldParameters(match.GroupCapturesValues(HttpRegex.ParameterNameGroupName),
match.GroupCapturesValues(HttpRegex.ParameterValueGroupName));
}
private const string MediaTypeGroupName = "MediaType";
private const string MediaSubTypeGroupName = "MediaSubtType";
private static readonly Regex _regex =
HttpRegex.MatchEntire(HttpRegex.Concat(HttpRegex.Capture(HttpRegex.Token, MediaTypeGroupName),
HttpRegex.Build('/'),
HttpRegex.Capture(HttpRegex.Token, MediaSubTypeGroupName),
HttpRegex.OptionalParameters));
}
}
\ No newline at end of file
...@@ -231,7 +231,7 @@ namespace PcapDotNet.Packets.Http ...@@ -231,7 +231,7 @@ namespace PcapDotNet.Packets.Http
int bodyOffsetValue = _bodyOffset.Value; int bodyOffsetValue = _bodyOffset.Value;
if (!IsBodyPossible) if (!IsBodyPossible)
{ {
_body = new Datagram(new byte[0]); _body = Empty;
return _body; return _body;
} }
...@@ -245,24 +245,35 @@ namespace PcapDotNet.Packets.Http ...@@ -245,24 +245,35 @@ namespace PcapDotNet.Packets.Http
} }
} }
// HttpContentLengthField contentLengthField = Header.ContentLength; HttpContentLengthField contentLengthField = Header.ContentLength;
// if (contentLengthField != null) if (contentLengthField != null)
// { {
// int contentLength = contentLengthField.ContentLength; uint? contentLength = contentLengthField.ContentLength;
// _body = new Datagram(Buffer, StartOffset + bodyOffsetValue, Math.Min(contentLength, Length - bodyOffsetValue)); if (contentLength != null)
// return _body; {
// } _body = new Datagram(Buffer, StartOffset + bodyOffsetValue, Math.Min((int)contentLength.Value, Length - bodyOffsetValue));
// return _body;
// HttpContentTypeField contentTypeField = Header.ContentType; }
// if (contentTypeField != null) }
// {
// if (contentTypeField.MediaTypes.Contains("multipart/byteranges")) HttpContentTypeField contentTypeField = Header.ContentType;
// { if (contentTypeField != null)
// int transferLength = contentTypeField.TransferLength; {
// _body = new Datagram(Buffer, StartOffset + bodyOffsetValue, Math.Min(transferLength, Length - bodyOffsetValue)); if (contentTypeField.MediaType == "multipart" &&
// } contentTypeField.MediaSubType == "byteranges")
// {
// } string boundary = contentTypeField.Parameters["boundary"];
if (boundary != null)
{
byte[] lastBoundaryBuffer = Encoding.ASCII.GetBytes(string.Format("--{0}--\r\n", boundary));
int lastBoundaryOffset = Buffer.Find(StartOffset + bodyOffsetValue, Length - bodyOffsetValue, lastBoundaryBuffer);
int lastBoundaryEnd = lastBoundaryOffset + lastBoundaryBuffer.Length;
_body = new Datagram(Buffer, StartOffset + bodyOffsetValue,
Math.Min(lastBoundaryEnd - StartOffset - bodyOffsetValue, Length - bodyOffsetValue));
return _body;
}
}
}
_body = new Datagram(Buffer, StartOffset + bodyOffsetValue, Length - bodyOffsetValue); _body = new Datagram(Buffer, StartOffset + bodyOffsetValue, Length - bodyOffsetValue);
} }
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace PcapDotNet.Packets.Http
{
public class HttpFieldParameters : IEnumerable<KeyValuePair<string, string>>
{
internal HttpFieldParameters(IEnumerable<string> parametersNames, IEnumerable<string> parametersValues)
{
var nameEnumerator = parametersNames.GetEnumerator();
var valueEnumerator = parametersValues.GetEnumerator();
while (nameEnumerator.MoveNext())
{
if (!valueEnumerator.MoveNext())
throw new ArgumentException(string.Format("more names ({0}) were given than values ({1})", parametersNames.Count(), parametersValues.Count()), "parametersValues");
_parameters.Add(nameEnumerator.Current, valueEnumerator.Current);
}
if (valueEnumerator.MoveNext())
throw new ArgumentException(string.Format("more values ({0}) were given than names ({1})", parametersValues.Count(), parametersNames.Count()), "parametersNames");
}
public string this[string name]
{
get
{
string value;
if (!_parameters.TryGetValue(name, out value))
return null;
return value;
}
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _parameters.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private readonly Dictionary<string, string> _parameters = new Dictionary<string, string>();
}
}
\ No newline at end of file
...@@ -22,10 +22,31 @@ namespace PcapDotNet.Packets.Http ...@@ -22,10 +22,31 @@ namespace PcapDotNet.Packets.Http
{ {
get get
{ {
HttpField field; return GetField<HttpTransferEncodingField>(HttpTransferEncodingField.Name);
if (!_fields.TryGetValue(HttpTransferEncodingField.Name, out field)) }
return null; }
return (HttpTransferEncodingField)field;
private T GetField<T>(string fieldName) where T : HttpField
{
HttpField field;
if (!_fields.TryGetValue(fieldName, out field))
return null;
return (T)field;
}
public HttpContentLengthField ContentLength
{
get
{
return GetField<HttpContentLengthField>(HttpContentLengthField.Name);
}
}
public HttpContentTypeField ContentType
{
get
{
return GetField<HttpContentTypeField>(HttpContentTypeField.Name);
} }
} }
......
...@@ -6,6 +6,9 @@ namespace PcapDotNet.Packets.Http ...@@ -6,6 +6,9 @@ namespace PcapDotNet.Packets.Http
{ {
internal static class HttpRegex internal static class HttpRegex
{ {
public const string ParameterNameGroupName = "ParameterName";
public const string ParameterValueGroupName = "ParameterValue";
public static Regex LinearWhiteSpace public static Regex LinearWhiteSpace
{ {
get { return _linearWhiteSpaceRegex; } get { return _linearWhiteSpaceRegex; }
...@@ -21,6 +24,11 @@ namespace PcapDotNet.Packets.Http ...@@ -21,6 +24,11 @@ namespace PcapDotNet.Packets.Http
get { return _quotedStringRegex; } get { return _quotedStringRegex; }
} }
public static Regex OptionalParameters
{
get { return _optionalParametersRegex; }
}
public static string GetString(byte[] buffer) public static string GetString(byte[] buffer)
{ {
return _encoding.GetString(buffer); return _encoding.GetString(buffer);
...@@ -99,6 +107,9 @@ namespace PcapDotNet.Packets.Http ...@@ -99,6 +107,9 @@ namespace PcapDotNet.Packets.Http
private static readonly Regex _qdtextRegex = Or(_linearWhiteSpaceRegex, Build(@"[^\x00-\x31\x127\""]")); 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 _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 Regex _tokenRegex = AtLeastOne(Build(@"[\x21\x23-\x27\x2A\x2B\x2D\x2E0-9A-Z\x5E-\x7A\x7C\x7E-\xFE]"));
private static readonly Regex _valueRegex = Or(Token, QuotedString);
private static readonly Regex _parameterRegex = Concat(Capture(_tokenRegex, ParameterNameGroupName), Build("="), Capture(_valueRegex, ParameterValueGroupName));
private static readonly Regex _optionalParametersRegex = Any(Concat(Build(";"), _parameterRegex));
private static readonly Encoding _encoding = Encoding.GetEncoding(28591); private static readonly Encoding _encoding = Encoding.GetEncoding(28591);
} }
......
...@@ -34,7 +34,7 @@ namespace PcapDotNet.Packets.Http ...@@ -34,7 +34,7 @@ namespace PcapDotNet.Packets.Http
protected override bool IsBodyPossible protected override bool IsBodyPossible
{ {
get { throw new NotImplementedException(); } get { return Header.ContentLength != null; }
} }
internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset) internal override void ParseSpecificFirstLine(out HttpVersion version, out int? headerOffset)
......
...@@ -39,7 +39,7 @@ namespace PcapDotNet.Packets.Http ...@@ -39,7 +39,7 @@ namespace PcapDotNet.Packets.Http
if (transferCodings.Any(coding => coding.Any(c => c.IsUpperCaseAlpha()))) if (transferCodings.Any(coding => coding.Any(c => c.IsUpperCaseAlpha())))
_transferCodings = transferCodings.Select(coding => coding.ToLowerInvariant()).ToArray().AsReadOnly(); _transferCodings = transferCodings.Select(coding => coding.ToLowerInvariant()).ToArray().AsReadOnly();
else else
_transferCodings = _transferCodings.AsReadOnly(); _transferCodings = transferCodings.AsReadOnly();
} }
public override bool Equals(HttpField other) public override bool Equals(HttpField other)
...@@ -55,7 +55,7 @@ namespace PcapDotNet.Packets.Http ...@@ -55,7 +55,7 @@ namespace PcapDotNet.Packets.Http
if (!match.Success) if (!match.Success)
return; return;
SetTransferCodings(match.Groups[RegexTransferCodingGroupName].Captures.Cast<Capture>().Select(capture => capture.Value).ToArray()); SetTransferCodings(match.GroupCapturesValues(RegexTransferCodingGroupName).ToArray());
} }
protected override string ValueToString() protected override string ValueToString()
...@@ -65,10 +65,7 @@ namespace PcapDotNet.Packets.Http ...@@ -65,10 +65,7 @@ namespace PcapDotNet.Packets.Http
private ReadOnlyCollection<string> _transferCodings; private ReadOnlyCollection<string> _transferCodings;
private static readonly Regex _valueRegex = HttpRegex.Or(HttpRegex.Token, HttpRegex.QuotedString); private static readonly Regex _transferExtensionRegex = HttpRegex.Concat(HttpRegex.Token, HttpRegex.OptionalParameters);
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 _transferCodingRegex = HttpRegex.Capture(HttpRegex.Or(HttpRegex.Build("chunked"), _transferExtensionRegex), RegexTransferCodingGroupName);
private static readonly Regex _regex = HttpRegex.MatchEntire(HttpRegex.CommaSeparatedRegex(_transferCodingRegex, 1)); private static readonly Regex _regex = HttpRegex.MatchEntire(HttpRegex.CommaSeparatedRegex(_transferCodingRegex, 1));
} }
......
...@@ -112,8 +112,10 @@ ...@@ -112,8 +112,10 @@
<Compile Include="Http\HttpCommaSeparatedField.cs" /> <Compile Include="Http\HttpCommaSeparatedField.cs" />
<Compile Include="Http\HttpCommaSeparatedInnerField.cs" /> <Compile Include="Http\HttpCommaSeparatedInnerField.cs" />
<Compile Include="Http\HttpContentLengthField.cs" /> <Compile Include="Http\HttpContentLengthField.cs" />
<Compile Include="Http\HttpContentTypeField.cs" />
<Compile Include="Http\HttpDatagram.cs" /> <Compile Include="Http\HttpDatagram.cs" />
<Compile Include="Http\HttpField.cs" /> <Compile Include="Http\HttpField.cs" />
<Compile Include="Http\HttpFieldParameters.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\HttpRegex.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