Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compress http request on the fly and without loading compressed buffer in memory

I need to send voluminous data in a http post request to a server supporting gziped encoded requests.

Starting from a simple

public async Task<string> DoPost(HttpContent content)
{
  HttpClient client = new HttpClient();
  HttpResponseMessage response = await client.PostAsync("http://myUri", content);

  response.EnsureSuccessStatusCode();
  return await response.Content.ReadAsStringAsync();
}

I've just added a pre compression

public async Task<string> DoPost(HttpContent content, bool compress)
{
  if (compress) 
    content= await CompressAsync(content);

  return await DoPost(content);
}

private static async Task<StreamContent> CompressAsync(HttpContent content)
{
  MemoryStream ms = new MemoryStream();
  using (GZipStream gzipStream = new GZipStream(ms, CompressionMode.Compress, true))
  {
    await content.CopyToAsync(gzipStream);
    await gzipStream.FlushAsync();
  }

  ms.Position = 0;
  StreamContent compressedStreamContent = new StreamContent(ms);
  compressedStreamContent.Headers.ContentType = content.Headers.ContentType;
  compressedStreamContent.Headers.Add("Content-Encoding", "gzip");

  return compressedStreamContent;
}

It works perfectly but compress data are completly loaded into memory before sending request. I would like to be able to compress data on the fly during sending in a streaming way.

To do it, I've tried following code:

private static async Task<HttpContent> CompressAsync2(HttpContent content)
{
  PushStreamContent pushStreamContent = new PushStreamContent(async (stream, content2, transport) =>
  {
    using (GZipStream gzipStream = new GZipStream(stream, CompressionMode.Compress, true))
    {
      try
      {
        await content.CopyToAsync(gzipStream);
        await gzipStream.FlushAsync();
      }
      catch (Exception exception)
      {
        throw;
      }
    }
  });
  pushStreamContent.Headers.ContentType = content.Headers.ContentType;
  pushStreamContent.Headers.Add("Content-Encoding", "gzip");

  return pushStreamContent;
}

but it never goes out of CopyToAsync(gzipStream). FlushAsync is never executed and no exception is thrown and Fiddler don't see any post started.

My questions are:

  • Why CompressAsync2 doesn't work?
  • How to compress on the fly during sending and without loading compressed buffer in memory?

Any help would be greatly appreciated.

like image 943
MuiBienCarlota Avatar asked May 21 '13 15:05

MuiBienCarlota


1 Answers

Try using the CompressedContent class from WebAPIContrib https://github.com/WebApiContrib/WebAPIContrib/blob/master/src/WebApiContrib/Content/CompressedContent.cs

public async Task<string> DoPost(HttpContent content)
{
  HttpClient client = new HttpClient();
  HttpResponseMessage response = await client.PostAsync("http://myUri", 
                                 new CompressedContent(content,"gzip"));

  response.EnsureSuccessStatusCode();
  return await response.Content.ReadAsStringAsync();
}

P.S. that this will only stream the content on .net 4.5. The .net 4 version of HttpWebRequest always buffers sent content.

P.P.S. Creating a new HttpClient for each request is not the best way to use HttpClient. Doing this will force a new TCP connection to be created for each request.

like image 159
Darrel Miller Avatar answered Sep 21 '22 19:09

Darrel Miller