diff --git a/src/NzbDrone.Api/Extensions/Pipelines/GZipPipeline.cs b/src/NzbDrone.Api/Extensions/Pipelines/GZipPipeline.cs index 8aa9f4ad2..2366b80ac 100644 --- a/src/NzbDrone.Api/Extensions/Pipelines/GZipPipeline.cs +++ b/src/NzbDrone.Api/Extensions/Pipelines/GZipPipeline.cs @@ -5,6 +5,7 @@ using System.Linq; using Nancy; using Nancy.Bootstrapper; using NLog; +using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; namespace NzbDrone.Api.Extensions.Pipelines @@ -15,9 +16,14 @@ namespace NzbDrone.Api.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 = PlatformInfo.IsMono ? WriteGZipStreamMono : (Action, Stream>)WriteGZipStream; } public void Register(IPipelines pipelines) @@ -43,14 +49,7 @@ namespace NzbDrone.Api.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 +60,25 @@ namespace NzbDrone.Api.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"); diff --git a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs index 01b345f9a..d3ae7bbdb 100644 --- a/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs +++ b/src/NzbDrone.Common/Http/Dispatchers/ManagedHttpDispatcher.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.IO.Compression; using System.Net; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; @@ -26,11 +28,20 @@ namespace NzbDrone.Common.Http.Dispatchers { webRequest = (HttpWebRequest) WebRequest.Create((Uri) request.Url); - // Deflate is not a standard and could break depending on implementation. - // we should just stick with the more compatible Gzip - //http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net - webRequest.AutomaticDecompression = DecompressionMethods.GZip; - + if (PlatformInfo.IsMono) + { + // On Mono GZipStream/DeflateStream leaks memory if an exception is thrown, use an intermediate buffer in that case. + webRequest.AutomaticDecompression = DecompressionMethods.None; + webRequest.Headers.Add("Accept-Encoding", "gzip"); + } + else + { + // Deflate is not a standard and could break depending on implementation. + // we should just stick with the more compatible Gzip + //http://stackoverflow.com/questions/8490718/how-to-decompress-stream-deflated-with-java-util-zip-deflater-in-net + webRequest.AutomaticDecompression = DecompressionMethods.GZip; + } + webRequest.Method = request.Method.ToString(); webRequest.UserAgent = request.UseSimplifiedUserAgent ? UserAgentBuilder.UserAgentSimplified @@ -86,6 +97,19 @@ namespace NzbDrone.Common.Http.Dispatchers if (responseStream != null) { data = responseStream.ToBytes(); + + if (PlatformInfo.IsMono && httpWebResponse.ContentEncoding == "gzip") + { + using (var compressedStream = new MemoryStream(data)) + using (var gzip = new GZipStream(compressedStream, CompressionMode.Decompress)) + using (var decompressedStream = new MemoryStream()) + { + gzip.CopyTo(decompressedStream); + data = decompressedStream.ToArray(); + } + + httpWebResponse.Headers.Remove("Content-Encoding"); + } } }