Skip to content

Releases

v7.3.0-beta.0

v7.3.0-beta.0 Pre-release
Pre-release

Choose a tag to compare

@lukebakken lukebakken released this 04 Nov 18:50
v7.3.0-beta.0
d024acc

Note

7.3.0-beta.0 is a prerelease of 7.3.0. Please report problems at https://github.com/rabbitmq/rabbitmq-dotnet-client/issues.

7.3.0

User-visible behavior changes. CHANGELOG.md carries the full merged pull request and issue list.

Security

  • An amqps:// URI now verifies the broker's certificate against the host name (GHSA-7xr9-fg47-hh5x). Assigning an amqps:// URI to ConnectionFactory.Uri accepted a certificate issued for any host. This also affected a factory bound from configuration with Ssl:Enabled set, because the reflection-based configuration binder assigns Uri even when no Uri key is present.
  • The name checked is Ssl.ServerName, which assigning Uri sets to the URI's host unless ServerName already holds a different name. A certificate that does not carry that name, as a DNS name or an IP address, now fails the handshake. That includes a HostName changed after the Uri, which does not update ServerName. For several nodes with their own certificates, give each AmqpTcpEndpoint its own SslOption instance with Enabled = true and that node's ServerName. Setting Ssl.AcceptablePolicyErrors = SslPolicyErrors.RemoteCertificateNameMismatch restores the old behavior, and has no effect when Ssl.CertificateValidationCallback is set.
  • Assigning Uri no longer discards settings made before it. It replaces Ssl with a copy that keeps the certificates, callbacks, revocation check, policy and version already set, so permissive settings made before the Uri now take effect: review them. When Ssl.Version is not set, an amqps:// URI still sets it from AmqpUriSslProtocols. MaxInboundMessageBodySize set before the Uri is now kept rather than reset to 64 MiB.
  • A factory with TLS enabled no longer connects in cleartext to an endpoint without TLS (GHSA-5wrf-cc49-8x6j). Given AmqpTcpEndpoints or an IEndpointResolver, or an assigned Endpoint, the factory used each endpoint's own SslOption, whose TLS is off unless set, so the connection and its recoveries went unencrypted, to port 5672 when the endpoint had no port of its own. Such an endpoint now uses the factory's Ssl, including its one ServerName, and port 5671 when it has no port of its own; nodes with their own certificates need their own SslOption, as above. An endpoint bound by the reflection-based configuration binder stores port 5672 when none is given, so give it the TLS port. IConnection.Endpoint reports a copy rather than the instance passed in, and its port can differ: 5671 where the instance passed in reports 5672. ConnectionFactory.Uri likewise names port 5671 for a factory with TLS enabled and no port. A deployment that relied on the cleartext connection fails to connect after upgrading unless the broker accepts TLS on that port with a certificate for Ssl.ServerName.
  • A connection keeps the TLS options, hosts and ports it was created with, including when it recovers. Changing an SslOption or an endpoint afterwards, on the factory, on an endpoint passed in or on IConnection.Endpoint, no longer reaches it; create a new connection to use different options. An IEndpointResolver, passed in or returned by EndpointResolverFactory, is still asked for its endpoints on every recovery, and their options are read then. To follow endpoints that change while a connection is open, such as from service discovery, return them from the resolver's All(), not from the sequence passed to EndpointResolverFactory, which is now copied when the connection is created.
  • An amqp:// URI no longer turns TLS off: assigning one to a factory with TLS enabled throws InvalidOperationException, which the reflection-based configuration binder wraps in TargetInvocationException. To connect without TLS, assign Ssl a new SslOption first; setting Ssl.Enabled = false also turns TLS off for any other factory or endpoint that shares that instance.
  • A connection.tune whose negotiated frame_max is from 1 to 4095, below the AMQP minimum, now fails the connection: CreateConnectionAsync throws BrokerUnreachableException with a ProtocolViolationException inside (GHSA-qqh4-4hmj-6ph2). A broker, or anything between client and broker, that sent 1 to 8 broke publishing, and 1 to 7 could make it write past the end of a buffer. A RequestedFrameMax from 1 to 4095 fails the same way. 0 still means no limit, and a value above about 2 GiB is now also treated as no limit instead of making every publish fail.
  • Field tables and arrays nested more than 32 levels deep are now rejected (GHSA-qhmm-xgrg-q669). Reading one recursed without a limit, so a connection.start from the broker, or message headers set by any publisher, could overflow the stack and terminate the application. Such a value now fails the connection and every channel on it, as an unknown field type already did: CreateConnectionAsync throws BrokerUnreachableException, and a delivery, a basic.return or a BasicGetAsync reply closes the connection with reply code 502. Deeper nesting was read before. This client now refuses to send it as well: BasicPublishAsync, or any method taking an arguments table, throws WireFormattingException before anything is sent, and a table that contains itself fails the same way instead of overflowing the stack. A message carrying such headers closes the connection again each time it is redelivered. From RabbitMQ 4.0, a quorum queue's default delivery limit eventually removes it; on other queues it stays until removed, for example by purging the queue, or on a stream until retention discards it.
  • BasicGetAsync no longer waits forever when the reply's headers cannot be read (#2076). It throws the parse error, such as SyntaxErrorException, and the connection closes with reply code 502.

Publishing a caller-owned or sequence body

Adds API and breaks implementors of IChannel (#1913, #1983):

  • IChannel gained four new abstract members, with no default implementations. The interface went from two BasicPublishAsync overloads to six, the new ones taking an IDisposable? bodyOwner alongside either a ReadOnlyMemory<byte> or a ReadOnlySequence<byte> body. Any type implementing IChannel directly, such as a test double or a decorator, will not compile against 7.3.0 until it implements them. This is the release's only hard source break, and it affects implementors rather than callers. Five new [Obsolete] warnings also fail a warnings-as-errors build; they are listed below.
  • A body can now be published from a ReadOnlySequence<byte> without being copied. Ten new IChannelExtensions overloads take bodyOwner, five of them for a sequence and five for a ReadOnlyMemory<byte>. Ownership of bodyOwner transfers to the client, which disposes it once, after the message reaches the wire or when publication fails, so every segment must stay valid and unmodified until then. Passing null is permitted but copies the body into one pooled buffer. A body that cannot be read when the client writes it closes the connection, and every channel on it (#2108). An exception from the owner's Dispose is contained, so a failed publish still throws its own exception.

Byte slices as field table values

  • A field table value, such as a message header, can now be a ReadOnlyMemory<byte>, Memory<byte> or ArraySegment<byte> (#2144). It is sent as a long string, like a byte[], and received as a byte[]. These types previously failed with WireFormattingException, which CreateConnectionAsync wraps in BrokerUnreachableException when the value is in ConnectionFactory.ClientProperties.
  • The client reads the slice each time it sends the table, so its memory must stay valid and unchanged until then. For a message header that is until the task returned by BasicPublishAsync completes, which can be after the call returns. With topology recovery, exchange, queue, binding and consumer arguments are sent again on each recovery, and ConnectionFactory.ClientProperties is sent by every connection the factory opens, so a slice in either must stay valid and unchanged until those connections are closed.
  • The slice is boxed in the table, so it allocates less than a copy into an exact-length byte[] only for values longer than 8 bytes, and it takes longer to encode than a byte[].

OpenTelemetry span status and duration

Two changes visible to anyone with dashboards or alerts on this client's spans (#1978):

  • Publish span duration rises by about an order of magnitude when publisher confirmations and confirmation tracking are both enabled, because the span now covers the confirmation round trip. The span also now starts before flow control, so a broker under a memory or disk alarm moves it even with confirmations off. Nothing became slower. Latency dashboards keyed on the publish span will move; docs/internal/opentelemetry-tracing-review.md has the measurements.
  • Failed operations now produce Error spans. Publisher and subscriber spans previously set no status, so a throwing consumer traced as a success, as did a nacked or returned publish on a channel with confirmations and tracking both enabled. They now set Error, error.type and an exception event, so error rates keyed on these spans go from zero to real. Cancellation the caller's own token requested is exempt and rethrows unmarked; a client-internal timeout, such as RequestedConnectionTimeout, is recorded as an error with error.type of BrokerUnreachableException.

OpenTelemetry tracing configuration

Now settable per factory (#2026, #2009):

  • A connection's tracing configuration can be set on the factory, via ConnectionFactory.TracingOptions, instead of only through process-wide statics. Every member of the new ConnectionTracingOptions is nullable and null means inherit, resolved per member, so setting one span-shaping flag leaves whatever propag...
Read more

v7.2.2

Choose a tag to compare

@lukebakken lukebakken released this 05 Aug 17:59
v7.2.2
740faf0

GitHub milestone

What's Changed

  • Fix URI round-trip for credentials containing special characters by @lukebakken in #1944
  • Retry recovery when a topology operation times out by @ansd in #1994

New Contributors

Full Changelog: v7.2.1...v7.2.2

v7.2.1

Choose a tag to compare

@lukebakken lukebakken released this 25 Feb 19:53
v7.2.1
76a88bd

GitHub milestone

What's Changed

New Contributors

Full Changelog: v7.2.0...v7.2.1

v7.2.0

Choose a tag to compare

@lukebakken lukebakken released this 06 Nov 17:53
v7.2.0
d18e312

GitHub milestone

What's Changed

New Contributors

Full Changelog: v7.1.2...v7.2.0

v7.1.2

Choose a tag to compare

@lukebakken lukebakken released this 17 Mar 17:52
v7.1.2
c48e7b0

GitHub milestone

What's Changed

Full Changelog: v7.1.1...v7.1.2

v7.1.1

Choose a tag to compare

@lukebakken lukebakken released this 26 Feb 16:43
v7.1.1
85fd42f

GitHub milestone

What's Changed

Full Changelog: v7.1.0...v7.1.1

v7.1.0

Choose a tag to compare

@lukebakken lukebakken released this 19 Feb 17:55
v7.1.0
a8dcbc9

GitHub milestone

What's Changed

New Contributors

Full Changelog: v7.0.0...v7.1.0

v7.1.0-alpha.1

v7.1.0-alpha.1 Pre-release
Pre-release

Choose a tag to compare

@lukebakken lukebakken released this 13 Feb 00:18
v7.1.0-alpha.1
0a4cec0

GitHub milestone

What's Changed

New Contributors

Full Changelog: v7.0.0...v7.1.0-alpha.1

v7.1.0-alpha.0

v7.1.0-alpha.0 Pre-release
Pre-release

Choose a tag to compare

@lukebakken lukebakken released this 16 Jan 23:23
v7.1.0-alpha.0
75822ae

GitHub milestone

What's Changed

New Contributors

Full Changelog: v7.0.0...v7.1.0-alpha.0

v7.0.0

Choose a tag to compare

@lukebakken lukebakken released this 06 Nov 00:30
v7.0.0

GitHub milestone for RabbitMQ.Client version 7.0.0

What's Changed

Read more