diff --git a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs index 1cc115e4..62e4760b 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs @@ -15,9 +15,10 @@ public class GZipArchiveEntry : GZipEntry, IArchiveEntry { //this is to reset the stream to be read multiple times var part = (GZipFilePart)Parts.Single(); - if (part.GetRawStream().Position != part.EntryStartPosition) + var rawStream = part.GetRawStream(); + if (rawStream.CanSeek && rawStream.Position != part.EntryStartPosition) { - part.GetRawStream().Position = part.EntryStartPosition; + rawStream.Position = part.EntryStartPosition; } return Parts.Single().GetCompressedStream().NotNull(); } diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index 4a1c9515..d3c614f9 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -24,8 +24,12 @@ internal sealed class GZipFilePart : FilePart stream.Position = stream.Length - 8; ReadTrailer(); stream.Position = position; + EntryStartPosition = position; + } + else + { + EntryStartPosition = 0; } - EntryStartPosition = stream.Position; } internal long EntryStartPosition { get; } diff --git a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs index 6a2b9795..c8a87327 100644 --- a/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs +++ b/tests/SharpCompress.Test/GZip/GZipArchiveTests.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Linq; using SharpCompress.Archives; @@ -124,4 +125,60 @@ public class GZipArchiveTests : ArchiveTests using var archive = GZipArchive.Open(stream); Assert.Equal(archive.Type, ArchiveType.GZip); } + + [Fact] + public void GZip_Archive_NonSeekableStream() + { + // Test that GZip extraction works with non-seekable streams (like HttpBaseStream) + using var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")); + var buffer = new MemoryStream(); + fileStream.CopyTo(buffer); + buffer.Position = 0; + + // Create a non-seekable wrapper around the MemoryStream + using var nonSeekableStream = new NonSeekableStream(buffer); + using var reader = SharpCompress.Readers.GZip.GZipReader.Open(nonSeekableStream); + + // Verify we can move to the first entry and read it without exceptions + Assert.True(reader.MoveToNextEntry()); + Assert.NotNull(reader.Entry); + + // Extract and verify the entry can be read + using var outputStream = new MemoryStream(); + reader.WriteEntryTo(outputStream); + + Assert.True(outputStream.Length > 0); + } + + // Helper class to simulate a non-seekable stream like HttpBaseStream + private class NonSeekableStream : Stream + { + private readonly Stream _baseStream; + + public NonSeekableStream(Stream baseStream) => _baseStream = baseStream; + + public override bool CanRead => _baseStream.CanRead; + public override bool CanSeek => false; // Simulate non-seekable stream + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override void Flush() => _baseStream.Flush(); + + public override int Read(byte[] buffer, int offset, int count) => + _baseStream.Read(buffer, offset, count); + + public override long Seek(long offset, SeekOrigin origin) => + throw new NotSupportedException(); + + public override void SetLength(long value) => throw new NotSupportedException(); + + public override void Write(byte[] buffer, int offset, int count) => + throw new NotSupportedException(); + } }