aboutsummaryrefslogtreecommitdiff
path: root/lib/Plugins.Essentials/src/HttpEntity.cs
diff options
context:
space:
mode:
authorLibravatar vnugent <public@vaughnnugent.com>2024-06-26 21:01:15 -0400
committerLibravatar vnugent <public@vaughnnugent.com>2024-06-26 21:01:15 -0400
commit12391e9a207b60b41a074600fc2373ad3eb1c3ab (patch)
tree5a3396a889a2226aaabdf9aee5cd7cb4f5b04e31 /lib/Plugins.Essentials/src/HttpEntity.cs
parent92e182ceaf843f8d859d38faa8b2c0ff53207ff6 (diff)
feat(server): Server arch update, Memory struct access
Diffstat (limited to 'lib/Plugins.Essentials/src/HttpEntity.cs')
-rw-r--r--lib/Plugins.Essentials/src/HttpEntity.cs75
1 files changed, 74 insertions, 1 deletions
diff --git a/lib/Plugins.Essentials/src/HttpEntity.cs b/lib/Plugins.Essentials/src/HttpEntity.cs
index ff728e3..a4788a3 100644
--- a/lib/Plugins.Essentials/src/HttpEntity.cs
+++ b/lib/Plugins.Essentials/src/HttpEntity.cs
@@ -30,7 +30,9 @@ using System.Diagnostics;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
+using VNLib.Utils.IO;
using VNLib.Net.Http;
+using VNLib.Plugins.Essentials.Content;
using VNLib.Plugins.Essentials.Sessions;
using VNLib.Plugins.Essentials.Extensions;
@@ -220,7 +222,41 @@ namespace VNLib.Plugins.Essentials
Entity.CloseResponse(
code,
type,
- new MemStreamWrapper(ms, (int)length)
+ entity: new MemStreamWrapper(ms, (int)length)
+ );
+
+ return;
+ }
+
+ /*
+ * Readonly vn streams can also use a shortcut to avoid http buffer allocation and
+ * async streaming. This is done by wrapping the stream in a memory response reader
+ *
+ * Allocating a memory manager requires that the stream is readonly
+ */
+ if (stream is VnMemoryStream vms && length < int.MaxValue)
+ {
+ Entity.CloseResponse(
+ code,
+ type,
+ entity: new VnStreamWrapper(vms, (int)length)
+ );
+
+ return;
+ }
+
+ /*
+ * Files can have a bit more performance using the RandomAccess library when reading
+ * sequential segments without buffering. It avoids a user-space copy and async reading
+ * performance without the file being opened as async.
+ */
+ if(stream is FileStream fs)
+ {
+ Entity.CloseResponse(
+ code,
+ type,
+ entity: new DirectFileStream(fs.SafeFileHandle),
+ length
);
return;
@@ -270,6 +306,40 @@ namespace VNLib.Plugins.Essentials
void IHttpEvent.DangerousChangeProtocol(IAlternateProtocol protocolHandler) => Entity.DangerousChangeProtocol(protocolHandler);
+ private sealed class VnStreamWrapper(VnMemoryStream memStream, int length) : IMemoryResponseReader
+ {
+ //Store memory buffer, causes an internal allocation, so avoid calling mutliple times
+ readonly ReadOnlyMemory<byte> _memory = memStream.AsMemory();
+
+ readonly int length = length;
+
+ /*
+ * Stream may be offset by the caller, it needs
+ * to be respected during streaming.
+ */
+ int read = (int)memStream.Position;
+
+ ///<inheritdoc/>
+ public int Remaining
+ {
+ get
+ {
+ Debug.Assert(length - read >= 0);
+ return length - read;
+ }
+ }
+
+ ///<inheritdoc/>
+ public void Advance(int written) => read += written;
+
+ ///<inheritdoc/>
+ public void Close() => memStream.Dispose();
+
+ ///<inheritdoc/>
+ public ReadOnlyMemory<byte> GetMemory() => _memory.Slice(read, Remaining);
+ }
+
+
private sealed class MemStreamWrapper(MemoryStream memStream, int length) : IMemoryResponseReader
{
readonly int length = length;
@@ -280,6 +350,7 @@ namespace VNLib.Plugins.Essentials
*/
int read = (int)memStream.Position;
+ ///<inheritdoc/>
public int Remaining
{
get
@@ -289,11 +360,13 @@ namespace VNLib.Plugins.Essentials
}
}
+ ///<inheritdoc/>
public void Advance(int written) => read += written;
///<inheritdoc/>
public void Close() => memStream.Dispose();
+ ///<inheritdoc/>
public ReadOnlyMemory<byte> GetMemory()
{
byte[] intBuffer = memStream.GetBuffer();