Skip to content

Add R1 transport and lifecycle foundations - #1562

Merged
jlouvel merged 9 commits into
3.0from
feat/rpc-r1-foundations
Oct 31, 2026
Merged

jlouvel merged 9 commits into
3.0from
feat/rpc-r1-foundations

Conversation

@jlouvel

@jlouvel jlouvel commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Deliver the R1 protocol-neutral transport foundations while preserving independent MCP, gRPC and Smithy SDK contracts. Stacked on #1556; this PR does not merge R0 or deliver the R2–R6 production extensions.

  • Add supported org.restlet.util.StreamTransport, backed by unexported engine.stdio: on-demand bounded reads, serialized/flushed opaque writes, borrowed-stream cancellation and owned subprocess/pipes, separately drained bounded stderr, explicit startup/exit/I/O/cleanup failures.
  • Require explicit or uniquely registered STDIO helper selection. No default MCP helper, framing, dispatcher, global stream redirection or core SDK dependency.
  • Add first-cause Request.getDisconnectSignal() independent of entity I/O, wrapper delegation and independent copied lifecycle. Remember cancellation before connector action registration; connector actions remain idempotent.
  • Fix idle HTTP/1.1 disconnect observation after body consumption with bounded ordered pipeline lookahead. Preserve upgrade/CONNECT handoff; release queued buffers on overflow, disconnect and handler replacement. Notify Netty client connection/send failures and HTTP stream failures.
  • Document finite core settings, independent extension limits/ownership, URI-origin authority/pooling and explicit deferral/rejection of unsupported physical-connection controls. Production MCP helpers/framing remain R2; native gRPC readiness/connection conformance remains later delivery; Smithy API remains unvalidated.

Final-head follow-ups

The latest head afe0801 also fixes a deterministically reproduced client completion-publication gap: terminal state is now visible before body EOF wakes consumers. Added repeated immediate closes on empty 401/403 responses over HTTP/1.1 and HTTP/2 and included all eight existing authorization cases. Strict Javadoc and static-analysis findings were corrected without changing workflow or gate configuration.

Validation (Java 25)

  • Final live-enabled R1 + native SDK + HTTP/TLS/HTTP3/SSE/upgrade selector: 145 tests, 0 failures, 0 errors, 0 skipped. Includes actual Ikanos differential, exact 1 MiB/1024-message lookahead limits and buffer release, blocked borrowed input/output, owned subprocess stderr/exit/stop, late/copy/wrapper lifecycle and pre-registration cancellation.
  • Compile independent named-module examples using only the public core byte API or native MCP/gRPC SDK APIs. Assert no core SDK module requirement, no exported STDIO implementation, and no official gRPC HTTP engine in the resolved layer.
  • Full core run: 2257 tests, 1 failure, 0 errors, 0 skipped. The failure is the existing shutdown wall-clock tolerance test (whenServerIsHandlingBlockingRequestThenItRefusesNewRequest): 1.715 s versus 1 s +/- 200 ms. The identical test also failed on unchanged R0 (1.635 s); the entire five-case shutdown class passed in isolation on R1. No timing assertion or safeguard was relaxed. This is not a clean full-suite claim.
  • Formatting, compilation and git diff --check pass. Reproduction command and all limitations are in readme.md.

Limits / ownership

Borrowed arbitrary input may ignore interruption: its owner must provide prompt cancellation. Stop reports a timeout rather than claiming cleanup if I/O is not released. Only owned process resources are terminated; process trees and borrowed connectors are not stopped. No physical-connection SPI is added; initial extension routing remains destination-URI-origin based.

Closes #1560 when the stacked work reaches the target release branch. Follow-up delivery: #1558, #1561, #1559.

Published checks

Verified head afe0801: repository build/test and strict Javadoc passed in CI run https://github.com/restlet/restlet-framework-java/actions/runs/39707048728. Both SonarCloud checks pass; quality gate OK and zero unresolved new-code issues. The branch is clean and synchronized; PR is ready for review and remains stacked on unmerged #1556.

@jlouvel

jlouvel commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-ups aff5257 and afe0801 address strict Javadoc/static-analysis findings without changing safeguards, and close a deterministically reproduced client EOF-before-terminal-state publication gap found during CI investigation. The expanded live selector now passes 145 tests (zero failures/errors/skips), including 128 immediate empty-error response/client-close exchanges, all eight authorization cases and strict Javadoc generation. The held-lock regression was confirmed failing before the ordering fix. The latest head is undergoing CI; earlier failed runs are retained as evidence rather than treated as green.

@jlouvel jlouvel self-assigned this Oct 24, 2026
@jlouvel
jlouvel requested a review from thboileau October 24, 2026 13:02
@jlouvel

jlouvel commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator Author

@thboileau Could you review this PR, part of a stack of three preparation for foundation for support of gRPC, MCP and Smithy on top of Restlet 3 / Netty 4.2 reusing a maximum of the official SDKs/libraries?

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.

🟡 Changes recommended

Pre-cancelled requests can leak connections, valid half-closed connections can lose responses, and protocol upgrades, disconnect signaling, and subprocess shutdown have unresolved failures.

10 open issues
What changed in this PR

Adds R1 protocol-neutral transport and request-lifecycle foundations on top of R0, preparing core integration points for independent MCP, gRPC and Smithy extensions without delivering production adapters.

Changes:

  • Adds bounded byte-stream transport with explicit borrowed-stream and owned-subprocess lifecycle handling.
  • Adds disconnect notifications, remembered cancellation and bounded HTTP/1.1 pipeline lookahead.
  • Documents ownership and integration limits, with transport, lifecycle and module-boundary regressions.
File Description
readme.md Documents R1 contracts, limits and validation.
org.restlet/​src/​test/​java/​org/​restlet/​util/​StreamTransportTestCase.java Tests byte limits, ownership and subprocess cleanup.
org.restlet/​src/​test/​java/​org/​restlet/​RequestLifecycleTestCase.java Tests disconnect signals and cancellation registration races.
org.restlet/​src/​test/​java/​org/​restlet/​engine/​netty/​Http1LookaheadTestCase.java Tests lookahead limits, buffer release and completion ordering.
org.restlet/​src/​test/​java/​org/​restlet/​engine/​connector/​StdioHelperSelectionTestCase.java Tests explicit and unique STDIO helper selection.
org.restlet/​src/​test/​java/​org/​restlet/​engine/​connector/​RpcSdkBoundaryTestCase.java Verifies public transport access and independent SDK module boundaries.
org.restlet/​src/​test/​java/​org/​restlet/​engine/​connector/​HttpDisconnectTestCase.java Tests disconnects, cancellation, pipelining and empty error responses.
org.restlet/​src/​main/​java/​org/​restlet/​util/​WrapperRequest.java Delegates disconnect lifecycle operations.
org.restlet/​src/​main/​java/​org/​restlet/​util/​StreamTransport.java Exposes the supported protocol-neutral byte API.
org.restlet/​src/​main/​java/​org/​restlet/​Request.java Adds first-cause disconnect signaling and remembered cancellation.
org.restlet/​src/​main/​java/​org/​restlet/​engine/​stdio/​StreamTransportHelper.java Implements bounded I/O and stream/process lifecycle management.
org.restlet/​src/​main/​java/​org/​restlet/​engine/​netty/​NettyServerHandler.java Adds bounded ordered lookahead and disconnect observation.
org.restlet/​src/​main/​java/​org/​restlet/​engine/​netty/​NettyServerCall.java Connects server request lifecycle with response completion.
org.restlet/​src/​main/​java/​org/​restlet/​engine/​netty/​NettyClientCall.java Reports failures and publishes completion before body EOF.
org.restlet/​src/​main/​java/​org/​restlet/​engine/​Engine.java Enforces unambiguous STDIO helper selection.
changes.md Records transport additions and lifecycle behavior changes.

Review effort: Balanced

@jlouvel
jlouvel force-pushed the feat/rpc-r1-foundations branch from afe0801 to b296e89 Compare October 31, 2026 06:37
Base automatically changed from feat/rpc-r0-sdk-feasibility to 3.0 October 31, 2026 21:10
jlouvel and others added 8 commits October 31, 2026 17:12
Provide bounded borrowed-stream and subprocess transport, explicit STDIO helper selection, idle HTTP disconnect signals and remembered pre-registration cancellation. Preserve HTTP/1 pipelining and upgrade handoff with finite lookahead, and document independent SDK ownership and connection limits.

Co-authored-by: Copilot <[email protected]>
Signed-off-by: Jerome Louvel <[email protected]>
Separate cleanup phases, preserve interruption causes, retain duration overflow validation, and avoid catching fatal errors. Use supported Javadoc markup and deterministic child blocking. Revalidated the 134-case live suite and strict Javadoc generation without relaxing safeguards.

Co-authored-by: Copilot <[email protected]>
Signed-off-by: Jerome Louvel <[email protected]>
Reproduce the EOF-before-terminal-publication gap with a held exchange lock, then publish completion before waking readers. Extend lifecycle coverage with immediate closes on empty 401/403 responses and the existing authorization cases. The expanded live regression selector passes 145 cases together with strict Javadoc generation.

Co-authored-by: Copilot <[email protected]>
Signed-off-by: Jerome Louvel <[email protected]>
Close the owned child's stdin before waiting for a graceful exit, cap the
exit waits against the shutdown deadline, return EOF when stdout closes
before the child exits, and reject I/O waiting on a lock once the
transport stops.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
A call cancelled before or while acquiring its channel no longer acquires
a connection, or releases the one acquired concurrently, so pre-cancelled
requests do not hold pool permits.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
With followRedirects, a failure of a redirected exchange now completes the
disconnect signal of the caller's request, not only the internal copy.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Do not resume reading after replaying a queued Upgrade or CONNECT request,
and serve an h2c upgrade pipelined behind an active HTTP/1.1 exchange over
HTTP/1.1 instead of switching protocols before the preceding response.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Server disconnect signals are now suppressed only once the terminal
response write succeeds, and a failed terminal write is reported as a
disconnect.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@jlouvel
jlouvel force-pushed the feat/rpc-r1-foundations branch from b296e89 to 45ffe88 Compare October 31, 2026 21:19
The Restlet-hosted spike server sends the HTTP/2 response headers as soon as
its handle method returns, which races with the first request message. The
"no headers before they are sent" check therefore only holds for the
SDK-hosted server and failed intermittently on CI for the Restlet-hosted case.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@sonarqubecloud

Copy link
Copy Markdown

@jlouvel
jlouvel merged commit 554d7b0 into 3.0 Oct 31, 2026
3 checks passed
@jlouvel
jlouvel deleted the feat/rpc-r1-foundations branch October 31, 2026 21:36
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.

2 participants