fix: move LogInformation inside disposal guard in both GetTrack streaming arms

Prevents an OS handle leak if the logger throws after the FileStream is opened but before File() takes ownership. Also corrects a stale "finally block" comment in the lossless arm — it has always been a catch.
This commit is contained in:
daniel-c-harvey
2026-06-26 15:16:29 -04:00
parent d72263aea1
commit afa862a67b
+7 -9
View File
@@ -741,7 +741,7 @@ public class TrackController : ControllerBase
}
// Resolve MIME and log before handing the stream to File().
// If anything here throws, the finally block disposes the wrapper
// If anything here throws, the catch block disposes the wrapper
// (and its inner FileStream) so neither leaks. On the success path
// File() takes ownership of the inner stream; ASP.NET Core disposes
// it after the response body is sent. The wrapper is a thin struct
@@ -755,16 +755,15 @@ public class TrackController : ControllerBase
streamMimeType = MimeTypeExtensions.GetMimeType(mediaStream.Extension);
streamLength = mediaStream.Stream.Length;
innerStream = mediaStream.Stream;
_logger.LogInformation(
"Streaming track from disk: {TrackId}, Size: {Size} bytes",
trackId, streamLength);
}
catch
{
await mediaStream.DisposeAsync();
throw;
}
_logger.LogInformation(
"Streaming track from disk: {TrackId}, Size: {Size} bytes",
trackId, streamLength);
// enableRangeProcessing: true — seek is served by HTTP Range requests.
// The FileStream is seekable, so ASP.NET Core honours an incoming
// Range header by slicing the file and responding 206 Partial Content.
@@ -808,6 +807,9 @@ public class TrackController : ControllerBase
// Length from the seekable FileStream — a metadata read, not a body load.
streamLength = resolved.Stream.Length;
innerStream = resolved.Stream;
_logger.LogInformation(
"Streaming track {TrackId} as {Format} ({Size} bytes, {ContentType})",
trackId, resolved.ResolvedFormat, streamLength, contentType);
}
catch
{
@@ -815,10 +817,6 @@ public class TrackController : ControllerBase
throw;
}
_logger.LogInformation(
"Streaming track {TrackId} as {Format} ({Size} bytes, {ContentType})",
trackId, resolved.ResolvedFormat, streamLength, contentType);
return File(innerStream, contentType, enableRangeProcessing: true);
}
catch (Exception ex)