-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[API Proposal]: Add property bag to QuicConnection #72511
Comments
Tagging subscribers to this area: @dotnet/ncl Issue DetailsBackground and motivation
runtime/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs Lines 33 to 36 in a54a823
In ASP.NET Core we allow people to hook into this callback, although we wrap it in our own signature because we have an abstraction called _quicListenerOptions = new QuicListenerOptions
{
ApplicationProtocols = _tlsConnectionOptions.ApplicationProtocols,
ListenEndPoint = listenEndPoint,
ListenBacklog = options.Backlog,
ConnectionOptionsCallback = async (quicConnection, helloInfo, cancellationToken) =>
{
var serverAuthenticationOptions = await _tlsConnectionOptions.OnConnection(new TlsConnectionCallbackContext
{
CancellationToken = cancellationToken,
ClientHelloInfo = helloInfo,
State = _tlsConnectionOptions.OnConnectionState,
Connection = new QuicConnectionContext(quicConnection, _context), // wrap QuicConnection with our abstraction here
});
var connectionOptions = new QuicServerConnectionOptions
{
ServerAuthenticationOptions = serverAuthenticationOptions,
IdleTimeout = options.IdleTimeout,
MaxInboundBidirectionalStreams = options.MaxBidirectionalStreamCount,
MaxInboundUnidirectionalStreams = options.MaxUnidirectionalStreamCount,
DefaultCloseErrorCode = 0,
DefaultStreamErrorCode = 0,
};
return connectionOptions;
}
} Once the connection is accepted and returned from var quicConnection = await _listener.AcceptConnectionAsync(cancellationToken);
var connectionContext = new QuicConnectionContext(quicConnection, _context); The code above is creating our abstraction - It would be better if we were able to create var quicConnection = await _listener.AcceptConnectionAsync(cancellationToken);
var connectionContext = (QuicConnectionContext)quicConnection.Properties[QuicConnectionContextKey]; Other options include adding This isn't critical for .NET 7. Workarounds include using a weak table, allocating twice (yuck), or leaving API Proposalnamespace System.Net.Quic;
public class QuicConnection
{
public IDictionary<string, object?> Properties { get; }
} API Usage_quicListenerOptions = new QuicListenerOptions
{
ApplicationProtocols = _tlsConnectionOptions.ApplicationProtocols,
ListenEndPoint = listenEndPoint,
ListenBacklog = options.Backlog,
ConnectionOptionsCallback = async (quicConnection, helloInfo, cancellationToken) =>
{
quicConnection.Properties[QuicConnectionContextKey] = new QuicConnectionContext(quicConnection, _context);
// ...figure out conneciton options...
return connectionOptions;
}
} var quicConnection = await _listener.AcceptConnectionAsync(cancellationToken);
var connectionContext = (QuicConnectionContext)quicConnection.Properties[QuicConnectionContextKey]; Alternative DesignsAdd a state argument to _quicListenerOptions = new QuicListenerOptions
{
ApplicationProtocols = _tlsConnectionOptions.ApplicationProtocols,
ListenEndPoint = listenEndPoint,
ListenBacklog = options.Backlog,
ConnectionOptionsCallback = async (quicConnection, helloInfo, state, cancellationToken) =>
{
var listenerContext = (QuicListenerContext)state!
listenerContext._acceptingQuicConnectionContext = new QuicConnectionContext(quicConnection, _context);
// ...figure out conneciton options...
return connectionOptions;
}
} var quicConnection = await _listener.AcceptConnectionAsync(cancellationToken, state: this);
var connectionContext = _acceptingQuicConnectionContext; // was set in callback RisksNo response
|
Following on from the alternative design idea:
|
I updated ASP.NET Core to create our abstraction inside the callback and get/set it on a field. It appears to work: dotnet/aspnetcore#42774 It feels a little hacky. A property bag on QuicConnection would be easier to follow. |
Triage: We do not like the Property bag -- feels kind of very general-purpose, which bit us in the past on other APIs. We expected that it is sufficient for 8.0 as we are past Platform Complete. Is it correct @JamesNK? |
The alternative proposal doesn't work. The callback isn't run when That brings us back to the idea of adding a property bag to I will see whether using a |
And we can change that if we decide to support the state object. It's similar to my alternative design for |
Triage: the decision must be done 8.0 (no matter which one) as this might alter an existing API. For that reason, this is a high prio. |
Would simple |
I think so. But a dictionary seems more flexible (multiple APIs could use it to stash information without overwriting each others changes) and it is like Are you worried about performance? The dictionary could be lazily allocated if it is used to make it pay for play. |
So we had another discussion about this internally. We're not fans of the dictionary, despite being a similar approach as in HTTP stack (we obsoleted that API some time ago and replaced it with the Options class which makes us wary of this approach). We'd still prefer to use a single object instead. |
Background and motivation
QuicListenerOptions.ConnectionOptionsCallback
is a callback that allows the server to modify connection options when it accepts a connection. One of its arguments isQuicConnection
.runtime/src/libraries/System.Net.Quic/src/System/Net/Quic/QuicListenerOptions.cs
Lines 33 to 36 in a54a823
In ASP.NET Core we allow people to hook into this callback, although we wrap it in our own signature because we have an abstraction called
ConnectionContext
for a server connection that's not specific to QUIC or HTTP/3.Once the connection is accepted and returned from
The code above is creating our abstraction -
QuicConnectionContext
- twice: once in the callback and then again afterAcceptConnectionAsync
returns. We don't want to do this because of allocations, but also because someone can update state on our connection abstraction, and that would disappear if it was recreated.It would be better if we were able to create
QuicConnectionContext
once inside the callback, use it, then add it to a property bag onQuicConnection
, and then use that value whenAcceptConnectionAsync
returns.Other options include adding
QuicConnectionContext
to aConditionalWeakTable<QuicConnection, QuicConnectionContext>
inside the callback, then removing it outside the callback. But a place to stash state on theQuicConnection
would be much simpler.This isn't critical for .NET 7. Workarounds include using a weak table, allocating twice (which fixes one problem but creates another...), or leaving
ConnectionContext
null in the callback in .NET 7 and waiting for this API in .NET 8.API Proposal
API Usage
Alternative Designs
Add a state argument to
ConnectionOptionsCallback
callback andAcceptConnectionAsync
.Risks
No response
The text was updated successfully, but these errors were encountered: