Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpClient: Conditionally set AcceptEncoding compression at runtime

We are trying to implement user-determined (on a settings screen) optional gzip compression in our client which uses HttpClient, so we can log and compare performance across a number of different calls over a period of time. Our first attempt was to simply conditionally add the header as follows:

HttpRequestMessage request = new HttpRequestMessage(Method, Uri);
if (AcceptGzipEncoding)
{
     _client.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
}

//Send to the server
result = await _client.SendAsync(request);

//Read the content of the result response from the server
content = await result.Content.ReadAsStringAsync();

This created the correct request, but the gzipped response was not decompressed on return, resulting in a garbled response. I found that we had to include the HttpClientHandler when constructing the HttpClient:

HttpClient _client = new HttpClient(new HttpClientHandler
    { 
        AutomaticDecompression = DecompressionMethods.GZip
    });

This all works well, but we'd like to change whether the client sends the Accept-Encoding: gzip header at runtime, and there doesn't appear to be any way to access or change the HttpClientHandler after it's passed to the HttpClient constructor. In addition, altering the headers of the HttpRequestMessage object doesn't have any effect on the headers of the request if they are defined by the HttpClientHandler.

Is there any way to do this without recreating the HttpClient each time this changes?

Edit: I've also tried to modify a reference to the HttpClientHandler to change AutomaticDecompression at runtime, but that's throwing this exception:

This instance has already started one or more requests. Properties can only be modified before sending the first request.

like image 321
pcdev Avatar asked Feb 26 '15 23:02

pcdev


People also ask

How do I compress a HTTP request body?

To compress the HTTP request body, you must attach the HTTP header indicating the sending of HTTP request body compressed in gzip format while sending the request message from the Web Service client. Implement the processing for attaching the HTTP header in the client application.

What is encoding gzip deflate BR?

gzip: It is a compression format using the Lempel-Ziv coding (LZ77), with a 32-bit CRC. compress: It is a compression format using the Lempel-Ziv-Welch (LZW) algorithm. deflate: It is a compression format using the zlib structure, with the deflate compression algorithm.

What is accept-encoding deflate?

It means the client can accept a response which has been compressed using the DEFLATE algorithm.

What does accept-encoding gzip mean?

The Accept-Encoding header is used for negotiating content encoding. Accept-Encoding: gzip, deflate. The server responds with the scheme used, indicated by the Content-Encoding response header. Content-Encoding: gzip. Note that the server is not obligated to use any compression method.


1 Answers

You're almost there with the first example, you just need to deflate the stream yourself. MS's GZipSteam will help with this:

HttpRequestMessage request = new HttpRequestMessage(Method, Uri);
if (AcceptGzipEncoding)
{
     _client.DefaultRequestHeaders.AcceptEncoding.Add(new System.Net.Http.Headers.StringWithQualityHeaderValue("gzip"));
}

//Send to the server
result = await _client.SendAsync(request);

//Read the content of the result response from the server
using (Stream stream = await result.Content.ReadAsStreamAsync())
using (Stream decompressed = new GZipStream(stream, CompressionMode.Decompress))
using (StreamReader reader = new StreamReader(decompressed))
{
    content = reader.ReadToEnd();
}
like image 178
Todd Menier Avatar answered Oct 31 '22 00:10

Todd Menier