refactor(inboundapi): pool the request audit buffer + reset Position in finally

This commit is contained in:
Joseph Doherty
2026-05-23 09:46:53 -04:00
parent e567eb334c
commit e6ccee1a16

View File

@@ -1,3 +1,4 @@
using System.Buffers;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
@@ -279,14 +280,18 @@ public sealed class AuditWriteMiddleware
return (null, false);
}
// Read AT MOST cap + 1 bytes — the extra byte tells us the body was
// over the cap without forcing us to allocate the whole payload. Rent
// the scratch buffer from the shared ArrayPool so we don't allocate
// (and immediately discard) `cap + 1` bytes per request — the pool
// may hand back a buffer LARGER than `limit`, so we treat `limit`
// (not `buffer.Length`) as the read ceiling.
var limit = capBytes + 1;
var buffer = ArrayPool<byte>.Shared.Rent(limit);
try
{
request.Body.Position = 0;
// Read AT MOST cap + 1 bytes — the extra byte tells us the body was
// over the cap without forcing us to allocate the whole payload.
var limit = capBytes + 1;
var buffer = new byte[limit];
var total = 0;
while (total < limit)
{
@@ -299,7 +304,6 @@ public sealed class AuditWriteMiddleware
}
total += read;
}
request.Body.Position = 0;
if (total == 0)
{
@@ -318,6 +322,15 @@ public sealed class AuditWriteMiddleware
// outcome.
return (null, false);
}
finally
{
// Even on a thrown read, the downstream handler must see the full
// body from position 0 — never let a failed audit copy leak a
// truncated view. A rewind failure is swallowed: best-effort,
// same philosophy as the rest of the file.
try { request.Body.Position = 0; } catch { /* swallow */ }
ArrayPool<byte>.Shared.Return(buffer);
}
}
/// <summary>