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");
...@@ -66,26 +65,26 @@ namespace PcapDotNet.Packets.Test ...@@ -66,26 +65,26 @@ namespace PcapDotNet.Packets.Test
new HttpField("Cache-Control", "no-cache"))); new HttpField("Cache-Control", "no-cache")));
TestHttpRequest("GET /url HTTP/1.1\r\n" + TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A:B\r\n" + "A:B\r\n" +
"B: C\r\n" + "B: C\r\n" +
"C: \r\n \r\n D\r\n" + "C: \r\n \r\n D\r\n" +
"D: E\r\n" + "D: E\r\n" +
"D: F\r\n" + "D: F\r\n" +
"E: G,H\r\n" + "E: G,H\r\n" +
"E: I,J\r\n", "E: I,J\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader( new HttpHeader(
new HttpField("A", "B"), new HttpField("A", "B"),
new HttpField("B", "C"), new HttpField("B", "C"),
new HttpField("C", "D"), new HttpField("C", "D"),
new HttpField("D", "E,F"), new HttpField("D", "E,F"),
new HttpField("E", "G,H,I,J"))); new HttpField("E", "G,H,I,J")));
TestHttpRequest("GET /url HTTP/1.1\r\n" + TestHttpRequest("GET /url HTTP/1.1\r\n" +
"\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" +
...@@ -114,10 +113,10 @@ namespace PcapDotNet.Packets.Test ...@@ -114,10 +113,10 @@ namespace PcapDotNet.Packets.Test
new HttpField("B", "C"))); new HttpField("B", "C")));
TestHttpRequest("GET /url HTTP/1.1\r\n" + TestHttpRequest("GET /url HTTP/1.1\r\n" +
"\r\n", "\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(), new HttpHeader(),
Datagram.Empty); string.Empty);
TestHttpResponse("HTTP/"); TestHttpResponse("HTTP/");
...@@ -134,10 +133,10 @@ namespace PcapDotNet.Packets.Test ...@@ -134,10 +133,10 @@ namespace PcapDotNet.Packets.Test
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, " OK"); TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, " OK");
TestHttpResponse("HTTP/1.1 200 OK\r\n" + TestHttpResponse("HTTP/1.1 200 OK\r\n" +
"Cache-Control: no-cache\r\n", "Cache-Control: no-cache\r\n",
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" + 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", "Transfer-Encoding: chunked,a, b , c\r\n\t,d , e;f=g;h=\"ijk lmn\"\r\n",
...@@ -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