compress function

Middleware compress({
  1. String encoding = 'gzip',
  2. int threshold = 1024,
})

Gzip/deflate response compression middleware.

Compresses responses whose body is at or above threshold bytes and whose client advertises the chosen encoding in the Accept-Encoding header. Already-compressed responses (Content-Encoding already set) are skipped.

// Defaults: gzip, 1024-byte threshold
app.use(compress());

// Deflate, lower threshold
app.use(compress(encoding: 'deflate', threshold: 512));

Supported encoding values: 'gzip' (default), 'deflate'.

Implementation

Middleware compress({
  String encoding = 'gzip',
  int threshold = 1024,
}) {
  assert(
    encoding == 'gzip' || encoding == 'deflate',
    'compress: encoding must be "gzip" or "deflate"',
  );

  return (Context c, Next next) async {
    await next();

    final response = c.response;
    if (response == null || response.alreadySent) return;

    // Skip if already compressed
    if (response.extraHeaders.containsKey('content-encoding') ||
        response.extraHeaders.containsKey('Content-Encoding')) return;

    // Skip if client doesn't accept this encoding
    final acceptEncoding = (c.req.header('accept-encoding') ?? '').toLowerCase();
    if (!acceptEncoding.contains(encoding)) return;

    // Convert body → raw bytes
    final bodyBytes = _toBytes(response);
    if (bodyBytes == null || bodyBytes.isEmpty) return;

    // Skip below threshold
    if (bodyBytes.length < threshold) return;

    // Compress
    final compressed = encoding == 'gzip'
        ? GZipCodec().encode(bodyBytes)
        : ZLibCodec().encode(bodyBytes);

    // Merge existing extra-headers, then set compression-specific ones.
    final existingVary = response.extraHeaders['Vary'] ??
        response.extraHeaders['vary'] ??
        '';
    final newVary = existingVary.isEmpty
        ? 'Accept-Encoding'
        : '$existingVary, Accept-Encoding';

    final headers = <String, String>{
      ...response.extraHeaders,
      'Content-Encoding': encoding,
      'Vary': newVary,
      'Content-Length': '${compressed.length}',
    };

    c.respond(Response.bytes(
      compressed,
      status: response.statusCode,
      contentType: response.contentType,
      headers: headers,
    ));
  };
}