Skip to content

Feedback Vertx

Sanne's AI assistant edited this page Oct 26, 2026 · 2 revisions

Feedback: Vert.x

Findings about Vert.x 5.1.8 from the Quarkus 3.40.1.1 to 4.0.0.Beta1 upgrade, which moved incus-spawn's proxy from Vert.x 4.5.35 to 5.1.8. They are recorded in #1714 and PR #1693, and in full in docs/dev/QUARKUS-4.md ("Feedback for the Vert.x team") on the quarkus-4-redo branch. Codes (V-*) and letters refer to that record and to #1714. The Quarkus side of the same upgrade is on Feedback: Quarkus. Nothing here has been filed upstream yet.

Each item has two follow-ups for Sanne:

  • Verify: how to check it, with the smallest reproducer we have.
  • Discuss with the team: the upstream change we propose, and what it costs.

The reproducers are single Java files in docs/dev/quarkus-4-repro/, depending only on vertx-core. ./run.sh <vertx-version> <file> runs one: it exits 1 when the bug reproduces, 0 when the behaviour is the expected one, and 2 when Maven cannot resolve that version. On 2026-10-26 every reproducer was rerun against 4.5.35 and 5.1.8 on JDK 25, with the results given below.

Known upstream issues we wait on

None so far: neither #1714 nor #1693 links an upstream Vert.x or Netty issue.

Other findings

TLS

  • V-T1. A server's SNI key-manager lookup runs on the event loop by default (regression from 4.5). 4.5 always ran keyManagerFactoryMapper on a worker; 5.1.8 runs it on the engine's delegated-task executor, which is immediate unless setUseWorkerThread(true). A mapper that loads or mints a certificate then blocks the event loop. isx works around it by setting useWorkerThread on the MITM server (#1687). (#1714)

    • Verify: ./run.sh 5.1.8 tls/SniMappingOnEventLoop.java prints mapper ran on EVENT LOOP and exits 1; ./run.sh 4.5.35 ... runs it on a worker and exits 0. The serverNameAsyncMapping javadoc still says "worker thread".
    • Discuss with the team: run the mapper on a worker again, or document the change and fix the javadoc. useWorkerThread also moves the engine's own handshake tasks to the worker pool, so the two cannot be chosen separately today; a separate switch for the mapper would let a server keep handshakes on the event loop.
  • V-T2. updateSSLOptions ignores changes when the key/cert options are equal. Restricting protocols to TLSv1.2 with the same certificate returns false, and the server keeps negotiating TLSv1.3. (#1714)

    • Verify: ./run.sh 5.1.8 tls/UpdateSslOptionsIgnoresNonKeyCertChanges.java prints returned false, client negotiated TLSv1.3 without force and exits 1; 4.5.35 applies the change and exits 0. The cause is SslContextManager's ConfigKey, whose equals covers only key/cert, trust and CRL options.
    • Discuss with the team: include protocols, cipher suites and client auth in the cache key, or compare the full options before reusing a provider. The cost is more cache entries for servers that update often.
  • V-T3. Per-connect ClientSSLOptions differing only in hostnameVerificationAlgorithm share a cached context (security-relevant). On one NetClient, a connect with "" followed by one with "HTTPS" to a mismatched certificate succeeds, so verification is silently skipped. isx sets no per-connect SSL options, so it is not affected. (#1714)

    • Verify: ./run.sh 5.1.8 tls/ClientSslCacheIgnoresHostnameVerification.java prints algo='HTTPS' -> connected (expected rejected) and exits 1. 4.5 has no per-connect ClientSSLOptions.
    • Discuss with the team: put the verification algorithm (and per-connect protocols and ciphers) in the cache key. Being security-relevant, this one may belong in Vert.x's private disclosure channel rather than a public issue; worth deciding with them first.
  • V-T4. updateSSLOptions(ServerSSLOptions) ignores the update's SNI, ALPN and handshake timeout (docs). Those stay as captured at listen time.

    • Verify: read NetServerImpl (the options captured at listen) against the updateSSLOptions javadoc, which does not say so.
    • Discuss with the team: document it, or apply those fields too.
  • V-T5. Cache sizes (minor). Superseded server SSL providers stay in a 64-entry cache after reloads, and the per-SNI SslContext cache is a fixed, undocumented 16-entry LRU.

    • Verify: reload a server's SSL options repeatedly with fresh key/cert options and watch the provider map in a heap dump.
    • Discuss with the team: evict a provider once no server uses it, and document or expose the SNI cache size.
  • V-T6. Netty 4.2 clients verify hostnames by default (migration guide). It changes code that builds Netty SslContexts directly.

    • Verify: a Netty client SslContext built the 4.1 way now rejects a certificate whose name does not match.
    • Discuss with the team: a line in the Vert.x 5 migration guide.

WebSocket

  • V-W1. A frame pipelined with the upgrade request is dropped, and its buffer leaks (regression). It is decoded before the HTTP handler is swapped for the WebSocket one, and Http1ServerConnection.handleMessage ignores it without releasing its buffer. RFC 6455 says a client waits for the 101, so conforming clients are not affected; isx disabled the test that pinned the 4.5 behaviour. (#1714, #1693)

    • Verify: ./run.sh 5.1.8 websocket/PipelinedFrameDropped.java prints pipelined: ... TIMEOUT (frame lost), where 4.5.35 prints echo:hi. ./run.sh 5.1.8 websocket/PipelinedFrameLeaks.java 2>&1 | grep -c LEAK: counts 26 to 31 leak reports, against 0 on 4.5.35.
    • Discuss with the team: at least release the buffer; better, hand the frame to the WebSocket once it exists. Relaying it costs buffering between the upgrade and the handler, which 4.5 already did.
  • V-W2. The server-side accept() hands over an unpaused socket, while the client side pauses a new WebSocket until the user's callback has run. A frame that arrives before the handler is set can be lost; isx pauses the socket first thing.

    • Verify: read ServerWebSocketHandshaker.accept against the client's WebSocketClientImpl, which pauses.
    • Discuss with the team: pause until the callback has run, as the client does. The cost is a behaviour change for code that relies on reading at once.
  • V-W3. The webSocketHandshakeHandler javadoc is silent on four things (docs). It now works without a webSocketHandler, setting both runs both, an undecided handshake hangs until the idle timeout, and accept() resolves only after the 101 is flushed.

    • Verify: the handshake mode of websocket/PipelinedFrameDropped.java on 5.1.8 uses the handshake handler alone. On 4.5.35 the same mode fails with Set request or WebSocket handler first, which shows the first point.
    • Discuss with the team: document all four; the hang could also be bounded by failing an undecided handshake when the handler returns.
  • V-W4. accept() cannot set the sub-protocol or response headers, and reject(int) cannot send headers or a body (API gap for proxies). A proxy cannot pass upstream's 401, 403 or 429 through as a real HTTP answer.

    • Verify: the ServerWebSocketHandshake API.
    • Discuss with the team: overloads taking headers, a sub-protocol and a body.
  • V-W5. WebSocketConnectOptions inherits setServer(), but WebSocketClient uses it only when no host is set. So a caller cannot pin an address while keeping the host for SNI, which the HTTP client allows.

    • Verify: WebSocketClientImpl reads the server address only as a fallback.
    • Discuss with the team: honour setServer() with a host set, as HttpClient does.
  • V-W6. ServerWebSocketHandshaker.reject() fails its own future with a bare RejectedExecutionException.

    • Verify: reject a handshake and log the returned future's cause.
    • Discuss with the team: a meaningful exception, or a succeeded future once the rejection is written.

HTTP client

  • V-H1. Nothing says whether HttpClientRequest.authority() is the Host value or the connect target under setServer() (docs). It is the Host value when the request sets a host; without one it falls back to the server address, and a trailing dot is stripped.

    • Verify: build a request with setServer(ip) and setHost(name), then one without a host, and print authority().
    • Discuss with the team: document it beside getHost()/getPort(), which authority() replaces.
  • V-H2. Client ALPN is decided by whether HTTP_2 is in the protocol list, not by setUseAlpn, and Vert.x 5 now also offers http/1.0.

    • Verify: capture the ClientHello of a client with setUseAlpn(false) and HTTP/2 enabled.
    • Discuss with the team: respect setUseAlpn, or deprecate it, and say why http/1.0 is offered.
  • V-H3. LIFO_SELECTOR never updates last, and PoolOptions() sets http3MaxSize from the HTTP/2 default (minor).

    • Verify: read the selector and the PoolOptions constructor.
    • Discuss with the team: two small fixes.

Packaging and migration

  • V-P1. vertx-core's Java21 profile makes Jackson 3 core and databind non-optional (Q-2). Every Vert.x app on JDK 21 or later then carries databind, about 18 MB in isx's native proxy, which excludes it. (#1714)

    • Verify: mvn dependency:tree -Dincludes=tools.jackson.core on any project depending only on vertx-core, on JDK 21 or later.
    • Discuss with the team: mark them optional, as the Jackson 2 ones are. The cost is that apps using JsonObject.mapTo and mapFrom must add databind themselves. As of 2026-10-26, vertx-core 5.2.2 (on Quarkus main) has no such profile, and a quarkus-vertx app gets no jackson-databind.
  • F. API removals and renames that need hand ports. setHandshake(Future) became webSocketHandshakeHandler, getHost()/getPort() became authority(), sendHead() became writeHead(), and updateSSLOptions takes ServerSSLOptions. PoolOptions and WebSocketClient replace the old client setters, and the Handler overloads of write and end are gone. (#1714)

    • Verify: the list in #1714 (F) and docs/dev/QUARKUS-4.md, "What the upgrade itself changes".
    • Discuss with the team: whether the Vert.x 5 migration guide covers each; recipes for them are a Quarkus item (I on Feedback: Quarkus).
  • G. Behaviour changes that still compile (V-T1, V-W1 to V-W3). Hanging handshakes, the SNI lookup on the event loop, and dropped pipelined frames all compile unchanged and fail at run time. (#1714)

    • Verify: the reproducers above.
    • Discuss with the team: a "behaviour changes" section in the Vert.x 5 migration guide; Quarkus would link it from its own guide (J on Feedback: Quarkus).

Clone this wiki locally