Skip to content

Latest commit

 

History

History
229 lines (180 loc) · 9.35 KB

libraries.md

File metadata and controls

229 lines (180 loc) · 9.35 KB

Libraries updates in .NET 9 Release Candidate 1

Here's a summary of what's new in .NET Libraries in this release:

Libraries updates in .NET 9 Release Candidate 1:

.NET 9 Release Candidate 1:

WebSocket Keep-Alive Ping and Timeout

We've added new APIs on ClientWebSocketOptions and WebSocketCreationOptions that let you opt-in to sending WebSocket pings, and abort the connection if the peer does not respond in time.

Until now, you could specify a KeepAliveInterval to keep the connection from staying idle, but there was no built-in mechanism to enforce that the peer is responding.

The following example pings the server every 5 seconds and aborts the connection if it does not respond within a second.

using var cws = new ClientWebSocket();
cws.Options.HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher;
cws.Options.KeepAliveInterval = TimeSpan.FromSeconds(5);
cws.Options.KeepAliveTimeout = TimeSpan.FromSeconds(1);

await cws.ConnectAsync(uri, httpClient, cancellationToken);

Add ZLib and Brotli compression options

We added ZLibCompressionOptions and BrotliCompressionOptions types for setting algorithm specific compression level and compression strategy for users who would like to set more fine-tuned settings than the only existing option CompressionLevel. The new compression options are designed to allow for expanding to more options in the future.

namespace System.IO.Compression
{
+    public enum ZLibCompressionStrategy
+    {
+        Default = 0,
+        Filtered = 1,
+        HuffmanOnly = 2,
+        RunLengthEncoding = 3,
+        Fixed = 4,
+    }
+    public sealed class ZLibCompressionOptions
+    {
+        public int CompressionLevel { get; set; }
+        public ZLibCompressionStrategy CompressionStrategy { get; set; }
+    }
    public sealed class ZLibStream : Stream
    {
+        public ZLibStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen = false);
    }
    public partial class DeflateStream : Stream
    {
+        public DeflateStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen = false);
    }
    public partial class GZipStream : Stream
    {
+        public GZipStream(Stream stream, ZLibCompressionOptions compressionOptions, bool leaveOpen = false);
    }
+    public sealed class BrotliCompressionOptions
+    {
+        public int Quality { get; set; }
+    }
    public sealed partial class BrotliStream : System.IO.Stream
    {
+        public BrotliStream(Stream stream, BrotliCompressionOptions compressionOptions, bool leaveOpen = false) { }
    }
}

API Usage:

private MemoryStream CompressStream(Stream uncompressedStream)
{
    var compressorOutput = new MemoryStream();
    using var compressionStream = new ZLibStream(compressorOutput, new ZLibCompressionOptions() { CompressionLevel = 6, CompressionStrategy = ZLibCompressionStrategy.HuffmanOnly });
    uncompressedStream.CopyTo(compressionStream);
    compressionStream.Flush();

    return compressorOutput;
}

Add TarEntry.DataOffset

We've made the position or offset of the data in the enclosing stream for a System.Formats.Tar.TarEntry object a public property.

public abstract partial class TarEntry
{
+        public long DataOffset { get; }
}

TarEntry.DataOffset returns a position in the entry's archive stream where the entry's first data byte is located. The entry's data is encapsulated in a sub-stream which users can access via TarEntry.DataStream, which hides the real position of the data relative to the archive stream. This is enough for most users, but for those who need more flexibility and want to know the real starting position of the data in the archive stream, we added this new API, which should make it easy to support features like concurrent access with very large TAR files.

// Create stream for tar ball data in Azure Blob Storage
var blobClient = Azure.Storage.Blobs.BlobClient(....);
var blobClientStream = await blobClient.OpenReadAsync(...);

// Create TarReader for the stream and get a TarEntry
var tarReader = new System.Formats.Tar.TarReader(blobClientStream);
var tarEntry = await tarReader.GetNextEntryAsync();

// get position of TarEntry data in blob stream
var entryOffsetInBlobStream = tarEntry.DataOffset;
var entryLength =  tarEntry.Length;

// create a separate stream
var newBlobClientStream = await TarBlob.OpenReadAsync(...);
newBlobClientStream.Seek(entryOffsetInBlobStream, SeekOrigin.Begin);

// read tar ball content from separate BlobClient stream
var bytes = new byte[length];
await tarBlobStream.ReadAsync(bytes, 0, (int)entryLength);

HttpClientFactory no longer logs header values by default

LogLevel.Trace events logged by HttpClientFactory no longer include header values by default. You may opt-in to logging values for specific headers via the RedactLoggedHeaders helper method.

The following example redacts all headers, except for the user agent.

services.AddHttpClient("myClient")
    .RedactLoggedHeaders(name => name != "User-Agent");

See the documentation for detailed breaking change guidance.

Out-of-proc Meter wildcard listening

It is already possible to listen to meters out-of-process using the System.Diagnostics.Metrics event source provider, but the current implementation requires specifying the full meter name. This update introduces support for listening to all meters by using the wildcard character *, allowing you to capture metrics from every meter in a process. Additionally, it adds support for listening by meter prefix, so you can listen to all meters whose names start with a specified prefix. For example, using MyMeter* will enable listening to all meters with names beginning with MyMeter.

using System.Diagnostics.Metrics;
using System.Diagnostics.Tracing;

var meter = new Meter("MyCompany.MyMeter"); // Note the complete meter name is "MyCompany.MyMeter"
meter.CreateObservableCounter("MyCounter", () => 1); // Create a counter and allow publishing values.

// Create the listener to use the wildcard character to listen to all meters using prefix names.
MyEventListener listener = new MyEventListener();

// Press Enter to exit the program.
Console.ReadLine();

The MyEventListener class is defined as follows:

internal class MyEventListener : EventListener
{
    protected override void OnEventSourceCreated(EventSource eventSource)
    {
        Console.WriteLine(eventSource.Name);
        if (eventSource.Name == "System.Diagnostics.Metrics")
        {
            // Listening to all meters with names starting with "MyCompany".
            // If using "*" allow listening to all meters.
            EnableEvents(eventSource, EventLevel.Informational, (EventKeywords)0x3, new Dictionary<string,string?>() { { "Metrics", "MyCompany*" } });
        }
    }

    protected override void OnEventWritten(EventWrittenEventArgs eventData)
    {
        if (eventData.EventSource.Name != "System.Diagnostics.Metrics" || eventData.EventName == "CollectionStart" || eventData.EventName == "CollectionStop" || eventData.EventName == "InstrumentPublished")
        {
            // ignore all events we are not interested in
            return;
        }

        Console.WriteLine(eventData.EventName);

        if (eventData.Payload is not null)
        {
            for (int i = 0; i < eventData.Payload.Count; i++)
            {
                Console.WriteLine($"\t{eventData.PayloadNames![i]}: {eventData.Payload[i]}");
            }
        }
    }
}

Upon executing the code, the output displaying the published counter values should appear:

CounterRateValuePublished
        sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522
        meterName: MyCompany.MyMeter
        meterVersion:
        instrumentName: MyCounter
        unit:
        tags:
        rate: 0
        value: 1
        instrumentId: 1
CounterRateValuePublished
        sessionId: 7cd94a65-0d0d-460e-9141-016bf390d522
        meterName: MyCompany.MyMeter
        meterVersion:
        instrumentName: MyCounter
        unit:
        tags:
        rate: 0
        value: 1
        instrumentId: 1

Monitoring tools like dotnet-counters can also be used to listen to the metrics using the wildcard.