Commit d46d80d9 authored by Brickner_cp's avatar Brickner_cp

HTTP Trailer field.

parent dc90ee8b
...@@ -148,10 +148,6 @@ namespace PcapDotNet.Core.Test ...@@ -148,10 +148,6 @@ namespace PcapDotNet.Core.Test
string expected = string expected =
httpDatagram.Header.ContentType.Parameters.Select(pair => pair.Key + '=' + pair.Value.ToWiresharkLiteral()). httpDatagram.Header.ContentType.Parameters.Select(pair => pair.Key + '=' + pair.Value.ToWiresharkLiteral()).
SequenceToString(';'); SequenceToString(';');
if (expected.Contains(@"\;"))
expected = expected.Split(new[] {@"\;"}, StringSplitOptions.None)[0] + @"\";
if (expected.Contains(@"\r"))
expected = expected.Split(new[] {@"\r"}, StringSplitOptions.None)[0];
Assert.AreEqual(expected, fieldShow.Substring(fieldShowParametersStart + 1)); Assert.AreEqual(expected, fieldShow.Substring(fieldShowParametersStart + 1));
} }
} }
......
...@@ -342,6 +342,12 @@ namespace PcapDotNet.Core.Test ...@@ -342,6 +342,12 @@ namespace PcapDotNet.Core.Test
++currentOptionIndex; ++currentOptionIndex;
break; break;
case "tcp.options.rvbd.trpy":
Assert.AreEqual((TcpOptionType)78, option.OptionType);
// TODO: Support Riverbed.
++currentOptionIndex;
break;
default: default:
throw new InvalidOperationException("Invalid tcp options field " + field.Name()); throw new InvalidOperationException("Invalid tcp options field " + field.Name());
} }
......
using System; using System;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using PcapDotNet.Packets; using PcapDotNet.Packets;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.VLanTaggedFrame; using PcapDotNet.Packets.VLanTaggedFrame;
namespace PcapDotNet.Core.Test namespace PcapDotNet.Core.Test
...@@ -38,7 +40,10 @@ namespace PcapDotNet.Core.Test ...@@ -38,7 +40,10 @@ namespace PcapDotNet.Core.Test
break; break;
case "vlan.trailer": case "vlan.trailer":
field.AssertValue(vLanTaggedFrameDatagram.Trailer); if (
!new[] {(EthernetType)1, (EthernetType)5, (EthernetType)17, (EthernetType)30, (EthernetType)43, (EthernetType)50}.Contains(
vLanTaggedFrameDatagram.EtherType))
field.AssertValue(vLanTaggedFrameDatagram.Trailer);
break; break;
default: default:
......
...@@ -22,6 +22,10 @@ namespace PcapDotNet.Core.Test ...@@ -22,6 +22,10 @@ namespace PcapDotNet.Core.Test
result.Append('\\'); result.Append('\\');
result.Append(currentChar); result.Append(currentChar);
continue; continue;
case '\t':
result.Append(@"\x9");
continue;
case '\r': case '\r':
result.Append(@"\r"); result.Append(@"\r");
......
...@@ -71,7 +71,7 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -71,7 +71,7 @@ namespace PcapDotNet.Packets.TestUtils
public static string NextHttpToken(this Random random) public static string NextHttpToken(this Random random)
{ {
char[] httpToken = ((Func<char>)(() => random.NextHttpTokenChar())).GenerateArray(random.NextInt(1, 100)); char[] httpToken = ((Func<char>)(() => random.NextHttpTokenChar())).GenerateArray(random.NextInt(1, 20));
return new string(httpToken); return new string(httpToken);
} }
...@@ -122,7 +122,11 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -122,7 +122,11 @@ namespace PcapDotNet.Packets.TestUtils
public static char NextHttpTextChar(this Random random) public static char NextHttpTextChar(this Random random)
{ {
char text = random.NextChar((char)33, (char)254); char text = random.NextChar((char)33, (char)252);
if (text == '\\')
return (char)252;
if (text == ';')
return (char)253;
if (text == '"') if (text == '"')
return (char)254; return (char)254;
if (text == 127) if (text == 127)
...@@ -155,13 +159,26 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -155,13 +159,26 @@ namespace PcapDotNet.Packets.TestUtils
fieldNames.Add(fields.Last().Name); fieldNames.Add(fields.Last().Name);
} }
if (fields.Any(field => field.Name.Equals(HttpContentLengthField.FieldName, StringComparison.InvariantCultureIgnoreCase)) &&
fields.Any(field => field.Name.Equals(HttpTransferEncodingField.FieldName, StringComparison.InvariantCultureIgnoreCase)))
{
fields.RemoveAll(field => field.Name.Equals(HttpContentLengthField.FieldName, StringComparison.InvariantCultureIgnoreCase));
}
return new HttpHeader(fields); return new HttpHeader(fields);
} }
public static HttpField NextHttpField(this Random random, HashSet<string> fieldNames) public static HttpField NextHttpField(this Random random, HashSet<string> fieldNames)
{ {
const string unknownField = "Unknown Name"; const string unknownField = "Unknown Name";
List<string> allOptions = new List<string> { unknownField, HttpTransferEncodingField.FieldNameUpper, HttpContentLengthField.FieldNameUpper, HttpContentTypeField.FieldNameUpper }; List<string> allOptions = new List<string>
{
unknownField,
HttpTransferEncodingField.FieldNameUpper,
HttpContentLengthField.FieldNameUpper,
HttpContentTypeField.FieldNameUpper,
HttpTrailerField.FieldNameUpper,
};
List<string> possibleOptions = new List<string>(allOptions.Count); List<string> possibleOptions = new List<string>(allOptions.Count);
foreach (string option in allOptions) foreach (string option in allOptions)
{ {
...@@ -169,17 +186,22 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -169,17 +186,22 @@ namespace PcapDotNet.Packets.TestUtils
possibleOptions.Add(option); possibleOptions.Add(option);
} }
string chosenOption = random.NextValue(possibleOptions); string chosenFieldName = random.NextValue(possibleOptions);
switch (chosenOption) if (chosenFieldName == unknownField)
{ {
case unknownField: do
string fieldName; {
do chosenFieldName = random.NextHttpToken();
{ } while (fieldNames.Contains(chosenFieldName));
fieldName = random.NextHttpToken(); }
} while (fieldNames.Contains(fieldName));
return HttpField.CreateField(fieldName, random.NextHttpFieldValue()); return random.NextHttpField(chosenFieldName);
}
public static HttpField NextHttpField(this Random random, string fieldName)
{
switch (fieldName.ToUpperInvariant())
{
case HttpTransferEncodingField.FieldNameUpper: case HttpTransferEncodingField.FieldNameUpper:
string[] transferCodings = ((Func<string>)(() => random.NextHttpTransferCoding())).GenerateArray(random.NextInt(1, 10)); string[] transferCodings = ((Func<string>)(() => random.NextHttpTransferCoding())).GenerateArray(random.NextInt(1, 10));
return new HttpTransferEncodingField(transferCodings); return new HttpTransferEncodingField(transferCodings);
...@@ -188,11 +210,13 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -188,11 +210,13 @@ namespace PcapDotNet.Packets.TestUtils
return new HttpContentLengthField(random.NextUInt(1000)); return new HttpContentLengthField(random.NextUInt(1000));
case HttpContentTypeField.FieldNameUpper: case HttpContentTypeField.FieldNameUpper:
return new HttpContentTypeField(random.NextHttpToken(), random.NextHttpToken(), random.NextHttpFieldParameters()); return new HttpContentTypeField(random.NextHttpToken(), random.NextHttpToken(), random.NextHttpFieldParameters());
case HttpTrailerField.FieldNameUpper:
return new HttpTrailerField(((Func<string>)(() => random.NextHttpToken())).GenerateArray(random.NextInt(1, 5)));
default: default:
throw new InvalidOperationException("Invalid option " + chosenOption); return HttpField.CreateField(fieldName, random.NextHttpFieldValue());
} }
} }
...@@ -240,12 +264,13 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -240,12 +264,13 @@ namespace PcapDotNet.Packets.TestUtils
for (int i = 0; i != numQuotedValues; ++i) for (int i = 0; i != numQuotedValues; ++i)
{ {
char quotedValue = random.NextHttpTextChar(); char quotedValue = random.NextHttpTextChar();
if (quotedValue != '\\') if (quotedValue != '"' && quotedValue != '\\')
quotedString.Append(quotedValue); quotedString.Append(quotedValue);
else else
{ {
quotedString.Append('\\'); quotedString.Append('\\');
quotedString.Append(random.NextChar((char)0, (char)128)); quotedValue = random.NextChar((char)0, (char)128);
quotedString.Append(quotedValue);
} }
} }
quotedString.Append('"'); quotedString.Append('"');
...@@ -262,7 +287,7 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -262,7 +287,7 @@ namespace PcapDotNet.Packets.TestUtils
if (httpHeader.TransferEncoding != null && if (httpHeader.TransferEncoding != null &&
httpHeader.TransferEncoding.TransferCodings.Any(coding => coding != "identity")) httpHeader.TransferEncoding.TransferCodings.Any(coding => coding != "identity"))
{ {
// chunked // chunked.
List<byte> chunkedBody = new List<byte>(); List<byte> chunkedBody = new List<byte>();
int numChunks = random.Next(10); int numChunks = random.Next(10);
for (int i = 0; i != numChunks; ++i) for (int i = 0; i != numChunks; ++i)
...@@ -293,10 +318,14 @@ namespace PcapDotNet.Packets.TestUtils ...@@ -293,10 +318,14 @@ namespace PcapDotNet.Packets.TestUtils
chunkedBody.AddRange(EncodingExtensions.Iso88591.GetBytes(parameter.Key)); chunkedBody.AddRange(EncodingExtensions.Iso88591.GetBytes(parameter.Key));
} }
chunkedBody.AddRange(Encoding.ASCII.GetBytes("\r\n")); chunkedBody.AddRange(Encoding.ASCII.GetBytes("\r\n"));
var trailer = random.NextHttpHeader(); HttpTrailerField trailerField = httpHeader.Trailer;
byte[] trailerBuffer = new byte[trailer.BytesLength]; if (trailerField != null)
trailer.Write(trailerBuffer, 0); {
chunkedBody.AddRange(trailerBuffer); HttpHeader trailer = new HttpHeader(trailerField.FieldsNames.Distinct().Select(fieldName => random.NextHttpField(fieldName)));
byte[] trailerBuffer = new byte[trailer.BytesLength];
trailer.Write(trailerBuffer, 0);
chunkedBody.AddRange(trailerBuffer);
}
return new Datagram(chunkedBody.ToArray()); return new Datagram(chunkedBody.ToArray());
} }
......
...@@ -35,6 +35,8 @@ namespace PcapDotNet.Packets.Http ...@@ -35,6 +35,8 @@ namespace PcapDotNet.Packets.Http
return new HttpContentLengthField(fieldValue); return new HttpContentLengthField(fieldValue);
case HttpContentTypeField.FieldNameUpper: case HttpContentTypeField.FieldNameUpper:
return new HttpContentTypeField(fieldValue); return new HttpContentTypeField(fieldValue);
case HttpTrailerField.FieldNameUpper:
return new HttpTrailerField(fieldValue);
default: default:
return new HttpField(fieldName, fieldValue.ToArray()); return new HttpField(fieldName, fieldValue.ToArray());
......
...@@ -89,6 +89,17 @@ namespace PcapDotNet.Packets.Http ...@@ -89,6 +89,17 @@ namespace PcapDotNet.Packets.Http
} }
} }
/// <summary>
/// The HTTP Trailer field if it exists (null otherwise).
/// </summary>
public HttpTrailerField Trailer
{
get
{
return GetField<HttpTrailerField>(HttpTrailerField.FieldName);
}
}
/// <summary> /// <summary>
/// Two HTTP headers are equal if they have the same fields with the same values. /// Two HTTP headers are equal if they have the same fields with the same values.
/// </summary> /// </summary>
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http
{
/// <summary>
/// RFC 2616.
/// The Trailer general field value indicates that the given set of header fields is present in the trailer of a message encoded
/// with chunked transfer-coding.
/// <pre>
/// Trailer = "Trailer" ":" 1#field-name
/// </pre>
/// An HTTP/1.1 message should include a Trailer header field in a message using chunked transfer-coding with a non-empty trailer.
/// Doing so allows the recipient to know which header fields to expect in the trailer.
///
/// If no Trailer header field is present, the trailer should not include any header fields.
///
/// Message header fields listed in the Trailer header field must not include the following header fields:
/// * Transfer-Encoding.
/// * Content-Length.
/// * Trailer.
/// </summary>
public sealed class HttpTrailerField : HttpField, IEquatable<HttpTrailerField>
{
/// <summary>
/// The field name.
/// </summary>
public const string FieldName = "Trailer";
/// <summary>
/// The field name in uppercase.
/// </summary>
public const string FieldNameUpper = "TRAILER";
/// <summary>
/// Creates a Trailer Field according to the given field names.
/// </summary>
/// <param name="fieldsNames">The names of the fields that should be encoded in the chunked body trailer.</param>
public HttpTrailerField(IEnumerable<string> fieldsNames)
: base(FieldName, fieldsNames.SequenceToString(','))
{
SetFieldNames(fieldsNames);
}
/// <summary>
/// The names of the fields that should be encoded in the chunked body trailer.
/// </summary>
public ReadOnlyCollection<string> FieldsNames { get { return _fieldNames; } }
/// <summary>
/// True iff the two fields are equal.
/// Two trailer fields are equal iff they have the fields names in the same order.
/// </summary>
public bool Equals(HttpTrailerField other)
{
return other != null &&
FieldsNames.SequenceEqual(other.FieldsNames);
}
/// <summary>
/// True iff the two fields are equal.
/// Two trailer fields are equal iff they have the fields names in the same order.
/// </summary>
public override bool Equals(HttpField other)
{
return Equals(other as HttpTrailerField);
}
internal HttpTrailerField(byte[] fieldValue)
: base(FieldName, fieldValue)
{
string fieldValueString = HttpRegex.GetString(fieldValue);
Match match = _regex.Match(fieldValueString);
if (!match.Success)
return;
SetFieldNames(match.GroupCapturesValues(FieldNameGroupName));
}
private void SetFieldNames(IEnumerable<string> fieldNames)
{
_fieldNames = fieldNames.Select(name => name.ToUpperInvariant()).ToArray().AsReadOnly();
}
private const string FieldNameGroupName = "FieldName";
private static readonly Regex _regex =
HttpRegex.MatchEntire(HttpRegex.CommaSeparatedRegex(HttpRegex.Capture(HttpRegex.Token, FieldNameGroupName), 1));
private ReadOnlyCollection<string> _fieldNames;
}
}
\ No newline at end of file
...@@ -234,6 +234,7 @@ ...@@ -234,6 +234,7 @@
<Compile Include="Http\HttpRequestMethod.cs" /> <Compile Include="Http\HttpRequestMethod.cs" />
<Compile Include="Http\HttpResponseDatagram.cs" /> <Compile Include="Http\HttpResponseDatagram.cs" />
<Compile Include="Http\HttpResponseLayer.cs" /> <Compile Include="Http\HttpResponseLayer.cs" />
<Compile Include="Http\HttpTrailerField.cs" />
<Compile Include="Http\HttpTransferEncodingField.cs" /> <Compile Include="Http\HttpTransferEncodingField.cs" />
<Compile Include="Http\HttpVersion.cs" /> <Compile Include="Http\HttpVersion.cs" />
<Compile Include="Icmp\IcmpAddressMaskReplyDatagram.cs" /> <Compile Include="Icmp\IcmpAddressMaskReplyDatagram.cs" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment