Skip to content

API proposal: IMultiplexedConnectionListenerFactory and company #17037

Closed
@jkotalik

Description

@jkotalik

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.

Metadata

Metadata

Assignees

Labels

Needs: DesignThis issue requires design work before implementating.api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions