Skip to content

feat(eventhubs): support AMQP-over-WebSockets transport - #5031

Open
Johnathan W (j7nw4r) wants to merge 26 commits into
mainfrom
j7nw4r/eventhubs-websocket-transport
Open

feat(eventhubs): support AMQP-over-WebSockets transport#5031
Johnathan W (j7nw4r) wants to merge 26 commits into
mainfrom
j7nw4r/eventhubs-websocket-transport

Conversation

@j7nw4r

Copy link
Copy Markdown
Member

Supersedes #4596. That pull request came from a fork. Fork pull requests cannot authenticate to the Azure Artifacts cargo feed that #4928 added, so the pipeline failed before it compiled anything. This branch lives in the Azure repository, so the pipeline authenticates normally. The code is unchanged, and the review history stays on #4596.

Summary

azure_messaging_eventhubs connected over AMQP on TCP/TLS only (port 5671). No option selected AMQP-over-WebSockets (wss://, port 443). Some networks block the native AMQP ports (5671 and 5672). Clients on those networks had no path to Event Hubs. The .NET, Java, and Python Azure SDKs all offer this option. (#3601)

This change adds a transport option, and it keeps the TLS stack a separate choice. Both parts are additive. No existing caller breaks, and Tcp stays the default.

Motivation

The AMQP WebSocket binding sends an AMQP connection over a WebSocket on port 443. The service treats that connection as equal to a 5671 connection. The client builder and the AMQP backend fixed the scheme to amqps:// and the transport to TCP, so no public option reached the WebSocket binding. Some firewalls block every outbound port except 443. Clients behind those firewalls cannot reach Event Hubs without a transport selector.

A WebSocket connection also needs a TLS stack. Most applications already link one stack. A second stack adds binary size, and it adds a second trust store to configure. azure_core keeps the transport and the stack apart for HTTP, where reqwest is the base feature and reqwest_rustls adds the stack. A consumer of the AMQP crates expects the same shape.

The TLS stack also controls which root certificates the client trusts. A change of stack must keep the anchors that the previous stack used, or a broker behind a private certificate authority stops working.

Changes

  • azure_core_amqp adds the public AmqpTransport { Tcp, WebSocket } enum and an AmqpConnectionOptions::transport field. AmqpConnectionOptions stays a plain options bag with public fields, so ..Default::default() still builds it. The open() function of the fe2o3 backend branches on the transport. The WebSocket arm connects a fe2o3_amqp_ws::WebSocketStream, and it gives the stream to Connection::builder().open_with_stream(...). The AMQP hostname stays the real service host, so AMQP link addressing does not change. SASL ANONYMOUS and CBS authorization stay the same.
  • The WebSocket arm calls connect_with_config, not connect_tls_with_config. fe2o3-amqp-ws puts connect_tls_with_config behind its own TLS features, and a call to it would keep fe2o3_amqp_ws from building on its own. connect_with_config carries no such gate. It uses whichever stack the features of fe2o3-amqp-ws select, and the connection reports TlsFeatureNotEnabled when the graph selects none.
  • The crate offers four features. fe2o3_amqp is the base backend. fe2o3_amqp_rustls adds the TLS stack for AMQP framed on TCP. fe2o3_amqp_ws turns on the WebSocket transport code and the fe2o3-amqp-ws dependency, and it names no TLS stack. fe2o3_amqp_ws_rustls adds the stack for the WebSocket transport. default selects all four. Each stack is rustls with the aws-lc-rs provider, which is the stack that the rest of sdk/core uses.
  • The TCP transport supplies its own TLS connector, which fe2o3_amqp_rustls builds on rustls-platform-verifier. The default connector of fe2o3-amqp fills its root store from webpki-roots, which is a compiled-in copy of the Mozilla root set, and it ignores the trust store of the operating system. native-tls read that store before this change, so the default would drop the roots that an operator installs, and a broker behind a private or an enterprise certificate authority would fail the handshake. The platform verifier reads the trust store of the operating system on every supported target, through Security.framework on Apple, through CryptoAPI on Windows, and through rustls-native-certs elsewhere. The TCP transport therefore keeps the anchors it had, and it agrees with reqwest/rustls on the HTTP side and with the WebSocket transport, which reads the same store through native roots. webpki-roots still arrives as a dependency, because the rustls feature of fe2o3-amqp always names it, and nothing reads it.
  • The connector reaches the builder through rustls_connector, not through the tls_connector alias. rustls_connector needs the rustls feature only, where the alias also needs native-tls to be off, so the call survives a consumer that unifies both features. The connector-typed builder keeps its own open, and that path hands alt_tls_estab to the same transport call as the default path, so alt_tls_establishment(true) keeps its behavior.
  • There is no native-tls feature. fe2o3-amqp accepts one TLS stack only, and it reports TlsConnectorNotFound at run time when both are on. Two features that cannot both be on would break --all-features. To build on another stack, turn off the default features, name the base features, and take a direct dependency on fe2o3-amqp and fe2o3-amqp-ws with the stack you want. Cargo unifies the features, and nothing pulls rustls in. samples/list_blobs_native_tls shows the same pattern for reqwest. The feature comments record this recipe.
  • fe2o3_amqp_rustls and fe2o3_amqp_ws_rustls each take a direct rustls dependency. That dependency only selects the crypto provider, and this crate names one rustls type, which is the ClientConfig that the connector needs. The tokio-tungstenite chain under fe2o3-amqp-ws takes rustls with default-features = false and turns on no provider feature, so ClientConfig::builder() panics when no process-level default is installed. The TCP connector calls the same builder and needs the same guarantee. The default features of rustls give aws-lc-rs, std, and tls12, and deny.toml bans ring, so this repository cannot reach that panic. An application that unifies a second provider into the graph must call CryptoProvider::install_default() first.
  • The workspace moves the fe2o3-amqp family from 0.14 to 0.16, and it adds fe2o3-amqp-ws 0.16, rustls 0.23, rustls-platform-verifier 0.7, and tokio-rustls 0.26. The rustls backend of fe2o3-amqp 0.14 was built on ring, which deny.toml bans. Version 0.16 uses aws-lc-rs instead, so this change closes Cannot use aws-lc-rs for AMQP #4189. reqwest already resolves rustls-platform-verifier and tokio-rustls through its own rustls feature, so the graph gains no new package, and Cargo.lock gains two dependency edges only.
  • azure_messaging_eventhubs forwards all four features. azure_core forwards the reqwest TLS features of typespec_client_core in the same way. An application depends on the Event Hubs crate, so the split does not reach the application without the forward.
  • azure_messaging_eventhubs re-exports AmqpTransport as models::AmqpTransport, next to the other azure_core_amqp types that this crate already re-exports. It adds with_transport(...) on the producer builder and the consumer builder. The transport reaches the connection on all four construction paths, which are open and open_with_connection_string on each of the two builders.
  • EventProcessor takes the transport from the ConsumerClient that build receives. It therefore runs over WebSockets when that client selects them. The documentation on build shows this with an example.
  • The WebSocket address is wss://{host}[:{port}]/$servicebus/websocket/. The other Azure SDKs use the same binding path. A custom endpoint redirects the socket, and the AMQP hostname stays the real service host.
  • The eventhubs_websocket_transport sample opens a producer and a consumer over WebSockets, sends a tagged event, and reads the event back.
  • The docs.rs metadata names all four features, so docs.rs documents both transports together with their TLS stacks.
  • azure_messaging_eventhubs needs the unreleased AmqpTransport. It therefore takes azure_core_amqp and azure_core as local path plus version dependencies, and it takes the local dev-dependencies as path only. This follows the policy in AGENTS.md. azure_messaging_eventhubs_checkpointstore_blob gets the same treatment. Without those lines, the registry build of azure_storage_blob pins the previous azure_core, and two copies of the crate land in the graph. Two copies break the trait bounds that cross between the two crates. These lines return to workspace = true when azure_core 1.2.0 publishes.

Related work

#4873 adds BufferedProducerClient, which gives the crate a third public builder. BufferedProducerClientBuilder forwards application_id, retry_options, and custom_endpoint to ProducerClient::builder(). It needs the same forward for the transport that this change adds. Without that one method, a buffered producer cannot use AMQP over WebSockets. Neither change blocks the other. The second change to merge adds the method.

Both changes edit producer/mod.rs, common/recoverable/connection.rs, and the changelog. The second change to merge therefore needs a small rebase. #4895 also edits common/recoverable/connection.rs.

Test plan

Five feature combinations of azure_core_amqp build, and each build passes --no-default-features. The feature sets are fe2o3_amqp; fe2o3_amqp with fe2o3_amqp_rustls; fe2o3_amqp with fe2o3_amqp_ws; fe2o3_amqp with fe2o3_amqp_ws and fe2o3_amqp_ws_rustls; and all four together. The default build passes, and --all-features --all-targets passes. The base-only build names fe2o3_amqp_ws with no TLS modifier, which is the combination that must compile without a stack.

The unit tests pass. azure_core_amqp runs 122 tests. azure_messaging_eventhubs runs 148 tests, and it ignores 14 live tests. 51 doc tests pass, 4 in the first crate and 47 in the second. The tests cover the WebSocket address construction, and they include an IPv6 case. They also cover the transport that reaches RecoverableConnection, the options that AmqpConnection::open receives, and the builder helper that every open path reads. One test builds the TCP connector, which reports a missing crypto provider at test time instead of at connect time.

cargo fmt --check passes, and cargo clippy --all-features --all-targets passes. cspell passes against the repository configuration. cargo tree shows no ring in the default graph.

A live run against an Event Hubs namespace validates both transports. Three connection-string tests passed over TCP, which are test_round_trip_connection_string, send_eventdata_with_connection_string, and consumer_open_with_connection_string. A temporary test then opened a producer on each transport, read the hub properties, and sent an event. Both transports passed. The eventhubs_websocket_transport sample opened a producer and a consumer with AmqpTransport::WebSocket, sent a tagged event, and read the event back. The process held one established socket during that run, to port 443. The eventhubs_connection_string sample keeps the default transport, and it ran against the same namespace as a control. That process held its socket on port 5671.

A negative control proves that the supplied connector governs the handshake. The service certificate chains to a public authority, and both root sets carry that authority, so a passing test alone does not separate the new connector from the default connector. A temporary build replaced the platform verifier with an empty root store. The live round trip then failed with invalid peer certificate: UnknownIssuer, and it passed again after the revert.

A run from a network that blocks 5671 outbound is still open. A broker behind a private certificate authority has the negative control only, because the test namespace uses a public authority. An application that selects another TLS stack has build coverage only.

@azure-pipelines

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

@azure-pipelines

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds AMQP-over-WebSockets support to Event Hubs, enabling connections over port 443 while retaining TCP as the default.

Changes:

  • Public API: adds AmqpTransport, connection transport options, and Event Hubs builder methods.
  • Implements WebSocket connections and configurable rustls-based TLS features.
  • Adds transport propagation, tests, documentation, and an end-to-end sample.

Reviewed changes

Copilot reviewed 20 out of 21 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Cargo.toml Updates AMQP dependencies and adds TLS dependencies.
Cargo.lock Locks the updated AMQP/WebSocket dependency graph.
eng/dict/crates.txt Registers new dependency names.
sdk/core/azure_core_amqp/Cargo.toml Defines backend, WebSocket, and TLS features.
sdk/core/azure_core_amqp/CHANGELOG.md Documents API, feature, and dependency changes.
sdk/core/azure_core_amqp/src/connection.rs Adds the public transport API.
sdk/core/azure_core_amqp/src/fe2o3/connection.rs Implements TCP TLS and WebSocket connection paths.
sdk/core/azure_core_amqp/src/fe2o3/error.rs Maps WebSocket establishment errors.
sdk/core/azure_core_amqp/src/lib.rs Exports AmqpTransport.
sdk/eventhubs/azure_messaging_eventhubs/Cargo.toml Forwards AMQP transport and TLS features.
sdk/eventhubs/azure_messaging_eventhubs/CHANGELOG.md Documents Event Hubs transport changes.
sdk/eventhubs/azure_messaging_eventhubs/examples/eventhubs_websocket_transport.rs Demonstrates a WebSocket round trip.
sdk/eventhubs/azure_messaging_eventhubs/src/common/authorizer.rs Updates test constructors.
sdk/eventhubs/azure_messaging_eventhubs/src/common/recoverable/connection.rs Propagates transport into AMQP connection options.
sdk/eventhubs/azure_messaging_eventhubs/src/consumer/event_receiver.rs Updates test construction defaults.
sdk/eventhubs/azure_messaging_eventhubs/src/consumer/mod.rs Adds consumer transport selection.
sdk/eventhubs/azure_messaging_eventhubs/src/event_processor/processor.rs Documents inherited consumer transport behavior.
sdk/eventhubs/azure_messaging_eventhubs/src/models/mod.rs Re-exports and documents AmqpTransport.
sdk/eventhubs/azure_messaging_eventhubs/src/producer/batch.rs Updates test construction defaults.
sdk/eventhubs/azure_messaging_eventhubs/src/producer/mod.rs Adds producer transport selection.
sdk/eventhubs/azure_messaging_eventhubs_checkpointstore_blob/Cargo.toml Aligns local core and storage dependencies.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread sdk/eventhubs/azure_messaging_eventhubs/CHANGELOG.md Outdated
Comment thread sdk/core/azure_core_amqp/src/connection.rs Outdated
@j7nw4r
Johnathan W (j7nw4r) force-pushed the j7nw4r/eventhubs-websocket-transport branch from 88ae286 to 3cdffcd Compare August 11, 2026 15:53
@j7nw4r
Johnathan W (j7nw4r) enabled auto-merge (squash) August 11, 2026 18:23
@j7nw4r

Copy link
Copy Markdown
Member Author

Copilot resolve the merge conflicts in this pull request

Copilot AI commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Copilot resolve the merge conflicts in this pull request

Merged main and resolved the conflict in Cargo.lock (commit 0d5442b). The conflict was limited to the lockfile; I kept our AMQP/WebSocket dependency additions and applied main's small azure_storage_blob version bump without a full re-resolve. azure_core_amqp and azure_messaging_eventhubs build, clippy, fmt, and tests all pass with --all-features.

Add a transport-type option so AMQP can be tunneled over secure
WebSockets (wss://, port 443) for networks that block the native AMQP
ports (5671/5672), matching the .NET, Java, and Python Azure SDKs.

- azure_core_amqp: add AmqpTransport enum and AmqpConnectionOptions::transport;
  the fe2o3 backend opens a WebSocketStream and uses open_with_stream for the
  WebSocket arm while keeping the amqps:// URL for AMQP link addressing.
- azure_messaging_eventhubs: add models::TransportType and with_transport_type
  on the producer and consumer builders.

Resolves #3601.
EventProcessor::builder().build() takes a caller-constructed ConsumerClient
and reuses its connection for every partition receiver, so the transport is
inherited from that client. Document this on build() with an
AMQP-over-WebSockets example, and add a test asserting the transport reaches
the RecoverableConnection.
Add a runnable sample that opens a producer and a consumer with
TransportType::AmqpWebSocket, sends a tagged event, and reads it back.
It mirrors the connection-string sample, so the two can be run side by
side to compare transports.
Add an IPv6 regression test for the WebSocket address builder, register
fe2o3-amqp-ws in the crate spelling dictionary in both the underscore and
the hyphen form, and record the AmqpConnectionOptions field addition
under Breaking Changes.
Add the native_tls and rustls features, which select the TLS backend for
both fe2o3-amqp and fe2o3-amqp-ws. The default feature selects native_tls.
The WebSocket transport needs one of them, because fe2o3-amqp-ws gates its
TLS entry point behind its own features. Without a backend the crate now
compiles and the transport returns an error at run time. Before this
change, "--no-default-features --features fe2o3_amqp" did not compile.

Mark AmqpConnectionOptions as non_exhaustive and add with_* methods for
each field, so later fields are additive. Update the Event Hubs call site
to use the new methods.
The rustls backend of fe2o3-amqp depends on ring, and deny.toml bans that
crate, so "cargo deny --all-features check bans" failed. Keep native_tls
as the only TLS backend and gate the WebSocket transport on it. See issue
#4189 for the ring work.
Extract RecoverableConnection::connection_options so a test can assert the
transport reaches the options given to AmqpConnection::open. A test on the
constructor alone passed even when create_connection dropped the call.

Route each builder open path through one transport() helper and test it,
which covers the connection-string path too.

Correct the CHANGELOG example: "..Default::default()" does not build a
non_exhaustive struct from another crate. Show the with_ method instead.
Document on AmqpTransport::WebSocket, on with_transport, and on the Event
Hubs TransportType::AmqpWebSocket that the variant needs a TLS backend.
A build with default-features = false can select the variant, but the
connection then returns an error when it opens.
The Event Hubs crate depends on azure_core_amqp with its default features,
so native_tls is always on and the WebSocket variant works in every build
of this crate. The previous text described a configuration the manifest
does not produce. Also qualify the azure_core_amqp text: the run-time
error needs the fe2o3_amqp feature, since the no-op backend panics.
The receiver attach test that came in with #4807 calls
RecoverableConnection::new with the previous six arguments, so it stopped
compiling after this branch added the transport argument. Pass the default
transport.
…mple

The open method takes the fully qualified namespace and uses it as the
AMQP host, so the short name in the TransportType example does not
resolve.
The run-time error needs the fe2o3_amqp feature. Without a backend
feature the no-op connection takes over, and its open calls
unimplemented!, so it panics.
`AmqpConnectionOptions` returns to a plain struct with public fields.
The `#[non_exhaustive]` marker and the `with_` methods are gone, so a
struct literal with `..Default::default()` builds it again. The repo
allows the `constructible_struct_adds_field` semver lint for this
reason.

AMQP over WebSockets is now a feature of this crate, not a TLS
selection. `websockets_rustls` and `websockets_native_tls` each turn on
`fe2o3-amqp-ws` and forward one of its TLS backends. `default` selects
`websockets_rustls`, which is rustls with the aws-lc-rs provider, the
same stack `azure_core` selects for HTTP. The direct `rustls`
dependency selects that provider, because `fe2o3-amqp-ws` takes rustls
without default features.

The `fe2o3_amqp` feature no longer pulls in `fe2o3-amqp-ws`, so a build
with that feature alone compiles. The TCP transport keeps
`fe2o3-amqp/native-tls`, which `default` selected before this change:
the rustls backend of `fe2o3-amqp` 0.14 pulls in `ring`, which
`deny.toml` bans (#4189).
`models::TransportType` duplicated `azure_core_amqp::AmqpTransport`,
which this crate already re-exports types from. The enum and its `From`
implementation are gone. `models` re-exports `AmqpTransport` beside
`AmqpMessage` and `AmqpValue`, and the builder method is
`with_transport`, which takes that type.
The WebSocket transport had two features, `websockets_rustls` and
`websockets_native_tls`. Each one turned on the transport and one TLS
stack in a single step, so a consumer could not select the TLS stack
that the rest of the application uses.

Give the transport the base plus modifier shape that `azure_core` uses
for HTTP, where `reqwest` is the base and `reqwest_rustls` adds the
stack. Name the features after the backend crate `fe2o3-amqp-ws`, which
is the rule that `reqwest` and the existing `fe2o3_amqp` both follow.
`fe2o3_amqp_ws` turns on the transport code, and
`fe2o3_amqp_ws_rustls` and `fe2o3_amqp_ws_native_tls` each add one
stack on top of it.

Add `fe2o3_amqp_native_tls` for the TLS stack of the TCP transport.
`default` named `fe2o3-amqp/native-tls` inline before, and `fe2o3-amqp`
is only a dev-dependency of `azure_messaging_eventhubs`, so that crate
could not forward it. Without the named feature, an application that
turned off the default features to select a WebSocket stack also lost
the TLS stack of the TCP transport.

Forward all five features from `azure_messaging_eventhubs`, in the same
way that `azure_core` forwards the `reqwest` TLS features of
`typespec_client_core`. That crate is the one an application depends
on, so the split was not reachable without the forward.

Keep the features additive, which the Cargo reference asks for. A build
can hold both WebSocket stacks; `fe2o3-amqp-ws` then uses native-tls,
and the connection now logs a warning that names the stack in use.
The rebase onto main brought in new tests that build a
`RecoverableConnection` or a `ProducerClient`. Both constructors take
the transport, so each new call site needs the argument. The tests do
not exercise the transport, so they pass the default, which is TCP.
`fe2o3_amqp_ws_native_tls` was a parity feature: it named a second TLS
stack that this crate does not otherwise use. `azure_core` has no such
feature. It offers `reqwest` as the base and `reqwest_rustls` for the
stack it ships, and `samples/list_blobs_native_tls` selects another
stack with a direct `reqwest` dependency and Cargo feature unification.

Follow that pattern. Keep `fe2o3_amqp_ws` as the base and
`fe2o3_amqp_ws_rustls` for the stack this repository ships. Gate the
transport code on the base feature alone, which is what makes
unification work: an application can now name `fe2o3_amqp_ws`, add
`fe2o3-amqp-ws` with `native-tls`, and get the transport on that stack.
The old gate read this crate's own TLS features, so the same manifest
compiled the transport out and returned an error at run time.

One stack must be selected somewhere in the graph, because
`fe2o3-amqp-ws` puts `connect_tls_with_config` behind its own TLS
features. `fe2o3_amqp_ws` on its own therefore does not build. `reqwest`
differs here, since it compiles with no TLS feature at all.

Add `tokio-tungstenite` to the crate dictionary, in both spellings,
beside the other entries.
`fe2o3-amqp` 0.16 builds its rustls backend on aws-lc-rs, where 0.14
built it on `ring`, which `deny.toml` bans. That was the only reason
this crate named native-tls for AMQP framed on TCP.

Update the `fe2o3-amqp` family from 0.14 to 0.16, which needs no source
change, and replace `fe2o3_amqp_native_tls` with `fe2o3_amqp_rustls`.
The `default` feature selects it, so both the TCP and the WebSocket
transport now run on rustls with the aws-lc-rs provider, the stack that
the rest of `sdk/core` uses.

There is no native-tls feature. `fe2o3-amqp` accepts one TLS stack and
reports `TlsConnectorNotFound` at run time when both are on, so a pair
of features that cannot both be on would break `--all-features`. A
consumer that wants another stack turns off the default features and
takes a direct dependency on `fe2o3-amqp` with the stack they want,
which is the pattern `samples/list_blobs_native_tls` shows for
`reqwest`.

The `azure_core_amqp` dependency of `azure_messaging_eventhubs` now
sets `default-features = false`, in the same shape as `azure_core`, so
that turning the default features off can drop the rustls stack.

Closes #4189.
`fe2o3_amqp_ws` is the base feature and names no TLS stack, but the
transport called `WebSocketStream::connect_tls_with_config`, which
`fe2o3-amqp-ws` puts behind its own TLS features. A build that named
`fe2o3_amqp_ws` alone therefore failed with E0599, so the advertised
base feature did not compile.

Call `connect_with_config` instead. It carries no feature gate, and it
reaches the same `tokio_tungstenite` connect path with no connector,
which is what the previous call passed. The behavior is unchanged when a
TLS stack is selected, and a build that selects none now reports
`TlsFeatureNotEnabled` when the connection opens.

This matches `reqwest`, which also compiles with no TLS feature and
fails at run time, and it needs no TLS modifier feature of its own.

Update the feature comment, the `AmqpTransport::WebSocket` doc, and the
changelog, which all said the base feature could not build.
`RecoverableConnection::new` takes a `transport` argument on this branch,
and `ConsumerClientOptions` carries a `transport` field. PR #4933 landed
on main after this branch forked and added a unit test and a builder call
that use the earlier shapes.

Git merges the two changes without a textual conflict, so the branch
built on its own while the pipeline, which builds the merge with main,
failed with E0061, E0063, and E0277. Pass the default transport at both
new call sites.
The `docs.rs` metadata named `fe2o3_amqp`, `fe2o3_amqp_ws`, and
`fe2o3_amqp_ws_rustls`, so docs.rs built the crate without the TLS stack
for AMQP framed on TCP. Add `fe2o3_amqp_rustls`.

The list now holds every feature that `default` selects. It leaves out
`test`, which is an internal helper, and `ffi`, which does not build on
its own (#4978). `azure_core` names its features the same way.
The comment said the receiver and the stream hold references to the
consumer's connection, so the caller must release them before the
consumer closes. That overstates the requirement.

`ConsumerClient::close` takes `&self` on the connection manager and
drains the receiver cache, so it detaches the receiver link on its own.
A receiver that outlives its client then reports the closed client
instead of opening a second connection (#4931). The order of
`receiver.close()` and `consumer.close()` is free.

The one order the caller must keep is the stream before the receiver.
`stream_events` borrows `&self` and `close` takes `self` by value, so
the compiler enforces it. The block above already covers that.
Two entries under Features Added are breaking. Restore the section and
move them.

The `AmqpConnectionOptions::transport` field lands on a struct that is
not `#[non_exhaustive]`, so an existing struct literal that names every
field no longer compiles.

The `default` feature swap from `fe2o3-amqp/native-tls` to
`fe2o3_amqp_rustls` changes the trust anchors. `fe2o3-amqp` builds its
rustls root store from `webpki-roots`, and native-tls read the root
store of the operating system.

The same swap reaches `azure_messaging_eventhubs` through its forwarded
features, so its changelog gets the matching entry.
An explicit port on `custom_endpoint` carries into the `wss://` address,
so a proxy named as `amqps://proxy:5671` dials 5671 and not 443.

The .NET Azure SDK does the same. `AmqpClient` builds its
`ConnectionEndpoint` from the host and the explicit port of
`CustomEndpointAddress`, and `CreateTransportSettingsForWebSockets`
carries that port into the WebSocket URI. A proxy that accepts
WebSockets on its own port stays reachable, so the behavior stands.

Document the rule on `AmqpConnectionOptions::custom_endpoint` and on the
`with_custom_endpoint` builders, and pin the AMQP-port case with a test.
The `rustls` feature of `fe2o3-amqp` 0.16 fills its root store from
`webpki-roots` only, which is a compiled-in copy of the Mozilla root
set. The connection called `open()` with no connector, so AMQP framed
directly on TCP took that default and stopped reading the trust store of
the operating system. A broker behind a private or an enterprise
certificate authority would then fail the handshake, even when the
operating system trusts that authority.

Supply a connector built on `rustls-platform-verifier` instead. The TCP
transport now trusts the same roots as `reqwest/rustls` on the HTTP
side, and it agrees with the WebSocket transport, which already reads
the same store through native roots.

Use `rustls_connector`, because it needs the `rustls` feature only,
where `tls_connector` also needs `native-tls` to be off. The
connector-typed builder hands `alt_tls_estab` to the same transport call
as the default path, so `alt_tls_establishment(true)` keeps its
behavior. Add a test that builds the connector, which catches a missing
crypto provider at test time instead of at connect time.
The Event Hubs breaking-change entry still described the webpki-roots
behavior that the platform verifier replaced, so it warned about a
handshake failure behind a private certificate authority that no
longer happens. It now matches the azure_core_amqp entry.

The AmqpTransport::WebSocket documentation promised an error when the
connection opens without the fe2o3_amqp_ws feature. A build that names
no backend feature gets NoopAmqpConnection, whose open panics, so the
statement now covers both cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Azure.Core The azure_core crate Event Hubs Storage Storage Service (Queues, Blobs, Files)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cannot use aws-lc-rs for AMQP

6 participants