Skip to content

Fix ReactorSender treating absent max-message-size on ATTACH as a zero-byte limit#49848

Open
thomhurst wants to merge 2 commits into
Azure:mainfrom
thomhurst:fix/amqp-absent-max-message-size
Open

Fix ReactorSender treating absent max-message-size on ATTACH as a zero-byte limit#49848
thomhurst wants to merge 2 commits into
Azure:mainfrom
thomhurst:fix/amqp-absent-max-message-size

Conversation

@thomhurst

@thomhurst thomhurst commented Jul 16, 2026

Copy link
Copy Markdown

Description

Fixes #49847.

ReactorSender.getLinkSize() reads the remote max-message-size from the broker's ATTACH frame. When the peer omits the field, linkSize was left at its initial value of 0, so every send failed immediately with:

Size of the payload exceeded maximum message size: 0 kb

Per the AMQP 1.0 spec (OASIS AMQP v1.0, section 2.7.3 "Attach", field max-message-size):

The maximum message size supported by the link endpoint. […] If this field is zero or unset, there is no maximum size imposed by the link endpoint.

The real Azure Service Bus / Event Hubs service always advertises the field, so this never fires against Azure itself — but spec-compliant brokers that omit it (e.g. ActiveMQ Artemis, which never sets it on its receiving links) are completely unusable for sending: the client rejects even a 1-byte payload before any bytes reach the wire.

Changes

  • getLinkSize() now treats an absent or zero remote max-message-size as "no limit imposed by the peer" and falls back to the pre-existing ClientConstants.MAX_MESSAGE_LENGTH_BYTES (256 KB) so a bounded encode buffer can still be allocated. The previous warning log is replaced with an informational message stating the default being applied.
  • Advertised values larger than Integer.MAX_VALUE (e.g. peers advertising the maximum unsigned long to signal an effectively unlimited link) are now clamped to Integer.MAX_VALUE instead of being truncated by UnsignedLong.intValue() into a negative linkSize, which would have produced a NegativeArraySizeException on the send path.
  • The saturation is extracted into a shared clampToInt helper used by both getLinkSize() and the max-message-size fallback in getMaxBatchSize(), so the two call sites cannot diverge. getMaxBatchSize()s documented contract is preserved: it still returns 0 when both the vendor property and max-message-size are absent, with downstream clients substituting their own default.

Tests

Added to ReactorSenderTest (all 622 module tests pass; checkstyle and spotbugs clean):

  • testLinkSizeUsesDefaultWhenRemoteMaxMessageSizeAbsent — absent field falls back to 256 KB and the result is cached.
  • testLinkSizeUsesDefaultWhenRemoteMaxMessageSizeZero — explicit zero is treated as "no limit" per spec.
  • testLinkSizeClampsRemoteMaxMessageSizeAboveIntegerMax — 2^64-1 clamps to Integer.MAX_VALUE.
  • testSendWhenRemoteMaxMessageSizeAbsent — a message sends successfully when the field is absent.

All SDK Contribution checklist:

  • The pull request does not introduce [breaking changes]
  • CHANGELOG is updated for new features, bug fixes or other significant changes.
  • I have read the contribution guidelines.

General Guidelines and Best Practices

  • Title of the pull request is clear and informative.
  • There are a small number of commits, each of which have an informative message. This means that previously merged commits do not appear in the history of the PR. For more information on cleaning up the commits in your PR, see this page.

Testing Guidelines

  • Pull request includes test coverage for the included changes.

Per AMQP 1.0 section 2.7.3, an absent or zero max-message-size on the
remote ATTACH frame means the peer imposes no message size limit. The
sender previously left linkSize at 0 in that case, so every send failed
with "Size of the payload exceeded maximum message size: 0 kb" against
brokers that do not advertise the field (e.g. ActiveMQ Artemis).

Fall back to ClientConstants.MAX_MESSAGE_LENGTH_BYTES (256 KB) when the
field is absent or zero, and clamp advertised values larger than
Integer.MAX_VALUE instead of truncating them to negative ints.

Fixes Azure#49847
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
31 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Extract the unsigned-long-to-int saturation into a clampToInt helper and
use it in both places that read the remote max-message-size, so a peer
advertising a value above Integer.MAX_VALUE cannot truncate the batch
size fallback to a negative int either. getMaxBatchSize still returns 0
when the field is absent, preserving its documented contract. Also fixes
the vendor-property comment that claimed intValue() matched getLinkSize.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant