Skip to content

Make the connection handler transport-generic (serve over TLS / any AsyncRead+AsyncWrite)#46

Open
rockwotj wants to merge 2 commits into
huggingface:mainfrom
rockwotj:transport-generic-handler
Open

Make the connection handler transport-generic (serve over TLS / any AsyncRead+AsyncWrite)#46
rockwotj wants to merge 2 commits into
huggingface:mainfrom
rockwotj:transport-generic-handler

Conversation

@rockwotj
Copy link
Copy Markdown

@rockwotj rockwotj commented Jun 4, 2026

Motivation

We want to serve NFS over connections that aren't a raw TcpStream — specifically an already-handshaked TLS stream (tokio_rustls::server::TlsStream). Two concrete cases drive this:

  • A sidecar terminates mTLS and forwards plaintext, and
  • terminating TLS in-process via tokio-rustls.

Both need to hand nfsserve a stream that isn't a TcpStream. Today process_socket and write_fragment are concretely typed to tokio::net::TcpStream, which blocks this. This PR genericizes the per-connection handler over the stream type so callers can bring their own transport, without nfsserve itself taking a TLS dependency.

Changes

  • write_fragment is now generic over W: AsyncWrite + Unpin (body unchanged — it only used write_all).
  • process_socket is now generic over S: AsyncRead + AsyncWrite + Send + 'static and is now pub, so external callers can serve a pre-established stream. It uses tokio::io::split + AsyncReadExt::read (cancel-safe in select!) in place of the TcpStream-specific readable()/try_read(). (split returns Unpin halves, so S needs no Unpin bound.)
  • Replies are now flushed after writing. A buffering transport (TLS) only encrypts and sends on flush — without it the reply never leaves the server. This is a no-op for raw TCP.
  • set_nodelay moved from process_socket into NFSTcpListener::handle_forever (it's TcpStream-specific). The TCP path keeps nodelay behavior unchanged.
  • New test: drives a MOUNTPROC3_NULL RPC through process_socket over an in-memory tokio::io::duplex stream (which is AsyncRead + AsyncWrite + Send + 'static, not a TcpStream) and asserts a well-formed reply comes back. This proves the genericization and adds no new dependency (no rustls), keeping the PR dependency-free.

Compatibility

NFSTcpListener behavior is unchanged and fully backward-compatible — it uses the now-generic process_socket internally for plaintext TCP.

rockwotj added 2 commits June 4, 2026 13:50
set_nodelay is TcpStream-specific. Moving it to NFSTcpListener::handle_forever,
right before process_socket is spawned, keeps the per-connection handler free of
TcpStream-only calls (a prerequisite for making it transport-generic) while the
existing TCP path keeps nodelay behavior unchanged.
…syncRead+AsyncWrite)

process_socket and write_fragment were concretely typed to tokio::net::TcpStream,
so callers couldn't serve NFS over anything else. Genericize the per-connection
handler over AsyncRead + AsyncWrite + Send + 'static so callers can hand it a
plaintext TcpStream, an already-handshaked TLS stream (tokio_rustls), a unix
socket, etc. — without nfsserve taking a TLS dependency.

- write_fragment is now generic over W: AsyncWrite + Unpin (body unchanged).
- process_socket is generic over the stream and is now pub, so external callers
  can serve a pre-established stream. It uses tokio::io::split + AsyncReadExt::read
  (cancel-safe in select!) instead of the TcpStream-specific readable()/try_read().
- Replies are flushed after writing: a buffering transport (TLS) only encrypts and
  sends on flush; without it the reply never leaves the server. No-op for raw TCP.
- Added a test driving a MOUNTPROC3_NULL RPC through process_socket over an
  in-memory tokio::io::duplex stream (not a TcpStream), proving the genericization.
  No new dependency required.

NFSTcpListener behavior is unchanged and remains backward-compatible.
@rockwotj rockwotj force-pushed the transport-generic-handler branch from 9cce6ff to 549577a Compare June 4, 2026 19:57
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.

1 participant