Commit 65032c0f authored by Brickner_cp's avatar Brickner_cp

Simple HTTP Header fields

parent 36d1d759
using System;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base;
using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.Http;
using PcapDotNet.Packets.IpV4;
......@@ -55,8 +57,59 @@ namespace PcapDotNet.Packets.Test
TestHttpRequest("GET /url HTTP/1.1", "GET", "/url");
TestHttpRequest("GET HTTP/1.1", "GET", "", HttpVersion.Version11);
TestHttpRequest("GET /url HTTP/1.1\r\nCache-Control: no-cache", "GET", "/url", HttpVersion.Version11,
new HttpHeader(HttpField.Create("Cache-Control", Encoding.ASCII.GetBytes("no-cache"))));
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"Cache-Control: no-cache\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(
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")));
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"\r\n" +
"A: B\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader());
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" +
"\r\n" +
"B: C\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(
new HttpField("A", "B")));
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" +
"abc\r\n" +
"B: C\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(
new HttpField("A", "B")));
TestHttpRequest("GET /url HTTP/1.1\r\n" +
"A: B\r\n" +
"abc:\r\n" +
"B: C\r\n",
"GET", "/url", HttpVersion.Version11,
new HttpHeader(
new HttpField("A", "B"),
new HttpField("abc", ""),
new HttpField("B", "C")));
TestHttpResponse("HTTP/");
TestHttpResponse("HTTP/1");
......@@ -70,6 +123,12 @@ namespace PcapDotNet.Packets.Test
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, "OK");
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11);
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")));
}
private static void TestHttpRequest(string httpString, string expectedMethod = null, string expectedUri = null, HttpVersion expectedVersion = null, HttpHeader expectedHeader = null)
......@@ -91,7 +150,7 @@ namespace PcapDotNet.Packets.Test
// Assert.IsNotNull(header);
}
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null)
private static void TestHttpResponse(string httpString, HttpVersion expectedVersion = null, uint? expectedStatusCode = null, string expectedReasonPhrase = null, HttpHeader expectedHeader = null)
{
Packet packet = BuildPacket(httpString);
......@@ -100,6 +159,7 @@ namespace PcapDotNet.Packets.Test
Assert.IsFalse(http.IsRequest, "IsRequest " + httpString);
Assert.IsTrue(http.IsResponse, "IsResponse " + httpString);
Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString);
Assert.AreEqual(expectedHeader, http.Header, "Header " + httpString);
HttpResponseDatagram response = (HttpResponseDatagram)http;
Assert.AreEqual(expectedStatusCode, response.StatusCode, "StatusCode " + httpString);
......
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using PcapDotNet.Base;
using IListExtensions = PcapDotNet.Base.IListExtensions;
namespace PcapDotNet.Packets.Http
{
public class HttpCommaSeparatedField : HttpField, IEnumerable<HttpCommaSeparatedInnerField>, IEquatable<HttpCommaSeparatedField>
{
internal HttpCommaSeparatedField(string name, params HttpCommaSeparatedInnerField[] value)
:this(name, (IEnumerable<HttpCommaSeparatedInnerField>)value)
{
}
internal HttpCommaSeparatedField(string name, IEnumerable<HttpCommaSeparatedInnerField> value = null)
:base(name)
{
_innerFields = value == null ? null : value.ToDictionary(innerField => innerField.Name, innerField => innerField);
}
public bool Equals(HttpCommaSeparatedField other)
{
return base.Equals(other) && _innerFields.DictionaryEquals(other._innerFields);
}
public override string ToString()
{
return base.ToString() + (_innerFields == null ? string.Empty : _innerFields.Values.SequenceToString(","));
}
internal HttpCommaSeparatedField(string name, IEnumerable<byte> value)
:base(name)
{
var innerFields = new Dictionary<string, List<ReadOnlyCollection<byte>>>();
foreach (var commaValue in CommaSeparate(value))
{
string innerFieldName = commaValue.Key;
List<ReadOnlyCollection<byte>> innerFieldValues;
if (!innerFields.TryGetValue(innerFieldName, out innerFieldValues))
{
innerFieldValues = new List<ReadOnlyCollection<byte>>();
innerFields.Add(innerFieldName, innerFieldValues);
}
innerFieldValues.Add(commaValue.Value == null ? null : commaValue.Value.ToArray().AsReadOnly());
}
_innerFields = innerFields.ToDictionary(field => field.Key, field => new HttpCommaSeparatedInnerField(field.Key, field.Value));
}
private static IEnumerable<KeyValuePair<string, IEnumerable<byte>>> CommaSeparate(IEnumerable<byte> buffer)
{
do
{
string key = Encoding.ASCII.GetString(buffer.TakeWhile(b => b.IsToken()).ToArray());
if (key.Length == 0)
yield break;
buffer = buffer.Skip(key.Length);
if (!buffer.Any())
{
yield return new KeyValuePair<string, IEnumerable<byte>>(key, null);
continue;
}
switch (buffer.First())
{
case AsciiBytes.EqualsSign:
buffer = buffer.Skip(1);
IEnumerable<byte> value = buffer;
int count = 0;
if (buffer.Any())
{
byte valueByte = buffer.First();
if (valueByte.IsToken()) // Token value
count = 1 + buffer.Skip(1).TakeWhile(b => b.IsToken()).Count();
else if (!TryExtractQuotedString(buffer, out count)) // Not Quoted value - Illegal value
yield break;
buffer = buffer.Skip(count);
}
yield return new KeyValuePair<string, IEnumerable<byte>>(key, value.Take(count));
if (!buffer.Any())
yield break;
switch (buffer.First())
{
case AsciiBytes.Comma:
case AsciiBytes.Space:
buffer = buffer.Skip(1).SkipWhile(b => b == AsciiBytes.Space || b == AsciiBytes.Comma);
break;
default:
yield break;
}
break;
case AsciiBytes.Comma:
case AsciiBytes.Space:
yield return new KeyValuePair<string, IEnumerable<byte>>(key, null);
buffer = buffer.Skip(1).SkipWhile(b => b == AsciiBytes.Space || b == AsciiBytes.Comma);
break;
default:
yield break;
}
} while (buffer.Any());
}
private static bool TryExtractQuotedString(IEnumerable<byte> buffer, out int count)
{
count = 0;
if (!buffer.Any() || buffer.First() != AsciiBytes.DoubleQuotationMark)
return false;
++count;
buffer = buffer.Skip(1);
while (buffer.Any())
{
++count;
byte current = buffer.First();
if (current == AsciiBytes.DoubleQuotationMark)
return true;
buffer = buffer.Skip(1);
if (current == AsciiBytes.BackSlash && buffer.Any())
{
current = buffer.First();
if (current.IsChar())
{
buffer = buffer.Skip(1);
++count;
}
}
}
return false;
}
public IEnumerator<HttpCommaSeparatedInnerField> GetEnumerator()
{
return _innerFields.Values.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private readonly Dictionary<string, HttpCommaSeparatedInnerField> _innerFields;
}
}
\ No newline at end of file
//using System;
//using System.Collections;
//using System.Collections.Generic;
//using System.Collections.ObjectModel;
//using System.Linq;
//using System.Text;
//using System.Text.RegularExpressions;
//using PcapDotNet.Base;
//using IListExtensions = PcapDotNet.Base.IListExtensions;
//
//namespace PcapDotNet.Packets.Http
//{
// public class HttpCommaSeparatedField : HttpField, IEnumerable<HttpCommaSeparatedInnerField>, IEquatable<HttpCommaSeparatedField>
// {
//
// internal HttpCommaSeparatedField(string name, params HttpCommaSeparatedInnerField[] value)
// :this(name, (IEnumerable<HttpCommaSeparatedInnerField>)value)
// {
// }
//
// internal HttpCommaSeparatedField(string name, IEnumerable<HttpCommaSeparatedInnerField> value = null)
// :base(name)
// {
// _innerFields = value == null ? null : value.ToDictionary(innerField => innerField.Name, innerField => innerField);
// }
//
// public bool Equals(HttpCommaSeparatedField other)
// {
// return base.Equals(other) && _innerFields.DictionaryEquals(other._innerFields);
// }
//
// public override string ToString()
// {
// return base.ToString() + (_innerFields == null ? string.Empty : _innerFields.Values.SequenceToString(","));
// }
//
// internal HttpCommaSeparatedField(string name, byte[] value)
// :base(name)
// {
// string valueString = new string(value.Select(b => (char)b).ToArray());
// Regex regex = new Regex(Regex.);
// HttpParser parser = new HttpParser(value);
// while (parser.Success)
// {
// string directiveName;
// parser.Token(out directiveName);
// if (parser.)
// if (parser..Skip(AsciiBytes.Space)
// }
// var innerFields = new Dictionary<string, List<ReadOnlyCollection<byte>>>();
// foreach (var commaValue in CommaSeparate(value))
// {
// string innerFieldName = commaValue.Key;
//
// List<ReadOnlyCollection<byte>> innerFieldValues;
// if (!innerFields.TryGetValue(innerFieldName, out innerFieldValues))
// {
// innerFieldValues = new List<ReadOnlyCollection<byte>>();
// innerFields.Add(innerFieldName, innerFieldValues);
// }
// innerFieldValues.Add(commaValue.Value == null ? null : commaValue.Value.ToArray().AsReadOnly());
// }
//
// _innerFields = innerFields.ToDictionary(field => field.Key, field => new HttpCommaSeparatedInnerField(field.Key, field.Value));
// }
//
// private static IEnumerable<KeyValuePair<string, IEnumerable<byte>>> CommaSeparate(IEnumerable<byte> buffer)
// {
// do
// {
// string key = Encoding.ASCII.GetString(buffer.TakeWhile(b => b.IsToken()).ToArray());
// if (key.Length == 0)
// yield break;
//
// buffer = buffer.Skip(key.Length);
// if (!buffer.Any())
// {
// yield return new KeyValuePair<string, IEnumerable<byte>>(key, null);
// continue;
// }
//
// switch (buffer.First())
// {
// case AsciiBytes.EqualsSign:
// buffer = buffer.Skip(1);
// IEnumerable<byte> value = buffer;
// int count = 0;
// if (buffer.Any())
// {
// byte valueByte = buffer.First();
// if (valueByte.IsToken()) // Token value
// count = 1 + buffer.Skip(1).TakeWhile(b => b.IsToken()).Count();
// else if (!TryExtractQuotedString(buffer, out count)) // Not Quoted value - Illegal value
// yield break;
// buffer = buffer.Skip(count);
// }
// yield return new KeyValuePair<string, IEnumerable<byte>>(key, value.Take(count));
//
// if (!buffer.Any())
// yield break;
// switch (buffer.First())
// {
// case AsciiBytes.Comma:
// case AsciiBytes.Space:
// buffer = buffer.Skip(1).SkipWhile(b => b == AsciiBytes.Space || b == AsciiBytes.Comma);
// break;
//
// default:
// yield break;
// }
// break;
//
// case AsciiBytes.Comma:
// case AsciiBytes.Space:
// yield return new KeyValuePair<string, IEnumerable<byte>>(key, null);
// buffer = buffer.Skip(1).SkipWhile(b => b == AsciiBytes.Space || b == AsciiBytes.Comma);
// break;
//
// default:
// yield break;
// }
// } while (buffer.Any());
// }
//
// private static bool TryExtractQuotedString(IEnumerable<byte> buffer, out int count)
// {
// count = 0;
// if (!buffer.Any() || buffer.First() != AsciiBytes.DoubleQuotationMark)
// return false;
//
// ++count;
// buffer = buffer.Skip(1);
//
// while (buffer.Any())
// {
// ++count;
// byte current = buffer.First();
//
// if (current == AsciiBytes.DoubleQuotationMark)
// return true;
//
// buffer = buffer.Skip(1);
// if (current == AsciiBytes.BackSlash && buffer.Any())
// {
// current = buffer.First();
// if (current.IsChar())
// {
// buffer = buffer.Skip(1);
// ++count;
// }
// }
// }
//
// return false;
// }
//
// public IEnumerator<HttpCommaSeparatedInnerField> GetEnumerator()
// {
// return _innerFields.Values.GetEnumerator();
// }
//
// IEnumerator IEnumerable.GetEnumerator()
// {
// return GetEnumerator();
// }
//
// private readonly Dictionary<string, HttpCommaSeparatedInnerField> _innerFields;
// }
//}
\ No newline at end of file
......@@ -262,7 +262,7 @@ namespace PcapDotNet.Packets.Http
{
string fieldName;
IEnumerable<byte> fieldValue;
parser.Token(out fieldName).Colon().FieldValue(out fieldValue);
parser.Token(out fieldName).Colon().FieldValue(out fieldValue).CarraigeReturnLineFeed();
if (parser.Success)
yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
}
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http
{
public class HttpField : IEquatable<HttpField>
{
public static HttpField Create(string name, IEnumerable<byte> value)
public HttpField(string name, string value)
: this(name, value, _defaultEncoding)
{
}
public HttpField(string name, string value, Encoding encoding)
: this(name, encoding.GetBytes(value))
{
}
public HttpField(string name, IEnumerable<byte> value)
: this(name, value.ToArray())
{
}
public HttpField(string name, IList<byte> value)
:this(name, value.AsReadOnly())
{
switch (name)
{
// general-header
case "Cache-Control":
return new HttpCommaSeparatedField(name, value);
case "Connection":
case "Date":
case "Pragma":
case "Trailer":
case "Transfer-Encoding":
case "Upgrade":
case "Via":
case "Warning":
break;
}
return new HttpField(name);
}
public HttpField(string name, ReadOnlyCollection<byte> value)
{
Name = name;
Value = value;
}
// public static HttpField Create(string name, byte[] value)
// {
// switch (name)
// {
// general-header
// case "Cache-Control":
// return new HttpCommaSeparatedField(name, value);
// case "Connection":
// case "Date":
// case "Pragma":
// case "Trailer":
// case "Transfer-Encoding":
// case "Upgrade":
// case "Via":
// case "Warning":
// break;
// }
//
// return new HttpField(name);
// }
public string Name { get; private set; }
public ReadOnlyCollection<byte> Value { get; private set; }
public bool Equals(HttpField other)
{
return other != null && Name.Equals(other.Name);
return other != null && Name.Equals(other.Name) && Value.SequenceEqual(other.Value);
}
public override bool Equals(object obj)
......@@ -40,12 +70,9 @@ namespace PcapDotNet.Packets.Http
public override string ToString()
{
return string.Format("{0}: ", Name);
return string.Format("{0}: {1}", Name, _defaultEncoding.GetString(Value.ToArray()));
}
internal HttpField(string name)
{
Name = name;
}
private static readonly Encoding _defaultEncoding = Encoding.GetEncoding(28591);
}
}
\ No newline at end of file
......@@ -47,10 +47,10 @@ namespace PcapDotNet.Packets.Http
mergedFields.Add(fieldName, fieldValue);
}
else
mergedFields[fieldName] = fieldValue.Concat(AsciiBytes.Space).Concat(fieldValue);
mergedFields[fieldName] = fieldValue.Concat(AsciiBytes.Comma).Concat(field.Value);
}
_fields = mergedFields.ToDictionary(field => field.Key, field => HttpField.Create(field.Key, field.Value));
_fields = mergedFields.ToDictionary(field => field.Key, field => new HttpField(field.Key, field.Value));
}
public IEnumerator<HttpField> GetEnumerator()
......
......@@ -8,6 +8,11 @@ namespace PcapDotNet.Packets.Http
{
internal class HttpParser
{
public HttpParser(byte[] buffer)
:this(buffer, 0, buffer.Length)
{
}
public HttpParser(byte[] buffer, int offset, int length)
{
_buffer = buffer;
......@@ -43,31 +48,6 @@ namespace PcapDotNet.Packets.Http
return this;
}
private HttpParser Bytes(byte value)
{
if (!Success)
return this;
var range = Range;
if (!range.Any() || range.First() != value)
return Fail();
++_offset;
return this;
}
private HttpParser Bytes(params byte[] values)
{
if (!Success)
return this;
if (!Range.Take(values.Length).SequenceEqual(values))
return Fail();
_offset += values.Length;
return this;
}
public HttpParser Colon()
{
return Bytes(AsciiBytes.Colon);
......@@ -83,8 +63,6 @@ namespace PcapDotNet.Packets.Http
return Bytes(AsciiBytes.Space);
}
public HttpParser SkipLws()
{
while (true)
......@@ -150,17 +128,6 @@ namespace PcapDotNet.Packets.Http
return this;
}
private HttpParser Fail()
{
Success = false;
return this;
}
private IEnumerable<byte> Range
{
get { return _buffer.Range(_offset, _totalLength - _offset); }
}
public HttpParser RequestUri(out string uri)
{
if (!Success)
......@@ -248,12 +215,57 @@ namespace PcapDotNet.Packets.Http
return this;
}
int reasonPhraseLength = Range.Count(value => !value.IsControl() || value == AsciiBytes.HorizontalTab);
int count = 0;
foreach (byte b in Range)
{
if (!b.IsControl() || b == AsciiBytes.HorizontalTab)
++count;
else
break;
}
Console.WriteLine(count);
int reasonPhraseLength = Range.TakeWhile(value => !value.IsControl() || value == AsciiBytes.HorizontalTab).Count();
reasonPhrase = new Datagram(_buffer, _offset, reasonPhraseLength);
_offset += reasonPhraseLength;
return this;
}
private HttpParser Fail()
{
Success = false;
return this;
}
private IEnumerable<byte> Range
{
get { return _buffer.Range(_offset, _totalLength - _offset); }
}
private HttpParser Bytes(byte value)
{
if (!Success)
return this;
var range = Range;
if (!range.Any() || range.First() != value)
return Fail();
++_offset;
return this;
}
private HttpParser Bytes(params byte[] values)
{
if (!Success)
return this;
if (!Range.Take(values.Length).SequenceEqual(values))
return Fail();
_offset += values.Length;
return this;
}
private static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/");
private readonly byte[] _buffer;
......
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