aboutsummaryrefslogtreecommitdiff
path: root/lib/Net.Http/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Net.Http/src')
-rw-r--r--lib/Net.Http/src/Core/TransportReader.cs180
-rw-r--r--lib/Net.Http/src/Helpers/HelperTypes.cs6
-rw-r--r--lib/Net.Http/src/Helpers/HttpHelpers.cs108
3 files changed, 185 insertions, 109 deletions
diff --git a/lib/Net.Http/src/Core/TransportReader.cs b/lib/Net.Http/src/Core/TransportReader.cs
index 8d605d1..a512331 100644
--- a/lib/Net.Http/src/Core/TransportReader.cs
+++ b/lib/Net.Http/src/Core/TransportReader.cs
@@ -41,14 +41,7 @@ namespace VNLib.Net.Http.Core
/// </summary>
internal readonly struct TransportReader : IVnTextReader
{
- /*
- * To make this structure read-only we can store the
- * mutable values in a private segment of the internal
- * buffer. 8 bytes are reserved at the beining and an
- * additional word is added for padding incase small/wild
- * under/over run occurs.
- */
- const int PrivateBufferOffset = 4 * sizeof(int);
+ private readonly static int BufferPosStructSize = Unsafe.SizeOf<BufferPosition>();
///<inheritdoc/>
public readonly Encoding Encoding { get; }
@@ -58,26 +51,10 @@ namespace VNLib.Net.Http.Core
///<inheritdoc/>
public readonly Stream BaseStream { get; }
-
- /*
- * Store the window start/end in the begging of the
- * data buffer. Then use a constant offset to get the
- * start of the buffer
- */
- private readonly int BufWindowStart
- {
- get => MemoryMarshal.Read<int>(Buffer.GetBinSpan());
- set => MemoryMarshal.Write(Buffer.GetBinSpan(), ref value);
- }
-
- private readonly int BufWindowEnd
- {
- get => MemoryMarshal.Read<int>(Buffer.GetBinSpan()[sizeof(int)..]);
- set => MemoryMarshal.Write(Buffer.GetBinSpan()[sizeof(int)..], ref value);
- }
+
private readonly IHttpHeaderParseBuffer Buffer;
- private readonly int MAxBufferSize;
+ private readonly uint MaxBufferSize;
/// <summary>
/// Initializes a new <see cref="TransportReader"/> for reading text lines from the transport stream
@@ -92,78 +69,177 @@ namespace VNLib.Net.Http.Core
BaseStream = transport;
LineTermination = lineTermination;
Buffer = buffer;
- MAxBufferSize = buffer.BinSize - PrivateBufferOffset;
+ MaxBufferSize = (uint)(buffer.BinSize - BufferPosStructSize);
+
+ //Assign an zeroed position
+ BufferPosition position = default;
+ SetPosition(ref position);
- //Initialize the buffer window
- SafeZeroPrivateSegments(Buffer);
+ AssertZeroPosition();
+ }
+
+ [Conditional("DEBUG")]
+ private void AssertZeroPosition()
+ {
+ BufferPosition position = default;
+ GetPosition(ref position);
+ Debug.Assert(position.WindowStart == 0);
+ Debug.Assert(position.WindowEnd == 0);
+ }
- Debug.Assert(BufWindowEnd == 0 && BufWindowStart == 0);
+ /// <summary>
+ /// Reads the current position from the buffer segment
+ /// </summary>
+ /// <param name="position">A reference to the varable to write the position to</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private readonly void GetPosition(ref BufferPosition position)
+ {
+ //Get the beining of the segment and read the position
+ Span<byte> span = Buffer.GetBinSpan();
+ position = MemoryMarshal.Read<BufferPosition>(span);
}
/// <summary>
- /// Clears the initial window start/end values with the
- /// extra padding
+ /// Updates the current position in the buffer segment
/// </summary>
- /// <param name="buffer">The buffer segment to initialize</param>
- private static void SafeZeroPrivateSegments(IHttpHeaderParseBuffer buffer)
+ /// <param name="position"></param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private readonly void SetPosition(ref BufferPosition position)
{
- ref byte start = ref MemoryMarshal.GetReference(buffer.GetBinSpan());
- Unsafe.InitBlock(ref start, 0, PrivateBufferOffset);
+ //Store the position at the beining of the segment
+ Span<byte> span = Buffer.GetBinSpan();
+ MemoryMarshal.Write(span, ref position);
}
/// <summary>
/// Gets the data segment of the buffer after the private segment
/// </summary>
/// <returns></returns>
- private readonly Span<byte> GetDataSegment() => Buffer.GetBinSpan()[PrivateBufferOffset..];
+ private readonly Span<byte> GetDataSegment()
+ {
+ //Get the beining of the segment
+ Span<byte> span = Buffer.GetBinSpan();
+ //Return the segment after the private segment
+ return span[BufferPosStructSize..];
+ }
///<inheritdoc/>
- public readonly int Available => BufWindowEnd - BufWindowStart;
+ public readonly int Available
+ {
+ get
+ {
+ //Read position and return the window size
+ BufferPosition position = default;
+ GetPosition(ref position);
+ return (int)position.GetWindowSize();
+ }
+ }
///<inheritdoc/>
- public readonly Span<byte> BufferedDataWindow => GetDataSegment()[BufWindowStart..BufWindowEnd];
-
+ public readonly Span<byte> BufferedDataWindow
+ {
+ get
+ {
+ //Read current position and return the window
+ BufferPosition position = default;
+ GetPosition(ref position);
+ return GetDataSegment()[(int)position.WindowStart..(int)position.WindowEnd];
+ }
+ }
///<inheritdoc/>
- public readonly void Advance(int count) => BufWindowStart += count;
+ public readonly void Advance(int count)
+ {
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), "Count must be positive");
+ }
+
+ //read the current position
+ BufferPosition position = default;
+ GetPosition(ref position);
+
+ //Advance the window start by the count and set the position
+ position.AdvanceStart(count);
+ SetPosition(ref position);
+ }
///<inheritdoc/>
public readonly void FillBuffer()
{
+ //Read the current position
+ BufferPosition bufferPosition = default;
+ GetPosition(ref bufferPosition);
+
//Get a buffer from the end of the current window to the end of the buffer
- Span<byte> bufferWindow = GetDataSegment()[BufWindowEnd..];
+ Span<byte> bufferWindow = GetDataSegment()[(int)bufferPosition.WindowEnd..];
//Read from stream
int read = BaseStream.Read(bufferWindow);
+ Debug.Assert(read > -1, "Read should never be negative");
//Update the end of the buffer window to the end of the read data
- BufWindowEnd += read;
+ bufferPosition.AdvanceEnd(read);
+ SetPosition(ref bufferPosition);
}
///<inheritdoc/>
public readonly ERRNO CompactBufferWindow()
{
+ //Read the current position
+ BufferPosition bufferPosition = default;
+ GetPosition(ref bufferPosition);
+
//No data to compact if window is not shifted away from start
- if (BufWindowStart > 0)
+ if (bufferPosition.WindowStart > 0)
{
//Get span over engire buffer
Span<byte> buffer = GetDataSegment();
//Get used data segment within window
- Span<byte> usedData = buffer[BufWindowStart..BufWindowEnd];
+ Span<byte> usedData = buffer[(int)bufferPosition.WindowStart..(int)bufferPosition.WindowEnd];
//Copy remaining to the begining of the buffer
usedData.CopyTo(buffer);
-
- //Buffer window start is 0
- BufWindowStart = 0;
-
- //Buffer window end is now the remaining size
- BufWindowEnd = usedData.Length;
+
+ /*
+ * Now that data has been shifted, update the position to
+ * the new window and write the new position to the buffer
+ */
+ bufferPosition.Set(0, usedData.Length);
+ SetPosition(ref bufferPosition);
}
//Return the number of bytes of available space from the end of the current window
- return MAxBufferSize - BufWindowEnd;
+ return (nint)(MaxBufferSize - bufferPosition.WindowEnd);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ private record struct BufferPosition
+ {
+ public uint WindowStart;
+ public uint WindowEnd;
+
+ /// <summary>
+ /// Sets the the buffer window position
+ /// </summary>
+ /// <param name="start">Window start</param>
+ /// <param name="end">Window end</param>
+ public void Set(int start, int end)
+ {
+ //Verify that the start and end are not negative
+ Debug.Assert(start >= 0, "Negative internal value passed to http buffer window start");
+ Debug.Assert(end >= 0, "Negative internal value passed to http buffer window end");
+
+ WindowStart = (uint)start;
+ WindowEnd = (uint)end;
+ }
+
+ public readonly uint GetWindowSize() => WindowEnd - WindowStart;
+
+ public void AdvanceEnd(int count) => WindowEnd += (uint)count;
+
+ public void AdvanceStart(int count) => WindowStart += (uint)count;
}
}
}
diff --git a/lib/Net.Http/src/Helpers/HelperTypes.cs b/lib/Net.Http/src/Helpers/HelperTypes.cs
index 7e7e068..ce13874 100644
--- a/lib/Net.Http/src/Helpers/HelperTypes.cs
+++ b/lib/Net.Http/src/Helpers/HelperTypes.cs
@@ -87,7 +87,11 @@ namespace VNLib.Net.Http
/// <summary>
/// Http UNLOCK request method
/// </summary>
- UNLOCK = 0x1000
+ UNLOCK = 0x1000,
+ /// <summary>
+ /// Http LIST request method
+ /// </summary>
+ LIST = 0x2000
}
/// <summary>
diff --git a/lib/Net.Http/src/Helpers/HttpHelpers.cs b/lib/Net.Http/src/Helpers/HttpHelpers.cs
index d5c471f..198396f 100644
--- a/lib/Net.Http/src/Helpers/HttpHelpers.cs
+++ b/lib/Net.Http/src/Helpers/HttpHelpers.cs
@@ -29,7 +29,6 @@ using System.Net.Sockets;
using System.Collections.Generic;
using System.Text.RegularExpressions;
-using VNLib.Net.Http.Core;
using VNLib.Utils.Memory;
using VNLib.Utils.Extensions;
@@ -66,7 +65,7 @@ namespace VNLib.Net.Http
* an HttpMethod enum value,
*/
- private static readonly IReadOnlyDictionary<int, HttpMethod> MethodHashLookup;
+ private static readonly IReadOnlyDictionary<int, HttpMethod> MethodHashLookup = HashHttpMethods();
/*
* Provides a constant lookup table from an MIME http request header string to a .NET
@@ -128,8 +127,8 @@ namespace VNLib.Net.Http
* during request parsing)
*
*/
- private static readonly IReadOnlyDictionary<int, HttpRequestHeader> RequestHeaderHashLookup;
-
+ private static readonly IReadOnlyDictionary<int, HttpRequestHeader> RequestHeaderHashLookup = HashRequestHeaders();
+
/*
* Provides a constant lookup table for http version hashcodes to an http
* version enum value
@@ -143,68 +142,63 @@ namespace VNLib.Net.Http
};
- //Pre-compiled strings for all status codes for http 1, 1.1, and 2
- private static readonly IReadOnlyDictionary<HttpStatusCode, string> V1_STAUTS_CODES;
- private static readonly IReadOnlyDictionary<HttpStatusCode, string> V1_1_STATUS_CODES;
- private static readonly IReadOnlyDictionary<HttpStatusCode, string> V2_STAUTS_CODES;
+ //Pre-compiled strings for all status codes for http 0.9 1, 1.1
+ private static readonly IReadOnlyDictionary<HttpStatusCode, string> V0_9_STATUS_CODES = GetStatusCodes("0.9");
+ private static readonly IReadOnlyDictionary<HttpStatusCode, string> V1_STAUTS_CODES = GetStatusCodes("1.0");
+ private static readonly IReadOnlyDictionary<HttpStatusCode, string> V1_1_STATUS_CODES = GetStatusCodes("1.1");
+ private static readonly IReadOnlyDictionary<HttpStatusCode, string> V2_STATUS_CODES = GetStatusCodes("2.0");
- static HttpHelpers()
+ private static IReadOnlyDictionary<HttpStatusCode, string> GetStatusCodes(string version)
{
+ //Setup status code dict
+ Dictionary<HttpStatusCode, string> statusCodes = new();
+ //Get all status codes
+ foreach (HttpStatusCode code in Enum.GetValues<HttpStatusCode>())
{
- //Setup status code dict
- Dictionary<HttpStatusCode, string> v1status = new();
- Dictionary<HttpStatusCode, string> v11status = new();
- Dictionary<HttpStatusCode, string> v2status = new();
- //Get all status codes
- foreach (HttpStatusCode code in Enum.GetValues<HttpStatusCode>())
- {
- //Use a regex to write the status code value as a string
- v1status[code] = $"HTTP/1.0 {(int)code} {HttpRequestBuilderRegex.Replace(code.ToString(), " $1")}";
- v11status[code] = $"HTTP/1.1 {(int)code} {HttpRequestBuilderRegex.Replace(code.ToString(), " $1")}";
- v2status[code] = $"HTTP/2.0 {(int)code} {HttpRequestBuilderRegex.Replace(code.ToString(), " $1")}";
- }
- //Store as readonly
- V1_STAUTS_CODES = v1status;
- V1_1_STATUS_CODES = v11status;
- V2_STAUTS_CODES = v2status;
+ //Use a regex to write the status code value as a string
+ statusCodes[code] = $"HTTP/{version} {(int)code} {HttpRequestBuilderRegex.Replace(code.ToString(), " $1")}";
}
+ return statusCodes;
+ }
+
+ private static IReadOnlyDictionary<int, HttpMethod> HashHttpMethods()
+ {
+ /*
+ * Http methods are hashed at runtime using the HttpMethod enum
+ * values, purley for compatability and automation
+ */
+ Dictionary<int, HttpMethod> methods = new();
+ //Add all HTTP methods
+ foreach (HttpMethod method in Enum.GetValues<HttpMethod>())
{
- /*
- * Http methods are hashed at runtime using the HttpMethod enum
- * values, purley for compatability and automation
- */
- Dictionary<int, HttpMethod> methods = new();
- //Add all HTTP methods
- foreach (HttpMethod method in Enum.GetValues<HttpMethod>())
+ //Exclude the not supported method
+ if (method == HttpMethod.None)
{
- //Exclude the not supported method
- if (method == HttpMethod.None)
- {
- continue;
- }
- //Store method string's hashcode for faster lookups
- methods[string.GetHashCode(method.ToString(), StringComparison.OrdinalIgnoreCase)] = method;
+ continue;
}
- MethodHashLookup = methods;
+ //Store method string's hashcode for faster lookups
+ methods[string.GetHashCode(method.ToString(), StringComparison.OrdinalIgnoreCase)] = method;
}
- {
- /*
- * Pre-compute common headers
- */
- Dictionary<int, HttpRequestHeader> requestHeaderHashes = new();
+ return methods;
+ }
- //Add all HTTP methods
- foreach (string headerValue in RequestHeaderLookup.Keys)
- {
- //Compute the hashcode for the header value
- int hashCode = string.GetHashCode(headerValue, StringComparison.OrdinalIgnoreCase);
- //Store the http header enum value with the hash-code of the string of said header
- requestHeaderHashes[hashCode] = RequestHeaderLookup[headerValue];
- }
+ private static IReadOnlyDictionary<int, HttpRequestHeader> HashRequestHeaders()
+ {
+ /*
+ * Pre-compute common headers
+ */
+ Dictionary<int, HttpRequestHeader> requestHeaderHashes = new();
- RequestHeaderHashLookup = requestHeaderHashes;
+ //Add all HTTP methods
+ foreach (string headerValue in RequestHeaderLookup.Keys)
+ {
+ //Compute the hashcode for the header value
+ int hashCode = string.GetHashCode(headerValue, StringComparison.OrdinalIgnoreCase);
+ //Store the http header enum value with the hash-code of the string of said header
+ requestHeaderHashes[hashCode] = RequestHeaderLookup[headerValue];
}
- }
+ return requestHeaderHashes;
+ }
/// <summary>
@@ -358,8 +352,10 @@ namespace VNLib.Net.Http
{
return version switch
{
+ HttpVersion.Http09 => V0_9_STATUS_CODES[code],
HttpVersion.Http1 => V1_STAUTS_CODES[code],
- HttpVersion.Http2 => V2_STAUTS_CODES[code],
+ HttpVersion.Http2 => V2_STATUS_CODES[code],
+ //Default to HTTP/1.1
_ => V1_1_STATUS_CODES[code],
};
}