/// <summary> /// Maps incoming requests with the specified path to the provided connection pipeline. /// </summary> /// <param name="endpoints">The <see cref="IEndpointRouteBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="options">Options used to configure the connection.</param> /// <param name="configure">A callback to configure the connection.</param> /// <returns>An <see cref="ConnectionEndpointRouteBuilder"/> for endpoints associated with the connections.</returns> public static ConnectionEndpointRouteBuilder MapConnections(this IEndpointRouteBuilder endpoints, string pattern, HttpConnectionDispatcherOptions options, Action <IConnectionBuilder> configure) { var dispatcher = endpoints.ServiceProvider.GetRequiredService <HttpConnectionDispatcher>(); var connectionBuilder = new ConnectionBuilder(endpoints.ServiceProvider); configure(connectionBuilder); var connectionDelegate = connectionBuilder.Build(); // REVIEW: Consider expanding the internals of the dispatcher as endpoint routes instead of // using if statements we can let the matcher handle var conventionBuilders = new List <IEndpointConventionBuilder>(); // Build the negotiate application var app = endpoints.CreateApplicationBuilder(); app.UseWebSockets(); app.Run(c => dispatcher.ExecuteNegotiateAsync(c, options)); var negotiateHandler = app.Build(); var negotiateBuilder = endpoints.Map(pattern + "/negotiate", negotiateHandler); conventionBuilders.Add(negotiateBuilder); // Add the negotiate metadata so this endpoint can be identified negotiateBuilder.WithMetadata(new NegotiateMetadata()); negotiateBuilder.WithMetadata(options); // build the execute handler part of the protocol app = endpoints.CreateApplicationBuilder(); app.UseWebSockets(); app.Run(c => dispatcher.ExecuteAsync(c, options, connectionDelegate)); var executehandler = app.Build(); var executeBuilder = endpoints.Map(pattern, executehandler); conventionBuilders.Add(executeBuilder); var compositeConventionBuilder = new CompositeEndpointConventionBuilder(conventionBuilders); // Add metadata to all of Endpoints compositeConventionBuilder.Add(e => { // Add the authorization data as metadata foreach (var data in options.AuthorizationData) { e.Metadata.Add(data); } }); return(new ConnectionEndpointRouteBuilder(compositeConventionBuilder)); }
/// <summary> /// Maps incoming requests with the specified path to the provided connection pipeline. /// </summary> /// <param name="endpoints">The <see cref="IEndpointBuilder"/> to add the route to.</param> /// <param name="pattern">The route pattern.</param> /// <param name="options">Options used to configure the connection.</param> /// <param name="configure">A callback to configure the connection.</param> /// <returns>An <see cref="ConnectionEndpointRouteBuilder"/> for endpoints associated with the connections.</returns> public static ConnectionEndpointRouteBuilder MapConnections(this IEndpointBuilder endpoints, string pattern, HttpConnectionDispatcherOptions options, Action <IConnectionBuilder> configure) { var dispatcher = endpoints.ServiceProvider.GetRequiredService(_httpConnectionDispatcherType); var ExecuteNegotiateAsync = (Func <HttpContext, HttpConnectionDispatcherOptions, Task>) _httpConnectionDispatcherType .GetMethod("ExecuteNegotiateAsync") ! .CreateDelegate(typeof(Func <HttpContext, HttpConnectionDispatcherOptions, Task>), dispatcher); var ExecuteAsync = (Func <HttpContext, HttpConnectionDispatcherOptions, ConnectionDelegate, Task>) _httpConnectionDispatcherType .GetMethod("ExecuteAsync") ! .CreateDelegate(typeof(Func <HttpContext, HttpConnectionDispatcherOptions, ConnectionDelegate, Task>), dispatcher); var connectionBuilder = new ConnectionBuilder(endpoints.ServiceProvider); configure(connectionBuilder); var connectionDelegate = connectionBuilder.Build(); // REVIEW: Consider expanding the internals of the dispatcher as endpoint routes instead of // using if statements we can let the matcher handle var conventionBuilders = new List <IEndpointConventionBuilder>(); // Build the negotiate application var app = endpoints.CreateApplicationBuilder(); app.UseWebSockets(); app.Run(c => ExecuteNegotiateAsync(c, options)); var negotiateHandler = app.Build(); var negotiateBuilder = endpoints.MapRequestDelegate(pattern + "/negotiate", negotiateHandler); conventionBuilders.Add(negotiateBuilder); // Add the negotiate metadata so this endpoint can be identified negotiateBuilder.WithMetadata(new NegotiateMetadata()); // build the execute handler part of the protocol app = endpoints.CreateApplicationBuilder(); app.UseWebSockets(); app.Run(c => ExecuteAsync(c, options, connectionDelegate)); var executehandler = app.Build(); var executeBuilder = endpoints.MapRequestDelegate(pattern, executehandler); conventionBuilders.Add(executeBuilder); var compositeConventionBuilder = new CompositeEndpointConventionBuilder(conventionBuilders); // Add metadata to all of Endpoints compositeConventionBuilder.Add(e => { // Add the authorization data as metadata foreach (var data in options.AuthorizationData) { e.Metadata.Add(data); } }); return((ConnectionEndpointRouteBuilder) typeof(ConnectionEndpointRouteBuilder) .GetTypeInfo().DeclaredConstructors .Single() .Invoke(new object[] { compositeConventionBuilder })); }