diff options
Diffstat (limited to 'src/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs')
| -rw-r--r-- | src/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs | 131 |
1 files changed, 65 insertions, 66 deletions
diff --git a/src/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs b/src/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs index d59e4e5e3..7d86434b8 100644 --- a/src/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs +++ b/src/Jellyfin.Networking/HappyEyeballs/HttpClientExtension.cs @@ -30,91 +30,90 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; -namespace Jellyfin.Networking.HappyEyeballs +namespace Jellyfin.Networking.HappyEyeballs; + +/// <summary> +/// Defines the <see cref="HttpClientExtension"/> class. +/// +/// Implementation taken from https://github.com/ppy/osu-framework/pull/4191 . +/// </summary> +public static class HttpClientExtension { /// <summary> - /// Defines the <see cref="HttpClientExtension"/> class. - /// - /// Implementation taken from https://github.com/ppy/osu-framework/pull/4191 . + /// Gets or sets a value indicating whether the client should use IPv6. /// </summary> - public static class HttpClientExtension + public static bool UseIPv6 { get; set; } = true; + + /// <summary> + /// Implements the httpclient callback method. + /// </summary> + /// <param name="context">The <see cref="SocketsHttpConnectionContext"/> instance.</param> + /// <param name="cancellationToken">The <see cref="CancellationToken"/> instance.</param> + /// <returns>The http steam.</returns> + public static async ValueTask<Stream> OnConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken) { - /// <summary> - /// Gets or sets a value indicating whether the client should use IPv6. - /// </summary> - public static bool UseIPv6 { get; set; } = true; - - /// <summary> - /// Implements the httpclient callback method. - /// </summary> - /// <param name="context">The <see cref="SocketsHttpConnectionContext"/> instance.</param> - /// <param name="cancellationToken">The <see cref="CancellationToken"/> instance.</param> - /// <returns>The http steam.</returns> - public static async ValueTask<Stream> OnConnect(SocketsHttpConnectionContext context, CancellationToken cancellationToken) + if (!UseIPv6) { - if (!UseIPv6) - { - return await AttemptConnection(AddressFamily.InterNetwork, context, cancellationToken).ConfigureAwait(false); - } + return await AttemptConnection(AddressFamily.InterNetwork, context, cancellationToken).ConfigureAwait(false); + } - using var cancelIPv6 = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - var tryConnectAsyncIPv6 = AttemptConnection(AddressFamily.InterNetworkV6, context, cancelIPv6.Token); + using var cancelIPv6 = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var tryConnectAsyncIPv6 = AttemptConnection(AddressFamily.InterNetworkV6, context, cancelIPv6.Token); - // GetAwaiter().GetResult() is used instead of .Result as this results in improved exception handling. - // The tasks have already been completed. - // See https://github.com/dotnet/corefx/pull/29792/files#r189415885 for more details. - if (await Task.WhenAny(tryConnectAsyncIPv6, Task.Delay(200, cancelIPv6.Token)).ConfigureAwait(false) == tryConnectAsyncIPv6 && tryConnectAsyncIPv6.IsCompletedSuccessfully) + // GetAwaiter().GetResult() is used instead of .Result as this results in improved exception handling. + // The tasks have already been completed. + // See https://github.com/dotnet/corefx/pull/29792/files#r189415885 for more details. + if (await Task.WhenAny(tryConnectAsyncIPv6, Task.Delay(200, cancelIPv6.Token)).ConfigureAwait(false) == tryConnectAsyncIPv6 && tryConnectAsyncIPv6.IsCompletedSuccessfully) + { + await cancelIPv6.CancelAsync().ConfigureAwait(false); + return tryConnectAsyncIPv6.GetAwaiter().GetResult(); + } + + using var cancelIPv4 = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + var tryConnectAsyncIPv4 = AttemptConnection(AddressFamily.InterNetwork, context, cancelIPv4.Token); + + if (await Task.WhenAny(tryConnectAsyncIPv6, tryConnectAsyncIPv4).ConfigureAwait(false) == tryConnectAsyncIPv6) + { + if (tryConnectAsyncIPv6.IsCompletedSuccessfully) { - await cancelIPv6.CancelAsync().ConfigureAwait(false); + await cancelIPv4.CancelAsync().ConfigureAwait(false); return tryConnectAsyncIPv6.GetAwaiter().GetResult(); } - using var cancelIPv4 = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - var tryConnectAsyncIPv4 = AttemptConnection(AddressFamily.InterNetwork, context, cancelIPv4.Token); - - if (await Task.WhenAny(tryConnectAsyncIPv6, tryConnectAsyncIPv4).ConfigureAwait(false) == tryConnectAsyncIPv6) + return tryConnectAsyncIPv4.GetAwaiter().GetResult(); + } + else + { + if (tryConnectAsyncIPv4.IsCompletedSuccessfully) { - if (tryConnectAsyncIPv6.IsCompletedSuccessfully) - { - await cancelIPv4.CancelAsync().ConfigureAwait(false); - return tryConnectAsyncIPv6.GetAwaiter().GetResult(); - } - + await cancelIPv6.CancelAsync().ConfigureAwait(false); return tryConnectAsyncIPv4.GetAwaiter().GetResult(); } - else - { - if (tryConnectAsyncIPv4.IsCompletedSuccessfully) - { - await cancelIPv6.CancelAsync().ConfigureAwait(false); - return tryConnectAsyncIPv4.GetAwaiter().GetResult(); - } - return tryConnectAsyncIPv6.GetAwaiter().GetResult(); - } + return tryConnectAsyncIPv6.GetAwaiter().GetResult(); } + } - private static async Task<Stream> AttemptConnection(AddressFamily addressFamily, SocketsHttpConnectionContext context, CancellationToken cancellationToken) + private static async Task<Stream> AttemptConnection(AddressFamily addressFamily, SocketsHttpConnectionContext context, CancellationToken cancellationToken) + { + // The following socket constructor will create a dual-mode socket on systems where IPV6 is available. + var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp) { - // The following socket constructor will create a dual-mode socket on systems where IPV6 is available. - var socket = new Socket(addressFamily, SocketType.Stream, ProtocolType.Tcp) - { - // Turn off Nagle's algorithm since it degrades performance in most HttpClient scenarios. - NoDelay = true - }; + // Turn off Nagle's algorithm since it degrades performance in most HttpClient scenarios. + NoDelay = true + }; - try - { - await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false); - // The stream should take the ownership of the underlying socket, - // closing it when it's disposed. - return new NetworkStream(socket, ownsSocket: true); - } - catch - { - socket.Dispose(); - throw; - } + try + { + await socket.ConnectAsync(context.DnsEndPoint, cancellationToken).ConfigureAwait(false); + // The stream should take the ownership of the underlying socket, + // closing it when it's disposed. + return new NetworkStream(socket, ownsSocket: true); + } + catch + { + socket.Dispose(); + throw; } } } |
