Skip to content

Conversation

@haze518
Copy link
Member

@haze518 haze518 commented Aug 22, 2025

This PR introduces a draft version of a sans-io TCP client. The main goal is to separate protocol logic from the actual I/O layer, so that the client is no longer tightly bound to a specific runtime or transport implementation.

What’s included

  • A new TCP client built on top of a sans-io core.
  • Connection and message handling are now decoupled from async runtime details.
  • Prepared foundation for supporting different transports (e.g. QUIC, TLS over TCP) using the same protocol core.

Why

  • Makes the protocol logic reusable and easier to reason about.
  • Allows plugging in different runtimes (Tokio, async-std, smol) without rewriting the core.
  • Simplifies future extension to other transports beyond plain TCP.

type Join: Send + 'static;
type Sleep: Future<Output = ()> + Send + 'static;

fn spawn(&self, fut: impl Future<Output = ()> + Send + 'static) -> Self::Join;
Copy link
Contributor

@numinnex numinnex Aug 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those trait bounds are very strict. I understand that tokio needs those, but other runtimes don't. This is the problem with such abstractions such as the Runtime trait. I saw it in the opentelemetry_sdk crate aswell.

I think we should search for different approach, in which the concrete implementations of sans-io provides those capabilities without polluting the internal state machine of the protocol.

The sleep function is acceptable as it is required for timeouts, but spawning is not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you’re right, that’s a good point. We should probably make spawn less strict (maybe by checking how other projects have approached it). As for time, it could be abstracted in the way described in the firezone blog https://www.firezone.dev/blog/sans-io#abstracting-time, while still allowing the ConnectionDriver to poll it directly

Copy link
Contributor

@numinnex numinnex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left one comment, so far it looks good, I think we should create an RFC for this feature and initiate a GH discussion. Let's use the rust-lang template for RFC.

@spetz
Copy link
Contributor

spetz commented Aug 24, 2025

I really like the idea, great job with the initial draft! I think that it'd be nice to have the different implementations available behind the feature flag. For example, the very basic SDK could only come with a raw, synchronous TCP client using the std library. In order to use e.g. tokio, smol etc., you'd simply need to include e.g. features = ["tokio"] - by doing so, the SDK will be very thin and runtime agnostic (also, it should be easier in the future to consider the no_std support).

@haze518
Copy link
Member Author

haze518 commented Aug 25, 2025

Left one comment, so far it looks good, I think we should create an RFC for this feature and initiate a GH discussion. Let's use the rust-lang template for RFC.

Sounds good, I’ll open a discussion

@haze518
Copy link
Member Author

haze518 commented Aug 25, 2025

I really like the idea, great job with the initial draft! I think that it'd be nice to have the different implementations available behind the feature flag. For example, the very basic SDK could only come with a raw, synchronous TCP client using the std library. In order to use e.g. tokio, smol etc., you'd simply need to include e.g. features = ["tokio"] - by doing so, the SDK will be very thin and runtime agnostic (also, it should be easier in the future to consider the no_std support).

Yeah, exactly 🙂 That’s what I had in mind as well. But in addition to being runtime-agnostic, we could also make it transport-agnostic, so features would let us pick combinations like ["tokio", "tcp"] or ["tokio", "http"], not just ["tokio"]

@spetz
Copy link
Contributor

spetz commented Aug 25, 2025

I really like the idea, great job with the initial draft! I think that it'd be nice to have the different implementations available behind the feature flag. For example, the very basic SDK could only come with a raw, synchronous TCP client using the std library. In order to use e.g. tokio, smol etc., you'd simply need to include e.g. features = ["tokio"] - by doing so, the SDK will be very thin and runtime agnostic (also, it should be easier in the future to consider the no_std support).

Yeah, exactly 🙂 That’s what I had in mind as well. But in addition to being runtime-agnostic, we could also make it transport-agnostic, so features would let us pick combinations like ["tokio", "tcp"] or ["tokio", "http"], not just ["tokio"]

I think that the default SDK, could contain just the sync std TCP client. All the rest, whether it's QUIC, TCP using Tokio, HTTP with reqwest etc. could be the separate features.

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