Commit 40180aa4 authored by Brickner_cp's avatar Brickner_cp

HTTP

parent 55dce1dd
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
......@@ -48,8 +49,6 @@ 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");
......@@ -66,26 +65,26 @@ namespace PcapDotNet.Packets.Test
new HttpField("Cache-Control", "no-cache")));
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A:B\r\n" +
"B: C\r\n" +
"C: \r\n \r\n D\r\n" +
"D: E\r\n" +
"D: F\r\n" +
"E: G,H\r\n" +
"E: I,J\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(
new HttpField("A", "B"),
new HttpField("B", "C"),
new HttpField("C", "D"),
new HttpField("D", "E,F"),
new HttpField("E", "G,H,I,J")));
"A:B\r\n" +
"B: C\r\n" +
"C: \r\n \r\n D\r\n" +
"D: E\r\n" +
"D: F\r\n" +
"E: G,H\r\n" +
"E: I,J\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(
new HttpField("A", "B"),
new HttpField("B", "C"),
new HttpField("C", "D"),
new HttpField("D", "E,F"),
new HttpField("E", "G,H,I,J")));
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"\r\n" +
"A: B\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(), Datagram.Empty);
new HttpHeader(), string.Empty);
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" +
......@@ -93,7 +92,7 @@ namespace PcapDotNet.Packets.Test
"B: C\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(
new HttpField("A", "B")), Datagram.Empty);
new HttpField("A", "B")), string.Empty);
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" +
......@@ -114,10 +113,10 @@ namespace PcapDotNet.Packets.Test
new HttpField("B", "C")));
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(),
Datagram.Empty);
"\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(),
string.Empty);
TestHttpResponse("HTTP/");
......@@ -134,10 +133,10 @@ namespace PcapDotNet.Packets.Test
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, " OK");
TestHttpResponse("HTTP/1.1 200 OK\r\n" +
"Cache-Control: no-cache\r\n",
HttpVersion.Version11, 200, "OK",
new HttpHeader(
new HttpField("Cache-Control", "no-cache")));
"Cache-Control: no-cache\r\n",
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",
......@@ -145,10 +144,81 @@ namespace PcapDotNet.Packets.Test
new HttpHeader(
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);
// HTTP
......@@ -164,8 +234,10 @@ namespace PcapDotNet.Packets.Test
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);
// HTTP
......
......@@ -3,6 +3,7 @@
public class HttpContentLengthField : HttpField
{
public const string Name = "Content-Length";
public const string NameLower = "content-length";
public HttpContentLengthField(uint contentLength)
:base(Name, contentLength.ToString())
......
......@@ -9,13 +9,22 @@ namespace PcapDotNet.Packets.Http
public class HttpContentTypeField : HttpField, IEquatable<HttpContentTypeField>
{
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)
{
return other != null &&
MediaType == other.MediaType &&
MediaSubType == other.MediaSubType &&
Parameters == other.Parameters;
Parameters.Equals(other.Parameters);
}
public string MediaType { get; private set; }
......
......@@ -261,7 +261,7 @@ namespace PcapDotNet.Packets.Http
string boundary = contentTypeField.Parameters["boundary"];
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 lastBoundaryEnd = lastBoundaryOffset + lastBoundaryBuffer.Length;
return new Datagram(buffer, offset,
......
......@@ -11,12 +11,14 @@ namespace PcapDotNet.Packets.Http
{
public static HttpField CreateField(string fieldName, byte[] fieldValue)
{
switch (fieldName)
switch (fieldName.ToLowerInvariant())
{
case HttpTransferEncodingField.Name:
case HttpTransferEncodingField.NameLower:
return new HttpTransferEncodingField(fieldValue);
case HttpContentLengthField.Name:
case HttpContentLengthField.NameLower:
return new HttpContentLengthField(fieldValue);
case HttpContentTypeField.NameLower:
return new HttpContentTypeField(fieldValue);
default:
return new HttpField(fieldName, fieldValue.ToArray());
......@@ -74,7 +76,7 @@ namespace PcapDotNet.Packets.Http
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)
......
......@@ -2,11 +2,23 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PcapDotNet.Base;
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)
{
var nameEnumerator = parametersNames.GetEnumerator();
......@@ -43,6 +55,34 @@ namespace PcapDotNet.Packets.Http
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>();
}
}
\ No newline at end of file
......@@ -8,9 +8,11 @@ namespace PcapDotNet.Packets.Http
{
public class HttpHeader : IEnumerable<HttpField>, IEquatable<HttpHeader>
{
public static HttpHeader Empty { get { return _empty; } }
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)
......@@ -78,7 +80,7 @@ namespace PcapDotNet.Packets.Http
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)
{
string fieldName = field.Key;
......@@ -92,9 +94,10 @@ namespace PcapDotNet.Packets.Http
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
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 Regex _optionalParametersRegex = Any(Concat(Build(";"), Optional(_linearWhiteSpaceRegex), _parameterRegex));
private static readonly Encoding _encoding = Encoding.GetEncoding(28591);
}
......
......@@ -11,6 +11,7 @@ namespace PcapDotNet.Packets.Http
public class HttpTransferEncodingField : HttpField, IEquatable<HttpTransferEncodingField>
{
public const string Name = "Transfer-Encoding";
public const string NameLower = "transfer-encoding";
private const string RegexTransferCodingGroupName = "TransferCoding";
public HttpTransferEncodingField(IList<string> transferCodings)
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using PcapDotNet.Packets.Http;
namespace PcapDotNet.Packets.Transport
......@@ -258,9 +260,29 @@ namespace PcapDotNet.Packets.Transport
{
get
{
if (_http == null && Length >= HeaderMinimumLength && Length >= HeaderLength)
_http = HttpDatagram.CreateDatagram(Buffer, StartOffset + HeaderLength, Length - HeaderLength);
return _http;
return HttpCollection == null ? null : HttpCollection[0];
}
}
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
}
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