From 40b34dbb5e452bbf37b2c7b930151fde2147831a Mon Sep 17 00:00:00 2001 From: Taloth Saldono Date: Tue, 8 Jan 2019 20:34:35 +0100 Subject: [PATCH] Do custom gzip compression on mono when returning api responses to avoid a memory leak. --- .../Extensions/Pipelines/GZipPipeline.cs | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/Sonarr.Http/Extensions/Pipelines/GZipPipeline.cs b/src/Sonarr.Http/Extensions/Pipelines/GZipPipeline.cs index d7ff3f3d4..938393019 100644 --- a/src/Sonarr.Http/Extensions/Pipelines/GZipPipeline.cs +++ b/src/Sonarr.Http/Extensions/Pipelines/GZipPipeline.cs @@ -15,9 +15,14 @@ namespace Sonarr.Http.Extensions.Pipelines public int Order => 0; + private readonly Action, Stream> _writeGZipStream; + public GzipCompressionPipeline(Logger logger) { _logger = logger; + + // On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case. + _writeGZipStream = NzbDrone.Common.EnvironmentInfo.PlatformInfo.IsMono ? WriteGZipStreamMono : (Action, Stream>)WriteGZipStream; } public void Register(IPipelines pipelines) @@ -43,14 +48,7 @@ namespace Sonarr.Http.Extensions.Pipelines var contents = response.Contents; response.Headers["Content-Encoding"] = "gzip"; - response.Contents = responseStream => - { - using (var gzip = new GZipStream(responseStream, CompressionMode.Compress, true)) - using (var buffered = new BufferedStream(gzip, 8192)) - { - contents.Invoke(buffered); - } - }; + response.Contents = responseStream => _writeGZipStream(contents, responseStream); } } @@ -61,6 +59,25 @@ namespace Sonarr.Http.Extensions.Pipelines } } + private static void WriteGZipStreamMono(Action innerContent, Stream targetStream) + { + using (var membuffer = new MemoryStream()) + { + WriteGZipStream(innerContent, membuffer); + membuffer.Position = 0; + membuffer.CopyTo(targetStream); + } + } + + private static void WriteGZipStream(Action innerContent, Stream targetStream) + { + using (var gzip = new GZipStream(targetStream, CompressionMode.Compress, true)) + using (var buffered = new BufferedStream(gzip, 8192)) + { + innerContent.Invoke(buffered); + } + } + private static bool ContentLengthIsTooSmall(Response response) { var contentLength = response.Headers.GetValueOrDefault("Content-Length");