Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/SharpCompress/Archives/GZip/GZipArchiveEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ public virtual Stream OpenEntryStream()
{
//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();
}
Expand Down
8 changes: 7 additions & 1 deletion src/SharpCompress/Common/GZip/GZipFilePart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ internal GZipFilePart(Stream stream, ArchiveEncoding archiveEncoding)
stream.Position = stream.Length - 8;
ReadTrailer();
stream.Position = position;
EntryStartPosition = position;
}
else
{
// For non-seekable streams, we can't read the trailer or track position.
// Set to 0 since the stream will be read sequentially from its current position.
EntryStartPosition = 0;
}
EntryStartPosition = stream.Position;
}

internal long EntryStartPosition { get; }
Expand Down
57 changes: 57 additions & 0 deletions tests/SharpCompress.Test/GZip/GZipArchiveTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
Expand Down Expand Up @@ -124,4 +125,60 @@ public void TestGzArchiveTypeGzip()
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();
}
}