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/Request/HttpInputStream.cs29
-rw-r--r--lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs6
-rw-r--r--lib/Net.Http/src/Helpers/CoreBufferHelpers.cs71
-rw-r--r--lib/Net.Http/src/Helpers/InitDataBuffer.cs136
4 files changed, 170 insertions, 72 deletions
diff --git a/lib/Net.Http/src/Core/Request/HttpInputStream.cs b/lib/Net.Http/src/Core/Request/HttpInputStream.cs
index e210776..3b929af 100644
--- a/lib/Net.Http/src/Core/Request/HttpInputStream.cs
+++ b/lib/Net.Http/src/Core/Request/HttpInputStream.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Net.Http
@@ -46,15 +46,19 @@ namespace VNLib.Net.Http.Core
private Stream? InputStream;
private long _position;
- private ISlindingWindowBuffer<byte>? _initalData;
+ private InitDataBuffer? _initalData;
public HttpInputStream(Func<Stream> getTransport) => GetTransport = getTransport;
internal void OnComplete()
{
- //Dispose the inigial data buffer
- _initalData?.Close();
- _initalData = null;
+ //Dispose the initial data buffer if set
+ if (_initalData.HasValue)
+ {
+ _initalData.Value.Release();
+ _initalData = null;
+ }
+
//Remove stream cache copy
InputStream = null;
//Reset position
@@ -69,7 +73,7 @@ namespace VNLib.Net.Http.Core
/// </summary>
/// <param name="contentLength">The number of bytes to allow being read from the transport or initial buffer</param>
/// <param name="initial">Entity body data captured on initial read</param>
- internal void Prepare(long contentLength, ISlindingWindowBuffer<byte>? initial)
+ internal void Prepare(long contentLength, in InitDataBuffer? initial)
{
ContentLength = contentLength;
_initalData = initial;
@@ -102,10 +106,10 @@ namespace VNLib.Net.Http.Core
ForwardOnlyWriter<byte> writer = new(buffer[..bytesToRead]);
//See if all data is internally buffered
- if (_initalData != null && _initalData.AccumulatedSize > 0)
+ if (_initalData.HasValue && _initalData.Value.Remaining > 0)
{
//Read as much as possible from internal buffer
- ERRNO read = _initalData.Read(writer.Remaining);
+ ERRNO read = _initalData.Value.Read(writer.Remaining);
//Advance writer
writer.Advance(read);
@@ -134,6 +138,7 @@ namespace VNLib.Net.Http.Core
{
return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
}
+
public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
//Calculate the amount of data that can be read into the buffer
@@ -147,10 +152,10 @@ namespace VNLib.Net.Http.Core
ForwardOnlyMemoryWriter<byte> writer = new(buffer[..bytesToRead]);
//See if all data is internally buffered
- if (_initalData != null && _initalData.AccumulatedSize > 0)
+ if (_initalData.HasValue && _initalData.Value.Remaining > 0)
{
//Read as much as possible from internal buffer
- ERRNO read = _initalData.Read(writer.Remaining.Span);
+ ERRNO read = _initalData.Value.Read(writer.Remaining.Span);
//Advance writer
writer.Advance(read);
@@ -183,12 +188,14 @@ namespace VNLib.Net.Http.Core
public async ValueTask DiscardRemainingAsync(int maxBufferSize)
{
long remaining = Remaining;
+
if(remaining == 0)
{
return;
}
+
//See if all data has already been buffered
- if(_initalData != null && remaining <= _initalData.AccumulatedSize)
+ if(_initalData.HasValue && remaining <= _initalData.Value.Remaining)
{
//All data has been buffred, so just clear the buffer
_position = Length;
diff --git a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs
index f876528..f633a4a 100644
--- a/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs
+++ b/lib/Net.Http/src/Core/RequestParse/Http11ParseExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Net.Http
@@ -521,9 +521,9 @@ namespace VNLib.Net.Http.Core
else if (parseState.ContentLength > 0)
{
//Open a temp buffer to store initial data in
- ISlindingWindowBuffer<byte>? initData = reader.GetReminaingData(parseState.ContentLength);
+ InitDataBuffer? initData = reader.GetReminaingData(parseState.ContentLength);
//Setup the input stream and capture the initial data from the reader, and wrap the transport stream to read data directly
- Request.InputStream.Prepare(parseState.ContentLength, initData);
+ Request.InputStream.Prepare(parseState.ContentLength, in initData);
Request.HasEntityBody = true;
}
//Success!
diff --git a/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs b/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs
index 15c617c..719f4f8 100644
--- a/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs
+++ b/lib/Net.Http/src/Helpers/CoreBufferHelpers.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Net.Http
@@ -31,10 +31,7 @@
* allocations and help provide memory optimization.
*/
-
-
using System;
-using System.IO;
using System.Buffers;
using System.Security;
using System.Threading;
@@ -51,54 +48,11 @@ namespace VNLib.Net.Http.Core
/// </summary>
internal static class CoreBufferHelpers
{
- private sealed class InitDataBuffer : ISlindingWindowBuffer<byte>
- {
- private readonly ArrayPool<byte> pool;
- private readonly int size;
-
- private byte[]? buffer;
-
- public InitDataBuffer(ArrayPool<byte> pool, int size)
- {
- this.buffer = pool.Rent(size, true);
- this.pool = pool;
- this.size = size;
- WindowStartPos = 0;
- WindowEndPos = 0;
- }
-
- public int WindowStartPos { get; set; }
- public int WindowEndPos { get; set; }
- Memory<byte> ISlindingWindowBuffer<byte>.Buffer => buffer.AsMemory(0, size);
-
- public void Advance(int count)
- {
- WindowEndPos += count;
- }
-
- public void AdvanceStart(int count)
- {
- WindowStartPos += count;
- }
-
- public void Reset()
- {
- WindowStartPos = 0;
- WindowEndPos = 0;
- }
-
- //Release the buffer back to the pool
- void ISlindingWindowBuffer<byte>.Close()
- {
- pool.Return(buffer!);
- buffer = null;
- }
- }
-
/// <summary>
/// An internal HTTP character binary pool for HTTP specific internal buffers
/// </summary>
public static ArrayPool<byte> HttpBinBufferPool { get; } = ArrayPool<byte>.Create();
+
/// <summary>
/// An <see cref="IUnmangedHeap"/> used for internal HTTP buffers
/// </summary>
@@ -117,7 +71,7 @@ namespace VNLib.Net.Http.Core
public static UnsafeMemoryHandle<byte> GetBinBuffer(int size, bool zero)
{
//Calc buffer size to the nearest page size
- size = (size / 4096 + 1) * 4096;
+ size = (int)MemoryUtil.NearestPage(size);
//If rpmalloc lib is loaded, use it
if (MemoryUtil.IsRpMallocLoaded)
@@ -137,7 +91,7 @@ namespace VNLib.Net.Http.Core
public static IMemoryOwner<byte> GetMemory(int size, bool zero)
{
//Calc buffer size to the nearest page size
- size = (size / 4096 + 1) * 4096;
+ size = (int)MemoryUtil.NearestPage(size);
//If rpmalloc lib is loaded, use it
if (MemoryUtil.IsRpMallocLoaded)
@@ -167,7 +121,7 @@ namespace VNLib.Net.Http.Core
/// <param name="reader"></param>
/// <param name="maxContentLength">Maximum content size to clamp the remaining buffer window to</param>
/// <returns></returns>
- public static ISlindingWindowBuffer<byte>? GetReminaingData<T>(this ref T reader, long maxContentLength) where T: struct, IVnTextReader
+ public static InitDataBuffer? GetReminaingData<T>(this ref T reader, long maxContentLength) where T: struct, IVnTextReader
{
//clamp max available to max content length
int available = Math.Clamp(reader.Available, 0, (int)maxContentLength);
@@ -175,13 +129,14 @@ namespace VNLib.Net.Http.Core
{
return null;
}
- //Alloc sliding window buffer
- ISlindingWindowBuffer<byte> buffer = new InitDataBuffer(HttpBinBufferPool, available);
- //Read remaining data
- reader.ReadRemaining(buffer.RemainingBuffer.Span);
- //Advance the buffer to the end of available data
- buffer.Advance(available);
- return buffer;
+
+ //Creates the new initial data buffer
+ InitDataBuffer buf = InitDataBuffer.AllocBuffer(HttpBinBufferPool, available);
+
+ //Read remaining data into the buffer's data segment
+ _ = reader.ReadRemaining(buf.DataSegment);
+
+ return buf;
}
}
diff --git a/lib/Net.Http/src/Helpers/InitDataBuffer.cs b/lib/Net.Http/src/Helpers/InitDataBuffer.cs
new file mode 100644
index 0000000..36e1f11
--- /dev/null
+++ b/lib/Net.Http/src/Helpers/InitDataBuffer.cs
@@ -0,0 +1,136 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Net.Http
+* File: InitDataBuffer.cs
+*
+* InitDataBuffer.cs is part of VNLib.Net.Http which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Net.Http is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License as
+* published by the Free Software Foundation, either version 3 of the
+* License, or (at your option) any later version.
+*
+* VNLib.Net.Http is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see https://www.gnu.org/licenses/.
+*/
+
+using System;
+using System.Buffers;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using VNLib.Utils;
+using VNLib.Utils.Extensions;
+
+namespace VNLib.Net.Http.Core
+{
+ /// <summary>
+ /// A structure that buffers data remaining from an initial transport read. Stored
+ /// data will be read by copying.
+ /// </summary>
+ internal readonly struct InitDataBuffer
+ {
+ const int POSITION_SEG_SIZE = sizeof(int);
+
+ readonly int _dataSize;
+ readonly byte[] _buffer;
+ readonly ArrayPool<byte> _pool;
+
+
+ InitDataBuffer(ArrayPool<byte> pool, byte[] buffer, int size)
+ {
+ _pool = pool;
+ _buffer = buffer;
+ _dataSize = size;
+ }
+
+ /// <summary>
+ /// Allocates the correct size buffer for the given data size
+ /// </summary>
+ /// <param name="pool">The pool to allocate the array from</param>
+ /// <param name="dataSize">The size of the remaining data segment</param>
+ /// <returns>The newly allocated data buffer</returns>
+ internal static InitDataBuffer AllocBuffer(ArrayPool<byte> pool, int dataSize)
+ {
+ //Alloc buffer
+ byte[] buffer = pool.Rent(dataSize + POSITION_SEG_SIZE, true);
+
+ return new(pool, buffer, dataSize);
+ }
+
+ readonly Span<byte> _positionSegment
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _buffer.AsSpan(0, POSITION_SEG_SIZE);
+ }
+
+ /// <summary>
+ /// Gets the entire internal data segment to read/write data to/from
+ /// </summary>
+ internal readonly Span<byte> DataSegment
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _buffer.AsSpan(POSITION_SEG_SIZE, _dataSize);
+ }
+
+ private readonly int Position
+ {
+ //Reading/wriging the data buffe postion segment
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => MemoryMarshal.Read<int>(_positionSegment);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set => MemoryMarshal.Write(_positionSegment, ref value);
+ }
+
+ /// <summary>
+ /// Get the amount of data remaining in the data buffer
+ /// </summary>
+ internal readonly int Remaining
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => _dataSize - Position;
+ }
+
+ /// <summary>
+ /// Reads data from the internal buffer into the supplied buffer
+ /// </summary>
+ /// <param name="buffer">The buffer to write data to</param>
+ /// <returns>The number of bytes written to the output buffer</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly ERRNO Read(Span<byte> buffer)
+ {
+ //Calc how many bytes can be read into the output buffer
+ int bytesToRead = Math.Min(Remaining, buffer.Length);
+
+ Span<byte> btr = DataSegment[Position..bytesToRead];
+
+ //Write data to output buffer
+ btr.CopyTo(buffer);
+
+ //Update position pointer
+ Position += bytesToRead;
+
+ //Return the number of bytes read
+ return bytesToRead;
+ }
+
+ /// <summary>
+ /// Releases the internal buffer back to its pool
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal readonly void Release()
+ {
+ //Return buffer back to pool
+ _pool.Return(_buffer);
+ }
+ }
+}