< Summary

Information
Class: IceRpc.Compressor.CompressorMiddleware
Assembly: IceRpc.Compressor
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Compressor/CompressorMiddleware.cs
Tag: 275_13775359185
Line coverage
95%
Covered lines: 47
Uncovered lines: 2
Coverable lines: 49
Total lines: 98
Line coverage: 95.9%
Branch coverage
91%
Covered branches: 22
Total branches: 24
Branch coverage: 91.6%
Method coverage
100%
Covered methods: 2
Total methods: 2
Method coverage: 100%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)75%4.06484.61%
DispatchAsync()95%2020100%

File(s)

/home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Compressor/CompressorMiddleware.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Extensions.DependencyInjection;
 4using IceRpc.Features;
 5using System.Buffers;
 6using System.Diagnostics;
 7using System.IO.Compression;
 8using System.IO.Pipelines;
 9using ZeroC.Slice;
 10
 11namespace IceRpc.Compressor;
 12
 13/// <summary>Represents a middleware that decompresses the payloads of incoming requests and compresses the payloads of
 14/// outgoing responses.</summary>
 15/// <remarks>This middleware decompresses the payload of an incoming request when the request carries a
 16/// <see cref="RequestFieldKey.CompressionFormat" /> field with a supported compression format (currently
 17/// <see cref="CompressionFormat.Brotli" /> or <see cref="CompressionFormat.Deflate" />).<br/>
 18/// This middleware compresses the payload of a response and sets the <see cref="ResponseFieldKey.CompressionFormat" />
 19/// field when the request has the <see cref="ICompressFeature" /> feature set and the response's CompressionFormat
 20/// field is unset.</remarks>
 21/// <seealso cref="CompressorRouterExtensions"/>
 22/// <seealso cref="CompressorDispatcherBuilderExtensions"/>
 23public class CompressorMiddleware : IDispatcher
 24{
 25    private readonly CompressionFormat _compressionFormat;
 26    private readonly CompressionLevel _compressionLevel;
 27    private readonly ReadOnlySequence<byte> _encodedCompressionFormatValue;
 28    private readonly IDispatcher _next;
 29
 30    /// <summary>Constructs a Compressor middleware.</summary>
 31    /// <param name="next">The next dispatcher in the dispatch pipeline.</param>
 32    /// <param name="compressionFormat">The compression format for the compress operation.</param>
 33    /// <param name="compressionLevel">The compression level for the compress operation.</param>
 934    public CompressorMiddleware(
 935        IDispatcher next,
 936        CompressionFormat compressionFormat,
 937        CompressionLevel compressionLevel = CompressionLevel.Fastest)
 938    {
 939        _next = next;
 940        if (compressionFormat != CompressionFormat.Brotli && compressionFormat != CompressionFormat.Deflate)
 041        {
 042            throw new NotSupportedException($"The compression format '{compressionFormat}' is not supported.");
 43        }
 944        _compressionFormat = compressionFormat;
 945        _compressionLevel = compressionLevel;
 946        _encodedCompressionFormatValue = new(new byte[] { (byte)compressionFormat });
 947    }
 48
 49    /// <inheritdoc/>
 50    public async ValueTask<OutgoingResponse> DispatchAsync(
 51        IncomingRequest request,
 52        CancellationToken cancellationToken = default)
 953    {
 954        if (request.Protocol.HasFields && request.Fields.ContainsKey(RequestFieldKey.CompressionFormat))
 555        {
 556            CompressionFormat compressionFormat = request.Fields.DecodeValue(
 557                RequestFieldKey.CompressionFormat,
 1058                (ref SliceDecoder decoder) => decoder.DecodeCompressionFormat());
 59
 560            if (compressionFormat == CompressionFormat.Brotli)
 261            {
 262                request.Payload = PipeReader.Create(
 263                    new BrotliStream(request.Payload.AsStream(), CompressionMode.Decompress));
 264            }
 365            else if (compressionFormat == CompressionFormat.Deflate)
 266            {
 267                request.Payload = PipeReader.Create(
 268                    new DeflateStream(request.Payload.AsStream(), CompressionMode.Decompress));
 269            }
 70            // else nothing to do
 571        }
 72
 973        OutgoingResponse response = await _next.DispatchAsync(request, cancellationToken).ConfigureAwait(false);
 74
 75        // The ICompressFeature is typically set through the Slice compress attribute.
 76
 977        if (request.Protocol.HasFields &&
 978            response.StatusCode == StatusCode.Ok &&
 979            request.Features.Get<ICompressFeature>() is ICompressFeature compress &&
 980            compress.Value &&
 981            !response.Fields.ContainsKey(ResponseFieldKey.CompressionFormat))
 482        {
 483            if (_compressionFormat == CompressionFormat.Brotli)
 284            {
 485                response.Use(next => PipeWriter.Create(new BrotliStream(next.AsStream(), _compressionLevel)));
 286            }
 87            else
 288            {
 289                Debug.Assert(_compressionFormat == CompressionFormat.Deflate);
 490                response.Use(next => PipeWriter.Create(new DeflateStream(next.AsStream(), _compressionLevel)));
 291            }
 92
 493            response.Fields = response.Fields.With(ResponseFieldKey.CompressionFormat, _encodedCompressionFormatValue);
 494        }
 95
 996        return response;
 997    }
 98}