Skip to content
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

[QUIC] Options classes details #44212

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open

Conversation

ManickaP
Copy link
Member

@ManickaP ManickaP commented Jan 10, 2025

Moving some details from the blogpost into docs.

Alternative to this extra documentation could be putting this info straight into API ref docs. I put this into separate conceptual docs because some of these are not exactly short and I'm aware of changes like dotnet/dotnet-api-docs#9577.
I 100% prefer to have this info in one place so either here or API docs are fine. I just don't want to have part in conceptual docs and the rest in API docs. LMK what you'd prefer.

cc @gewarren @dotnet/ncl


Internal previews

📄 File 🔗 Preview link
docs/fundamentals/networking/quic/quic-options.md QUIC configuration options
docs/fundamentals/toc.yml docs/fundamentals/toc

Internal previews

📄 File 🔗 Preview link
docs/fundamentals/networking/quic/quic-options.md QUIC configuration options
docs/fundamentals/toc.yml docs/fundamentals/toc

@ManickaP ManickaP marked this pull request as ready for review January 17, 2025 11:50
@ManickaP ManickaP requested a review from a team as a code owner January 17, 2025 11:50
@gewarren
Copy link
Contributor

@ManickaP I like it where you've put it in the conceptual docs. We should add links from the API docs though.

Copy link
Contributor

@gewarren gewarren left a comment

Choose a reason for hiding this comment

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

I also committed some changes directly since it was much easier than leaving comments. Feel free to revert if you don't like it.


### ConnectionOptionsCallback

<xref:System.Net.Quic.QuicListenerOptions.ConnectionOptionsCallback> is a delegate to choose <xref:System.Net.Quic.QuicServerConnectionOptions> for an incoming connection. The function is given a not fully initialized instance of <xref:System.Net.Quic.QuicConnection> and <xref:System.Net.Security.SslClientHelloInfo> containing the server name requested by the client ([RFC 6066 - SNI](https://www.rfc-editor.org/rfc/rfc6066.html#section-3)). The delegate is invoked for each incoming connection. It can return different options based on the provided client info or it can safely return the same instance of the options every time. The delegate purpose and shape is intentionally similar to <xref:System.Net.Security.ServerOptionsSelectionCallback> used in <xref:System.Net.Security.SslStream.AuthenticateAsServerAsync(System.Net.Security.ServerOptionsSelectionCallback,System.Object,System.Threading.CancellationToken)?displayProperty=nameWithType>. **This property is mandatory.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<xref:System.Net.Quic.QuicListenerOptions.ConnectionOptionsCallback> is a delegate to choose <xref:System.Net.Quic.QuicServerConnectionOptions> for an incoming connection. The function is given a not fully initialized instance of <xref:System.Net.Quic.QuicConnection> and <xref:System.Net.Security.SslClientHelloInfo> containing the server name requested by the client ([RFC 6066 - SNI](https://www.rfc-editor.org/rfc/rfc6066.html#section-3)). The delegate is invoked for each incoming connection. It can return different options based on the provided client info or it can safely return the same instance of the options every time. The delegate purpose and shape is intentionally similar to <xref:System.Net.Security.ServerOptionsSelectionCallback> used in <xref:System.Net.Security.SslStream.AuthenticateAsServerAsync(System.Net.Security.ServerOptionsSelectionCallback,System.Object,System.Threading.CancellationToken)?displayProperty=nameWithType>. **This property is mandatory.**
<xref:System.Net.Quic.QuicListenerOptions.ConnectionOptionsCallback> is a delegate to choose <xref:System.Net.Quic.QuicServerConnectionOptions> for an incoming connection. The function is given a partially initialized instance of <xref:System.Net.Quic.QuicConnection> and <xref:System.Net.Security.SslClientHelloInfo> containing the server name requested by the client ([RFC 6066 - SNI](https://www.rfc-editor.org/rfc/rfc6066.html#section-3)). The delegate is invoked for each incoming connection. It can return different options based on the provided client info or it can safely return the same instance of the options every time. The delegate purpose and shape is intentionally similar to <xref:System.Net.Security.ServerOptionsSelectionCallback> used in <xref:System.Net.Security.SslStream.AuthenticateAsServerAsync(System.Net.Security.ServerOptionsSelectionCallback,System.Object,System.Threading.CancellationToken)?displayProperty=nameWithType>. **This property is mandatory.**


### ListenBacklog

<xref:System.Net.Quic.QuicListenerOptions.ListenBacklog> determines how many incoming connections can be held by the listener before additional ones start being refused. Every attempt to establish a connection counts, even when it fails or when the connection gets shut down while waiting in the queue. Ongoing processes to establish a new connection count towards this limit as well. Connections or connection attempts are counted until they're retrieved via <xref:System.Net.Quic.QuicListener.AcceptConnectionAsync(System.Threading.CancellationToken)?displayProperty=nameWithType>. The purpose of the backlog limit is to prevent servers from being overwhelmed by more incoming connections than it can process. **This property is optional, default value is 512.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<xref:System.Net.Quic.QuicListenerOptions.ListenBacklog> determines how many incoming connections can be held by the listener before additional ones start being refused. Every attempt to establish a connection counts, even when it fails or when the connection gets shut down while waiting in the queue. Ongoing processes to establish a new connection count towards this limit as well. Connections or connection attempts are counted until they're retrieved via <xref:System.Net.Quic.QuicListener.AcceptConnectionAsync(System.Threading.CancellationToken)?displayProperty=nameWithType>. The purpose of the backlog limit is to prevent servers from being overwhelmed by more incoming connections than it can process. **This property is optional, default value is 512.**
<xref:System.Net.Quic.QuicListenerOptions.ListenBacklog> determines how many incoming connections can be held by the listener before additional ones start being refused. Every attempt to establish a connection counts, even when it fails or when the connection gets shut down while waiting in the queue. Ongoing processes to establish a new connection count towards this limit as well. Connections or connection attempts are counted until they're retrieved via <xref:System.Net.Quic.QuicListener.AcceptConnectionAsync(System.Threading.CancellationToken)?displayProperty=nameWithType>. The purpose of the backlog limit is to prevent servers from being overwhelmed by more incoming connections than they can process. **This property is optional, default value is 512.**


### ListenEndPoint

<xref:System.Net.Quic.QuicListenerOptions.ListenEndPoint> contains IP address and port on which the listener will accept new connections. Due to underlying implementation, `MsQuic`, the listener will always bind to dual-stack wildcard socket regardless to what is specified here. This can lead to some unexpected behaviors, especially in comparison with ordinary TCP sockets like in HTTP/1.1 and HTTP/2 cases. For more information, see [QUIC Troubleshooting Guide](quic-troubleshooting.md). **This property is mandatory.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<xref:System.Net.Quic.QuicListenerOptions.ListenEndPoint> contains IP address and port on which the listener will accept new connections. Due to underlying implementation, `MsQuic`, the listener will always bind to dual-stack wildcard socket regardless to what is specified here. This can lead to some unexpected behaviors, especially in comparison with ordinary TCP sockets like in HTTP/1.1 and HTTP/2 cases. For more information, see [QUIC Troubleshooting Guide](quic-troubleshooting.md). **This property is mandatory.**
<xref:System.Net.Quic.QuicListenerOptions.ListenEndPoint> contains the IP address and port on which the listener will accept new connections. Due to underlying implementation, `MsQuic`, the listener, always binds to a dual-stack wildcard socket regardless of what's specified here. This can lead to some unexpected behaviors, especially in comparison with ordinary TCP sockets like in HTTP/1.1 and HTTP/2 cases. For more information, see [QUIC Troubleshooting Guide](quic-troubleshooting.md). **This property is mandatory.**


### DefaultStreamErrorCode

<xref:System.Net.Quic.QuicConnectionOptions.DefaultStreamErrorCode> is used when a stream is disposed before finishing reading all the data. When receiving data over QUIC stream, application can either consume all the data or if not it needs to abort its reading side. And similarly to connection closing, QUIC protocol requires an application-level reason for aborting the reading side ([RFC 9000 - Stop Sending](https://www.rfc-editor.org/rfc/rfc9000.html#name-stop_sending-frames)). **This property is mandatory.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<xref:System.Net.Quic.QuicConnectionOptions.DefaultStreamErrorCode> is used when a stream is disposed before finishing reading all the data. When receiving data over QUIC stream, application can either consume all the data or if not it needs to abort its reading side. And similarly to connection closing, QUIC protocol requires an application-level reason for aborting the reading side ([RFC 9000 - Stop Sending](https://www.rfc-editor.org/rfc/rfc9000.html#name-stop_sending-frames)). **This property is mandatory.**
<xref:System.Net.Quic.QuicConnectionOptions.DefaultStreamErrorCode> is used when a stream is disposed before all the data is read. When receiving data over QUIC stream, an application can either consume all the data or, if not, it needs to abort its reading side. And similarly to connection closing, QUIC protocol requires an application-level reason for aborting the reading side ([RFC 9000 - Stop Sending](https://www.rfc-editor.org/rfc/rfc9000.html#name-stop_sending-frames)). **This property is mandatory.**


### IdleTimeout

If the connection is inactive for more than the specified <xref:System.Net.Quic.QuicConnectionOptions.IdleTimeout>, it gets disconnected. This option is part of the QUIC protocol specification ([RFC 9000 - Idle Timeout](https://www.rfc-editor.org/rfc/rfc9000.html#name-idle-timeout)) and is sent to the peer during connection handshake. The connection then takes the smaller of its and the peer's idle timeouts and uses that. Thus the connection can get closed on idle timeout sooner than what this option was set up. **This property is optional, default value is based on MsQuic which is 30 seconds.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
If the connection is inactive for more than the specified <xref:System.Net.Quic.QuicConnectionOptions.IdleTimeout>, it gets disconnected. This option is part of the QUIC protocol specification ([RFC 9000 - Idle Timeout](https://www.rfc-editor.org/rfc/rfc9000.html#name-idle-timeout)) and is sent to the peer during connection handshake. The connection then takes the smaller of its and the peer's idle timeouts and uses that. Thus the connection can get closed on idle timeout sooner than what this option was set up. **This property is optional, default value is based on MsQuic which is 30 seconds.**
If the connection is inactive for more than the specified <xref:System.Net.Quic.QuicConnectionOptions.IdleTimeout>, it gets disconnected. This option is part of the QUIC protocol specification ([RFC 9000 - Idle Timeout](https://www.rfc-editor.org/rfc/rfc9000.html#name-idle-timeout)) and is sent to the peer during connection handshake. The connection then takes the smaller of it and the peer's idle timeouts and uses that. Thus the connection can get closed on idle timeout sooner than what this option was set to. **This property is optional, default value is based on MsQuic, which is 30 seconds.**

- <xref:System.Net.Quic.QuicReceiveWindowSizes.RemotelyInitiatedBidirectionalStream>: limit for received data on an incoming bidirectional stream.
- <xref:System.Net.Quic.QuicReceiveWindowSizes.UnidirectionalStream>: limit for received data on an incoming unidirectional stream.

These values must be non-negative integer which is power of 2; this is an inherited limitation from `MsQuic`. Setting any of these values to 0 essentially means that no data will ever be received by the specific stream or a connection as a whole. **This property is optional, default values are 64KB for a stream and 64MB for a connection.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
These values must be non-negative integer which is power of 2; this is an inherited limitation from `MsQuic`. Setting any of these values to 0 essentially means that no data will ever be received by the specific stream or a connection as a whole. **This property is optional, default values are 64KB for a stream and 64MB for a connection.**
These values must be a non-negative integer that's a power of 2; this is an inherited limitation from `MsQuic`. Setting any of these values to 0 essentially means that no data will ever be received by the specific stream or a connection as a whole. **This property is optional, default values are 64 KB for a stream and 64 MB for a connection.**


### MaxInboundBidirectionalStreams

<xref:System.Net.Quic.QuicConnectionOptions.MaxInboundBidirectionalStreams> determines the maximum number of concurrently active bidirectional streams that the connection is willing to accept. Note that this differs from how QUIC specification defines handling concurrency ([RFC 9000 - Controlling Concurrency](https://www.rfc-editor.org/rfc/rfc9000.html#name-controlling-concurrency)). The QUIC protocol counts the streams cumulatively, over the connection lifetime, and uses ever increasing limit to determine the overall number of streams accepted by the connection, including already closed streams ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This property simplifies this so that the application only specifies the concurrent stream limit and `MsQuic` takes care of translating this limit to the corresponding `MAX_STREAMS` frames. **This property is optional, default value is 0 for client connections and 100 for server connections.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<xref:System.Net.Quic.QuicConnectionOptions.MaxInboundBidirectionalStreams> determines the maximum number of concurrently active bidirectional streams that the connection is willing to accept. Note that this differs from how QUIC specification defines handling concurrency ([RFC 9000 - Controlling Concurrency](https://www.rfc-editor.org/rfc/rfc9000.html#name-controlling-concurrency)). The QUIC protocol counts the streams cumulatively, over the connection lifetime, and uses ever increasing limit to determine the overall number of streams accepted by the connection, including already closed streams ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This property simplifies this so that the application only specifies the concurrent stream limit and `MsQuic` takes care of translating this limit to the corresponding `MAX_STREAMS` frames. **This property is optional, default value is 0 for client connections and 100 for server connections.**
<xref:System.Net.Quic.QuicConnectionOptions.MaxInboundBidirectionalStreams> determines the maximum number of concurrently active bidirectional streams that the connection is willing to accept. Note that this differs from how QUIC specification defines handling concurrency ([RFC 9000 - Controlling Concurrency](https://www.rfc-editor.org/rfc/rfc9000.html#name-controlling-concurrency)). The QUIC protocol counts the streams cumulatively, over the connection lifetime, and uses an ever-increasing limit to determine the overall number of streams accepted by the connection, including already closed streams ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This property simplifies this so that the application only specifies the concurrent stream limit and `MsQuic` takes care of translating this limit to the corresponding `MAX_STREAMS` frames. **This property is optional, default value is 0 for client connections and 100 for server connections.**


### MaxInboundUnidirectionalStreams

<xref:System.Net.Quic.QuicConnectionOptions.MaxInboundUnidirectionalStreams> determines the maximum number of concurrently active unidirectional streams that the connection is willing to accept. Note that this differs from how QUIC specification defines handling stream concurrency ([RFC 9000 - Controlling Concurrency](https://www.rfc-editor.org/rfc/rfc9000.html#name-controlling-concurrency)). The QUIC protocol counts the streams cumulatively, over the connection lifetime, and uses ever increasing limit to determine the overall number of streams accepted by the connection, including already closed streams ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This property simplifies this so that the application only specifies the concurrent stream limit and `MsQuic` takes care of translating this limit to the corresponding `MAX_STREAMS` frames. **This property is optional, default value is 0 for client connections and 10 for server connections.**
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<xref:System.Net.Quic.QuicConnectionOptions.MaxInboundUnidirectionalStreams> determines the maximum number of concurrently active unidirectional streams that the connection is willing to accept. Note that this differs from how QUIC specification defines handling stream concurrency ([RFC 9000 - Controlling Concurrency](https://www.rfc-editor.org/rfc/rfc9000.html#name-controlling-concurrency)). The QUIC protocol counts the streams cumulatively, over the connection lifetime, and uses ever increasing limit to determine the overall number of streams accepted by the connection, including already closed streams ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This property simplifies this so that the application only specifies the concurrent stream limit and `MsQuic` takes care of translating this limit to the corresponding `MAX_STREAMS` frames. **This property is optional, default value is 0 for client connections and 10 for server connections.**
<xref:System.Net.Quic.QuicConnectionOptions.MaxInboundUnidirectionalStreams> determines the maximum number of concurrently active unidirectional streams that the connection is willing to accept. Note that this differs from how QUIC specification defines handling stream concurrency ([RFC 9000 - Controlling Concurrency](https://www.rfc-editor.org/rfc/rfc9000.html#name-controlling-concurrency)). The QUIC protocol counts the streams cumulatively, over the connection lifetime, and uses an ever-increasing limit to determine the overall number of streams accepted by the connection, including already closed streams ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This property simplifies this so that the application only specifies the concurrent stream limit and `MsQuic` takes care of translating this limit to the corresponding `MAX_STREAMS` frames. **This property is optional, default value is 0 for client connections and 10 for server connections.**


### StreamCapacityCallback

<xref:System.Net.Quic.QuicConnectionOptions.StreamCapacityCallback> is a callback that gets invoked whenever the peer releases a new stream capacity via `MAX_STREAMS` and as a result, the current capacity is above 0. The values provided in the callback arguments are capacity increments, meaning that the sum of all values from the callback will equal to the last value received from `MAX_STREAMS` ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This callback was designed to support <xref:System.Net.Http.SocketsHttpHandler.EnableMultipleHttp3Connections?displayProperty=nameWithType> functionality and comes with several caveats:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<xref:System.Net.Quic.QuicConnectionOptions.StreamCapacityCallback> is a callback that gets invoked whenever the peer releases a new stream capacity via `MAX_STREAMS` and as a result, the current capacity is above 0. The values provided in the callback arguments are capacity increments, meaning that the sum of all values from the callback will equal to the last value received from `MAX_STREAMS` ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This callback was designed to support <xref:System.Net.Http.SocketsHttpHandler.EnableMultipleHttp3Connections?displayProperty=nameWithType> functionality and comes with several caveats:
<xref:System.Net.Quic.QuicConnectionOptions.StreamCapacityCallback> is a callback that's invoked whenever the peer releases a new stream capacity via `MAX_STREAMS` and as a result, the current capacity is above 0. The values provided in the callback arguments are capacity increments, meaning that the sum of all values from the callback will equal the last value received from `MAX_STREAMS` ([RFC 9000 - MAX_STREAMS Frames](https://www.rfc-editor.org/rfc/rfc9000.html#frame-max-streams)). This callback was designed to support <xref:System.Net.Http.SocketsHttpHandler.EnableMultipleHttp3Connections?displayProperty=nameWithType> functionality and comes with several caveats:

```csharp
var stream1 = await connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional);
var stream2 = await connection.OpenOutboundStreamAsync(QuicStreamType.Unidirectional);
// The following call will get suspended because the stream is limit has been reached.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// The following call will get suspended because the stream is limit has been reached.
// The following call will get suspended because the stream's limit has been reached.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants