Commit 40180aa4 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent 55dce1dd
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
...@@ -48,8 +49,6 @@ namespace PcapDotNet.Packets.Test ...@@ -48,8 +49,6 @@ 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");
...@@ -85,7 +84,7 @@ namespace PcapDotNet.Packets.Test ...@@ -85,7 +84,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(), Datagram.Empty); new HttpHeader(), string.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 +92,7 @@ namespace PcapDotNet.Packets.Test ...@@ -93,7 +92,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")), Datagram.Empty); new HttpField("A", "B")), string.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" +
...@@ -117,7 +116,7 @@ namespace PcapDotNet.Packets.Test ...@@ -117,7 +116,7 @@ namespace PcapDotNet.Packets.Test
"\r\n", "\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(), new HttpHeader(),
Datagram.Empty); string.Empty);
TestHttpResponse("HTTP/"); TestHttpResponse("HTTP/");
...@@ -145,10 +144,81 @@ namespace PcapDotNet.Packets.Test ...@@ -145,10 +144,81 @@ namespace PcapDotNet.Packets.Test
new HttpHeader( new HttpHeader(
new HttpTransferEncodingField("chunked", "a", "b", "c", "d", "e;f=g;h=\"ijk lmn\""))); new HttpTransferEncodingField("chunked", "a", "b", "c", "d", "e;f=g;h=\"ijk lmn\"")));
TestHttpResponse("HTTP/1.1 200 OK\r\n" +
"\r\n" +
"Body",
HttpVersion.Version11, 200, "OK", HttpHeader.Empty,
"Body");
TestHttpResponse("HTTP/1.1 200 OK\r\n" +
"Transfer-Encoding: chunked\r\n" +
"\r\n" +
"5\r\n" +
"This \r\n" +
"3\r\n" +
"is \r\n" +
"4\r\n" +
"the \r\n" +
"4\r\n" +
"body\r\n" +
"0\r\n",
HttpVersion.Version11, 200, "OK", new HttpHeader(new HttpTransferEncodingField("chunked")),
"5\r\n" +
"This \r\n" +
"3\r\n" +
"is \r\n" +
"4\r\n" +
"the \r\n" +
"4\r\n" +
"body\r\n" +
"0\r\n");
TestHttpResponse("HTTP/1.1 200 OK\r\n" +
"Content-Length: 16\r\n" +
"\r\n" +
"This is the body",
HttpVersion.Version11, 200, "OK", new HttpHeader(new HttpContentLengthField(16)),
"This is the body");
TestHttpResponse("HTTP/1.1 206 Partial content\r\n" +
"Date: Wed, 15 Nov 1995 06:25:24 GMT\r\n" +
"Last-modified: Wed, 15 Nov 1995 04:58:08 GMT\r\n" +
"Content-type: multipart/byteranges; boundary=THIS_STRING_SEPARATES\r\n" +
"\r\n" +
"--THIS_STRING_SEPARATES\r\n" +
"Content-type: application/pdf\r\n" +
"Content-range: bytes 500-999/8000\r\n" +
"\r\n" +
"...the first range...\r\n" +
"--THIS_STRING_SEPARATES\r\n" +
"Content-type: application/pdf\r\n" +
"Content-range: bytes 7000-7999/8000\r\n" +
"\r\n" +
"...the second range\r\n" +
"--THIS_STRING_SEPARATES--",
HttpVersion.Version11, 206, "Partial content",
new HttpHeader(
new HttpField("Date", "Wed, 15 Nov 1995 06:25:24 GMT"),
new HttpField("Last-modified", "Wed, 15 Nov 1995 04:58:08 GMT"),
new HttpContentTypeField("multipart", "byteranges",
new HttpFieldParameters(new KeyValuePair<string, string>("boundary", "THIS_STRING_SEPARATES")))),
"--THIS_STRING_SEPARATES\r\n" +
"Content-type: application/pdf\r\n" +
"Content-range: bytes 500-999/8000\r\n" +
"\r\n" +
"...the first range...\r\n" +
"--THIS_STRING_SEPARATES\r\n" +
"Content-type: application/pdf\r\n" +
"Content-range: bytes 7000-7999/8000\r\n" +
"\r\n" +
"...the second range\r\n" +
"--THIS_STRING_SEPARATES--");
} }
private static void TestHttpRequest(string httpString, string expectedMethod = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null, Datagram expectedBody = null) private static void TestHttpRequest(string httpString, string expectedMethod = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null, string expectedBodyString = null)
{ {
Datagram expectedBody = expectedBodyString == null ? null : new Datagram(Encoding.ASCII.GetBytes(expectedBodyString));
Packet packet = BuildPacket(httpString); Packet packet = BuildPacket(httpString);
// HTTP // HTTP
...@@ -164,8 +234,10 @@ namespace PcapDotNet.Packets.Test ...@@ -164,8 +234,10 @@ namespace PcapDotNet.Packets.Test
Assert.AreEqual(expectedBody, request.Body, "Body " + httpString); 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, string expectedBodyString = null)
{ {
Datagram expectedBody = expectedBodyString == null ? null : new Datagram(Encoding.ASCII.GetBytes(expectedBodyString));
Packet packet = BuildPacket(httpString); Packet packet = BuildPacket(httpString);
// HTTP // HTTP
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
public class HttpContentLengthField : HttpField public class HttpContentLengthField : HttpField
{ {
public const string Name = "Content-Length"; public const string Name = "Content-Length";
public const string NameLower = "content-length";
public HttpContentLengthField(uint contentLength) public HttpContentLengthField(uint contentLength)
:base(Name, contentLength.ToString()) :base(Name, contentLength.ToString())
......
...@@ -9,13 +9,22 @@ namespace PcapDotNet.Packets.Http ...@@ -9,13 +9,22 @@ namespace PcapDotNet.Packets.Http
public class HttpContentTypeField : HttpField, IEquatable<HttpContentTypeField> public class HttpContentTypeField : HttpField, IEquatable<HttpContentTypeField>
{ {
public const string Name = "Content-Type"; public const string Name = "Content-Type";
public const string NameLower = "content-type";
public HttpContentTypeField(string mediaType, string mediaSubType, HttpFieldParameters parameters)
:base(Name, string.Format("{0}/{1}{2}", mediaType, mediaSubType, parameters))
{
MediaType = mediaType;
MediaSubType = mediaSubType;
Parameters = parameters;
}
public bool Equals(HttpContentTypeField other) public bool Equals(HttpContentTypeField other)
{ {
return other != null && return other != null &&
MediaType == other.MediaType && MediaType == other.MediaType &&
MediaSubType == other.MediaSubType && MediaSubType == other.MediaSubType &&
Parameters == other.Parameters; Parameters.Equals(other.Parameters);
} }
public string MediaType { get; private set; } public string MediaType { get; private set; }
......
...@@ -261,7 +261,7 @@ namespace PcapDotNet.Packets.Http ...@@ -261,7 +261,7 @@ namespace PcapDotNet.Packets.Http
string boundary = contentTypeField.Parameters["boundary"]; string boundary = contentTypeField.Parameters["boundary"];
if (boundary != null) if (boundary != null)
{ {
byte[] lastBoundaryBuffer = Encoding.ASCII.GetBytes(string.Format("--{0}--\r\n", boundary)); byte[] lastBoundaryBuffer = Encoding.ASCII.GetBytes(string.Format("\r\n--{0}--", boundary));
int lastBoundaryOffset = buffer.Find(offset, length, lastBoundaryBuffer); int lastBoundaryOffset = buffer.Find(offset, length, lastBoundaryBuffer);
int lastBoundaryEnd = lastBoundaryOffset + lastBoundaryBuffer.Length; int lastBoundaryEnd = lastBoundaryOffset + lastBoundaryBuffer.Length;
return new Datagram(buffer, offset, return new Datagram(buffer, offset,
......
...@@ -11,12 +11,14 @@ namespace PcapDotNet.Packets.Http ...@@ -11,12 +11,14 @@ namespace PcapDotNet.Packets.Http
{ {
public static HttpField CreateField(string fieldName, byte[] fieldValue) public static HttpField CreateField(string fieldName, byte[] fieldValue)
{ {
switch (fieldName) switch (fieldName.ToLowerInvariant())
{ {
case HttpTransferEncodingField.Name: case HttpTransferEncodingField.NameLower:
return new HttpTransferEncodingField(fieldValue); return new HttpTransferEncodingField(fieldValue);
case HttpContentLengthField.Name: case HttpContentLengthField.NameLower:
return new HttpContentLengthField(fieldValue); return new HttpContentLengthField(fieldValue);
case HttpContentTypeField.NameLower:
return new HttpContentTypeField(fieldValue);
default: default:
return new HttpField(fieldName, fieldValue.ToArray()); return new HttpField(fieldName, fieldValue.ToArray());
...@@ -74,7 +76,7 @@ namespace PcapDotNet.Packets.Http ...@@ -74,7 +76,7 @@ namespace PcapDotNet.Packets.Http
public virtual 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, StringComparison.InvariantCultureIgnoreCase) && Value.SequenceEqual(other.Value);
} }
public override bool Equals(object obj) public override bool Equals(object obj)
......
...@@ -2,11 +2,23 @@ ...@@ -2,11 +2,23 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
public class HttpFieldParameters : IEnumerable<KeyValuePair<string, string>> public class HttpFieldParameters : IEnumerable<KeyValuePair<string, string>>, IEquatable<HttpFieldParameters>
{ {
public HttpFieldParameters(params KeyValuePair<string, string>[] parameters)
:this((IEnumerable<KeyValuePair<string, string>>)parameters)
{
}
public HttpFieldParameters(IEnumerable<KeyValuePair<string, string>> parameters)
{
_parameters = parameters.ToDictionary(pair => pair.Key, pair => pair.Value);
}
internal HttpFieldParameters(IEnumerable<string> parametersNames, IEnumerable<string> parametersValues) internal HttpFieldParameters(IEnumerable<string> parametersNames, IEnumerable<string> parametersValues)
{ {
var nameEnumerator = parametersNames.GetEnumerator(); var nameEnumerator = parametersNames.GetEnumerator();
...@@ -43,6 +55,34 @@ namespace PcapDotNet.Packets.Http ...@@ -43,6 +55,34 @@ namespace PcapDotNet.Packets.Http
return GetEnumerator(); return GetEnumerator();
} }
public bool Equals(HttpFieldParameters other)
{
return _parameters.DictionaryEquals(other._parameters);
}
public override bool Equals(object obj)
{
return Equals(obj as HttpFieldParameters);
}
public override string ToString()
{
if (!this.Any())
return string.Empty;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append(" ");
foreach (var parameter in this)
{
stringBuilder.Append(";");
stringBuilder.Append(parameter.Key);
stringBuilder.Append("=");
stringBuilder.Append(parameter.Value);
}
return stringBuilder.ToString();
}
private readonly Dictionary<string, string> _parameters = new Dictionary<string, string>(); private readonly Dictionary<string, string> _parameters = new Dictionary<string, string>();
} }
} }
\ No newline at end of file
...@@ -8,9 +8,11 @@ namespace PcapDotNet.Packets.Http ...@@ -8,9 +8,11 @@ namespace PcapDotNet.Packets.Http
{ {
public class HttpHeader : IEnumerable<HttpField>, IEquatable<HttpHeader> public class HttpHeader : IEnumerable<HttpField>, IEquatable<HttpHeader>
{ {
public static HttpHeader Empty { get { return _empty; } }
public HttpHeader(IEnumerable<HttpField> fields) public HttpHeader(IEnumerable<HttpField> fields)
{ {
_fields = fields.ToDictionary(field => field.Name, field => field); _fields = fields.ToDictionary(field => field.Name, field => field, StringComparer.InvariantCultureIgnoreCase);
} }
public HttpHeader(params HttpField[] fields) public HttpHeader(params HttpField[] fields)
...@@ -78,7 +80,7 @@ namespace PcapDotNet.Packets.Http ...@@ -78,7 +80,7 @@ namespace PcapDotNet.Packets.Http
internal HttpHeader(IEnumerable<KeyValuePair<string, IEnumerable<byte>>> fields) internal HttpHeader(IEnumerable<KeyValuePair<string, IEnumerable<byte>>> fields)
{ {
var mergedFields = new Dictionary<string, IEnumerable<byte>>(); var mergedFields = new Dictionary<string, IEnumerable<byte>>(StringComparer.InvariantCultureIgnoreCase);
foreach (var field in fields) foreach (var field in fields)
{ {
string fieldName = field.Key; string fieldName = field.Key;
...@@ -92,9 +94,10 @@ namespace PcapDotNet.Packets.Http ...@@ -92,9 +94,10 @@ namespace PcapDotNet.Packets.Http
mergedFields[fieldName] = fieldValue.Concat(AsciiBytes.Comma).Concat(field.Value); mergedFields[fieldName] = fieldValue.Concat(AsciiBytes.Comma).Concat(field.Value);
} }
_fields = mergedFields.ToDictionary(field => field.Key, field => HttpField.CreateField(field.Key, field.Value.ToArray())); _fields = mergedFields.ToDictionary(field => field.Key, field => HttpField.CreateField(field.Key, field.Value.ToArray()), StringComparer.InvariantCultureIgnoreCase);
} }
private readonly Dictionary<string, HttpField> _fields = new Dictionary<string, HttpField>(); private static readonly HttpHeader _empty = new HttpHeader();
private readonly Dictionary<string, HttpField> _fields = new Dictionary<string, HttpField>(StringComparer.InvariantCultureIgnoreCase);
} }
} }
...@@ -109,7 +109,7 @@ namespace PcapDotNet.Packets.Http ...@@ -109,7 +109,7 @@ namespace PcapDotNet.Packets.Http
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 _valueRegex = Or(Token, QuotedString);
private static readonly Regex _parameterRegex = Concat(Capture(_tokenRegex, ParameterNameGroupName), Build("="), Capture(_valueRegex, ParameterValueGroupName)); 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 Regex _optionalParametersRegex = Any(Concat(Build(";"), Optional(_linearWhiteSpaceRegex), _parameterRegex));
private static readonly Encoding _encoding = Encoding.GetEncoding(28591); private static readonly Encoding _encoding = Encoding.GetEncoding(28591);
} }
......
...@@ -11,6 +11,7 @@ namespace PcapDotNet.Packets.Http ...@@ -11,6 +11,7 @@ namespace PcapDotNet.Packets.Http
public class HttpTransferEncodingField : HttpField, IEquatable<HttpTransferEncodingField> public class HttpTransferEncodingField : HttpField, IEquatable<HttpTransferEncodingField>
{ {
public const string Name = "Transfer-Encoding"; public const string Name = "Transfer-Encoding";
public const string NameLower = "transfer-encoding";
private const string RegexTransferCodingGroupName = "TransferCoding"; private const string RegexTransferCodingGroupName = "TransferCoding";
public HttpTransferEncodingField(IList<string> transferCodings) public HttpTransferEncodingField(IList<string> transferCodings)
......
using System; using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using PcapDotNet.Packets.Http; using PcapDotNet.Packets.Http;
namespace PcapDotNet.Packets.Transport namespace PcapDotNet.Packets.Transport
...@@ -258,9 +260,29 @@ namespace PcapDotNet.Packets.Transport ...@@ -258,9 +260,29 @@ namespace PcapDotNet.Packets.Transport
{ {
get get
{ {
if (_http == null && Length >= HeaderMinimumLength && Length >= HeaderLength) return HttpCollection == null ? null : HttpCollection[0];
_http = HttpDatagram.CreateDatagram(Buffer, StartOffset + HeaderLength, Length - HeaderLength); }
return _http; }
public ReadOnlyCollection<HttpDatagram> HttpCollection
{
get
{
if (_httpCollection == null && Length >= HeaderMinimumLength && Length >= HeaderLength)
{
List<HttpDatagram> httpList = new List<HttpDatagram>();
int httpParsed = 0;
do
{
HttpDatagram httpDatagram = HttpDatagram.CreateDatagram(Buffer, StartOffset + HeaderLength + httpParsed, Length - HeaderLength - httpParsed);
httpParsed += httpDatagram.Length;
httpList.Add(httpDatagram);
} while (Length > HeaderLength + httpParsed);
_httpCollection = httpList.AsReadOnly();
}
return _httpCollection;
} }
} }
...@@ -314,6 +336,6 @@ namespace PcapDotNet.Packets.Transport ...@@ -314,6 +336,6 @@ namespace PcapDotNet.Packets.Transport
} }
private TcpOptions _options; private TcpOptions _options;
private HttpDatagram _http; private ReadOnlyCollection<HttpDatagram> _httpCollection;
} }
} }
\ No newline at end of file
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