File: HttpConnectionFactory.cs
Web Access
Project: src\src\SignalR\clients\csharp\Http.Connections.Client\src\Microsoft.AspNetCore.Http.Connections.Client.csproj (Microsoft.AspNetCore.Http.Connections.Client)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Connections;
using Microsoft.AspNetCore.Shared;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
 
namespace Microsoft.AspNetCore.Http.Connections.Client;
 
/// <summary>
/// A factory for creating <see cref="HttpConnection"/> instances.
/// </summary>
public class HttpConnectionFactory : IConnectionFactory
{
    private readonly HttpConnectionOptions _httpConnectionOptions;
    private readonly ILoggerFactory _loggerFactory;
 
    /// <summary>
    /// Initializes a new instance of the <see cref="HttpConnectionFactory"/> class.
    /// </summary>
    /// <param name="options">The connection options.</param>
    /// <param name="loggerFactory">The logger factory.</param>
    public HttpConnectionFactory(IOptions<HttpConnectionOptions> options, ILoggerFactory loggerFactory)
    {
        ArgumentNullThrowHelper.ThrowIfNull(options);
 
        _httpConnectionOptions = options.Value;
        _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
    }
 
    /// <summary>
    /// Creates a new connection to an <see cref="UriEndPoint"/>.
    /// </summary>
    /// <param name="endPoint">The <see cref="UriEndPoint"/> to connect to.</param>
    /// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <see cref="CancellationToken.None" />.</param>
    /// <returns>
    /// A <see cref="ValueTask{TResult}" /> that represents the asynchronous connect, yielding the <see cref="ConnectionContext" /> for the new connection when completed.
    /// </returns>
    public async ValueTask<ConnectionContext> ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default)
    {
        ArgumentNullThrowHelper.ThrowIfNull(endPoint);
 
        if (!(endPoint is UriEndPoint uriEndPoint))
        {
            throw new NotSupportedException($"The provided {nameof(EndPoint)} must be of type {nameof(UriEndPoint)}.");
        }
 
        if (_httpConnectionOptions.Url != null && _httpConnectionOptions.Url != uriEndPoint.Uri)
        {
            throw new InvalidOperationException($"If {nameof(HttpConnectionOptions)}.{nameof(HttpConnectionOptions.Url)} was set, it must match the {nameof(UriEndPoint)}.{nameof(UriEndPoint.Uri)} passed to {nameof(ConnectAsync)}.");
        }
 
        // Shallow copy before setting the Url property so we don't mutate the user-defined options object.
        var shallowCopiedOptions = ShallowCopyHttpConnectionOptions(_httpConnectionOptions);
        shallowCopiedOptions.Url = uriEndPoint.Uri;
 
        var connection = new HttpConnection(shallowCopiedOptions, _loggerFactory);
 
        try
        {
            await connection.StartAsync(cancellationToken).ConfigureAwait(false);
            return connection;
        }
        catch
        {
            // Make sure the connection is disposed, in case it allocated any resources before failing.
            await connection.DisposeAsync().ConfigureAwait(false);
            throw;
        }
    }
 
    // Internal for testing
    internal static HttpConnectionOptions ShallowCopyHttpConnectionOptions(HttpConnectionOptions options)
    {
        var newOptions = new HttpConnectionOptions
        {
            HttpMessageHandlerFactory = options.HttpMessageHandlerFactory,
            Headers = options.Headers,
            Url = options.Url,
            Transports = options.Transports,
            SkipNegotiation = options.SkipNegotiation,
            AccessTokenProvider = options.AccessTokenProvider,
            CloseTimeout = options.CloseTimeout,
            DefaultTransferFormat = options.DefaultTransferFormat,
            ApplicationMaxBufferSize = options.ApplicationMaxBufferSize,
            TransportMaxBufferSize = options.TransportMaxBufferSize,
            UseStatefulReconnect = options.UseStatefulReconnect,
        };
 
        if (!OperatingSystem.IsBrowser())
        {
            newOptions.Cookies = options.Cookies;
            newOptions.ClientCertificates = options.ClientCertificates;
            newOptions.Credentials = options.Credentials;
            newOptions.Proxy = options.Proxy;
            newOptions.UseDefaultCredentials = options.UseDefaultCredentials;
            newOptions.WebSocketConfiguration = options.WebSocketConfiguration;
            newOptions.WebSocketFactory = options.WebSocketFactory;
        }
 
        return newOptions;
    }
}