Description
Today, when accepting a connection from an IConnectionTransport, you get a ConnectionContext that has information about the connection. However, for scenarios like QUIC, after you get the connection, you need to accept again in order to get a QUIC stream on said connection. This is fairly awkward and isn't intuitive as the System.IO.PipeReader/PipeWriter will be unusable.
When implementing HTTP/3, right now we have the following:
public interface IConnectionListenerFactory
{
ValueTask<IConnectionListener> BindAsync(EndPoint endpoint, CancellationToken cancellationToken = default);
}
public interface IConnectionListener : IAsyncDisposable
{
EndPoint EndPoint { get; }
ValueTask<ConnectionContext> AcceptAsync(CancellationToken cancellationToken = default);
ValueTask UnbindAsync(CancellationToken cancellationToken = default);
}
public interface IQuicStreamListenerFeature
{
ValueTask<ConnectionContext> AcceptAsync(CancellationToken cancellationToken = default);
}
Usage:
IConnectionListener connectionListener = connectionListenerFactory.BindAsync(new IPEndPoint());
ConnectionContext context = await connectionListener.AcceptAsync();
IQuicStreamListenerFeature feature = context.ConnectionFeatures.Get<IQuicStreamListenerFeature>();
ConnectionContext streamContext = await feature.AcceptAsync();
I'd like to propose a new API for accepting connections and streams from a multiplexed connection. Though this may only apply to QUIC for now, the notion of streams/connections will probably apply to other scenarios.
public interface IMultiplexedConnectionListenerFactory : IConnectionListenerFactory
{
}
public interface IMultiplexedConnectionListener : IConnectionListener // Need to downcast
{
ValueTask<MultiplexedConnectionContext> AcceptMultiplexedAsync(CancellationToken cancellationToken = default);
}
public class MultiplexedConnectionContext : ConnectionContext
{
ValueTask<StreamConnectionContext> AcceptStreamAsync();
ValueTask<StreamConnectionContext> ConnectStreamAsync(bool unidirectional); // undirectional or bidirectional stream, like half open/half close
}
public class StreamConnectionContext : ConnectionContext
{
int StreamId { get; }
bool Unidirectional { get; }
}
Also, we could see how IMultiplexedConnectionListenerFactory without implementing IConnectionListenerFactory.
Usage:
IMultiplexedConnectionListener connectionListener = connectionListenerFactory.BindAsync(new IPEndPoint()) as IMultiplexedConnectionListener;
MultiplexedConnectionContext context = await connectionListener.AcceptAsync();
ConnectionContext streamContext = await context.AcceptAsync();
To me this feels a bit cleaner that what we are doing with features.