Returning Stream from within Services return empty content after lib upgrade to 4.5.14

We just upgraded all of our libs within our Project and so did we upgraded ServiceStack from 4.5.6 - 4.5.14.

There are many services in which we are just directly returning Stream content, like File, Images, Pdf etc.

Everything was working fine until after once we upgraded the lib, we noticed that from our client when we hit the endpoint nothing would get return from the service. Which did use to work before the upgrade. A little experimenting with fiddler we find out that the service is returning Content-Size:0 and Body being empty.

Then I looked back at the stream and thought maybe its with the Position that probably you were resetting the position before but not anymore and so I thought of resetting stream.Position = 0 which surprisingly worked.

Ex.

var stream = new MemoryStream();

stream.Write(list.ToFormattedCsv());

// This doesn't work(meaning that service doesn't seem to write to response output stream) unless I do 
//__stream.Position = 0__
// or 
// __ stream.ToBytes()__
// or 
return stream;

So, Now I couldn’t find the reason as to why this is happening, since it was much flexible and smoother returning the stream.

Also, I tried to look over in the SS source code to see and convince my self with that but wasn’t able to truly find it.

Is there anything am I missing or there has been a change made to it?

Also, I thought of returning the bytes but still was wanting to find out why the Streams won’t work anymore …?

Any help would be greatly appreciated. Thanks

You’ll no longer need to reset the Stream position from this commit which is available from v5.0.1 that’s now available on MyGet.

Hi Mythz,

I really appreciate the quick response and the fix.

However, I was anticipating the fix to happen on pre v5 release(like a patch on 4.5).

We would love to upgrade to v5 but we are not any close to upgrading it to 5 because it may require us work to fix any breaking changes. Plus we just released our branch that was totally devoted to lib/packages within our project both the client/service side.

So, is it possible if you could make the patch on the 4.5 as well?

Again,
Thank you

Releases are and have always been forward only where fixes are always applied to the latest/current version which is what gets deployed to NuGet. You can just reset the stream position yourself (which is all this fix does) if you don’t want to upgrade to v5.

Hi Mythz,

Just a follow up on this, so as you remember after the upgrading lib. to 4.5.14 returning Stream directly from services, returned nothing in the body.

So to fix that, I had the Position set manually at my end. Good so far and worked just fine, for memory streams.

However, I have a few more of my services that talks to RavenDb(if you may have heard of NoSql Db) and retrieves the File content as GZipStream, note the file themselves aren’t zipped by any means they just get wrapped inside GZipStream.

private async Task<object> GetFile(string fileId)
{
     var stream = await Session.DownloadAsync(fileId)
     // Response.AddHeader() -- Content-Type, Content-Disposition etc
     return stream; // note: stream = GZipStream
}

The issue I am having is that when I return that from the Service ServiceStack returns nothing 0 content-length and empty content and not sure what is going wrong as this piece was working perfectly fine in 4.5.6.

Let me know, if you need more info. from my side.

Is this still an issue with the latest v5.0.2 of ServiceStack? If it is can you provide a stand alone repro that worked before but isn’t working now.

Ok Mythz,

Here you go https://github.com/segfault-ed/servicestack-stream-download.

The repo contains 2 folders. (everything is in Global.asax file for both)

  1. SS-FileDownload-Working - 4.5.6 (SS)
  2. SS-FileDownload-Non-Working - 5.0.2 (SS-Latest)

I had pointed my IIS to both the folders with Anonymous Disabled and Windows Auth enabled.

The endpoint to hit is http://localhost:[port]/s/download-adhoc-file

  1. Should be able to download the Test Pdf file
  2. This will error out saying set_position or something called on the GZipStream

I have removed my Raven dependency which does the following … so I have to replicate that here
It returns

  • GZipStream on the
    • BufferedStream

Hope this helps, let me know of anything else.

Thanks

I’m still tracking down support for naked GZipStream return types, but I want to show a more working optimal implementation where you return the compressed bytes if the Client supports it or the original uncompressed file if not:

[Route("/gzip/{FileName}")]
public class DownloadGzipFile : IReturn<byte[]>
{
    public string FileName { get; set; }
}
public class DocumentServices : Service
{
    public object Get(DownloadGzipFile request)
    {
        var name = request.FileName;
        var filePath = HostContext.AppHost.MapProjectPath("~/img/" + name);
        if (Request.RequestPreferences.AcceptsGzip)
        {
            var targetPath = string.Concat(filePath, ".gz");
            Compress(filePath, targetPath);
            return new HttpResult(new FileInfo(targetPath)) {
                Headers = {
                  { HttpHeaders.ContentDisposition, "attachment; filename=" + name },
                  { HttpHeaders.ContentEncoding, CompressionTypes.GZip}
                }
            };
        }

        return new HttpResult(filePath) {
            Headers = {
              { HttpHeaders.ContentDisposition, "attachment; filename=" + name },
            }
        };
    }
}

Where Compress uses your same impl:

private void Compress(string readFrom, string writeTo)
{
    byte[] b;
    using (var f = new FileStream(readFrom, FileMode.Open))
    {
        b = new byte[f.Length];
        f.Read(b, 0, (int)f.Length);
    }

    using (var fs = new FileStream(writeTo, FileMode.OpenOrCreate))
    using (var gz = new GZipStream(fs, CompressionMode.Compress, false))
    {
        gz.Write(b, 0, b.Length);
    }
}

Basically avoiding any decompression overhead on the server and taking advantage of compression when the client supports it.

Should now be fixed with this commit, this change is available from v5.0.3 that’s now available on MyGet.

Yes its working now.

Thanks a lot.

1 Like