Repository navigation
Feedback Vertx
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.
None so far: neither #1714 nor #1693 links an upstream Vert.x or Netty issue.
-
V-T1. A server's SNI key-manager lookup runs on the event loop by default (regression from 4.5). 4.5 always ran
keyManagerFactoryMapperon a worker; 5.1.8 runs it on the engine's delegated-task executor, which is immediate unlesssetUseWorkerThread(true). A mapper that loads or mints a certificate then blocks the event loop. isx works around it by settinguseWorkerThreadon the MITM server (#1687). (#1714)-
Verify:
./run.sh 5.1.8 tls/SniMappingOnEventLoop.javaprintsmapper ran on EVENT LOOPand exits 1;./run.sh 4.5.35 ...runs it on a worker and exits 0. TheserverNameAsyncMappingjavadoc still says "worker thread". -
Discuss with the team: run the mapper on a worker again, or document the change and fix the javadoc.
useWorkerThreadalso 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.
-
Verify:
-
V-T2.
updateSSLOptionsignores changes when the key/cert options are equal. Restricting protocols to TLSv1.2 with the same certificate returnsfalse, and the server keeps negotiating TLSv1.3. (#1714)-
Verify:
./run.sh 5.1.8 tls/UpdateSslOptionsIgnoresNonKeyCertChanges.javaprintsreturned false, client negotiated TLSv1.3withoutforceand exits 1; 4.5.35 applies the change and exits 0. The cause isSslContextManager'sConfigKey, whoseequalscovers 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.
-
Verify:
-
V-T3. Per-connect
ClientSSLOptionsdiffering only inhostnameVerificationAlgorithmshare a cached context (security-relevant). On oneNetClient, 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.javaprintsalgo='HTTPS' -> connected (expected rejected)and exits 1. 4.5 has no per-connectClientSSLOptions. - 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.
-
Verify:
-
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 theupdateSSLOptionsjavadoc, which does not say so. - Discuss with the team: document it, or apply those fields too.
-
Verify: read
-
V-T5. Cache sizes (minor). Superseded server SSL providers stay in a 64-entry cache after reloads, and the per-SNI
SslContextcache 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
SslContextbuilt 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.
-
Verify: a Netty client
-
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.handleMessageignores 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.javaprintspipelined: ... TIMEOUT (frame lost), where 4.5.35 printsecho: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.
-
Verify:
-
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.acceptagainst the client'sWebSocketClientImpl, 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.
-
Verify: read
-
V-W3. The
webSocketHandshakeHandlerjavadoc is silent on four things (docs). It now works without awebSocketHandler, setting both runs both, an undecided handshake hangs until the idle timeout, andaccept()resolves only after the 101 is flushed.-
Verify: the
handshakemode ofwebsocket/PipelinedFrameDropped.javaon 5.1.8 uses the handshake handler alone. On 4.5.35 the same mode fails withSet 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.
-
Verify: the
-
V-W4.
accept()cannot set the sub-protocol or response headers, andreject(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
ServerWebSocketHandshakeAPI. - Discuss with the team: overloads taking headers, a sub-protocol and a body.
-
Verify: the
-
V-W5.
WebSocketConnectOptionsinheritssetServer(), butWebSocketClientuses 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:
WebSocketClientImplreads the server address only as a fallback. -
Discuss with the team: honour
setServer()with a host set, asHttpClientdoes.
-
Verify:
-
V-W6.
ServerWebSocketHandshaker.reject()fails its own future with a bareRejectedExecutionException.- 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.
-
V-H1. Nothing says whether
HttpClientRequest.authority()is theHostvalue or the connect target undersetServer()(docs). It is theHostvalue 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)andsetHost(name), then one without a host, and printauthority(). -
Discuss with the team: document it beside
getHost()/getPort(), whichauthority()replaces.
-
Verify: build a request with
-
V-H2. Client ALPN is decided by whether
HTTP_2is in the protocol list, not bysetUseAlpn, and Vert.x 5 now also offershttp/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 whyhttp/1.0is offered.
-
Verify: capture the ClientHello of a client with
-
V-H3.
LIFO_SELECTORnever updateslast, andPoolOptions()setshttp3MaxSizefrom the HTTP/2 default (minor).-
Verify: read the selector and the
PoolOptionsconstructor. - Discuss with the team: two small fixes.
-
Verify: read the selector and the
-
V-P1.
vertx-core'sJava21profile 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.coreon any project depending only onvertx-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.mapToandmapFrommust add databind themselves. As of 2026-10-26,vertx-core5.2.2 (on Quarkusmain) has no such profile, and aquarkus-vertxapp gets nojackson-databind.
-
Verify:
-
F. API removals and renames that need hand ports.
setHandshake(Future)becamewebSocketHandshakeHandler,getHost()/getPort()becameauthority(),sendHead()becamewriteHead(), andupdateSSLOptionstakesServerSSLOptions.PoolOptionsandWebSocketClientreplace the old client setters, and theHandleroverloads ofwriteandendare 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).
-
Verify: the list in #1714 (F) and
-
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).