Commit 60f12a94 authored by Brickner_cp's avatar Brickner_cp

Warnings, Code Analysis and Documentation. 3 warnings left.

Ignore annoying Wireshark's frame.time field and use frame.time_epoch instead (in tests).
Code Coverage 93.24%
parent 389166c0
...@@ -68,11 +68,13 @@ namespace PcapDotNet.Core.Test ...@@ -68,11 +68,13 @@ namespace PcapDotNet.Core.Test
[TestMethod] [TestMethod]
public void ComparePacketsToWiresharkTest() public void ComparePacketsToWiresharkTest()
{ {
#pragma warning disable 162 // This code is unreachable on purpose
if (IsRetry) if (IsRetry)
{ {
ComparePacketsToWireshark(null); ComparePacketsToWireshark(null);
return; return;
} }
#pragma warning restore 162
Random random = new Random(); Random random = new Random();
for (int i = 0; i != 10; ++i) for (int i = 0; i != 10; ++i)
...@@ -147,7 +149,7 @@ namespace PcapDotNet.Core.Test ...@@ -147,7 +149,7 @@ namespace PcapDotNet.Core.Test
DateTime packetTimestamp = DateTime packetTimestamp =
random.NextDateTime(new DateTime(2010,1,1), new DateTime(2010,12,31)).ToUniversalTime().ToLocalTime(); random.NextDateTime(new DateTime(2010,1,1), new DateTime(2010,12,31)).ToUniversalTime().ToLocalTime();
//random.NextDateTime(PacketTimestamp.MinimumPacketTimestamp, PacketTimestamp.MaximumPacketTimestamp).ToUniversalTime().ToLocalTime(); //random.NextDateTime(PacketTimestamp.MinimumPacketTimestamp, PacketTimestamp.MaximumPacketTimestamp).ToUniversalTime().ToLocalTime();
EthernetLayer ethernetLayer = random.NextEthernetLayer(); EthernetLayer ethernetLayer = random.NextEthernetLayer();
IpV4Layer ipV4Layer = random.NextIpV4Layer(); IpV4Layer ipV4Layer = random.NextIpV4Layer();
PayloadLayer payloadLayer = random.NextPayloadLayer(random.Next(100)); PayloadLayer payloadLayer = random.NextPayloadLayer(random.Next(100));
...@@ -437,15 +439,21 @@ namespace PcapDotNet.Core.Test ...@@ -437,15 +439,21 @@ namespace PcapDotNet.Core.Test
{ {
switch (field.Name()) switch (field.Name())
{ {
case "frame.time": // case "frame.time":
string fieldShow = field.Show(); // string fieldShow = field.Show();
if (fieldShow == "Not representable") // if (fieldShow == "Not representable")
break; // break;
fieldShow = fieldShow.Substring(0, fieldShow.Length - 2); // fieldShow = fieldShow.Substring(0, fieldShow.Length - 2);
DateTime fieldTimestamp = fieldShow[4] == ' ' // DateTime fieldTimestamp = fieldShow[4] == ' '
? DateTime.ParseExact(fieldShow, "MMM d, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture) // ? DateTime.ParseExact(fieldShow, "MMM d, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture)
: DateTime.ParseExact(fieldShow, "MMM dd, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture); // : DateTime.ParseExact(fieldShow, "MMM dd, yyyy HH:mm:ss.fffffff", CultureInfo.InvariantCulture);
MoreAssert.IsInRange(fieldTimestamp.AddSeconds(-2), fieldTimestamp.AddSeconds(2), packet.Timestamp, "Timestamp"); // MoreAssert.IsInRange(fieldTimestamp.AddSeconds(-2), fieldTimestamp.AddSeconds(2), packet.Timestamp, "Timestamp");
// break;
case "frame.time_epoch":
double timeEpoch = double.Parse(field.Show());
DateTime fieldTimestamp = new DateTime(1970, 1, 1).AddSeconds(timeEpoch);
MoreAssert.IsInRange(fieldTimestamp.AddSeconds(-2), fieldTimestamp.AddSeconds(2), packet.Timestamp.ToUniversalTime(), "Timestamp");
break; break;
case "frame.len": case "frame.len":
...@@ -1203,7 +1211,7 @@ namespace PcapDotNet.Core.Test ...@@ -1203,7 +1211,7 @@ namespace PcapDotNet.Core.Test
{ {
// todo seems like a bug in tshark https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5235 // todo seems like a bug in tshark https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5235
break; break;
field.AssertShowDecimal(tcpDatagram.Length); // field.AssertShowDecimal(tcpDatagram.Length);
} }
else else
field.AssertShowDecimal(tcpDatagram.Payload.Length); field.AssertShowDecimal(tcpDatagram.Payload.Length);
......
...@@ -141,7 +141,7 @@ namespace PcapDotNet.Packets.Test ...@@ -141,7 +141,7 @@ namespace PcapDotNet.Packets.Test
"Cache-Control: no-cache\r\n", "Cache-Control: no-cache\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader( new HttpHeader(
new HttpField("Cache-Control", "no-cache"))); HttpField.CreateField("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" +
...@@ -153,11 +153,11 @@ namespace PcapDotNet.Packets.Test ...@@ -153,11 +153,11 @@ namespace PcapDotNet.Packets.Test
"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"), HttpField.CreateField("A", "B"),
new HttpField("B", "C"), HttpField.CreateField("B", "C"),
new HttpField("C", "D"), HttpField.CreateField("C", "D"),
new HttpField("D", "E,F"), HttpField.CreateField("D", "E,F"),
new HttpField("E", "G,H,I,J"))); HttpField.CreateField("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" +
...@@ -171,7 +171,7 @@ namespace PcapDotNet.Packets.Test ...@@ -171,7 +171,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")), string.Empty); HttpField.CreateField("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" +
...@@ -179,7 +179,7 @@ namespace PcapDotNet.Packets.Test ...@@ -179,7 +179,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"))); HttpField.CreateField("A", "B")));
TestHttpRequest("GET /url HTTP/1.1\r\n" + TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" + "A: B\r\n" +
...@@ -187,50 +187,50 @@ namespace PcapDotNet.Packets.Test ...@@ -187,50 +187,50 @@ 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"), HttpField.CreateField("A", "B"),
new HttpField("abc", ""), HttpField.CreateField("abc", ""),
new HttpField("B", "C"))); HttpField.CreateField("B", "C")));
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", "B",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(new HttpField("A", "B"))); new HttpHeader(HttpField.CreateField("A", "B")));
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\r\n", "B\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(new HttpField("A", "B"))); new HttpHeader(HttpField.CreateField("A", "B")));
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\r\n" + "B\r\n" +
"C:D\r\n", "C:D\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(new HttpField("A", "B"))); new HttpHeader(HttpField.CreateField("A", "B")));
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:", "B:",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(new HttpField("A", "B"))); new HttpHeader(HttpField.CreateField("A", "B")));
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:\r\n", "B:\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(new HttpField("A", "B"), new HttpHeader(HttpField.CreateField("A", "B"),
new HttpField("B", string.Empty))); HttpField.CreateField("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" +
"B:\r\n" + "B:\r\n" +
"C:D\r\n", "C:D\r\n",
"GET", "/url", HttpVersion.Version11, "GET", "/url", HttpVersion.Version11,
new HttpHeader(new HttpField("A", "B"), new HttpHeader(HttpField.CreateField("A", "B"),
new HttpField("B", string.Empty), HttpField.CreateField("B", string.Empty),
new HttpField("C", "D"))); HttpField.CreateField("C", "D")));
// Request Body // Request Body
TestHttpRequest("GET /url HTTP/1.1\r\n" + TestHttpRequest("GET /url HTTP/1.1\r\n" +
...@@ -268,7 +268,7 @@ namespace PcapDotNet.Packets.Test ...@@ -268,7 +268,7 @@ namespace PcapDotNet.Packets.Test
"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"))); HttpField.CreateField("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",
...@@ -341,8 +341,8 @@ namespace PcapDotNet.Packets.Test ...@@ -341,8 +341,8 @@ namespace PcapDotNet.Packets.Test
"--THIS_STRING_SEPARATES--", "--THIS_STRING_SEPARATES--",
HttpVersion.Version11, 206, "Partial content", HttpVersion.Version11, 206, "Partial content",
new HttpHeader( new HttpHeader(
new HttpField("Date", "Wed, 15 Nov 1995 06:25:24 GMT"), HttpField.CreateField("Date", "Wed, 15 Nov 1995 06:25:24 GMT"),
new HttpField("Last-modified", "Wed, 15 Nov 1995 04:58:08 GMT"), HttpField.CreateField("Last-modified", "Wed, 15 Nov 1995 04:58:08 GMT"),
new HttpContentTypeField("multipart", "byteranges", new HttpContentTypeField("multipart", "byteranges",
new HttpFieldParameters(new KeyValuePair<string, string>("boundary", "THIS_STRING_SEPARATES")))), new HttpFieldParameters(new KeyValuePair<string, string>("boundary", "THIS_STRING_SEPARATES")))),
"--THIS_STRING_SEPARATES\r\n" + "--THIS_STRING_SEPARATES\r\n" +
...@@ -383,12 +383,12 @@ namespace PcapDotNet.Packets.Test ...@@ -383,12 +383,12 @@ namespace PcapDotNet.Packets.Test
HttpRequestDatagram request = (HttpRequestDatagram)https[0]; HttpRequestDatagram request = (HttpRequestDatagram)https[0];
Assert.AreEqual(new HttpRequestMethod(HttpRequestKnownMethod.Get), request.Method); Assert.AreEqual(new HttpRequestMethod(HttpRequestKnownMethod.Get), request.Method);
Assert.AreEqual("/url1", request.Uri); Assert.AreEqual("/url1", request.Uri);
Assert.AreEqual(new HttpHeader(new HttpField("A", "B"), new HttpField("B", "C")), request.Header); Assert.AreEqual(new HttpHeader(HttpField.CreateField("A", "B"), HttpField.CreateField("B", "C")), request.Header);
request = (HttpRequestDatagram)https[1]; request = (HttpRequestDatagram)https[1];
Assert.AreEqual(new HttpRequestMethod(HttpRequestKnownMethod.Get), request.Method); Assert.AreEqual(new HttpRequestMethod(HttpRequestKnownMethod.Get), request.Method);
Assert.AreEqual("/url2", request.Uri); Assert.AreEqual("/url2", request.Uri);
Assert.AreEqual(new HttpHeader(new HttpField("C", "D"), new HttpField("D", "E")), request.Header); Assert.AreEqual(new HttpHeader(HttpField.CreateField("C", "D"), HttpField.CreateField("D", "E")), request.Header);
} }
[TestMethod] [TestMethod]
......
...@@ -1122,7 +1122,7 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -1122,7 +1122,7 @@ namespace PcapDotNet.Packets.TestUtils
{ {
fieldName = random.NextHttpToken(); fieldName = random.NextHttpToken();
} while (fieldNames.Contains(fieldName)); } while (fieldNames.Contains(fieldName));
return new HttpField(fieldName, random.NextHttpFieldValue()); return HttpField.CreateField(fieldName, random.NextHttpFieldValue());
case HttpTransferEncodingField.FieldNameUpper: case HttpTransferEncodingField.FieldNameUpper:
int numTransferCodings = random.Next(1, 10); int numTransferCodings = random.Next(1, 10);
......
...@@ -15,16 +15,27 @@ namespace PcapDotNet.Packets.Http ...@@ -15,16 +15,27 @@ namespace PcapDotNet.Packets.Http
public const string FieldName = "Content-Length"; public const string FieldName = "Content-Length";
/// <summary> /// <summary>
/// The field name in lowercase. /// The field name in uppercase.
/// </summary> /// </summary>
public const string FieldNameUpper = "CONTENT-LENGTH"; public const string FieldNameUpper = "CONTENT-LENGTH";
/// <summary>
/// Creates a Content Length Field according to a given content length.
/// </summary>
/// <param name="contentLength">
/// The size of the entity-body, in decimal number of OCTETs, sent to the recipient or,
/// in the case of the HEAD method, the size of the entity-body that would have been sent had the request been a GET.
/// </param>
public HttpContentLengthField(uint contentLength) public HttpContentLengthField(uint contentLength)
:base(FieldName, contentLength.ToString(CultureInfo.InvariantCulture)) :base(FieldName, contentLength.ToString(CultureInfo.InvariantCulture))
{ {
ContentLength = contentLength; ContentLength = contentLength;
} }
/// <summary>
/// The size of the entity-body, in decimal number of OCTETs, sent to the recipient or,
/// in the case of the HEAD method, the size of the entity-body that would have been sent had the request been a GET.
/// </summary>
public uint? ContentLength { get; private set;} public uint? ContentLength { get; private set;}
internal HttpContentLengthField(byte[] fieldValue) internal HttpContentLengthField(byte[] fieldValue)
......
...@@ -7,11 +7,38 @@ using PcapDotNet.Base; ...@@ -7,11 +7,38 @@ using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
/// <summary>
/// RFC 2616.
/// The Content-Type entity-header field indicates the media type of the entity-body sent to the recipient or, in the case of the HEAD method,
/// the media type that would have been sent had the request been a GET.
///
/// <pre>
/// Content-Type = "Content-Type" ":" media-type
/// </pre>
///
/// An example of the field is
/// <pre>
/// Content-Type: text/html; charset=ISO-8859-4
/// </pre>
/// </summary>
public class HttpContentTypeField : HttpField, IEquatable<HttpContentTypeField> public class HttpContentTypeField : HttpField, IEquatable<HttpContentTypeField>
{ {
/// <summary>
/// The field name.
/// </summary>
public const string FieldName = "Content-Type"; public const string FieldName = "Content-Type";
/// <summary>
/// The field name in uppercase.
/// </summary>
public const string FieldNameUpper = "CONTENT-TYPE"; public const string FieldNameUpper = "CONTENT-TYPE";
/// <summary>
/// Creates a Content Type Field according to the given media type, media subtype and parameters.
/// </summary>
/// <param name="mediaType">The main type of the content of this HTTP message.</param>
/// <param name="mediaSubtype">The subtype of the content of this HTTP message.</param>
/// <param name="parameters">Parameters on the specific type.</param>
public HttpContentTypeField(string mediaType, string mediaSubtype, HttpFieldParameters parameters) public HttpContentTypeField(string mediaType, string mediaSubtype, HttpFieldParameters parameters)
: base(FieldName, string.Format(CultureInfo.InvariantCulture, "{0}/{1}{2}", mediaType, mediaSubtype, parameters)) : base(FieldName, string.Format(CultureInfo.InvariantCulture, "{0}/{1}{2}", mediaType, mediaSubtype, parameters))
{ {
...@@ -20,6 +47,25 @@ namespace PcapDotNet.Packets.Http ...@@ -20,6 +47,25 @@ namespace PcapDotNet.Packets.Http
Parameters = parameters; Parameters = parameters;
} }
/// <summary>
/// The main type of the content of this HTTP message.
/// </summary>
public string MediaType { get; private set; }
/// <summary>
/// The subtype of the content of this HTTP message.
/// </summary>
public string MediaSubtype { get; private set; }
/// <summary>
/// Parameters on the specific type.
/// </summary>
public HttpFieldParameters Parameters { get; private set; }
/// <summary>
/// True iff the two fields are equal.
/// Two content type fields are equal if they have the same media type and subtype and same parameters.
/// </summary>
public bool Equals(HttpContentTypeField other) public bool Equals(HttpContentTypeField other)
{ {
return other != null && return other != null &&
...@@ -28,10 +74,10 @@ namespace PcapDotNet.Packets.Http ...@@ -28,10 +74,10 @@ namespace PcapDotNet.Packets.Http
Parameters.Equals(other.Parameters); Parameters.Equals(other.Parameters);
} }
public string MediaType { get; private set; } /// <summary>
public string MediaSubtype { get; private set; } /// True iff the two fields are equal.
public HttpFieldParameters Parameters { get; private set;} /// Two content type fields are equal if they have the same media type and subtype and same parameters.
/// </summary>
public override bool Equals(HttpField other) public override bool Equals(HttpField other)
{ {
return Equals(other as HttpContentTypeField); return Equals(other as HttpContentTypeField);
......
...@@ -207,13 +207,29 @@ namespace PcapDotNet.Packets.Http ...@@ -207,13 +207,29 @@ namespace PcapDotNet.Packets.Http
public Datagram Body { get; set; } public Datagram Body { get; set; }
} }
/// <summary>
/// True iff the message is a request and iff the message is not a response.
/// </summary>
public abstract bool IsRequest { get; } public abstract bool IsRequest { get; }
/// <summary>
/// True iff the message is a response and iff the message is not a request.
/// </summary>
public bool IsResponse { get { return !IsRequest; } } public bool IsResponse { get { return !IsRequest; } }
/// <summary>
/// The version of this HTTP message.
/// </summary>
public HttpVersion Version { get; private set;} public HttpVersion Version { get; private set;}
/// <summary>
/// The header of the HTTP message.
/// </summary>
public HttpHeader Header { get; private set;} public HttpHeader Header { get; private set;}
/// <summary>
/// Message Body.
/// </summary>
public Datagram Body { get; private set; } public Datagram Body { get; private set; }
internal static HttpDatagram CreateDatagram(byte[] buffer, int offset, int length) internal static HttpDatagram CreateDatagram(byte[] buffer, int offset, int length)
......
...@@ -11,8 +11,17 @@ using PcapDotNet.Base; ...@@ -11,8 +11,17 @@ using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
/// <summary>
/// An HTTP field with a name and value.
/// </summary>
public class HttpField : IEquatable<HttpField> public class HttpField : IEquatable<HttpField>
{ {
/// <summary>
/// Creates a field according to the field name and value.
/// </summary>
/// <param name="fieldName">The name of the field to create.</param>
/// <param name="fieldValue">The bytes value of the field to create.</param>
/// <returns>The constructed HTTP field.</returns>
public static HttpField CreateField(string fieldName, byte[] fieldValue) public static HttpField CreateField(string fieldName, byte[] fieldValue)
{ {
if (fieldName == null) if (fieldName == null)
...@@ -32,65 +41,45 @@ namespace PcapDotNet.Packets.Http ...@@ -32,65 +41,45 @@ namespace PcapDotNet.Packets.Http
} }
} }
public HttpField(string name, string value) /// <summary>
: this(name, value, _defaultEncoding) /// Creates a field according to the field name and encoded string value.
/// </summary>
/// <param name="fieldName">The name of the field to create.</param>
/// <param name="fieldValue">The value of the field to create encoded in the given encoding.</param>
/// <param name="fieldValueEncoding">The encoding that encodes the given field value.</param>
/// <returns>The constructed HTTP field.</returns>
public static HttpField CreateField(string fieldName, string fieldValue, Encoding fieldValueEncoding)
{ {
} if (fieldValueEncoding == null)
throw new ArgumentNullException("fieldValueEncoding");
public HttpField(string name, string value, Encoding encoding) return CreateField(fieldName, fieldValueEncoding.GetBytes(NormalizeValue(fieldValue)));
: this(name, encoding == null ? null : encoding.GetBytes(NormalizeValue(value)))
{
} }
private static string NormalizeValue(string value) /// <summary>
/// Creates a field according to the field name and encoded string value.
/// </summary>
/// <param name="fieldName">The name of the field to create.</param>
/// <param name="fieldValue">The value of the field to create encoded in ISO-8859-1 encoding.</param>
/// <returns>The constructed HTTP field.</returns>
public static HttpField CreateField(string fieldName, string fieldValue)
{ {
StringBuilder stringBuilder = new StringBuilder(value.Length); return CreateField(fieldName, fieldValue, _defaultEncoding);
int offset = 0;
while (offset != value.Length)
{
if (value[offset] == '"')
{
int start = offset;
++offset;
while (offset != value.Length && value[offset] != '"')
{
if (value[offset] == '\\' && offset != value.Length - 1)
++offset;
++offset;
}
if (value[offset] == '"')
++offset;
stringBuilder.Append(value.Substring(start, offset - start));
}
else if (value[offset] == '\t' || value[offset] == ' ' || value[offset] == '\r' || value[offset] == '\n')
{
stringBuilder.Append(' ');
++offset;
while (offset != value.Length && (value[offset] == '\t' || value[offset] == ' ' || value[offset] == '\r' || value[offset] == '\n'))
++offset;
}
else
{
stringBuilder.Append(value[offset]);
++offset;
}
}
return stringBuilder.ToString();
}
public HttpField(string name, IList<byte> value)
:this(name, value.AsReadOnly())
{
}
public HttpField(string name, ReadOnlyCollection<byte> value)
{
Name = name;
Value = value;
} }
/// <summary>
/// The name of the field.
/// </summary>
public string Name { get; private set; } public string Name { get; private set; }
/// <summary>
/// The Value of the field.
/// </summary>
public ReadOnlyCollection<byte> Value { get; private set; } public ReadOnlyCollection<byte> Value { get; private set; }
/// <summary>
/// The Value of the field as a string using ISO-8859-1 encoding.
/// </summary>
public string ValueString public string ValueString
{ {
get get
...@@ -99,6 +88,9 @@ namespace PcapDotNet.Packets.Http ...@@ -99,6 +88,9 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// The number of bytes the field will take in the HTTP protocol.
/// </summary>
public int Length public int Length
{ {
get get
...@@ -107,26 +99,62 @@ namespace PcapDotNet.Packets.Http ...@@ -107,26 +99,62 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// True iff the two HTTP fields are of equal value.
/// Two fields are equal iff they have the same name (case insensitive) and the same bytes value.
/// </summary>
public virtual bool Equals(HttpField other) public virtual bool Equals(HttpField other)
{ {
return other != null && Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) && Value.SequenceEqual(other.Value); return other != null && Name.Equals(other.Name, StringComparison.OrdinalIgnoreCase) && Value.SequenceEqual(other.Value);
} }
/// <summary>
/// True iff the two HTTP fields are of equal value.
/// Two fields are equal iff they have the same name (case insensitive) and the same bytes value.
/// </summary>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return Equals(obj as HttpField); return Equals(obj as HttpField);
} }
/// <summary>
/// Returns a hash code of this field according to the name and value.
/// </summary>
public override int GetHashCode() public override int GetHashCode()
{ {
return Name.ToUpperInvariant().GetHashCode() ^ Value.BytesSequenceGetHashCode(); return Name.ToUpperInvariant().GetHashCode() ^ Value.BytesSequenceGetHashCode();
} }
/// <summary>
/// A string representing the field similar to how it would like in the HTTP protocol.
/// </summary>
/// <returns></returns>
public override string ToString() public override string ToString()
{ {
return string.Format(CultureInfo.InvariantCulture, "{0}: {1}", Name, ValueString); return string.Format(CultureInfo.InvariantCulture, "{0}: {1}", Name, ValueString);
} }
internal HttpField(string name, string value)
: this(name, value, _defaultEncoding)
{
}
internal HttpField(string name, string value, Encoding encoding)
: this(name, encoding == null ? null : encoding.GetBytes(NormalizeValue(value)))
{
}
internal HttpField(string name, IList<byte> value)
: this(name, value.AsReadOnly())
{
}
internal HttpField(string name, ReadOnlyCollection<byte> value)
{
Name = name;
Value = value;
}
internal void Write(byte[] buffer, ref int offset) internal void Write(byte[] buffer, ref int offset)
{ {
buffer.Write(ref offset, Name, Encoding.ASCII); buffer.Write(ref offset, Name, Encoding.ASCII);
...@@ -136,6 +164,42 @@ namespace PcapDotNet.Packets.Http ...@@ -136,6 +164,42 @@ namespace PcapDotNet.Packets.Http
buffer.WriteCarriageReturnLinefeed(ref offset); buffer.WriteCarriageReturnLinefeed(ref offset);
} }
private static string NormalizeValue(string value)
{
StringBuilder stringBuilder = new StringBuilder(value.Length);
int offset = 0;
while (offset != value.Length)
{
if (value[offset] == '"')
{
int start = offset;
++offset;
while (offset != value.Length && value[offset] != '"')
{
if (value[offset] == '\\' && offset != value.Length - 1)
++offset;
++offset;
}
if (value[offset] == '"')
++offset;
stringBuilder.Append(value.Substring(start, offset - start));
}
else if (value[offset] == '\t' || value[offset] == ' ' || value[offset] == '\r' || value[offset] == '\n')
{
stringBuilder.Append(' ');
++offset;
while (offset != value.Length && (value[offset] == '\t' || value[offset] == ' ' || value[offset] == '\r' || value[offset] == '\n'))
++offset;
}
else
{
stringBuilder.Append(value[offset]);
++offset;
}
}
return stringBuilder.ToString();
}
private static readonly Encoding _defaultEncoding = Encoding.GetEncoding(28591); private static readonly Encoding _defaultEncoding = Encoding.GetEncoding(28591);
} }
} }
\ No newline at end of file
...@@ -8,20 +8,41 @@ using PcapDotNet.Base; ...@@ -8,20 +8,41 @@ using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
/// <summary>
/// Represents a set of HTTP field parameters.
/// Used for some of HTTP fields.
/// All parameters must have different names.
/// </summary>
public class HttpFieldParameters : IEnumerable<KeyValuePair<string, string>>, IEquatable<HttpFieldParameters> public class HttpFieldParameters : IEnumerable<KeyValuePair<string, string>>, IEquatable<HttpFieldParameters>
{ {
/// <summary>
/// Creates the parameters from an array of parameters. Keys are the parameters names and values are the parameters values.
/// </summary>
public HttpFieldParameters(params KeyValuePair<string, string>[] parameters) public HttpFieldParameters(params KeyValuePair<string, string>[] parameters)
:this((IEnumerable<KeyValuePair<string, string>>)parameters) :this((IEnumerable<KeyValuePair<string, string>>)parameters)
{ {
} }
/// <summary>
/// Creates the parameters from an enumerable of parameters. Keys are the parameters names and values are the parameters values.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public HttpFieldParameters(IEnumerable<KeyValuePair<string, string>> parameters) public HttpFieldParameters(IEnumerable<KeyValuePair<string, string>> parameters)
{ {
_parameters = parameters.ToDictionary(pair => pair.Key, pair => pair.Value); _parameters = parameters.ToDictionary(pair => pair.Key, pair => pair.Value);
} }
/// <summary>
/// Number of parameters.
/// </summary>
public int Count { get { return _parameters.Count; } } public int Count { get { return _parameters.Count; } }
/// <summary>
/// Returns the value of the given parameter name.
/// </summary>
/// <param name="name">The name of the parameter.</param>
/// <returns>The value of the parameter.</returns>
public string this[string name] public string this[string name]
{ {
get get
...@@ -33,6 +54,9 @@ namespace PcapDotNet.Packets.Http ...@@ -33,6 +54,9 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// Enumerates over the parameters.
/// </summary>
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{ {
return _parameters.GetEnumerator(); return _parameters.GetEnumerator();
...@@ -43,31 +67,42 @@ namespace PcapDotNet.Packets.Http ...@@ -43,31 +67,42 @@ namespace PcapDotNet.Packets.Http
return GetEnumerator(); return GetEnumerator();
} }
/// <summary>
/// Two HTTP field parameters are equal if all of their parameters are equal.
/// </summary>
public bool Equals(HttpFieldParameters other) public bool Equals(HttpFieldParameters other)
{ {
return other != null && _parameters.DictionaryEquals(other._parameters); return other != null && _parameters.DictionaryEquals(other._parameters);
} }
/// <summary>
/// Two HTTP field parameters are equal if all of their parameters are equal.
/// </summary>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return Equals(obj as HttpFieldParameters); return Equals(obj as HttpFieldParameters);
} }
/// <summary>
/// Xor of all of the hash codes of the parameters names and values.
/// </summary>
/// <returns></returns>
public override int GetHashCode() public override int GetHashCode()
{ {
return return
_parameters.Select(pair => new KeyValuePair<string, string>(pair.Key.ToUpperInvariant(), pair.Value)) _parameters.Select(pair => new KeyValuePair<string, string>(pair.Key.ToUpperInvariant(), pair.Value))
.OrderBy(pair => pair.Key)
.Xor(pair => pair.Key.GetHashCode() ^ pair.Value.GetHashCode()); .Xor(pair => pair.Key.GetHashCode() ^ pair.Value.GetHashCode());
} }
/// <summary>
/// Returns a string of parameters beginning and separated by semicolon and equal sign between keys and values.
/// </summary>
public override string ToString() public override string ToString()
{ {
if (!this.Any()) if (!this.Any())
return string.Empty; return string.Empty;
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
// stringBuilder.Append(" ");
foreach (var parameter in this) foreach (var parameter in this)
{ {
stringBuilder.Append(";"); stringBuilder.Append(";");
......
...@@ -6,20 +6,37 @@ using PcapDotNet.Base; ...@@ -6,20 +6,37 @@ using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
/// <summary>
/// Represents HTTP header.
/// The header is a container for HTTP fields.
/// Insensitive to the case of HTTP field names.
/// </summary>
public class HttpHeader : IEnumerable<HttpField>, IEquatable<HttpHeader> public class HttpHeader : IEnumerable<HttpField>, IEquatable<HttpHeader>
{ {
/// <summary>
/// An empty HTTP header.
/// </summary>
public static HttpHeader Empty { get { return _empty; } } public static HttpHeader Empty { get { return _empty; } }
/// <summary>
/// Creates a header from an enumerable of fields.
/// </summary>
public HttpHeader(IEnumerable<HttpField> fields) public HttpHeader(IEnumerable<HttpField> fields)
{ {
_fields = fields.ToDictionary(field => field.Name, field => field, StringComparer.OrdinalIgnoreCase); _fields = fields.ToDictionary(field => field.Name, field => field, StringComparer.OrdinalIgnoreCase);
} }
/// <summary>
/// Creates a header from an array of fields.
/// </summary>
public HttpHeader(params HttpField[] fields) public HttpHeader(params HttpField[] fields)
:this((IEnumerable<HttpField>)fields) :this((IEnumerable<HttpField>)fields)
{ {
} }
/// <summary>
/// The number of bytes the header takes.
/// </summary>
public int BytesLength public int BytesLength
{ {
get get
...@@ -28,11 +45,20 @@ namespace PcapDotNet.Packets.Http ...@@ -28,11 +45,20 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// Returns the field with the given field name or null if it doesn't exist.
/// Case insensitive.
/// </summary>
/// <param name="fieldName">The case insensitive name of the field.</param>
/// <returns>The field with the matching case insensitive name or null if it doesn't exist.</returns>
public HttpField this[string fieldName] public HttpField this[string fieldName]
{ {
get { return GetField<HttpField>(fieldName); } get { return GetField<HttpField>(fieldName); }
} }
/// <summary>
/// The HTTP Transfer Encoding field if it exists (null otherwise).
/// </summary>
public HttpTransferEncodingField TransferEncoding public HttpTransferEncodingField TransferEncoding
{ {
get get
...@@ -41,6 +67,9 @@ namespace PcapDotNet.Packets.Http ...@@ -41,6 +67,9 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// The HTTP Content Length field if it exists (null otherwise).
/// </summary>
public HttpContentLengthField ContentLength public HttpContentLengthField ContentLength
{ {
get get
...@@ -49,6 +78,9 @@ namespace PcapDotNet.Packets.Http ...@@ -49,6 +78,9 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// The HTTP Content Type field if it exists (null otherwise).
/// </summary>
public HttpContentTypeField ContentType public HttpContentTypeField ContentType
{ {
get get
...@@ -57,27 +89,42 @@ namespace PcapDotNet.Packets.Http ...@@ -57,27 +89,42 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// Two HTTP headers are equal if they have the same fields with the same values.
/// </summary>
public bool Equals(HttpHeader other) public bool Equals(HttpHeader other)
{ {
return other != null && return other != null &&
_fields.DictionaryEquals(other._fields); _fields.DictionaryEquals(other._fields);
} }
/// <summary>
/// Two HTTP headers are equal if they have the same fields with the same values.
/// </summary>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return Equals(obj as HttpHeader); return Equals(obj as HttpHeader);
} }
/// <summary>
/// Xor of the hash codes of the fields.
/// </summary>
public override int GetHashCode() public override int GetHashCode()
{ {
return _fields.Select(pair => pair.Value).SequenceGetHashCode(); return _fields.Select(pair => pair.Value).SequenceGetHashCode();
} }
/// <summary>
/// Returns a string of all the fields with endline separators.
/// </summary>
public override string ToString() public override string ToString()
{ {
return this.SequenceToString("\r\n"); return this.SequenceToString("\r\n");
} }
/// <summary>
/// Enumerates over the HTTP fields.
/// </summary>
public IEnumerator<HttpField> GetEnumerator() public IEnumerator<HttpField> GetEnumerator()
{ {
return _fields.Values.GetEnumerator(); return _fields.Values.GetEnumerator();
...@@ -88,6 +135,29 @@ namespace PcapDotNet.Packets.Http ...@@ -88,6 +135,29 @@ namespace PcapDotNet.Packets.Http
return GetEnumerator(); return GetEnumerator();
} }
/// <summary>
/// Writes the HTTP header to the given buffer in the given offset.
/// </summary>
/// <param name="buffer">The buffer to write the header to.</param>
/// <param name="offset">The offset in the given buffer to start writing the header.</param>
public void Write(byte[] buffer, int offset)
{
Write(buffer, ref offset);
}
/// <summary>
/// Writes the HTTP header to the given buffer in the given offset.
/// Increments the offset by the number of bytes written.
/// </summary>
/// <param name="buffer">The buffer to write the header to.</param>
/// <param name="offset">The offset in the given buffer to start writing the header. Incremented by the number of bytes written.</param>
public void Write(byte[] buffer, ref int offset)
{
foreach (HttpField field in this)
field.Write(buffer, ref offset);
buffer.WriteCarriageReturnLinefeed(ref offset);
}
internal HttpHeader(IEnumerable<KeyValuePair<string, IEnumerable<byte>>> fields) internal HttpHeader(IEnumerable<KeyValuePair<string, IEnumerable<byte>>> fields)
{ {
var mergedFields = new Dictionary<string, IEnumerable<byte>>(StringComparer.OrdinalIgnoreCase); var mergedFields = new Dictionary<string, IEnumerable<byte>>(StringComparer.OrdinalIgnoreCase);
...@@ -107,18 +177,6 @@ namespace PcapDotNet.Packets.Http ...@@ -107,18 +177,6 @@ namespace PcapDotNet.Packets.Http
_fields = mergedFields.ToDictionary(field => field.Key, field => HttpField.CreateField(field.Key, field.Value.ToArray()), StringComparer.OrdinalIgnoreCase); _fields = mergedFields.ToDictionary(field => field.Key, field => HttpField.CreateField(field.Key, field.Value.ToArray()), StringComparer.OrdinalIgnoreCase);
} }
public void Write(byte[] buffer, int offset)
{
Write(buffer, ref offset);
}
public void Write(byte[] buffer, ref int offset)
{
foreach (HttpField field in this)
field.Write(buffer, ref offset);
buffer.WriteCarriageReturnLinefeed(ref offset);
}
private T GetField<T>(string fieldName) where T : HttpField private T GetField<T>(string fieldName) where T : HttpField
{ {
HttpField field; HttpField field;
......
...@@ -8,12 +8,34 @@ namespace PcapDotNet.Packets.Http ...@@ -8,12 +8,34 @@ namespace PcapDotNet.Packets.Http
/// </summary> /// </summary>
public abstract class HttpLayer : SimpleLayer, IEquatable<HttpLayer> public abstract class HttpLayer : SimpleLayer, IEquatable<HttpLayer>
{ {
/// <summary>
/// True iff the message is a request and iff the message is not a response.
/// </summary>
public abstract bool IsRequest { get; } public abstract bool IsRequest { get; }
/// <summary>
/// True iff the message is a response and iff the message is not a request.
/// </summary>
public bool IsResponse { get { return !IsRequest; } } public bool IsResponse { get { return !IsRequest; } }
/// <summary>
/// The version of this HTTP message.
/// </summary>
public HttpVersion Version { get; set; } public HttpVersion Version { get; set; }
/// <summary>
/// The header of the HTTP message.
/// </summary>
public HttpHeader Header { get; set; } public HttpHeader Header { get; set; }
/// <summary>
/// Message Body.
/// </summary>
public Datagram Body { get; set; } public Datagram Body { get; set; }
/// <summary>
/// The number of bytes this layer will take.
/// </summary>
public override int Length public override int Length
{ {
get get
...@@ -24,11 +46,19 @@ namespace PcapDotNet.Packets.Http ...@@ -24,11 +46,19 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// Two HTTP layers are equal iff they have the same version, header and body.
/// Extended by specific HTTP layers types for more checks.
/// </summary>
public override bool Equals(Layer other) public override bool Equals(Layer other)
{ {
return Equals(other as HttpLayer); return Equals(other as HttpLayer);
} }
/// <summary>
/// Two HTTP layers are equal iff they have the same version, header and body.
/// Extended by specific HTTP layers types for more checks.
/// </summary>
public virtual bool Equals(HttpLayer other) public virtual bool Equals(HttpLayer other)
{ {
return other != null && return other != null &&
...@@ -37,6 +67,11 @@ namespace PcapDotNet.Packets.Http ...@@ -37,6 +67,11 @@ namespace PcapDotNet.Packets.Http
(ReferenceEquals(Body, other.Body) || Body.Equals(other.Body)); (ReferenceEquals(Body, other.Body) || Body.Equals(other.Body));
} }
/// <summary>
/// Writes the HTTP layer to the buffer.
/// </summary>
/// <param name="buffer">The buffer to write the layer to.</param>
/// <param name="offset">The offset in the buffer to start writing the layer at.</param>
protected override void Write(byte[] buffer, int offset) protected override void Write(byte[] buffer, int offset)
{ {
WriteFirstLine(buffer, ref offset); WriteFirstLine(buffer, ref offset);
...@@ -46,7 +81,7 @@ namespace PcapDotNet.Packets.Http ...@@ -46,7 +81,7 @@ namespace PcapDotNet.Packets.Http
buffer.Write(offset, Body); buffer.Write(offset, Body);
} }
protected abstract int FirstLineLength { get; } internal abstract int FirstLineLength { get; }
protected abstract void WriteFirstLine(byte[] buffer, ref int offset); internal abstract void WriteFirstLine(byte[] buffer, ref int offset);
} }
} }
\ No newline at end of file
...@@ -14,14 +14,27 @@ namespace PcapDotNet.Packets.Http ...@@ -14,14 +14,27 @@ namespace PcapDotNet.Packets.Http
public string Uri { get; set; } public string Uri { get; set; }
} }
/// <summary>
/// True since the message is a request.
/// </summary>
public override bool IsRequest public override bool IsRequest
{ {
get { return true; } get { return true; }
} }
/// <summary>
/// The HTTP Request Method.
/// </summary>
public HttpRequestMethod Method { get; private set; } public HttpRequestMethod Method { get; private set; }
/// <summary>
/// The HTTP Request URI.
/// </summary>
public string Uri { get; private set; } public string Uri { get; private set; }
/// <summary>
/// Creates a Layer that represents the datagram to be used with PacketBuilder.
/// </summary>
public override ILayer ExtractLayer() public override ILayer ExtractLayer()
{ {
return new HttpRequestLayer return new HttpRequestLayer
......
...@@ -9,17 +9,32 @@ namespace PcapDotNet.Packets.Http ...@@ -9,17 +9,32 @@ namespace PcapDotNet.Packets.Http
/// </summary> /// </summary>
public class HttpRequestLayer : HttpLayer, IEquatable<HttpRequestLayer> public class HttpRequestLayer : HttpLayer, IEquatable<HttpRequestLayer>
{ {
/// <summary>
/// True since the message is a request.
/// </summary>
public override bool IsRequest { get { return true; } } public override bool IsRequest { get { return true; } }
/// <summary>
/// The HTTP Request Method.
/// </summary>
public HttpRequestMethod Method { get; set; } public HttpRequestMethod Method { get; set; }
/// <summary>
/// The HTTP Request URI.
/// </summary>
public string Uri { get; set; } public string Uri { get; set; }
/// <summary>
/// Two HTTP Request layers are equal iff they have the same version, header, body, method and uri.
/// </summary>
public override bool Equals(HttpLayer other) public override bool Equals(HttpLayer other)
{ {
return Equals(other as HttpRequestLayer); return Equals(other as HttpRequestLayer);
} }
/// <summary>
/// Two HTTP Request layers are equal iff they have the same version, header, body, method and uri.
/// </summary>
public bool Equals(HttpRequestLayer other) public bool Equals(HttpRequestLayer other)
{ {
return base.Equals(other) && return base.Equals(other) &&
...@@ -27,7 +42,7 @@ namespace PcapDotNet.Packets.Http ...@@ -27,7 +42,7 @@ namespace PcapDotNet.Packets.Http
(ReferenceEquals(Uri, other.Uri) || Uri.Equals(other.Uri)); (ReferenceEquals(Uri, other.Uri) || Uri.Equals(other.Uri));
} }
protected override int FirstLineLength internal override int FirstLineLength
{ {
get get
{ {
...@@ -46,7 +61,7 @@ namespace PcapDotNet.Packets.Http ...@@ -46,7 +61,7 @@ namespace PcapDotNet.Packets.Http
} }
} }
protected override void WriteFirstLine(byte[] buffer, ref int offset) internal override void WriteFirstLine(byte[] buffer, ref int offset)
{ {
if (Method == null) if (Method == null)
return; return;
......
...@@ -5,13 +5,23 @@ using PcapDotNet.Base; ...@@ -5,13 +5,23 @@ using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
/// <summary>
/// HTTP request method.
/// Example: GET
/// </summary>
public class HttpRequestMethod : IEquatable<HttpRequestMethod> public class HttpRequestMethod : IEquatable<HttpRequestMethod>
{ {
/// <summary>
/// Creates a method from a method string.
/// </summary>
public HttpRequestMethod(string method) public HttpRequestMethod(string method)
{ {
Method = method; Method = method;
} }
/// <summary>
/// Creates a method from a known method.
/// </summary>
public HttpRequestMethod(HttpRequestKnownMethod method) public HttpRequestMethod(HttpRequestKnownMethod method)
{ {
Method = method.ToString().ToUpperInvariant(); Method = method.ToString().ToUpperInvariant();
...@@ -19,8 +29,15 @@ namespace PcapDotNet.Packets.Http ...@@ -19,8 +29,15 @@ namespace PcapDotNet.Packets.Http
throw new ArgumentException("Invalid known request method given: " + method, "method"); throw new ArgumentException("Invalid known request method given: " + method, "method");
} }
/// <summary>
/// The method string.
/// </summary>
public string Method { get; private set; } public string Method { get; private set; }
/// <summary>
/// Returns the known method that matches the method string.
/// Returns HttpRequestKnownMethod.Unknown if no matching known method could be found.
/// </summary>
public HttpRequestKnownMethod KnownMethod public HttpRequestKnownMethod KnownMethod
{ {
get get
...@@ -33,6 +50,9 @@ namespace PcapDotNet.Packets.Http ...@@ -33,6 +50,9 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// The number of bytes this method takes.
/// </summary>
public int Length public int Length
{ {
get { return Method.Length; } get { return Method.Length; }
...@@ -54,16 +74,25 @@ namespace PcapDotNet.Packets.Http ...@@ -54,16 +74,25 @@ namespace PcapDotNet.Packets.Http
return result; return result;
} }
/// <summary>
/// Two methods are equal iff they have the same method string.
/// </summary>
public bool Equals(HttpRequestMethod other) public bool Equals(HttpRequestMethod other)
{ {
return other != null && Method.Equals(other.Method); return other != null && Method.Equals(other.Method);
} }
/// <summary>
/// Two methods are equal iff they have the same method string.
/// </summary>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return Equals(obj as HttpRequestMethod); return Equals(obj as HttpRequestMethod);
} }
/// <summary>
/// The hash code of the method string.
/// </summary>
public override int GetHashCode() public override int GetHashCode()
{ {
return Method.GetHashCode(); return Method.GetHashCode();
......
...@@ -15,15 +15,29 @@ namespace PcapDotNet.Packets.Http ...@@ -15,15 +15,29 @@ namespace PcapDotNet.Packets.Http
public uint? StatusCode{ get; set;} public uint? StatusCode{ get; set;}
} }
/// <summary>
/// False since this is message is a response.
/// </summary>
public override bool IsRequest public override bool IsRequest
{ {
get { return false; } get { return false; }
} }
/// <summary>
/// The status code of the response.
/// null if no status code exists.
/// </summary>
public uint? StatusCode{get; private set;} public uint? StatusCode{get; private set;}
/// <summary>
/// The data of the reason phrase.
/// Example: OK
/// </summary>
public Datagram ReasonPhrase { get; private set;} public Datagram ReasonPhrase { get; private set;}
/// <summary>
/// Creates a Layer that represents the datagram to be used with PacketBuilder.
/// </summary>
public override ILayer ExtractLayer() public override ILayer ExtractLayer()
{ {
return new HttpResponseLayer return new HttpResponseLayer
......
...@@ -9,17 +9,34 @@ namespace PcapDotNet.Packets.Http ...@@ -9,17 +9,34 @@ namespace PcapDotNet.Packets.Http
/// </summary> /// </summary>
public class HttpResponseLayer : HttpLayer, IEquatable<HttpResponseLayer> public class HttpResponseLayer : HttpLayer, IEquatable<HttpResponseLayer>
{ {
/// <summary>
/// false since this is a response.
/// </summary>
public override bool IsRequest { get { return false; } } public override bool IsRequest { get { return false; } }
/// <summary>
/// The status code of the response.
/// null if no status code exists.
/// </summary>
public uint? StatusCode { get; set; } public uint? StatusCode { get; set; }
/// <summary>
/// The data of the reason phrase.
/// Example: OK
/// </summary>
public Datagram ReasonPhrase { get; set; } public Datagram ReasonPhrase { get; set; }
/// <summary>
/// Two HTTP response layers are equal iff they have the same version, header, body, status code and reason phrase.
/// </summary>
public override bool Equals(HttpLayer other) public override bool Equals(HttpLayer other)
{ {
return Equals(other as HttpResponseLayer); return Equals(other as HttpResponseLayer);
} }
/// <summary>
/// Two HTTP response layers are equal iff they have the same version, header, body, status code and reason phrase.
/// </summary>
public bool Equals(HttpResponseLayer other) public bool Equals(HttpResponseLayer other)
{ {
return base.Equals(other) && return base.Equals(other) &&
...@@ -27,7 +44,7 @@ namespace PcapDotNet.Packets.Http ...@@ -27,7 +44,7 @@ namespace PcapDotNet.Packets.Http
(ReferenceEquals(ReasonPhrase, other.ReasonPhrase) || ReasonPhrase.Equals(other.ReasonPhrase)); (ReferenceEquals(ReasonPhrase, other.ReasonPhrase) || ReasonPhrase.Equals(other.ReasonPhrase));
} }
protected override int FirstLineLength internal override int FirstLineLength
{ {
get get
{ {
...@@ -48,7 +65,7 @@ namespace PcapDotNet.Packets.Http ...@@ -48,7 +65,7 @@ namespace PcapDotNet.Packets.Http
} }
} }
protected override void WriteFirstLine(byte[] buffer, ref int offset) internal override void WriteFirstLine(byte[] buffer, ref int offset)
{ {
if (Version == null) if (Version == null)
return; return;
......
...@@ -8,23 +8,87 @@ using PcapDotNet.Base; ...@@ -8,23 +8,87 @@ using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
/// <summary>
/// RFC 2616.
/// The Transfer-Encoding general-header field indicates what (if any) type of transformation has been applied to the message body
/// in order to safely transfer it between the sender and the recipient.
/// This differs from the content-coding in that the transfer-coding is a property of the message, not of the entity.
///
/// <pre>
/// Transfer-Encoding = "Transfer-Encoding" ":" 1#transfer-coding
/// </pre>
///
/// Example:
///
/// <pre>
/// Transfer-Encoding: chunked
/// </pre>
///
/// If multiple encodings have been applied to an entity, the transfer-codings MUST be listed in the order in which they were applied.
/// Additional information about the encoding parameters MAY be provided by other entity-header fields not defined by this specification.
/// </summary>
public class HttpTransferEncodingField : HttpField, IEquatable<HttpTransferEncodingField> public class HttpTransferEncodingField : HttpField, IEquatable<HttpTransferEncodingField>
{ {
/// <summary>
/// The field name.
/// </summary>
public const string FieldName = "Transfer-Encoding"; public const string FieldName = "Transfer-Encoding";
/// <summary>
/// The field name in uppercase.
/// </summary>
public const string FieldNameUpper = "TRANSFER-ENCODING"; public const string FieldNameUpper = "TRANSFER-ENCODING";
private const string RegexTransferCodingGroupName = "TransferCoding"; private const string RegexTransferCodingGroupName = "TransferCoding";
/// <summary>
/// Creates an HTTP transfer encoding field from a set of transfer codings.
/// </summary>
public HttpTransferEncodingField(IList<string> transferCodings) public HttpTransferEncodingField(IList<string> transferCodings)
:base(FieldName, transferCodings.SequenceToString(",")) :base(FieldName, transferCodings.SequenceToString(","))
{ {
SetTransferCodings(transferCodings); SetTransferCodings(transferCodings);
} }
/// <summary>
/// Creates an HTTP transfer encoding field from a set of transfer codings.
/// </summary>
public HttpTransferEncodingField(params string[] transferCodings) public HttpTransferEncodingField(params string[] transferCodings)
:this((IList<string>)transferCodings) :this((IList<string>)transferCodings)
{ {
} }
/// <summary>
/// Transfer-coding values are used to indicate an encoding transformation that has been, can be,
/// or may need to be applied to an entity-body in order to ensure "safe transport" through the network.
/// This differs from a content coding in that the transfer-coding is a property of the message, not of the original entity.
///
/// <pre>
/// transfer-coding = "chunked" | transfer-extension
/// transfer-extension = token *( ";" parameter )
/// </pre>
///
/// Parameters are in the form of attribute/value pairs.
///
/// <pre>
/// parameter = attribute "=" value
/// attribute = token
/// value = token | quoted-string
/// </pre>
///
/// All transfer-coding values are case-insensitive.
/// Whenever a transfer-coding is applied to a message-body, the set of transfer-codings MUST include "chunked",
/// unless the message is terminated by closing the connection.
/// When the "chunked" transfer-coding is used, it MUST be the last transfer-coding applied to the message-body.
/// The "chunked" transfer-coding MUST NOT be applied more than once to a message-body.
/// These rules allow the recipient to determine the transfer-length of the message.
/// </summary>
public ReadOnlyCollection<string> TransferCodings { get { return _transferCodings; } }
/// <summary>
/// True iff the two HTTP transfer encoding fields are of equal value.
/// Two HTTP transfer encoding fields are equal iff they have the same transfer codings.
/// </summary>
public bool Equals(HttpTransferEncodingField other) public bool Equals(HttpTransferEncodingField other)
{ {
return other != null && return other != null &&
...@@ -32,17 +96,10 @@ namespace PcapDotNet.Packets.Http ...@@ -32,17 +96,10 @@ namespace PcapDotNet.Packets.Http
TransferCodings != null && other.TransferCodings != null && TransferCodings.SequenceEqual(other.TransferCodings)); TransferCodings != null && other.TransferCodings != null && TransferCodings.SequenceEqual(other.TransferCodings));
} }
public ReadOnlyCollection<string> TransferCodings{get{return _transferCodings;}} /// <summary>
/// True iff the two HTTP transfer encoding fields are of equal value.
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] /// Two HTTP transfer encoding fields are equal iff they have the same transfer codings.
private void SetTransferCodings(IList<string> transferCodings) /// </summary>
{
if (transferCodings.Any(coding => coding.Any(c => c.IsUppercaseAlpha())))
_transferCodings = transferCodings.Select(coding => coding.ToLowerInvariant()).ToArray().AsReadOnly();
else
_transferCodings = transferCodings.AsReadOnly();
}
public override bool Equals(HttpField other) public override bool Equals(HttpField other)
{ {
return Equals(other as HttpTransferEncodingField); return Equals(other as HttpTransferEncodingField);
...@@ -59,6 +116,15 @@ namespace PcapDotNet.Packets.Http ...@@ -59,6 +116,15 @@ namespace PcapDotNet.Packets.Http
SetTransferCodings(match.GroupCapturesValues(RegexTransferCodingGroupName).ToArray()); SetTransferCodings(match.GroupCapturesValues(RegexTransferCodingGroupName).ToArray());
} }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
private void SetTransferCodings(IList<string> transferCodings)
{
if (transferCodings.Any(coding => coding.Any(c => c.IsUppercaseAlpha())))
_transferCodings = transferCodings.Select(coding => coding.ToLowerInvariant()).ToArray().AsReadOnly();
else
_transferCodings = transferCodings.AsReadOnly();
}
private ReadOnlyCollection<string> _transferCodings; private ReadOnlyCollection<string> _transferCodings;
private static readonly Regex _transferExtensionRegex = HttpRegex.Concat(HttpRegex.Token, HttpRegex.OptionalParameters); private static readonly Regex _transferExtensionRegex = HttpRegex.Concat(HttpRegex.Token, HttpRegex.OptionalParameters);
......
...@@ -10,28 +10,58 @@ namespace PcapDotNet.Packets.Http ...@@ -10,28 +10,58 @@ namespace PcapDotNet.Packets.Http
/// </summary> /// </summary>
public class HttpVersion : IEquatable<HttpVersion> public class HttpVersion : IEquatable<HttpVersion>
{ {
/// <summary>
/// Creates a version from the major and minor version numbers.
/// </summary>
/// <param name="major">The major version number. 0 for 0.9, 1 for 1.0 or 1.1.</param>
/// <param name="minor">The minor version number. 9 for 0.9, 0, for 1.0 and 1 for 1.1.</param>
public HttpVersion(uint major, uint minor) public HttpVersion(uint major, uint minor)
{ {
Major = major; Major = major;
Minor = minor; Minor = minor;
} }
/// <summary>
/// A built version for HTTP/1.0.
/// </summary>
public static HttpVersion Version10 { get { return _version10; } } public static HttpVersion Version10 { get { return _version10; } }
/// <summary>
/// A built version for HTTP/1.1.
/// </summary>
public static HttpVersion Version11 { get { return _version11; } } public static HttpVersion Version11 { get { return _version11; } }
/// <summary>
/// The major version number.
/// </summary>
public uint Major { get; private set; } public uint Major { get; private set; }
/// <summary>
/// The minor version number.
/// </summary>
public uint Minor { get; private set; } public uint Minor { get; private set; }
/// <summary>
/// The number of bytes this version takes.
/// </summary>
public int Length public int Length
{ {
get { return _httpSlashBytes.Length + Major.DigitsCount(10) + 1 + Minor.DigitsCount(10); } get { return _httpSlashBytes.Length + Major.DigitsCount(10) + 1 + Minor.DigitsCount(10); }
} }
/// <summary>
/// A string represneting the version.
/// Example: &quot;HTTP/1.1&quot;.
/// </summary>
/// <returns></returns>
public override string ToString() public override string ToString()
{ {
return string.Format(CultureInfo.InvariantCulture, "HTTP/{0}.{1}", Major, Minor); return string.Format(CultureInfo.InvariantCulture, "HTTP/{0}.{1}", Major, Minor);
} }
/// <summary>
/// Two HTTP versions are equal iff they have the same major and minor versions.
/// </summary>
public bool Equals(HttpVersion other) public bool Equals(HttpVersion other)
{ {
return other != null && return other != null &&
...@@ -39,11 +69,17 @@ namespace PcapDotNet.Packets.Http ...@@ -39,11 +69,17 @@ namespace PcapDotNet.Packets.Http
Minor == other.Minor; Minor == other.Minor;
} }
/// <summary>
/// Two HTTP versions are equal iff they have the same major and minor versions.
/// </summary>
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
return Equals(obj as HttpVersion); return Equals(obj as HttpVersion);
} }
/// <summary>
/// The hash code of an http version is the xor of the hash codes of the minor version and the major version.
/// </summary>
public override int GetHashCode() public override int GetHashCode()
{ {
return Minor.GetHashCode() ^ Major.GetHashCode(); return Minor.GetHashCode() ^ Major.GetHashCode();
......
...@@ -75,6 +75,9 @@ namespace PcapDotNet.Packets.Igmp ...@@ -75,6 +75,9 @@ namespace PcapDotNet.Packets.Igmp
/// </summary> /// </summary>
LeaveGroupVersion2 = 0x17, LeaveGroupVersion2 = 0x17,
/// <summary>
/// Multicast Traceroute Response.
/// </summary>
MulticastTraceRouteResponse = 0x1E, MulticastTraceRouteResponse = 0x1E,
} }
} }
\ No newline at end of file
...@@ -80,6 +80,9 @@ namespace PcapDotNet.Packets.IpV4 ...@@ -80,6 +80,9 @@ namespace PcapDotNet.Packets.IpV4
get { return EthernetType.IpV4; } get { return EthernetType.IpV4; }
} }
/// <summary>
/// The protocol that should be written in the previous (IPv4) layer (in this case: IP).
/// </summary>
public IpV4Protocol PreviousLayerProtocol public IpV4Protocol PreviousLayerProtocol
{ {
get { return IpV4Protocol.Ip; } get { return IpV4Protocol.Ip; }
......
...@@ -256,6 +256,9 @@ namespace PcapDotNet.Packets.Transport ...@@ -256,6 +256,9 @@ namespace PcapDotNet.Packets.Transport
}; };
} }
/// <summary>
/// The first HTTP message in this TCP datagram.
/// </summary>
public HttpDatagram Http public HttpDatagram Http
{ {
get get
...@@ -264,6 +267,9 @@ namespace PcapDotNet.Packets.Transport ...@@ -264,6 +267,9 @@ namespace PcapDotNet.Packets.Transport
} }
} }
/// <summary>
/// All of the available HTTP messages in this TCP datagram.
/// </summary>
public ReadOnlyCollection<HttpDatagram> HttpCollection public ReadOnlyCollection<HttpDatagram> HttpCollection
{ {
get get
......
...@@ -69,6 +69,10 @@ namespace PcapDotNet.Packets.Transport ...@@ -69,6 +69,10 @@ namespace PcapDotNet.Packets.Transport
/// The minimum number of bytes this option take. /// The minimum number of bytes this option take.
/// </summary> /// </summary>
public const int OptionMinimumLength = OptionHeaderLength + OptionValueMinimumLength; public const int OptionMinimumLength = OptionHeaderLength + OptionValueMinimumLength;
/// <summary>
/// The maximum number of bytes this option take.
/// </summary>
public const int OptionMaximumLength = OptionHeaderLength + OptionValueMaximumLength; public const int OptionMaximumLength = OptionHeaderLength + OptionValueMaximumLength;
/// <summary> /// <summary>
...@@ -103,6 +107,9 @@ namespace PcapDotNet.Packets.Transport ...@@ -103,6 +107,9 @@ namespace PcapDotNet.Packets.Transport
/// </summary> /// </summary>
public TcpOptionMoodEmotion Emotion { get; private set; } public TcpOptionMoodEmotion Emotion { get; private set; }
/// <summary>
/// The ASCII string of the emotion.
/// </summary>
public string EmotionString public string EmotionString
{ {
get get
...@@ -123,11 +130,6 @@ namespace PcapDotNet.Packets.Transport ...@@ -123,11 +130,6 @@ namespace PcapDotNet.Packets.Transport
get { return OptionHeaderLength + ValueLength; } get { return OptionHeaderLength + ValueLength; }
} }
public int ValueLength
{
get { return EmotionString.Length; }
}
/// <summary> /// <summary>
/// True iff this option may appear at most once in a datagram. /// True iff this option may appear at most once in a datagram.
/// </summary> /// </summary>
...@@ -189,6 +191,11 @@ namespace PcapDotNet.Packets.Transport ...@@ -189,6 +191,11 @@ namespace PcapDotNet.Packets.Transport
buffer.Write(ref offset, Encoding.ASCII.GetBytes(EmotionString)); buffer.Write(ref offset, Encoding.ASCII.GetBytes(EmotionString));
} }
private int ValueLength
{
get { return EmotionString.Length; }
}
private static TcpOptionMoodEmotion StringToEmotion(string emotionString) private static TcpOptionMoodEmotion StringToEmotion(string emotionString)
{ {
TcpOptionMoodEmotion emotion; TcpOptionMoodEmotion emotion;
......
...@@ -65,6 +65,9 @@ ...@@ -65,6 +65,9 @@
/// </summary> /// </summary>
Evil, Evil,
/// <summary>
/// An unknown emotion.
/// </summary>
None, None,
} }
} }
\ 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