Skip to content

feat(server)!: convert server.rs internals + on_disconnected to ServerError#1244

Open
Greg Lamberson (glamberson) wants to merge 5 commits into
Devolutions:masterfrom
lamco-admin:feat/server-typed-error-server
Open

feat(server)!: convert server.rs internals + on_disconnected to ServerError#1244
Greg Lamberson (glamberson) wants to merge 5 commits into
Devolutions:masterfrom
lamco-admin:feat/server-typed-error-server

Conversation

@glamberson
Copy link
Copy Markdown
Contributor

@glamberson Greg Lamberson (glamberson) commented Apr 30, 2026

Summary

Third step of the staged migration started in #1242 and continued in #1243. Combines the on_disconnected signature change with the server.rs internal site conversion since both touch anyhow-flowing code; doing them together avoids a temporary anyhow ↔ ServerError two-way bridge during the intermediate state.

Public API change

 fn on_disconnected(
     &mut self,
     peer: SocketAddr,
     duration: Duration,
-    error: Option<&anyhow::Error>,
+    error: Option<&ServerError>,
 ) -> PostConnectionAction { ... }

This is a breaking change for handler implementations of ConnectionHandler. Pre-1.0, and per Marc-Andre Lureau (@elmarco)'s note on #1209: "I am ok with breaking API at this point :)".

Internal changes

  • The two run_inner / run_connection_inner wrapper functions introduced in feat(server)!: introduce typed ServerError on the public API boundary #1242 are folded back into run / run_connection. The accept loop calls the public method directly; result.as_ref().err() now feeds the new ServerError-typed parameter naturally.
  • ~25 .context() / bail! sites in run, run_connection, accept_finalize, handle_io_channel_data, handle_x224, handle_input_backlog, and the encode_share_data_pdu / deactivate_all helpers replaced with typed ServerError variants. Pattern alignment with ConnectorErrorExt:
    • EncodeError sources → ServerError::encode
    • DecodeError sources → ServerError::decode
    • std::io::Error sources → ServerError::io
    • Option<channel> with .ok_or_elseServerError::channel
    • bail!("Fastpath output not supported!")ServerError::unsupported
    • everything else → ServerError::custom with a static context.

What this PR does NOT touch

Stack ordering

This is step 3 of 4. The full chain (must merge in PR-number order):

Step PR Scope
1 #1242 Add ServerError / ServerErrorKind / ext traits, convert 5 public functions, internal anyhow stays via private bridge
2 #1243 Convert encoder/helper/echo internals (anyhow → typed)
3 this PR Convert server.rs internals + on_disconnected parameter (breaking)
4 #1245 Convert display traits, drop anyhow dep, finish (breaking)

Branch is stacked on feat/server-typed-error-internal (#1243), which is stacked on feat/server-typed-error (#1242).

Coordinate with #1239

The ConnectionHandler trait was extended in #1239 (SuppressOutput / RefreshRectangle / FrameAcknowledge handlers). If this PR lands first, #1239 needs a trivial rebase to the new trait shape (the new methods stay; only on_disconnected's signature changes around them). If #1239 lands first, this PR rebases onto its trait shape. Either order works.

Test plan

  • cargo xtask check fmt -v clean
  • cargo xtask check lints -v clean (workspace, all-targets, with helper + __bench features)
  • cargo xtask check tests -v passes
  • cargo build --workspace --all-targets clean

Tracking: #1209.

@glamberson Greg Lamberson (glamberson) force-pushed the feat/server-typed-error-server branch from 8cd5e23 to e5c33c7 Compare May 13, 2026 00:18
Greg Lamberson (glamberson) pushed a commit to lamco-admin/IronRDP that referenced this pull request May 13, 2026
Final step of the staged migration started in Devolutions#1242, continued in
Devolutions#1243 and Devolutions#1244. Completes the typed error story for ironrdp-server.

Public API changes:

  RdpServerDisplay::updates
      -> Result<Box<dyn RdpServerDisplayUpdates>, anyhow::Error>
      => ServerResult<Box<dyn RdpServerDisplayUpdates>>

  RdpServerDisplayUpdates::next_update
      -> Result<Option<DisplayUpdate>, anyhow::Error>
      => ServerResult<Option<DisplayUpdate>>

These are breaking changes for handler implementations of the two
display traits.

Internal changes:

  - The from_anyhow private bridge and the AnyhowError wrapper struct
    in error.rs are removed.
  - The anyhow dependency is removed from ironrdp-server/Cargo.toml.
  - builder.rs's NoopDisplayUpdates / NoopDisplay impls and the
    docstring example in display.rs and README.md are updated to match
    the new trait shapes.
  - The example in crates/ironrdp/examples/server.rs and the integration
    test in crates/ironrdp-testsuite-extra/tests/main.rs are updated to
    return ServerResult from their RdpServerDisplay/Updates impls.
  - benches/src/perfenc.rs is updated to construct ServerError variants
    instead of anyhow::Error and converts at its own anyhow::Result main
    boundary via .map_err(|e| anyhow::anyhow!(e)).

After this commit, ironrdp-server has no anyhow dependency and the
public surface is fully typed against ServerError. Closes Devolutions#1209.
@glamberson Greg Lamberson (glamberson) force-pushed the feat/server-typed-error-server branch from e5c33c7 to b368125 Compare May 15, 2026 03:06
@glamberson Greg Lamberson (glamberson) force-pushed the feat/server-typed-error-server branch from b368125 to b098ace Compare May 20, 2026 18:49
@glamberson Greg Lamberson (glamberson) force-pushed the feat/server-typed-error-server branch from b098ace to a0b1000 Compare May 20, 2026 22:47
Introduces a typed error story for ironrdp-server, mirroring the shape
already used by ironrdp-connector and the rest of the connection-management
layer:

  pub type ServerError = ironrdp_error::Error<ServerErrorKind>;
  pub type ServerResult<T> = Result<T, ServerError>;

ServerErrorKind is a non-exhaustive enum with concretely typed variants
for the failure modes the crate surfaces (Encode, Decode, Io, Channel,
Unsupported, Reason, General, Custom). Sources are attached through
ironrdp_error::Error::with_source rather than embedded as Box<dyn Error>
in variant data, matching ConnectorErrorKind exactly.

This first PR converts public boundaries only:

  - RdpServer::run                -> ServerResult<()>
  - RdpServer::run_connection<S>  -> ServerResult<()>
  - TlsIdentityCtx::init_from_paths -> ServerResult<Self>
  - TlsIdentityCtx::make_acceptor   -> ServerResult<TlsAcceptor>
  - EchoServerHandle::send_request  -> ServerResult<()>

Internal call sites continue to use anyhow::Result during the staged
migration. A private  helper bridges at the public boundary.
A follow-up PR converts internal sites to use the typed kinds directly
and drops the anyhow dependency. A second follow-up changes the
ConnectionHandler::on_disconnected error parameter from
Option<&anyhow::Error> to Option<&ServerError>.

This is a breaking change to consumers of the listed public functions:
return types change from anyhow::Result<T> to ServerResult<T>.

Tracking: Devolutions#1209
…rverError

Second step of the staged migration started in the previous commit.
Replaces anyhow construction sites in modules whose internal flow does
not pass through the ConnectionHandler::on_disconnected callback (which
still takes &anyhow::Error and is the subject of a separate follow-up
PR per Devolutions#1209):

  - encoder/mod.rs: ~15 anyhow! / .context() / bail! sites converted
    to typed ServerError variants (Encode, Reason, Custom).
    EncodeError sources go through ServerError::encode (matching
    ConnectorErrorExt::encode). spawn_blocking JoinError, qoi codec
    errors, and zstd error codes go through ServerError::custom or
    ServerError::reason as appropriate.
  - helper.rs: TLS cert/key loading paths construct ServerError::io
    for std::io::Error sources, ServerError::reason for missing-key
    cases, ServerError::custom for x509-cert and PEM parsing errors.
    Removes the from_anyhow bridge and the inner-fn split introduced
    in the previous commit.
  - echo.rs: build_echo_request returns ServerResult, builds errors
    via ServerError::custom directly. send_request keeps its already-
    typed Channel and Reason variants.

server.rs internals stay on anyhow because they propagate into
ConnectionHandler::on_disconnected which still takes &anyhow::Error;
that conversion plus the remaining server.rs internal sites land in
PR Devolutions#3. The anyhow dep stays in Cargo.toml for now and will be dropped
when the public traits (ConnectionHandler, RdpServerDisplay, and
RdpServerDisplayUpdates) finish their typed migration.
Replaces the closure-based `.map_err(|e| ServerError::custom(...))?`
pattern with the `ServerResultExt::with_context` ext-trait introduced
in Devolutions#1242. Preserves the inner `Reason` kind from QoizHandler::new
(was being lost programmatically inside a Custom-wrapping outer error)
while replacing the context string with the more informative
"failed to initialize qoiz handler" wording.

Precheck-driven cleanup; no behavioral change beyond preserving the
typed kind at this site.
…rError

Third step of the staged migration started in Devolutions#1242 and continued in
the previous commit. Combines the on_disconnected signature change with
the server.rs internal site conversion since both touch anyhow-flowing
code; doing them together avoids a temporary anyhow-to-ServerError
two-way bridge during the intermediate state.

Public API change:

  ConnectionHandler::on_disconnected
      error: Option<&anyhow::Error>  ->  Option<&ServerError>

This is a breaking change for handler implementations.

Internal changes:

  - The two run_inner / run_connection_inner wrapper functions are
    folded back into run / run_connection. The accept loop calls the
    public method directly; result.as_ref().err() now feeds the new
    ServerError-typed parameter naturally.
  - ~25 .context() / bail! sites in run, run_connection, accept_finalize,
    handle_io_channel_data, handle_x224, handle_input_backlog, and the
    encode_share_data_pdu / deactivate_all helpers replaced with typed
    ServerError variants. Pattern alignment with ConnectorErrorExt:
    EncodeError sources -> ServerError::encode, DecodeError sources ->
    ServerError::decode, std::io::Error sources -> ServerError::io,
    Option<channel> with .ok_or_else -> ServerError::channel,
    bail!("Fastpath output not supported!") ->
    ServerError::unsupported, anything else -> ServerError::custom with
    a static context.
  - The from_anyhow bridge is retained only at one boundary: the
    RdpServerDisplay::updates() trait method still returns
    anyhow::Result, so the call site in run_connection wraps the anyhow
    result via from_anyhow. PR Devolutions#4 of this migration converts the
    display traits and drops the bridge entirely.

The anyhow dependency stays in Cargo.toml because RdpServerDisplay,
RdpServerDisplayUpdates, and a small handful of consumer-facing trait
returns still use anyhow::Result. PR Devolutions#4 finishes that conversion and
drops the dep.

Tracking: Devolutions#1209
…sites

Replaces the closure-based set_context pattern at the UpdateEncoder::new
and client_loop call sites in run_connection with the
ServerResultExt::with_context ext-trait. Same semantics (preserves the
inner kind, replaces the context string), substantially less verbose.

Precheck-driven cleanup; no behavioral change.
@glamberson Greg Lamberson (glamberson) force-pushed the feat/server-typed-error-server branch from a0b1000 to 08f744d Compare May 21, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant