Commit 65032c0f authored by Brickner_cp's avatar Brickner_cp

Simple HTTP Header fields

parent 36d1d759
using System; using System;
using System.Linq;
using System.Text; using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using PcapDotNet.Base;
using PcapDotNet.Packets.Ethernet; using PcapDotNet.Packets.Ethernet;
using PcapDotNet.Packets.Http; using PcapDotNet.Packets.Http;
using PcapDotNet.Packets.IpV4; using PcapDotNet.Packets.IpV4;
...@@ -55,8 +57,59 @@ namespace PcapDotNet.Packets.Test ...@@ -55,8 +57,59 @@ namespace PcapDotNet.Packets.Test
TestHttpRequest("GET /url HTTP/1.1", "GET", "/url"); TestHttpRequest("GET /url HTTP/1.1", "GET", "/url");
TestHttpRequest("GET HTTP/1.1", "GET", "", HttpVersion.Version11); TestHttpRequest("GET HTTP/1.1", "GET", "", HttpVersion.Version11);
TestHttpRequest("GET /url HTTP/1.1\r\nCache-Control: no-cache", "GET", "/url", HttpVersion.Version11, TestHttpRequest("GET /url HTTP/1.1\r\n" +
new HttpHeader(HttpField.Create("Cache-Control", Encoding.ASCII.GetBytes("no-cache")))); "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/");
TestHttpResponse("HTTP/1"); TestHttpResponse("HTTP/1");
...@@ -70,6 +123,12 @@ namespace PcapDotNet.Packets.Test ...@@ -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, 200, "OK");
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11); TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11);
TestHttpResponse("HTTP/1.1 200 OK", HttpVersion.Version11, 200, " OK"); 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) 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 ...@@ -91,7 +150,7 @@ namespace PcapDotNet.Packets.Test
// Assert.IsNotNull(header); // 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); Packet packet = BuildPacket(httpString);
...@@ -100,6 +159,7 @@ namespace PcapDotNet.Packets.Test ...@@ -100,6 +159,7 @@ namespace PcapDotNet.Packets.Test
Assert.IsFalse(http.IsRequest, "IsRequest " + httpString); Assert.IsFalse(http.IsRequest, "IsRequest " + httpString);
Assert.IsTrue(http.IsResponse, "IsResponse " + httpString); Assert.IsTrue(http.IsResponse, "IsResponse " + httpString);
Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString); Assert.AreEqual(expectedVersion, http.Version, "Version " + httpString);
Assert.AreEqual(expectedHeader, http.Header, "Header " + httpString);
HttpResponseDatagram response = (HttpResponseDatagram)http; HttpResponseDatagram response = (HttpResponseDatagram)http;
Assert.AreEqual(expectedStatusCode, response.StatusCode, "StatusCode " + httpString); Assert.AreEqual(expectedStatusCode, response.StatusCode, "StatusCode " + httpString);
......
...@@ -262,7 +262,7 @@ namespace PcapDotNet.Packets.Http ...@@ -262,7 +262,7 @@ namespace PcapDotNet.Packets.Http
{ {
string fieldName; string fieldName;
IEnumerable<byte> fieldValue; IEnumerable<byte> fieldValue;
parser.Token(out fieldName).Colon().FieldValue(out fieldValue); parser.Token(out fieldName).Colon().FieldValue(out fieldValue).CarraigeReturnLineFeed();
if (parser.Success) if (parser.Success)
yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue); yield return new KeyValuePair<string, IEnumerable<byte>>(fieldName, fieldValue);
} }
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using PcapDotNet.Base;
namespace PcapDotNet.Packets.Http namespace PcapDotNet.Packets.Http
{ {
public class HttpField : IEquatable<HttpField> 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 string Name { get; private set; }
public ReadOnlyCollection<byte> Value { get; private set; }
public bool Equals(HttpField other) 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) public override bool Equals(object obj)
...@@ -40,12 +70,9 @@ namespace PcapDotNet.Packets.Http ...@@ -40,12 +70,9 @@ namespace PcapDotNet.Packets.Http
public override string ToString() public override string ToString()
{ {
return string.Format("{0}: ", Name); return string.Format("{0}: {1}", Name, _defaultEncoding.GetString(Value.ToArray()));
} }
internal HttpField(string name) private static readonly Encoding _defaultEncoding = Encoding.GetEncoding(28591);
{
Name = name;
}
} }
} }
\ No newline at end of file
...@@ -47,10 +47,10 @@ namespace PcapDotNet.Packets.Http ...@@ -47,10 +47,10 @@ namespace PcapDotNet.Packets.Http
mergedFields.Add(fieldName, fieldValue); mergedFields.Add(fieldName, fieldValue);
} }
else 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() public IEnumerator<HttpField> GetEnumerator()
......
...@@ -8,6 +8,11 @@ namespace PcapDotNet.Packets.Http ...@@ -8,6 +8,11 @@ namespace PcapDotNet.Packets.Http
{ {
internal class HttpParser internal class HttpParser
{ {
public HttpParser(byte[] buffer)
:this(buffer, 0, buffer.Length)
{
}
public HttpParser(byte[] buffer, int offset, int length) public HttpParser(byte[] buffer, int offset, int length)
{ {
_buffer = buffer; _buffer = buffer;
...@@ -43,31 +48,6 @@ namespace PcapDotNet.Packets.Http ...@@ -43,31 +48,6 @@ namespace PcapDotNet.Packets.Http
return this; 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() public HttpParser Colon()
{ {
return Bytes(AsciiBytes.Colon); return Bytes(AsciiBytes.Colon);
...@@ -83,8 +63,6 @@ namespace PcapDotNet.Packets.Http ...@@ -83,8 +63,6 @@ namespace PcapDotNet.Packets.Http
return Bytes(AsciiBytes.Space); return Bytes(AsciiBytes.Space);
} }
public HttpParser SkipLws() public HttpParser SkipLws()
{ {
while (true) while (true)
...@@ -150,17 +128,6 @@ namespace PcapDotNet.Packets.Http ...@@ -150,17 +128,6 @@ namespace PcapDotNet.Packets.Http
return this; 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) public HttpParser RequestUri(out string uri)
{ {
if (!Success) if (!Success)
...@@ -248,12 +215,57 @@ namespace PcapDotNet.Packets.Http ...@@ -248,12 +215,57 @@ namespace PcapDotNet.Packets.Http
return this; 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); reasonPhrase = new Datagram(_buffer, _offset, reasonPhraseLength);
_offset += reasonPhraseLength; _offset += reasonPhraseLength;
return this; 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 static readonly byte[] _httpSlash = Encoding.ASCII.GetBytes("HTTP/");
private readonly byte[] _buffer; 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