Bug 30686

Summary: ZipArchive ctor throws InvalidDataException for WebConnectionStream
Product: [Mono] Class Libraries Reporter: Nate McMaster <nate.mcmaster>
Component: GeneralAssignee: João Matos <joao.matos>
Status: RESOLVED FIXED    
Severity: normal CC: martin, masafa, mono-bugs+mono, pamorris
Priority: ---    
Version: 4.0.0   
Target Milestone: Untriaged   
Hardware: Macintosh   
OS: Mac OS   
URL: https://gist.github.com/natemcmaster/f551808a45873ab2ab01
Tags: Is this bug a regression?: ---
Last known good build:
Attachments: Reproduction of bug

Description Nate McMaster 2015-06-02 15:10:19 UTC
Created attachment 11436 [details]
Reproduction of bug

The constructor for System.IO.Compression.ZipArchive(Stream) does not work with the result of HttpClient.GetStreamAsync.Result (which returns System.Net.WebConnectionStream).

Instead, it throws System.IO.InvalidDataException with the message "The contents of the stream are not in the zip archive format."
Comment 1 Marek Safar 2015-06-03 11:45:57 UTC
This is System.IO.Compression bug which requires Seek capability for zip read mode whereas .net requires CanRead only.
Comment 2 Martin Lantzsch 2015-06-15 17:15:28 UTC
Same bug here for System.Net.FtpDataStream (CanSeek=false, CanRead=true) on Linux. So I just got the mono source and compiled my app against the System.IO.Compression.dll and .mdb. The inner exception which was before "null" is now the following System.ArgumentException from the SharpCompress library:

    "Archive streams must be Readable and Seekable"

The method in SharpCompress's AbstractArchive class:

    private static Stream CheckStreams(Stream stream)
    {
        if (!stream.CanSeek || !stream.CanRead)
        {
           throw new ArgumentException("Archive streams must be Readable and Seekable");
        }
        return stream;
    }

The bug seems to sit in sharpcompress which is testing all input streams against this method. Funnily they write in their readme: "The major feature is support for non-seekable streams so large files can be processed on the fly (i.e. download stream)." (https://github.com/adamhathcock/sharpcompress)
Comment 3 João Matos 2015-06-15 17:50:26 UTC
I've tried to look into this bug last week.

If you remove those checks then the stream will fail when SharpCompress tries to seek to the end, to read the Zip header. 

A simple fix would be to download the whole stream first into memory and pass it for further handling but that feels like an hack.

Another solution would be try to use their ReaderFactory API which I think is the one that supports streaming but I didn't look if it would be possible to implement the whole .NET API with it.
Comment 4 Paul Morris 2016-03-18 19:28:39 UTC
I am getting this exception when opening a stream that I have fully loaded into memory. So my unit test looks like this:

Assert.True(stream.CanRead);
Assert.True(stream.CanSeek);
Assert.Equal(36085092, stream.Length);

var zip = new ZipArchive(stream);

The first three assertions work fine. The Length is correct. The final line throws the "ArgumentException: Archive streams must be Readable and Seekable". This one has me baffled.
Comment 5 João Matos 2016-03-18 22:07:09 UTC
Do you have a reproduction case I could test with?
Comment 6 Paul Morris 2016-03-19 12:47:33 UTC
Ok I will try to put together a a simple app to isolate the issue and post it here. Thank you.
Comment 7 Paul Morris 2016-03-19 23:05:38 UTC
Sorry for the trouble. I was dealing with a zip within a zip and the inner zipped folder was not being wrapped in a seekable stream on Mono. Working now. It would be nice of course if the Mono (SharpCompress) implementation behaved as .NET does, not require a seekable stream. Thanks for your prompt response by the way.
Comment 8 Martin Lantzsch 2016-03-21 19:58:33 UTC
Hello, I still have this problem and created a application for you to simply reproduce this issue: https://github.com/LinuxDoku/mono-bug-30686/blob/master/mono-bug-30686/Program.cs

This application works on my windows 10 machine with .NET Framework 4.5, but not with the latest mono version on ubuntu: "Stable 4.2.3.4/832de4b".

In the app above I faked the "CanSeek" property which is set to "false", equal to the behaviour when you are receiving the data from "FtpDataStream".
Comment 9 João Matos 2016-06-24 00:31:37 UTC
Looking into this.
Comment 10 João Matos 2016-07-02 01:10:31 UTC
https://github.com/mono/mono/pull/3234