Skip to content

Compression

samgerene edited this page Oct 10, 2026 · 2 revisions

Compression

Request and response bodies can be compressed with gzip using the HTTP-style Content-Encoding / Accept-Encoding headers. Compression is opt-in on both sides and transparent to handlers: received bodies are decompressed before your code sees them.

Enabling compression

// Server: compress responses for clients that accept gzip
services.AddArgusPipeHost(options =>
{
    options.PipeName = "my-pipe";
    options.Compression.Enabled = true;
});

// Client: accept compressed responses and compress large request bodies
services.AddArgusClient("my-pipe", client => client.Compression.Enabled = true);

Or on a client instance: client.Compression.Enabled = true;.

How negotiation works

Side Behavior
Client, Compression.Enabled Adds Accept-Encoding: gzip to every request. Adds Content-Encoding: gzip and compresses the body when it is at least MinimumBodySize bytes.
Host, always Decodes requests whose Content-Encoding is one of the configured encodings -- even when its own compression is disabled. An unknown encoding gets 400 Bad Request.
Host, Compression.Enabled Compresses a response body of at least MinimumBodySize bytes when the request's Accept-Encoding accepts a configured encoding.
  • Accept-Encoding is parsed like HTTP: q=0 excludes an encoding, * accepts any, and the PreferredEncoding wins when several are accepted.
  • Headers you set yourself are kept: setting Content-Encoding: identity on a request prevents compressing it.
  • The client adds its headers only to what it sends: the ArgusRequest you pass to SendAsync is not modified, so it can be inspected afterwards or reused with another client.
  • After decoding, the Content-Encoding header is removed from the message, so Body / BodyStream and the headers describe the decoded content.
  • Streamed bodies (BodyStream) are compressed on the fly in chunked transfer encoding, without buffering the whole body. A stream of unknown length is always compressed when compression applies.

Client compression assumes the server supports it. A server older than this feature would not decode compressed requests, so only enable client compression against an up-to-date host. Response compression is safe either way: the host only compresses when the client asked for it.

ArgusCompressionOptions

Available as ArgusClient.Compression and ArgusPipeHostOptions.Compression:

Property Type Default Description
Enabled bool false Compress outgoing bodies (see the table above). Decoding is always on.
MinimumBodySize int 1024 Bodies smaller than this (in bytes) are sent uncompressed, because compressing them costs more than it saves.
PreferredEncoding string "gzip" The encoding used to compress, when the other side accepts it. Must match one of Encodings.
Encodings IList<IArgusContentEncoding> [ GZipArgusContentEncoding ] Supported encodings for decoding, the Accept-Encoding header and compressing.

GZipArgusContentEncoding.CompressionLevel defaults to CompressionLevel.Fastest, which suits local IPC where CPU time matters more than the last few percent of size.

Protection against zip bombs

On the host, ArgusPipeHostOptions.MaxRequestBodySize limits both the compressed size on the wire and the decompressed size. A small request that inflates beyond the limit is rejected with 400 Bad Request ("Decompressed body size exceeds the maximum allowed size of N bytes"). Corrupt compressed data is also answered with 400.

Custom encodings

Implement IArgusContentEncoding and add it to Encodings:

public class BrotliContentEncoding : IArgusContentEncoding
{
    public string Name => "br";

    public Stream CreateCompressionStream(Stream destination) =>
        new BrotliStream(destination, CompressionLevel.Fastest, leaveOpen: true);

    public Stream CreateDecompressionStream(Stream source) =>
        new BrotliStream(source, CompressionMode.Decompress, leaveOpen: true);
}

options.Compression.Encodings.Add(new BrotliContentEncoding());
options.Compression.PreferredEncoding = "br";

The returned streams must leave the inner stream open when disposed.

Using the serializers directly

The wire serializers compress and decompress based on the Content-Encoding header, using their ContentEncodings property (gzip by default). This only works on the byte-safe Stream overloads (WriteAsync(Stream, …) / ReadAsync(Stream, …)). The text-based overloads (Write(message) returning a string, Read(string), the StreamReader readers and the synchronous StreamWriter writes) cannot carry compressed bytes and throw InvalidOperationException for a message with a Content-Encoding. See Serialization.

See also

Clone this wiki locally