< Summary

Information
Class: IceRpc.Compressor.CompressorInterceptor
Assembly: IceRpc.Compressor
File(s): /home/runner/work/icerpc-csharp/icerpc-csharp/src/IceRpc.Compressor/CompressorInterceptor.cs
Tag: 275_13775359185
Line coverage
96%
Covered lines: 48
Uncovered lines: 2
Coverable lines: 50
Total lines: 103
Line coverage: 96%
Branch coverage
90%
Covered branches: 20
Total branches: 22
Branch coverage: 90.9%
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%
InvokeAsync()94.44%1818100%

File(s)

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

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using IceRpc.Extensions.DependencyInjection;
 4using IceRpc.Features;
 5using IceRpc.Internal;
 6using System.Buffers;
 7using System.Diagnostics;
 8using System.IO.Compression;
 9using System.IO.Pipelines;
 10using ZeroC.Slice;
 11
 12namespace IceRpc.Compressor;
 13
 14/// <summary>Represents an interceptor that compresses the payloads of outgoing requests and decompresses the payloads
 15/// of incoming responses.</summary>
 16/// <remarks>This interceptor compresses the payload of a request and sets the
 17/// <see cref="RequestFieldKey.CompressionFormat" /> field when this request has the <see cref="ICompressFeature" />
 18/// feature set and the CompressionFormat field is unset.<br/>
 19/// This interceptor decompresses the payload of a response when this response's status code is
 20/// <see cref="StatusCode.Ok" /> and the response carries a <see cref="ResponseFieldKey.CompressionFormat" /> field
 21/// with a supported compression format (currently <see cref="CompressionFormat.Brotli" /> or
 22/// <see cref="CompressionFormat.Deflate" />).</remarks>
 23/// <seealso cref="CompressorPipelineExtensions"/>
 24/// <seealso cref="CompressorDispatcherBuilderExtensions"/>
 25public class CompressorInterceptor : IInvoker
 26{
 27    private readonly CompressionFormat _compressionFormat;
 28    private readonly CompressionLevel _compressionLevel;
 29    private readonly ReadOnlySequence<byte> _encodedCompressionFormatValue;
 30    private readonly IInvoker _next;
 31
 32    /// <summary>Constructs a Compressor interceptor.</summary>
 33    /// <param name="next">The next invoker in the invocation pipeline.</param>
 34    /// <param name="compressionFormat">The compression format for the compress operation.</param>
 35    /// <param name="compressionLevel">The compression level for the compress operation.</param>
 936    public CompressorInterceptor(
 937        IInvoker next,
 938        CompressionFormat compressionFormat,
 939        CompressionLevel compressionLevel = CompressionLevel.Fastest)
 940    {
 941        _next = next;
 942        if (compressionFormat != CompressionFormat.Brotli && compressionFormat != CompressionFormat.Deflate)
 043        {
 044            throw new NotSupportedException($"The compression format '{compressionFormat}' is not supported.");
 45        }
 946        _compressionFormat = compressionFormat;
 947        _compressionLevel = compressionLevel;
 948        _encodedCompressionFormatValue = new(new byte[] { (byte)compressionFormat });
 949    }
 50
 51    /// <inheritdoc/>
 52    public async Task<IncomingResponse> InvokeAsync(OutgoingRequest request, CancellationToken cancellationToken)
 953    {
 54        // The ICompressFeature is typically set through the Slice compress attribute.
 55
 956        if (request.Protocol.HasFields &&
 957            request.Features.Get<ICompressFeature>() is ICompressFeature compress &&
 958            compress.Value &&
 959            !request.Fields.ContainsKey(RequestFieldKey.CompressionFormat))
 460        {
 461            if (_compressionFormat == CompressionFormat.Brotli)
 262            {
 463                request.Use(next => PipeWriter.Create(new BrotliStream(next.AsStream(), _compressionLevel)));
 264            }
 65            else
 266            {
 267                Debug.Assert(_compressionFormat == CompressionFormat.Deflate);
 468                request.Use(next => PipeWriter.Create(new DeflateStream(next.AsStream(), _compressionLevel)));
 269            }
 70
 471            request.Fields = request.Fields.With(RequestFieldKey.CompressionFormat, _encodedCompressionFormatValue);
 472        }
 73
 974        IncomingResponse response = await _next.InvokeAsync(request, cancellationToken).ConfigureAwait(false);
 75
 976        if (request.Protocol.HasFields && response.StatusCode == StatusCode.Ok)
 977        {
 978            CompressionFormat compressionFormat = response.Fields.DecodeValue(
 979               ResponseFieldKey.CompressionFormat,
 1480               (ref SliceDecoder decoder) => decoder.DecodeCompressionFormat());
 81
 982            if (compressionFormat == CompressionFormat.Brotli)
 283            {
 284                response.Payload = PipeReader.Create(
 285                    new BrotliStream(response.Payload.AsStream(), CompressionMode.Decompress));
 86
 87                // Work around bug from StreamPipeReader with the BugFixStreamPipeReaderDecorator
 288                response.Payload = new BugFixStreamPipeReaderDecorator(response.Payload);
 289            }
 790            else if (compressionFormat == CompressionFormat.Deflate)
 291            {
 292                response.Payload = PipeReader.Create(
 293                    new DeflateStream(response.Payload.AsStream(), CompressionMode.Decompress));
 94
 95                // Work around bug from StreamPipeReader with the BugFixStreamPipeReaderDecorator
 296                response.Payload = new BugFixStreamPipeReaderDecorator(response.Payload);
 297            }
 98            // else nothing to do
 999        }
 100
 9101        return response;
 9102    }
 103}