diff --git a/benchmarks/BenchmarkServer/Hubs/EchoHub.cs b/benchmarks/BenchmarkServer/Hubs/EchoHub.cs index 739be3fc61..2e4144dc6d 100644 --- a/benchmarks/BenchmarkServer/Hubs/EchoHub.cs +++ b/benchmarks/BenchmarkServer/Hubs/EchoHub.cs @@ -16,7 +16,7 @@ namespace BenchmarkServer.Hubs { var t = new CancellationTokenSource(); t.CancelAfter(TimeSpan.FromSeconds(duration)); - while (!t.IsCancellationRequested && !Context.Connection.ConnectionAbortedToken.IsCancellationRequested) + while (!t.IsCancellationRequested && !Context.ConnectionAborted.IsCancellationRequested) { await Clients.All.SendAsync("echo", DateTime.UtcNow); } diff --git a/client-ts/FunctionalTests/TestHub.cs b/client-ts/FunctionalTests/TestHub.cs index e8a9d12b0c..a963f66c07 100644 --- a/client-ts/FunctionalTests/TestHub.cs +++ b/client-ts/FunctionalTests/TestHub.cs @@ -30,7 +30,7 @@ namespace FunctionalTests public Task InvokeWithString(string message) { - return Clients.Client(Context.Connection.ConnectionId).SendAsync("Message", message); + return Clients.Client(Context.ConnectionId).SendAsync("Message", message); } public Task SendCustomObject(CustomObject customObject) @@ -55,7 +55,7 @@ namespace FunctionalTests public string GetActiveTransportName() { - return Context.Connection.Items[ConnectionMetadataNames.Transport].ToString(); + return Context.Items[ConnectionMetadataNames.Transport].ToString(); } public ComplexObject EchoComplexObject(ComplexObject complexObject) diff --git a/client-ts/signalr/src/HttpClient.ts b/client-ts/signalr/src/HttpClient.ts index 2f8e329834..d3996e7e6b 100644 --- a/client-ts/signalr/src/HttpClient.ts +++ b/client-ts/signalr/src/HttpClient.ts @@ -8,7 +8,7 @@ export interface HttpRequest { method?: string; url?: string; content?: string | ArrayBuffer; - headers?: Map; + headers?: { [key: string]: string }; responseType?: XMLHttpRequestResponseType; abortSignal?: AbortSignal; timeout?: number; @@ -58,7 +58,8 @@ export class DefaultHttpClient extends HttpClient { xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); if (request.headers) { - request.headers.forEach((value, header) => xhr.setRequestHeader(header, value)); + Object.keys(request.headers) + .forEach((header) => xhr.setRequestHeader(header, request.headers[header])); } if (request.responseType) { diff --git a/client-ts/signalr/src/HttpConnection.ts b/client-ts/signalr/src/HttpConnection.ts index 178d67ecba..974a4bdb08 100644 --- a/client-ts/signalr/src/HttpConnection.ts +++ b/client-ts/signalr/src/HttpConnection.ts @@ -83,8 +83,9 @@ export class HttpConnection implements IConnection { let headers; const token = this.options.accessTokenFactory(); if (token) { - headers = new Map(); - headers.set("Authorization", `Bearer ${token}`); + headers = { + ["Authorization"]: `Bearer ${token}`, + }; } const negotiatePayload = await this.httpClient.post(this.resolveNegotiateUrl(this.baseUrl), { diff --git a/client-ts/signalr/src/HubConnection.ts b/client-ts/signalr/src/HubConnection.ts index f6aa33f333..3335b5eade 100644 --- a/client-ts/signalr/src/HubConnection.ts +++ b/client-ts/signalr/src/HubConnection.ts @@ -25,8 +25,8 @@ export class HubConnection { private readonly connection: IConnection; private readonly logger: ILogger; private protocol: IHubProtocol; - private callbacks: Map void>; - private methods: Map void>>; + private callbacks: { [invocationId: string]: (invocationEvent: StreamItemMessage | CompletionMessage, error?: Error) => void }; + private methods: { [name: string]: Array<(...args: any[]) => void> }; private id: number; private closedCallbacks: ConnectionClosed[]; private timeoutHandle: NodeJS.Timer; @@ -53,8 +53,8 @@ export class HubConnection { this.connection.onreceive = (data: any) => this.processIncomingData(data); this.connection.onclose = (error?: Error) => this.connectionClosed(error); - this.callbacks = new Map void>(); - this.methods = new Map void>>(); + this.callbacks = {}; + this.methods = {}; this.closedCallbacks = []; this.id = 0; } @@ -79,10 +79,10 @@ export class HubConnection { break; case MessageType.StreamItem: case MessageType.Completion: - const callback = this.callbacks.get(message.invocationId); + const callback = this.callbacks[message.invocationId]; if (callback != null) { if (message.type === MessageType.Completion) { - this.callbacks.delete(message.invocationId); + delete this.callbacks[message.invocationId]; } callback(message); } @@ -174,7 +174,7 @@ export class HubConnection { } private invokeClientMethod(invocationMessage: InvocationMessage) { - const methods = this.methods.get(invocationMessage.target.toLowerCase()); + const methods = this.methods[invocationMessage.target.toLowerCase()]; if (methods) { methods.forEach((m) => m.apply(this, invocationMessage.arguments)); if (invocationMessage.invocationId) { @@ -189,10 +189,14 @@ export class HubConnection { } private connectionClosed(error?: Error) { - this.callbacks.forEach((callback) => { - callback(undefined, error ? error : new Error("Invocation canceled due to connection being closed.")); - }); - this.callbacks.clear(); + const callbacks = this.callbacks; + this.callbacks = {}; + + Object.keys(callbacks) + .forEach((key) => { + const callback = callbacks[key]; + callback(undefined, error ? error : new Error("Invocation canceled due to connection being closed.")); + }); this.cleanupTimeout(); @@ -228,12 +232,12 @@ export class HubConnection { const cancelInvocation: CancelInvocationMessage = this.createCancelInvocation(invocationDescriptor.invocationId); const cancelMessage: any = this.protocol.writeMessage(cancelInvocation); - this.callbacks.delete(invocationDescriptor.invocationId); + delete this.callbacks[invocationDescriptor.invocationId]; return this.connection.send(cancelMessage); }); - this.callbacks.set(invocationDescriptor.invocationId, (invocationEvent: CompletionMessage | StreamItemMessage, error?: Error) => { + this.callbacks[invocationDescriptor.invocationId] = (invocationEvent: CompletionMessage | StreamItemMessage, error?: Error) => { if (error) { subject.error(error); return; @@ -248,14 +252,14 @@ export class HubConnection { } else { subject.next((invocationEvent.item) as T); } - }); + }; const message = this.protocol.writeMessage(invocationDescriptor); this.connection.send(message) .catch((e) => { subject.error(e); - this.callbacks.delete(invocationDescriptor.invocationId); + delete this.callbacks[invocationDescriptor.invocationId]; }); return subject; @@ -273,7 +277,7 @@ export class HubConnection { const invocationDescriptor = this.createInvocation(methodName, args, false); const p = new Promise((resolve, reject) => { - this.callbacks.set(invocationDescriptor.invocationId, (invocationEvent: StreamItemMessage | CompletionMessage, error?: Error) => { + this.callbacks[invocationDescriptor.invocationId] = (invocationEvent: StreamItemMessage | CompletionMessage, error?: Error) => { if (error) { reject(error); return; @@ -288,14 +292,14 @@ export class HubConnection { } else { reject(new Error(`Unexpected message type: ${invocationEvent.type}`)); } - }); + }; const message = this.protocol.writeMessage(invocationDescriptor); this.connection.send(message) .catch((e) => { reject(e); - this.callbacks.delete(invocationDescriptor.invocationId); + delete this.callbacks[invocationDescriptor.invocationId]; }); }); @@ -308,11 +312,11 @@ export class HubConnection { } methodName = methodName.toLowerCase(); - if (!this.methods.has(methodName)) { - this.methods.set(methodName, []); + if (!this.methods[methodName]) { + this.methods[methodName] = []; } - this.methods.get(methodName).push(method); + this.methods[methodName].push(method); } public off(methodName: string, method: (...args: any[]) => void) { @@ -321,7 +325,7 @@ export class HubConnection { } methodName = methodName.toLowerCase(); - const handlers = this.methods.get(methodName); + const handlers = this.methods[methodName]; if (!handlers) { return; } @@ -329,7 +333,7 @@ export class HubConnection { if (removeIdx !== -1) { handlers.splice(removeIdx, 1); if (handlers.length === 0) { - this.methods.delete(methodName); + delete this.methods[methodName]; } } } diff --git a/client-ts/signalr/src/Transports.ts b/client-ts/signalr/src/Transports.ts index 480196bc81..50f23e30b9 100644 --- a/client-ts/signalr/src/Transports.ts +++ b/client-ts/signalr/src/Transports.ts @@ -238,7 +238,7 @@ export class LongPollingTransport implements ITransport { private async poll(url: string, transferFormat: TransferFormat): Promise { const pollOptions: HttpRequest = { abortSignal: this.pollAbort.signal, - headers: new Map(), + headers: {}, timeout: 90000, }; @@ -248,7 +248,8 @@ export class LongPollingTransport implements ITransport { const token = this.accessTokenFactory(); if (token) { - pollOptions.headers.set("Authorization", `Bearer ${token}`); + // tslint:disable-next-line:no-string-literal + pollOptions.headers["Authorization"] = `Bearer ${token}`; } while (!this.pollAbort.signal.aborted) { @@ -316,8 +317,9 @@ async function send(httpClient: HttpClient, url: string, accessTokenFactory: () let headers; const token = accessTokenFactory(); if (token) { - headers = new Map(); - headers.set("Authorization", `Bearer ${accessTokenFactory()}`); + headers = { + ["Authorization"]: `Bearer ${accessTokenFactory()}`, + }; } await httpClient.post(url, { diff --git a/client-ts/tsconfig-base.json b/client-ts/tsconfig-base.json index 2122a6c242..78b45efe77 100644 --- a/client-ts/tsconfig-base.json +++ b/client-ts/tsconfig-base.json @@ -13,6 +13,6 @@ "noImplicitAny": true, "suppressImplicitAnyIndexErrors": true, "noEmitOnError": true, - "lib": [ "es2016", "dom" ] + "lib": [ "es5", "es2015.promise", "es2015.iterable", "dom" ] } } diff --git a/samples/ChatSample/PresenceHubLifetimeManager.cs b/samples/ChatSample/PresenceHubLifetimeManager.cs index 485bac061c..a318fee4a0 100644 --- a/samples/ChatSample/PresenceHubLifetimeManager.cs +++ b/samples/ChatSample/PresenceHubLifetimeManager.cs @@ -85,7 +85,7 @@ namespace ChatSample else { return hub.OnUsersJoined( - users.Where(u => u.ConnectionId != hub.Context.Connection.ConnectionId).ToArray()); + users.Where(u => u.ConnectionId != hub.Context.ConnectionId).ToArray()); } return Task.CompletedTask; }); @@ -112,7 +112,7 @@ namespace ChatSample } hub.Clients = new HubCallerClients(_hubContext.Clients, connection.ConnectionId); - hub.Context = new HubCallerContext(connection); + hub.Context = new DefaultHubCallerContext(connection); hub.Groups = _hubContext.Groups; try diff --git a/src/Microsoft.AspNetCore.SignalR.Core/DefaultHubCallerContext.cs b/src/Microsoft.AspNetCore.SignalR.Core/DefaultHubCallerContext.cs new file mode 100644 index 0000000000..9a22ef1bd6 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Core/DefaultHubCallerContext.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.SignalR +{ + public class DefaultHubCallerContext : HubCallerContext + { + private readonly HubConnectionContext _connection; + + public DefaultHubCallerContext(HubConnectionContext connection) + { + _connection = connection; + } + + public override string ConnectionId => _connection.ConnectionId; + + public override string UserIdentifier => _connection.UserIdentifier; + + public override ClaimsPrincipal User => _connection.User; + + public override IDictionary Items => _connection.Items; + + public override IFeatureCollection Features => _connection.Features; + + public override CancellationToken ConnectionAborted => _connection.ConnectionAborted; + + public override void Abort() => _connection.Abort(); + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubCallerContext.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubCallerContext.cs index 3f75195538..4f3e0a1572 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/HubCallerContext.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubCallerContext.cs @@ -1,21 +1,29 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Security.Claims; +using System.Threading; +using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.SignalR { - public class HubCallerContext + public abstract class HubCallerContext { - public HubCallerContext(HubConnectionContext connection) - { - Connection = connection; - } + public abstract string ConnectionId { get; } - public HubConnectionContext Connection { get; } + public abstract string UserIdentifier { get; } - public ClaimsPrincipal User => Connection.User; + public abstract ClaimsPrincipal User { get; } - public string ConnectionId => Connection.ConnectionId; + public abstract IDictionary Items { get; } + + public abstract IFeatureCollection Features { get; } + + public abstract CancellationToken ConnectionAborted { get; } + + public abstract void Abort(); } } diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs index 8026a1ad00..e723d6adce 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubConnectionContext.cs @@ -41,11 +41,11 @@ namespace Microsoft.AspNetCore.SignalR { _connectionContext = connectionContext; _logger = loggerFactory.CreateLogger(); - ConnectionAbortedToken = _connectionAbortedTokenSource.Token; + ConnectionAborted = _connectionAbortedTokenSource.Token; _keepAliveDuration = (int)keepAliveInterval.TotalMilliseconds * (Stopwatch.Frequency / 1000); } - public virtual CancellationToken ConnectionAbortedToken { get; } + public virtual CancellationToken ConnectionAborted { get; } public virtual string ConnectionId => _connectionContext.ConnectionId; @@ -66,14 +66,6 @@ namespace Microsoft.AspNetCore.SignalR // Currently used only for streaming methods internal ConcurrentDictionary ActiveRequestCancellationSources { get; } = new ConcurrentDictionary(); - public IPAddress RemoteIpAddress => Features.Get()?.RemoteIpAddress; - - public IPAddress LocalIpAddress => Features.Get()?.LocalIpAddress; - - public int? RemotePort => Features.Get()?.RemotePort; - - public int? LocalPort => Features.Get()?.LocalPort; - public virtual ValueTask WriteAsync(HubMessage message) { // We were unable to get the lock so take the slow async path of waiting for the semaphore diff --git a/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs b/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs index 85a707aba6..2ff9c0de1b 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/HubEndPoint.cs @@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.SignalR { while (true) { - var result = await connection.Input.ReadAsync(connection.ConnectionAbortedToken); + var result = await connection.Input.ReadAsync(connection.ConnectionAborted); var buffer = result.Buffer; var consumed = buffer.End; var examined = buffer.End; diff --git a/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs b/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs index b1092c0987..b4b29aebd6 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/Internal/DefaultHubDispatcher.cs @@ -300,7 +300,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal private void InitializeHub(THub hub, HubConnectionContext connection) { hub.Clients = new HubCallerClients(_hubContext.Clients, connection.ConnectionId); - hub.Context = new HubCallerContext(connection); + hub.Context = new DefaultHubCallerContext(connection); hub.Groups = _hubContext.Groups; } @@ -376,7 +376,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal { var streamCts = new CancellationTokenSource(); connection.ActiveRequestCancellationSources.TryAdd(invocationId, streamCts); - return CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAbortedToken, streamCts.Token).Token; + return CancellationTokenSource.CreateLinkedTokenSource(connection.ConnectionAborted, streamCts.Token).Token; } } diff --git a/src/Microsoft.AspNetCore.SignalR/HttpConnectionContextExtensions.cs b/src/Microsoft.AspNetCore.SignalR/HubCallerContextExtensions.cs similarity index 73% rename from src/Microsoft.AspNetCore.SignalR/HttpConnectionContextExtensions.cs rename to src/Microsoft.AspNetCore.SignalR/HubCallerContextExtensions.cs index 3842012f04..45ef53def2 100644 --- a/src/Microsoft.AspNetCore.SignalR/HttpConnectionContextExtensions.cs +++ b/src/Microsoft.AspNetCore.SignalR/HubCallerContextExtensions.cs @@ -6,9 +6,9 @@ using Microsoft.AspNetCore.Sockets.Http.Features; namespace Microsoft.AspNetCore.SignalR { - public static class DefaultConnectionContextExtensions + public static class HubCallerContextExtensions { - public static HttpContext GetHttpContext(this HubConnectionContext connection) + public static HttpContext GetHttpContext(this HubCallerContext connection) { return connection.Features.Get()?.HttpContext; } diff --git a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs index da95142802..58f7358234 100644 --- a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs +++ b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/Hubs.cs @@ -9,6 +9,7 @@ using System.Threading.Channels; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Sockets; namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests @@ -37,7 +38,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests public IEnumerable GetHeaderValues(string[] headerNames) { - var context = Context.Connection.GetHttpContext(); + var context = Context.GetHttpContext(); if (context == null) { @@ -56,17 +57,19 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests public string GetCookieValue(string cookieName) { - return Context.Connection.GetHttpContext().Request.Cookies[cookieName]; + return Context.GetHttpContext().Request.Cookies[cookieName]; } public object[] GetIHttpConnectionFeatureProperties() { + var feature = Context.Features.Get(); + object[] result = { - Context.Connection.LocalPort, - Context.Connection.RemotePort, - Context.Connection.LocalIpAddress.ToString(), - Context.Connection.RemoteIpAddress.ToString() + feature.LocalPort, + feature.RemotePort, + feature.LocalIpAddress.ToString(), + feature.RemoteIpAddress.ToString() }; return result; @@ -74,7 +77,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests public string GetActiveTransportName() { - return Context.Connection.Items[ConnectionMetadataNames.Transport].ToString(); + return Context.Items[ConnectionMetadataNames.Transport].ToString(); } } diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTestUtils/Hubs.cs b/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTestUtils/Hubs.cs index 8b9a5b8d4d..b78324bdc6 100644 --- a/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTestUtils/Hubs.cs +++ b/test/Microsoft.AspNetCore.SignalR.Tests/HubEndpointTestUtils/Hubs.cs @@ -144,7 +144,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils public bool HasHttpContext() { - return Context.Connection.GetHttpContext() != null; + return Context.GetHttpContext() != null; } public Task SendToOthers(string message) @@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils { public override Task OnConnectedAsync() { - var tcs = (TaskCompletionSource)Context.Connection.Items["ConnectedTask"]; + var tcs = (TaskCompletionSource)Context.Items["ConnectedTask"]; tcs?.TrySetResult(true); return base.OnConnectedAsync(); } @@ -172,7 +172,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils { public override Task OnConnectedAsync() { - var tcs = (TaskCompletionSource)Context.Connection.Items["ConnectedTask"]; + var tcs = (TaskCompletionSource)Context.Items["ConnectedTask"]; tcs?.TrySetResult(true); return base.OnConnectedAsync(); } @@ -252,7 +252,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils { public override Task OnConnectedAsync() { - var tcs = (TaskCompletionSource)Context.Connection.Items["ConnectedTask"]; + var tcs = (TaskCompletionSource)Context.Items["ConnectedTask"]; tcs?.TrySetResult(true); return base.OnConnectedAsync(); } @@ -437,7 +437,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils { public void Kill() { - Context.Connection.Abort(); + Context.Abort(); } } @@ -623,9 +623,9 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils public override Task OnConnectedAsync() { - _state.TokenStateInConnected = Context.Connection.ConnectionAbortedToken.IsCancellationRequested; + _state.TokenStateInConnected = Context.ConnectionAborted.IsCancellationRequested; - Context.Connection.ConnectionAbortedToken.Register(() => + Context.ConnectionAborted.Register(() => { _state.TokenCallbackTriggered = true; }); @@ -635,7 +635,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests.HubEndpointTestUtils public override Task OnDisconnectedAsync(Exception exception) { - _state.TokenStateInDisconnected = Context.Connection.ConnectionAbortedToken.IsCancellationRequested; + _state.TokenStateInDisconnected = Context.ConnectionAborted.IsCancellationRequested; return base.OnDisconnectedAsync(exception); }