Skip to content

Replace Jetty with Netty in the core connectors (3.0 milestone 2) - #1537

Merged
jlouvel merged 6 commits into
3.0from
feat/netty-connector
Oct 20, 2026
Merged

jlouvel merged 6 commits into
3.0from
feat/netty-connector

Conversation

@jlouvel

@jlouvel jlouvel commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator

Milestone 2 of Restlet 3.0: the HTTP/HTTPS server and client connectors of the core move from Jetty 12 to Netty 4.2, and Java 25 becomes the baseline. The connector class names are unchanged (HttpServerHelper, HttpsServerHelper, HttpClientHelper), and no Netty type is exposed by the public API.

Main changes

  • Java 25 minimum, in the build and the CI workflows.
  • Server:
    • Calls run on virtual threads by default (threadPool.virtual).
    • Supports HTTP/1.1 and HTTP/2: ALPN over TLS, and h2c with the upgrade mechanism or prior knowledge.
    • Back-pressure on reads and writes.
    • server.maxConcurrentCalls answers 503 above its limit.
    • Graceful shutdown.
  • Client:
    • Per-origin connection pool, with HTTP/2 connections shared by concurrent calls.
    • Proxy support: absolute URIs for HTTP, CONNECT for HTTPS.
    • gzip decoding and host name verification.
    • Redirects are followed by the connector, without forwarding credentials to other origins.
  • Experimental HTTP/3 over QUIC:
    • Off by default.
    • Needs the optional io.netty:netty-codec-http3 dependency and its native library.
    • The server advertises it with Alt-Svc.
    • Its limits are documented in Http3Support.
  • MultiPartRepresentation has its own Part type, parser and generator, with no Jetty types.
  • Removed: the Jetty classes, the client cookie and authentication stores, and the Jetty-specific parameters.
  • SLF4J extension: re-exports org.slf4j and uses slf4j-simple in tests, because the logger implementation used to come from Jetty.
  • Separate commit: removes the Protocol.WAR constant, a leftover of the Servlet mode.

Breaking changes

All breaking changes are listed in the Milestone 2 section of changes.md: removed APIs, renamed or removed parameters, the HTTP/3 status, and the multipart API.

Tests

  • Existing connector tests are adapted.
  • New tests cover redirects, the proxy, back-pressure, HTTP/2 multiplexing, the 503 limit, TLS client authentication and HTTP/3. The HTTP/3 tests are skipped when the native QUIC library isn't available.
  • Local full build passes on Java 25 for all modules: core verify with 2,188 tests, JaCoCo coverage checks met, and SpotBugs within its threshold.
  • The last full build ran before the Protocol.WAR commit. After it, only ProtocolTestCase and Http3TestCase were re-run, and both pass.

Review notes

  • The back-pressure tests depend on timing and could be flaky on slow CI runners.
  • HTTP/1.1 stays the default for the HTTPS server and the client; HTTP/2 must be enabled explicitly.
  • The request protocol stays HTTPS/1.1 on HTTP/2 and HTTP/3 calls, as with Jetty. The real version is in the org.restlet.http.version attribute.

The "war" scheme (Web Archive Access Protocol) was a leftover of the Servlet
mode, which is no longer supported. Protocol.valueOf("war") now returns a new,
unknown protocol. The SpringContext Javadoc example uses a CLAP reference
instead.

Signed-off-by: Jerome Louvel <374450+jlouvel@users.noreply.github.com>
The HTTP/HTTPS server and client connectors of the core now rely on Netty 4.2
instead of Jetty 12, keeping the connector class names (HttpServerHelper,
HttpsServerHelper, HttpClientHelper). No Netty type is exposed by the public
API.

- Java 25 is the minimum version, in the build and the CI workflows.
- Server calls run on virtual threads by default ("threadPool.virtual").
- HTTP/1.1, HTTP/2 (ALPN, h2c upgrade and prior knowledge) and back-pressure on
  reads and writes; "server.maxConcurrentCalls" answers 503 above its limit.
- Client: per-origin connection pool, shared HTTP/2 connections, proxy support
  (absolute URIs and CONNECT), gzip decoding, host name verification, and
  redirects followed by the connector without leaking credentials.
- Experimental HTTP/3 over QUIC, off by default, with the optional
  io.netty:netty-codec-http3 dependency and its native library. The server
  advertises it with Alt-Svc. Limits are documented in Http3Support.
- MultiPartRepresentation has its own Part type, parser and generator.
- Removed the Jetty classes, the client cookie and authentication stores, and
  the Jetty-specific parameters (see changes.md).
- The SLF4J extension re-exports org.slf4j and uses slf4j-simple in tests.
- New tests for redirects, the proxy, back-pressure, HTTP/2 multiplexing, the
  503 limit, TLS client authentication and HTTP/3.

Signed-off-by: Jerome Louvel <374450+jlouvel@users.noreply.github.com>
@jlouvel jlouvel self-assigned this Oct 17, 2026
@jlouvel
jlouvel requested a review from thboileau October 17, 2026 23:36
- Wait for the calls in progress with a condition on graceful stop, instead
  of polling with Thread.sleep (S2276, the blocker failing the quality gate).
- Retry on another ephemeral port when the UDP port matching the TCP port is
  already in use, so HTTP/3 servers started on port 0 don't fail randomly.
- Reduce the cognitive complexity of NettyServerHelper.start(),
  initConnection(), NettyClientCall.writeRequest(),
  NettyClientHandler.channelRead() and ChannelInputStream.awaitData().
- Pass the helper to NettyServerCall instead of eight parameters.
- Declare specific exceptions instead of Exception in Http3Support and the
  validateConfiguration() hook.
- Catch specific throwables in NettyServerHandler.runCall().
- Validate before super() in MultiPartRepresentation, using flexible
  constructor bodies, and stop assigning the loop counter in unquote().
- Use unnamed catch variables, Math.clamp, constants for the pipeline handler
  names, parameterized logging, and a loop without break statements.
- Tests: detect back-pressure stalls instead of sleeping a fixed time, and
  remove an empty debug block.

Signed-off-by: Jerome Louvel <374450+jlouvel@users.noreply.github.com>
- Re-interrupt the thread when interrupted while creating the HTTP/3 client
  (S2142, the reliability issue failing the quality gate), and catch the
  specific exceptions it declares.
- Keep the Condition waits of ChannelInputStream inside the while loop that
  rechecks the state (S2274).
- Move the argument checks of the MultiPartRepresentation parsing constructor
  into a static method called in super() (S8444).

Signed-off-by: Jerome Louvel <374450+jlouvel@users.noreply.github.com>
@jlouvel

jlouvel commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator Author

@thboileau Could you review this PR?

@thboileau-git

Copy link
Copy Markdown
Collaborator

I see some unit tests failing when I run mvn clean verify. But, it just shows that tests are kind of flaky:

  • these tests are not linked to the update
  • in my IDE these tests are fine when I run them one by one

@sonarqubecloud

Copy link
Copy Markdown

@jlouvel
jlouvel merged commit 47c1d9a into 3.0 Oct 20, 2026
3 checks passed
@jlouvel
jlouvel deleted the feat/netty-connector branch October 20, 2026 12:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants