Commit d46d80d9 authored by Brickner_cp's avatar Brickner_cp

HTTP Trailer field.

parent dc90ee8b
......@@ -148,10 +148,6 @@ namespace PcapDotNet.Core.Test
string expected =
httpDatagram.Header.ContentType.Parameters.Select(pair => pair.Key + '=' + pair.Value.ToWiresharkLiteral()).
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));
}
}
......
......@@ -342,6 +342,12 @@ namespace PcapDotNet.Core.Test
++currentOptionIndex;
break;
case "tcp.options.rvbd.trpy":
Assert.AreEqual((TcpOptionType)78, option.OptionType);
// TODO: Support Riverbed.
++currentOptionIndex;
break;
default:
throw new InvalidOperationException("Invalid tcp options field " + field.Name());
}
......
using System;
using System.Linq;
using System.Xml.Linq;
using PcapDotNet.Packets;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.VLanTaggedFrame;
namespace PcapDotNet.Core.Test
......@@ -38,7 +40,10 @@ namespace PcapDotNet.Core.Test
break;
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;
default:
......
......@@ -22,6 +22,10 @@ namespace PcapDotNet.Core.Test
result.Append('\\');
result.Append(currentChar);
continue;
case '\t':
result.Append(@"\x9");
continue;
case '\r':
result.Append(@"\r");
......
......@@ -71,7 +71,7 @@ namespace PcapDotNet.Packets.TestUtils
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);
}
......@@ -122,7 +122,11 @@ namespace PcapDotNet.Packets.TestUtils
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 == '"')
return (char)254;
if (text == 127)
......@@ -155,13 +159,26 @@ namespace PcapDotNet.Packets.TestUtils
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);
}
public static HttpField NextHttpField(this Random random, HashSet<string> fieldNames)
{
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);
foreach (string option in allOptions)
{
......@@ -169,17 +186,22 @@ namespace PcapDotNet.Packets.TestUtils
possibleOptions.Add(option);
}
string chosenOption = random.NextValue(possibleOptions);
switch (chosenOption)
string chosenFieldName = random.NextValue(possibleOptions);
if (chosenFieldName == unknownField)
{
case unknownField:
string fieldName;
do
{
fieldName = random.NextHttpToken();
} while (fieldNames.Contains(fieldName));
return HttpField.CreateField(fieldName, random.NextHttpFieldValue());
do
{
chosenFieldName = random.NextHttpToken();
} while (fieldNames.Contains(chosenFieldName));
}
return random.NextHttpField(chosenFieldName);
}
public static HttpField NextHttpField(this Random random, string fieldName)
{
switch (fieldName.ToUpperInvariant())
{
case HttpTransferEncodingField.FieldNameUpper:
string[] transferCodings = ((Func<string>)(() => random.NextHttpTransferCoding())).GenerateArray(random.NextInt(1, 10));
return new HttpTransferEncodingField(transferCodings);
......@@ -188,11 +210,13 @@ namespace PcapDotNet.Packets.TestUtils
return new HttpContentLengthField(random.NextUInt(1000));
case HttpContentTypeField.FieldNameUpper:
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:
throw new InvalidOperationException("Invalid option " + chosenOption);
return HttpField.CreateField(fieldName, random.NextHttpFieldValue());
}
}
......@@ -240,12 +264,13 @@ namespace PcapDotNet.Packets.TestUtils
for (int i = 0; i != numQuotedValues; ++i)
{
char quotedValue = random.NextHttpTextChar();
if (quotedValue != '\\')
if (quotedValue != '"' && quotedValue != '\\')
quotedString.Append(quotedValue);
else
{
quotedString.Append('\\');
quotedString.Append(random.NextChar((char)0, (char)128));
quotedValue = random.NextChar((char)0, (char)128);
quotedString.Append(quotedValue);
}
}
quotedString.Append('"');
......@@ -262,7 +287,7 @@ namespace PcapDotNet.Packets.TestUtils
if (httpHeader.TransferEncoding != null &&
httpHeader.TransferEncoding.TransferCodings.Any(coding => coding != "identity"))
{
// chunked
// chunked.
List<byte> chunkedBody = new List<byte>();
int numChunks = random.Next(10);
for (int i = 0; i != numChunks; ++i)
......@@ -293,10 +318,14 @@ namespace PcapDotNet.Packets.TestUtils
chunkedBody.AddRange(EncodingExtensions.Iso88591.GetBytes(parameter.Key));
}
chunkedBody.AddRange(Encoding.ASCII.GetBytes("\r\n"));
var trailer = random.NextHttpHeader();
byte[] trailerBuffer = new byte[trailer.BytesLength];
trailer.Write(trailerBuffer, 0);
chunkedBody.AddRange(trailerBuffer);
HttpTrailerField trailerField = httpHeader.Trailer;
if (trailerField != null)
{
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());
}
......
......@@ -35,6 +35,8 @@ namespace PcapDotNet.Packets.Http
return new HttpContentLengthField(fieldValue);
case HttpContentTypeField.FieldNameUpper:
return new HttpContentTypeField(fieldValue);
case HttpTrailerField.FieldNameUpper:
return new HttpTrailerField(fieldValue);
default:
return new HttpField(fieldName, fieldValue.ToArray());
......
......@@ -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>
/// Two HTTP headers are equal if they have the same fields with the same values.
/// </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 @@
<Compile Include="Http\HttpRequestMethod.cs" />
<Compile Include="Http\HttpResponseDatagram.cs" />
<Compile Include="Http\HttpResponseLayer.cs" />
<Compile Include="Http\HttpTrailerField.cs" />
<Compile Include="Http\HttpTransferEncodingField.cs" />
<Compile Include="Http\HttpVersion.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